Compare commits

...

188 Commits

Author SHA1 Message Date
Artem Bochkarev
581e741e44 8242354: implemented remaining bufImgOps (LookupOp, ConvolveOp) 2020-05-10 07:00:20 +03:00
Artem Bochkarev
54abe9b4be 8242354: refactoring (use single struct instead of parameters lists)
8242354: add missing header
2020-05-10 07:00:20 +03:00
Artem Bochkarev
467f86c734 8242354: initial support for bufImgOps (RescaleOp) 2020-05-10 07:00:20 +03:00
Vitaly Provodin
b7e5659e28 updated JTreg exclude list 2020-05-10 07:00:20 +03:00
Vitaly Provodin
5f20e87ed2 remove the Nashorn JavaScript Engine
see http://openjdk.java.net/jeps/372,
https://bugs.openjdk.java.net/browse/JDK-8236933 and https://bugs.openjdk.java.net/browse/JDK-8241749
2020-05-10 07:00:20 +03:00
Vitaly Provodin
afd7dfdc13 updated JTreg exclude list 2020-05-10 07:00:19 +03:00
Vitaly Provodin
57f52aeee8 updated JTreg exclude list 2020-05-10 07:00:19 +03:00
Vitaly Provodin
4367fa1051 updated JTreg exclude list 2020-05-10 07:00:19 +03:00
Vitaly Provodin
4797e88a2d updated JTreg exclude list 2020-05-10 07:00:19 +03:00
Vitaly Provodin
fb2297c32f updated JTreg exclude list 2020-05-10 07:00:19 +03:00
Vitaly Provodin
7588a2040f add notarization scripts 2020-05-10 07:00:19 +03:00
Vitaly Provodin
377589f319 JBR-2212 add JBR building scripts 2020-05-10 07:00:19 +03:00
Vitaly Provodin
936c64b9de updated JTreg exclude list 2020-05-10 07:00:19 +03:00
Vitaly Provodin
9a9764e7bc JBR-1988: switch building onto 10.13 2020-05-10 07:00:18 +03:00
Vitaly Provodin
1d2461d900 updated JTreg exclude list 2020-05-10 07:00:18 +03:00
Vitaly Provodin
f1f3280b31 updated JTreg exclude list 2020-05-10 07:00:18 +03:00
Vitaly Provodin
b8704fbba0 updated JTreg exclude list 2020-05-10 07:00:18 +03:00
Vitaly Provodin
57a5af6d4c updated JTreg exclude list 2020-05-10 07:00:18 +03:00
Vitaly Provodin
d82177187b updated JTreg exclude list 2020-05-10 07:00:18 +03:00
Vitaly Provodin
92a62f86f6 updated JTreg exclude list 2020-05-10 07:00:18 +03:00
Vitaly Provodin
9b26892e5f updated JTreg exclude list 2020-05-10 07:00:18 +03:00
Vitaly Provodin
b28de97e32 updated JTreg exclude list 2020-05-10 07:00:17 +03:00
Vitaly Provodin
6488138f22 updated JTreg exclude list 2020-05-10 07:00:17 +03:00
Vitaly.Provodin
6af241c58b updated JTreg exclude list 2020-05-10 07:00:17 +03:00
Vitaly Provodin
4e3e10697a updated JTreg exclude list 2020-05-10 07:00:17 +03:00
Vitaly Provodin
e6433d2507 updated JTreg exclude list 2020-05-10 07:00:17 +03:00
duke
de6cd3a80b Automatic merge of client:master into master 2020-05-09 04:26:30 +00:00
duke
8927d5c2f7 Automatic merge of client:master into master 2020-05-08 06:17:29 +00:00
duke
7b6d7af13f Automatic merge of client:master into master 2020-05-08 04:18:56 +00:00
duke
03b75851a3 Automatic merge of client:master into master 2020-05-08 04:16:49 +00:00
Alexey Ushakov
167a33c011 8242338: Shape clip with AA not working in J2DDemo 2020-05-07 23:25:33 +00:00
Artem Bochkarev
c76eeefa8e 8243545: don't use clip rect in texture-mode (in IsoBlit) 2020-05-07 21:45:30 +00:00
duke
161711cb2f Automatic merge of client:master into master 2020-05-07 17:03:48 +00:00
duke
de2abf17d6 Automatic merge of client:master into master 2020-05-06 08:35:51 +00:00
duke
b8f0b5fc66 Automatic merge of client:master into master 2020-05-06 08:31:23 +00:00
duke
5a37b5225a Automatic merge of client:master into master 2020-05-04 18:36:20 +00:00
duke
a3691b07ef Automatic merge of client:master into master 2020-05-04 03:42:11 +00:00
duke
38811d3a0e Automatic merge of client:master into master 2020-05-04 03:33:23 +00:00
duke
04523d9d80 Automatic merge of client:master into master 2020-05-02 04:41:49 +00:00
duke
0002a91517 Automatic merge of client:master into master 2020-05-01 21:43:02 +00:00
duke
5f36a43220 Automatic merge of client:master into master 2020-05-01 00:24:27 +00:00
duke
6698ac4ded Automatic merge of client:master into master 2020-04-30 17:49:06 +00:00
duke
ef98db1d0d Automatic merge of client:master into master 2020-04-30 14:38:17 +00:00
Jayathirth D V
f643afdb22 8244193: Remove nextDrawableCount and lock RQ only during the blit op… 2020-04-30 13:53:32 +00:00
duke
be4bea9585 Automatic merge of client:master into master 2020-04-30 07:15:31 +00:00
duke
20f6e79c6d Automatic merge of client:master into master 2020-04-30 07:12:26 +00:00
duke
d22b543058 Automatic merge of client:master into master 2020-04-29 08:35:35 +00:00
duke
341cf3716a Automatic merge of client:master into master 2020-04-28 19:32:26 +00:00
Alexey Ushakov
7c59839250 8243508: Update alpha blending and compositing functions 2020-04-27 22:25:31 +00:00
duke
923bd0bb87 Automatic merge of client:master into master 2020-04-27 21:23:57 +00:00
duke
9d847b543a Automatic merge of client:master into master 2020-04-27 10:32:36 +00:00
duke
e54446332d Automatic merge of client:master into master 2020-04-27 05:11:43 +00:00
duke
ac569a56a0 Automatic merge of client:master into master 2020-04-24 11:52:19 +00:00
Jayathirth D V
89705e4cd8 8243538: Use glyphCacheLCD for LCD text rendering 2020-04-24 08:47:32 +00:00
Kevin Rushforth
07e9fbf764 8243505: SRC_OVER alpha composite mode doesn't render correctly 2020-04-24 01:43:10 +00:00
duke
e9d7168fc7 Automatic merge of client:master into master 2020-04-23 17:48:02 +00:00
Artem Bochkarev
7cc7448622 8242354: implemented MaskBlit 2020-04-23 12:22:07 +00:00
duke
b6f2fd6c44 Automatic merge of client:master into master 2020-04-22 08:34:08 +00:00
duke
bc4283413d Automatic merge of client:master into master 2020-04-22 06:49:26 +00:00
duke
92f27e94d7 Automatic merge of client:master into master 2020-04-22 00:05:52 +00:00
Jayathirth D V
4ffc9752bc 8243253: Disable incomplete cached text rendering for LCD 2020-04-21 11:51:02 +00:00
duke
b5c1be0c30 Automatic merge of client:master into master 2020-04-20 22:22:11 +00:00
duke
fd4cf1d519 Automatic merge of client:master into master 2020-04-20 20:53:28 +00:00
duke
130560f769 Automatic merge of client:master into master 2020-04-20 18:34:19 +00:00
duke
fd85e5fbd1 Automatic merge of client:master into master 2020-04-20 16:42:21 +00:00
Ajit Ghaisas
d991baed46 8243158: Lanai - Interpolation constants should be mapped from AffineTransformOp 2020-04-20 11:44:40 +00:00
Jayathirth D V
79f176b4a1 8243160: Update AddToGlyphCache to handle LCD text rendering 2020-04-20 11:33:22 +00:00
Jayathirth D V
61fdc8a95a 8243159: Disable unused glyph cache code in LCD rendering 2020-04-20 10:53:52 +00:00
Jayathirth D V
ec0ca2bc72 8243131: DisableMaskCache in MTLMaskFill to maintain proper state 2020-04-20 05:45:36 +00:00
duke
137168f694 Automatic merge of client:master into master 2020-04-17 06:02:55 +00:00
duke
ce891e9756 Automatic merge of client:master into master 2020-04-17 05:44:49 +00:00
duke
3eb5cfb91a Automatic merge of client:master into master 2020-04-16 17:23:24 +00:00
Alexey Ushakov
ee7fee1d07 8242653: Lanai: SRC_OVER with extra alpha does not work for VI 2020-04-16 10:14:01 +00:00
duke
616100a6c4 Automatic merge of client:master into master 2020-04-15 07:13:21 +00:00
duke
63f81c9587 Automatic merge of client:master into master 2020-04-14 08:15:34 +00:00
duke
1c0fe72535 Automatic merge of client:master into master 2020-04-13 12:16:17 +00:00
duke
45534e2389 Automatic merge of client:master into master 2020-04-11 05:10:28 +00:00
duke
469c179c9a Automatic merge of client:master into master 2020-04-10 06:30:24 +00:00
Artem Bochkarev
5cae755f21 8233309: implement image rendering options 2020-04-09 13:53:45 +00:00
Alexey Ushakov
8d90ece185 8233305: Implement rendering to volatile image 2020-04-09 12:33:55 +00:00
Alexey Ushakov
35aa6e1810 8233305: Implement rendering to volatile image 2020-04-09 11:30:50 +00:00
Prasanta Sadhukhan
aeb0b443d1 8242444: LCD Text Rendering characters other than 1st are not rendere… 2020-04-09 10:07:49 +00:00
duke
e945ccef4b Automatic merge of client:master into master 2020-04-09 00:01:14 +00:00
duke
c7ab913154 Automatic merge of client:master into master 2020-04-08 20:23:14 +00:00
duke
639f384353 Automatic merge of client:master into master 2020-04-08 17:55:17 +00:00
duke
e00df2c2d3 Automatic merge of client:master into master 2020-04-08 11:02:32 +00:00
duke
4ae50c4519 Automatic merge of client:master into master 2020-04-08 09:40:32 +00:00
duke
0772c1933b Automatic merge of client:master into master 2020-04-08 09:08:30 +00:00
duke
15d9b35741 Automatic merge of client:master into master 2020-04-08 08:47:32 +00:00
Kevin Rushforth
5574cca908 8242185: Lanai: JDK built on macOS 10.15 fails to run on macOS 10.13
Specify metal version 2.0 when compiling shaders
2020-04-07 17:10:18 +00:00
duke
3fcc3f71d9 Automatic merge of client:master into master 2020-04-07 04:27:32 +00:00
duke
76e7b41c53 Automatic merge of client:master into master 2020-04-07 03:22:29 +00:00
duke
7d657e8c94 Automatic merge of client:master into master 2020-04-06 19:05:32 +00:00
duke
0de241433d Automatic merge of client:master into master 2020-04-03 22:05:27 +00:00
Prasanta Sadhukhan
fd95090fa2 8242079: Update RenderPerf LCD Test to ensure LCD code path is used 2020-04-03 09:05:13 +00:00
Jayathirth D V
9cb40a9bc7 8242035: Combine drawPrimitive calls in text drawing flush
Co-authored-by: Ajit Ghaisas <ajit.ghaisas@oracle.com>
2020-04-02 12:00:25 +00:00
duke
97c7a63ab6 Automatic merge of client:master into master 2020-04-01 03:00:25 +00:00
duke
623aa48c4f Automatic merge of client:master into master 2020-04-01 02:50:27 +00:00
duke
83bbb3941e Automatic merge of client:master into master 2020-03-31 17:06:37 +00:00
Phil Race
cae4b9cc37 Merge 2020-03-31 17:04:42 +00:00
Alexey Ushakov
9226e84501 8241594: Lanai: javax/swing/JFileChooser/8013442: SIGSEGV at AMDMTLBr… 2020-03-26 17:42:15 +00:00
Ajit Ghaisas
3da068b282 8238223: Lanai - JPopupMenu/7154841 - no mouse events on the popup menu 2020-03-26 15:14:38 +00:00
Alexey Ushakov
45fea40dbd JDK-8241422: Regression in RenderPerfTest after JDK-8238535 2020-03-24 13:46:02 +00:00
Jayathirth D V
e5d516d182 8241490: Add large text performance tests in RenderPerfTest 2020-03-24 10:46:23 +00:00
Alexey Ushakov
db88ce172a JDK-8241156: Lanai: JPEG Image does not render 2020-03-20 14:03:41 +00:00
Alexey Ushakov
4a9a1708b3 JDK-8241143 Lanai: LinearGradient does not work in AA mode 2020-03-18 12:19:59 +00:00
Alexey Ushakov
43bbd51b72 JDK-8238535: Shape Clip does not work with AA rendering 2020-03-17 17:02:50 +00:00
Alexey Ushakov
c02799eede JDK-8240996: Lanai: rendering artifacts with external GPU 2020-03-13 10:07:41 +00:00
Alexey Ushakov
6ff6db02e7 JDK-8240573: Texture paint does not work with AA rendering 2020-03-12 20:18:02 +00:00
Prasanta Sadhukhan
032aeb34e6 JDK-8240926: Do not prevent metal initialisation even if metal suppor… 2020-03-12 06:59:05 +00:00
Prasanta Sadhukhan
8b636c6a87 8240796: Infinite loop in Diagnostic message code 2020-03-10 10:32:02 +00:00
Prasanta Sadhukhan
26d9a0794c Metal support diagnostics message 2020-03-09 11:53:36 +00:00
Ajit Ghaisas
7744fb099e 8238674: WindowOwnedByEmbeddedFrameTest: sun.java2d.opengl.CGLLayer cannot be cast to sun.java2d.metal.MTLLayer 2020-03-09 09:27:47 +00:00
Prasanta Sadhukhan
f66a1f1263 8233314: LCD Text rendering implementation with glyph cache 2020-03-04 10:50:14 +00:00
Kevin Rushforth
bc73e009cc 8240328: Update .jcheck/conf for lanai 2020-03-03 19:05:51 +00:00
Prasanta Sadhukhan
ff5966de7f 8233312: LCD text rendering implementation without glyph cache
First character of a string is rendered without artifact. Subsequent characters are rendered but not in same contrast as first chatacter.
2020-03-02 16:08:47 +05:30
Jayathirth D V
6ab1ed5817 Merge 2020-03-02 09:26:25 +05:30
Alexey Ushakov
075e1321e3 8240234: Improve performance of bulk MTLRenderer_FillAAParallelogram ops 2020-02-28 19:47:31 +03:00
Ajit Ghaisas
6a57e1fd9c 8233226: Implement XOR Mode rendering option
XOR Mode rendering support for primitive rendering and text rendering.
2020-02-28 15:59:05 +05:30
Alexey Ushakov
2e6bdc171a 8240176: Lanai: MTLPaint copyFrom - incorrect color handling 2020-02-27 15:44:44 +03:00
Jayathirth D V
72cac62f89 8239858: Initial implementation of caching for LCD text 2020-02-24 17:29:53 +05:30
Prasanta Sadhukhan
98743762dd 8233312: LCD text rendering implementation without glyph cache
First character of a string is rendered.
2020-02-24 13:59:01 +05:30
Jayathirth D V
f0445c24e8 Merge 2020-02-21 11:00:23 +05:30
Jayathirth D V
d0b2ad3e82 8239546: Update Vertexcache and GlyphCache flush limits 2020-02-20 16:08:28 +05:30
Jayathirth D V
32a1a537f0 8239220: Update text cache management to handle lot of unique glyphs 2020-02-17 17:32:53 +05:30
Prasanta Sadhukhan
5199fb702d 8233312: LCD text rendering implementation without glyph cache
Incremental implementation of LCD text.
2020-02-17 15:56:03 +05:30
Prasanta Sadhukhan
3bd02981db 8238744: JCK interactive test doesn't show instructions
Made JCK instruction frame go via grayscale code path as LCD text rendering is not fully done yet.
2020-02-11 16:10:59 +05:30
Jayathirth D V
8a1132d3a2 Merge 2020-02-07 10:39:23 +05:30
Prasanta Sadhukhan
3174bd2c75 8233312: LCD Text Rendering implementation without glyph cache
Initial draft implementation of LCD text without glyph cache.
2020-02-06 14:39:05 +05:30
Ajit Ghaisas
6e24ac5e09 8233231: Implement PaintType : Texture Paint
Modified shader to support all transformations of texture paint anchor rectangle.
2020-02-05 15:23:56 +05:30
Ajit Ghaisas
6d8ae023f2 8238465: [Lanai] Fix build errors on non-mac platforms 2020-02-04 15:31:58 +05:30
Ajit Ghaisas
c8002d297a 8233231: Implement PaintType : Texture Paint
This is basic untransformed texture paint support.
2020-02-03 22:54:17 +05:30
Jayathirth D V
3b48643a82 Merge 2020-01-31 13:39:17 +05:30
Alexey Ushakov
07ac868b77 8238165: Lanai: crashes at [libobjc.A.dylib+0x601d] objc_msgSend+0x1d
Fixed incorrect deallocation of native resources connected with MTLSurfaceData. Applied OGL fix logic (8146238)
2020-01-29 21:32:10 +03:00
Kevin Rushforth
a2d1099854 8234813: MTLRenderer_FillAAParallelogram is not implemented
Fix build failure
2020-01-23 10:32:13 -08:00
Alexey Ushakov
96a52e96de 8234813: MTLRenderer_FillAAParallelogram is not implemented
Added _Nonnull annotation
2020-01-23 20:07:34 +03:00
Alexey Ushakov
57e1ab737d 8234813: MTLRenderer_FillAAParallelogram is not implemented
Use multisampling rendering for AA parallelogram rendering
2020-01-23 17:24:18 +03:00
Alexey Ushakov
7bc2660967 8237458: J2Ddemo test becomes unresponsive after launching
Fixed memory leak in texture pool and restored changeset 09b8eb38bbfa
8236137 (avoid texture reallocations inside vertex-cache)
2020-01-22 22:52:18 +03:00
Jayathirth D V
c4ef423d8e 8237608: Initial implementation of Grayscale with cache text rendering 2020-01-22 11:09:37 +05:30
Alexey Ushakov
269e7e98e6 8237458: J2Ddemo test becomes unresponsive after launching
Backed out changeset 09b8eb38bbfa
8236137 (avoid texture reallocations inside vertex-cache)
2020-01-20 16:54:42 +03:00
Jayathirth D V
136f957e05 Merge 2020-01-14 15:32:41 +05:30
Ajit Ghaisas
364be79d47 8236616: Polyline drawing is incorrect 2020-01-03 16:13:00 +05:30
Artem Bochkarev
2ad73d2e67 8236137: avoid texture reallocations inside vertex-cache 2019-12-25 02:25:33 +03:00
Artem Bochkarev
a941e59035 8236104: introduce EncoderManager, split MTLContext into several classes
EncoderManager:
1. checks destination texture changes (creates new encoder when dest changes)
2. changes states of encoder only when cached state doesn't equal required states

Also:
- extract Composite, Transform, Clip and Paint entities from MTLContext (shaders and buffers are set in MTLPaint.setPipelineState)
- reimplemented MTLPipelineStatesStorage (simplified), add several optimizations (don't use stringWithFormat, fast state search)
- fix texture shaders
- fixed various composite problems
- optimizations
2019-12-25 02:21:28 +03:00
Artem Bochkarev
c4baadb3c9 8233710: support all params for native blit loops
with:
1. code cleanup (remove unnecessary code)
2. fix clip-rect usage
3. add debug under define
4. minor optimizations
2019-12-25 01:50:00 +03:00
Ajit Ghaisas
e2d1e4c804 8233233 : Implement Shape Clip 2019-12-19 14:38:40 +05:30
Jayathirth D V
dee4009a97 8235176: Add local glyph state management and remove global references 2019-12-02 14:20:32 +05:30
Jayathirth D V
e682c5129a Merge 2019-11-27 13:34:58 +05:30
Jayathirth D V
cdcb094b65 8233190: Use multi commandbuffer and draw back buffer in QueueFlusher 2019-11-26 16:35:19 +05:30
Artem Bochkarev
537653bd49 8234506: implement cleaning logic for textures pool
* use Last-Recent-Used strategy
* also optimized search of item
* also add command buffer wrapper (that manages binded per-frame resources)
2019-11-20 14:41:12 +03:00
Artem Bochkarev
4fd7c3c449 8234352: fixed memory leaks (for textures)
also remove unnecessary code that has no sense (given from initial GL impl)
2019-11-18 21:22:51 +03:00
Alexey Ushakov
2ec02181fa 8233879 : Improve performance of MTLVertexCache
Improved performance by avoiding creation of MTLBuffer objects for passing vertices
2019-11-09 18:58:48 +03:00
Artem Bochkarev
14e6b1244c 8233840: implemented MTLBlitLoops_CopyArea 2019-11-08 13:06:44 +03:00
Artem Bochkarev
24bff4e701 8233711: support alpha-composite modes (through multipliers of MTLRenderPipelineColorAttachmentDescriptor) 2019-11-08 12:07:14 +03:00
Artem Bochkarev
18b550e872 8233714: support 32bpp raster formats (for SwToTexture blit)
* use swizzle when available (otherwise perform raster conversion on cpu)
* supported flag SurfaceData.isOpaue
* fixed memory management for TexturesPool
2019-11-07 12:24:48 +03:00
Magnus Ihse Bursie
fab846163c 8233034: configure needs to provide path to Xcode metal build tools
Reviewed-by: prr
2019-11-04 12:14:45 -08:00
Phil Race
03aa3101a1 Backout 8233039 due to build breakage 2019-11-04 11:39:32 -08:00
Artem Bochkarev
c8b91c2d3f 8233039: support different raster formats (for SwToTexture blit)
* supported flag SurfaceData.isOpaue
* added implementation for missing composite rules (some combinations of parameters and extra-alpha can't be supported with CAD-multipliers, need to reimplement via shaders)
2019-11-01 17:25:07 +03:00
Artem Bochkarev
5b3c412357 8233039: minor code cleanup
minor simplification for MTLContext.createRenderEncoder, fixed logging utils
2019-11-01 17:23:42 +03:00
Kevin Rushforth
95b69fe529 8232918: Unguarded use of displaySyncEnabled causes build to fail
Wrap displaySyncEnabled in @available check to avoid warning
2019-10-23 12:04:35 -07:00
Jayathirth D V
535fff0dac Merge 2019-10-11 18:14:10 +05:30
Jayathirth D V
bbfac12c68 8232164: Lock renderqueue for least amount of time by making CAMetalLayer asynchronous
Reviewed-by: aghaisas
2019-10-11 15:20:54 +05:30
Alexey Ushakov
1768149951 8230958: AA Geometry rendering is not supported in metal
Fixed severe memory leaks that caused J2Demo hangs in AA mode
2019-10-03 00:38:17 +03:00
Ajit Ghaisas
49ca894f15 8231614: Avoid passing of redundant vertex 'z' data to vertex shader 2019-09-30 14:18:02 +05:30
Alexey Ushakov
0f76adc28d Fixed out of bounds access to transform matrix 2019-09-26 17:51:40 +03:00
Alexey Ushakov
cfc96d96f7 8230958: AA Geometry rendering is not supported in metal
Replaced mallocs with local arrays
2019-09-15 17:22:08 +03:00
Jayathirth D V
4921d927e0 Merge 2019-09-26 12:26:28 +05:30
Jayathirth D V
61aeca21c1 8231215: Add missed change for JDK-8231056
Reviewed-by: aghaisas
2019-09-19 11:30:46 +05:30
Jayathirth D V
fda678bef9 8231056: Refactor code to remove repetative logic in MTLLayer
Reviewed-by: aghaisas
2019-09-19 10:58:49 +05:30
Ajit Ghaisas
aba2c30d1e 8231178 : testImgBubbles of RenderPerfTest reports less FPS when run with Metal
Reviewed-by : jdv
2019-09-18 15:25:25 +05:30
Ajit Ghaisas
3ecd356574 8231175 : Fix J2DDemo sanity breakage caused due to initial commit of 8230958 2019-09-18 14:50:04 +05:30
Alexey Ushakov
762853fe8f 8230958: AA Geometry rendering is not supported in metal
Supported flat color fills in AA rendering mode
2019-09-13 21:17:07 +03:00
Alexey Ushakov
f879fa4e34 8230958: AA Geometry rendering is not supported in metal
Implemented MTLMaskFill_MaskFill operation
2019-09-13 15:38:18 +03:00
Ajit Ghaisas
6c8022b1ee 8230837: MTLRenderer_DrawParallelogram method - can be optimized by combining 4 drawPrimitives calls
Reviewed-by: jdv
2019-09-12 15:10:50 +05:30
Alexey Ushakov
d691007acc 8230859: fillOval with stroke width < 1 does not render
Implemented missing primitive
2019-09-11 17:47:02 +03:00
Jayathirth D V
9f250e53c8 8230849: Remove scheduleBlitAllModifiedLayers code after setNeedsDisplay
Reviewed-by: aghaisas
2019-09-11 17:37:53 +05:30
Alexey Ushakov
7cba2b956a 8230810: JFrame has incorrect insets with metal pipeline enabled
Adjusted metal rendering to use correct insets in JFrame
2019-09-10 18:21:35 +03:00
Jayathirth D V
c4ee724176 8230793: Remove CALayerDelegate usage for metal
Reviewed-by: aghaisas
2019-09-10 15:21:59 +05:30
Jayathirth D V
393e924d2d 8230789: Call setNeedsDisplay when we are drawing child layers recursively
Reviewed-by: aghaisas
2019-09-10 12:15:30 +05:30
Alexey Ushakov
fcba1675a7 8230657: Create fine grained render perf test for metal pipeline
Converted gradle JUnit test to plain java for ant and gnumake

To run the tests:
cd src/demo/share/java2d/RenderPerfTest

ant run
or
java -jar dist/RenderPerfTest.jar
or
java -jar dist/RenderPerfTest.jar testWhiteTextBubblesGray
2019-09-06 16:06:07 +03:00
Jayathirth D V
493e7d102a 8228576: Use setNeedsDisplay to start blitting in Appkit thread in Metal
Co-authored-by: Ajit Ghaisas <ajit.ghaisas@oracle.com>
Reviewed-by: aghaisas
2019-09-06 16:09:19 +05:30
Alexey Ushakov
54f1e4ec23 8230657: Create fine grained render perf test for metal pipeline
To run the tests:
cd src/demo/share/java2d/RenderPerfTest

sh gradlew test -i
or
sh gradlew test --tests *testWiredBoxBubbles* -i
2019-09-05 17:35:46 +03:00
Alexey Ushakov
a59e9ab2ac 8230647: Provide metal shaders library for all build targets 2019-09-05 13:15:38 +03:00
Ajit Ghaisas
6bd87cb96f 8230641: MTLRenderer_FillSpans method needs to be optimized by reducing total number of drawPrimitives calls
Reviewed-by: jdv
2019-09-05 15:28:54 +05:30
Jayathirth D V
7a448bc74d 8230287: Use single render encoder per destination for metal 2019-08-28 17:23:11 +05:30
Alexey Ushakov
b5cc29da48 Added support for Xcode 10 2019-08-23 22:51:17 +03:00
Jayathirth D V
d98d18b297 8230042: Fix metal shader generation path
Reviewed-by: aghaisas
2019-08-22 19:52:56 +05:30
Jayathirth D V
1441538b23 8229915: Migrate the metal changes from sandbox to lanai repository
Co-authored-by: Alexey Ushakov <alexey.ushakov@jetbrains.com>
Co-authored-by: Ajit Ghaisas <ajit.ghaisas@oracle.com>
Reviewed-by: aghaisas
2019-08-22 17:57:55 +05:30
113 changed files with 19036 additions and 74 deletions

View File

@@ -1,2 +1,36 @@
project=jdk
bugids=dup
;
; Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
; DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
;
; This code is free software; you can redistribute it and/or modify it
; under the terms of the GNU General Public License version 2 only, as
; published by the Free Software Foundation.
;
; This code is distributed in the hope that it will be useful, but WITHOUT
; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
; version 2 for more details (a copy is included in the LICENSE file that
; accompanied this code).
;
; You should have received a copy of the GNU General Public License version
; 2 along with this work; if not, write to the Free Software Foundation,
; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
;
; Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
; or visit www.oracle.com if you need additional information or have any
; questions.
;
[general]
project=lanai
jbs=JDK
[checks]
error=author,whitespace,executable
[census]
version=0
domain=openjdk.org
[checks "whitespace"]
files=.*\.java$|.*\.c$|.*\.h$|.*\.cpp$|.*\.hpp$

View File

@@ -0,0 +1,10 @@
# jetbrains/runtime:jbr15env
FROM centos:7
RUN yum -y install centos-release-scl
RUN yum -y install devtoolset-8
RUN yum -y install zip bzip2 unzip tar wget make autoconf automake libtool alsa-devel cups-devel xorg-x11-devel libjpeg62-devel giflib-devel freetype-devel file which libXtst-devel libXt-devel libXrender-devel alsa-lib-devel fontconfig-devel libXrandr-devel libXi-devel git
# Install Java 11
RUN wget https://download.java.net/java/GA/jdk13.0.1/cec27d702aa74d5a8630c65ae61e4305/9/GPL/openjdk-13.0.1_linux-x64_bin.tar.gz \
-O - | tar xz -C /
ENV JAVA_HOME /jbrsdk
ENV PATH $JAVA_HOME/bin:/opt/rh/devtoolset-8/root/usr/bin:$PATH

View File

@@ -0,0 +1,9 @@
#!/bin/bash -x
JBSDK_VERSION=$1
JDK_BUILD_NUMBER=$2
build_number=$3
script_dir=jb/project/tools/linux/scripts
${script_dir}/mkimages_x64.sh $JBSDK_VERSION $JDK_BUILD_NUMBER $build_number "jcef" || exit $?
${script_dir}/mkimages_x64.sh $JBSDK_VERSION $JDK_BUILD_NUMBER $build_number "jfx" || exit $?
${script_dir}/mkimages_x64.sh $JBSDK_VERSION $JDK_BUILD_NUMBER $build_number "jfx_jcef" || exit $?

View File

@@ -0,0 +1,77 @@
#!/bin/bash -x
# The following parameters must be specified:
# JBSDK_VERSION - specifies the current version of OpenJDK e.g. 11_0_6
# JDK_BUILD_NUMBER - specifies the number of OpenJDK build or the value of --with-version-build argument to configure
# build_number - specifies the number of JetBrainsRuntime build
# bundle_type - specifies bundle to bu built; possible values:
# jcef - the bundles 1) jbr with jcef+javafx, 2) jbrsdk and 3) test will be created
# jfx - the bundle 1) jbr with javafx only will be created
#
# jbrsdk-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
# jbr-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
#
# $ ./java --version
# openjdk 11.0.6 2020-01-14
# OpenJDK Runtime Environment (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number})
# OpenJDK 64-Bit Server VM (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number}, mixed mode)
#
JBSDK_VERSION=$1
JDK_BUILD_NUMBER=$2
build_number=$3
JBRSDK_BASE_NAME=jbrsdk-${JBSDK_VERSION}
sh configure \
--disable-warnings-as-errors \
--with-debug-level=release \
--with-version-build=$JDK_BUILD_NUMBER \
--with-version-pre= \
--with-version-opt=b$build_number \
--with-boot-jdk=amazon-corretto-11.0.5.10.1-linux-aarch64 \
--with-import-modules=./modular-sdk \
--enable-cds=yes || exit $?
make clean CONF=linux-aarch64-normal-server-release || exit $?
make images CONF=linux-aarch64-normal-server-release test-image || exit $?
JBSDK=${JBRSDK_BASE_NAME}-linux-aarch64-b${build_number}
BASE_DIR=build/linux-aarch64-normal-server-release/images
JSDK=${BASE_DIR}/jdk
JBRSDK_BUNDLE=jbrsdk
echo Fixing permissions
chmod -R a+r $JSDK
rm -rf $BASE_DIR/$JBRSDK_BUNDLE
cp -r $JSDK $BASE_DIR/$JBRSDK_BUNDLE || exit $?
echo Creating $JBSDK.tar.gz ...
tar -pcf $JBSDK.tar \
--exclude=*.debuginfo --exclude=demo --exclude=sample --exclude=man \
-C $BASE_DIR ${JBRSDK_BUNDLE} || exit $?
gzip $JBSDK.tar || exit $?
JBR_BUNDLE=jbr
JBR_BASE_NAME=jbr-$JBSDK_VERSION
rm -rf $BASE_DIR/$JBR_BUNDLE
JBR=$JBR_BASE_NAME-linux-aarch64-b$build_number
grep -v javafx modules.list | grep -v "jdk.internal.vm\|jdk.aot\|jcef" > modules.list.aarch64
echo Running jlink....
${JSDK}/bin/jlink \
--module-path ${JSDK}/jmods --no-man-pages --compress=2 \
--add-modules $(xargs < modules.list.aarch64 | sed s/" "//g | sed s/,$//g) \
--output ${BASE_DIR}/${JBR_BUNDLE} || exit $?
echo Modifying release info ...
grep -v \"^JAVA_VERSION\" ${JSDK}/release | grep -v \"^MODULES\" >> ${BASE_DIR}/${JBR_BUNDLE}/release
echo Creating $JBR.tar.gz ...
tar -pcf $JBR.tar -C $BASE_DIR ${JBR_BUNDLE} || exit $?
gzip $JBR.tar || exit $?
JBRSDK_TEST=$JBRSDK_BASE_NAME-linux-test-aarch64-b$build_number
echo Creating $JBRSDK_TEST.tar.gz ...
tar -pcf $JBRSDK_TEST.tar -C $BASE_DIR --exclude='test/jdk/demos' test || exit $?
gzip $JBRSDK_TEST.tar || exit $?

View File

@@ -0,0 +1,139 @@
#!/bin/bash -x
# The following parameters must be specified:
# JBSDK_VERSION - specifies the current version of OpenJDK e.g. 11_0_6
# JDK_BUILD_NUMBER - specifies the number of OpenJDK build or the value of --with-version-build argument to configure
# build_number - specifies the number of JetBrainsRuntime build
# bundle_type - specifies bundle to bu built; possible values:
# jcef - the bundles 1) jbr with jcef+javafx, 2) jbrsdk and 3) test will be created
# jfx - the bundle 1) jbr with javafx only will be created
#
# jbrsdk-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
# jbr-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
#
# $ ./java --version
# openjdk 11.0.6 2020-01-14
# OpenJDK Runtime Environment (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number})
# OpenJDK 64-Bit Server VM (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number}, mixed mode)
#
JBSDK_VERSION=$1
JDK_BUILD_NUMBER=$2
build_number=$3
bundle_type=$4
function create_jbr {
case "$1" in
"${bundle_type}_lw")
JBR_BASE_NAME=jbr_${bundle_type}_lw-${JBSDK_VERSION}
grep -v "jdk.compiler\|jdk.hotspot.agent" modules.list > modules_tmp.list
;;
"jfx" | "jcef")
JBR_BASE_NAME=jbr_${bundle_type}-${JBSDK_VERSION}
cat modules.list > modules_tmp.list
;;
"jfx_jcef")
JBR_BASE_NAME=jbr-${JBSDK_VERSION}
cat modules.list > modules_tmp.list
;;
*)
JBR_BASE_NAME=jbr-${JBSDK_VERSION}
cat modules.list > modules_tmp.list
;;
esac
rm -rf ${BASE_DIR}/${JBR_BUNDLE}
JBR=$JBR_BASE_NAME-linux-x64-b$build_number
echo Running jlink....
$JSDK/bin/jlink \
--module-path $JSDK/jmods --no-man-pages --compress=2 \
--add-modules $(xargs < modules_tmp.list | sed s/" "//g) --output $BASE_DIR/$JBR_BUNDLE
if [[ "$bundle_type" == *jcef* ]]; then
cp -R $BASE_DIR/$JBR_BUNDLE $BASE_DIR/jbr
cp -R jcef_linux_x64/* $BASE_DIR/$JBR_BUNDLE/lib || exit $?
fi
grep -v "^JAVA_VERSION" $JSDK/release | grep -v "^MODULES" >> $BASE_DIR/$JBR_BUNDLE/release
echo Creating $JBR.tar.gz ...
if [ ! -z "$bundle_type" ]; then
rm -rf ${BASE_DIR}/jbr
cp -R ${BASE_DIR}/${JBR_BUNDLE} ${BASE_DIR}/jbr
fi
tar -pcf $JBR.tar -C $BASE_DIR jbr || exit $?
gzip $JBR.tar || exit $?
rm -rf ${BASE_DIR}/${JBR_BUNDLE}
}
JBRSDK_BASE_NAME=jbrsdk-$JBSDK_VERSION
#git checkout -- modules.list src
case "$bundle_type" in
"jfx")
git apply -p0 < jb/project/tools/exclude_jcef_module.patch
;;
"jcef")
git apply -p0 < jb/project/tools/exclude_jfx_module.patch
;;
esac
if [ -z "$bundle_type" ]; then
JBR_BUNDLE=jbr
sh configure \
--disable-warnings-as-errors \
--with-debug-level=release \
--with-version-pre= \
--with-version-build=${JDK_BUILD_NUMBER} \
--with-version-opt=b${build_number} \
--with-boot-jdk=$BOOT_JDK \
--enable-cds=yes || exit $?
else
JBR_BUNDLE=jbr_${bundle_type}
sh configure \
--disable-warnings-as-errors \
--with-debug-level=release \
--with-version-pre= \
--with-version-build=${JDK_BUILD_NUMBER} \
--with-version-opt=b${build_number} \
--with-import-modules=./modular-sdk \
--with-boot-jdk=$BOOT_JDK \
--enable-cds=yes || exit $?
fi
make images CONF=linux-x86_64-server-release || exit $?
JSDK=build/linux-x86_64-server-release/images/jdk
JBSDK=$JBRSDK_BASE_NAME-linux-x64-b$build_number
echo Fixing permissions
chmod -R a+r $JSDK
BASE_DIR=build/linux-x86_64-server-release/images
JBRSDK_BUNDLE=jbrsdk
rm -rf $BASE_DIR/$JBRSDK_BUNDLE
cp -r $JSDK $BASE_DIR/$JBRSDK_BUNDLE || exit $?
if [[ "$bundle_type" == *jcef* ]]; then
cp -R jcef_linux_x64/* $BASE_DIR/$JBRSDK_BUNDLE/lib || exit $?
fi
if [[ "$bundle_type" == "jfx_jcef" || -z "$bundle_type" ]]; then
echo Creating $JBSDK.tar.gz ...
tar -pcf $JBSDK.tar --exclude=*.debuginfo --exclude=demo --exclude=sample --exclude=man \
-C $BASE_DIR $JBRSDK_BUNDLE || exit $?
gzip $JBSDK.tar || exit $?
fi
create_jbr ${bundle_type}
if [[ "$bundle_type" == "jfx_jcef" || -z "$bundle_type" ]]; then
make test-image || exit $?
JBRSDK_TEST=$JBRSDK_BASE_NAME-linux-test-x64-b$build_number
echo Creating $JBSDK_TEST.tar.gz ...
tar -pcf $JBRSDK_TEST.tar -C $BASE_DIR --exclude='test/jdk/demos' test || exit $?
gzip $JBRSDK_TEST.tar || exit $?
fi

View File

@@ -0,0 +1,72 @@
#!/bin/bash -x
# The following parameters must be specified:
# JBSDK_VERSION - specifies the current version of OpenJDK e.g. 11_0_6
# JDK_BUILD_NUMBER - specifies the number of OpenJDK build or the value of --with-version-build argument to configure
# build_number - specifies the number of JetBrainsRuntime build
# bundle_type - specifies bundle to bu built; possible values:
# jcef - the bundles 1) jbr with jcef+javafx, 2) jbrsdk and 3) test will be created
# jfx - the bundle 1) jbr with javafx only will be created
#
# jbrsdk-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
# jbr-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
#
# $ ./java --version
# openjdk 11.0.6 2020-01-14
# OpenJDK Runtime Environment (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number})
# OpenJDK 64-Bit Server VM (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number}, mixed mode)
#
JBSDK_VERSION=$1
JDK_BUILD_NUMBER=$2
build_number=$3
JBRSDK_BASE_NAME=jbrsdk-${JBSDK_VERSION}
sh configure \
--disable-warnings-as-errors \
--with-debug-level=fastdebug \
--with-version-build=$JDK_BUILD_NUMBER \
--with-version-pre= \
--with-version-opt=b$build_number \
--with-import-modules=./modular-sdk \
--enable-cds=yes || exit $?
make clean CONF=linux-x86_64-normal-server-fastdebug || exit $?
make images CONF=linux-x86_64-normal-server-fastdebug || exit $?
JBSDK=${JBRSDK_BASE_NAME}-linux-x64-fastdebug-b${build_number}
BASE_DIR=build/linux-x86_64-normal-server-fastdebug/images
JSDK=${BASE_DIR}/jdk
JBRSDK_BUNDLE=jbrsdk
echo Fixing permissions
chmod -R a+r $JSDK
rm -rf $BASE_DIR/$JBRSDK_BUNDLE
cp -r $JSDK $BASE_DIR/$JBRSDK_BUNDLE || exit $?
cp -R jcef_linux_x64/* $BASE_DIR/$JBRSDK_BUNDLE/lib || exit $?
echo Creating $JBSDK.tar.gz ...
tar -pcf $JBSDK.tar \
--exclude=*.debuginfo --exclude=demo --exclude=sample --exclude=man \
-C $BASE_DIR ${JBRSDK_BUNDLE} || exit $?
gzip $JBSDK.tar || exit $?
JBR_BUNDLE=jbr
JBR_BASE_NAME=jbr-$JBSDK_VERSION
rm -rf $BASE_DIR/$JBR_BUNDLE
JBR=$JBR_BASE_NAME-linux-x64-fastdebug-b$build_number
echo Running jlink....
${JSDK}/bin/jlink \
--module-path ${JSDK}/jmods --no-man-pages --compress=2 \
--add-modules $(xargs < modules.list | sed s/" "//g | sed s/,$//g) \
--output ${BASE_DIR}/${JBR_BUNDLE} || exit $?
cp -R jcef_linux_x64/* $BASE_DIR/$JBR_BUNDLE/lib || exit $?
echo Modifying release info ...
grep -v \"^JAVA_VERSION\" ${JSDK}/release | grep -v \"^MODULES\" >> ${BASE_DIR}/${JBR_BUNDLE}/release
echo Creating $JBR.tar.gz ...
tar -czf $JBR.tar -C $BASE_DIR ${JBR_BUNDLE} || exit $?
gzip $JBR.tar || exit $?

View File

@@ -0,0 +1,73 @@
#!/bin/bash -x
# The following parameters must be specified:
# JBSDK_VERSION - specifies the current version of OpenJDK e.g. 11_0_6
# JDK_BUILD_NUMBER - specifies the number of OpenJDK build or the value of --with-version-build argument to configure
# build_number - specifies the number of JetBrainsRuntime build
# bundle_type - specifies bundle to bu built; possible values:
# jcef - the bundles 1) jbr with jcef+javafx, 2) jbrsdk and 3) test will be created
# jfx - the bundle 1) jbr with javafx only will be created
#
# jbrsdk-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
# jbr-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
#
# $ ./java --version
# openjdk 11.0.6 2020-01-14
# OpenJDK Runtime Environment (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number})
# OpenJDK 64-Bit Server VM (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number}, mixed mode)
#
JBSDK_VERSION=$1
JDK_BUILD_NUMBER=$2
build_number=$3
JBRSDK_BASE_NAME=jbrsdk-${JBSDK_VERSION}
linux32 bash configure \
--disable-warnings-as-errors \
--with-debug-level=release \
--with-version-build=$JDK_BUILD_NUMBER \
--with-version-pre= \
--with-version-opt=b$build_number \
--with-boot-jdk=/jbrsdk-11.0.5-b1 \
--enable-cds=yes || exit $?
make clean CONF=linux-x86-normal-server-release || exit $?
make images CONF=linux-x86-normal-server-release test-image || exit $?
JBSDK=${JBRSDK_BASE_NAME}-linux-x86-b${build_number}
BASE_DIR=build/linux-x86-normal-server-release/images
JSDK=${BASE_DIR}/jdk
JBRSDK_BUNDLE=jbrsdk
echo Fixing permissions
chmod -R a+r $JSDK
rm -rf $BASE_DIR/$JBRSDK_BUNDLE
cp -r $JSDK $BASE_DIR/$JBRSDK_BUNDLE || exit $?
echo Creating $JBSDK.tar.gz ...
tar -pcf $JBSDK.tar --exclude=*.debuginfo --exclude=demo --exclude=sample --exclude=man -C $BASE_DIR ${JBRSDK_BUNDLE} || exit $?
gzip $JBSDK.tar || exit $?
JBR_BUNDLE=jbr
JBR_BASE_NAME=jbr-$JBSDK_VERSION
rm -rf $BASE_DIR/$JBR_BUNDLE
JBR=$JBR_BASE_NAME-linux-x86-b$build_number
grep -v javafx modules.list | grep -v "jdk.internal.vm\|jdk.aot\|jcef" > modules.list.x86
echo Running jlink....
${JSDK}/bin/jlink \
--module-path ${JSDK}/jmods --no-man-pages --compress=2 \
--add-modules $(xargs < modules.list.x86 | sed s/" "//g | sed s/,$//g) --output ${BASE_DIR}/${JBR_BUNDLE} || exit $?
echo Modifying release info ...
grep -v \"^JAVA_VERSION\" ${JSDK}/release | grep -v \"^MODULES\" >> ${BASE_DIR}/${JBR_BUNDLE}/release
echo Creating $JBR.tar.gz ...
tar -pcf $JBR.tar -C $BASE_DIR $JBR_BUNDLE || exit $?
gzip $JBR.tar || exit $?
JBRSDK_TEST=$JBRSDK_BASE_NAME-linux-test-x86-b$build_number
echo Creating $JBRSDK_TEST.tar.gz ...
tar -pcf $JBRSDK_TEST.tar -C $BASE_DIR --exclude='test/jdk/demos' --exclude='test/hotspot/gtest' test || exit $?
gzip $JBRSDK_TEST.tar || exit $?

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.disable-executable-page-protection</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,9 @@
#!/bin/bash -x
JBSDK_VERSION=$1
JDK_BUILD_NUMBER=$2
build_number=$3
script_dir=jb/project/tools/mac/scripts
${script_dir}/mkimages.sh $JBSDK_VERSION $JDK_BUILD_NUMBER $build_number "jcef" || exit $?
${script_dir}/mkimages.sh $JBSDK_VERSION $JDK_BUILD_NUMBER $build_number "jfx" || exit $?
${script_dir}/mkimages.sh $JBSDK_VERSION $JDK_BUILD_NUMBER $build_number "jfx_jcef" || exit $?

View File

@@ -0,0 +1,148 @@
#!/bin/bash -x
# The following parameters must be specified:
# JBSDK_VERSION - specifies the current version of OpenJDK e.g. 11_0_6
# JDK_BUILD_NUMBER - specifies the number of OpenJDK build or the value of --with-version-build argument to configure
# build_number - specifies the number of JetBrainsRuntime build
# bundle_type - specifies bundle to bu built; possible values:
# jcef - the bundles 1) jbr with jcef+javafx, 2) jbrsdk and 3) test will be created
# jfx - the bundle 1) jbr with javafx only will be created
#
# jbrsdk-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
# jbr-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
#
# $ ./java --version
# openjdk 11.0.6 2020-01-14
# OpenJDK Runtime Environment (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number})
# OpenJDK 64-Bit Server VM (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number}, mixed mode)
#
JBSDK_VERSION=$1
JDK_BUILD_NUMBER=$2
build_number=$3
bundle_type=$4
function create_jbr {
case "$1" in
"${bundle_type}_lw")
JBR_BASE_NAME=jbr_${bundle_type}_lw-${JBSDK_VERSION}
grep -v "jdk.compiler\|jdk.hotspot.agent" modules.list > modules_tmp.list
;;
"jfx" | "jcef")
JBR_BASE_NAME=jbr_${bundle_type}-${JBSDK_VERSION}
cat modules.list > modules_tmp.list
;;
"jfx_jcef")
JBR_BASE_NAME=jbr-${JBSDK_VERSION}
cat modules.list > modules_tmp.list
;;
*)
JBR_BASE_NAME=jbr-${JBSDK_VERSION}
cat modules.list > modules_tmp.list
;;
esac
rm -rf ${BASE_DIR}/${JBR_BUNDLE}
JRE_CONTENTS=${BASE_DIR}/${JBR_BUNDLE}/Contents
JRE_HOME=${JRE_CONTENTS}/Home
if [ -d "${JRE_CONTENTS}" ]; then
rm -rf ${JRE_CONTENTS}
fi
mkdir -p ${JRE_CONTENTS}
JBR=${JBR_BASE_NAME}-osx-x64-b${build_number}
${BASE_DIR}/$JBRSDK_BUNDLE/Contents/Home/bin/jlink \
--module-path ${BASE_DIR}/${JBRSDK_BUNDLE}/Contents/Home/jmods --no-man-pages --compress=2 \
--add-modules $(xargs < modules_tmp.list | sed s/" "//g) --output ${JRE_HOME} || exit $?
grep -v "^JAVA_VERSION" ${BASE_DIR}/${JBRSDK_BUNDLE}/Contents/Home/release | grep -v "^MODULES" >> ${JRE_HOME}/release
cp -R ${BASE_DIR}/${JBRSDK_BUNDLE}/Contents/MacOS ${JRE_CONTENTS}
cp ${BASE_DIR}/${JBRSDK_BUNDLE}/Contents/Info.plist ${JRE_CONTENTS}
if [[ "${bundle_type}" == *jcef* ]]; then
rm -rf ${JRE_CONTENTS}/Frameworks || exit $?
rm -rf ${JRE_CONTENTS}/Helpers || exit $?
cp -a jcef_mac/Frameworks ${JRE_CONTENTS} || exit $?
cp -a jcef_mac/Helpers ${JRE_CONTENTS} || exit $?
fi
echo Creating ${JBR}.tar.gz ...
if [ ! -z "$bundle_type" ]; then
rm -rf ${BASE_DIR}/jbr
cp -R ${BASE_DIR}/${JBR_BUNDLE} ${BASE_DIR}/jbr
fi
COPYFILE_DISABLE=1 tar -pczf ${JBR}.tar.gz --exclude='*.dSYM' --exclude='man' -C ${BASE_DIR} jbr || exit $?
rm -rf ${BASE_DIR}/${JBR_BUNDLE}
}
JBRSDK_BASE_NAME=jbrsdk-${JBSDK_VERSION}
#git checkout -- modules.list src
case "$bundle_type" in
"jfx")
git apply -p0 < jb/project/tools/exclude_jcef_module.patch
;;
"jcef")
git apply -p0 < jb/project/tools/exclude_jfx_module.patch
;;
esac
if [ -z "$bundle_type" ]; then
JBR_BUNDLE=jbr
sh configure \
--disable-warnings-as-errors \
--with-debug-level=release \
--with-version-pre= \
--with-version-build=${JDK_BUILD_NUMBER} \
--with-version-opt=b${build_number} \
--with-boot-jdk=`/usr/libexec/java_home -v $BOOT_JDK` \
--enable-cds=yes || exit $?
else
JBR_BUNDLE=jbr_${bundle_type}
sh configure \
--disable-warnings-as-errors \
--with-debug-level=release \
--with-version-pre= \
--with-version-build=${JDK_BUILD_NUMBER} \
--with-version-opt=b${build_number} \
--with-import-modules=./modular-sdk \
--with-boot-jdk=`/usr/libexec/java_home -v $BOOT_JDK` \
--enable-cds=yes || exit $?
fi
make images CONF=macosx-x86_64-server-release || exit $?
JSDK=build/macosx-x86_64-server-release/images/jdk-bundle
JBSDK=${JBRSDK_BASE_NAME}-osx-x64-b${build_number}
BASE_DIR=jre
JBRSDK_BUNDLE=jbrsdk
rm -rf $BASE_DIR
mkdir $BASE_DIR || exit $?
JBSDK_VERSION_WITH_DOTS=$(echo $JBSDK_VERSION | sed 's/_/\./g')
cp -a $JSDK/jdk-$JBSDK_VERSION_WITH_DOTS.jdk $BASE_DIR/$JBRSDK_BUNDLE || exit $?
if [[ "$bundle_type" == *jcef* ]]; then
cp -a jcef_mac/Frameworks $BASE_DIR/$JBRSDK_BUNDLE/Contents/ || exit $?
cp -a jcef_mac/Helpers $BASE_DIR/$JBRSDK_BUNDLE/Contents/ || exit $?
fi
if [[ "$bundle_type" == "jfx_jcef" || -z "$bundle_type" ]]; then
echo Creating $JBSDK.tar.gz ...
COPYFILE_DISABLE=1 tar -pczf $JBSDK.tar.gz -C $BASE_DIR \
--exclude='._*' --exclude='.DS_Store' --exclude='*~' \
--exclude='Home/demo' --exclude='Home/man' --exclude='Home/sample' \
$JBRSDK_BUNDLE || exit $?
fi
create_jbr "${bundle_type}" || exit $?
if [[ "$bundle_type" == "jfx_jcef" || -z "$bundle_type" ]]; then
make test-image || exit $?
JBRSDK_TEST=$JBRSDK_BASE_NAME-osx-test-x64-b$build_number
echo Creating $JBRSDK_TEST.tar.gz ...
COPYFILE_DISABLE=1 tar -pczf $JBRSDK_TEST.tar.gz -C build/macosx-x86_64-server-release/images \
--exclude='test/jdk/demos' test || exit $?
fi

View File

@@ -0,0 +1,83 @@
#!/bin/bash -x
# The following parameters must be specified:
# JBSDK_VERSION - specifies the current version of OpenJDK e.g. 11_0_6
# JDK_BUILD_NUMBER - specifies the number of OpenJDK build or the value of --with-version-build argument to configure
# build_number - specifies the number of JetBrainsRuntime build
# bundle_type - specifies bundle to bu built; possible values:
# jcef - the bundles 1) jbr with jcef+javafx, 2) jbrsdk and 3) test will be created
# jfx - the bundle 1) jbr with javafx only will be created
#
# jbrsdk-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
# jbr-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
#
# $ ./java --version
# openjdk 11.0.6 2020-01-14
# OpenJDK Runtime Environment (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number})
# OpenJDK 64-Bit Server VM (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number}, mixed mode)
#
JBSDK_VERSION=$1
JDK_BUILD_NUMBER=$2
build_number=$3
JBRSDK_BASE_NAME=jbrsdk-${JBSDK_VERSION}
sh configure \
--disable-warnings-as-errors \
--with-debug-level=fastdebug \
--with-version-build=$JDK_BUILD_NUMBER \
--with-version-pre= \
--with-version-opt=b$build_number \
--with-import-modules=./modular-sdk \
--with-boot-jdk=`/usr/libexec/java_home -v 11` \
--enable-cds=yes || exit $?
make clean CONF=macosx-x86_64-normal-server-fastdebug || exit $?
make images CONF=macosx-x86_64-normal-server-fastdebug || exit $?
JSDK=build/macosx-x86_64-normal-server-fastdebug/images/jdk-bundle
JBSDK=${JBRSDK_BASE_NAME}-osx-x64-fastdebug-b${build_number}
BASE_DIR=jre
JBRSDK_BUNDLE=jbrsdk
rm -rf $BASE_DIR
mkdir $BASE_DIR || exit $?
JBSDK_VERSION_WITH_DOTS=$(echo $JBSDK_VERSION | sed 's/_/\./g')
cp -a $JSDK/jdk-$JBSDK_VERSION_WITH_DOTS.jdk $BASE_DIR/$JBRSDK_BUNDLE || exit $?
echo Creating $JBSDK.tar.gz ...
cp -a jcef_mac/Frameworks $BASE_DIR/$JBRSDK_BUNDLE/Contents/
cp -a jcef_mac/Helpers $BASE_DIR/$JBRSDK_BUNDLE/Contents
COPYFILE_DISABLE=1 \
tar -pczf ${JBSDK}.tar.gz -C ${BASE_DIR} \
--exclude='._*' --exclude='.DS_Store' --exclude='*~' \
--exclude='Home/demo' --exclude='Home/man' --exclude='Home/sample' \
${JBRSDK_BUNDLE} || exit $?
JBR_BUNDLE=jbr
JRE_CONTENTS=$BASE_DIR/$JBR_BUNDLE/Contents
JRE_HOME=$JRE_CONTENTS/Home
JBR_BASE_NAME=jbr-$JBSDK_VERSION
mkdir -p $JRE_CONTENTS
if [ -d "$JRE_HOME" ]; then
rm -rf $JRE_HOME
fi
JBR=${JBR_BASE_NAME}-osx-x64-fastdebug-b${build_number}
$BASE_DIR/$JBRSDK_BUNDLE/Contents/Home/bin/jlink \
--module-path $BASE_DIR/$JBRSDK_BUNDLE/Contents/Home/jmods --no-man-pages --compress=2 \
--add-modules $(xargs < modules.list | sed s/" "//g) --output $JRE_HOME || exit $?
grep -v "^JAVA_VERSION" $BASE_DIR/$JBRSDK_BUNDLE/Contents/Home/release | grep -v "^MODULES" >> $JRE_HOME/release
cp -R $BASE_DIR/$JBRSDK_BUNDLE/Contents/MacOS $JRE_CONTENTS
cp $BASE_DIR/$JBRSDK_BUNDLE/Contents/Info.plist $JRE_CONTENTS
cp -a jcef_mac/Frameworks ${JRE_CONTENTS} || exit $?
cp -a jcef_mac/Helpers ${JRE_CONTENTS} || exit $?
echo Creating $JBR.tar.gz ...
COPYFILE_DISABLE=1 tar -pczf $JBR.tar.gz --exclude='*.dSYM' --exclude='man' -C $BASE_DIR $JBR_BUNDLE || exit $?

View File

@@ -0,0 +1,120 @@
#!/bin/bash
APP_DIRECTORY=$1
APPL_USER=$2
APPL_PASSWORD=$3
APP_NAME=$4
BUNDLE_ID=$5
FAKE_ROOT="${6:-fake-root}"
if [[ -z "$APP_DIRECTORY" ]] || [[ -z "$APPL_USER" ]] || [[ -z "$APPL_PASSWORD" ]]; then
echo "Usage: $0 AppDirectory Username Password"
exit 1
fi
if [[ ! -d "$APP_DIRECTORY" ]]; then
echo "AppDirectory '$APP_DIRECTORY' does not exist or not a directory"
exit 1
fi
function log() {
echo "$(date '+[%H:%M:%S]') $*"
}
function publish-log() {
id=$1
file=$2
curl -T "$file" "$ARTIFACTORY_URL/$id" || true
}
function altool-upload() {
# Since altool uses same file for upload token we have to trick it into using different folders for token file location
# Also it copies zip into TMPDIR so we override it too, to simplify cleanup
OLD_HOME="$HOME"
export HOME="$FAKE_ROOT/home"
export TMPDIR="$FAKE_ROOT/tmp"
mkdir -p "$HOME"
mkdir -p "$TMPDIR"
export _JAVA_OPTIONS="-Duser.home=$HOME -Djava.io.tmpdir=$TMPDIR"
# Reduce amount of downloads, cache transporter libraries
shared_itmstransporter="$OLD_HOME/shared-itmstransporter"
if [[ -f "$shared_itmstransporter" ]]; then
cp -r "$shared_itmstransporter" "$HOME/.itmstransporter"
fi
# For some reason altool prints everything to stderr, not stdout
set +e
xcrun altool --notarize-app \
--username "$APPL_USER" --password "$APPL_PASSWORD" \
--primary-bundle-id "$BUNDLE_ID" \
--asc-provider JetBrainssro --file "$1" 2>&1 | tee "altool.init.out"
unset TMPDIR
export HOME="$OLD_HOME"
set -e
}
#immediately exit script with an error if a command fails
set -euo pipefail
file="$APP_NAME.zip"
log "Zipping $file..."
rm -rf "$file"
ditto -c -k --sequesterRsrc --keepParent "$APP_DIRECTORY/Contents" "$file"
log "Notarizing $file..."
rm -rf "altool.init.out" "altool.check.out"
altool-upload "$file"
rm -rf "$file"
notarization_info="$(grep -e "RequestUUID" "altool.init.out" | grep -oE '([0-9a-f-]{36})')"
if [ -z "$notarization_info" ]; then
log "Faile to read RequestUUID from altool.init.out"
exit 10
fi
PATH="$PATH:/usr/local/bin/"
log "Notarization request sent, awaiting response"
spent=0
while true; do
# For some reason altool prints everything to stderr, not stdout
xcrun altool --username "$APPL_USER" --notarization-info "$notarization_info" --password "$APPL_PASSWORD" >"altool.check.out" 2>&1 || true
status="$(grep -oe 'Status: .*' "altool.check.out" | cut -c 9- || true)"
log "Current status: $status"
if [ "$status" = "invalid" ]; then
log "Notarization failed"
ec=1
elif [ "$status" = "success" ]; then
log "Notarization succeeded"
ec=0
else
if [ "$status" != "in progress" ]; then
log "Unknown notarization status, waiting more, altool output:"
cat "altool.check.out"
fi
if [[ $spent -gt 60 ]]; then
log "Waiting time out (apx 60 minutes)"
ec=2
break
fi
sleep 60
((spent += 1))
continue
fi
developer_log="developer_log.json"
log "Fetching $developer_log"
# TODO: Replace cut with trim or something better
url="$(grep -oe 'LogFileURL: .*' "altool.check.out" | cut -c 13-)"
wget "$url" -O "$developer_log" && cat "$developer_log" || true
if [ $ec != 0 ]; then
log "Publishing $developer_log"
publish-log "$notarization_info" "$developer_log"
fi
break
done
cat "altool.check.out"
rm -rf "altool.init.out" "altool.check.out"
exit $ec

View File

@@ -0,0 +1,93 @@
#!/bin/bash
APP_DIRECTORY=$1
JB_CERT=$2
if [[ -z "$APP_DIRECTORY" ]] || [[ -z "$JB_CERT" ]]; then
echo "Usage: $0 AppDirectory CertificateID"
exit 1
fi
if [[ ! -d "$APP_DIRECTORY" ]]; then
echo "AppDirectory '$APP_DIRECTORY' does not exist or not a directory"
exit 1
fi
function log() {
echo "$(date '+[%H:%M:%S]') $*"
}
#immediately exit script with an error if a command fails
set -euo pipefail
# Cleanup files left from previous sign attempt (if any)
find "$APP_DIRECTORY" -name '*.cstemp' -exec rm '{}' \;
log "Signing libraries and executables..."
# -perm +111 searches for executables
for f in \
"Contents/Home/bin" \
"Contents/Home/lib"; do
if [ -d "$APP_DIRECTORY/$f" ]; then
find "$APP_DIRECTORY/$f" \
-type f \( -name "*.jnilib" -o -name "*.dylib" -o -name "*.so" -o -perm +111 \) \
-exec codesign --timestamp \
-v -s "$JB_CERT" --options=runtime \
--entitlements entitlements.xml {} \;
fi
done
log "Signing libraries in jars in $PWD"
# todo: add set -euo pipefail; into the inner sh -c
# `-e` prevents `grep -q && printf` loginc
# with `-o pipefail` there's no input for 'while' loop
find "$APP_DIRECTORY" -name '*.jar' \
-exec sh -c "set -u; unzip -l \"\$0\" | grep -q -e '\.dylib\$' -e '\.jnilib\$' -e '\.so\$' -e '^jattach\$' && printf \"\$0\0\" " {} \; |
while IFS= read -r -d $'\0' file; do
log "Processing libraries in $file"
rm -rf jarfolder jar.jar
mkdir jarfolder
filename="${file##*/}"
log "Filename: $filename"
cp "$file" jarfolder && (cd jarfolder && jar xf "$filename" && rm "$filename")
find jarfolder \
-type f \( -name "*.jnilib" -o -name "*.dylib" -o -name "*.so" -o -name "jattach" \) \
-exec codesign --timestamp \
-v -s "$JB_CERT" --options=runtime \
--entitlements entitlements.xml {} \;
(cd jarfolder; zip -q -r -o ../jar.jar .)
mv jar.jar "$file"
done
rm -rf jarfolder jar.jar
log "Signing other files..."
for f in \
"Contents/MacOS"; do
if [ -d "$APP_DIRECTORY/$f" ]; then
find "$APP_DIRECTORY/$f" \
-type f \( -name "*.jnilib" -o -name "*.dylib" -o -name "*.so" -o -perm +111 \) \
-exec codesign --timestamp \
-v -s "$JB_CERT" --options=runtime \
--entitlements entitlements.xml {} \;
fi
done
#log "Signing executable..."
#codesign --timestamp \
# -v -s "$JB_CERT" --options=runtime \
# --force \
# --entitlements entitlements.xml "$APP_DIRECTORY/Contents/MacOS/idea"
log "Signing whole app..."
codesign --timestamp \
-v -s "$JB_CERT" --options=runtime \
--force \
--entitlements entitlements.xml "$APP_DIRECTORY"
log "Verifying java is not broken"
find "$APP_DIRECTORY" \
-type f -name 'java' -perm +111 -exec {} -version \;

View File

@@ -0,0 +1,145 @@
#!/bin/bash
#immediately exit script with an error if a command fails
set -euo pipefail
export COPY_EXTENDED_ATTRIBUTES_DISABLE=true
export COPYFILE_DISABLE=true
INPUT_FILE=$1
EXPLODED=$2.exploded
BACKUP_JMODS=$2.backup
USERNAME=$3
PASSWORD=$4
CODESIGN_STRING=$5
NOTARIZE=$6
BUNDLE_ID=$7
cd "$(dirname "$0")"
function log() {
echo "$(date '+[%H:%M:%S]') $*"
}
log "Deleting $EXPLODED ..."
if test -d "$EXPLODED"; then
find "$EXPLODED" -mindepth 1 -maxdepth 1 -exec chmod -R u+wx '{}' \;
fi
rm -rf "$EXPLODED"
mkdir "$EXPLODED"
rm -rf "$BACKUP_JMODS"
mkdir "$BACKUP_JMODS"
log "Unzipping $INPUT_FILE to $EXPLODED ..."
tar -xzvf "$INPUT_FILE" --directory $EXPLODED
rm "$INPUT_FILE"
BUILD_NAME="$(ls "$EXPLODED")"
if test -d $EXPLODED/$BUILD_NAME/Contents/Home/jmods; then
mv $EXPLODED/$BUILD_NAME/Contents/Home/jmods $BACKUP_JMODS
fi
if test -d $EXPLODED/$BUILD_NAME/Contents/Home/Frameworks; then
mv $EXPLODED/$BUILD_NAME/Contents/Home/Frameworks $BACKUP_JMODS
fi
if test -f $EXPLODED/$BUILD_NAME/Contents/MacOS/libjli.dylib; then
mv $EXPLODED/$BUILD_NAME/Contents/MacOS/libjli.dylib $BACKUP_JMODS
fi
#log "$INPUT_FILE unzipped and removed"
log "$INPUT_FILE extracted and removed"
APPLICATION_PATH="$EXPLODED/$BUILD_NAME"
find "$APPLICATION_PATH/Contents/Home/bin" \
-maxdepth 1 -type f -name '*.jnilib' -print0 |
while IFS= read -r -d $'\0' file; do
if [ -f "$file" ]; then
log "Linking $file"
b="$(basename "$file" .jnilib)"
ln -sf "$b.jnilib" "$(dirname "$file")/$b.dylib"
fi
done
find "$APPLICATION_PATH/Contents/" \
-maxdepth 1 -type f -name '*.txt' -print0 |
while IFS= read -r -d $'\0' file; do
if [ -f "$file" ]; then
log "Moving $file"
mv "$file" "$APPLICATION_PATH/Contents/Resources"
fi
done
non_plist=$(find "$APPLICATION_PATH/Contents/" -maxdepth 1 -type f -and -not -name 'Info.plist' | wc -l)
if [[ $non_plist -gt 0 ]]; then
log "Only Info.plist file is allowed in Contents directory but found $non_plist file(s):"
log "$(find "$APPLICATION_PATH/Contents/" -maxdepth 1 -type f -and -not -name 'Info.plist')"
exit 1
fi
log "Unlocking keychain..."
# Make sure *.p12 is imported into local KeyChain
security unlock-keychain -p "$PASSWORD" "/Users/$USERNAME/Library/Keychains/login.keychain"
attempt=1
limit=3
set +e
while [[ $attempt -le $limit ]]; do
log "Signing (attempt $attempt) $APPLICATION_PATH ..."
./sign.sh "$APPLICATION_PATH" "$CODESIGN_STRING"
ec=$?
if [[ $ec -ne 0 ]]; then
((attempt += 1))
if [ $attempt -eq $limit ]; then
set -e
fi
log "Signing failed, wait for 30 sec and try to sign again"
sleep 30
else
log "Signing done"
codesign -v "$APPLICATION_PATH" -vvvvv
log "Check sign done"
((attempt += limit))
fi
done
set -e
if [ "$NOTARIZE" = "yes" ]; then
log "Notarizing..."
# shellcheck disable=SC1090
source "$HOME/.notarize_token"
APP_NAME=$(echo ${INPUT_FILE} | awk -F"." '{ print $1 }')
# Since notarization tool uses same file for upload token we have to trick it into using different folders, hence fake root
# Also it leaves copy of zip file in TMPDIR, so notarize.sh overrides it and uses FAKE_ROOT as location for temp TMPDIR
FAKE_ROOT="$(pwd)/fake-root"
mkdir -p "$FAKE_ROOT"
echo "Notarization will use fake root: $FAKE_ROOT"
./notarize.sh "$APPLICATION_PATH" "$APPLE_USERNAME" "$APPLE_PASSWORD" "$APP_NAME" "$BUNDLE_ID" "$FAKE_ROOT"
rm -rf "$FAKE_ROOT"
set +e
log "Stapling..."
xcrun stapler staple "$APPLICATION_PATH"
else
log "Notarization disabled"
log "Stapling disabled"
fi
log "Zipping $BUILD_NAME to $INPUT_FILE ..."
(
#cd "$EXPLODED"
#ditto -c -k --sequesterRsrc --keepParent "$BUILD_NAME" "../$INPUT_FILE"
if test ! -z $(ls $BACKUP_JMODS/libjli.dylib); then
mv $BACKUP_JMODS/libjli.dylib $EXPLODED/$BUILD_NAME/Contents/MacOS
fi
if test -d $BACKUP_JMODS/jmods; then
mv $BACKUP_JMODS/jmods $EXPLODED/$BUILD_NAME/Contents/Home
fi
if test -d $BACKUP_JMODS/Frameworks; then
mv $BACKUP_JMODS/Frameworks $EXPLODED/$BUILD_NAME/Contents/Home
fi
COPYFILE_DISABLE=1 tar -pczf $INPUT_FILE --exclude='*.dSYM' --exclude='man' -C $EXPLODED $BUILD_NAME
log "Finished zipping"
)
rm -rf "$EXPLODED"
log "Done"

View File

@@ -0,0 +1,9 @@
#!/bin/bash -x
JBSDK_VERSION=$1
JDK_BUILD_NUMBER=$2
build_number=$3
script_dir=jb/project/tools/windows/scripts
${script_dir}/mkimages_x64.sh $JBSDK_VERSION $JDK_BUILD_NUMBER $build_number "jcef" || exit $?
${script_dir}/mkimages_x64.sh $JBSDK_VERSION $JDK_BUILD_NUMBER $build_number "jfx" || exit $?
${script_dir}/mkimages_x64.sh $JBSDK_VERSION $JDK_BUILD_NUMBER $build_number "jfx_jcef" || exit $?

View File

@@ -0,0 +1,119 @@
#!/bin/bash -x
# The following parameters must be specified:
# JBSDK_VERSION - specifies the current version of OpenJDK e.g. 11_0_6
# JDK_BUILD_NUMBER - specifies the number of OpenJDK build or the value of --with-version-build argument to configure
# build_number - specifies the number of JetBrainsRuntime build
# bundle_type - specifies bundle to bu built; possible values:
# jcef - the bundles 1) jbr with jcef+javafx, 2) jbrsdk and 3) test will be created
# jfx - the bundle 1) jbr with javafx only will be created
#
# jbrsdk-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
# jbr-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
#
# $ ./java --version
# openjdk 11.0.6 2020-01-14
# OpenJDK Runtime Environment (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number})
# OpenJDK 64-Bit Server VM (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number}, mixed mode)
#
JBSDK_VERSION=$1
JDK_BUILD_NUMBER=$2
build_number=$3
bundle_type=$4
function create_jbr {
case "$1" in
"${bundle_type}_lw")
grep -v "jdk.compiler\|jdk.hotspot.agent" modules.list > modules_tmp.list
;;
"jfx" | "jcef" | "jfx_jcef")
cat modules.list > modules_tmp.list
;;
*)
cat modules.list > modules_tmp.list
;;
esac
rm -rf ${JBR_BUNDLE}
${JSDK}/bin/jlink \
--module-path ${JSDK}/jmods --no-man-pages --compress=2 \
--add-modules $(xargs < modules_tmp.list | sed s/" "//g) --output ${JBR_BUNDLE} || exit $?
if [[ "${bundle_type}" == *jcef* ]]
then
cp -R jcef_win_x64/* ${JBR_BUNDLE}/bin
fi
echo Modifying release info ...
grep -v \"^JAVA_VERSION\" ${JSDK}/release | grep -v \"^MODULES\" >> ${JBR_BUNDLE}/release
}
JBRSDK_BASE_NAME=jbrsdk-${JBSDK_VERSION}
WORK_DIR=$(pwd)
#git checkout -- modules.list src
case "$bundle_type" in
"jfx")
echo "Excluding jcef modules"
git apply -p0 < jb/project/tools/exclude_jcef_module.patch
;;
"jcef")
echo "Excluding jfx modules"
git apply -p0 < jb/project/tools/exclude_jfx_module.patch
;;
esac
PATH="/usr/local/bin:/usr/bin:${PATH}"
if [ -z "$bundle_type" ]; then
bash ./configure \
--disable-warnings-as-errors \
--with-target-bits=64 \
--with-version-pre= \
--with-version-build=${JDK_BUILD_NUMBER} \
--with-version-opt=b${build_number} \
--with-toolchain-version=${TOOLCHAIN_VERSION} \
--with-boot-jdk=${BOOT_JDK} \
--disable-ccache \
--enable-cds=yes || exit 1
else
bash ./configure \
--disable-warnings-as-errors \
--with-target-bits=64 \
--with-version-pre= \
--with-version-build=${JDK_BUILD_NUMBER} \
--with-version-opt=b${build_number} \
--with-import-modules=${WORK_DIR}/modular-sdk \
--with-toolchain-version=${TOOLCHAIN_VERSION} \
--with-boot-jdk=${BOOT_JDK} \
--disable-ccache \
--enable-cds=yes || exit 1
fi
if [[ "$bundle_type" == "jfx_jcef" || -z "$bundle_type" ]]; then
make LOG=info images CONF=windows-x86_64-server-release test-image || exit 1
else
make LOG=info images CONF=windows-x86_64-server-release || exit 1
fi
JSDK=build/windows-x86_64-server-release/images/jdk
if [[ "$bundle_type" == "*jcef*" || -z "$bundle_type" ]]; then
JBSDK=${JBRSDK_BASE_NAME}-windows-x64-b${build_number}
fi
BASE_DIR=build/windows-x86_64-server-release/images
JBRSDK_BUNDLE=jbrsdk
rm -rf ${BASE_DIR}/${JBRSDK_BUNDLE} && rsync -a --exclude demo --exclude sample ${JSDK}/ ${JBRSDK_BUNDLE} || exit 1
if [[ "$bundle_type" == "*jcef*" ]]; then
cp -R jcef_win_x64/* ${JBRSDK_BUNDLE}/bin
fi
if [ -z "$bundle_type" ]; then
JBR_BUNDLE=jbr
else
JBR_BUNDLE=jbr_${bundle_type}
fi
create_jbr ${bundle_type}
#JBR_BUNDLE=jbr_${bundle_type}_lw
#create_jbr ${bundle_type}_lw

View File

@@ -0,0 +1,57 @@
#!/bin/bash -x
# The following parameters must be specified:
# JBSDK_VERSION - specifies the current version of OpenJDK e.g. 11_0_6
# JDK_BUILD_NUMBER - specifies the number of OpenJDK build or the value of --with-version-build argument to configure
# build_number - specifies the number of JetBrainsRuntime build
# bundle_type - specifies bundle to bu built; possible values:
# jcef - the bundles 1) jbr with jcef+javafx, 2) jbrsdk and 3) test will be created
# jfx - the bundle 1) jbr with javafx only will be created
#
# jbrsdk-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
# jbr-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
#
# $ ./java --version
# openjdk 11.0.6 2020-01-14
# OpenJDK Runtime Environment (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number})
# OpenJDK 64-Bit Server VM (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number}, mixed mode)
#
JBSDK_VERSION=$1
JDK_BUILD_NUMBER=$2
build_number=$3
JBRSDK_BASE_NAME=jbrsdk-${JBSDK_VERSION}
WORK_DIR=$(pwd)
PATH="/usr/local/bin:/usr/bin:${PATH}"
./configure \
--disable-warnings-as-errors \
--disable-debug-symbols \
--with-target-bits=32 \
--with-version-pre= \
--with-version-build=${JDK_BUILD_NUMBER} \
--with-version-opt=b${build_number} \
--with-toolchain-version=2015 \
--with-boot-jdk=${BOOT_JDK} \
--disable-ccache \
--enable-cds=yes || exit 1
make clean CONF=windows-x86-normal-server-release || exit 1
make LOG=info images CONF=windows-x86-normal-server-release test-image || exit 1
JBSDK=${JBRSDK_BASE_NAME}-windows-x86-b${build_number}
BASE_DIR=build/windows-x86-normal-server-release/images
JSDK=${BASE_DIR}/jdk
JBRSDK_BUNDLE=jbrsdk
rm -rf ${BASE_DIR}/${JBRSDK_BUNDLE} && rsync -a --exclude demo --exclude sample ${JSDK}/ ${JBRSDK_BUNDLE} || exit 1
JBR_BUNDLE=jbr
rm -rf ${JBR_BUNDLE}
grep -v javafx modules.list | grep -v "jdk.internal.vm\|jdk.aot\|jcef" > modules.list.x86
${JSDK}/bin/jlink \
--module-path ${JSDK}/jmods --no-man-pages --compress=2 \
--add-modules $(xargs < modules.list.x86 | sed s/" "//g) --output ${JBR_BUNDLE} || exit $?
echo Modifying release info ...
grep -v \"^JAVA_VERSION\" ${JSDK}/release | grep -v \"^MODULES\" >> ${JBR_BUNDLE}/release

View File

@@ -0,0 +1,79 @@
#!/bin/bash -x
# The following parameters must be specified:
# JBSDK_VERSION - specifies the current version of OpenJDK e.g. 11_0_6
# JDK_BUILD_NUMBER - specifies the number of OpenJDK build or the value of --with-version-build argument to configure
# build_number - specifies the number of JetBrainsRuntime build
# bundle_type - specifies bundle to bu built; possible values:
# jcef - the bundles 1) jbr with jcef+javafx, 2) jbrsdk and 3) test will be created
# jfx - the bundle 1) jbr with javafx only will be created
#
# jbrsdk-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
# jbr-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
#
# $ ./java --version
# openjdk 11.0.6 2020-01-14
# OpenJDK Runtime Environment (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number})
# OpenJDK 64-Bit Server VM (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number}, mixed mode)
#
JBSDK_VERSION=$1
JDK_BUILD_NUMBER=$2
build_number=$3
bundle_type=$4
function pack_jbr {
case "$1" in
"${bundle_type}_lw")
JBR_BASE_NAME=jbr_${bundle_type}_lw-${JBSDK_VERSION}
;;
"jfx" | "jcef")
JBR_BASE_NAME=jbr_${bundle_type}-${JBSDK_VERSION}
;;
"jfx_jcef" | "")
JBR_BASE_NAME=jbr-${JBSDK_VERSION}
;;
*)
echo "***ERR*** bundle was not specified" && exit 1
;;
esac
JBR=$JBR_BASE_NAME-windows-x64-b$build_number
echo Creating $JBR.tar.gz ...
if [ ! -z "$bundle_type" ]; then
rm -rf ${BASE_DIR}/jbr
cp -R ${BASE_DIR}/${JBR_BUNDLE} ${BASE_DIR}/jbr
fi
/usr/bin/tar -czf $JBR.tar.gz -C $BASE_DIR jbr || exit 1
#rm -rf ${BASE_DIR}/${JBR_BUNDLE}
}
JBRSDK_BASE_NAME=jbrsdk-$JBSDK_VERSION
JBR_BASE_NAME=jbr-$JBSDK_VERSION
IMAGES_DIR=build/windows-x86_64-server-release/images
JSDK=$IMAGES_DIR/jdk
JBSDK=$JBRSDK_BASE_NAME-windows-x64-b$build_number
BASE_DIR=.
if [ -z "$bundle_type" ]; then
JBR_BUNDLE=jbr
else
JBR_BUNDLE=jbr_${bundle_type}
fi
if [[ "$bundle_type" == "jfx_jcef" || -z "$bundle_type" ]]; then
JBRSDK_BUNDLE=jbrsdk
echo Creating $JBSDK.tar.gz ...
/usr/bin/tar -czf $JBSDK.tar.gz $JBRSDK_BUNDLE || exit 1
fi
JBR_BUNDLE=jbr_${bundle_type}
pack_jbr $bundle_type
if [[ "$bundle_type" == "jfx_jcef" || -z "$bundle_type" ]]; then
JBRSDK_TEST=$JBRSDK_BASE_NAME-windows-test-x64-b$build_number
echo Creating $JBRSDK_TEST.tar.gz ...
/usr/bin/tar -czf $JBRSDK_TEST.tar.gz -C $IMAGES_DIR --exclude='test/jdk/demos' test || exit 1
fi

View File

@@ -0,0 +1,45 @@
#!/bin/bash -x
# The following parameters must be specified:
# JBSDK_VERSION - specifies the current version of OpenJDK e.g. 11_0_6
# JDK_BUILD_NUMBER - specifies the number of OpenJDK build or the value of --with-version-build argument to configure
# build_number - specifies the number of JetBrainsRuntime build
# bundle_type - specifies bundle to bu built; possible values:
# jcef - the bundles 1) jbr with jcef+javafx, 2) jbrsdk and 3) test will be created
# jfx - the bundle 1) jbr with javafx only will be created
#
# jbrsdk-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
# jbr-${JBSDK_VERSION}-osx-x64-b${build_number}.tar.gz
#
# $ ./java --version
# openjdk 11.0.6 2020-01-14
# OpenJDK Runtime Environment (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number})
# OpenJDK 64-Bit Server VM (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number}, mixed mode)
#
JBSDK_VERSION=$1
JDK_BUILD_NUMBER=$2
build_number=$3
JBRSDK_BASE_NAME=jbrsdk-$JBSDK_VERSION
JBR_BASE_NAME=jbr-$JBSDK_VERSION
IMAGES_DIR=build/windows-x86-normal-server-release/images
JSDK=$IMAGES_DIR/jdk
JBSDK=$JBRSDK_BASE_NAME-windows-x86-b$build_number
BASE_DIR=.
JBRSDK_BUNDLE=jbrsdk
echo Creating $JBSDK.tar.gz ...
/usr/bin/tar -czf $JBSDK.tar.gz $JBRSDK_BUNDLE || exit 1
JBR_BUNDLE=jbr
JBR_BASE_NAME=jbr-${JBSDK_VERSION}
JBR=$JBR_BASE_NAME-windows-x86-b$build_number
echo Creating $JBR.tar.gz ...
/usr/bin/tar -czf $JBR.tar.gz -C $BASE_DIR ${JBR_BUNDLE} || exit 1
JBRSDK_TEST=$JBRSDK_BASE_NAME-windows-test-x86-b$build_number
echo Creating $JBRSDK_TEST.tar.gz ...
/usr/bin/tar -czf $JBRSDK_TEST.tar.gz -C $IMAGES_DIR --exclude='test/jdk/demos' test || exit 1

View File

@@ -569,6 +569,8 @@ CXXFILT:=@CXXFILT@
LIPO:=@LIPO@
INSTALL_NAME_TOOL:=@INSTALL_NAME_TOOL@
METAL := @METAL@
METALLIB := @METALLIB@
# Options to linker to specify a mapfile.
# (Note absence of := assignment, because we do not want to evaluate the macro body here)

View File

@@ -807,6 +807,32 @@ AC_DEFUN_ONCE([TOOLCHAIN_DETECT_TOOLCHAIN_EXTRA],
UTIL_FIXUP_EXECUTABLE(OTOOL)
UTIL_REQUIRE_PROGS(INSTALL_NAME_TOOL, install_name_tool)
UTIL_FIXUP_EXECUTABLE(INSTALL_NAME_TOOL)
UTIL_PATH_PROGS(METAL, metal)
if test "x$METAL" = x; then
AC_MSG_CHECKING([if metal can be run using xcrun])
METAL="xcrun -sdk macosx metal"
test_metal=`$METAL --version 2>&1`
if test $? -ne 0; then
AC_MSG_RESULT([no])
AC_MSG_ERROR([XCode tool 'metal' neither found in path nor with xcrun])
else
AC_MSG_RESULT([yes, will be using '$METAL'])
fi
fi
UTIL_PATH_PROGS(METALLIB, metallib)
if test "x$METALLIB" = x; then
AC_MSG_CHECKING([if metallib can be run using xcrun])
METALLIB="xcrun -sdk macosx metallib"
test_metallib=`$METALLIB --version 2>&1`
if test $? -ne 0; then
AC_MSG_RESULT([no])
AC_MSG_ERROR([XCode tool 'metallib' neither found in path nor with xcrun])
else
AC_MSG_RESULT([yes, will be using '$METALLIB'])
fi
fi
fi
if test "x$TOOLCHAIN_TYPE" = xmicrosoft; then

View File

@@ -24,6 +24,7 @@
#
include LibCommon.gmk
include Execute.gmk
# Hook to include the corresponding custom file, if present.
$(eval $(call IncludeCustomExtension, modules/java.desktop/Lib.gmk))

View File

@@ -172,6 +172,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \
LIBS_macosx := -lmlib_image \
-framework Cocoa \
-framework OpenGL \
-framework Metal \
-framework JavaNativeFoundation \
-framework JavaRuntimeSupport \
-framework ApplicationServices \
@@ -748,6 +749,7 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false)
-framework Foundation \
-framework Security \
-framework Cocoa \
-framework Metal \
-framework JavaNativeFoundation
else ifeq ($(call isTargetOs, windows), true)
LIBSPLASHSCREEN_LIBS += kernel32.lib user32.lib gdi32.lib delayimp.lib $(WIN_JAVA_LIB) jvm.lib
@@ -809,6 +811,7 @@ ifeq ($(call isTargetOs, macosx), true)
libawt_lwawt/awt \
libawt_lwawt/font \
libawt_lwawt/java2d/opengl \
libawt_lwawt/java2d/metal \
include \
common/awt/debug \
common/java2d/opengl \
@@ -844,6 +847,7 @@ ifeq ($(call isTargetOs, macosx), true)
-framework AudioToolbox \
-framework Carbon \
-framework Cocoa \
-framework Metal \
-framework Security \
-framework ExceptionHandling \
-framework JavaNativeFoundation \
@@ -867,6 +871,28 @@ endif
################################################################################
ifeq ($(call isTargetOs, macosx), true)
SHADERS_SRC := $(TOPDIR)/src/java.desktop/macosx/native/libawt_lwawt/awt/shaders.metal
SHADERS_SUPPORT_DIR := $(SUPPORT_OUTPUTDIR)/native/java.desktop/libosxui
SHADERS_AIR := $(SHADERS_SUPPORT_DIR)/shaders.air
SHADERS_LIB := $(INSTALL_LIBRARIES_HERE)/shaders.metallib
$(eval $(call SetupExecute, metal_shaders, \
INFO := Running metal on $(notdir $(SHADERS_SRC)) (for libosxui.dylib), \
DEPS := $(SHADERS_SRC), \
OUTPUT_FILE := $(SHADERS_AIR), \
SUPPORT_DIR := $(SHADERS_SUPPORT_DIR), \
COMMAND := $(METAL) -c -std=osx-metal2.0 -o $(SHADERS_AIR) $(SHADERS_SRC), \
))
$(eval $(call SetupExecute, metallib_shaders, \
INFO := Running metallib on $(notdir $(SHADERS_AIR)) (for libosxui.dylib), \
DEPS := $(SHADERS_AIR), \
OUTPUT_FILE := $(SHADERS_LIB), \
SUPPORT_DIR := $(SHADERS_SUPPORT_DIR), \
COMMAND := $(METALLIB) -o $(SHADERS_LIB) $(SHADERS_AIR), \
))
TARGETS += $(SHADERS_LIB)
$(eval $(call SetupJdkLibrary, BUILD_LIBOSXUI, \
NAME := osxui, \
@@ -882,6 +908,7 @@ ifeq ($(call isTargetOs, macosx), true)
-L$(INSTALL_LIBRARIES_HERE), \
LIBS := -lawt -losxapp -lawt_lwawt \
-framework Cocoa \
-framework Metal \
-framework Carbon \
-framework ApplicationServices \
-framework JavaNativeFoundation \
@@ -890,6 +917,7 @@ ifeq ($(call isTargetOs, macosx), true)
))
TARGETS += $(BUILD_LIBOSXUI)
$(BUILD_LIBOSXUI): $(SHADERS_LIB)
$(BUILD_LIBOSXUI): $(BUILD_LIBAWT)

53
modules.list Normal file
View File

@@ -0,0 +1,53 @@
java.base,
java.compiler,
java.datatransfer,
java.desktop,
java.instrument,
java.logging,
java.management,
java.management.rmi,
java.naming,
java.net.http,
java.prefs,
java.rmi,
java.scripting,
java.se,
java.security.jgss,
java.security.sasl,
java.smartcardio,
java.sql,
java.sql.rowset,
java.transaction.xa,
java.xml,
java.xml.crypto,
jdk.accessibility,
jdk.aot,
jdk.charsets,
jdk.compiler,
jdk.crypto.cryptoki,
jdk.crypto.ec,
jdk.dynalink,
jdk.httpserver,
jdk.internal.ed,
jdk.internal.le,
jdk.internal.vm.ci,
jdk.internal.vm.compiler,
jdk.internal.vm.compiler.management,
jdk.jdi,
jdk.jdwp.agent,
jdk.jfr,
jdk.jsobject,
jdk.localedata,
jdk.management,
jdk.management.agent,
jdk.management.jfr,
jdk.naming.dns,
jdk.naming.rmi,
jdk.net,
jdk.sctp,
jdk.security.auth,
jdk.security.jgss,
jdk.unsupported,
jdk.xml.dom,
jdk.zipfs,
jdk.hotspot.agent

View File

@@ -0,0 +1,86 @@
#
# Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# - Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# - Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# - Neither the name of Oracle nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
SOURCEPATH=src
CLASSES=build
DIST=dist
RESOURCES=resources
RENDERPERF_CLASSES = $(CLASSES)/renderperf/RenderPerfTest.class
RENDERPERFLCD_CLASSES = $(CLASSES)/renderperf/RenderPerfLCDTest.class
RENDERPERF_SOURCES = $(SOURCEPATH)/renderperf/RenderPerfTest.java
RENDERPERFLCD_SOURCES = $(SOURCEPATH)/renderperf/RenderPerfLCDTest.java
RENDERPERF_RESOURCES = $(CLASSES)/renderperf/images/duke.png
all: mkdirs $(DIST)/RenderPerfTest.jar $(DIST)/RenderPerfLCDTest.jar
run: mkdirs $(DIST)/RenderPerfTest.jar
java -jar $(DIST)/RenderPerfTest.jar
$(DIST)/RenderPerfTest.jar: \
$(RENDERPERF_CLASSES) $(RENDERPERF_RESOURCES) \
$(CLASSES)/renderperf.manifest
jar cvmf $(CLASSES)/renderperf.manifest $(DIST)/RenderPerfTest.jar -C $(CLASSES) .
$(DIST)/RenderPerfLCDTest.jar: \
$(RENDERPERFLCD_CLASSES) $(RENDERPERFLCD_RESOURCES) \
$(CLASSES)/renderperflcd.manifest
jar cvmf $(CLASSES)/renderperflcd.manifest $(DIST)/RenderPerfLCDTest.jar -C $(CLASSES) .
$(CLASSES)/renderperf/images/%: $(RESOURCES)/renderperf/images/%
cp -r $< $@
$(CLASSES)/renderperf.manifest:
echo "Main-Class: renderperf.RenderPerfTest" > $@
$(CLASSES)/renderperflcd.manifest:
echo "Main-Class: renderperf.RenderPerfLCDTest" > $@
$(DIST):
mkdir $(DIST)
$(CLASSES):
mkdir $(CLASSES)
mkdir -p $(CLASSES)/renderperf/images
mkdirs: $(DIST) $(CLASSES)
$(RENDERPERF_CLASSES): $(RENDERPERF_SOURCES)
javac -g:none -d $(CLASSES) -sourcepath $(SOURCEPATH) $<
$(RENDERPERFLCD_CLASSES): $(RENDERPERFLCD_SOURCES)
javac -g:none -d $(CLASSES) -sourcepath $(SOURCEPATH) $<
clean:
rm -rf $(CLASSES)
rm -rf $(DIST)

View File

@@ -0,0 +1,34 @@
-----------------------------------------------------------------------
Introduction
-----------------------------------------------------------------------
RenderPerfTest is a set of on-screen rendering microbenchmarks to
analyze the performance of Java2D graphical primitives rendering
-----------------------------------------------------------------------
How To Compile
-----------------------------------------------------------------------
#> cd RenderPerfTest
The benchmark can be compiled by using either ant:
#> ant
or gnumake (assuming there's 'javac' in the path):
#> gnumake
The jar files will be generated into RenderPerfTest/dist directory.
-----------------------------------------------------------------------
How To Run RenderPerfTest
-----------------------------------------------------------------------
Run all tests
#> ant run
or
#> java -jar dist/RenderPerfTest.jar
Run particular test cases
#> java -jar dist/RenderPerfTest.jar testWhiteTextBubblesGray ...

View File

@@ -0,0 +1,94 @@
<!--
Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of Oracle nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
<project name="RenderPerfTest" default="dist" basedir=".">
<description>
simple example build file
</description>
<!-- set global properties for this build -->
<property name="src" location="src"/>
<property name="build" location="build"/>
<property name="dist" location="dist"/>
<property name="resources" location="resources"/>
<target name="init">
<!-- Create the time stamp -->
<tstamp/>
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init"
description="compile the source " >
<!-- Compile the java code from ${src} into ${build} -->
<javac includeantruntime="false" debug="off" srcdir="${src}" destdir="${build}"/>
</target>
<target name="run" depends="dist"
description="run RenderPerfTest" >
<java jar="${dist}/RenderPerfTest.jar"
fork="true"
>
</java>
</target>
<target name="resources" depends="init"
description="copy resources into build dir" >
<!-- Copy the resource files from ${resources} into ${build}/ -->
<mkdir dir="${dist}"/>
<mkdir dir="${dist}/renderperf"/>
<mkdir dir="${build}/renderperf/images"/>
<copy todir="${build}/renderperf/images">
<fileset dir="resources/renderperf/images" />
</copy>
</target>
<target name="dist" depends="compile, resources"
description="generate the distribution" >
<!-- Create the distribution directory -->
<mkdir dir="${dist}"/>
<!-- Put everything in ${build} into the J2DBench.jar file -->
<jar jarfile="${dist}/RenderPerfTest.jar" basedir="${build}">
<manifest>
<attribute name="Built-By" value="${user.name}"/>
<attribute name="Main-Class" value="renderperf.RenderPerfTest"/>
</manifest>
</jar>
</target>
<target name="clean"
description="clean up" >
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,367 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package renderperf;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
public class RenderPerfLCDTest {
private static HashSet<String> ignoredTests = new HashSet<>();
private final static int N = 1000;
private final static float WIDTH = 800;
private final static float HEIGHT = 800;
private final static float R = 25;
private final static int BW = 50;
private final static int BH = 50;
private final static int COUNT = 300;
private final static int DELAY = 10;
private final static int RESOLUTION = 5;
private final static int COLOR_TOLERANCE = 10;
private final static int MAX_MEASURE_TIME = 5000;
interface Configurable {
void configure(Graphics2D g2d);
}
interface Renderable {
void setup(Graphics2D g2d);
void render(Graphics2D g2d);
void update();
}
static class Particles {
private float[] bx;
private float[] by;
private float[] vx;
private float[] vy;
private float r;
private int n;
private float x0;
private float y0;
private float width;
private float height;
Particles(int n, float r, float x0, float y0, float width, float height) {
bx = new float[n];
by = new float[n];
vx = new float[n];
vy = new float[n];
this.n = n;
this.r = r;
this.x0 = x0;
this.y0 = y0;
this.width = width;
this.height = height;
for (int i = 0; i < n; i++) {
bx[i] = (float) (x0 + r + 0.1 + Math.random() * (width - 2 * r - 0.2 - x0));
by[i] = (float) (y0 + r + 0.1 + Math.random() * (height - 2 * r - 0.2 - y0));
vx[i] = 0.1f * (float) (Math.random() * 2 * r - r);
vy[i] = 0.1f * (float) (Math.random() * 2 * r - r);
}
}
void render(Graphics2D g2d, ParticleRenderer renderer) {
for (int i = 0; i < n; i++) {
renderer.render(g2d, i, bx, by, vx, vy);
}
}
void update() {
for (int i = 0; i < n; i++) {
bx[i] += vx[i];
if (bx[i] + r > width || bx[i] - r < x0) vx[i] = -vx[i];
by[i] += vy[i];
if (by[i] + r > height || by[i] - r < y0) vy[i] = -vy[i];
}
}
}
ParticleRenderable createPR(ParticleRenderer renderer) {
return new ParticleRenderable(renderer);
}
static class ParticleRenderable implements Renderable {
ParticleRenderer renderer;
Configurable configure;
ParticleRenderable(ParticleRenderer renderer, Configurable configure) {
this.renderer = renderer;
this.configure = configure;
}
ParticleRenderable(ParticleRenderer renderer) {
this(renderer, null);
}
@Override
public void setup(Graphics2D g2d) {
if (configure != null) configure.configure(g2d);
}
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, renderer);
}
@Override
public void update() {
balls.update();
}
public ParticleRenderable configure(Configurable configure) {
this.configure = configure;
return this;
}
}
interface ParticleRenderer {
void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy);
}
static class WhiteTextParticleRenderer implements ParticleRenderer {
float r;
WhiteTextParticleRenderer(float r) {
this.r = r;
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(Color.WHITE);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
setPaint(g2d, id);
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)(y[id] - r));
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)y[id]);
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)(y[id] + r));
}
}
static class TextParticleRenderer extends WhiteTextParticleRenderer {
Color[] colors;
float r;
TextParticleRenderer(int n, float r) {
super(r);
colors = new Color[n];
this.r = r;
for (int i = 0; i < n; i++) {
colors[i] = new Color((float) Math.random(),
(float) Math.random(), (float) Math.random());
}
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(colors[id % colors.length]);
}
}
static class PerfMeter {
private String name;
private int frame = 0;
private JPanel panel;
private long time;
private double execTime = 0;
private Color expColor = Color.RED;
AtomicBoolean waiting = new AtomicBoolean(false);
private double fps;
PerfMeter(String name) {
this.name = name;
}
PerfMeter exec(final Renderable renderable) throws Exception {
final CountDownLatch latch = new CountDownLatch(COUNT);
final CountDownLatch latchFrame = new CountDownLatch(1);
final long endTime = System.currentTimeMillis() + MAX_MEASURE_TIME;
final Frame f = new Frame();
f.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
latchFrame.countDown();
}
});
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
panel = new JPanel()
{
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
time = System.nanoTime();
Graphics2D g2d = (Graphics2D) g.create();
renderable.setup(g2d);
renderable.render(g2d);
g2d.setColor(expColor);
g.fillRect(0, 0, BW, BH);
}
};
panel.setPreferredSize(new Dimension((int)(WIDTH + BW), (int)(HEIGHT + BH)));
panel.setBackground(Color.BLACK);
f.add(panel);
//f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
});
Robot robot = new Robot();
Timer timer = new Timer(DELAY, e -> {
if (waiting.compareAndSet(false, true)) {
Color c = robot.getPixelColor(
panel.getTopLevelAncestor().getX() + panel.getTopLevelAncestor().getInsets().left + BW / 2,
panel.getTopLevelAncestor().getY() + panel.getTopLevelAncestor().getInsets().top + BW / 2);
if (isAlmostEqual(c, Color.BLUE)) {
expColor = Color.RED;
} else {
expColor = Color.BLUE;
}
renderable.update();
panel.getParent().repaint();
} else {
while (!isAlmostEqual(
robot.getPixelColor(
panel.getTopLevelAncestor().getX() + panel.getTopLevelAncestor().getInsets().left + BW/2,
panel.getTopLevelAncestor().getY() + panel.getTopLevelAncestor().getInsets().top + BH/2),
expColor))
{
try {
Thread.sleep(RESOLUTION);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
time = System.nanoTime() - time;
execTime += time;
frame++;
waiting.set(false);
}
if (System.currentTimeMillis() < endTime) {
latch.countDown();
} else {
while(latch.getCount() > 0) latch.countDown();
}
});
timer.start();
latch.await();
SwingUtilities.invokeAndWait(() -> {
timer.stop();
f.setVisible(false);
f.dispose();
});
latchFrame.await();
if (execTime != 0 && frame != 0) {
fps = 1e9 / (execTime / frame);
} else {
fps = 0;
}
return this;
}
private void report() {
System.err.println(name + " : " + String.format("%.2f FPS", fps));
}
private boolean isAlmostEqual(Color c1, Color c2) {
return Math.abs(c1.getRed() - c2.getRed()) < COLOR_TOLERANCE ||
Math.abs(c1.getGreen() - c2.getGreen()) < COLOR_TOLERANCE ||
Math.abs(c1.getBlue() - c2.getBlue()) < COLOR_TOLERANCE;
}
}
private static final Particles balls = new Particles(N, R, BW, BH, WIDTH, HEIGHT);
private static final ParticleRenderer textRenderer = new TextParticleRenderer(N, R);
private static final Configurable TextLCD = (Graphics2D g2d) ->
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
public void testTextBubblesLCD() throws Exception {
(new PerfMeter("TextLCD")).exec(createPR(textRenderer).configure(TextLCD)).report();
}
public static void main(String[] args)
throws InvocationTargetException, IllegalAccessException, NoSuchMethodException
{
RenderPerfLCDTest test = new RenderPerfLCDTest();
if (args.length > 0) {
for (String testCase : args) {
Method m = RenderPerfLCDTest.class.getDeclaredMethod(testCase);
m.invoke(test);
}
} else {
Method[] methods = RenderPerfLCDTest.class.getDeclaredMethods();
for (Method m : methods) {
if (m.getName().startsWith("test") && !ignoredTests.contains(m.getName())) {
m.invoke(test);
}
}
}
}
}

View File

@@ -0,0 +1,748 @@
/*
* Copyright (c) 2019, 2020 Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package renderperf;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
public class RenderPerfTest {
private static HashSet<String> ignoredTests = new HashSet<>();
static {
ignoredTests.add("testWhiteTextBubblesNoAA");
ignoredTests.add("testWhiteTextBubblesLCD");
ignoredTests.add("testWhiteTextBubblesGray");
ignoredTests.add("testLinGradOvalRotBubblesAA");
ignoredTests.add("testWiredBoxBubblesAA");
ignoredTests.add("testLinesAA");
}
private final static int N = 1000;
private final static float WIDTH = 800;
private final static float HEIGHT = 800;
private final static float R = 25;
private final static int BW = 50;
private final static int BH = 50;
private final static int COUNT = 300;
private final static int DELAY = 10;
private final static int RESOLUTION = 5;
private final static int COLOR_TOLERANCE = 10;
private final static int MAX_MEASURE_TIME = 5000;
interface Configurable {
void configure(Graphics2D g2d);
}
interface Renderable {
void setup(Graphics2D g2d);
void render(Graphics2D g2d);
void update();
}
static class Particles {
private float[] bx;
private float[] by;
private float[] vx;
private float[] vy;
private float r;
private int n;
private float x0;
private float y0;
private float width;
private float height;
Particles(int n, float r, float x0, float y0, float width, float height) {
bx = new float[n];
by = new float[n];
vx = new float[n];
vy = new float[n];
this.n = n;
this.r = r;
this.x0 = x0;
this.y0 = y0;
this.width = width;
this.height = height;
for (int i = 0; i < n; i++) {
bx[i] = (float) (x0 + r + 0.1 + Math.random() * (width - 2 * r - 0.2 - x0));
by[i] = (float) (y0 + r + 0.1 + Math.random() * (height - 2 * r - 0.2 - y0));
vx[i] = 0.1f * (float) (Math.random() * 2 * r - r);
vy[i] = 0.1f * (float) (Math.random() * 2 * r - r);
}
}
void render(Graphics2D g2d, ParticleRenderer renderer) {
for (int i = 0; i < n; i++) {
renderer.render(g2d, i, bx, by, vx, vy);
}
}
void update() {
for (int i = 0; i < n; i++) {
bx[i] += vx[i];
if (bx[i] + r > width || bx[i] - r < x0) vx[i] = -vx[i];
by[i] += vy[i];
if (by[i] + r > height || by[i] - r < y0) vy[i] = -vy[i];
}
}
}
ParticleRenderable createPR(ParticleRenderer renderer) {
return new ParticleRenderable(renderer);
}
static class ParticleRenderable implements Renderable {
ParticleRenderer renderer;
Configurable configure;
ParticleRenderable(ParticleRenderer renderer, Configurable configure) {
this.renderer = renderer;
this.configure = configure;
}
ParticleRenderable(ParticleRenderer renderer) {
this(renderer, null);
}
@Override
public void setup(Graphics2D g2d) {
if (configure != null) configure.configure(g2d);
}
@Override
public void render(Graphics2D g2d) {
balls.render(g2d, renderer);
}
@Override
public void update() {
balls.update();
}
public ParticleRenderable configure(Configurable configure) {
this.configure = configure;
return this;
}
}
interface ParticleRenderer {
void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy);
}
static class FlatParticleRenderer implements ParticleRenderer {
Color[] colors;
float r;
FlatParticleRenderer(int n, float r) {
colors = new Color[n];
this.r = r;
for (int i = 0; i < n; i++) {
colors[i] = new Color((float) Math.random(),
(float) Math.random(), (float) Math.random());
}
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.fillOval((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r));
}
}
static class WhiteTextParticleRenderer implements ParticleRenderer {
float r;
WhiteTextParticleRenderer(float r) {
this.r = r;
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(Color.WHITE);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
setPaint(g2d, id);
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)(y[id] - r));
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)y[id]);
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)(y[id] + r));
}
}
static class TextParticleRenderer extends WhiteTextParticleRenderer {
Color[] colors;
float r;
TextParticleRenderer(int n, float r) {
super(r);
colors = new Color[n];
this.r = r;
for (int i = 0; i < n; i++) {
colors[i] = new Color((float) Math.random(),
(float) Math.random(), (float) Math.random());
}
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(colors[id % colors.length]);
}
}
static class LargeTextParticleRenderer extends TextParticleRenderer {
LargeTextParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
setPaint(g2d, id);
Font font = new Font("LucidaGrande", Font.PLAIN, 32);
g2d.setFont(font);
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)(y[id] - r));
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)y[id]);
g2d.drawString("The quick brown fox jumps over the lazy dog",
(int)(x[id] - r), (int)(y[id] + r));
}
}
static class FlatOvalRotParticleRenderer extends FlatParticleRenderer {
FlatOvalRotParticleRenderer(int n, float r) {
super(n, r);
}
void setPaint(Graphics2D g2d, int id) {
g2d.setColor(colors[id % colors.length]);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
setPaint(g2d, id);
if (Math.abs(vx[id] + vy[id]) > 0.001) {
AffineTransform t = (AffineTransform) g2d.getTransform().clone();
double l = vx[id] / Math.sqrt(vx[id] * vx[id] + vy[id] * vy[id]);
if (vy[id] < 0) {
l = -l;
}
g2d.translate(x[id], y[id]);
g2d.rotate(Math.acos(l));
g2d.fillOval(-(int)r, (int)(-0.5*r), (int) (2 * r), (int)r);
g2d.setTransform(t);
} else {
g2d.fillOval((int)(x[id] - r), (int)(y[id] - 0.5*r),
(int) (2 * r), (int) r);
}
}
}
static class LinGradOvalRotParticleRenderer extends FlatOvalRotParticleRenderer {
LinGradOvalRotParticleRenderer(int n, float r) {
super(n, r);
}
@Override
void setPaint(Graphics2D g2d, int id) {
Point2D start = new Point2D.Double(- r, - 0.5*r);
Point2D end = new Point2D.Double( 2 * r, r);
float[] dist = {0.0f, 1.0f};
Color[] cls = {colors[id %colors.length], colors[(colors.length - id) %colors.length]};
LinearGradientPaint p =
new LinearGradientPaint(start, end, dist, cls);
g2d.setPaint(p);
}
}
static class FlatBoxParticleRenderer extends FlatParticleRenderer {
FlatBoxParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.fillRect((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r));
}
}
static class ImgParticleRenderer extends FlatParticleRenderer {
BufferedImage dukeImg;
ImgParticleRenderer(int n, float r) {
super(n, r);
try {
dukeImg = ImageIO.read(
Objects.requireNonNull(
RenderPerfTest.class.getClassLoader().getResourceAsStream(
"renderperf/images/duke.png")));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.drawImage(dukeImg, (int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r), null);
}
}
static class FlatBoxRotParticleRenderer extends FlatParticleRenderer {
FlatBoxRotParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
if (Math.abs(vx[id] + vy[id]) > 0.001) {
AffineTransform t = (AffineTransform) g2d.getTransform().clone();
double l = vx[id] / Math.sqrt(vx[id] * vx[id] + vy[id] * vy[id]);
if (vy[id] < 0) {
l = -l;
}
g2d.translate(x[id], y[id]);
g2d.rotate(Math.acos(l));
g2d.fillRect(-(int)r, -(int)r, (int) (2 * r), (int) (2 * r));
g2d.setTransform(t);
} else {
g2d.fillRect((int)(x[id] - r), (int)(y[id] - r),
(int) (2 * r), (int) (2 * r));
}
}
}
static class WiredParticleRenderer extends FlatParticleRenderer {
WiredParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.drawOval((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r));
}
}
static class WiredBoxParticleRenderer extends FlatParticleRenderer {
WiredBoxParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
g2d.setColor(colors[id % colors.length]);
g2d.drawRect((int)(x[id] - r), (int)(y[id] - r), (int)(2*r), (int)(2*r));
}
}
static class SegParticleRenderer extends FlatParticleRenderer {
SegParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
double v = Math.sqrt(vx[id]*vx[id]+vy[id]*vy[id]);
float nvx = (float) (vx[id]/v);
float nvy = (float) (vy[id]/v);
g2d.setColor(colors[id % colors.length]);
g2d.drawLine((int)(x[id] - r*nvx), (int)(y[id] - r*nvy),
(int)(x[id] + 2*r*nvx), (int)(y[id] + 2*r*nvy));
}
}
static class WiredQuadParticleRenderer extends FlatParticleRenderer {
WiredQuadParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
if (id > 2 && (id % 3) == 0) {
g2d.setColor(colors[id % colors.length]);
g2d.draw(new QuadCurve2D.Float(x[id-3], y[id-3], x[id-2], y[id-2], x[id-1], y[id-1]));
}
}
}
static class FlatQuadParticleRenderer extends FlatParticleRenderer {
FlatQuadParticleRenderer(int n, float r) {
super(n, r);
}
@Override
public void render(Graphics2D g2d, int id, float[] x, float[] y, float[] vx, float[] vy) {
if (id > 2 && (id % 3) == 0) {
g2d.setColor(colors[id % colors.length]);
g2d.fill(new QuadCurve2D.Float(x[id-3], y[id-3], x[id-2], y[id-2], x[id-1], y[id-1]));
}
}
}
static class PerfMeter {
private String name;
private int frame = 0;
private JPanel panel;
private long time;
private double execTime = 0;
private Color expColor = Color.RED;
AtomicBoolean waiting = new AtomicBoolean(false);
private double fps;
PerfMeter(String name) {
this.name = name;
}
PerfMeter exec(final Renderable renderable) throws Exception {
final CountDownLatch latch = new CountDownLatch(COUNT);
final CountDownLatch latchFrame = new CountDownLatch(1);
final long endTime = System.currentTimeMillis() + MAX_MEASURE_TIME;
final JFrame f = new JFrame();
f.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
latchFrame.countDown();
}
});
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
panel = new JPanel()
{
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
time = System.nanoTime();
Graphics2D g2d = (Graphics2D) g.create();
renderable.setup(g2d);
renderable.render(g2d);
g2d.setColor(expColor);
g.fillRect(0, 0, BW, BH);
}
};
panel.setPreferredSize(new Dimension((int)(WIDTH + BW), (int)(HEIGHT + BH)));
panel.setBackground(Color.BLACK);
f.add(panel);
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
});
Robot robot = new Robot();
Timer timer = new Timer(DELAY, e -> {
if (waiting.compareAndSet(false, true)) {
Color c = robot.getPixelColor(
panel.getTopLevelAncestor().getX() + panel.getTopLevelAncestor().getInsets().left + BW / 2,
panel.getTopLevelAncestor().getY() + panel.getTopLevelAncestor().getInsets().top + BW / 2);
if (isAlmostEqual(c, Color.BLUE)) {
expColor = Color.RED;
} else {
expColor = Color.BLUE;
}
renderable.update();
panel.getParent().repaint();
} else {
while (!isAlmostEqual(
robot.getPixelColor(
panel.getTopLevelAncestor().getX() + panel.getTopLevelAncestor().getInsets().left + BW/2,
panel.getTopLevelAncestor().getY() + panel.getTopLevelAncestor().getInsets().top + BH/2),
expColor))
{
try {
Thread.sleep(RESOLUTION);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
time = System.nanoTime() - time;
execTime += time;
frame++;
waiting.set(false);
}
if (System.currentTimeMillis() < endTime) {
latch.countDown();
} else {
while(latch.getCount() > 0) latch.countDown();
}
});
timer.start();
latch.await();
SwingUtilities.invokeAndWait(() -> {
timer.stop();
f.setVisible(false);
f.dispose();
});
latchFrame.await();
if (execTime != 0 && frame != 0) {
fps = 1e9 / (execTime / frame);
} else {
fps = 0;
}
return this;
}
private void report() {
System.err.println(name + " : " + String.format("%.2f FPS", fps));
}
private boolean isAlmostEqual(Color c1, Color c2) {
return Math.abs(c1.getRed() - c2.getRed()) < COLOR_TOLERANCE ||
Math.abs(c1.getGreen() - c2.getGreen()) < COLOR_TOLERANCE ||
Math.abs(c1.getBlue() - c2.getBlue()) < COLOR_TOLERANCE;
}
}
private static final Particles balls = new Particles(N, R, BW, BH, WIDTH, HEIGHT);
private static final ParticleRenderer flatRenderer = new FlatParticleRenderer(N, R);
private static final ParticleRenderer flatOvalRotRenderer = new FlatOvalRotParticleRenderer(N, R);
private static final ParticleRenderer flatBoxRenderer = new FlatBoxParticleRenderer(N, R);
private static final ParticleRenderer flatBoxRotRenderer = new FlatBoxRotParticleRenderer(N, R);
private static final ParticleRenderer linGradOvalRotRenderer = new LinGradOvalRotParticleRenderer(N, R);
private static final ParticleRenderer wiredRenderer = new WiredParticleRenderer(N, R);
private static final ParticleRenderer wiredBoxRenderer = new WiredBoxParticleRenderer(N, R);
private static final ParticleRenderer segRenderer = new SegParticleRenderer(N, R);
private static final ParticleRenderer flatQuadRenderer = new FlatQuadParticleRenderer(N, R);
private static final ParticleRenderer wiredQuadRenderer = new WiredQuadParticleRenderer(N, R);
private static final ParticleRenderer imgRenderer = new ImgParticleRenderer(N, R);
private static final ParticleRenderer textRenderer = new TextParticleRenderer(N, R);
private static final ParticleRenderer largeTextRenderer = new LargeTextParticleRenderer(N, R);
private static final ParticleRenderer whiteTextRenderer = new WhiteTextParticleRenderer(R);
private static final Configurable AA = (Graphics2D g2d) ->
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
private static final Configurable TextLCD = (Graphics2D g2d) ->
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
private static final Configurable TextAA = (Graphics2D g2d) ->
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
public void testFlatBubbles() throws Exception {
(new PerfMeter("FlatOval")).exec(createPR(flatRenderer)).report();
}
public void testFlatBubblesAA() throws Exception {
(new PerfMeter("FlatOvalAA")).exec(createPR(flatRenderer).configure(AA)).report();
}
public void testFlatBoxBubbles() throws Exception {
(new PerfMeter("FlatBox")).exec(createPR(flatBoxRenderer)).report();
}
public void testFlatBoxBubblesAA() throws Exception {
(new PerfMeter("FlatBoxAA")).exec(createPR(flatBoxRenderer).configure(AA)).report();
}
public void testImgBubbles() throws Exception {
(new PerfMeter("Image")).exec(createPR(imgRenderer)).report();
}
public void testImgBubblesAA() throws Exception {
(new PerfMeter("ImageAA")).exec(createPR(imgRenderer).configure(AA)).report();
}
public void testFlatBoxRotBubbles() throws Exception {
(new PerfMeter("RotatedBox")).exec(createPR(flatBoxRotRenderer)).report();
}
public void testFlatBoxRotBubblesAA() throws Exception {
(new PerfMeter("RotatedBoxAA")).exec(createPR(flatBoxRotRenderer).configure(AA)).report();
}
public void testFlatOvalRotBubbles() throws Exception {
(new PerfMeter("RotatedOval")).exec(createPR(flatOvalRotRenderer)).report();
}
public void testFlatOvalRotBubblesAA() throws Exception {
(new PerfMeter("RotatedOvalAA")).exec(createPR(flatOvalRotRenderer).configure(AA)).report();
}
public void testLinGradOvalRotBubbles() throws Exception {
(new PerfMeter("LinGradRotatedOval")).exec(createPR(linGradOvalRotRenderer)).report();
}
public void testLinGradOvalRotBubblesAA() throws Exception {
(new PerfMeter("LinGradRotatedOvalAA")).exec(createPR(linGradOvalRotRenderer).configure(AA)).report();
}
public void testWiredBubbles() throws Exception {
(new PerfMeter("WiredBubbles")).exec(createPR(wiredRenderer)).report();
}
public void testWiredBubblesAA() throws Exception {
(new PerfMeter("WiredBubblesAA")).exec(createPR(wiredRenderer).configure(AA)).report();
}
public void testWiredBoxBubbles() throws Exception {
(new PerfMeter("WiredBox")).exec(createPR(wiredBoxRenderer)).report();
}
public void testWiredBoxBubblesAA() throws Exception {
(new PerfMeter("WiredBoxAA")).exec(createPR(wiredBoxRenderer).configure(AA)).report();
}
public void testLines() throws Exception {
(new PerfMeter("Lines")).exec(createPR(segRenderer)).report();
}
public void testLinesAA() throws Exception {
(new PerfMeter("LinesAA")).exec(createPR(segRenderer).configure(AA)).report();
}
public void testFlatQuad() throws Exception {
(new PerfMeter("FlatQuad")).exec(createPR(flatQuadRenderer)).report();
}
public void testFlatQuadAA() throws Exception {
(new PerfMeter("FlatQuadAA")).exec(createPR(flatQuadRenderer).configure(AA)).report();
}
public void testWiredQuad() throws Exception {
(new PerfMeter("WiredQuad")).exec(createPR(wiredQuadRenderer)).report();
}
public void testWiredQuadAA() throws Exception {
(new PerfMeter("WiredQuadAA")).exec(createPR(wiredQuadRenderer).configure(AA)).report();
}
public void testTextBubblesNoAA() throws Exception {
(new PerfMeter("TextNoAA")).exec(createPR(textRenderer)).report();
}
public void testTextBubblesLCD() throws Exception {
(new PerfMeter("TextLCD")).exec(createPR(textRenderer).configure(TextLCD)).report();
}
public void testTextBubblesGray() throws Exception {
(new PerfMeter("TextGray")).exec(createPR(textRenderer).configure(TextAA)).report();
}
public void testLargeTextBubblesNoAA() throws Exception {
(new PerfMeter("LargeTextNoAA")).exec(createPR(largeTextRenderer)).report();
}
public void testLargeTextBubblesLCD() throws Exception {
(new PerfMeter("LargeTextLCD")).exec(createPR(largeTextRenderer).configure(TextLCD)).report();
}
public void testLargeTextBubblesGray() throws Exception {
(new PerfMeter("LargeTextGray")).exec(createPR(largeTextRenderer).configure(TextAA)).report();
}
public void testWhiteTextBubblesNoAA() throws Exception {
(new PerfMeter("WhiteTextNoAA")).exec(createPR(whiteTextRenderer)).report();
}
public void testWhiteTextBubblesLCD() throws Exception {
(new PerfMeter("WhiteTextLCD")).exec(createPR(whiteTextRenderer).configure(TextLCD)).report();
}
public void testWhiteTextBubblesGray() throws Exception {
(new PerfMeter("WhiteTextGray")).exec(createPR(whiteTextRenderer).configure(TextAA)).report();
}
public static void main(String[] args)
throws InvocationTargetException, IllegalAccessException, NoSuchMethodException
{
RenderPerfTest test = new RenderPerfTest();
if (args.length > 0) {
for (String testCase : args) {
Method m = RenderPerfTest.class.getDeclaredMethod(testCase);
m.invoke(test);
}
} else {
Method[] methods = RenderPerfTest.class.getDeclaredMethods();
for (Method m : methods) {
if (m.getName().startsWith("test") && !ignoredTests.contains(m.getName())) {
m.invoke(test);
}
}
}
}
}

View File

@@ -34,6 +34,7 @@ import java.awt.image.ColorModel;
import sun.java2d.SurfaceData;
import sun.java2d.opengl.CGLLayer;
import sun.lwawt.LWGraphicsConfig;
import sun.lwawt.macosx.CFRetainedResource;
public abstract class CGraphicsConfig extends GraphicsConfiguration
implements LWGraphicsConfig {
@@ -80,7 +81,7 @@ public abstract class CGraphicsConfig extends GraphicsConfiguration
* Creates a new SurfaceData that will be associated with the given
* CGLLayer.
*/
public abstract SurfaceData createSurfaceData(CGLLayer layer);
public abstract SurfaceData createSurfaceData(CFRetainedResource layer);
@Override
public final boolean isTranslucencyCapable() {

View File

@@ -37,6 +37,8 @@ import java.awt.peer.WindowPeer;
import java.util.Objects;
import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.macos.MacOSFlags;
import sun.java2d.metal.MTLGraphicsConfig;
import sun.java2d.opengl.CGLGraphicsConfig;
import static java.awt.peer.ComponentPeer.SET_BOUNDS;
@@ -63,7 +65,9 @@ public final class CGraphicsDevice extends GraphicsDevice
public CGraphicsDevice(final int displayID) {
this.displayID = displayID;
config = CGLGraphicsConfig.getConfig(this);
config = MacOSFlags.isMetalEnabled() ?
MTLGraphicsConfig.getConfig(this, displayID, 0) :
CGLGraphicsConfig.getConfig(this);
// initializes default device state, might be redundant step since we
// call "displayChanged()" later anyway, but we do not want to leave the
// device in an inconsistent state after construction

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,8 @@ package sun.java2d;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.macos.MacOSFlags;
import sun.java2d.metal.MTLVolatileSurfaceManager;
import sun.java2d.opengl.CGLVolatileSurfaceManager;
/**
@@ -49,6 +51,7 @@ public class MacosxSurfaceManagerFactory extends SurfaceManagerFactory {
public VolatileSurfaceManager createVolatileManager(SunVolatileImage vImg,
Object context)
{
return new CGLVolatileSurfaceManager(vImg, context);
return MacOSFlags.isMetalEnabled() ? new MTLVolatileSurfaceManager(vImg, context) :
new CGLVolatileSurfaceManager(vImg, context);
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.macos;
import java.security.PrivilegedAction;
public class MacOSFlags {
/**
* Description of command-line flags. All flags with [true|false]
* values
* metalEnabled: usage: "-Dsun.java2d.metal=[true|false]"
*/
private static boolean metalEnabled;
static {
initJavaFlags();
initNativeFlags();
}
private static native boolean initNativeFlags();
private static boolean getBooleanProp(String p, boolean defaultVal) {
String propString = System.getProperty(p);
boolean returnVal = defaultVal;
if (propString != null) {
if (propString.equals("true") ||
propString.equals("t") ||
propString.equals("True") ||
propString.equals("T") ||
propString.equals("")) // having the prop name alone
{ // is equivalent to true
returnVal = true;
} else if (propString.equals("false") ||
propString.equals("f") ||
propString.equals("False") ||
propString.equals("F"))
{
returnVal = false;
}
}
return returnVal;
}
private static boolean getPropertySet(String p) {
String propString = System.getProperty(p);
return (propString != null) ? true : false;
}
private static void initJavaFlags() {
java.security.AccessController.doPrivileged(
(PrivilegedAction<Object>) () -> {
metalEnabled = getBooleanProp("sun.java2d.metal", false);
return null;
});
}
public static boolean isMetalEnabled() {
return metalEnabled;
}
}

View File

@@ -0,0 +1,951 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.SurfaceData;
import sun.java2d.loops.*;
import sun.java2d.pipe.Region;
import sun.java2d.pipe.RenderBuffer;
import sun.java2d.pipe.RenderQueue;
import sun.java2d.pipe.hw.AccelSurface;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.lang.annotation.Native;
import java.lang.ref.WeakReference;
import static sun.java2d.pipe.BufferedOpCodes.BLIT;
import static sun.java2d.pipe.BufferedOpCodes.SURFACE_TO_SW_BLIT;
final class MTLBlitLoops {
static void register() {
Blit blitIntArgbPreToSurface =
new MTLSwToSurfaceBlit(SurfaceType.IntArgbPre,
MTLSurfaceData.PF_INT_ARGB_PRE);
Blit blitIntArgbPreToTexture =
new MTLSwToTextureBlit(SurfaceType.IntArgbPre,
MTLSurfaceData.PF_INT_ARGB_PRE);
TransformBlit transformBlitIntArgbPreToSurface =
new MTLSwToSurfaceTransform(SurfaceType.IntArgbPre,
MTLSurfaceData.PF_INT_ARGB_PRE);
MTLSurfaceToSwBlit blitSurfaceToIntArgbPre =
new MTLSurfaceToSwBlit(SurfaceType.IntArgbPre,
MTLSurfaceData.PF_INT_ARGB_PRE);
GraphicsPrimitive[] primitives = {
// surface->surface ops
new MTLSurfaceToSurfaceBlit(),
new MTLSurfaceToSurfaceScale(),
new MTLSurfaceToSurfaceTransform(),
// render-to-texture surface->surface ops
new MTLRTTSurfaceToSurfaceBlit(),
new MTLRTTSurfaceToSurfaceScale(),
new MTLRTTSurfaceToSurfaceTransform(),
// surface->sw ops
new MTLSurfaceToSwBlit(SurfaceType.IntArgb,
MTLSurfaceData.PF_INT_ARGB),
blitSurfaceToIntArgbPre,
// sw->surface ops
blitIntArgbPreToSurface,
new MTLSwToSurfaceBlit(SurfaceType.IntRgb,
MTLSurfaceData.PF_INT_RGB),
new MTLSwToSurfaceBlit(SurfaceType.IntRgbx,
MTLSurfaceData.PF_INT_RGBX),
new MTLSwToSurfaceBlit(SurfaceType.IntBgr,
MTLSurfaceData.PF_INT_BGR),
new MTLSwToSurfaceBlit(SurfaceType.IntBgrx,
MTLSurfaceData.PF_INT_BGRX),
// TODO: Provide native implementation
// It may not be effective/possible. For example, there is no direct
// support in Metal for SurfaceType.ThreeByteBgr
// new MTLSwToSurfaceBlit(SurfaceType.ThreeByteBgr,
// MTLSurfaceData.PF_3BYTE_BGR),
// new MTLSwToSurfaceBlit(SurfaceType.Ushort565Rgb,
// MTLSurfaceData.PF_USHORT_565_RGB),
// new MTLSwToSurfaceBlit(SurfaceType.Ushort555Rgb,
// MTLSurfaceData.PF_USHORT_555_RGB),
// new MTLSwToSurfaceBlit(SurfaceType.Ushort555Rgbx,
// MTLSurfaceData.PF_USHORT_555_RGBX),
// new MTLSwToSurfaceBlit(SurfaceType.ByteGray,
// MTLSurfaceData.PF_BYTE_GRAY),
// new MTLSwToSurfaceBlit(SurfaceType.UshortGray,
// MTLSurfaceData.PF_USHORT_GRAY),
new MTLGeneralBlit(MTLSurfaceData.MTLSurface,
CompositeType.AnyAlpha,
blitIntArgbPreToSurface),
new MTLAnyCompositeBlit(MTLSurfaceData.MTLSurface,
blitSurfaceToIntArgbPre,
blitSurfaceToIntArgbPre,
blitIntArgbPreToSurface),
new MTLAnyCompositeBlit(SurfaceType.Any,
null,
blitSurfaceToIntArgbPre,
blitIntArgbPreToSurface),
new MTLSwToSurfaceScale(SurfaceType.IntRgb,
MTLSurfaceData.PF_INT_RGB),
new MTLSwToSurfaceScale(SurfaceType.IntRgbx,
MTLSurfaceData.PF_INT_RGBX),
new MTLSwToSurfaceScale(SurfaceType.IntBgr,
MTLSurfaceData.PF_INT_BGR),
new MTLSwToSurfaceScale(SurfaceType.IntBgrx,
MTLSurfaceData.PF_INT_BGRX),
// TODO: Provide native implementation
// new MTLSwToSurfaceScale(SurfaceType.ThreeByteBgr,
// MTLSurfaceData.PF_3BYTE_BGR),
// new MTLSwToSurfaceScale(SurfaceType.Ushort565Rgb,
// MTLSurfaceData.PF_USHORT_565_RGB),
// new MTLSwToSurfaceScale(SurfaceType.Ushort555Rgb,
// MTLSurfaceData.PF_USHORT_555_RGB),
// new MTLSwToSurfaceScale(SurfaceType.Ushort555Rgbx,
// MTLSurfaceData.PF_USHORT_555_RGBX),
// new MTLSwToSurfaceScale(SurfaceType.ByteGray,
// MTLSurfaceData.PF_BYTE_GRAY),
// new MTLSwToSurfaceScale(SurfaceType.UshortGray,
// MTLSurfaceData.PF_USHORT_GRAY),
new MTLSwToSurfaceScale(SurfaceType.IntArgbPre,
MTLSurfaceData.PF_INT_ARGB_PRE),
new MTLSwToSurfaceTransform(SurfaceType.IntRgb,
MTLSurfaceData.PF_INT_RGB),
new MTLSwToSurfaceTransform(SurfaceType.IntRgbx,
MTLSurfaceData.PF_INT_RGBX),
new MTLSwToSurfaceTransform(SurfaceType.IntBgr,
MTLSurfaceData.PF_INT_BGR),
new MTLSwToSurfaceTransform(SurfaceType.IntBgrx,
MTLSurfaceData.PF_INT_BGRX),
// TODO: Provide native implementation
// new MTLSwToSurfaceTransform(SurfaceType.ThreeByteBgr,
// MTLSurfaceData.PF_3BYTE_BGR),
// new MTLSwToSurfaceTransform(SurfaceType.Ushort565Rgb,
// MTLSurfaceData.PF_USHORT_565_RGB),
// new MTLSwToSurfaceTransform(SurfaceType.Ushort555Rgb,
// MTLSurfaceData.PF_USHORT_555_RGB),
// new MTLSwToSurfaceTransform(SurfaceType.Ushort555Rgbx,
// MTLSurfaceData.PF_USHORT_555_RGBX),
// new MTLSwToSurfaceTransform(SurfaceType.ByteGray,
// MTLSurfaceData.PF_BYTE_GRAY),
// new MTLSwToSurfaceTransform(SurfaceType.UshortGray,
// MTLSurfaceData.PF_USHORT_GRAY),
transformBlitIntArgbPreToSurface,
new MTLGeneralTransformedBlit(transformBlitIntArgbPreToSurface),
// texture->surface ops
new MTLTextureToSurfaceBlit(),
new MTLTextureToSurfaceScale(),
new MTLTextureToSurfaceTransform(),
// sw->texture ops
blitIntArgbPreToTexture,
new MTLSwToTextureBlit(SurfaceType.IntRgb,
MTLSurfaceData.PF_INT_RGB),
new MTLSwToTextureBlit(SurfaceType.IntRgbx,
MTLSurfaceData.PF_INT_RGBX),
new MTLSwToTextureBlit(SurfaceType.IntBgr,
MTLSurfaceData.PF_INT_BGR),
new MTLSwToTextureBlit(SurfaceType.IntBgrx,
MTLSurfaceData.PF_INT_BGRX),
// TODO: Provide native implementation
// new MTLSwToTextureBlit(SurfaceType.ThreeByteBgr,
// MTLSurfaceData.PF_3BYTE_BGR),
// new MTLSwToTextureBlit(SurfaceType.Ushort565Rgb,
// MTLSurfaceData.PF_USHORT_565_RGB),
// new MTLSwToTextureBlit(SurfaceType.Ushort555Rgb,
// MTLSurfaceData.PF_USHORT_555_RGB),
// new MTLSwToTextureBlit(SurfaceType.Ushort555Rgbx,
// MTLSurfaceData.PF_USHORT_555_RGBX),
// new MTLSwToTextureBlit(SurfaceType.ByteGray,
// MTLSurfaceData.PF_BYTE_GRAY),
// new MTLSwToTextureBlit(SurfaceType.UshortGray,
// MTLSurfaceData.PF_USHORT_GRAY),
new MTLGeneralBlit(MTLSurfaceData.MTLTexture,
CompositeType.SrcNoEa,
blitIntArgbPreToTexture),
};
GraphicsPrimitiveMgr.register(primitives);
}
/**
* The following offsets are used to pack the parameters in
* createPackedParams(). (They are also used at the native level when
* unpacking the params.)
*/
@Native private static final int OFFSET_SRCTYPE = 16;
@Native private static final int OFFSET_HINT = 8;
@Native private static final int OFFSET_TEXTURE = 3;
@Native private static final int OFFSET_RTT = 2;
@Native private static final int OFFSET_XFORM = 1;
@Native private static final int OFFSET_ISOBLIT = 0;
/**
* Packs the given parameters into a single int value in order to save
* space on the rendering queue.
*/
private static int createPackedParams(boolean isoblit, boolean texture,
boolean rtt, boolean xform,
int hint, int srctype)
{
return
((srctype << OFFSET_SRCTYPE) |
(hint << OFFSET_HINT ) |
((texture ? 1 : 0) << OFFSET_TEXTURE) |
((rtt ? 1 : 0) << OFFSET_RTT ) |
((xform ? 1 : 0) << OFFSET_XFORM ) |
((isoblit ? 1 : 0) << OFFSET_ISOBLIT));
}
/**
* Enqueues a BLIT operation with the given parameters. Note that the
* RenderQueue lock must be held before calling this method.
*/
private static void enqueueBlit(RenderQueue rq,
SurfaceData src, SurfaceData dst,
int packedParams,
int sx1, int sy1,
int sx2, int sy2,
double dx1, double dy1,
double dx2, double dy2)
{
// assert rq.lock.isHeldByCurrentThread();
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(72, 24);
buf.putInt(BLIT);
buf.putInt(packedParams);
buf.putInt(sx1).putInt(sy1);
buf.putInt(sx2).putInt(sy2);
buf.putDouble(dx1).putDouble(dy1);
buf.putDouble(dx2).putDouble(dy2);
buf.putLong(src.getNativeOps());
buf.putLong(dst.getNativeOps());
}
static void Blit(SurfaceData srcData, SurfaceData dstData,
Composite comp, Region clip,
AffineTransform xform, int hint,
int sx1, int sy1,
int sx2, int sy2,
double dx1, double dy1,
double dx2, double dy2,
int srctype, boolean texture)
{
int ctxflags = 0;
if (srcData.getTransparency() == Transparency.OPAQUE) {
ctxflags |= MTLContext.SRC_IS_OPAQUE;
}
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
// make sure the RenderQueue keeps a hard reference to the
// source (sysmem) SurfaceData to prevent it from being
// disposed while the operation is processed on the QFT
rq.addReference(srcData);
MTLSurfaceData oglDst = (MTLSurfaceData)dstData;
if (texture) {
// make sure we have a current context before uploading
// the sysmem data to the texture object
MTLGraphicsConfig gc = oglDst.getMTLGraphicsConfig();
MTLContext.setScratchSurface(gc);
} else {
MTLContext.validateContext(oglDst, oglDst,
clip, comp, xform, null, null,
ctxflags);
}
int packedParams = createPackedParams(false, texture,
false /*unused*/, xform != null,
hint, srctype);
enqueueBlit(rq, srcData, dstData,
packedParams,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2);
// always flush immediately, since we (currently) have no means
// of tracking changes to the system memory surface
rq.flushNow();
} finally {
rq.unlock();
}
}
/**
* Note: The srcImg and biop parameters are only used when invoked
* from the MTLBufImgOps.renderImageWithOp() method; in all other cases,
* this method can be called with null values for those two parameters,
* and they will be effectively ignored.
*/
static void IsoBlit(SurfaceData srcData, SurfaceData dstData,
BufferedImage srcImg, BufferedImageOp biop,
Composite comp, Region clip,
AffineTransform xform, int hint,
int sx1, int sy1,
int sx2, int sy2,
double dx1, double dy1,
double dx2, double dy2,
boolean texture)
{
int ctxflags = 0;
if (srcData.getTransparency() == Transparency.OPAQUE) {
ctxflags |= MTLContext.SRC_IS_OPAQUE;
}
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
MTLSurfaceData oglSrc = (MTLSurfaceData)srcData;
MTLSurfaceData oglDst = (MTLSurfaceData)dstData;
int srctype = oglSrc.getType();
boolean rtt;
MTLSurfaceData srcCtxData;
if (srctype == MTLSurfaceData.TEXTURE) {
// the source is a regular texture object; we substitute
// the destination surface for the purposes of making a
// context current
rtt = false;
srcCtxData = oglDst;
} else {
// the source is a pbuffer, backbuffer, or render-to-texture
// surface; we set rtt to true to differentiate this kind
// of surface from a regular texture object
rtt = true;
if (srctype == AccelSurface.RT_TEXTURE) {
srcCtxData = oglDst;
} else {
srcCtxData = oglSrc;
}
}
MTLContext.validateContext(srcCtxData, oglDst,
clip, comp, xform, null, null,
ctxflags);
if (biop != null) {
MTLBufImgOps.enableBufImgOp(rq, oglSrc, srcImg, biop);
}
int packedParams = createPackedParams(true, texture,
false /*unused*/, xform != null,
hint, 0 /*unused*/);
enqueueBlit(rq, srcData, dstData,
packedParams,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2);
if (biop != null) {
MTLBufImgOps.disableBufImgOp(rq, biop);
}
if (rtt && oglDst.isOnScreen()) {
// we only have to flush immediately when copying from a
// (non-texture) surface to the screen; otherwise Swing apps
// might appear unresponsive until the auto-flush completes
rq.flushNow();
}
} finally {
rq.unlock();
}
}
}
class MTLSurfaceToSurfaceBlit extends Blit {
MTLSurfaceToSurfaceBlit() {
super(MTLSurfaceData.MTLSurface,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy, int w, int h)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
false);
}
}
class MTLSurfaceToSurfaceScale extends ScaledBlit {
MTLSurfaceToSurfaceScale() {
super(MTLSurfaceData.MTLSurface,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Scale(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx1, int sy1,
int sx2, int sy2,
double dx1, double dy1,
double dx2, double dy2)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2,
false);
}
}
class MTLSurfaceToSurfaceTransform extends TransformBlit {
MTLSurfaceToSurfaceTransform() {
super(MTLSurfaceData.MTLSurface,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Transform(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
AffineTransform at, int hint,
int sx, int sy, int dx, int dy,
int w, int h)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, at, hint,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
false);
}
}
class MTLRTTSurfaceToSurfaceBlit extends Blit {
MTLRTTSurfaceToSurfaceBlit() {
super(MTLSurfaceData.MTLSurfaceRTT,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy, int w, int h)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
true);
}
}
class MTLRTTSurfaceToSurfaceScale extends ScaledBlit {
MTLRTTSurfaceToSurfaceScale() {
super(MTLSurfaceData.MTLSurfaceRTT,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Scale(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx1, int sy1,
int sx2, int sy2,
double dx1, double dy1,
double dx2, double dy2)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2,
true);
}
}
class MTLRTTSurfaceToSurfaceTransform extends TransformBlit {
MTLRTTSurfaceToSurfaceTransform() {
super(MTLSurfaceData.MTLSurfaceRTT,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Transform(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
AffineTransform at, int hint,
int sx, int sy, int dx, int dy, int w, int h)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, at, hint,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
true);
}
}
final class MTLSurfaceToSwBlit extends Blit {
private final int typeval;
private WeakReference<SurfaceData> srcTmp;
// destination will actually be ArgbPre or Argb
MTLSurfaceToSwBlit(final SurfaceType dstType, final int typeval) {
super(MTLSurfaceData.MTLSurface,
CompositeType.SrcNoEa,
dstType);
this.typeval = typeval;
}
private synchronized void complexClipBlit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy,
int w, int h) {
SurfaceData cachedSrc = null;
if (srcTmp != null) {
// use cached intermediate surface, if available
cachedSrc = srcTmp.get();
}
// We can convert argb_pre data from MTL surface in two places:
// - During MTL surface -> SW blit
// - During SW -> SW blit
// The first one is faster when we use opaque MTL surface, because in
// this case we simply skip conversion and use color components as is.
// Because of this we align intermediate buffer type with type of
// destination not source.
final int type = typeval == MTLSurfaceData.PF_INT_ARGB_PRE ?
BufferedImage.TYPE_INT_ARGB_PRE :
BufferedImage.TYPE_INT_ARGB;
src = convertFrom(this, src, sx, sy, w, h, cachedSrc, type);
// copy intermediate SW to destination SW using complex clip
final Blit performop = Blit.getFromCache(src.getSurfaceType(),
CompositeType.SrcNoEa,
dst.getSurfaceType());
performop.Blit(src, dst, comp, clip, 0, 0, dx, dy, w, h);
if (src != cachedSrc) {
// cache the intermediate surface
srcTmp = new WeakReference<>(src);
}
}
public void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy,
int w, int h)
{
if (clip != null) {
clip = clip.getIntersectionXYWH(dx, dy, w, h);
// At the end this method will flush the RenderQueue, we should exit
// from it as soon as possible.
if (clip.isEmpty()) {
return;
}
sx += clip.getLoX() - dx;
sy += clip.getLoY() - dy;
dx = clip.getLoX();
dy = clip.getLoY();
w = clip.getWidth();
h = clip.getHeight();
if (!clip.isRectangular()) {
complexClipBlit(src, dst, comp, clip, sx, sy, dx, dy, w, h);
return;
}
}
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
// make sure the RenderQueue keeps a hard reference to the
// destination (sysmem) SurfaceData to prevent it from being
// disposed while the operation is processed on the QFT
rq.addReference(dst);
RenderBuffer buf = rq.getBuffer();
MTLContext.validateContext((MTLSurfaceData)src);
rq.ensureCapacityAndAlignment(48, 32);
buf.putInt(SURFACE_TO_SW_BLIT);
buf.putInt(sx).putInt(sy);
buf.putInt(dx).putInt(dy);
buf.putInt(w).putInt(h);
buf.putInt(typeval);
buf.putLong(src.getNativeOps());
buf.putLong(dst.getNativeOps());
// always flush immediately
rq.flushNow();
} finally {
rq.unlock();
}
}
}
class MTLSwToSurfaceBlit extends Blit {
private int typeval;
MTLSwToSurfaceBlit(SurfaceType srcType, int typeval) {
super(srcType,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
this.typeval = typeval;
}
public void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy, int w, int h)
{
MTLBlitLoops.Blit(src, dst,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
typeval, false);
}
}
class MTLSwToSurfaceScale extends ScaledBlit {
private int typeval;
MTLSwToSurfaceScale(SurfaceType srcType, int typeval) {
super(srcType,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
this.typeval = typeval;
}
public void Scale(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx1, int sy1,
int sx2, int sy2,
double dx1, double dy1,
double dx2, double dy2)
{
MTLBlitLoops.Blit(src, dst,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2,
typeval, false);
}
}
class MTLSwToSurfaceTransform extends TransformBlit {
private int typeval;
MTLSwToSurfaceTransform(SurfaceType srcType, int typeval) {
super(srcType,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
this.typeval = typeval;
}
public void Transform(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
AffineTransform at, int hint,
int sx, int sy, int dx, int dy, int w, int h)
{
MTLBlitLoops.Blit(src, dst,
comp, clip, at, hint,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
typeval, false);
}
}
class MTLSwToTextureBlit extends Blit {
private int typeval;
MTLSwToTextureBlit(SurfaceType srcType, int typeval) {
super(srcType,
CompositeType.SrcNoEa,
MTLSurfaceData.MTLTexture);
this.typeval = typeval;
}
public void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy, int w, int h)
{
MTLBlitLoops.Blit(src, dst,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
typeval, true);
}
}
class MTLTextureToSurfaceBlit extends Blit {
MTLTextureToSurfaceBlit() {
super(MTLSurfaceData.MTLTexture,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy, int w, int h)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
true);
}
}
class MTLTextureToSurfaceScale extends ScaledBlit {
MTLTextureToSurfaceScale() {
super(MTLSurfaceData.MTLTexture,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Scale(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx1, int sy1,
int sx2, int sy2,
double dx1, double dy1,
double dx2, double dy2)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, null,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2,
true);
}
}
class MTLTextureToSurfaceTransform extends TransformBlit {
MTLTextureToSurfaceTransform() {
super(MTLSurfaceData.MTLTexture,
CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
}
public void Transform(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
AffineTransform at, int hint,
int sx, int sy, int dx, int dy,
int w, int h)
{
MTLBlitLoops.IsoBlit(src, dst,
null, null,
comp, clip, at, hint,
sx, sy, sx+w, sy+h,
dx, dy, dx+w, dy+h,
true);
}
}
/**
* This general Blit implementation converts any source surface to an
* intermediate IntArgbPre surface, and then uses the more specific
* IntArgbPre->MTLSurface/Texture loop to get the intermediate
* (premultiplied) surface down to OpenGL using simple blit.
*/
class MTLGeneralBlit extends Blit {
private final Blit performop;
private WeakReference<SurfaceData> srcTmp;
MTLGeneralBlit(SurfaceType dstType,
CompositeType compType,
Blit performop)
{
super(SurfaceType.Any, compType, dstType);
this.performop = performop;
}
public synchronized void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy,
int w, int h)
{
Blit convertsrc = Blit.getFromCache(src.getSurfaceType(),
CompositeType.SrcNoEa,
SurfaceType.IntArgbPre);
SurfaceData cachedSrc = null;
if (srcTmp != null) {
// use cached intermediate surface, if available
cachedSrc = srcTmp.get();
}
// convert source to IntArgbPre
src = convertFrom(convertsrc, src, sx, sy, w, h,
cachedSrc, BufferedImage.TYPE_INT_ARGB_PRE);
// copy IntArgbPre intermediate surface to OpenGL surface
performop.Blit(src, dst, comp, clip,
0, 0, dx, dy, w, h);
if (src != cachedSrc) {
// cache the intermediate surface
srcTmp = new WeakReference<>(src);
}
}
}
/**
* This general TransformedBlit implementation converts any source surface to an
* intermediate IntArgbPre surface, and then uses the more specific
* IntArgbPre->MTLSurface/Texture loop to get the intermediate
* (premultiplied) surface down to OpenGL using simple transformBlit.
*/
final class MTLGeneralTransformedBlit extends TransformBlit {
private final TransformBlit performop;
private WeakReference<SurfaceData> srcTmp;
MTLGeneralTransformedBlit(final TransformBlit performop) {
super(SurfaceType.Any, CompositeType.AnyAlpha,
MTLSurfaceData.MTLSurface);
this.performop = performop;
}
@Override
public synchronized void Transform(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
AffineTransform at, int hint, int srcx,
int srcy, int dstx, int dsty, int width,
int height){
Blit convertsrc = Blit.getFromCache(src.getSurfaceType(),
CompositeType.SrcNoEa,
SurfaceType.IntArgbPre);
// use cached intermediate surface, if available
final SurfaceData cachedSrc = srcTmp != null ? srcTmp.get() : null;
// convert source to IntArgbPre
src = convertFrom(convertsrc, src, srcx, srcy, width, height, cachedSrc,
BufferedImage.TYPE_INT_ARGB_PRE);
// transform IntArgbPre intermediate surface to OpenGL surface
performop.Transform(src, dst, comp, clip, at, hint, 0, 0, dstx, dsty,
width, height);
if (src != cachedSrc) {
// cache the intermediate surface
srcTmp = new WeakReference<>(src);
}
}
}
/**
* This general MTLAnyCompositeBlit implementation can convert any source/target
* surface to an intermediate surface using convertsrc/convertdst loops, applies
* necessary composite operation, and then uses convertresult loop to get the
* intermediate surface down to OpenGL.
*/
final class MTLAnyCompositeBlit extends Blit {
private WeakReference<SurfaceData> dstTmp;
private WeakReference<SurfaceData> srcTmp;
private final Blit convertsrc;
private final Blit convertdst;
private final Blit convertresult;
MTLAnyCompositeBlit(SurfaceType srctype, Blit convertsrc, Blit convertdst,
Blit convertresult) {
super(srctype, CompositeType.Any, MTLSurfaceData.MTLSurface);
this.convertsrc = convertsrc;
this.convertdst = convertdst;
this.convertresult = convertresult;
}
public synchronized void Blit(SurfaceData src, SurfaceData dst,
Composite comp, Region clip,
int sx, int sy, int dx, int dy,
int w, int h)
{
if (convertsrc != null) {
SurfaceData cachedSrc = null;
if (srcTmp != null) {
// use cached intermediate surface, if available
cachedSrc = srcTmp.get();
}
// convert source to IntArgbPre
src = convertFrom(convertsrc, src, sx, sy, w, h, cachedSrc,
BufferedImage.TYPE_INT_ARGB_PRE);
if (src != cachedSrc) {
// cache the intermediate surface
srcTmp = new WeakReference<>(src);
}
}
SurfaceData cachedDst = null;
if (dstTmp != null) {
// use cached intermediate surface, if available
cachedDst = dstTmp.get();
}
// convert destination to IntArgbPre
SurfaceData dstBuffer = convertFrom(convertdst, dst, dx, dy, w, h,
cachedDst, BufferedImage.TYPE_INT_ARGB_PRE);
Region bufferClip =
clip == null ? null : clip.getTranslatedRegion(-dx, -dy);
Blit performop = Blit.getFromCache(src.getSurfaceType(),
CompositeType.Any, dstBuffer.getSurfaceType());
performop.Blit(src, dstBuffer, comp, bufferClip, sx, sy, 0, 0, w, h);
if (dstBuffer != cachedDst) {
// cache the intermediate surface
dstTmp = new WeakReference<>(dstBuffer);
}
// now blit the buffer back to the destination
convertresult.Blit(dstBuffer, dst, AlphaComposite.Src, clip, 0, 0, dx,
dy, w, h);
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.loops.CompositeType;
import sun.java2d.pipe.BufferedBufImgOps;
import java.awt.image.*;
import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_BIOP_SHADER;
class MTLBufImgOps extends BufferedBufImgOps {
/**
* This method is called from MTLDrawImage.transformImage() only. It
* validates the provided BufferedImageOp to determine whether the op
* is one that can be accelerated by the MTL pipeline. If the operation
* cannot be completed for any reason, this method returns false;
* otherwise, the given BufferedImage is rendered to the destination
* using the provided BufferedImageOp and this method returns true.
*/
static boolean renderImageWithOp(SunGraphics2D sg, BufferedImage img,
BufferedImageOp biop, int x, int y)
{
// Validate the provided BufferedImage (make sure it is one that
// is supported, and that its properties are acceleratable)
if (biop instanceof ConvolveOp) {
if (!isConvolveOpValid((ConvolveOp)biop)) {
return false;
}
} else if (biop instanceof RescaleOp) {
if (!isRescaleOpValid((RescaleOp)biop, img)) {
return false;
}
} else if (biop instanceof LookupOp) {
if (!isLookupOpValid((LookupOp)biop, img)) {
return false;
}
} else {
// No acceleration for other BufferedImageOps (yet)
return false;
}
SurfaceData dstData = sg.surfaceData;
if (!(dstData instanceof MTLSurfaceData) ||
(sg.interpolationType == AffineTransformOp.TYPE_BICUBIC) ||
(sg.compositeState > SunGraphics2D.COMP_ALPHA))
{
return false;
}
SurfaceData srcData =
dstData.getSourceSurfaceData(img, SunGraphics2D.TRANSFORM_ISIDENT,
CompositeType.SrcOver, null);
if (!(srcData instanceof MTLSurfaceData)) {
// REMIND: this hack tries to ensure that we have a cached texture
srcData =
dstData.getSourceSurfaceData(img, SunGraphics2D.TRANSFORM_ISIDENT,
CompositeType.SrcOver, null);
if (!(srcData instanceof MTLSurfaceData)) {
return false;
}
}
// Verify that the source surface is actually a texture and
// that the operation is supported
MTLSurfaceData mtlSrc = (MTLSurfaceData)srcData;
MTLGraphicsConfig gc = mtlSrc.getMTLGraphicsConfig();
if (mtlSrc.getType() != MTLSurfaceData.TEXTURE ||
!gc.isCapPresent(CAPS_EXT_BIOP_SHADER))
{
return false;
}
int sw = img.getWidth();
int sh = img.getHeight();
MTLBlitLoops.IsoBlit(srcData, dstData,
img, biop,
sg.composite, sg.getCompClip(),
sg.transform, sg.interpolationType,
0, 0, sw, sh,
x, y, x+sw, y+sh,
true);
return true;
}
}

View File

@@ -0,0 +1,183 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.pipe.BufferedContext;
import sun.java2d.pipe.RenderBuffer;
import sun.java2d.pipe.RenderQueue;
import sun.java2d.pipe.hw.ContextCapabilities;
import java.lang.annotation.Native;
import static sun.java2d.pipe.BufferedOpCodes.*;
/**
* Note that the RenderQueue lock must be acquired before calling any of
* the methods in this class.
*/
final class MTLContext extends BufferedContext {
public MTLContext(RenderQueue rq) {
super(rq);
}
/**
* Convenience method that delegates to setScratchSurface() below.
*/
static void setScratchSurface(MTLGraphicsConfig gc) {
setScratchSurface(gc.getNativeConfigInfo());
}
/**
* Makes the given GraphicsConfig's context current to its associated
* "scratch surface". Each GraphicsConfig maintains a native context
* (MTLDevice) as well as a native pbuffer
* known as the "scratch surface". By making the context current to the
* scratch surface, we are assured that we have a current context for
* the relevant GraphicsConfig, and can therefore perform operations
* depending on the capabilities of that GraphicsConfig.
* This method should be used for operations with an MTL texture
* as the destination surface (e.g. a sw->texture blit loop), or in those
* situations where we may not otherwise have a current context (e.g.
* when disposing a texture-based surface).
*/
public static void setScratchSurface(long pConfigInfo) {
// assert MTLRenderQueue.getInstance().lock.isHeldByCurrentThread();
// invalidate the current context
currentContext = null;
// set the scratch context
MTLRenderQueue rq = MTLRenderQueue.getInstance();
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(12, 4);
buf.putInt(SET_SCRATCH_SURFACE);
buf.putLong(pConfigInfo);
}
/**
* Invalidates the currentContext field to ensure that we properly
* revalidate the MTLContext (make it current, etc.) next time through
* the validate() method. This is typically invoked from methods
* that affect the current context state (e.g. disposing a context or
* surface).
*/
public static void invalidateCurrentContext() {
// assert MTLRenderQueue.getInstance().lock.isHeldByCurrentThread();
// invalidate the current Java-level context so that we
// revalidate everything the next time around
if (currentContext != null) {
currentContext.invalidateContext();
currentContext = null;
}
// invalidate the context reference at the native level, and
// then flush the queue so that we have no pending operations
// dependent on the current context
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.ensureCapacity(4);
rq.getBuffer().putInt(INVALIDATE_CONTEXT);
rq.flushNow();
}
/**
* Returns a string representing adapter id (vendor, renderer, version).
* Must be called on the rendering thread.
*
* @return an id string for the adapter
*/
public static final native String getMTLIdString();
public static class MTLContextCaps extends ContextCapabilities {
/**
* This cap will only be set if the fbobject system property has been
* enabled and we are able to create an FBO with depth buffer.
*/
@Native
public static final int CAPS_EXT_FBOBJECT =
(CAPS_RT_TEXTURE_ALPHA | CAPS_RT_TEXTURE_OPAQUE);
/** Indicates that the context is doublebuffered. */
@Native
public static final int CAPS_DOUBLEBUFFERED = (FIRST_PRIVATE_CAP << 0);
/**
* This cap will only be set if the lcdshader system property has been
* enabled and the hardware supports the minimum number of texture units
*/
@Native
static final int CAPS_EXT_LCD_SHADER = (FIRST_PRIVATE_CAP << 1);
/**
* This cap will only be set if the biopshader system property has been
* enabled and the hardware meets our minimum requirements.
*/
@Native
public static final int CAPS_EXT_BIOP_SHADER = (FIRST_PRIVATE_CAP << 2);
/**
* This cap will only be set if the gradshader system property has been
* enabled and the hardware meets our minimum requirements.
*/
@Native
static final int CAPS_EXT_GRAD_SHADER = (FIRST_PRIVATE_CAP << 3);
/** Indicates the presence of the GL_ARB_texture_rectangle extension. */
@Native
static final int CAPS_EXT_TEXRECT = (FIRST_PRIVATE_CAP << 4);
/** Indicates the presence of the GL_NV_texture_barrier extension. */
@Native
static final int CAPS_EXT_TEXBARRIER = (FIRST_PRIVATE_CAP << 5);
public MTLContextCaps(int caps, String adapterId) {
super(caps, adapterId);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
if ((caps & CAPS_EXT_FBOBJECT) != 0) {
sb.append("CAPS_EXT_FBOBJECT|");
}
if ((caps & CAPS_DOUBLEBUFFERED) != 0) {
sb.append("CAPS_DOUBLEBUFFERED|");
}
if ((caps & CAPS_EXT_LCD_SHADER) != 0) {
sb.append("CAPS_EXT_LCD_SHADER|");
}
if ((caps & CAPS_EXT_BIOP_SHADER) != 0) {
sb.append("CAPS_BIOP_SHADER|");
}
if ((caps & CAPS_EXT_GRAD_SHADER) != 0) {
sb.append("CAPS_EXT_GRAD_SHADER|");
}
if ((caps & CAPS_EXT_TEXRECT) != 0) {
sb.append("CAPS_EXT_TEXRECT|");
}
if ((caps & CAPS_EXT_TEXBARRIER) != 0) {
sb.append("CAPS_EXT_TEXBARRIER|");
}
return sb.toString();
}
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.loops.SurfaceType;
import sun.java2d.loops.TransformBlit;
import sun.java2d.pipe.DrawImage;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
public class MTLDrawImage extends DrawImage {
@Override
protected void renderImageXform(SunGraphics2D sg, Image img,
AffineTransform tx, int interpType,
int sx1, int sy1, int sx2, int sy2,
Color bgColor)
{
// punt to the MediaLib-based transformImage() in the superclass if:
// - bicubic interpolation is specified
// - a background color is specified and will be used
// - the source surface is neither a texture nor render-to-texture
// surface, and a non-default interpolation hint is specified
// (we can only control the filtering for texture->surface
// copies)
// REMIND: we should tweak the sw->texture->surface
// transform case to handle filtering appropriately
// (see 4841762)...
// - an appropriate TransformBlit primitive could not be found
if (interpType != AffineTransformOp.TYPE_BICUBIC) {
SurfaceData dstData = sg.surfaceData;
SurfaceData srcData =
dstData.getSourceSurfaceData(img,
SunGraphics2D.TRANSFORM_GENERIC,
sg.imageComp,
bgColor);
if (srcData != null &&
!isBgOperation(srcData, bgColor) &&
(srcData.getSurfaceType() == MTLSurfaceData.MTLTexture ||
srcData.getSurfaceType() == MTLSurfaceData.MTLSurfaceRTT ||
interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR))
{
SurfaceType srcType = srcData.getSurfaceType();
SurfaceType dstType = dstData.getSurfaceType();
TransformBlit blit = TransformBlit.getFromCache(srcType,
sg.imageComp,
dstType);
if (blit != null) {
blit.Transform(srcData, dstData,
sg.composite, sg.getCompClip(),
tx, interpType,
sx1, sy1, 0, 0, sx2-sx1, sy2-sy1);
return;
}
}
}
super.renderImageXform(sg, img, tx, interpType,
sx1, sy1, sx2, sy2, bgColor);
}
@Override
public void transformImage(SunGraphics2D sg, BufferedImage img,
BufferedImageOp op, int x, int y)
{
if (op != null) {
if (op instanceof AffineTransformOp) {
AffineTransformOp atop = (AffineTransformOp) op;
transformImage(sg, img, x, y,
atop.getTransform(),
atop.getInterpolationType());
return;
} else {
if (MTLBufImgOps.renderImageWithOp(sg, img, op, x, y)) {
return;
}
}
img = op.filter(img, null);
}
copyImage(sg, img, x, y, null);
}
}

View File

@@ -0,0 +1,404 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.awt.CGraphicsConfig;
import sun.awt.CGraphicsDevice;
import sun.awt.image.OffScreenImage;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.SurfaceManager;
import sun.java2d.Disposer;
import sun.java2d.DisposerRecord;
import sun.java2d.Surface;
import sun.java2d.SurfaceData;
import sun.java2d.pipe.hw.AccelGraphicsConfig;
import sun.java2d.pipe.hw.AccelSurface;
import sun.java2d.pipe.hw.AccelTypedVolatileImage;
import sun.java2d.pipe.hw.ContextCapabilities;
import sun.lwawt.LWComponentPeer;
import sun.lwawt.macosx.CFRetainedResource;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.io.File;
import java.security.AccessController;
import java.security.PrivilegedAction;
import static sun.java2d.opengl.OGLSurfaceData.TEXTURE;
import static sun.java2d.pipe.hw.AccelSurface.RT_TEXTURE;
import static sun.java2d.pipe.hw.ContextCapabilities.*;
import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_BIOP_SHADER;
public final class MTLGraphicsConfig extends CGraphicsConfig
implements AccelGraphicsConfig, SurfaceManager.ProxiedGraphicsConfig
{
//private static final int kOpenGLSwapInterval =
// RuntimeOptions.getCurrentOptions().OpenGLSwapInterval;
private static final int kOpenGLSwapInterval = 0; // TODO
private static boolean mtlAvailable;
private static ImageCapabilities imageCaps = new MTLImageCaps();
private static final String mtlShadersLib = AccessController.doPrivileged(
(PrivilegedAction<String>) () ->
System.getProperty("java.home", "") + File.separator +
"lib" + File.separator + "shaders.metallib");
private int pixfmt;
private BufferCapabilities bufferCaps;
private long pConfigInfo;
private ContextCapabilities mtlCaps;
private final MTLContext context;
private final Object disposerReferent = new Object();
private final int maxTextureSize;
private static native boolean initMTL();
private static native long getMTLConfigInfo(int displayID, String mtlShadersLib);
/**
* Returns GL_MAX_TEXTURE_SIZE from the shared opengl context. Must be
* called under OGLRQ lock, because this method change current context.
*
* @return GL_MAX_TEXTURE_SIZE
*/
private static native int nativeGetMaxTextureSize();
static {
mtlAvailable = initMTL();
}
private MTLGraphicsConfig(CGraphicsDevice device, int pixfmt,
long configInfo, int maxTextureSize,
ContextCapabilities mtlCaps) {
super(device);
this.pixfmt = pixfmt;
this.pConfigInfo = configInfo;
this.mtlCaps = mtlCaps;
this.maxTextureSize = maxTextureSize;
context = new MTLContext(MTLRenderQueue.getInstance());
// add a record to the Disposer so that we destroy the native
// MTLGraphicsConfigInfo data when this object goes away
Disposer.addRecord(disposerReferent,
new MTLGCDisposerRecord(pConfigInfo));
}
@Override
public Object getProxyKey() {
return this;
}
public SurfaceData createManagedSurface(int w, int h, int transparency) {
return MTLSurfaceData.createData(this, w, h,
getColorModel(transparency),
null,
MTLSurfaceData.TEXTURE);
}
public static MTLGraphicsConfig getConfig(CGraphicsDevice device,
int displayID, int pixfmt)
{
if (!mtlAvailable) {
return null;
}
long cfginfo = 0;
int textureSize = 0;
final String[] ids = new String[1];
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
// getCGLConfigInfo() creates and destroys temporary
// surfaces/contexts, so we should first invalidate the current
// Java-level context and flush the queue...
MTLContext.invalidateCurrentContext();
cfginfo = getMTLConfigInfo(displayID, mtlShadersLib);
if (cfginfo != 0L) {
textureSize = nativeGetMaxTextureSize();
// 7160609: GL still fails to create a square texture of this
// size. Half should be safe enough.
// Explicitly not support a texture more than 2^14, see 8010999.
textureSize = textureSize <= 16384 ? textureSize / 2 : 8192;
MTLContext.setScratchSurface(cfginfo);
rq.flushAndInvokeNow(() -> {
ids[0] = MTLContext.getMTLIdString();
});
}
} finally {
rq.unlock();
}
if (cfginfo == 0) {
return null;
}
ContextCapabilities caps = new MTLContext.MTLContextCaps(
CAPS_PS30 | CAPS_PS20 | CAPS_RT_PLAIN_ALPHA |
CAPS_RT_TEXTURE_ALPHA | CAPS_RT_TEXTURE_OPAQUE |
CAPS_MULTITEXTURE | CAPS_TEXNONPOW2 | CAPS_TEXNONSQUARE | CAPS_EXT_BIOP_SHADER,
ids[0]);
return new MTLGraphicsConfig(device, pixfmt, cfginfo, textureSize, caps);
}
/**
* Returns true if the provided capability bit is present for this config.
* See MTLContext.java for a list of supported capabilities.
*/
public boolean isCapPresent(int cap) {
return ((mtlCaps.getCaps() & cap) != 0);
}
public long getNativeConfigInfo() {
return pConfigInfo;
}
/**
* {@inheritDoc}
*
* @see sun.java2d.pipe.hw.BufferedContextProvider#getContext
*/
@Override
public MTLContext getContext() {
return context;
}
@Override
public BufferedImage createCompatibleImage(int width, int height) {
ColorModel model = new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
WritableRaster
raster = model.createCompatibleWritableRaster(width, height);
return new BufferedImage(model, raster, model.isAlphaPremultiplied(),
null);
}
@Override
public ColorModel getColorModel(int transparency) {
switch (transparency) {
case Transparency.OPAQUE:
// REMIND: once the ColorModel spec is changed, this should be
// an opaque premultiplied DCM...
return new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
case Transparency.BITMASK:
return new DirectColorModel(25, 0xff0000, 0xff00, 0xff, 0x1000000);
case Transparency.TRANSLUCENT:
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
return new DirectColorModel(cs, 32,
0xff0000, 0xff00, 0xff, 0xff000000,
true, DataBuffer.TYPE_INT);
default:
return null;
}
}
public boolean isDoubleBuffered() {
return true;
}
private static class MTLGCDisposerRecord implements DisposerRecord {
private long pCfgInfo;
public MTLGCDisposerRecord(long pCfgInfo) {
this.pCfgInfo = pCfgInfo;
}
public void dispose() {
if (pCfgInfo != 0) {
MTLRenderQueue.disposeGraphicsConfig(pCfgInfo);
pCfgInfo = 0;
}
}
}
// TODO: CGraphicsConfig doesn't implement displayChanged() yet
//@Override
public synchronized void displayChanged() {
//super.displayChanged();
// the context could hold a reference to a MTLSurfaceData, which in
// turn has a reference back to this MTLGraphicsConfig, so in order
// for this instance to be disposed we need to break the connection
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
MTLContext.invalidateCurrentContext();
} finally {
rq.unlock();
}
}
@Override
public String toString() {
return ("MTLGraphicsConfig[" + getDevice().getIDstring() +
",pixfmt="+pixfmt+"]");
}
@Override
public SurfaceData createSurfaceData(CFRetainedResource layer) {
return MTLSurfaceData.createData((MTLLayer) layer);
}
@Override
public Image createAcceleratedImage(Component target,
int width, int height)
{
ColorModel model = getColorModel(Transparency.OPAQUE);
WritableRaster wr = model.createCompatibleWritableRaster(width, height);
return new OffScreenImage(target, model, wr,
model.isAlphaPremultiplied());
}
@Override
public void assertOperationSupported(final int numBuffers,
final BufferCapabilities caps)
throws AWTException {
// Assume this method is never called with numBuffers != 2, as 0 is
// unsupported, and 1 corresponds to a SingleBufferStrategy which
// doesn't depend on the peer. Screen is considered as a separate
// "buffer".
if (numBuffers != 2) {
throw new AWTException("Only double buffering is supported");
}
final BufferCapabilities configCaps = getBufferCapabilities();
if (!configCaps.isPageFlipping()) {
throw new AWTException("Page flipping is not supported");
}
if (caps.getFlipContents() == BufferCapabilities.FlipContents.PRIOR) {
throw new AWTException("FlipContents.PRIOR is not supported");
}
}
@Override
public Image createBackBuffer(final LWComponentPeer<?, ?> peer) {
final Rectangle r = peer.getBounds();
// It is possible for the component to have size 0x0, adjust it to
// be at least 1x1 to avoid IAE
final int w = Math.max(1, r.width);
final int h = Math.max(1, r.height);
final int transparency = peer.isTranslucent() ? Transparency.TRANSLUCENT
: Transparency.OPAQUE;
return new SunVolatileImage(this, w, h, transparency, null);
}
@Override
public void destroyBackBuffer(final Image backBuffer) {
if (backBuffer != null) {
backBuffer.flush();
}
}
@Override
public void flip(final LWComponentPeer<?, ?> peer, final Image backBuffer,
final int x1, final int y1, final int x2, final int y2,
final BufferCapabilities.FlipContents flipAction) {
final Graphics g = peer.getGraphics();
try {
g.drawImage(backBuffer, x1, y1, x2, y2, x1, y1, x2, y2, null);
} finally {
g.dispose();
}
if (flipAction == BufferCapabilities.FlipContents.BACKGROUND) {
final Graphics2D bg = (Graphics2D) backBuffer.getGraphics();
try {
bg.setBackground(peer.getBackground());
bg.clearRect(0, 0, backBuffer.getWidth(null),
backBuffer.getHeight(null));
} finally {
bg.dispose();
}
}
}
private static class MTLBufferCaps extends BufferCapabilities {
public MTLBufferCaps(boolean dblBuf) {
super(imageCaps, imageCaps,
dblBuf ? FlipContents.UNDEFINED : null);
}
}
@Override
public BufferCapabilities getBufferCapabilities() {
if (bufferCaps == null) {
bufferCaps = new MTLBufferCaps(isDoubleBuffered());
}
return bufferCaps;
}
private static class MTLImageCaps extends ImageCapabilities {
private MTLImageCaps() {
super(true);
}
public boolean isTrueVolatile() {
return true;
}
}
@Override
public ImageCapabilities getImageCapabilities() {
return imageCaps;
}
@Override
public VolatileImage createCompatibleVolatileImage(int width, int height,
int transparency,
int type) {
if (type != RT_TEXTURE && type != TEXTURE) {
return null;
}
SunVolatileImage vi = new AccelTypedVolatileImage(this, width, height,
transparency, type);
Surface sd = vi.getDestSurface();
if (!(sd instanceof AccelSurface) ||
((AccelSurface)sd).getType() != type)
{
vi.flush();
vi = null;
}
return vi;
}
/**
* {@inheritDoc}
*
* @see sun.java2d.pipe.hw.AccelGraphicsConfig#getContextCapabilities
*/
@Override
public ContextCapabilities getContextCapabilities() {
return mtlCaps;
}
@Override
public int getMaxTextureWidth() {
return Math.max(maxTextureSize / getDevice().getScaleFactor(),
getBounds().width);
}
@Override
public int getMaxTextureHeight() {
return Math.max(maxTextureSize / getDevice().getScaleFactor(),
getBounds().height);
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.NullSurfaceData;
import sun.java2d.SurfaceData;
import sun.lwawt.LWWindowPeer;
import sun.lwawt.macosx.CFRetainedResource;
import java.awt.*;
public class MTLLayer extends CFRetainedResource {
private native long nativeCreateLayer();
private static native void nativeSetScale(long layerPtr, double scale);
private static native void nativeSetInsets(long layerPtr, int top, int left);
private static native void validate(long layerPtr, MTLSurfaceData cglsd);
private static native void blitTexture(long layerPtr);
private LWWindowPeer peer;
private int scale = 1;
private SurfaceData surfaceData; // represents intermediate buffer (texture)
public MTLLayer(LWWindowPeer peer) {
super(0, true);
setPtr(nativeCreateLayer());
this.peer = peer;
}
public long getPointer() {
return ptr;
}
public Rectangle getBounds() {
return peer.getBounds();
}
public GraphicsConfiguration getGraphicsConfiguration() {
return peer.getGraphicsConfiguration();
}
public boolean isOpaque() {
return !peer.isTranslucent();
}
public int getTransparency() {
return isOpaque() ? Transparency.OPAQUE : Transparency.TRANSLUCENT;
}
public Object getDestination() {
return peer.getTarget();
}
public SurfaceData replaceSurfaceData() {
if (getBounds().isEmpty()) {
surfaceData = NullSurfaceData.theInstance;
return surfaceData;
}
// the layer redirects all painting to the buffer's graphics
// and blits the buffer to the layer surface (in drawInCGLContext callback)
MTLGraphicsConfig gc = (MTLGraphicsConfig)getGraphicsConfiguration();
surfaceData = gc.createSurfaceData(this);
setScale(gc.getDevice().getScaleFactor());
Insets insets = peer.getInsets();
execute(ptr -> nativeSetInsets(ptr, insets.top, insets.left));
// the layer holds a reference to the buffer, which in
// turn has a reference back to this layer
if (surfaceData instanceof MTLSurfaceData) {
validate((MTLSurfaceData)surfaceData);
}
return surfaceData;
}
public SurfaceData getSurfaceData() {
return surfaceData;
}
public void validate(final MTLSurfaceData cglsd) {
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
execute(ptr -> validate(ptr, cglsd));
} finally {
rq.unlock();
}
}
@Override
public void dispose() {
// break the connection between the layer and the buffer
validate(null);
SurfaceData oldData = surfaceData;
surfaceData = NullSurfaceData.theInstance;;
if (oldData != null) {
oldData.flush();
}
super.dispose();
}
private void setScale(final int _scale) {
if (scale != _scale) {
scale = _scale;
execute(ptr -> nativeSetScale(ptr, scale));
}
}
// ----------------------------------------------------------------------
// NATIVE CALLBACKS
// ----------------------------------------------------------------------
private void drawInMTLContext() {
// tell the flusher thread not to update the intermediate buffer
// until we are done blitting from it
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
execute(ptr -> blitTexture(ptr));
} finally {
rq.unlock();
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.SurfaceData;
import sun.java2d.loops.CompositeType;
import sun.java2d.loops.GraphicsPrimitive;
import sun.java2d.loops.GraphicsPrimitiveMgr;
import sun.java2d.loops.SurfaceType;
import sun.java2d.pipe.BufferedMaskBlit;
import sun.java2d.pipe.Region;
import java.awt.*;
import static sun.java2d.loops.CompositeType.SrcNoEa;
import static sun.java2d.loops.CompositeType.SrcOver;
import static sun.java2d.loops.SurfaceType.*;
class MTLMaskBlit extends BufferedMaskBlit {
static void register() {
GraphicsPrimitive[] primitives = {
new MTLMaskBlit(IntArgb, SrcOver),
new MTLMaskBlit(IntArgbPre, SrcOver),
new MTLMaskBlit(IntRgb, SrcOver),
new MTLMaskBlit(IntRgb, SrcNoEa),
new MTLMaskBlit(IntBgr, SrcOver),
new MTLMaskBlit(IntBgr, SrcNoEa),
};
GraphicsPrimitiveMgr.register(primitives);
}
private MTLMaskBlit(SurfaceType srcType,
CompositeType compType)
{
super(MTLRenderQueue.getInstance(),
srcType, compType, MTLSurfaceData.MTLSurface);
}
@Override
protected void validateContext(SurfaceData dstData,
Composite comp, Region clip)
{
MTLSurfaceData oglDst = (MTLSurfaceData)dstData;
MTLContext.validateContext(oglDst, oglDst,
clip, comp, null, null, null,
MTLContext.NO_CONTEXT_FLAGS);
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.InvalidPipeException;
import sun.java2d.SunGraphics2D;
import sun.java2d.loops.CompositeType;
import sun.java2d.loops.GraphicsPrimitive;
import sun.java2d.loops.GraphicsPrimitiveMgr;
import sun.java2d.loops.SurfaceType;
import sun.java2d.pipe.BufferedMaskFill;
import java.awt.*;
import static sun.java2d.loops.CompositeType.SrcNoEa;
import static sun.java2d.loops.CompositeType.SrcOver;
import static sun.java2d.loops.SurfaceType.*;
class MTLMaskFill extends BufferedMaskFill {
static void register() {
GraphicsPrimitive[] primitives = {
new MTLMaskFill(AnyColor, SrcOver),
new MTLMaskFill(OpaqueColor, SrcNoEa),
new MTLMaskFill(GradientPaint, SrcOver),
new MTLMaskFill(OpaqueGradientPaint, SrcNoEa),
new MTLMaskFill(LinearGradientPaint, SrcOver),
new MTLMaskFill(OpaqueLinearGradientPaint, SrcNoEa),
new MTLMaskFill(RadialGradientPaint, SrcOver),
new MTLMaskFill(OpaqueRadialGradientPaint, SrcNoEa),
new MTLMaskFill(TexturePaint, SrcOver),
new MTLMaskFill(OpaqueTexturePaint, SrcNoEa),
};
GraphicsPrimitiveMgr.register(primitives);
}
protected MTLMaskFill(SurfaceType srcType, CompositeType compType) {
super(MTLRenderQueue.getInstance(),
srcType, compType, MTLSurfaceData.MTLSurface);
}
@Override
protected native void maskFill(int x, int y, int w, int h,
int maskoff, int maskscan, int masklen,
byte[] mask);
@Override
protected void validateContext(SunGraphics2D sg2d,
Composite comp, int ctxflags)
{
MTLSurfaceData dstData;
try {
dstData = (MTLSurfaceData) sg2d.surfaceData;
} catch (ClassCastException e) {
throw new InvalidPipeException("wrong surface data type: " +
sg2d.surfaceData);
}
MTLContext.validateContext(dstData, dstData,
sg2d.getCompClip(), comp,
null, sg2d.paint, sg2d, ctxflags);
}
}

View File

@@ -0,0 +1,197 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.loops.CompositeType;
import java.awt.*;
import java.awt.MultipleGradientPaint.ColorSpaceType;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_GRAD_SHADER;
import static sun.java2d.pipe.BufferedPaints.MULTI_MAX_FRACTIONS;
abstract class MTLPaints {
/**
* Holds all registered implementations, using the corresponding
* SunGraphics2D.PAINT_* constant as the hash key.
*/
private static Map<Integer, MTLPaints> impls =
new HashMap<Integer, MTLPaints>(4, 1.0f);
static {
impls.put(SunGraphics2D.PAINT_GRADIENT, new Gradient());
impls.put(SunGraphics2D.PAINT_LIN_GRADIENT, new LinearGradient());
impls.put(SunGraphics2D.PAINT_RAD_GRADIENT, new RadialGradient());
impls.put(SunGraphics2D.PAINT_TEXTURE, new Texture());
}
/**
* Attempts to locate an implementation corresponding to the paint state
* of the provided SunGraphics2D object. If no implementation can be
* found, or if the paint cannot be accelerated under the conditions
* of the SunGraphics2D, this method returns false; otherwise, returns
* true.
*/
static boolean isValid(SunGraphics2D sg2d) {
MTLPaints impl = impls.get(sg2d.paintState);
return (impl != null && impl.isPaintValid(sg2d));
}
/**
* Returns true if this implementation is able to accelerate the
* Paint object associated with, and under the conditions of, the
* provided SunGraphics2D instance; otherwise returns false.
*/
abstract boolean isPaintValid(SunGraphics2D sg2d);
/************************* GradientPaint support ****************************/
private static class Gradient extends MTLPaints {
private Gradient() {}
/**
* There are no restrictions for accelerating GradientPaint, so
* this method always returns true.
*/
@Override
boolean isPaintValid(SunGraphics2D sg2d) {
return true;
}
}
/************************** TexturePaint support ****************************/
private static class Texture extends MTLPaints {
private Texture() {}
/**
* Returns true if the given TexturePaint instance can be used by the
* accelerated MTLPaints.Texture implementation. A TexturePaint is
* considered valid if the following conditions are met:
* - the texture image dimensions are power-of-two (or the
* GL_ARB_texture_non_power_of_two extension is present)
* - the texture image can be (or is already) cached in an OpenGL
* texture object
*/
@Override
boolean isPaintValid(SunGraphics2D sg2d) {
TexturePaint paint = (TexturePaint)sg2d.paint;
MTLSurfaceData dstData = (MTLSurfaceData)sg2d.surfaceData;
BufferedImage bi = paint.getImage();
SurfaceData srcData =
dstData.getSourceSurfaceData(bi,
SunGraphics2D.TRANSFORM_ISIDENT,
CompositeType.SrcOver, null);
if (!(srcData instanceof MTLSurfaceData)) {
// REMIND: this is a hack that attempts to cache the system
// memory image from the TexturePaint instance into an
// OpenGL texture...
srcData =
dstData.getSourceSurfaceData(bi,
SunGraphics2D.TRANSFORM_ISIDENT,
CompositeType.SrcOver, null);
if (!(srcData instanceof MTLSurfaceData)) {
return false;
}
}
// verify that the source surface is actually a texture
MTLSurfaceData oglData = (MTLSurfaceData)srcData;
if (oglData.getType() != MTLSurfaceData.TEXTURE) {
return false;
}
return true;
}
}
/****************** Shared MultipleGradientPaint support ********************/
private abstract static class MultiGradient extends MTLPaints {
protected MultiGradient() {}
/**
* Returns true if the given MultipleGradientPaint instance can be
* used by the accelerated MTLPaints.MultiGradient implementation.
* A MultipleGradientPaint is considered valid if the following
* conditions are met:
* - the number of gradient "stops" is <= MAX_FRACTIONS
* - the destination has support for fragment shaders
*/
@Override
boolean isPaintValid(SunGraphics2D sg2d) {
MultipleGradientPaint paint = (MultipleGradientPaint)sg2d.paint;
// REMIND: ugh, this creates garbage; would be nicer if
// we had a MultipleGradientPaint.getNumStops() method...
if (paint.getFractions().length > MULTI_MAX_FRACTIONS) {
return false;
}
MTLSurfaceData dstData = (MTLSurfaceData)sg2d.surfaceData;
MTLGraphicsConfig gc = dstData.getMTLGraphicsConfig();
if (!gc.isCapPresent(CAPS_EXT_GRAD_SHADER)) {
return false;
}
return true;
}
}
/********************** LinearGradientPaint support *************************/
private static class LinearGradient extends MultiGradient {
private LinearGradient() {}
@Override
boolean isPaintValid(SunGraphics2D sg2d) {
LinearGradientPaint paint = (LinearGradientPaint)sg2d.paint;
if (paint.getFractions().length == 2 &&
paint.getCycleMethod() != CycleMethod.REPEAT &&
paint.getColorSpace() != ColorSpaceType.LINEAR_RGB)
{
// we can delegate to the optimized two-color gradient
// codepath, which does not require fragment shader support
return true;
}
return super.isPaintValid(sg2d);
}
}
/********************** RadialGradientPaint support *************************/
private static class RadialGradient extends MultiGradient {
private RadialGradient() {}
}
}

View File

@@ -0,0 +1,254 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.awt.util.ThreadGroupUtils;
import sun.java2d.pipe.RenderBuffer;
import sun.java2d.pipe.RenderQueue;
import java.security.AccessController;
import java.security.PrivilegedAction;
import static sun.java2d.pipe.BufferedOpCodes.DISPOSE_CONFIG;
import static sun.java2d.pipe.BufferedOpCodes.SYNC;
/**
* OGL-specific implementation of RenderQueue. This class provides a
* single (daemon) thread that is responsible for periodically flushing
* the queue, thus ensuring that only one thread communicates with the native
* OpenGL libraries for the entire process.
*/
public class MTLRenderQueue extends RenderQueue {
private static MTLRenderQueue theInstance;
private final QueueFlusher flusher;
private MTLRenderQueue() {
/*
* The thread must be a member of a thread group
* which will not get GCed before VM exit.
*/
flusher = AccessController.doPrivileged((PrivilegedAction<QueueFlusher>) QueueFlusher::new);
}
/**
* Returns the single MTLRenderQueue instance. If it has not yet been
* initialized, this method will first construct the single instance
* before returning it.
*/
public static synchronized MTLRenderQueue getInstance() {
if (theInstance == null) {
theInstance = new MTLRenderQueue();
}
return theInstance;
}
/**
* Flushes the single MTLRenderQueue instance synchronously. If an
* MTLRenderQueue has not yet been instantiated, this method is a no-op.
* This method is useful in the case of Toolkit.sync(), in which we want
* to flush the OGL pipeline, but only if the OGL pipeline is currently
* enabled. Since this class has few external dependencies, callers need
* not be concerned that calling this method will trigger initialization
* of the OGL pipeline and related classes.
*/
public static void sync() {
if (theInstance != null) {
theInstance.lock();
try {
theInstance.ensureCapacity(4);
theInstance.getBuffer().putInt(SYNC);
theInstance.flushNow();
} finally {
theInstance.unlock();
}
}
}
/**
* Disposes the native memory associated with the given native
* graphics config info pointer on the single queue flushing thread.
*/
public static void disposeGraphicsConfig(long pConfigInfo) {
MTLRenderQueue rq = getInstance();
rq.lock();
try {
// make sure we make the context associated with the given
// GraphicsConfig current before disposing the native resources
MTLContext.setScratchSurface(pConfigInfo);
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(12, 4);
buf.putInt(DISPOSE_CONFIG);
buf.putLong(pConfigInfo);
// this call is expected to complete synchronously, so flush now
rq.flushNow();
} finally {
rq.unlock();
}
}
/**
* Returns true if the current thread is the OGL QueueFlusher thread.
*/
public static boolean isQueueFlusherThread() {
return (Thread.currentThread() == getInstance().flusher.thread);
}
@Override
public void flushNow() {
// assert lock.isHeldByCurrentThread();
try {
flusher.flushNow();
} catch (Exception e) {
System.err.println("exception in flushNow:");
e.printStackTrace();
}
}
public void flushAndInvokeNow(Runnable r) {
// assert lock.isHeldByCurrentThread();
try {
flusher.flushAndInvokeNow(r);
} catch (Exception e) {
System.err.println("exception in flushAndInvokeNow:");
e.printStackTrace();
}
}
private native void flushBuffer(long buf, int limit);
private void flushBuffer() {
// assert lock.isHeldByCurrentThread();
int limit = buf.position();
if (limit > 0) {
// process the queue
flushBuffer(buf.getAddress(), limit);
}
// reset the buffer position
buf.clear();
// clear the set of references, since we no longer need them
refSet.clear();
}
private class QueueFlusher implements Runnable {
private boolean needsFlush;
private Runnable task;
private Error error;
private final Thread thread;
public QueueFlusher() {
String name = "Java2D Queue Flusher";
thread = new Thread(ThreadGroupUtils.getRootThreadGroup(),
this, name, 0, false);
thread.setDaemon(true);
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
public synchronized void flushNow() {
// wake up the flusher
needsFlush = true;
notify();
// wait for flush to complete
while (needsFlush) {
try {
wait();
} catch (InterruptedException e) {
}
}
// re-throw any error that may have occurred during the flush
if (error != null) {
throw error;
}
}
public synchronized void flushAndInvokeNow(Runnable task) {
this.task = task;
flushNow();
}
public synchronized void run() {
boolean timedOut = false;
while (true) {
while (!needsFlush) {
try {
timedOut = false;
/*
* Wait until we're woken up with a flushNow() call,
* or the timeout period elapses (so that we can
* flush the queue periodically).
*/
wait(100);
/*
* We will automatically flush the queue if the
* following conditions apply:
* - the wait() timed out
* - we can lock the queue (without blocking)
* - there is something in the queue to flush
* Otherwise, just continue (we'll flush eventually).
*/
if (!needsFlush && (timedOut = tryLock())) {
if (buf.position() > 0) {
needsFlush = true;
} else {
unlock();
}
}
} catch (InterruptedException e) {
}
}
try {
// reset the throwable state
error = null;
// flush the buffer now
flushBuffer();
// if there's a task, invoke that now as well
if (task != null) {
task.run();
}
} catch (Error e) {
error = e;
} catch (Exception x) {
System.err.println("exception in QueueFlusher:");
x.printStackTrace();
} finally {
if (timedOut) {
unlock();
}
task = null;
// allow the waiting thread to continue
needsFlush = false;
notify();
}
}
}
}
}

View File

@@ -0,0 +1,223 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.InvalidPipeException;
import sun.java2d.SunGraphics2D;
import sun.java2d.loops.GraphicsPrimitive;
import sun.java2d.pipe.BufferedRenderPipe;
import sun.java2d.pipe.ParallelogramPipe;
import sun.java2d.pipe.RenderQueue;
import sun.java2d.pipe.SpanIterator;
import java.awt.*;
import java.awt.geom.Path2D;
import static sun.java2d.pipe.BufferedOpCodes.COPY_AREA;
class MTLRenderer extends BufferedRenderPipe {
MTLRenderer(RenderQueue rq) {
super(rq);
}
@Override
protected void validateContext(SunGraphics2D sg2d) {
int ctxflags =
sg2d.paint.getTransparency() == Transparency.OPAQUE ?
MTLContext.SRC_IS_OPAQUE : MTLContext.NO_CONTEXT_FLAGS;
MTLSurfaceData dstData;
try {
dstData = (MTLSurfaceData)sg2d.surfaceData;
} catch (ClassCastException e) {
throw new InvalidPipeException("wrong surface data type: " + sg2d.surfaceData);
}
MTLContext.validateContext(dstData, dstData,
sg2d.getCompClip(), sg2d.composite,
null, sg2d.paint, sg2d, ctxflags);
}
@Override
protected void validateContextAA(SunGraphics2D sg2d) {
int ctxflags = MTLContext.NO_CONTEXT_FLAGS;
MTLSurfaceData dstData;
try {
dstData = (MTLSurfaceData)sg2d.surfaceData;
} catch (ClassCastException e) {
throw new InvalidPipeException("wrong surface data type: " + sg2d.surfaceData);
}
MTLContext.validateContext(dstData, dstData,
sg2d.getCompClip(), sg2d.composite,
null, sg2d.paint, sg2d, ctxflags);
}
void copyArea(SunGraphics2D sg2d,
int x, int y, int w, int h, int dx, int dy)
{
rq.lock();
try {
int ctxflags =
sg2d.surfaceData.getTransparency() == Transparency.OPAQUE ?
MTLContext.SRC_IS_OPAQUE : MTLContext.NO_CONTEXT_FLAGS;
MTLSurfaceData dstData;
try {
dstData = (MTLSurfaceData)sg2d.surfaceData;
} catch (ClassCastException e) {
throw new InvalidPipeException("wrong surface data type: " + sg2d.surfaceData);
}
MTLContext.validateContext(dstData, dstData,
sg2d.getCompClip(), sg2d.composite,
null, null, null, ctxflags);
rq.ensureCapacity(28);
buf.putInt(COPY_AREA);
buf.putInt(x).putInt(y).putInt(w).putInt(h);
buf.putInt(dx).putInt(dy);
} finally {
rq.unlock();
}
}
@Override
protected native void drawPoly(int[] xPoints, int[] yPoints,
int nPoints, boolean isClosed,
int transX, int transY);
MTLRenderer traceWrap() {
return new Tracer(this);
}
private class Tracer extends MTLRenderer {
private MTLRenderer mtlr;
Tracer(MTLRenderer mtlr) {
super(mtlr.rq);
this.mtlr = mtlr;
}
public ParallelogramPipe getAAParallelogramPipe() {
final ParallelogramPipe realpipe = mtlr.getAAParallelogramPipe();
return new ParallelogramPipe() {
public void fillParallelogram(SunGraphics2D sg2d,
double ux1, double uy1,
double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2)
{
GraphicsPrimitive.tracePrimitive("MTLFillAAParallelogram");
realpipe.fillParallelogram(sg2d,
ux1, uy1, ux2, uy2,
x, y, dx1, dy1, dx2, dy2);
}
public void drawParallelogram(SunGraphics2D sg2d,
double ux1, double uy1,
double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2,
double lw1, double lw2)
{
GraphicsPrimitive.tracePrimitive("MTLDrawAAParallelogram");
realpipe.drawParallelogram(sg2d,
ux1, uy1, ux2, uy2,
x, y, dx1, dy1, dx2, dy2,
lw1, lw2);
}
};
}
protected void validateContext(SunGraphics2D sg2d) {
mtlr.validateContext(sg2d);
}
public void drawLine(SunGraphics2D sg2d,
int x1, int y1, int x2, int y2)
{
GraphicsPrimitive.tracePrimitive("MTLDrawLine");
mtlr.drawLine(sg2d, x1, y1, x2, y2);
}
public void drawRect(SunGraphics2D sg2d, int x, int y, int w, int h) {
GraphicsPrimitive.tracePrimitive("MTLDrawRect");
mtlr.drawRect(sg2d, x, y, w, h);
}
protected void drawPoly(SunGraphics2D sg2d,
int[] xPoints, int[] yPoints,
int nPoints, boolean isClosed)
{
GraphicsPrimitive.tracePrimitive("MTLDrawPoly");
mtlr.drawPoly(sg2d, xPoints, yPoints, nPoints, isClosed);
}
public void fillRect(SunGraphics2D sg2d, int x, int y, int w, int h) {
GraphicsPrimitive.tracePrimitive("MTLFillRect");
mtlr.fillRect(sg2d, x, y, w, h);
}
protected void drawPath(SunGraphics2D sg2d,
Path2D.Float p2df, int transx, int transy)
{
GraphicsPrimitive.tracePrimitive("MTLDrawPath");
mtlr.drawPath(sg2d, p2df, transx, transy);
}
protected void fillPath(SunGraphics2D sg2d,
Path2D.Float p2df, int transx, int transy)
{
GraphicsPrimitive.tracePrimitive("MTLFillPath");
mtlr.fillPath(sg2d, p2df, transx, transy);
}
protected void fillSpans(SunGraphics2D sg2d, SpanIterator si,
int transx, int transy)
{
GraphicsPrimitive.tracePrimitive("MTLFillSpans");
mtlr.fillSpans(sg2d, si, transx, transy);
}
public void fillParallelogram(SunGraphics2D sg2d,
double ux1, double uy1,
double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2)
{
GraphicsPrimitive.tracePrimitive("MTLFillParallelogram");
mtlr.fillParallelogram(sg2d,
ux1, uy1, ux2, uy2,
x, y, dx1, dy1, dx2, dy2);
}
public void drawParallelogram(SunGraphics2D sg2d,
double ux1, double uy1,
double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2,
double lw1, double lw2)
{
GraphicsPrimitive.tracePrimitive("MTLDrawParallelogram");
mtlr.drawParallelogram(sg2d,
ux1, uy1, ux2, uy2,
x, y, dx1, dy1, dx2, dy2, lw1, lw2);
}
public void copyArea(SunGraphics2D sg2d,
int x, int y, int w, int h, int dx, int dy)
{
GraphicsPrimitive.tracePrimitive("MTLCopyArea");
mtlr.copyArea(sg2d, x, y, w, h, dx, dy);
}
}
}

View File

@@ -0,0 +1,695 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.awt.SunHints;
import sun.awt.image.PixelConverter;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.SurfaceDataProxy;
import sun.java2d.loops.CompositeType;
import sun.java2d.loops.GraphicsPrimitive;
import sun.java2d.loops.MaskFill;
import sun.java2d.loops.SurfaceType;
import sun.java2d.pipe.ParallelogramPipe;
import sun.java2d.pipe.PixelToParallelogramConverter;
import sun.java2d.pipe.RenderBuffer;
import sun.java2d.pipe.TextPipe;
import sun.java2d.pipe.hw.AccelSurface;
import java.awt.*;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_LCD_SHADER;
import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_TEXRECT;
import static sun.java2d.pipe.BufferedOpCodes.*;
import static sun.java2d.pipe.hw.ContextCapabilities.*;
public abstract class MTLSurfaceData extends SurfaceData
implements AccelSurface {
/**
* Pixel formats
*/
public static final int PF_INT_ARGB = 0;
public static final int PF_INT_ARGB_PRE = 1;
public static final int PF_INT_RGB = 2;
public static final int PF_INT_RGBX = 3;
public static final int PF_INT_BGR = 4;
public static final int PF_INT_BGRX = 5;
public static final int PF_USHORT_565_RGB = 6;
public static final int PF_USHORT_555_RGB = 7;
public static final int PF_USHORT_555_RGBX = 8;
public static final int PF_BYTE_GRAY = 9;
public static final int PF_USHORT_GRAY = 10;
public static final int PF_3BYTE_BGR = 11;
/**
* SurfaceTypes
*/
private static final String DESC_MTL_SURFACE = "MTL Surface";
private static final String DESC_MTL_SURFACE_RTT =
"MTL Surface (render-to-texture)";
private static final String DESC_MTL_TEXTURE = "MTL Texture";
static final SurfaceType MTLSurface =
SurfaceType.Any.deriveSubType(DESC_MTL_SURFACE,
PixelConverter.ArgbPre.instance);
static final SurfaceType MTLSurfaceRTT =
MTLSurface.deriveSubType(DESC_MTL_SURFACE_RTT);
static final SurfaceType MTLTexture =
SurfaceType.Any.deriveSubType(DESC_MTL_TEXTURE);
protected static MTLRenderer mtlRenderPipe;
protected static PixelToParallelogramConverter mtlTxRenderPipe;
protected static ParallelogramPipe mtlAAPgramPipe;
protected static MTLTextRenderer mtlTextPipe;
protected static MTLDrawImage mtlImagePipe;
/** This will be true if the fbobject system property has been enabled. */
private static boolean isFBObjectEnabled;
/** This will be true if the lcdshader system property has been enabled.*/
private static boolean isLCDShaderEnabled;
/** This will be true if the biopshader system property has been enabled.*/
private static boolean isBIOpShaderEnabled;
/** This will be true if the gradshader system property has been enabled.*/
private static boolean isGradShaderEnabled;
static {
if (!GraphicsEnvironment.isHeadless()) {
// fbobject currently enabled by default; use "false" to disable
String fbo = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"java2d.metal.fbobject"));
isFBObjectEnabled = !"false".equals(fbo);
// lcdshader currently enabled by default; use "false" to disable
String lcd = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"java2d.metal.lcdshader"));
isLCDShaderEnabled = !"false".equals(lcd);
// biopshader currently enabled by default; use "false" to disable
String biop = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"java2d.metal.biopshader"));
isBIOpShaderEnabled = !"false".equals(biop);
// gradshader currently enabled by default; use "false" to disable
String grad = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"java2d.metal.gradshader"));
isGradShaderEnabled = !"false".equals(grad);
MTLRenderQueue rq = MTLRenderQueue.getInstance();
mtlImagePipe = new MTLDrawImage();
mtlTextPipe = new MTLTextRenderer(rq);
mtlRenderPipe = new MTLRenderer(rq);
if (GraphicsPrimitive.tracingEnabled()) {
mtlTextPipe = mtlTextPipe.traceWrap();
//The wrapped mtlRenderPipe will wrap the AA pipe as well...
//mtlAAPgramPipe = mtlRenderPipe.traceWrap();
}
mtlAAPgramPipe = mtlRenderPipe.getAAParallelogramPipe();
mtlTxRenderPipe =
new PixelToParallelogramConverter(mtlRenderPipe,
mtlRenderPipe,
1.0, 0.25, true);
MTLBlitLoops.register();
MTLMaskFill.register();
MTLMaskBlit.register();
}
}
protected final int scale;
protected final int width;
protected final int height;
protected int type;
private MTLGraphicsConfig graphicsConfig;
// these fields are set from the native code when the surface is
// initialized
private int nativeWidth;
private int nativeHeight;
/**
* Returns the appropriate SurfaceType corresponding to the given OpenGL
* surface type constant (e.g. TEXTURE -> MTLTexture).
*/
private static SurfaceType getCustomSurfaceType(int oglType) {
switch (oglType) {
case TEXTURE:
return MTLTexture;
case RT_TEXTURE:
return MTLSurfaceRTT;
default:
return MTLSurface;
}
}
static void swapBuffers(long window) {
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(12, 4);
buf.putInt(SWAP_BUFFERS);
buf.putLong(window);
rq.flushNow();
} finally {
rq.unlock();
}
}
private native void initOps(MTLGraphicsConfig gc, long pConfigInfo, long pPeerData, long layerPtr,
int xoff, int yoff, boolean isOpaque);
private MTLSurfaceData(MTLLayer layer, MTLGraphicsConfig gc,
ColorModel cm, int type, int width, int height)
{
super(getCustomSurfaceType(type), cm);
this.graphicsConfig = gc;
this.type = type;
setBlitProxyKey(gc.getProxyKey());
// TEXTURE shouldn't be scaled, it is used for managed BufferedImages.
scale = type == TEXTURE ? 1 : gc.getDevice().getScaleFactor();
this.width = width * scale;
this.height = height * scale;
long pConfigInfo = gc.getNativeConfigInfo();
long layerPtr = 0L;
boolean isOpaque = true;
if (layer != null) {
layerPtr = layer.getPointer();
isOpaque = layer.isOpaque();
}
initOps(gc, pConfigInfo, 0, layerPtr, 0, 0, isOpaque);
}
@Override //SurfaceData
public GraphicsConfiguration getDeviceConfiguration() {
return graphicsConfig;
}
/**
* Creates a SurfaceData object representing the intermediate buffer
* between the Java2D flusher thread and the AppKit thread.
*/
public static MTLLayerSurfaceData createData(MTLLayer layer) {
MTLGraphicsConfig gc = (MTLGraphicsConfig)layer.getGraphicsConfiguration();
Rectangle r = layer.getBounds();
return new MTLLayerSurfaceData(layer, gc, r.width, r.height);
}
/**
* Creates a SurfaceData object representing an off-screen buffer (either a
* FBO or Texture).
*/
public static MTLOffScreenSurfaceData createData(MTLGraphicsConfig gc,
int width, int height, ColorModel cm, Image image, int type) {
return new MTLOffScreenSurfaceData(gc, width, height, image, cm,
type);
}
@Override
public double getDefaultScaleX() {
return scale;
}
@Override
public double getDefaultScaleY() {
return scale;
}
@Override
public Rectangle getBounds() {
return new Rectangle(width, height);
}
protected native void clearWindow();
protected native boolean initTexture(long pData, boolean isOpaque, int width, int height);
protected native boolean initRTexture(long pData, boolean isOpaque, int width, int height);
protected native boolean initFlipBackbuffer(long pData);
@Override
public SurfaceDataProxy makeProxyFor(SurfaceData srcData) {
return MTLSurfaceDataProxy.createProxy(srcData, graphicsConfig);
}
/**
* Note: This should only be called from the QFT under the AWT lock.
* This method is kept separate from the initSurface() method below just
* to keep the code a bit cleaner.
*/
private void initSurfaceNow(int width, int height) {
boolean isOpaque = (getTransparency() == Transparency.OPAQUE);
boolean success = false;
switch (type) {
case TEXTURE:
success = initTexture(getNativeOps(), isOpaque, width, height);
break;
case RT_TEXTURE:
success = initRTexture(getNativeOps(), isOpaque, width, height);
break;
case FLIP_BACKBUFFER:
success = initFlipBackbuffer(getNativeOps());
break;
default:
break;
}
if (!success) {
throw new OutOfMemoryError("can't create offscreen surface");
}
}
/**
* Initializes the appropriate OpenGL offscreen surface based on the value
* of the type parameter. If the surface creation fails for any reason,
* an OutOfMemoryError will be thrown.
*/
protected void initSurface(final int width, final int height) {
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
switch (type) {
case TEXTURE:
case RT_TEXTURE:
// need to make sure the context is current before
// creating the texture or fbobject
MTLContext.setScratchSurface(graphicsConfig);
break;
default:
break;
}
rq.flushAndInvokeNow(new Runnable() {
public void run() {
initSurfaceNow(width, height);
}
});
} finally {
rq.unlock();
}
}
/**
* Returns the MTLContext for the GraphicsConfig associated with this
* surface.
*/
public final MTLContext getContext() {
return graphicsConfig.getContext();
}
/**
* Returns the MTLGraphicsConfig associated with this surface.
*/
final MTLGraphicsConfig getMTLGraphicsConfig() {
return graphicsConfig;
}
/**
* Returns one of the surface type constants defined above.
*/
public final int getType() {
return type;
}
/**
* For now, we can only render LCD text if:
* - the fragment shader extension is available, and
* - the source color is opaque, and
* - blending is SrcOverNoEa or disabled
* - and the destination is opaque
*
* Eventually, we could enhance the native OGL text rendering code
* and remove the above restrictions, but that would require significantly
* more code just to support a few uncommon cases.
*/
public boolean canRenderLCDText(SunGraphics2D sg2d) {
return
sg2d.surfaceData.getTransparency() == Transparency.OPAQUE &&
sg2d.paintState <= SunGraphics2D.PAINT_OPAQUECOLOR &&
(sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY ||
(sg2d.compositeState <= SunGraphics2D.COMP_ALPHA && canHandleComposite(sg2d.composite)));
}
private boolean canHandleComposite(Composite c) {
if (c instanceof AlphaComposite) {
AlphaComposite ac = (AlphaComposite)c;
return ac.getRule() == AlphaComposite.SRC_OVER && ac.getAlpha() >= 1f;
}
return false;
}
public void validatePipe(SunGraphics2D sg2d) {
TextPipe textpipe;
boolean validated = false;
// MTLTextRenderer handles both AA and non-AA text, but
// only works with the following modes:
// (Note: For LCD text we only enter this code path if
// canRenderLCDText() has already validated that the mode is
// CompositeType.SrcNoEa (opaque color), which will be subsumed
// by the CompositeType.SrcNoEa (any color) test below.)
if (/* CompositeType.SrcNoEa (any color) */
(sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY &&
sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) ||
/* CompositeType.SrcOver (any color) */
(sg2d.compositeState == SunGraphics2D.COMP_ALPHA &&
sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR &&
(((AlphaComposite)sg2d.composite).getRule() ==
AlphaComposite.SRC_OVER)) ||
/* CompositeType.Xor (any color) */
(sg2d.compositeState == SunGraphics2D.COMP_XOR &&
sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR))
{
textpipe = mtlTextPipe;
} else {
// do this to initialize textpipe correctly; we will attempt
// to override the non-text pipes below
super.validatePipe(sg2d);
textpipe = sg2d.textpipe;
validated = true;
}
PixelToParallelogramConverter txPipe = null;
MTLRenderer nonTxPipe = null;
if (sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) {
if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) {
if (sg2d.compositeState <= SunGraphics2D.COMP_XOR) {
txPipe = mtlTxRenderPipe;
nonTxPipe = mtlRenderPipe;
}
} else if (sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) {
if (MTLPaints.isValid(sg2d)) {
txPipe = mtlTxRenderPipe;
nonTxPipe = mtlRenderPipe;
}
// custom paints handled by super.validatePipe() below
}
} else {
if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) {
if (graphicsConfig.isCapPresent(CAPS_PS30) &&
(sg2d.imageComp == CompositeType.SrcOverNoEa ||
sg2d.imageComp == CompositeType.SrcOver))
{
if (!validated) {
super.validatePipe(sg2d);
validated = true;
}
PixelToParallelogramConverter aaConverter =
new PixelToParallelogramConverter(sg2d.shapepipe,
mtlAAPgramPipe,
1.0/8.0, 0.499,
false);
sg2d.drawpipe = aaConverter;
sg2d.fillpipe = aaConverter;
sg2d.shapepipe = aaConverter;
} else if (sg2d.compositeState == SunGraphics2D.COMP_XOR) {
// install the solid pipes when AA and XOR are both enabled
txPipe = mtlTxRenderPipe;
nonTxPipe = mtlRenderPipe;
}
}
// other cases handled by super.validatePipe() below
}
if (txPipe != null) {
if (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) {
sg2d.drawpipe = txPipe;
sg2d.fillpipe = txPipe;
} else if (sg2d.strokeState != SunGraphics2D.STROKE_THIN) {
sg2d.drawpipe = txPipe;
sg2d.fillpipe = nonTxPipe;
} else {
sg2d.drawpipe = nonTxPipe;
sg2d.fillpipe = nonTxPipe;
}
// Note that we use the transforming pipe here because it
// will examine the shape and possibly perform an optimized
// operation if it can be simplified. The simplifications
// will be valid for all STROKE and TRANSFORM types.
sg2d.shapepipe = txPipe;
} else {
if (!validated) {
super.validatePipe(sg2d);
}
}
// install the text pipe based on our earlier decision
sg2d.textpipe = textpipe;
// always override the image pipe with the specialized OGL pipe
sg2d.imagepipe = mtlImagePipe;
}
@Override
protected MaskFill getMaskFill(SunGraphics2D sg2d) {
if (sg2d.paintState > SunGraphics2D.PAINT_ALPHACOLOR) {
/*
* We can only accelerate non-Color MaskFill operations if
* all of the following conditions hold true:
* - there is an implementation for the given paintState
* - the current Paint can be accelerated for this destination
* - multitexturing is available (since we need to modulate
* the alpha mask texture with the paint texture)
*
* In all other cases, we return null, in which case the
* validation code will choose a more general software-based loop.
*/
if (!MTLPaints.isValid(sg2d) ||
!graphicsConfig.isCapPresent(CAPS_MULTITEXTURE))
{
return null;
}
}
return super.getMaskFill(sg2d);
}
public void flush() {
invalidate();
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
// make sure we have a current context before
// disposing the native resources (e.g. texture object)
MTLContext.setScratchSurface(graphicsConfig);
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(12, 4);
buf.putInt(FLUSH_SURFACE);
buf.putLong(getNativeOps());
// this call is expected to complete synchronously, so flush now
rq.flushNow();
} finally {
rq.unlock();
}
}
/**
* Returns true if the surface is an on-screen window surface or
* a FBO texture attached to an on-screen CALayer.
*
* Needed by Mac OS X port.
*/
public boolean isOnScreen() {
return getType() == WINDOW;
}
private native long getMTLTexturePointer(long pData);
/**
* Returns native resource of specified {@code resType} associated with
* this surface.
*
* Specifically, for {@code MTLSurfaceData} this method returns the
* the following:
* <pre>
* TEXTURE - texture id
* </pre>
*
* Note: the resource returned by this method is only valid on the rendering
* thread.
*
* @return native resource of specified type or 0L if
* such resource doesn't exist or can not be retrieved.
* @see AccelSurface#getNativeResource
*/
public long getNativeResource(int resType) {
if (resType == TEXTURE) {
return getMTLTexturePointer(getNativeOps());
}
return 0L;
}
public Raster getRaster(int x, int y, int w, int h) {
throw new InternalError("not implemented yet");
}
@Override
public boolean copyArea(SunGraphics2D sg2d, int x, int y, int w, int h,
int dx, int dy) {
if (sg2d.compositeState >= SunGraphics2D.COMP_XOR) {
return false;
}
mtlRenderPipe.copyArea(sg2d, x, y, w, h, dx, dy);
return true;
}
public Rectangle getNativeBounds() {
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
return new Rectangle(nativeWidth, nativeHeight);
} finally {
rq.unlock();
}
}
/**
* A surface which implements an intermediate buffer between
* the Java2D flusher thread and the AppKit thread.
*
* This surface serves as a buffer attached to a MTLLayer and
* the layer redirects all painting to the buffer's graphics.
*/
public static class MTLLayerSurfaceData extends MTLSurfaceData {
private final MTLLayer layer;
private MTLLayerSurfaceData(MTLLayer layer, MTLGraphicsConfig gc,
int width, int height) {
super(layer, gc, gc.getColorModel(), RT_TEXTURE, width, height);
this.layer = layer;
initSurface(this.width, this.height);
}
@Override
public SurfaceData getReplacement() {
return layer.getSurfaceData();
}
@Override
public boolean isOnScreen() {
return true;
}
@Override
public Object getDestination() {
return layer.getDestination();
}
@Override
public int getTransparency() {
return layer.getTransparency();
}
@Override
public void invalidate() {
super.invalidate();
clearWindow();
}
}
/**
* SurfaceData object representing an off-screen buffer (either a FBO or
* Texture).
+ */
public static class MTLOffScreenSurfaceData extends MTLSurfaceData {
private final Image offscreenImage;
public MTLOffScreenSurfaceData(MTLGraphicsConfig gc, int width,
int height, Image image,
ColorModel cm, int type) {
super(null, gc, cm, type, width, height);
offscreenImage = image;
initSurface(this.width, this.height);
}
@Override
public SurfaceData getReplacement() {
return restoreContents(offscreenImage);
}
/**
* Returns destination Image associated with this SurfaceData.
*/
@Override
public Object getDestination() {
return offscreenImage;
}
}
// additional cleanup
private static native void destroyCGLContext(long ctx);
public static void destroyOGLContext(long ctx) {
if (ctx != 0L) {
destroyCGLContext(ctx);
}
}
/**
* Disposes the native resources associated with the given MTLSurfaceData
* (referenced by the pData parameter). This method is invoked from
* the native Dispose() method from the Disposer thread when the
* Java-level MTLSurfaceData object is about to go away.
*/
public static void dispose(long pData, MTLGraphicsConfig gc) {
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
// make sure we have a current context before
// disposing the native resources (e.g. texture object)
MTLContext.setScratchSurface(gc);
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(12, 4);
buf.putInt(DISPOSE_SURFACE);
buf.putLong(pData);
// this call is expected to complete synchronously, so flush now
rq.flushNow();
} finally {
rq.unlock();
}
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.SurfaceData;
import sun.java2d.SurfaceDataProxy;
import sun.java2d.loops.CompositeType;
import java.awt.*;
/**
* The proxy class contains the logic for when to replace a
* SurfaceData with a cached OGL Texture and the code to create
* the accelerated surfaces.
*/
public class MTLSurfaceDataProxy extends SurfaceDataProxy {
public static SurfaceDataProxy createProxy(SurfaceData srcData,
MTLGraphicsConfig dstConfig)
{
if (srcData instanceof MTLSurfaceData) {
// srcData must be a VolatileImage which either matches
// our pixel format or not - either way we do not cache it...
return UNCACHED;
}
return new MTLSurfaceDataProxy(dstConfig, srcData.getTransparency());
}
MTLGraphicsConfig oglgc;
int transparency;
public MTLSurfaceDataProxy(MTLGraphicsConfig oglgc, int transparency) {
this.oglgc = oglgc;
this.transparency = transparency;
}
@Override
public SurfaceData validateSurfaceData(SurfaceData srcData,
SurfaceData cachedData,
int w, int h)
{
if (cachedData == null) {
try {
cachedData = oglgc.createManagedSurface(w, h, transparency);
} catch (OutOfMemoryError er) {
return null;
}
}
return cachedData;
}
@Override
public boolean isSupportedOperation(SurfaceData srcData,
int txtype,
CompositeType comp,
Color bgColor)
{
return comp.isDerivedFrom(CompositeType.AnyAlpha) &&
(bgColor == null || transparency == Transparency.OPAQUE);
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.font.GlyphList;
import sun.java2d.SunGraphics2D;
import sun.java2d.loops.GraphicsPrimitive;
import sun.java2d.pipe.BufferedTextPipe;
import sun.java2d.pipe.RenderQueue;
import java.awt.*;
class MTLTextRenderer extends BufferedTextPipe {
MTLTextRenderer(RenderQueue rq) {
super(rq);
}
@Override
protected native void drawGlyphList(int numGlyphs, boolean usePositions,
boolean subPixPos, boolean rgbOrder,
int lcdContrast,
float glOrigX, float glOrigY,
long[] images, float[] positions);
@Override
protected void validateContext(SunGraphics2D sg2d, Composite comp) {
// assert rq.lock.isHeldByCurrentThread();
MTLSurfaceData oglDst = (MTLSurfaceData)sg2d.surfaceData;
MTLContext.validateContext(oglDst, oglDst,
sg2d.getCompClip(), comp,
null, sg2d.paint, sg2d,
MTLContext.NO_CONTEXT_FLAGS);
}
MTLTextRenderer traceWrap() {
return new Tracer(this);
}
private static class Tracer extends MTLTextRenderer {
Tracer(MTLTextRenderer mtltr) {
super(mtltr.rq);
}
protected void drawGlyphList(SunGraphics2D sg2d, GlyphList gl) {
GraphicsPrimitive.tracePrimitive("MTLDrawGlyphs");
super.drawGlyphList(sg2d, gl);
}
}
}

View File

@@ -0,0 +1,278 @@
/*
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.pipe.Region;
import java.awt.*;
/**
* This class contains a number of static utility methods that may be
* called (via reflection) by a third-party library in order
* to interoperate with the metal-based Java 2D pipeline.
*
*/
class MTLUtilities {
/**
* These OGL-specific surface type constants are the same as those
* defined in the MTLSurfaceData class and are duplicated here so that
* clients of this API can access them more easily via reflection.
*/
public static final int UNDEFINED = MTLSurfaceData.UNDEFINED;
public static final int WINDOW = MTLSurfaceData.WINDOW;
public static final int TEXTURE = MTLSurfaceData.TEXTURE;
public static final int FLIP_BACKBUFFER = MTLSurfaceData.FLIP_BACKBUFFER;
public static final int RT_TEXTURE = MTLSurfaceData.RT_TEXTURE;
private MTLUtilities() {
}
/**
* Returns true if the current thread is the OGL QueueFlusher thread.
*/
public static boolean isQueueFlusherThread() {
return MTLRenderQueue.isQueueFlusherThread();
}
/**
* Invokes the given Runnable on the MTL QueueFlusher thread with the
* MTL context corresponding to the given Graphics object made
* current. It is legal for MTL code executed in the given
* Runnable to change the current MTL context; it will be reset
* once the Runnable completes. No guarantees are made as to the
* state of the MTL context of the Graphics object; for
*
* In order to avoid deadlock, it is important that the given Runnable
* does not attempt to acquire the AWT lock, as that will be handled
* automatically as part of the {@code rq.flushAndInvokeNow()} step.
*
* @param g the Graphics object for the corresponding destination surface;
* if null, the step making a context current to the destination surface
* will be skipped
* @param r the action to be performed on the QFT; cannot be null
* @return true if the operation completed successfully, or false if
* there was any problem making a context current to the surface
* associated with the given Graphics object
*/
public static boolean invokeWithMTLContextCurrent(Graphics g, Runnable r) {
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
if (g != null) {
if (!(g instanceof SunGraphics2D)) {
return false;
}
SurfaceData sData = ((SunGraphics2D)g).surfaceData;
if (!(sData instanceof MTLSurfaceData)) {
return false;
}
// make a context current to the destination surface
MTLContext.validateContext((MTLSurfaceData)sData);
}
// invoke the given runnable on the QFT
rq.flushAndInvokeNow(r);
// invalidate the current context so that the next time we render
// with Java 2D, the context state will be completely revalidated
MTLContext.invalidateCurrentContext();
} finally {
rq.unlock();
}
return true;
}
/**
* Invokes the given Runnable on the MTL QueueFlusher thread with the
* "shared" MTL context (corresponding to the given
* GraphicsConfiguration object) made current. This method is typically
* used when the Runnable needs a current context to complete its
* operation, but does not require that the context be made current to
* a particular surface. For example, an application may call this
* method so that the given Runnable can query the OpenGL capabilities
* of the given GraphicsConfiguration, without making a context current
* to a dummy surface (or similar hacky techniques).
*
* In order to avoid deadlock, it is important that the given Runnable
* does not attempt to acquire the AWT lock, as that will be handled
* automatically as part of the {@code rq.flushAndInvokeNow()} step.
*
* @param config the GraphicsConfiguration object whose "shared"
* context will be made current during this operation; if this value is
* null or if MTL is not enabled for the GraphicsConfiguration, this
* method will return false
* @param r the action to be performed on the QFT; cannot be null
* @return true if the operation completed successfully, or false if
* there was any problem making the shared context current
*/
public static boolean
invokeWithMTLSharedContextCurrent(GraphicsConfiguration config,
Runnable r)
{
if (!(config instanceof MTLGraphicsConfig)) {
return false;
}
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
// make the "shared" context current for the given GraphicsConfig
MTLContext.setScratchSurface((MTLGraphicsConfig)config);
// invoke the given runnable on the QFT
rq.flushAndInvokeNow(r);
// invalidate the current context so that the next time we render
// with Java 2D, the context state will be completely revalidated
MTLContext.invalidateCurrentContext();
} finally {
rq.unlock();
}
return true;
}
/**
* Returns the Rectangle describing the MTL viewport on the
* Java 2D surface associated with the given Graphics object and
* component width and height. When a third-party library is
* performing MTL rendering directly into the visible region of
* the associated surface, this viewport helps the application
* position the MTL output correctly on that surface.
*
* Note that the x/y values in the returned Rectangle object represent
* the lower-left corner of the viewport region, relative to the
* lower-left corner of the given surface.
*
* @param g the Graphics object for the corresponding destination surface;
* cannot be null
* @param componentWidth width of the component to be painted
* @param componentHeight height of the component to be painted
* @return a Rectangle describing the MTL viewport for the given
* destination surface and component dimensions, or null if the given
* Graphics object is invalid
*/
public static Rectangle getMTLViewport(Graphics g,
int componentWidth,
int componentHeight)
{
if (!(g instanceof SunGraphics2D)) {
return null;
}
SunGraphics2D sg2d = (SunGraphics2D)g;
SurfaceData sData = sg2d.surfaceData;
// this is the upper-left origin of the region to be painted,
// relative to the upper-left origin of the surface
// (in Java2D coordinates)
int x0 = sg2d.transX;
int y0 = sg2d.transY;
// this is the lower-left origin of the region to be painted,
// relative to the lower-left origin of the surface
// (in OpenGL coordinates)
Rectangle surfaceBounds = sData.getBounds();
int x1 = x0;
int y1 = surfaceBounds.height - (y0 + componentHeight);
return new Rectangle(x1, y1, componentWidth, componentHeight);
}
/**
* Returns the Rectangle describing the MTL scissor box on the
* Java 2D surface associated with the given Graphics object. When a
* third-party library is performing MTL rendering directly
* into the visible region of the associated surface, this scissor box
* must be set to avoid drawing over existing rendering results.
*
* Note that the x/y values in the returned Rectangle object represent
* the lower-left corner of the scissor region, relative to the
* lower-left corner of the given surface.
*
* @param g the Graphics object for the corresponding destination surface;
* cannot be null
* @return a Rectangle describing the MTL scissor box for the given
* Graphics object and corresponding destination surface, or null if the
* given Graphics object is invalid or the clip region is non-rectangular
*/
public static Rectangle getOGLScissorBox(Graphics g) {
if (!(g instanceof SunGraphics2D)) {
return null;
}
SunGraphics2D sg2d = (SunGraphics2D)g;
SurfaceData sData = sg2d.surfaceData;
Region r = sg2d.getCompClip();
if (!r.isRectangular()) {
// caller probably doesn't know how to handle shape clip
// appropriately, so just return null (Swing currently never
// sets a shape clip, but that could change in the future)
return null;
}
// this is the upper-left origin of the scissor box relative to the
// upper-left origin of the surface (in Java 2D coordinates)
int x0 = r.getLoX();
int y0 = r.getLoY();
// this is the width and height of the scissor region
int w = r.getWidth();
int h = r.getHeight();
// this is the lower-left origin of the scissor box relative to the
// lower-left origin of the surface (in OpenGL coordinates)
Rectangle surfaceBounds = sData.getBounds();
int x1 = x0;
int y1 = surfaceBounds.height - (y0 + h);
return new Rectangle(x1, y1, w, h);
}
/**
* Returns an Object identifier for the Java 2D surface associated with
* the given Graphics object. This identifier may be used to determine
* whether the surface has changed since the last invocation of this
* operation, and thereby whether the MTL state corresponding to the
* old surface must be destroyed and recreated.
*
* @param g the Graphics object for the corresponding destination surface;
* cannot be null
* @return an identifier for the surface associated with the given
* Graphics object, or null if the given Graphics object is invalid
*/
public static Object getMTLSurfaceIdentifier(Graphics g) {
if (!(g instanceof SunGraphics2D)) {
return null;
}
return ((SunGraphics2D)g).surfaceData;
}
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.metal;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.VolatileSurfaceManager;
import sun.java2d.SurfaceData;
import sun.java2d.opengl.OGLSurfaceData;
import java.awt.*;
import java.awt.image.ColorModel;
import static java.awt.BufferCapabilities.FlipContents.COPIED;
public class MTLVolatileSurfaceManager extends VolatileSurfaceManager {
private final boolean accelerationEnabled;
public MTLVolatileSurfaceManager(SunVolatileImage vImg, Object context) {
super(vImg, context);
/*
* We will attempt to accelerate this image only under the
* following conditions:
* - the image is not bitmask AND the GraphicsConfig supports the FBO
* extension
*/
int transparency = vImg.getTransparency();
MTLGraphicsConfig gc = (MTLGraphicsConfig) vImg.getGraphicsConfig();
accelerationEnabled = true;
//gc.isCapPresent(CAPS_EXT_FBOBJECT)
//&& transparency != Transparency.BITMASK;
}
protected boolean isAccelerationEnabled() {
return accelerationEnabled;
}
/**
* Create a FBO-based SurfaceData object (or init the backbuffer
* of an existing window if this is a double buffered GraphicsConfig)
*/
protected SurfaceData initAcceleratedSurface() {
try {
MTLGraphicsConfig gc =
(MTLGraphicsConfig)vImg.getGraphicsConfig();
ColorModel cm = gc.getColorModel(vImg.getTransparency());
int type = vImg.getForcedAccelSurfaceType();
// if acceleration type is forced (type != UNDEFINED) then
// use the forced type, otherwise choose RT_TEXTURE
if (type == OGLSurfaceData.UNDEFINED) {
type = OGLSurfaceData.FBOBJECT;
}
return MTLSurfaceData.createData(gc,
vImg.getWidth(),
vImg.getHeight(),
cm, vImg, type);
} catch (NullPointerException | OutOfMemoryError ignored) {
return null;
}
}
@Override
protected boolean isConfigValid(GraphicsConfiguration gc) {
return ((gc == null) || (gc == vImg.getGraphicsConfig()));
}
@Override
public void initContents() {
if (vImg.getForcedAccelSurfaceType() != OGLSurfaceData.TEXTURE) {
super.initContents();
}
}
}

View File

@@ -55,6 +55,7 @@ import sun.java2d.pipe.hw.AccelSurface;
import sun.java2d.pipe.hw.AccelTypedVolatileImage;
import sun.java2d.pipe.hw.ContextCapabilities;
import sun.lwawt.LWComponentPeer;
import sun.lwawt.macosx.CFRetainedResource;
import static sun.java2d.opengl.OGLContext.OGLContextCaps.CAPS_DOUBLEBUFFERED;
import static sun.java2d.opengl.OGLContext.OGLContextCaps.CAPS_EXT_FBOBJECT;
@@ -62,7 +63,7 @@ import static sun.java2d.opengl.OGLSurfaceData.FBOBJECT;
import static sun.java2d.opengl.OGLSurfaceData.TEXTURE;
public final class CGLGraphicsConfig extends CGraphicsConfig
implements OGLGraphicsConfig
implements OGLGraphicsConfig
{
private static boolean cglAvailable;
private static ImageCapabilities imageCaps = new CGLImageCaps();
@@ -101,7 +102,7 @@ public final class CGLGraphicsConfig extends CGraphicsConfig
// add a record to the Disposer so that we destroy the native
// CGLGraphicsConfigInfo data when this object goes away
Disposer.addRecord(disposerReferent,
new CGLGCDisposerRecord(pConfigInfo));
new CGLGCDisposerRecord(pConfigInfo));
}
@Override
@@ -112,9 +113,9 @@ public final class CGLGraphicsConfig extends CGraphicsConfig
@Override
public SurfaceData createManagedSurface(int w, int h, int transparency) {
return CGLSurfaceData.createData(this, w, h,
getColorModel(transparency),
null,
OGLSurfaceData.TEXTURE);
getColorModel(transparency),
null,
OGLSurfaceData.TEXTURE);
}
public static CGLGraphicsConfig getConfig(CGraphicsDevice device)
@@ -184,27 +185,27 @@ public final class CGLGraphicsConfig extends CGraphicsConfig
public BufferedImage createCompatibleImage(int width, int height) {
ColorModel model = new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
WritableRaster
raster = model.createCompatibleWritableRaster(width, height);
raster = model.createCompatibleWritableRaster(width, height);
return new BufferedImage(model, raster, model.isAlphaPremultiplied(),
null);
null);
}
@Override
public ColorModel getColorModel(int transparency) {
switch (transparency) {
case Transparency.OPAQUE:
// REMIND: once the ColorModel spec is changed, this should be
// an opaque premultiplied DCM...
return new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
case Transparency.BITMASK:
return new DirectColorModel(25, 0xff0000, 0xff00, 0xff, 0x1000000);
case Transparency.TRANSLUCENT:
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
return new DirectColorModel(cs, 32,
0xff0000, 0xff00, 0xff, 0xff000000,
true, DataBuffer.TYPE_INT);
default:
return null;
case Transparency.OPAQUE:
// REMIND: once the ColorModel spec is changed, this should be
// an opaque premultiplied DCM...
return new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
case Transparency.BITMASK:
return new DirectColorModel(25, 0xff0000, 0xff00, 0xff, 0x1000000);
case Transparency.TRANSLUCENT:
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
return new DirectColorModel(cs, 32,
0xff0000, 0xff00, 0xff, 0xff000000,
true, DataBuffer.TYPE_INT);
default:
return null;
}
}
@@ -248,8 +249,8 @@ public final class CGLGraphicsConfig extends CGraphicsConfig
}
@Override
public SurfaceData createSurfaceData(CGLLayer layer) {
return CGLSurfaceData.createData(layer);
public SurfaceData createSurfaceData(CFRetainedResource layer) {
return CGLSurfaceData.createData((CGLLayer) layer);
}
@Override
@@ -259,7 +260,7 @@ public final class CGLGraphicsConfig extends CGraphicsConfig
ColorModel model = getColorModel(Transparency.OPAQUE);
WritableRaster wr = model.createCompatibleWritableRaster(width, height);
return new OffScreenImage(target, model, wr,
model.isAlphaPremultiplied());
model.isAlphaPremultiplied());
}
@Override
@@ -290,7 +291,7 @@ public final class CGLGraphicsConfig extends CGraphicsConfig
final int w = Math.max(1, r.width);
final int h = Math.max(1, r.height);
final int transparency = peer.isTranslucent() ? Transparency.TRANSLUCENT
: Transparency.OPAQUE;
: Transparency.OPAQUE;
return new SunVolatileImage(this, w, h, transparency, null);
}
@@ -316,7 +317,7 @@ public final class CGLGraphicsConfig extends CGraphicsConfig
try {
bg.setBackground(peer.getBackground());
bg.clearRect(0, 0, backBuffer.getWidth(null),
backBuffer.getHeight(null));
backBuffer.getHeight(null));
} finally {
bg.dispose();
}
@@ -326,7 +327,7 @@ public final class CGLGraphicsConfig extends CGraphicsConfig
private static class CGLBufferCaps extends BufferCapabilities {
public CGLBufferCaps(boolean dblBuf) {
super(imageCaps, imageCaps,
dblBuf ? FlipContents.UNDEFINED : null);
dblBuf ? FlipContents.UNDEFINED : null);
}
}
@@ -362,10 +363,10 @@ public final class CGLGraphicsConfig extends CGraphicsConfig
return null;
}
SunVolatileImage vi = new AccelTypedVolatileImage(this, width, height,
transparency, type);
transparency, type);
Surface sd = vi.getDestSurface();
if (!(sd instanceof AccelSurface) ||
((AccelSurface)sd).getType() != type)
((AccelSurface)sd).getType() != type)
{
vi.flush();
vi = null;
@@ -382,12 +383,12 @@ public final class CGLGraphicsConfig extends CGraphicsConfig
@Override
public int getMaxTextureWidth() {
return Math.max(maxTextureSize / getDevice().getScaleFactor(),
getBounds().width);
getBounds().width);
}
@Override
public int getMaxTextureHeight() {
return Math.max(maxTextureSize / getDevice().getScaleFactor(),
getBounds().height);
getBounds().height);
}
}

View File

@@ -74,8 +74,11 @@ import sun.awt.SunToolkit;
import sun.awt.event.IgnorePaintEvent;
import sun.awt.image.SunVolatileImage;
import sun.java2d.SunGraphics2D;
import sun.java2d.macos.MacOSFlags;
import sun.java2d.metal.MTLRenderQueue;
import sun.java2d.opengl.OGLRenderQueue;
import sun.java2d.pipe.Region;
import sun.java2d.pipe.RenderQueue;
import sun.util.logging.PlatformLogger;
public abstract class LWComponentPeer<T extends Component, D extends JComponent>
@@ -1414,7 +1417,8 @@ public abstract class LWComponentPeer<T extends Component, D extends JComponent>
}
protected static final void flushOnscreenGraphics(){
final OGLRenderQueue rq = OGLRenderQueue.getInstance();
RenderQueue rq = MacOSFlags.isMetalEnabled() ?
MTLRenderQueue.getInstance() : OGLRenderQueue.getInstance();
rq.lock();
try {
rq.flushNow();

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,11 +29,15 @@ import java.awt.*;
import java.awt.event.FocusEvent;
import sun.java2d.SurfaceData;
import sun.java2d.macos.MacOSFlags;
import sun.java2d.metal.MTLLayer;
import sun.java2d.opengl.CGLLayer;
import sun.lwawt.LWWindowPeer;
import sun.lwawt.PlatformWindow;
import sun.lwawt.macosx.CFRetainedResource;
import sun.util.logging.PlatformLogger;
/*
* Provides a lightweight implementation of the EmbeddedFrame.
*/
@@ -42,7 +46,7 @@ public class CPlatformEmbeddedFrame implements PlatformWindow {
private static final PlatformLogger focusLogger = PlatformLogger.getLogger(
"sun.lwawt.macosx.focus.CPlatformEmbeddedFrame");
private CGLLayer windowLayer;
private CFRetainedResource windowLayer;
private LWWindowPeer peer;
private CEmbeddedFrame target;
@@ -52,7 +56,11 @@ public class CPlatformEmbeddedFrame implements PlatformWindow {
@Override // PlatformWindow
public void initialize(Window target, final LWWindowPeer peer, PlatformWindow owner) {
this.peer = peer;
this.windowLayer = new CGLLayer(peer);
if (MacOSFlags.isMetalEnabled()) {
this.windowLayer = new MTLLayer(peer);
} else {
this.windowLayer = new CGLLayer(peer);
}
this.target = (CEmbeddedFrame)target;
}
@@ -63,12 +71,20 @@ public class CPlatformEmbeddedFrame implements PlatformWindow {
@Override
public long getLayerPtr() {
return windowLayer.getPointer();
if (MacOSFlags.isMetalEnabled()) {
return ((MTLLayer)windowLayer).getPointer();
} else {
return ((CGLLayer)windowLayer).getPointer();
}
}
@Override
public void dispose() {
windowLayer.dispose();
if (MacOSFlags.isMetalEnabled()) {
((MTLLayer)windowLayer).dispose();
} else {
((CGLLayer)windowLayer).dispose();
}
}
@Override
@@ -99,12 +115,20 @@ public class CPlatformEmbeddedFrame implements PlatformWindow {
@Override
public SurfaceData getScreenSurface() {
return windowLayer.getSurfaceData();
if (MacOSFlags.isMetalEnabled()) {
return ((MTLLayer)windowLayer).getSurfaceData();
} else {
return ((CGLLayer)windowLayer).getSurfaceData();
}
}
@Override
public SurfaceData replaceSurfaceData() {
return windowLayer.replaceSurfaceData();
if (MacOSFlags.isMetalEnabled()) {
return ((MTLLayer)windowLayer).replaceSurfaceData();
} else {
return ((CGLLayer)windowLayer).replaceSurfaceData();
}
}
@Override

View File

@@ -35,9 +35,12 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import sun.awt.CGraphicsEnvironment;
import sun.java2d.macos.MacOSFlags;
import sun.java2d.metal.MTLLayer;
import sun.lwawt.LWWindowPeer;
import sun.java2d.SurfaceData;
import sun.java2d.opengl.CGLLayer;
import sun.lwawt.LWWindowPeer;
public class CPlatformView extends CFRetainedResource {
private native long nativeCreateView(int x, int y, int width, int height, long windowLayerPtr);
@@ -48,7 +51,7 @@ public class CPlatformView extends CFRetainedResource {
private LWWindowPeer peer;
private SurfaceData surfaceData;
private CGLLayer windowLayer;
private CFRetainedResource windowLayer;
private CPlatformResponder responder;
public CPlatformView() {
@@ -58,7 +61,7 @@ public class CPlatformView extends CFRetainedResource {
public void initialize(LWWindowPeer peer, CPlatformResponder responder) {
initializeBase(peer, responder);
this.windowLayer = createCGLayer();
this.windowLayer = MacOSFlags.isMetalEnabled()? createMTLLayer() : createCGLayer();
setPtr(nativeCreateView(0, 0, 0, 0, getWindowLayerPtr()));
}
@@ -66,6 +69,11 @@ public class CPlatformView extends CFRetainedResource {
return new CGLLayer(peer);
}
public MTLLayer createMTLLayer() {
return new MTLLayer(peer);
}
protected void initializeBase(LWWindowPeer peer, CPlatformResponder responder) {
this.peer = peer;
this.responder = responder;
@@ -96,7 +104,10 @@ public class CPlatformView extends CFRetainedResource {
// PAINTING METHODS
// ----------------------------------------------------------------------
public SurfaceData replaceSurfaceData() {
surfaceData = windowLayer.replaceSurfaceData();
surfaceData = (MacOSFlags.isMetalEnabled()) ?
((MTLLayer)windowLayer).replaceSurfaceData() :
((CGLLayer)windowLayer).replaceSurfaceData()
;
return surfaceData;
}
@@ -111,7 +122,9 @@ public class CPlatformView extends CFRetainedResource {
}
public long getWindowLayerPtr() {
return windowLayer.getPointer();
return MacOSFlags.isMetalEnabled() ?
((MTLLayer)windowLayer).getPointer() :
((CGLLayer)windowLayer).getPointer();
}
public void setAutoResizable(boolean toResize) {
@@ -176,18 +189,18 @@ public class CPlatformView extends CFRetainedResource {
if (event.getType() == CocoaConstants.NSScrollWheel) {
responder.handleScrollEvent(x, y, absX, absY, event.getModifierFlags(),
event.getScrollDeltaX(), event.getScrollDeltaY(),
event.getScrollPhase());
event.getScrollDeltaX(), event.getScrollDeltaY(),
event.getScrollPhase());
} else {
responder.handleMouseEvent(event.getType(), event.getModifierFlags(), event.getButtonNumber(),
event.getClickCount(), x, y,
absX, absY);
event.getClickCount(), x, y,
absX, absY);
}
}
private void deliverKeyEvent(NSEvent event) {
responder.handleKeyEvent(event.getType(), event.getModifierFlags(), event.getCharacters(),
event.getCharactersIgnoringModifiers(), event.getKeyCode(), true, false);
event.getCharactersIgnoringModifiers(), event.getKeyCode(), true, false);
}
/**

View File

@@ -29,6 +29,7 @@ import sun.awt.AWTAccessor;
import sun.awt.IconInfo;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.metal.MTLLayer;
import sun.java2d.opengl.CGLLayer;
import sun.lwawt.LWWindowPeer;
import sun.lwawt.PlatformEventNotifier;
@@ -222,8 +223,8 @@ public final class CWarningWindow extends CPlatformWindow
owner.execute(ownerPtr -> {
execute(ptr -> {
CWrapper.NSWindow.orderWindow(ptr,
CWrapper.NSWindow.NSWindowAbove,
ownerPtr);
CWrapper.NSWindow.NSWindowAbove,
ownerPtr);
});
});
@@ -300,6 +301,23 @@ public final class CWarningWindow extends CPlatformWindow
}
};
}
public MTLLayer createMTLLayer() {
return new MTLLayer(null) {
public Rectangle getBounds() {
return CWarningWindow.this.getBounds();
}
public GraphicsConfiguration getGraphicsConfiguration() {
LWWindowPeer peer = ownerPeer.get();
return peer.getGraphicsConfiguration();
}
public boolean isOpaque() {
return false;
}
};
}
};
}
@@ -349,7 +367,7 @@ public final class CWarningWindow extends CPlatformWindow
currentSize = newSize;
IconInfo ico = getSecurityIconInfo(currentSize, 0);
AWTAccessor.getWindowAccessor().setSecurityWarningSize(
ownerWindow, ico.getWidth(), ico.getHeight());
ownerWindow, ico.getWidth(), ico.getHeight());
}
}
}
@@ -361,7 +379,7 @@ public final class CWarningWindow extends CPlatformWindow
}
return new SunGraphics2D(sd, SystemColor.windowText, SystemColor.window,
ownerWindow.getFont());
ownerWindow.getFont());
}

View File

@@ -109,6 +109,8 @@ import sun.awt.SunToolkit;
import sun.awt.datatransfer.DataTransferer;
import sun.awt.dnd.SunDragSourceContextPeer;
import sun.awt.util.ThreadGroupUtils;
import sun.java2d.macos.MacOSFlags;
import sun.java2d.metal.MTLRenderQueue;
import sun.java2d.opengl.OGLRenderQueue;
import sun.lwawt.LWComponentPeer;
import sun.lwawt.LWCursorManager;
@@ -496,7 +498,11 @@ public final class LWCToolkit extends LWToolkit {
@Override
public void sync() {
// flush the OGL pipeline (this is a no-op if OGL is not enabled)
OGLRenderQueue.sync();
if (MacOSFlags.isMetalEnabled()) {
MTLRenderQueue.sync();
} else {
OGLRenderQueue.sync();
}
// setNeedsDisplay() selector was sent to the appropriate CALayer so now
// we have to flush the native selectors queue.
flushNativeSelectors();

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,7 @@
#import <JavaNativeFoundation/JavaNativeFoundation.h>
#import <QuartzCore/CATransaction.h>
#import <QuartzCore/CAMetalLayer.h>
@implementation AWTSurfaceLayers
@@ -67,10 +68,11 @@
}
}
// Updates back buffer size of the layer if it's an OpenGL layer
// including all OpenGL sublayers
// Updates back buffer size of the layer if it's an OpenGL/Metal layer
// including all OpenGL/Metal sublayers
+ (void) repaintLayersRecursively:(CALayer*)aLayer {
if ([aLayer isKindOfClass:[CAOpenGLLayer class]]) {
if ([aLayer isKindOfClass:[CAOpenGLLayer class]] ||
[aLayer isKindOfClass:[CAMetalLayer class]]) {
[aLayer setNeedsDisplay];
}
for(CALayer *child in aLayer.sublayers) {
@@ -92,7 +94,7 @@
/*
* Class: sun_lwawt_macosx_CPlatformComponent
* Method: nativeCreateLayer
* Method: nativeCreateComponent
* Signature: ()J
*/
JNIEXPORT jlong JNICALL

View File

@@ -37,6 +37,8 @@
#import <Carbon/Carbon.h>
#import <JavaNativeFoundation/JavaNativeFoundation.h>
jboolean metalEnabled = JNI_FALSE;
@interface AWTView()
@property (retain) CDropTarget *_dropTarget;
@property (retain) CDragSource *_dragSource;
@@ -52,6 +54,8 @@
//#define IM_DEBUG TRUE
//#define EXTRA_DEBUG
#define METAL_DEBUG
static BOOL shouldUsePressAndHold() {
static int shouldUsePressAndHold = -1;
if (shouldUsePressAndHold != -1) return shouldUsePressAndHold;
@@ -66,6 +70,7 @@ static BOOL shouldUsePressAndHold() {
@synthesize cglLayer;
@synthesize mouseIsOver;
// Note: Must be called on main (AppKit) thread only
- (id) initWithRect: (NSRect) rect
platformView: (jobject) cPlatformView
@@ -1499,3 +1504,19 @@ JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPlatformView_nativeIsViewUnder
return underMouse;
}
jboolean GetStaticBoolean(JNIEnv *env, jclass fClass, const char *fieldName)
{
jfieldID fieldID = (*env)->GetStaticFieldID(env, fClass, fieldName, "Z");
return (*env)->GetStaticBooleanField(env, fClass, fieldID);
}
JNIEXPORT void JNICALL
Java_sun_java2d_macos_MacOSFlags_initNativeFlags(JNIEnv *env,
jclass flagsClass)
{
metalEnabled = GetStaticBoolean(env, flagsClass, "metalEnabled");
#ifdef METAL_DEBUG
fprintf(stderr, "metalEnabled=%d\n", metalEnabled);
#endif
}

View File

@@ -1171,6 +1171,8 @@ JNF_COCOA_ENTER(env);
JNF_COCOA_EXIT(env);
}
extern jboolean metalEnabled;
/*
* Class: sun_lwawt_macosx_CPlatformWindow
* Method: nativeGetNSWindowInsets

View File

@@ -0,0 +1,120 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef COMMON_H
#define COMMON_H
#include <simd/SIMD.h>
#define PGRAM_VERTEX_COUNT 6
#define QUAD_VERTEX_COUNT 4
enum VertexAttributes {
VertexAttributePosition = 0,
VertexAttributeTexPos = 1
};
enum BufferIndex {
MeshVertexBuffer = 0,
FrameUniformBuffer = 1,
MatrixBuffer = 2
};
struct FrameUniforms {
vector_float4 color;
};
struct TransformMatrix {
matrix_float4x4 transformMatrix;
};
struct GradFrameUniforms {
vector_float3 params;
vector_float4 color1;
vector_float4 color2;
};
struct Vertex {
float position[2];
};
struct TxtVertex {
float position[2];
float txtpos[2];
};
// These values are mapped from AffineTransformOp
#define INTERPOLATION_NEAREST_NEIGHBOR 1
#define INTERPOLATION_BILINEAR 2
// #define INTERPOLATION_BICUBIC 3
// NOTE: Metal samplers doesn't supports bicubic interpolation
// see table 2.7 from https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf
// (probably we need to implement separate fragment shader with bicubic interpolation)
struct TxtFrameUniforms {
vector_float4 color;
int mode; // NOTE: consider to use bit fields
int isSrcOpaque;
int isDstOpaque;
float extraAlpha;
};
struct TxtFrameOpRescaleUniforms {
vector_float4 color;
float extraAlpha;
int isSrcOpaque;
int isNonPremult;
vector_float4 normScaleFactors;
vector_float4 normOffsets;
};
struct TxtFrameOpConvolveUniforms {
float extraAlpha;
vector_float4 imgEdge;
int kernelSize;
int isEdgeZeroFill;
};
struct TxtFrameOpLookupUniforms {
float extraAlpha;
vector_float4 offset;
int isUseSrcAlpha;
int isNonPremult;
};
struct AnchorData
{
vector_float3 xParams;
vector_float3 yParams;
};
struct LCDFrameUniforms {
vector_float3 src_adj;
vector_float3 gamma;
vector_float3 invgamma;
};
#endif

View File

@@ -0,0 +1,434 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <simd/simd.h>
#include <metal_stdlib>
#include "common.h"
using namespace metal;
struct VertexInput {
float2 position [[attribute(VertexAttributePosition)]];
};
struct TxtVertexInput {
float2 position [[attribute(VertexAttributePosition)]];
float2 texCoords [[attribute(VertexAttributeTexPos)]];
};
struct ColShaderInOut {
float4 position [[position]];
half4 color;
};
struct StencilShaderInOut {
float4 position [[position]];
char color;
};
struct TxtShaderInOut {
float4 position [[position]];
float2 texCoords;
float2 tpCoords;
};
struct GradShaderInOut {
float4 position [[position]];
float2 texCoords;
};
vertex ColShaderInOut vert_col(VertexInput in [[stage_in]],
constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
ColShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.color = half4(uniforms.color.r, uniforms.color.g, uniforms.color.b, uniforms.color.a);
return out;
}
vertex StencilShaderInOut vert_stencil(VertexInput in [[stage_in]],
constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
StencilShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix * pos4;
out.color = 0xFF;
return out;
}
vertex GradShaderInOut vert_grad(VertexInput in [[stage_in]], constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
GradShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
return out;
}
vertex TxtShaderInOut vert_txt(TxtVertexInput in [[stage_in]], constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
TxtShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.texCoords = in.texCoords;
return out;
}
vertex TxtShaderInOut vert_txt_tp(TxtVertexInput in [[stage_in]], constant AnchorData& anchorData [[buffer(FrameUniformBuffer)]], constant TransformMatrix& transform [[buffer(MatrixBuffer)]])
{
TxtShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix * pos4;
// Compute texture coordinates here w.r.t. anchor rect of texture paint
out.tpCoords.x = (anchorData.xParams[0] * in.position.x) +
(anchorData.xParams[1] * in.position.y) +
(anchorData.xParams[2] * out.position.w);
out.tpCoords.y = (anchorData.yParams[0] * in.position.x) +
(anchorData.yParams[1] * in.position.y) +
(anchorData.yParams[2] * out.position.w);
out.texCoords = in.texCoords;
return out;
}
vertex GradShaderInOut vert_txt_grad(TxtVertexInput in [[stage_in]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
GradShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.texCoords = in.texCoords;
return out;
}
fragment half4 frag_col(ColShaderInOut in [[stage_in]]) {
return in.color;
}
fragment unsigned int frag_stencil(StencilShaderInOut in [[stage_in]]) {
return in.color;
}
// NOTE:
// 1. consider to make shaders without IF-conditions
// 2. we can pass interpolation mode via uniforms and select corresponding sampler in shader
// but it can cause performance problems (something like getTextureSampler(hint) will be invoked
// for every pixel)
fragment half4 frag_txt(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
constant TxtFrameUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
float srcA = uniforms.isSrcOpaque ? 1 : pixelColor.a;
if (uniforms.mode) {
float4 c = mix(pixelColor, uniforms.color, srcA);
return half4(c.r, c.g, c.b , c.a);
}
return half4(pixelColor.r,
pixelColor.g,
pixelColor.b, srcA*uniforms.extraAlpha);
}
fragment half4 frag_txt_tp(TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
texture2d<float, access::sample> paintTexture [[texture(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 renderColor = renderTexture.sample(textureSampler, vert.texCoords);
float4 paintColor = paintTexture.sample(textureSampler, vert.tpCoords);
return half4(paintColor.r*renderColor.a,
paintColor.g*renderColor.a,
paintColor.b*renderColor.a,
renderColor.a);
}
fragment half4 frag_txt_grad(GradShaderInOut in [[stage_in]],
constant GradFrameUniforms& uniforms [[buffer(0)]],
texture2d<float, access::sample> renderTexture [[texture(0)]])
{
constexpr sampler textureSampler (address::repeat, mag_filter::nearest,
min_filter::nearest);
float4 renderColor = renderTexture.sample(textureSampler, in.texCoords);
float3 v = float3(in.position.x, in.position.y, 1);
float a = (dot(v,uniforms.params)-0.25)*2.0;
float4 c = mix(uniforms.color1, uniforms.color2, a);
return half4(c.r*renderColor.a,
c.g*renderColor.a,
c.b*renderColor.a,
renderColor.a);
}
fragment half4 aa_frag_txt(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
texture2d<float, access::sample> stencilTexture [[texture(1)]],
constant TxtFrameUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
if (!is_null_texture(stencilTexture)) {
float4 stencil = stencilTexture.sample(textureSampler, vert.texCoords);
if (stencil.r == 0.0) {
discard_fragment();
}
}
return half4(pixelColor.r, pixelColor.g, pixelColor.b, pixelColor.a);
}
fragment half4 frag_txt_op_rescale(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> srcTex [[texture(0)]],
constant TxtFrameOpRescaleUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 srcColor = srcTex.sample(textureSampler, vert.texCoords);
const float srcA = uniforms.isSrcOpaque ? 1 : srcColor.a;
// TODO: check uniforms.isNonPremult and pre-multiply if necessary
return half4(srcColor.r*uniforms.normScaleFactors.r + uniforms.normOffsets.r,
srcColor.g*uniforms.normScaleFactors.g + uniforms.normOffsets.g,
srcColor.b*uniforms.normScaleFactors.b + uniforms.normOffsets.b, srcA*uniforms.extraAlpha);
// NOTE: GL-shader multiplies result with glColor (in order to apply extra alpha), probably it's better to do the
// same here.
//
// GL-shader impl:
//" vec4 srcColor = texture%s(baseImage, gl_TexCoord[0].st);"
//" %s" // (placeholder for un-premult code: srcColor.rgb /= srcColor.a;)
//" vec4 result = (srcColor * scaleFactors) + offsets;" // rescale source value
//" %s" // (placeholder for re-premult code: result.rgb *= result.a;)
//" gl_FragColor = result * gl_Color;" // modulate with gl_Color in order to apply extra alpha
}
fragment half4 frag_txt_op_convolve(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> srcTex [[texture(0)]],
constant TxtFrameOpConvolveUniforms& uniforms [[buffer(1)]],
const device float3 * kernelVals [[buffer(2)]],
sampler textureSampler [[sampler(0)]]
) {
float4 sum = float4(0, 0, 0, 0);
if (vert.texCoords[0] < uniforms.imgEdge[0]
|| vert.texCoords[1] < uniforms.imgEdge[1]
|| vert.texCoords[0] > uniforms.imgEdge[2]
|| vert.texCoords[1] > uniforms.imgEdge[3]
) {
if (!uniforms.isEdgeZeroFill) {
sum = srcTex.sample(textureSampler, vert.texCoords);
}
}
for (int i = 0; i < uniforms.kernelSize; i++) {
float3 kern = kernelVals[i];
float2 pos = float2(vert.texCoords.x + kern.x, vert.texCoords.y + kern.y);
float4 pixCol = srcTex.sample(textureSampler, pos);
sum.r += kern.z * pixCol.r;
sum.g += kern.z * pixCol.g;
sum.b += kern.z * pixCol.b;
sum.a += kern.z * pixCol.a;
}
return half4(sum.r, sum.g, sum.b, sum.a*uniforms.extraAlpha);
// NOTE: GL-shader multiplies result with glColor (in order to apply extra alpha), probably it's better to do the
// same here.
//
// GL-shader impl:
//" if (any(lessThan(gl_TexCoord[0].st, imgEdge.xy)) ||"
//" any(greaterThan(gl_TexCoord[0].st, imgEdge.zw)))"
//" {"
//" %s" // (placeholder for edge condition code)
//" } else {"
//" sum = vec4(0.0);"
//" for (i = 0; i < MAX_KERNEL_SIZE; i++) {"
//" sum +="
//" kernelVals[i].z *"
//" texture%s(baseImage,"
//" gl_TexCoord[0].st + kernelVals[i].xy);"
//" }"
//" }"
//""
//" gl_FragColor = sum * gl_Color;" // modulate with gl_Color in order to apply extra alpha
}
fragment half4 frag_txt_op_lookup(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> srcTex [[texture(0)]],
texture2d<float, access::sample> lookupTex [[texture(1)]],
constant TxtFrameOpLookupUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 srcColor = srcTex.sample(textureSampler, vert.texCoords);
float4 srcIndex = srcColor - uniforms.offset;
const float2 posR = float2(srcIndex.r, 0.125);
const float2 posG = float2(srcIndex.g, 0.375);
const float2 posB = float2(srcIndex.b, 0.625);
float4 lookupR = lookupTex.sample(textureSampler, posR);
float4 lookupG = lookupTex.sample(textureSampler, posG);
float4 lookupB = lookupTex.sample(textureSampler, posB);
const float a = uniforms.isUseSrcAlpha ? srcColor.a : lookupTex.sample(textureSampler, float2(srcIndex.a, 0.875)).a;
// TODO: check uniforms.isNonPremult and pre-multiply if necessary
return half4(lookupR.a, lookupG.a, lookupB.a, a*uniforms.extraAlpha);
// NOTE: GL-shader multiplies result with glColor (in order to apply extra alpha), probably it's better to do the
// same here.
//
// GL-shader impl:
//" vec4 srcColor = texture%s(baseImage, gl_TexCoord[0].st);"
//" %s" // (placeholder for un-premult code)
//" vec4 srcIndex = srcColor - offset;" // subtract offset from original index
//
// // use source value as input to lookup table (note that
// // "v" texcoords are hardcoded to hit texel centers of
// // each row/band in texture)
//" vec4 result;"
//" result.r = texture2D(lookupTable, vec2(srcIndex.r, 0.125)).r;"
//" result.g = texture2D(lookupTable, vec2(srcIndex.g, 0.375)).r;"
//" result.b = texture2D(lookupTable, vec2(srcIndex.b, 0.625)).r;"
//" %s" // (placeholder for alpha store code)
//" %s" // (placeholder for re-premult code)
//" gl_FragColor = result * gl_Color;" // modulate with gl_Color in order to apply extra alpha
}
fragment half4 frag_grad(GradShaderInOut in [[stage_in]],
constant GradFrameUniforms& uniforms [[buffer(0)]]) {
float3 v = float3(in.position.x, in.position.y, 1);
float a = (dot(v,uniforms.params)-0.25)*2.0;
float4 c = mix(uniforms.color1, uniforms.color2, a);
return half4(c);
}
vertex TxtShaderInOut vert_tp(VertexInput in [[stage_in]],
constant AnchorData& anchorData [[buffer(FrameUniformBuffer)]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]])
{
TxtShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix * pos4;
// Compute texture coordinates here w.r.t. anchor rect of texture paint
out.texCoords.x = (anchorData.xParams[0] * in.position.x) +
(anchorData.xParams[1] * in.position.y) +
(anchorData.xParams[2] * out.position.w);
out.texCoords.y = (anchorData.yParams[0] * in.position.x) +
(anchorData.yParams[1] * in.position.y) +
(anchorData.yParams[2] * out.position.w);
return out;
}
fragment half4 frag_tp(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]])
{
constexpr sampler textureSampler (address::repeat,
mag_filter::nearest,
min_filter::nearest);
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
return half4(pixelColor.r, pixelColor.g, pixelColor.b, 1.0);
// This implementation defaults alpha to 1.0 as if source is opaque
//TODO : implement alpha component value if source is transparent
}
fragment half4 frag_tp_xorMode(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
constant int& xorColor[[buffer(0)]])
{
constexpr sampler textureSampler (address::repeat,
mag_filter::nearest,
min_filter::nearest);
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
pixelColor.r = float( (unsigned char)(pixelColor.r * 255.0) ^ ((xorColor >> 16) & 0xFF) ) / 255.0f;
pixelColor.g = float( (unsigned char)(pixelColor.g * 255.0) ^ ((xorColor >> 8) & 0xFF)) / 255.0f;
pixelColor.b = float( (unsigned char)(pixelColor.b * 255.0) ^ (xorColor & 0xFF)) / 255.0f;
pixelColor.a = 1.0;
return half4(pixelColor.r, pixelColor.g, pixelColor.b, 1.0);
// This implementation defaults alpha to 1.0 as if source is opaque
//TODO : implement alpha component value if source is transparent
}
/* The variables involved in the equation can be expressed as follows:
*
* Cs = Color component of the source (foreground color) [0.0, 1.0]
* Cd = Color component of the destination (background color) [0.0, 1.0]
* Cr = Color component to be written to the destination [0.0, 1.0]
* Ag = Glyph alpha (aka intensity or coverage) [0.0, 1.0]
* Ga = Gamma adjustment in the range [1.0, 2.5]
* (^ means raised to the power)
*
* And here is the theoretical equation approximated by this shader:
*
* Cr = (Ag*(Cs^Ga) + (1-Ag)*(Cd^Ga)) ^ (1/Ga)
*/
fragment float4 lcd_color(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> glyphTexture [[texture(0)]],
texture2d<float, access::sample> dstTexture [[texture(1)]],
constant LCDFrameUniforms& uniforms [[buffer(1)]])
{
float3 src_adj = uniforms.src_adj;
float3 gamma = uniforms.gamma;
float3 invgamma = uniforms.invgamma;
constexpr sampler glyphTextureSampler (mag_filter::linear,
min_filter::linear);
// load the RGB value from the glyph image at the current texcoord
float3 glyph_clr = float3(glyphTexture.sample(glyphTextureSampler, vert.texCoords));
if (glyph_clr.r == 0.0f && glyph_clr.g == 0.0f && glyph_clr.b == 0.0f) {
// zero coverage, so skip this fragment
discard_fragment();
}
constexpr sampler dstTextureSampler (mag_filter::linear,
min_filter::linear);
// load the RGB value from the corresponding destination pixel
float3 dst_clr = float3(dstTexture.sample(dstTextureSampler, vert.texCoords));
// gamma adjust the dest color
float3 dst_adj = pow(dst_clr.rgb, gamma);
// linearly interpolate the three color values
float3 result = mix(dst_adj, src_adj, glyph_clr);
// gamma re-adjust the resulting color (alpha is always set to 1.0)
return float4(pow(result.rgb, invgamma), 1.0);
}

View File

@@ -0,0 +1,67 @@
#ifndef EncoderManager_h_Included
#define EncoderManager_h_Included
#import <Metal/Metal.h>
#include "RenderOptions.h"
@class MTLContex;
/**
* The EncoderManager class used to obtain MTLRenderCommandEncoder (or MTLBlitCommandEncoder) corresponding
* to the current state of MTLContext.
*
* Due to performance issues (creation of MTLRenderCommandEncoder isn't cheap), each getXXXEncoder invocation
* updates properties of common (cached) encoder and returns this encoder.
*
* Base method getEncoder does the following:
* 1. Checks whether common encoder must be closed and recreated (some of encoder properties is 'persistent',
* for example destination, stencil, or any other property of MTLRenderPassDescriptor)
* 2. Updates 'mutable' properties encoder: pipelineState (with corresponding buffers), clip, transform, e.t.c. To avoid
* unnecessary calls of [encoder setXXX] this manager compares requested state with cached one.
* */
@interface EncoderManager : NSObject
- (id _Nonnull)init;
- (void)dealloc;
- (void)setContext:(MTLContex * _Nonnull)mtlc;
// returns encoder that renders/fills geometry with current paint and composite
- (id<MTLRenderCommandEncoder> _Nonnull)getRenderEncoder:(const BMTLSDOps * _Nonnull)dstOps;
- (id<MTLRenderCommandEncoder> _Nonnull)getAARenderEncoder:(const BMTLSDOps * _Nonnull)dstOps;
- (id<MTLRenderCommandEncoder> _Nonnull)getRenderEncoder:(id<MTLTexture> _Nonnull)dest
isDstOpaque:(bool)isOpaque;
// returns encoder that renders/fills geometry with current composite and with given texture
// (user must call [encoder setFragmentTexture] before any rendering)
- (id<MTLRenderCommandEncoder> _Nonnull)getTextureEncoder:(const BMTLSDOps * _Nonnull)dstOps
isSrcOpaque:(bool)isSrcOpaque;
- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque;
- (id<MTLRenderCommandEncoder> _Nonnull)getTextureEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque
interpolation:(int)interpolation;
- (id<MTLRenderCommandEncoder> _Nonnull)getTextureEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque
interpolation:(int)interpolation
isAA:(jboolean)isAA;
// Base method to obtain any MTLRenderCommandEncoder
- (id<MTLRenderCommandEncoder> _Nonnull) getEncoder:(id<MTLTexture> _Nonnull)dest
isDestOpaque:(jboolean)isDestOpaque
renderOptions:(const RenderOptions * _Nonnull)renderOptions;
- (id<MTLBlitCommandEncoder> _Nonnull)createBlitEncoder;
- (void)endEncoder;
@end
#endif // EncoderManager_h_Included

View File

@@ -0,0 +1,437 @@
#include "EncoderManager.h"
#include "MTLContext.h"
#include "sun_java2d_SunGraphics2D.h"
#import "common.h"
// NOTE: uncomment to disable comparing cached encoder states with requested (for debugging)
// #define ALWAYS_UPDATE_ENCODER_STATES
const SurfaceRasterFlags defaultRasterFlags = { JNI_FALSE, JNI_TRUE };
// Internal utility class that represents the set of 'mutable' encoder properties
@interface EncoderStates : NSObject
@property (readonly) MTLClip * clip;
- (id)init;
- (void)dealloc;
- (void)reset:(id<MTLTexture>)destination
isDstOpaque:(jboolean)isDstOpaque
isDstPremultiplied:(jboolean)isDstPremultiplied
isAA:(jboolean)isAA;
- (void)updateEncoder:(id<MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
forceUpdate:(jboolean)forceUpdate;
@property jboolean aa;
@end
@implementation EncoderStates {
MTLPipelineStatesStorage * _pipelineStateStorage;
id<MTLDevice> _device;
// Persistent encoder properties
id<MTLTexture> _destination;
SurfaceRasterFlags _dstFlags;
jboolean _isAA;
//
// Cached 'mutable' states of encoder
//
// Composite rule and source raster flags (it affects the CAD-multipliers (of pipelineState))
MTLComposite * _composite;
SurfaceRasterFlags _srcFlags;
// Paint mode (it affects shaders (of pipelineState) and corresponding buffers)
MTLPaint * _paint;
// If true, indicates that encoder is used for texture drawing (user must do [encoder setFragmentTexture:] before drawing)
jboolean _isTexture;
int _interpolationMode;
// Clip rect or stencil
MTLClip * _clip;
// Transform (affects transformation inside vertex shader)
MTLTransform * _transform;
}
@synthesize aa = _isAA;
- (id)init {
self = [super init];
if (self) {
_destination = nil;
_composite = [[MTLComposite alloc] init];
_paint = [[MTLPaint alloc] init];
_transform = [[MTLTransform alloc] init];
_clip = [[MTLClip alloc] init];
}
return self;
}
- (void)dealloc {
[_composite release];
[_paint release];
[_transform release];
[super dealloc];
}
- (void)setContext:(MTLContext * _Nonnull)mtlc {
self->_pipelineStateStorage = mtlc.pipelineStateStorage;
self->_device = mtlc.device;
}
- (void)reset:(id<MTLTexture>)destination
isDstOpaque:(jboolean)isDstOpaque
isDstPremultiplied:(jboolean)isDstPremultiplied
isAA:(jboolean)isAA {
_destination = destination;
_dstFlags.isOpaque = isDstOpaque;
_dstFlags.isPremultiplied = isDstPremultiplied;
_isAA = isAA;
// NOTE: probably it's better to invalidate/reset all cached states now
}
- (void)updateEncoder:(id<MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
forceUpdate:(jboolean)forceUpdate
{
// 1. Process special case for stencil mask generation
if (mtlc.clip.stencilMaskGenerationInProgress == JNI_TRUE) {
// use separate pipeline state for stencil generation
if (forceUpdate || (_clip.stencilMaskGenerationInProgress != JNI_TRUE)) {
[_clip copyFrom:mtlc.clip];
[_clip setMaskGenerationPipelineState:encoder
destWidth:_destination.width
destHeight:_destination.height
pipelineStateStorage:_pipelineStateStorage];
}
[self updateTransform:encoder transform:mtlc.transform forceUpdate:forceUpdate];
return;
}
// 2. Otherwise update all 'mutable' properties of encoder
[self updatePipelineState:encoder
context:mtlc
renderOptions:renderOptions
forceUpdate:forceUpdate];
[self updateTransform:encoder transform:mtlc.transform forceUpdate:forceUpdate];
[self updateClip:encoder clip:mtlc.clip forceUpdate:forceUpdate];
}
//
// Internal methods that update states when necessary (compare with cached states)
//
// Updates pipelineState (and corresponding buffers) with use of paint+composite+flags
- (void)updatePipelineState:(id<MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
forceUpdate:(jboolean)forceUpdate
{
if (!forceUpdate
&& [_paint isEqual:mtlc.paint]
&& [_composite isEqual:mtlc.composite]
&& (_isTexture == renderOptions->isTexture && (!renderOptions->isTexture || _interpolationMode == renderOptions->interpolation)) // interpolation is used only in texture mode
&& _isAA == renderOptions->isAA
&& _srcFlags.isOpaque == renderOptions->srcFlags.isOpaque && _srcFlags.isPremultiplied == renderOptions->srcFlags.isPremultiplied)
return;
[_paint copyFrom:mtlc.paint];
[_composite copyFrom:mtlc.composite];
_isTexture = renderOptions->isTexture;
_interpolationMode = renderOptions->interpolation;
_isAA = renderOptions->isAA;
_srcFlags = renderOptions->srcFlags;
if ((jint)[mtlc.composite getCompositeState] == sun_java2d_SunGraphics2D_COMP_XOR) {
[mtlc.paint setXorModePipelineState:encoder
context:mtlc
renderOptions:renderOptions
pipelineStateStorage:_pipelineStateStorage];
} else {
[mtlc.paint setPipelineState:encoder
context:mtlc
renderOptions:renderOptions
pipelineStateStorage:_pipelineStateStorage];
}
}
- (void) updateClip:(id<MTLRenderCommandEncoder>)encoder clip:(MTLClip *)clip forceUpdate:(jboolean)forceUpdate
{
if (clip.stencilMaskGenerationInProgress == JNI_TRUE) {
// don't set setScissorOrStencil when generateion in progress
return;
}
if (!forceUpdate && [_clip isEqual:clip])
return;
[_clip copyFrom:clip];
[_clip setScissorOrStencil:encoder
destWidth:_destination.width
destHeight:_destination.height
device:_device];
}
- (void)updateTransform:(id <MTLRenderCommandEncoder>)encoder
transform:(MTLTransform *)transform
forceUpdate:(jboolean)forceUpdate
{
if (!forceUpdate
&& [_transform isEqual:transform])
return;
[_transform copyFrom:transform];
[_transform setVertexMatrix:encoder
destWidth:_destination.width
destHeight:_destination.height];
}
@end
@implementation EncoderManager {
MTLContext * _mtlc; // used to obtain CommandBufferWrapper and Composite/Paint/Transform
id<MTLRenderCommandEncoder> _encoder;
// 'Persistent' properties of encoder
id<MTLTexture> _destination;
id<MTLTexture> _aaDestination;
BOOL _useStencil;
// 'Mutable' states of encoder
EncoderStates * _encoderStates;
}
- (id _Nonnull)init {
self = [super init];
if (self) {
_encoder = nil;
_destination = nil;
_aaDestination = nil;
_useStencil = NO;
_encoderStates = [[EncoderStates alloc] init];
}
return self;
}
- (void)dealloc {
[_encoderStates release];
[super dealloc];
}
- (void)setContext:(MTLContex * _Nonnull)mtlc {
self->_mtlc = mtlc;
[self->_encoderStates setContext:mtlc];
}
- (id<MTLRenderCommandEncoder> _Nonnull) getRenderEncoder:(const BMTLSDOps * _Nonnull)dstOps
{
return [self getRenderEncoder:dstOps->pTexture isDstOpaque:dstOps->isOpaque];
}
- (id<MTLRenderCommandEncoder> _Nonnull)getAARenderEncoder:(const BMTLSDOps * _Nonnull)dstOps {
id<MTLTexture> dstTxt = dstOps->pTexture;
RenderOptions roptions = {JNI_FALSE, JNI_TRUE, INTERPOLATION_NEAREST_NEIGHBOR, defaultRasterFlags, {dstOps->isOpaque, JNI_TRUE}};
return [self getEncoder:dstTxt renderOptions:&roptions];
}
- (id<MTLRenderCommandEncoder> _Nonnull)getRenderEncoder:(id<MTLTexture> _Nonnull)dest
isDstOpaque:(bool)isOpaque
{
RenderOptions roptions = {JNI_FALSE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, defaultRasterFlags, {isOpaque, JNI_TRUE}};
return [self getEncoder:dest renderOptions:&roptions];
}
- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(const BMTLSDOps * _Nonnull)dstOps
isSrcOpaque:(bool)isSrcOpaque
{
return [self getTextureEncoder:dstOps->pTexture
isSrcOpaque:isSrcOpaque
isDstOpaque:dstOps->isOpaque
interpolation:INTERPOLATION_NEAREST_NEIGHBOR];
}
- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque
{
return [self getTextureEncoder:dest
isSrcOpaque:isSrcOpaque
isDstOpaque:isDstOpaque
interpolation:INTERPOLATION_NEAREST_NEIGHBOR
isAA:JNI_FALSE];
}
- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque
interpolation:(int)interpolation
isAA:(jboolean)isAA
{
RenderOptions roptions = {JNI_TRUE, isAA, interpolation, { isSrcOpaque, JNI_TRUE }, {isDstOpaque, JNI_TRUE}};
return [self getEncoder:dest renderOptions:&roptions];
}
- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque
interpolation:(int)interpolation
{
return [self getTextureEncoder:dest isSrcOpaque:isSrcOpaque isDstOpaque:isDstOpaque interpolation:interpolation isAA:JNI_FALSE];
}
- (id<MTLRenderCommandEncoder> _Nonnull) getEncoder:(id <MTLTexture> _Nonnull)dest
renderOptions:(const RenderOptions * _Nonnull)renderOptions
{
//
// 1. check whether it's necessary to call endEncoder
//
jboolean needEnd = JNI_FALSE;
if (_encoder != nil) {
if (_destination != dest || renderOptions->isAA != _encoderStates.aa) {
J2dTraceLn2(J2D_TRACE_VERBOSE,
"end common encoder because of dest change: %p -> %p",
_destination, dest);
needEnd = JNI_TRUE;
} else if ((_useStencil == NO) != ([_mtlc.clip isShape] == NO)) {
// 1. When mode changes RECT -> SHAPE we must recreate encoder with
// stencilAttachment (todo: consider the case when current encoder already
// has stencil)
//
// 2. When mode changes SHAPE -> RECT it seems that we can use the same
// encoder with disabled stencil test, but [encoder
// setDepthStencilState:nil] causes crash, so we have to recreate encoder
// in such case
J2dTraceLn2(J2D_TRACE_VERBOSE,
"end common encoder because toggle stencil: %d -> %d",
(int)_useStencil, (int)[_mtlc.clip isShape]);
needEnd = JNI_TRUE;
}
}
if (needEnd)
[self endEncoder];
//
// 2. recreate encoder if necessary
//
jboolean forceUpdate = JNI_FALSE;
#ifdef ALWAYS_UPDATE_ENCODER_STATES
forceUpdate = JNI_TRUE;
#endif // ALWAYS_UPDATE_ENCODER_STATES
if (_encoder == nil) {
_destination = dest;
_useStencil = [_mtlc.clip isShape];
forceUpdate = JNI_TRUE;
MTLCommandBufferWrapper *cbw = [_mtlc getCommandBufferWrapper];
MTLRenderPassDescriptor *rpd =
[MTLRenderPassDescriptor renderPassDescriptor];
MTLRenderPassColorAttachmentDescriptor *ca = rpd.colorAttachments[0];
if (renderOptions->isAA && !renderOptions->isTexture) {
MTLTexturePoolItem *tiBuf = [_mtlc.texturePool getTexture:dest.width
height:dest.height
format:MTLPixelFormatBGRA8Unorm];
[cbw registerPooledTexture:tiBuf];
[tiBuf release];
_aaDestination = tiBuf.texture;
MTLTexturePoolItem *ti = [_mtlc.texturePool getTexture:dest.width
height:dest.height
format:_aaDestination.pixelFormat
isMultiSample:YES];
[cbw registerPooledTexture:ti];
[ti release];
ca.texture = ti.texture;
ca.resolveTexture = _aaDestination;
ca.clearColor = MTLClearColorMake(0.0f, 0.0f, 0.0f, 0.0f);
ca.loadAction = MTLLoadActionClear;
ca.storeAction = MTLStoreActionMultisampleResolve;
} else {
ca.texture = dest;
ca.loadAction = MTLLoadActionLoad;
ca.storeAction = MTLStoreActionStore;
}
if (_useStencil && !renderOptions->isAA) {
// If you enable stencil testing or stencil writing, the
// MTLRenderPassDescriptor must include a stencil attachment.
rpd.stencilAttachment.loadAction = MTLLoadActionLoad;
rpd.stencilAttachment.storeAction = MTLStoreActionStore;
rpd.stencilAttachment.texture = _mtlc.clip.stencilTextureRef;
}
// J2dTraceLn1(J2D_TRACE_VERBOSE, "created render encoder to draw on
// tex=%p", dest);
_encoder = [[cbw getCommandBuffer] renderCommandEncoderWithDescriptor:rpd];
[rpd release];
[_encoderStates reset:dest
isDstOpaque:renderOptions->dstFlags.isOpaque
isDstPremultiplied:YES
isAA:renderOptions->isAA];
}
//
// 3. update encoder states
//
[_encoderStates updateEncoder:_encoder
context:_mtlc
renderOptions:renderOptions
forceUpdate:forceUpdate];
return _encoder;
}
- (id<MTLBlitCommandEncoder> _Nonnull) createBlitEncoder {
[self endEncoder];
return [[[_mtlc getCommandBufferWrapper] getCommandBuffer] blitCommandEncoder];
}
- (void) endEncoder {
if (_encoder != nil) {
[_encoder endEncoding];
[_encoder release];
_encoder = nil;
if (_aaDestination != nil) {
id<MTLTexture> aaDest = _aaDestination;
_aaDestination = nil;
NSUInteger _w = _destination.width;
NSUInteger _h = _destination.height;
_encoder = [self getTextureEncoder:_destination
isSrcOpaque:JNI_FALSE
isDstOpaque:JNI_TRUE
interpolation:INTERPOLATION_NEAREST_NEIGHBOR
isAA:JNI_TRUE];
[_encoder setFragmentTexture:_mtlc.clip.stencilAADataRef atIndex: 1];
struct TxtVertex quadTxVerticesBuffer[] = {
{{0, 0}, {0, 0}},
{{0,_h}, {0, 1}},
{{_w, 0},{1, 0}},
{{0, _h},{0, 1}},
{{_w, _h}, {1, 1}},
{{_w, 0}, {1, 0}}
};
[_encoder setVertexBytes:quadTxVerticesBuffer length:sizeof(quadTxVerticesBuffer) atIndex:MeshVertexBuffer];
[_encoder setFragmentTexture:aaDest atIndex: 0];
[_encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];
[_encoder endEncoding];
[_encoder release];
}
_encoder = nil;
_destination = nil;
}
}
@end

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLBlitLoops_h_Included
#define MTLBlitLoops_h_Included
#include "sun_java2d_metal_MTLBlitLoops.h"
#include "MTLSurfaceDataBase.h"
#include "MTLContext.h"
#define OFFSET_SRCTYPE sun_java2d_metal_MTLBlitLoops_OFFSET_SRCTYPE
#define OFFSET_HINT sun_java2d_metal_MTLBlitLoops_OFFSET_HINT
#define OFFSET_TEXTURE sun_java2d_metal_MTLBlitLoops_OFFSET_TEXTURE
#define OFFSET_RTT sun_java2d_metal_MTLBlitLoops_OFFSET_RTT
#define OFFSET_XFORM sun_java2d_metal_MTLBlitLoops_OFFSET_XFORM
#define OFFSET_ISOBLIT sun_java2d_metal_MTLBlitLoops_OFFSET_ISOBLIT
void MTLBlitLoops_IsoBlit(JNIEnv *env,
MTLContext *mtlc, jlong pSrcOps, jlong pDstOps,
jboolean xform, jint hint,
jboolean texture,
jint sx1, jint sy1,
jint sx2, jint sy2,
jdouble dx1, jdouble dy1,
jdouble dx2, jdouble dy2);
void MTLBlitLoops_Blit(JNIEnv *env,
MTLContext *mtlc, jlong pSrcOps, jlong pDstOps,
jboolean xform, jint hint,
jint srctype, jboolean texture,
jint sx1, jint sy1,
jint sx2, jint sy2,
jdouble dx1, jdouble dy1,
jdouble dx2, jdouble dy2);
void MTLBlitLoops_SurfaceToSwBlit(JNIEnv *env, MTLContext *mtlc,
jlong pSrcOps, jlong pDstOps, jint dsttype,
jint srcx, jint srcy,
jint dstx, jint dsty,
jint width, jint height);
void MTLBlitLoops_CopyArea(JNIEnv *env,
MTLContext *mtlc, BMTLSDOps *dstOps,
jint x, jint y,
jint width, jint height,
jint dx, jint dy);
void MTLBlitTex2Tex(MTLContext *mtlc, id<MTLTexture> src, id<MTLTexture> dest);
void drawTex2Tex(MTLContext *mtlc,
id<MTLTexture> src, id<MTLTexture> dst,
jboolean isSrcOpaque, jboolean isDstOpaque, jint hint,
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2);
#endif /* MTLBlitLoops_h_Included */

View File

@@ -0,0 +1,830 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <jni.h>
#include <jlong.h>
#include "SurfaceData.h"
#include "MTLBlitLoops.h"
#include "MTLRenderQueue.h"
#include "MTLSurfaceData.h"
#include "MTLUtils.h"
#include "GraphicsPrimitiveMgr.h"
#include <stdlib.h> // malloc
#include <string.h> // memcpy
#include "IntArgbPre.h"
#import <Accelerate/Accelerate.h>
#ifdef DEBUG
#define TRACE_ISOBLIT
#define TRACE_BLIT
#endif //DEBUG
//#define DEBUG_ISOBLIT
//#define DEBUG_BLIT
typedef struct {
MTLPixelFormat format;
jboolean hasAlpha;
jboolean isPremult;
const uint8_t * permuteMap;
} MTLRasterFormatInfo;
// 0 denotes the alpha channel, 1 the red channel, 2 the green channel, and 3 the blue channel.
const uint8_t permuteMap_rgbx[4] = { 1, 2, 3, 0 };
const uint8_t permuteMap_bgrx[4] = { 3, 2, 1, 0 };
static uint8_t revertPerm(const uint8_t * perm, uint8_t pos) {
for (int c = 0; c < 4; ++c) {
if (perm[c] == pos)
return c;
}
return -1;
}
#define uint2swizzle(channel) (channel == 0 ? MTLTextureSwizzleAlpha : (channel == 1 ? MTLTextureSwizzleRed : (channel == 2 ? MTLTextureSwizzleGreen : (channel == 3 ? MTLTextureSwizzleBlue : MTLTextureSwizzleZero))))
/**
* This table contains the "pixel formats" for all system memory surfaces
* that Metal is capable of handling, indexed by the "PF_" constants defined
* in MTLLSurfaceData.java. These pixel formats contain information that is
* passed to Metal when copying from a system memory ("Sw") surface to
* an Metal surface
*/
MTLRasterFormatInfo RasterFormatInfos[] = {
{ MTLPixelFormatBGRA8Unorm, 1, 0, NULL }, /* 0 - IntArgb */ // Argb (in java notation)
{ MTLPixelFormatBGRA8Unorm, 1, 1, NULL }, /* 1 - IntArgbPre */
{ MTLPixelFormatBGRA8Unorm, 0, 1, NULL }, /* 2 - IntRgb */ // xrgb
{ MTLPixelFormatBGRA8Unorm, 0, 1, permuteMap_rgbx }, /* 3 - IntRgbx */
{ MTLPixelFormatRGBA8Unorm, 0, 1, NULL }, /* 4 - IntBgr */ // xbgr
{ MTLPixelFormatBGRA8Unorm, 0, 1, permuteMap_bgrx }, /* 5 - IntBgrx */
// TODO: support 2-byte formats
// { GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV,
// 2, 0, 1, }, /* 7 - Ushort555Rgb */
// { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1,
// 2, 0, 1, }, /* 8 - Ushort555Rgbx*/
// { GL_LUMINANCE, GL_UNSIGNED_BYTE,
// 1, 0, 1, }, /* 9 - ByteGray */
// { GL_LUMINANCE, GL_UNSIGNED_SHORT,
// 2, 0, 1, }, /*10 - UshortGray */
// { GL_BGR, GL_UNSIGNED_BYTE,
// 1, 0, 1, }, /*11 - ThreeByteBgr */
};
extern void J2dTraceImpl(int level, jboolean cr, const char *string, ...);
void fillTxQuad(
struct TxtVertex * txQuadVerts,
jint sx1, jint sy1, jint sx2, jint sy2, jint sw, jint sh,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2, jdouble dw, jdouble dh
) {
const float nsx1 = sx1/(float)sw;
const float nsy1 = sy1/(float)sh;
const float nsx2 = sx2/(float)sw;
const float nsy2 = sy2/(float)sh;
txQuadVerts[0].position[0] = dx1;
txQuadVerts[0].position[1] = dy1;
txQuadVerts[0].txtpos[0] = nsx1;
txQuadVerts[0].txtpos[1] = nsy1;
txQuadVerts[1].position[0] = dx2;
txQuadVerts[1].position[1] = dy1;
txQuadVerts[1].txtpos[0] = nsx2;
txQuadVerts[1].txtpos[1] = nsy1;
txQuadVerts[2].position[0] = dx2;
txQuadVerts[2].position[1] = dy2;
txQuadVerts[2].txtpos[0] = nsx2;
txQuadVerts[2].txtpos[1] = nsy2;
txQuadVerts[3].position[0] = dx2;
txQuadVerts[3].position[1] = dy2;
txQuadVerts[3].txtpos[0] = nsx2;
txQuadVerts[3].txtpos[1] = nsy2;
txQuadVerts[4].position[0] = dx1;
txQuadVerts[4].position[1] = dy2;
txQuadVerts[4].txtpos[0] = nsx1;
txQuadVerts[4].txtpos[1] = nsy2;
txQuadVerts[5].position[0] = dx1;
txQuadVerts[5].position[1] = dy1;
txQuadVerts[5].txtpos[0] = nsx1;
txQuadVerts[5].txtpos[1] = nsy1;
}
//#define TRACE_drawTex2Tex
void drawTex2Tex(MTLContext *mtlc,
id<MTLTexture> src, id<MTLTexture> dst,
jboolean isSrcOpaque, jboolean isDstOpaque, jint hint,
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
{
#ifdef TRACE_drawTex2Tex
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "drawTex2Tex: src tex=%p, dst tex=%p", src, dst);
J2dRlsTraceLn4(J2D_TRACE_VERBOSE, " sw=%d sh=%d dw=%d dh=%d", src.width, src.height, dst.width, dst.height);
J2dRlsTraceLn4(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d", sx1, sy1, sx2, sy2);
J2dRlsTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f", dx1, dy1, dx2, dy2);
#endif //TRACE_drawTex2Tex
id<MTLRenderCommandEncoder> encoder = [mtlc.encoderManager getTextureEncoder:dst
isSrcOpaque:isSrcOpaque
isDstOpaque:isDstOpaque
interpolation:hint
];
struct TxtVertex quadTxVerticesBuffer[6];
fillTxQuad(quadTxVerticesBuffer, sx1, sy1, sx2, sy2, src.width, src.height, dx1, dy1, dx2, dy2, dst.width, dst.height);
[encoder setVertexBytes:quadTxVerticesBuffer length:sizeof(quadTxVerticesBuffer) atIndex:MeshVertexBuffer];
[encoder setFragmentTexture:src atIndex: 0];
[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];
}
static
id<MTLTexture> replaceTextureRegion(id<MTLTexture> dest, const SurfaceDataRasInfo * srcInfo, const MTLRasterFormatInfo * rfi, int dx1, int dy1, int dx2, int dy2) {
const int dw = dx2 - dx1;
const int dh = dy2 - dy1;
const void * raster = srcInfo->rasBase;
id<MTLTexture> result = nil;
if (rfi->permuteMap != NULL) {
#if defined(__MAC_10_15) && __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_15
if (@available(macOS 10.15, *)) {
@autoreleasepool {
const uint8_t swzRed = revertPerm(rfi->permuteMap, 1);
const uint8_t swzGreen = revertPerm(rfi->permuteMap, 2);
const uint8_t swzBlue = revertPerm(rfi->permuteMap, 3);
const uint8_t swzAlpha = revertPerm(rfi->permuteMap, 0);
MTLTextureSwizzleChannels swizzle = MTLTextureSwizzleChannelsMake(
uint2swizzle(swzRed),
uint2swizzle(swzGreen),
uint2swizzle(swzBlue),
rfi->hasAlpha ? uint2swizzle(swzAlpha) : MTLTextureSwizzleOne
);
result = [dest
newTextureViewWithPixelFormat:MTLPixelFormatBGRA8Unorm
textureType:MTLTextureType2D
levels:NSMakeRange(0, 1) slices:NSMakeRange(0, 1)
swizzle:swizzle];
J2dTraceLn5(J2D_TRACE_VERBOSE, "replaceTextureRegion [use swizzle for pooled]: %d, %d, %d, %d, hasA=%d",
swizzle.red, swizzle.green, swizzle.blue, swizzle.alpha, rfi->hasAlpha);
}
} else
#endif // __MAC_10_15 && __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_15
{
// perform raster conversion
// invoked only from rq-thread, so use static buffers
// but it's better to use thread-local buffers (or special buffer manager)
const int destRasterSize = dw*dh*4;
static int bufferSize = 0;
static void * buffer = NULL;
if (buffer == NULL || bufferSize < destRasterSize) {
bufferSize = destRasterSize;
buffer = realloc(buffer, bufferSize);
}
if (buffer == NULL) {
J2dTraceLn1(J2D_TRACE_ERROR, "replaceTextureRegion: can't alloc buffer for raster conversion, size=%d", bufferSize);
bufferSize = 0;
return nil;
}
vImage_Buffer srcBuf;
srcBuf.height = dh;
srcBuf.width = dw;
srcBuf.rowBytes = srcInfo->scanStride;
srcBuf.data = srcInfo->rasBase;
vImage_Buffer destBuf;
destBuf.height = dh;
destBuf.width = dw;
destBuf.rowBytes = dw*4;
destBuf.data = buffer;
vImagePermuteChannels_ARGB8888(&srcBuf, &destBuf, rfi->permuteMap, kvImageNoFlags);
raster = buffer;
J2dTraceLn5(J2D_TRACE_VERBOSE, "replaceTextureRegion [use conversion]: %d, %d, %d, %d, hasA=%d",
rfi->permuteMap[0], rfi->permuteMap[1], rfi->permuteMap[2], rfi->permuteMap[3], rfi->hasAlpha);
}
}
MTLRegion region = MTLRegionMake2D(dx1, dy1, dw, dh);
if (result != nil)
dest = result;
[dest replaceRegion:region mipmapLevel:0 withBytes:raster bytesPerRow:srcInfo->scanStride];
return result;
}
/**
* Inner loop used for copying a source system memory ("Sw") surface to a
* destination MTL "Surface". This method is invoked from
* MTLBlitLoops_Blit().
*/
static void
MTLBlitSwToTextureViaPooledTexture(
MTLContext *mtlc, SurfaceDataRasInfo *srcInfo, BMTLSDOps * bmtlsdOps,
MTLRasterFormatInfo * rfi, jboolean useBlitEncoder, jint hint,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
{
const int sw = srcInfo->bounds.x2 - srcInfo->bounds.x1;
const int sh = srcInfo->bounds.y2 - srcInfo->bounds.y1;
id<MTLTexture> dest = bmtlsdOps->pTexture;
MTLPooledTextureHandle * texHandle = [mtlc.texturePool getTexture:sw height:sh format:rfi->format];
if (texHandle == nil) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitSwToTextureViaPooledTexture: can't obtain temporary texture object from pool");
return;
}
[[mtlc getCommandBufferWrapper] registerPooledTexture:texHandle];
[texHandle release];
id<MTLTexture> texBuff = texHandle.texture;
id<MTLTexture> swizzledTexture = replaceTextureRegion(texBuff, srcInfo, rfi, 0, 0, sw, sh);
if (useBlitEncoder) {
id <MTLBlitCommandEncoder> blitEncoder = [mtlc.encoderManager createBlitEncoder];
[blitEncoder copyFromTexture:swizzledTexture != nil ? swizzledTexture : texBuff
sourceSlice:0
sourceLevel:0
sourceOrigin:MTLOriginMake(0, 0, 0)
sourceSize:MTLSizeMake(sw, sh, 1)
toTexture:dest
destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(dx1, dy1, 0)];
[blitEncoder endEncoding];
} else {
drawTex2Tex(mtlc, swizzledTexture != nil ? swizzledTexture : texBuff, dest, !rfi->hasAlpha, bmtlsdOps->isOpaque, hint,
0, 0, sw, sh, dx1, dy1, dx2, dy2);
}
if (swizzledTexture != nil) {
[swizzledTexture release];
}
}
static
jboolean isIntegerAndUnscaled(
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2
) {
const jdouble epsilon = 0.0001f;
// check that dx1,dy1 is integer
if (fabs(dx1 - (int)dx1) > epsilon || fabs(dy1 - (int)dy1) > epsilon) {
return JNI_FALSE;
}
// check that destSize equals srcSize
if (fabs(dx2 - dx1 - sx2 + sx1) > epsilon || fabs(dy2 - dy1 - sy2 + sy1) > epsilon) {
return JNI_FALSE;
}
return JNI_TRUE;
}
static
jboolean clipDestCoords(
jdouble *dx1, jdouble *dy1, jdouble *dx2, jdouble *dy2,
jint *sx1, jint *sy1, jint *sx2, jint *sy2,
jint destW, jint destH, const MTLScissorRect * clipRect
) {
// Trim destination rect by clip-rect (or dest.bounds)
const jint sw = *sx2 - *sx1;
const jint sh = *sy2 - *sy1;
const jdouble dw = *dx2 - *dx1;
const jdouble dh = *dy2 - *dy1;
jdouble dcx1 = 0;
jdouble dcx2 = destW;
jdouble dcy1 = 0;
jdouble dcy2 = destH;
if (clipRect != NULL) {
if (clipRect->x > dcx1)
dcx1 = clipRect->x;
const int maxX = clipRect->x + clipRect->width;
if (dcx2 > maxX)
dcx2 = maxX;
if (clipRect->y > dcy1)
dcy1 = clipRect->y;
const int maxY = clipRect->y + clipRect->height;
if (dcy2 > maxY)
dcy2 = maxY;
if (dcx1 >= dcx2) {
J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcx1=%1.2f, dcx2=%1.2f", dcx1, dcx2);
dcx1 = dcx2;
}
if (dcy1 >= dcy2) {
J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcy1=%1.2f, dcy2=%1.2f", dcy1, dcy2);
dcy1 = dcy2;
}
}
if (*dx2 <= dcx1 || *dx1 >= dcx2 || *dy2 <= dcy1 || *dy1 >= dcy2) {
J2dTraceLn(J2D_TRACE_INFO, "\tclipDestCoords: dest rect doesn't intersect clip area");
J2dTraceLn4(J2D_TRACE_INFO, "\tdx2=%1.4f <= dcx1=%1.4f || *dx1=%1.4f >= dcx2=%1.4f", *dx2, dcx1, *dx1, dcx2);
J2dTraceLn4(J2D_TRACE_INFO, "\t*dy2=%1.4f <= dcy1=%1.4f || *dy1=%1.4f >= dcy2=%1.4f", *dy2, dcy1, *dy1, dcy2);
return JNI_FALSE;
}
if (*dx1 < dcx1) {
J2dTraceLn2(J2D_TRACE_VERBOSE, "\t\tdx1=%1.2f, will be clipped to %1.2f", *dx1, dcx1);
*sx1 += (jint)((dcx1 - *dx1) * (sw/dw));
*dx1 = dcx1;
}
if (*dx2 > dcx2) {
J2dTraceLn2(J2D_TRACE_VERBOSE, "\t\tdx2=%1.2f, will be clipped to %1.2f", *dx2, dcx2);
*sx2 -= (jint)((*dx2 - dcx2) * (sw/dw));
*dx2 = dcx2;
}
if (*dy1 < dcy1) {
J2dTraceLn2(J2D_TRACE_VERBOSE, "\t\tdy1=%1.2f, will be clipped to %1.2f", *dy1, dcy1);
*sy1 += (jint)((dcy1 - *dy1) * (sh/dh));
*dy1 = dcy1;
}
if (*dy2 > dcy2) {
J2dTraceLn2(J2D_TRACE_VERBOSE, "\t\tdy2=%1.2f, will be clipped to %1.2f", *dy2, dcy2);
*sy2 -= (jint)((*dy2 - dcy2) * (sh/dh));
*dy2 = dcy2;
}
return JNI_TRUE;
}
/**
* General blit method for copying a native MTL surface to another MTL "Surface".
* Parameter texture == true forces to use 'texture' codepath (dest coordinates will always be integers).
* Parameter xform == true only when AffineTransform is used (invoked only from TransformBlit, dest coordinates will always be integers).
*/
void
MTLBlitLoops_IsoBlit(JNIEnv *env,
MTLContext *mtlc, jlong pSrcOps, jlong pDstOps,
jboolean xform, jint hint, jboolean texture,
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
{
BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps);
BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps);
RETURN_IF_NULL(mtlc);
RETURN_IF_NULL(srcOps);
RETURN_IF_NULL(dstOps);
id<MTLTexture> srcTex = srcOps->pTexture;
id<MTLTexture> dstTex = dstOps->pTexture;
if (srcTex == nil || srcTex == nil) {
J2dTraceLn2(J2D_TRACE_ERROR, "MTLBlitLoops_IsoBlit: surface is null (stex=%p, dtex=%p)", srcTex, dstTex);
return;
}
const jint sw = sx2 - sx1;
const jint sh = sy2 - sy1;
const jdouble dw = dx2 - dx1;
const jdouble dh = dy2 - dy1;
if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) {
J2dTraceLn4(J2D_TRACE_WARNING, "MTLBlitLoops_IsoBlit: invalid dimensions: sw=%d, sh%d, dw=%d, dh=%d", sw, sh, dw, dh);
return;
}
#ifdef DEBUG_ISOBLIT
if ((xform == JNI_TRUE) != (mtlc.useTransform == JNI_TRUE)) {
J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE,
"MTLBlitLoops_IsoBlit state error: xform=%d, mtlc.useTransform=%d, texture=%d",
xform, mtlc.useTransform, texture);
}
#endif // DEBUG_ISOBLIT
clipDestCoords(
&dx1, &dy1, &dx2, &dy2,
&sx1, &sy1, &sx2, &sy2,
dstTex.width, dstTex.height, texture ? NULL : [mtlc.clip getRect]
);
SurfaceDataBounds bounds;
bounds.x1 = sx1;
bounds.y1 = sy1;
bounds.x2 = sx2;
bounds.y2 = sy2;
SurfaceData_IntersectBoundsXYXY(&bounds, 0, 0, srcOps->width, srcOps->height);
if (bounds.x2 <= bounds.x1 || bounds.y2 <= bounds.y1) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLBlitLoops_IsoBlit: source rectangle doesn't intersect with source surface bounds");
J2dTraceLn6(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d sw=%d sh=%d", sx1, sy1, sx2, sy2, srcOps->width, srcOps->height);
J2dTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f", dx1, dy1, dx2, dy2);
return;
}
if (bounds.x1 != sx1) {
dx1 += (bounds.x1 - sx1) * (dw / sw);
sx1 = bounds.x1;
}
if (bounds.y1 != sy1) {
dy1 += (bounds.y1 - sy1) * (dh / sh);
sy1 = bounds.y1;
}
if (bounds.x2 != sx2) {
dx2 += (bounds.x2 - sx2) * (dw / sw);
sx2 = bounds.x2;
}
if (bounds.y2 != sy2) {
dy2 += (bounds.y2 - sy2) * (dh / sh);
sy2 = bounds.y2;
}
#ifdef TRACE_ISOBLIT
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE,
"MTLBlitLoops_IsoBlit [tx=%d, xf=%d, AC=%s]: src=%s, dst=%s | (%d, %d, %d, %d)->(%1.2f, %1.2f, %1.2f, %1.2f)",
texture, xform, [mtlc getCompositeDescription].cString,
getSurfaceDescription(srcOps).cString, getSurfaceDescription(dstOps).cString,
sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
#endif //TRACE_ISOBLIT
if (!texture && !xform
&& [mtlc isBlendingDisabled:srcOps->isOpaque]
&& isIntegerAndUnscaled(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)
&& (dstOps->isOpaque || !srcOps->isOpaque)
) {
#ifdef TRACE_ISOBLIT
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via blitEncoder]");
#endif //TRACE_ISOBLIT
id <MTLBlitCommandEncoder> blitEncoder = [mtlc.encoderManager createBlitEncoder];
[blitEncoder copyFromTexture:srcTex
sourceSlice:0
sourceLevel:0
sourceOrigin:MTLOriginMake(sx1, sy1, 0)
sourceSize:MTLSizeMake(sx2 - sx1, sy2 - sy1, 1)
toTexture:dstTex
destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(dx1, dy1, 0)];
[blitEncoder endEncoding];
return;
}
#ifdef TRACE_ISOBLIT
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via sampling]");
#endif //TRACE_ISOBLIT
drawTex2Tex(mtlc, srcTex, dstTex,
[mtlc isBlendingDisabled:srcOps->isOpaque],
dstOps->isOpaque, hint, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
}
/**
* General blit method for copying a system memory ("Sw") surface to a native MTL surface.
* Parameter texture == true only in SwToTextureBlit (straight copy from sw to texture), dest coordinates will always be integers.
* Parameter xform == true only when AffineTransform is used (invoked only from TransformBlit, dest coordinates will always be integers).
*/
void
MTLBlitLoops_Blit(JNIEnv *env,
MTLContext *mtlc, jlong pSrcOps, jlong pDstOps,
jboolean xform, jint hint,
jint srctype, jboolean texture,
jint sx1, jint sy1, jint sx2, jint sy2,
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
{
SurfaceDataOps *srcOps = (SurfaceDataOps *)jlong_to_ptr(pSrcOps);
BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps);
RETURN_IF_NULL(mtlc);
RETURN_IF_NULL(srcOps);
RETURN_IF_NULL(dstOps);
id<MTLTexture> dest = dstOps->pTexture;
if (dest == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: dest is null");
return;
}
if (srctype < 0 || srctype >= sizeof(RasterFormatInfos)/ sizeof(MTLRasterFormatInfo)) {
J2dTraceLn1(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: source pixel format %d isn't supported", srctype);
return;
}
const jint sw = sx2 - sx1;
const jint sh = sy2 - sy1;
const jdouble dw = dx2 - dx1;
const jdouble dh = dy2 - dy1;
if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: invalid dimensions");
return;
}
#ifdef DEBUG_BLIT
if (
(xform == JNI_TRUE) != (mtlc.useTransform == JNI_TRUE)
|| (xform && texture)
) {
J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE,
"MTLBlitLoops_Blit state error: xform=%d, mtlc.useTransform=%d, texture=%d",
xform, mtlc.useTransform, texture);
}
if (texture) {
if (!isIntegerAndUnscaled(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)) {
J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE,
"MTLBlitLoops_Blit state error: texture=true, but src and dst dimensions aren't equal or dest coords aren't integers");
}
if (!dstOps->isOpaque && !RasterFormatInfos[srctype].hasAlpha) {
J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE,
"MTLBlitLoops_Blit state error: texture=true, but dest has alpha and source hasn't alpha, can't use texture-codepath");
}
}
#endif // DEBUG_BLIT
clipDestCoords(
&dx1, &dy1, &dx2, &dy2,
&sx1, &sy1, &sx2, &sy2,
dest.width, dest.height, texture ? NULL : [mtlc.clip getRect]
);
SurfaceDataRasInfo srcInfo;
srcInfo.bounds.x1 = sx1;
srcInfo.bounds.y1 = sy1;
srcInfo.bounds.x2 = sx2;
srcInfo.bounds.y2 = sy2;
// NOTE: This function will modify the contents of the bounds field to represent the maximum available raster data.
if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) != SD_SUCCESS) {
J2dTraceLn(J2D_TRACE_WARNING, "MTLBlitLoops_Blit: could not acquire lock");
return;
}
if (srcInfo.bounds.x2 > srcInfo.bounds.x1 && srcInfo.bounds.y2 > srcInfo.bounds.y1) {
srcOps->GetRasInfo(env, srcOps, &srcInfo);
if (srcInfo.rasBase) {
if (srcInfo.bounds.x1 != sx1) {
const int dx = srcInfo.bounds.x1 - sx1;
dx1 += dx * (dw / sw);
}
if (srcInfo.bounds.y1 != sy1) {
const int dy = srcInfo.bounds.y1 - sy1;
dy1 += dy * (dh / sh);
}
if (srcInfo.bounds.x2 != sx2) {
const int dx = srcInfo.bounds.x2 - sx2;
dx2 += dx * (dw / sw);
}
if (srcInfo.bounds.y2 != sy2) {
const int dy = srcInfo.bounds.y2 - sy2;
dy2 += dy * (dh / sh);
}
#ifdef TRACE_BLIT
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE,
"MTLBlitLoops_Blit [tx=%d, xf=%d, AC=%s]: bdst=%s, src=%p (%dx%d) O=%d premul=%d | (%d, %d, %d, %d)->(%1.2f, %1.2f, %1.2f, %1.2f)",
texture, xform, [mtlc getCompositeDescription].cString,
getSurfaceDescription(dstOps).cString, srcOps,
sx2 - sx1, sy2 - sy1,
RasterFormatInfos[srctype].hasAlpha ? 0 : 1, RasterFormatInfos[srctype].isPremult ? 1 : 0,
sx1, sy1, sx2, sy2,
dx1, dy1, dx2, dy2);
#endif //TRACE_BLIT
MTLRasterFormatInfo rfi = RasterFormatInfos[srctype];
const jboolean useReplaceRegion = texture ||
([mtlc isBlendingDisabled:!rfi.hasAlpha]
&& !xform
&& isIntegerAndUnscaled(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2));
if (useReplaceRegion) {
if (dstOps->isOpaque || rfi.hasAlpha) {
#ifdef TRACE_BLIT
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [replaceTextureRegion]");
#endif //TRACE_BLIT
replaceTextureRegion(dest, &srcInfo, &rfi, (int) dx1, (int) dy1, (int) dx2, (int) dy2);
} else {
#ifdef TRACE_BLIT
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via pooled + blit]");
#endif //TRACE_BLIT
MTLBlitSwToTextureViaPooledTexture(mtlc, &srcInfo, dstOps, &rfi, true, hint, dx1, dy1, dx2, dy2);
}
} else { // !useReplaceRegion
#ifdef TRACE_BLIT
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via pooled texture]");
#endif //TRACE_BLIT
MTLBlitSwToTextureViaPooledTexture(mtlc, &srcInfo, dstOps, &rfi, false, hint, dx1, dy1, dx2, dy2);
}
}
SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
}
SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
}
/**
* Specialized blit method for copying a native MTL "Surface" (pbuffer,
* window, etc.) to a system memory ("Sw") surface.
*/
void
MTLBlitLoops_SurfaceToSwBlit(JNIEnv *env, MTLContext *mtlc,
jlong pSrcOps, jlong pDstOps, jint dsttype,
jint srcx, jint srcy, jint dstx, jint dsty,
jint width, jint height)
{
J2dTraceLn6(J2D_TRACE_VERBOSE, "MTLBlitLoops_SurfaceToSwBlit: sx=%d sy=%d w=%d h=%d dx=%d dy=%d", srcx, srcy, width, height, dstx, dsty);
BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps);
SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps);
SurfaceDataRasInfo srcInfo, dstInfo;
if (dsttype < 0 || dsttype >= sizeof(RasterFormatInfos)/ sizeof(MTLRasterFormatInfo)) {
J2dTraceLn1(J2D_TRACE_ERROR, "MTLBlitLoops_SurfaceToSwBlit: destination pixel format %d isn't supported", dsttype);
return;
}
if (width <= 0 || height <= 0) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_SurfaceToSwBlit: dimensions are non-positive");
return;
}
RETURN_IF_NULL(srcOps);
RETURN_IF_NULL(dstOps);
RETURN_IF_NULL(mtlc);
srcInfo.bounds.x1 = srcx;
srcInfo.bounds.y1 = srcy;
srcInfo.bounds.x2 = srcx + width;
srcInfo.bounds.y2 = srcy + height;
dstInfo.bounds.x1 = dstx;
dstInfo.bounds.y1 = dsty;
dstInfo.bounds.x2 = dstx + width;
dstInfo.bounds.y2 = dsty + height;
if (dstOps->Lock(env, dstOps, &dstInfo, SD_LOCK_WRITE) != SD_SUCCESS) {
J2dTraceLn(J2D_TRACE_WARNING,"MTLBlitLoops_SurfaceToSwBlit: could not acquire dst lock");
return;
}
SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds,
0, 0, srcOps->width, srcOps->height);
SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds,
srcx - dstx, srcy - dsty);
if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&
srcInfo.bounds.y2 > srcInfo.bounds.y1)
{
dstOps->GetRasInfo(env, dstOps, &dstInfo);
if (dstInfo.rasBase) {
void *pDst = dstInfo.rasBase;
srcx = srcInfo.bounds.x1;
srcy = srcInfo.bounds.y1;
dstx = dstInfo.bounds.x1;
dsty = dstInfo.bounds.y1;
width = srcInfo.bounds.x2 - srcInfo.bounds.x1;
height = srcInfo.bounds.y2 - srcInfo.bounds.y1;
pDst = PtrAddBytes(pDst, dstx * dstInfo.pixelStride);
pDst = PtrPixelsRow(pDst, dsty, dstInfo.scanStride);
// this accounts for lower-left origin of the source region
srcx = srcOps->xOffset + srcx;
srcy = srcOps->yOffset + srcOps->height - srcy - height;
const int srcLength = width * height * 4; // NOTE: assume that src format is MTLPixelFormatBGRA8Unorm
#ifdef DEBUG
void *pDstEnd = dstInfo.rasBase + (height - 1)*dstInfo.scanStride + width*dstInfo.pixelStride;
if (pDst + srcLength > pDstEnd) {
J2dTraceLn6(J2D_TRACE_ERROR, "MTLBlitLoops_SurfaceToSwBlit: length mismatch: dstx=%d, dsty=%d, w=%d, h=%d, pixStride=%d, scanStride=%d",
dstx, dsty, width, height, dstInfo.pixelStride, dstInfo.scanStride);
return;
}
#endif //DEBUG
// Create MTLBuffer (or use static)
MTLRasterFormatInfo rfi = RasterFormatInfos[dsttype];
const jboolean directCopy = rfi.permuteMap == NULL;
id<MTLBuffer> mtlbuf;
#ifdef USE_STATIC_BUFFER
if (directCopy) {
// NOTE: theoretically we can use newBufferWithBytesNoCopy, but pDst must be allocated with special API
// mtlbuf = [mtlc.device
// newBufferWithBytesNoCopy:pDst
// length:(NSUInteger) srcLength
// options:MTLResourceCPUCacheModeDefaultCache
// deallocator:nil];
//
// see https://developer.apple.com/documentation/metal/mtldevice/1433382-newbufferwithbytesnocopy?language=objc
//
// The storage allocation of the returned new MTLBuffer object is the same as the pointer input value.
// The existing memory allocation must be covered by a single VM region, typically allocated with vm_allocate or mmap.
// Memory allocated by malloc is specifically disallowed.
}
static id<MTLBuffer> mtlIntermediateBuffer = nil; // need to reimplement with MTLBufferManager
if (mtlIntermediateBuffer == nil || mtlIntermediateBuffer.length < srcLength) {
if (mtlIntermediateBuffer != nil) {
[mtlIntermediateBuffer release];
}
mtlIntermediateBuffer = [mtlc.device newBufferWithLength:srcLength options:MTLResourceCPUCacheModeDefaultCache];
}
mtlbuf = mtlIntermediateBuffer;
#else // USE_STATIC_BUFFER
mtlbuf = [mtlc.device newBufferWithLength:width*height*4 options:MTLResourceStorageModeShared];
#endif // USE_STATIC_BUFFER
// Read from surface into MTLBuffer
// NOTE: using of separate blitCommandBuffer can produce errors (draw into surface (with general cmd-buf)
// can be unfinished when reading raster from blit cmd-buf).
// Consider to use [mtlc.encoderManager createBlitEncoder] and [mtlc commitCommandBuffer:JNI_TRUE];
J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLBlitLoops_SurfaceToSwBlit: source texture %p", srcOps->pTexture);
id<MTLCommandBuffer> cb = [mtlc createBlitCommandBuffer];
id<MTLBlitCommandEncoder> blitEncoder = [cb blitCommandEncoder];
[blitEncoder synchronizeTexture:srcOps->pTexture slice:0 level:0];
[blitEncoder copyFromTexture:srcOps->pTexture
sourceSlice:0
sourceLevel:0
sourceOrigin:MTLOriginMake(srcx, srcy, 0)
sourceSize:MTLSizeMake(width, height, 1)
toBuffer:mtlbuf
destinationOffset:0 /*offset already taken in: pDst = PtrAddBytes(pDst, dstx * dstInfo.pixelStride)*/
destinationBytesPerRow:width*4
destinationBytesPerImage:width * height*4];
[blitEncoder endEncoding];
// Commit and wait for reading complete
[cb commit];
[cb waitUntilCompleted];
// Perform conversion if necessary
if (directCopy) {
memcpy(pDst, mtlbuf.contents, srcLength);
} else {
J2dTraceLn6(J2D_TRACE_VERBOSE,"MTLBlitLoops_SurfaceToSwBlit: dsttype=%d, raster conversion will be performed, dest rfi: %d, %d, %d, %d, hasA=%d",
dsttype, rfi.permuteMap[0], rfi.permuteMap[1], rfi.permuteMap[2], rfi.permuteMap[3], rfi.hasAlpha);
// perform raster conversion: mtlIntermediateBuffer(8888) -> pDst(rfi)
// invoked only from rq-thread, so use static buffers
// but it's better to use thread-local buffers (or special buffer manager)
vImage_Buffer srcBuf;
srcBuf.height = height;
srcBuf.width = width;
srcBuf.rowBytes = 4*width;
srcBuf.data = mtlbuf.contents;
vImage_Buffer destBuf;
destBuf.height = height;
destBuf.width = width;
destBuf.rowBytes = dstInfo.scanStride;
destBuf.data = pDst;
vImagePermuteChannels_ARGB8888(&srcBuf, &destBuf, rfi.permuteMap, kvImageNoFlags);
}
#ifndef USE_STATIC_BUFFER
[mtlbuf release];
#endif // USE_STATIC_BUFFER
}
SurfaceData_InvokeRelease(env, dstOps, &dstInfo);
}
SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);
}
void
MTLBlitLoops_CopyArea(JNIEnv *env,
MTLContext *mtlc, BMTLSDOps *dstOps,
jint x, jint y, jint width, jint height,
jint dx, jint dy)
{
#ifdef DEBUG
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_CopyArea: bdst=%p [tex=%p] %dx%d | src (%d, %d), %dx%d -> dst (%d, %d)",
dstOps, dstOps->pTexture, ((id<MTLTexture>)dstOps->pTexture).width, ((id<MTLTexture>)dstOps->pTexture).height, x, y, width, height, dx, dy);
#endif //DEBUG
id <MTLBlitCommandEncoder> blitEncoder = [mtlc.encoderManager createBlitEncoder];
[blitEncoder
copyFromTexture:dstOps->pTexture
sourceSlice:0 sourceLevel:0 sourceOrigin:MTLOriginMake(x, y, 0) sourceSize:MTLSizeMake(width, height, 1)
toTexture:dstOps->pTexture destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(x + dx, y + dy, 0)];
[blitEncoder endEncoding];
// TODO:
// 1. check rect bounds
// 2. support CopyArea with extra-alpha (and with custom Composite if necessary)
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLBufImgOps_h_Included
#define MTLBufImgOps_h_Included
#include "MTLContext.h"
@interface MTLRescaleOp : NSObject
- (id)init:(jboolean)isNonPremult factors:(unsigned char *)factors offsets:(unsigned char *)offsets;
- (jfloat *)getScaleFactors;
- (jfloat *)getOffsets;
- (NSString *)getDescription; // creates autorelease string
@property (readonly) jboolean isNonPremult;
@end
@interface MTLConvolveOp : NSObject
- (id)init:(jboolean)edgeZeroFill kernelWidth:(jint)kernelWidth
kernelHeight:(jint)kernelHeight
srcWidth:(jint)srcWidth
srcHeight:(jint)srcHeight
kernel:(unsigned char *)kernel
device:(id<MTLDevice>)device;
- (void) dealloc;
- (id<MTLBuffer>) getBuffer;
- (const float *) getImgEdge;
- (NSString *)getDescription; // creates autorelease string
@property (readonly) jboolean isEdgeZeroFill;
@property (readonly) int kernelSize;
@end
@interface MTLLookupOp : NSObject
- (id)init:(jboolean)nonPremult shortData:(jboolean)shortData
numBands:(jint)numBands
bandLength:(jint)bandLength
offset:(jint)offset
tableValues:(void *)tableValues
device:(id<MTLDevice>)device;
- (void) dealloc;
- (jfloat *)getOffset;
- (id<MTLTexture>) getLookupTexture;
- (NSString *)getDescription; // creates autorelease string
@property (readonly) jboolean isUseSrcAlpha;
@property (readonly) jboolean isNonPremult;
@end
#endif /* MTLBufImgOps_h_Included */

View File

@@ -0,0 +1,227 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <jlong.h>
#include "MTLBufImgOps.h"
#include "MTLContext.h"
#include "MTLRenderQueue.h"
#include "MTLSurfaceDataBase.h"
#include "GraphicsPrimitiveMgr.h"
@implementation MTLRescaleOp {
jboolean _isNonPremult;
jfloat _normScaleFactors[4];
jfloat _normOffsets[4];
}
-(jfloat *) getScaleFactors {
return _normScaleFactors;
}
-(jfloat *) getOffsets {
return _normOffsets;
}
- (id)init:(jboolean)isNonPremult factors:(unsigned char *)factors offsets:(unsigned char *)offsets {
self = [super init];
if (self) {
J2dTraceLn1(J2D_TRACE_INFO,"Created MTLRescaleOp: isNonPremult=%d", isNonPremult);
_isNonPremult = isNonPremult;
_normScaleFactors[0] = NEXT_FLOAT(factors);
_normScaleFactors[1] = NEXT_FLOAT(factors);
_normScaleFactors[2] = NEXT_FLOAT(factors);
_normScaleFactors[3] = NEXT_FLOAT(factors);
_normOffsets[0] = NEXT_FLOAT(offsets);
_normOffsets[1] = NEXT_FLOAT(offsets);
_normOffsets[2] = NEXT_FLOAT(offsets);
_normOffsets[3] = NEXT_FLOAT(offsets);
}
return self;
}
- (NSString *)getDescription {
return [NSString stringWithFormat:@"rescale: nonPremult=%d", _isNonPremult];
}
@end
@implementation MTLConvolveOp {
id<MTLBuffer> _buffer;
float _imgEdge[4];
int _kernelSize;
jboolean _isEdgeZeroFill;
}
- (id)init:(jboolean)edgeZeroFill kernelWidth:(jint)kernelWidth
kernelHeight:(jint)kernelHeight
srcWidth:(jint)srcWidth
srcHeight:(jint)srcHeight
kernel:(unsigned char *)kernel
device:(id<MTLDevice>)device {
self = [super init];
if (self) {
J2dTraceLn2(J2D_TRACE_INFO,"Created MTLConvolveOp: kernelW=%d kernelH=%d", kernelWidth, kernelHeight);
_isEdgeZeroFill = edgeZeroFill;
_kernelSize = kernelWidth * kernelHeight;
_buffer = [device newBufferWithLength:_kernelSize*sizeof(vector_float3) options:MTLResourceStorageModeShared];
float * kernelVals = [_buffer contents];
int kIndex = 0;
for (int i = -kernelHeight/2; i < kernelHeight/2+1; i++) {
for (int j = -kernelWidth/2; j < kernelWidth/2+1; j++) {
kernelVals[kIndex+0] = j/(float)srcWidth;
kernelVals[kIndex+1] = i/(float)srcHeight;
kernelVals[kIndex+2] = NEXT_FLOAT(kernel);
kIndex += 3;
}
}
_imgEdge[0] = (kernelWidth/2)/(float)srcWidth;
_imgEdge[1] = (kernelHeight/2)/(float)srcHeight;
_imgEdge[2] = 1 - _imgEdge[0];
_imgEdge[3] = 1 - _imgEdge[1];
}
return self;
}
- (void) dealloc {
[_buffer release];
[super dealloc];
}
- (id<MTLBuffer>) getBuffer {
return _buffer;
}
- (const float *) getImgEdge {
return _imgEdge;
}
- (NSString *)getDescription {
return [NSString stringWithFormat:@"convolve: isEdgeZeroFill=%d", _isEdgeZeroFill];
}
@end
@implementation MTLLookupOp {
float _offset[4];
jboolean _isUseSrcAlpha;
jboolean _isNonPremult;
id<MTLTexture> _lookupTex;
}
- (id)init:(jboolean)nonPremult shortData:(jboolean)shortData
numBands:(jint)numBands
bandLength:(jint)bandLength
offset:(jint)offset
tableValues:(void *)tableValues
device:(id<MTLDevice>)device {
self = [super init];
if (self) {
J2dTraceLn4(J2D_TRACE_INFO,"Created MTLLookupOp: short=%d num=%d len=%d off=%d",
shortData, numBands, bandLength, offset);
_isUseSrcAlpha = numBands != 4;
_isNonPremult = nonPremult;
_offset[0] = offset / 255.0f;
_offset[1] = _offset[0];
_offset[2] = _offset[0];
_offset[3] = _offset[0];
MTLTextureDescriptor *textureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatA8Unorm
width:(NSUInteger)256
height:(NSUInteger)4
mipmapped:NO];
_lookupTex = [device newTextureWithDescriptor:textureDescriptor];
void *bands[4];
for (int i = 0; i < 4; i++) {
bands[i] = NULL;
}
int bytesPerElem = (shortData ? 2 : 1);
if (numBands == 1) {
// replicate the single band for R/G/B; alpha band is unused
for (int i = 0; i < 3; i++) {
bands[i] = tableValues;
}
bands[3] = NULL;
} else if (numBands == 3) {
// user supplied band for each of R/G/B; alpha band is unused
for (int i = 0; i < 3; i++) {
bands[i] = PtrAddBytes(tableValues, i*bandLength*bytesPerElem);
}
bands[3] = NULL;
} else if (numBands == 4) {
// user supplied band for each of R/G/B/A
for (int i = 0; i < 4; i++) {
bands[i] = PtrAddBytes(tableValues, i*bandLength*bytesPerElem);
}
}
for (int i = 0; i < 4; i++) {
if (bands[i] == NULL)
continue;
MTLRegion region = {
{0, i, 0},
{bandLength, 1,1}
};
[_lookupTex replaceRegion:region
mipmapLevel:0
withBytes:bands[i]
bytesPerRow:bandLength*bytesPerElem];
}
}
return self;
}
- (void) dealloc {
[_lookupTex release];
[super dealloc];
}
- (jfloat *) getOffset {
return _offset;
}
- (id<MTLTexture>) getLookupTexture {
return _lookupTex;
}
- (NSString *)getDescription {
return [NSString stringWithFormat:@"lookup: offset=%f", _offset[0]];
}
@end
#endif /* !HEADLESS */

View File

@@ -0,0 +1,57 @@
#import <limits.h>
#ifndef MTLClip_h_Included
#define MTLClip_h_Included
#import <Metal/Metal.h>
#include <jni.h>
#include "MTLSurfaceDataBase.h"
enum Clip {
NO_CLIP,
RECT_CLIP,
SHAPE_CLIP
};
@class MTLContext;
@class MTLPipelineStatesStorage;
/**
* The MTLClip class represents clip mode (rect or stencil)
* */
@interface MTLClip : NSObject
@property (readonly) id<MTLTexture> stencilAADataRef;
@property (readonly) id<MTLTexture> stencilTextureRef;
@property (readonly) jboolean stencilMaskGenerationInProgress;
- (id)init;
- (BOOL)isEqual:(MTLClip *)other; // used to compare requested with cached
- (void)copyFrom:(MTLClip *)other; // used to save cached
- (BOOL)isShape;
- (BOOL)isRect;
// returns null when clipType != RECT_CLIP
- (const MTLScissorRect *) getRect;
- (void)reset;
- (void)setClipRectX1:(jint)x1 Y1:(jint)y1 X2:(jint)x2 Y2:(jint)y2;
- (void)beginShapeClip:(BMTLSDOps *)dstOps context:(MTLContext *)mtlc;
- (void)endShapeClip:(BMTLSDOps *)dstOps context:(MTLContext *)mtlc;
- (void)setScissorOrStencil:(id<MTLRenderCommandEncoder>)encoder
destWidth:(NSUInteger)dw
destHeight:(NSUInteger)dh
device:(id<MTLDevice>)device;
- (void)setMaskGenerationPipelineState:(id<MTLRenderCommandEncoder>)encoder
destWidth:(NSUInteger)dw
destHeight:(NSUInteger)dh
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage;
- (NSString *)getDescription __unused; // creates autorelease string
@end
#endif // MTLClip_h_Included

View File

@@ -0,0 +1,359 @@
#include "MTLClip.h"
#include "MTLContext.h"
#include "common.h"
static MTLRenderPipelineDescriptor * templateStencilPipelineDesc = nil;
static void initTemplatePipelineDescriptors() {
if (templateStencilPipelineDesc != nil)
return;
MTLVertexDescriptor *vertDesc = [[MTLVertexDescriptor new] autorelease];
vertDesc.attributes[VertexAttributePosition].format = MTLVertexFormatFloat2;
vertDesc.attributes[VertexAttributePosition].offset = 0;
vertDesc.attributes[VertexAttributePosition].bufferIndex = MeshVertexBuffer;
vertDesc.layouts[MeshVertexBuffer].stride = sizeof(struct Vertex);
vertDesc.layouts[MeshVertexBuffer].stepRate = 1;
vertDesc.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;
templateStencilPipelineDesc = [[MTLRenderPipelineDescriptor new] autorelease];
templateStencilPipelineDesc.sampleCount = 1;
templateStencilPipelineDesc.vertexDescriptor = vertDesc;
templateStencilPipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatR8Uint; // A byte buffer format
templateStencilPipelineDesc.label = @"template_stencil";
}
static id<MTLDepthStencilState> getStencilState(id<MTLDevice> device) {
static id<MTLDepthStencilState> stencilState = nil;
if (stencilState == nil) {
MTLDepthStencilDescriptor* stencilDescriptor;
stencilDescriptor = [[MTLDepthStencilDescriptor new] autorelease];
stencilDescriptor.frontFaceStencil.stencilCompareFunction = MTLCompareFunctionEqual;
stencilDescriptor.frontFaceStencil.stencilFailureOperation = MTLStencilOperationKeep;
// TODO : backFaceStencil can be set to nil if all primitives are drawn as front-facing primitives
// currently, fill parallelogram uses back-facing primitive drawing - that needs to be changed.
// Once that part is changed, set backFaceStencil to nil
//stencilDescriptor.backFaceStencil = nil;
stencilDescriptor.backFaceStencil.stencilCompareFunction = MTLCompareFunctionEqual;
stencilDescriptor.backFaceStencil.stencilFailureOperation = MTLStencilOperationKeep;
stencilState = [device newDepthStencilStateWithDescriptor:stencilDescriptor];
}
return stencilState;
}
@implementation MTLClip {
jint _clipType;
MTLScissorRect _clipRect;
jboolean _stencilMaskGenerationInProgress;
id<MTLTexture> _stencilTextureRef;
id<MTLTexture> _stencilAADataRef;
}
- (id)init {
self = [super init];
if (self) {
_clipType = NO_CLIP;
_stencilMaskGenerationInProgress = JNI_FALSE;
_stencilTextureRef = nil;
}
return self;
}
- (BOOL)isEqual:(MTLClip *)other {
if (self == other)
return YES;
if (_stencilMaskGenerationInProgress == JNI_TRUE)
return other->_stencilMaskGenerationInProgress == JNI_TRUE;
if (_clipType != other->_clipType)
return NO;
if (_clipType == NO_CLIP)
return YES;
if (_clipType == RECT_CLIP) {
return _clipRect.x == other->_clipRect.x && _clipRect.y == other->_clipRect.y
&& _clipRect.width == other->_clipRect.width && _clipRect.height == other->_clipRect.height;
}
// NOTE: can compare stencil-data pointers here
return YES;
}
- (BOOL)isShape {
return _clipType == SHAPE_CLIP;
}
- (BOOL)isRect __unused {
return _clipType == RECT_CLIP;
}
- (const MTLScissorRect * _Nullable) getRect {
return _clipType == RECT_CLIP ? &_clipRect : NULL;
}
- (void)copyFrom:(MTLClip *)other {
_clipType = other->_clipType;
_stencilMaskGenerationInProgress = other->_stencilMaskGenerationInProgress;
_stencilTextureRef = other->_stencilTextureRef;
_stencilAADataRef = other->_stencilAADataRef;
if (other->_clipType == RECT_CLIP) {
_clipRect = other->_clipRect;
}
}
- (void)reset {
_clipType = NO_CLIP;
_stencilTextureRef = nil;
_stencilAADataRef = nil;
_stencilMaskGenerationInProgress = JNI_FALSE;
}
- (void)setClipRectX1:(jint)x1 Y1:(jint)y1 X2:(jint)x2 Y2:(jint)y2 {
if (_clipType == SHAPE_CLIP) {
_stencilTextureRef = nil;
_stencilAADataRef = nil;
}
if (x1 >= x2 || y1 >= y2) {
J2dTraceLn4(J2D_TRACE_ERROR, "MTLClip.setClipRect: invalid rect: x1=%d y1=%d x2=%d y2=%d", x1, y1, x2, y2);
_clipType = NO_CLIP;
}
const jint width = x2 - x1;
const jint height = y2 - y1;
J2dTraceLn4(J2D_TRACE_INFO, "MTLClip.setClipRect: x=%d y=%d w=%d h=%d", x1, y1, width, height);
_clipRect.x = (NSUInteger)((x1 >= 0) ? x1 : 0);
_clipRect.y = (NSUInteger)((y1 >= 0) ? y1 : 0);
_clipRect.width = (NSUInteger)((width >= 0) ? width : 0);
_clipRect.height = (NSUInteger)((height >= 0) ? height : 0);
_clipType = RECT_CLIP;
}
- (void)beginShapeClip:(BMTLSDOps *)dstOps context:(MTLContext *)mtlc {
_stencilMaskGenerationInProgress = JNI_TRUE;
if ((dstOps == NULL) || (dstOps->pStencilData == NULL) || (dstOps->pStencilTexture == NULL)) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext_beginShapeClip: stencil render target or stencil texture is NULL");
return;
}
// Clear the stencil render buffer & stencil texture
@autoreleasepool {
if (dstOps->width <= 0 || dstOps->height <= 0) {
return;
}
NSUInteger width = (NSUInteger)dstOps->width;
NSUInteger height = (NSUInteger)dstOps->height;
NSUInteger size = width*height;
id <MTLBuffer> buff = [mtlc.device newBufferWithLength:size*4 options:MTLResourceStorageModePrivate];
id<MTLCommandBuffer> commandBuf = [mtlc createBlitCommandBuffer];
id<MTLBlitCommandEncoder> blitEncoder = [commandBuf blitCommandEncoder];
[blitEncoder fillBuffer:buff range:NSMakeRange(0, size*4) value:0];
MTLOrigin origin = MTLOriginMake(0, 0, 0);
MTLSize sourceSize = MTLSizeMake(width, height, 1);
[blitEncoder copyFromBuffer:buff
sourceOffset:0
sourceBytesPerRow:width
sourceBytesPerImage:size
sourceSize:sourceSize
toTexture:dstOps->pStencilData
destinationSlice:0
destinationLevel:0
destinationOrigin:origin];
[blitEncoder copyFromBuffer:buff
sourceOffset:0
sourceBytesPerRow:width*4
sourceBytesPerImage:size*4
sourceSize:sourceSize
toTexture:dstOps->pAAStencilData
destinationSlice:0
destinationLevel:0
destinationOrigin:origin];
[blitEncoder copyFromBuffer:buff
sourceOffset:0
sourceBytesPerRow:width
sourceBytesPerImage:size
sourceSize:sourceSize
toTexture:dstOps->pStencilTexture
destinationSlice:0
destinationLevel:0
destinationOrigin:origin];
[blitEncoder endEncoding];
[commandBuf commit];
[commandBuf waitUntilCompleted];
[buff release];
}
}
- (void)endShapeClip:(BMTLSDOps *)dstOps context:(MTLContext *)mtlc {
if ((dstOps == NULL) || (dstOps->pStencilData == NULL) || (dstOps->pStencilTexture == NULL)) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext_endShapeClip: stencil render target or stencil texture is NULL");
return;
}
// Complete the rendering to the stencil buffer ------------
[mtlc.encoderManager endEncoder];
MTLCommandBufferWrapper* cbWrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandBuffer = [cbWrapper getCommandBuffer];
[commandBuffer addCompletedHandler:^(id <MTLCommandBuffer> c) {
[cbWrapper release];
}];
[commandBuffer commit];
[commandBuffer waitUntilCompleted];
// Now the stencil data is ready, this needs to be used while rendering further
@autoreleasepool {
if (dstOps->width > 0 && dstOps->height > 0) {
NSUInteger width = (NSUInteger)dstOps->width;
NSUInteger height = (NSUInteger)dstOps->height;
NSUInteger size = width*height;
NSUInteger sizeX4 = size*4;
id<MTLBuffer> buff =
[mtlc.device newBufferWithLength:size
options:MTLResourceStorageModeShared];
id<MTLBuffer> aaBuff =
[mtlc.device newBufferWithLength:sizeX4
options:MTLResourceStorageModeShared];
id<MTLCommandBuffer> cb = [mtlc createBlitCommandBuffer];
id<MTLBlitCommandEncoder> blitEncoder = [cb blitCommandEncoder];
MTLSize sourceSize = MTLSizeMake(width, height, 1);
MTLOrigin origin = MTLOriginMake(0, 0, 0);
[blitEncoder copyFromTexture:dstOps->pStencilData
sourceSlice:0
sourceLevel:0
sourceOrigin:origin
sourceSize:sourceSize
toBuffer:buff
destinationOffset:0
destinationBytesPerRow:width
destinationBytesPerImage:size];
[blitEncoder copyFromBuffer:buff
sourceOffset:0
sourceBytesPerRow:width
sourceBytesPerImage:size
sourceSize:sourceSize
toTexture:dstOps->pStencilTexture
destinationSlice:0
destinationLevel:0
destinationOrigin:origin];
[blitEncoder endEncoding];
[cb commit];
[cb waitUntilCompleted];
// TODO: Implement via compute shader
for (int i = 0; i < width*height; i++) {
unsigned char c = ((unsigned char*)(buff.contents))[i];
((jint*)(aaBuff.contents))[i] = c + (c << 8) + (c << 16) + (c << 24);
}
cb = [mtlc createBlitCommandBuffer];
blitEncoder = [cb blitCommandEncoder];
[blitEncoder copyFromBuffer:aaBuff
sourceOffset:0
sourceBytesPerRow:width*4
sourceBytesPerImage:sizeX4
sourceSize:sourceSize
toTexture:dstOps->pAAStencilData
destinationSlice:0
destinationLevel:0
destinationOrigin:origin];
[blitEncoder endEncoding];
[cb commit];
[cb waitUntilCompleted];
[buff release];
[aaBuff release];
}
}
_stencilMaskGenerationInProgress = JNI_FALSE;
_stencilTextureRef = dstOps->pStencilTexture;
_stencilAADataRef = dstOps->pAAStencilData;
_clipType = SHAPE_CLIP;
}
- (void)setMaskGenerationPipelineState:(id<MTLRenderCommandEncoder>)encoder
destWidth:(NSUInteger)dw
destHeight:(NSUInteger)dh
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
{
initTemplatePipelineDescriptors();
// A PipelineState for rendering to a byte-buffered texture that will be used as a stencil
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:templateStencilPipelineDesc
vertexShaderId:@"vert_stencil"
fragmentShaderId:@"frag_stencil"];
[encoder setRenderPipelineState:pipelineState];
struct FrameUniforms uf; // color is ignored while writing to stencil buffer
memset(&uf, 0, sizeof(uf));
[encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
_clipRect.x = 0;
_clipRect.y = 0;
_clipRect.width = dw;
_clipRect.height = dh;
[encoder setScissorRect:_clipRect]; // just for insurance (to reset possible clip from previous drawing)
}
- (void)setScissorOrStencil:(id<MTLRenderCommandEncoder>)encoder
destWidth:(NSUInteger)dw
destHeight:(NSUInteger)dh
device:(id<MTLDevice>)device
{
if (_clipType == NO_CLIP || _clipType == SHAPE_CLIP) {
_clipRect.x = 0;
_clipRect.y = 0;
_clipRect.width = dw;
_clipRect.height = dh;
}
[encoder setScissorRect:_clipRect];
if (_clipType == NO_CLIP || _clipType == RECT_CLIP) {
// NOTE: It seems that we can use the same encoder (with disabled stencil test) when mode changes from SHAPE to RECT.
// But [encoder setDepthStencilState:nil] causes crash, so we have to recreate encoder in such case.
// So we can omit [encoder setDepthStencilState:nil] here.
return;
}
if (_clipType == SHAPE_CLIP) {
// Enable stencil test
[encoder setDepthStencilState:getStencilState(device)];
[encoder setStencilReferenceValue:0xFF];
}
}
- (NSString *)getDescription __unused {
if (_clipType == NO_CLIP) {
return @"NO_CLIP";
}
if (_clipType == RECT_CLIP) {
return [NSString stringWithFormat:@"RECT_CLIP [%lu,%lu - %lux%lu]", _clipRect.x, _clipRect.y, _clipRect.width, _clipRect.height];
}
return [NSString stringWithFormat:@"SHAPE_CLIP"];
}
@end

View File

@@ -0,0 +1,40 @@
#ifndef MTLComposite_h_Included
#define MTLComposite_h_Included
#import <Metal/Metal.h>
#include <jni.h>
#define FLT_EPS (0.001f)
#define FLT_LT(x,y) ((x) < (y) - FLT_EPS)
#define FLT_GE(x,y) ((x) >= (y) - FLT_EPS)
#define FLT_LE(x,y) ((x) <= (y) + FLT_EPS)
#define FLT_GT(x,y) ((x) > (y) + FLT_EPS)
/**
* The MTLComposite class represents composite mode
* */
@interface MTLComposite : NSObject
- (id)init;
- (BOOL)isEqual:(MTLComposite *)other; // used to compare requested with cached
- (void)copyFrom:(MTLComposite *)other; // used to save cached
- (void)setRule:(jint)rule; // sets extraAlpha=1
- (void)setRule:(jint)rule extraAlpha:(jfloat)extraAlpha;
- (void)reset;
- (void)setXORComposite:(jint)color;
- (void)setAlphaComposite:(jint)rule;
- (jint)getCompositeState;
- (jint)getRule;
- (jint)getXorColor;
- (jfloat)getExtraAlpha;
- (jboolean)isBlendingDisabled:(jboolean) isSrcOpaque;
- (NSString *)getDescription; // creates autorelease string
@end
#endif // MTLComposite_h_Included

View File

@@ -0,0 +1,181 @@
#include "MTLComposite.h"
#include "sun_java2d_SunGraphics2D.h"
#include "java_awt_AlphaComposite.h"
@implementation MTLComposite {
jint _compState;
jint _compositeRule;
jint _xorPixel;
jfloat _extraAlpha;
}
- (id)init {
self = [super init];
if (self) {
_compositeRule = -1;
_compState = -1;
_xorPixel = 0;
_extraAlpha = 1;
}
return self;
}
- (BOOL)isEqual:(MTLComposite *)other {
if (self == other)
return YES;
if (_compState == other->_compState) {
if (_compState == sun_java2d_SunGraphics2D_COMP_XOR) {
return _xorPixel == other->_xorPixel;
}
if (_compState == sun_java2d_SunGraphics2D_COMP_ALPHA) {
return _extraAlpha == other->_extraAlpha
&& _compositeRule == other->_compositeRule;
}
}
return NO;
}
- (void)copyFrom:(MTLComposite *)other {
_extraAlpha = other->_extraAlpha;
_compositeRule = other->_compositeRule;
_compState = other->_compState;
_xorPixel = other->_xorPixel;
}
- (void)setRule:(jint)rule {
_extraAlpha = 1.f;
_compositeRule = rule;
}
- (void)setRule:(jint)rule extraAlpha:(jfloat)extraAlpha {
_compState = sun_java2d_SunGraphics2D_COMP_ALPHA;
_extraAlpha = extraAlpha;
_compositeRule = rule;
}
- (void)reset {
_compState = sun_java2d_SunGraphics2D_COMP_ISCOPY;
_compositeRule = java_awt_AlphaComposite_SRC;
_extraAlpha = 1.f;
}
- (jint)getRule {
return _compositeRule;
}
- (NSString *)getDescription {
const char * result = "";
switch (_compositeRule) {
case java_awt_AlphaComposite_CLEAR:
{
result = "CLEAR";
}
break;
case java_awt_AlphaComposite_SRC:
{
result = "SRC";
}
break;
case java_awt_AlphaComposite_DST:
{
result = "DST";
}
break;
case java_awt_AlphaComposite_SRC_OVER:
{
result = "SRC_OVER";
}
break;
case java_awt_AlphaComposite_DST_OVER:
{
result = "DST_OVER";
}
break;
case java_awt_AlphaComposite_SRC_IN:
{
result = "SRC_IN";
}
break;
case java_awt_AlphaComposite_DST_IN:
{
result = "DST_IN";
}
break;
case java_awt_AlphaComposite_SRC_OUT:
{
result = "SRC_OUT";
}
break;
case java_awt_AlphaComposite_DST_OUT:
{
result = "DST_OUT";
}
break;
case java_awt_AlphaComposite_SRC_ATOP:
{
result = "SRC_ATOP";
}
break;
case java_awt_AlphaComposite_DST_ATOP:
{
result = "DST_ATOP";
}
break;
case java_awt_AlphaComposite_XOR:
{
result = "XOR";
}
break;
default:
result = "UNKNOWN";
break;
}
const double epsilon = 0.001f;
if (fabs(_extraAlpha - 1.f) > epsilon) {
return [NSString stringWithFormat:@"%s [%1.2f]", result, _extraAlpha];
}
return [NSString stringWithFormat:@"%s", result];
}
- (jboolean)isBlendingDisabled:(jboolean)isSrcOpaque {
// We need to check both extra alpha and isSrcOpaque for
// SRC mode for correct handling TRANSLUCENT images.
// See test/jdk/java/awt/image/DrawImage/IncorrectUnmanagedImageRotatedClip.java
// for example
if (_compositeRule == java_awt_AlphaComposite_SRC ||
_compositeRule == java_awt_AlphaComposite_SRC_OVER)
{
return FLT_GE(_extraAlpha, 1.0f) && isSrcOpaque;
}
return isSrcOpaque;
}
- (void)setAlphaComposite:(jint)rule {
_compState = sun_java2d_SunGraphics2D_COMP_ALPHA;
[self setRule:rule];
}
- (jint)getCompositeState {
return _compState;
}
-(void)setXORComposite:(jint)color {
_compState = sun_java2d_SunGraphics2D_COMP_XOR;
_xorPixel = color;
}
-(jint)getXorColor {
return _xorPixel;
}
- (jfloat)getExtraAlpha {
return _extraAlpha;
}
@end

View File

@@ -0,0 +1,269 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLContext_h_Included
#define MTLContext_h_Included
#include "sun_java2d_pipe_BufferedContext.h"
#include "sun_java2d_metal_MTLContext.h"
#include "sun_java2d_metal_MTLContext_MTLContextCaps.h"
#import <Metal/Metal.h>
#include "MTLTexturePool.h"
#include "MTLPipelineStatesStorage.h"
#include "MTLTransform.h"
#include "MTLComposite.h"
#include "MTLPaints.h"
#include "MTLClip.h"
#include "EncoderManager.h"
#define MTLC_BLIT_TILE_SIZE 128
/**
* The MTLCommandBufferWrapper class contains command buffer and
* associated resources that will be released in completion handler
* */
@interface MTLCommandBufferWrapper : NSObject
- (id<MTLCommandBuffer>) getCommandBuffer;
- (void) onComplete; // invoked from completion handler in some pooled thread
- (void) registerPooledTexture:(MTLPooledTextureHandle *)handle;
@end
/**
* The MTLContext class contains cached state relevant to the native
* MTL context stored within the native ctxInfo field. Each Java-level
* MTLContext object is associated with a native-level MTLContext class.
* */
@interface MTLContext : NSObject
@property (readonly) MTLComposite * composite;
@property (readonly) MTLPaint * paint;
@property (readonly) MTLTransform * transform;
@property (readonly) MTLClip * clip;
@property jint textureFunction;
@property jboolean vertexCacheEnabled;
@property jboolean aaEnabled;
@property (readonly, strong) id<MTLDevice> device;
@property (strong) id<MTLLibrary> library;
@property (strong) id<MTLCommandQueue> commandQueue;
@property (strong) id<MTLBuffer> vertexBuffer;
@property (readonly) EncoderManager * encoderManager;
@property (strong)MTLPipelineStatesStorage* pipelineStateStorage;
@property (strong)MTLTexturePool* texturePool;
- (MTLCommandBufferWrapper *) getCommandBufferWrapper; // creates command buffer wrapper (when doesn't exist)
- (MTLCommandBufferWrapper *) pullCommandBufferWrapper; // returns current buffer wrapper with loosing object ownership
/**
* Fetches the MTLContext associated with the given destination surface,
* makes the context current for those surfaces, updates the destination
* viewport, and then returns a pointer to the MTLContext.
*/
+ (MTLContext*) setSurfacesEnv:(JNIEnv*)env src:(jlong)pSrc dst:(jlong)pDst;
- (id)initWithDevice:(id<MTLDevice>)d shadersLib:(NSString*)shadersLib;
- (void)dealloc;
/**
* Resets the current clip state (disables both scissor and depth tests).
*/
- (void)resetClip;
/**
* Sets the Metal scissor bounds to the provided rectangular clip bounds.
*/
- (void)setClipRectX1:(jint)x1 Y1:(jint)y1 X2:(jint)x2 Y2:(jint)y2;
- (const MTLScissorRect *)clipRect;
/**
* Sets up a complex (shape) clip using the Metal stencil buffer. This
* method prepares the stencil buffer so that the clip Region spans can
* be "rendered" into it. The stencil buffer is first cleared, then the
* stencil func is setup so that when we render the clip spans,
* nothing is rendered into the color buffer, but for each pixel that would
* be rendered, a 0xFF value is placed into that location in the stencil
* buffer. With stencil test enabled, pixels will only be rendered into the
* color buffer if the corresponding value at that (x,y) location in the
* stencil buffer is equal to 0xFF.
*/
- (void)beginShapeClip:(BMTLSDOps *)dstOps;
/**
* Finishes setting up the shape clip by resetting the stencil func
* so that future rendering operations will once again be encoded for the
* color buffer (while respecting the clip set up in the stencil buffer).
*/
- (void)endShapeClip:(BMTLSDOps *)dstOps;
/**
* Initializes the OpenGL state responsible for applying extra alpha. This
* step is only necessary for any operation that uses glDrawPixels() or
* glCopyPixels() with a non-1.0f extra alpha value. Since the source is
* always premultiplied, we apply the extra alpha value to both alpha and
* color components using GL_*_SCALE.
*/
- (void)setExtraAlpha:(jfloat)ea;
/**
* Resets all OpenGL compositing state (disables blending and logic
* operations).
*/
- (void)resetComposite;
/**
* Initializes the OpenGL blending state. XOR mode is disabled and the
* appropriate blend functions are setup based on the AlphaComposite rule
* constant.
*/
- (void)setAlphaCompositeRule:(jint)rule extraAlpha:(jfloat)extraAlpha
flags:(jint)flags;
/**
* Returns autorelease string with composite description (for debugging only)
*/
- (NSString*)getCompositeDescription;
/**
* Returns autorelease string with paint description (for debugging only)
*/
- (NSString*)getPaintDescription;
/**
* Initializes the OpenGL logic op state to XOR mode. Blending is disabled
* before enabling logic op mode. The XOR pixel value will be applied
* later in the MTLContext_SetColor() method.
*/
- (void)setXorComposite:(jint)xorPixel;
- (jboolean)isBlendingDisabled:(jboolean) isSrcOpaque;
/**
* Resets the OpenGL transform state back to the identity matrix.
*/
- (void)resetTransform;
/**
* Initializes the OpenGL transform state by setting the modelview transform
* using the given matrix parameters.
*
* REMIND: it may be worthwhile to add serial id to AffineTransform, so we
* could do a quick check to see if the xform has changed since
* last time... a simple object compare won't suffice...
*/
- (void)setTransformM00:(jdouble) m00 M10:(jdouble) m10
M01:(jdouble) m01 M11:(jdouble) m11
M02:(jdouble) m02 M12:(jdouble) m12;
/**
* Initializes a small texture tile for use with tiled blit operations (see
* MTLBlitLoops.c and MTLMaskBlit.c for usage examples). The texture ID for
* the tile is stored in the given MTLContext. The tile is initially filled
* with garbage values, but the tile is updated as needed (via
* glTexSubImage2D()) with real RGBA values used in tiled blit situations.
* The internal format for the texture is GL_RGBA8, which should be sufficient
* for storing system memory surfaces of any known format (see PixelFormats
* for a list of compatible surface formats).
*/
- (jboolean)initBlitTileTexture;
/**
* Creates a 2D texture of the given format and dimensions and returns the
* texture object identifier. This method is typically used to create a
* temporary texture for intermediate work, such as in the
* MTLContext_InitBlitTileTexture() method below.
*/
- (jint)createBlitTextureFormat:(jint)internalFormat pixelFormat:(jint)pixelFormat
width:(jint)width height:(jint)height;
- (void)destroyContextResources;
- (void)resetPaint;
- (void)setColorPaint:(int)pixel;
- (void)setGradientPaintUseMask:(jboolean)useMask
cyclic:(jboolean)cyclic
p0:(jdouble)p0
p1:(jdouble)p1
p3:(jdouble)p3
pixel1:(jint)pixel1
pixel2:(jint) pixel2;
- (void)setLinearGradientPaint:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jboolean)cycleMethod
numStops:(jint)numStops
p0:(jfloat)p0
p1:(jfloat)p1
p3:(jfloat)p3
fractions:(void *)fractions
pixels:(void *)pixels;
- (void)setRadialGradientPaint:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jboolean)cycleMethod
numStops:(jint)numStops
m00:(jfloat)m00
m01:(jfloat)m01
m02:(jfloat)m02
m10:(jfloat)m10
m11:(jfloat)m11
m12:(jfloat)m12
focusX:(jfloat)focusX
fractions:(void *)fractions
pixels:(void *)pixels;
- (void)setTexturePaint:(jboolean)useMask
pSrcOps:(jlong)pSrcOps
filter:(jboolean)filter
xp0:(jdouble)xp0
xp1:(jdouble)xp1
xp3:(jdouble)xp3
yp0:(jdouble)yp0
yp1:(jdouble)yp1
yp3:(jdouble)yp3;
// Sets current image conversion operation (instance of MTLConvolveOp, MTLRescaleOp, MTLLookupOp).
// Used only in MTLIsoBlit (to blit image with some conversion). Pattern of usage: enableOp -> IsoBlit -> disableOp.
// TODO: Need to remove it from MTLContext and pass it as an argument for IsoBlit (because it's more
// simple and clear)
-(void)setBufImgOp:(NSObject*)bufImgOp;
-(NSObject*)getBufImgOp;
- (id<MTLCommandBuffer>)createBlitCommandBuffer;
@end
/**
* See BufferedContext.java for more on these flags...
*/
#define MTLC_NO_CONTEXT_FLAGS \
sun_java2d_pipe_BufferedContext_NO_CONTEXT_FLAGS
#define MTLC_SRC_IS_OPAQUE \
sun_java2d_pipe_BufferedContext_SRC_IS_OPAQUE
#define MTLC_USE_MASK \
sun_java2d_pipe_BufferedContext_USE_MASK
#endif /* MTLContext_h_Included */

View File

@@ -0,0 +1,448 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <stdlib.h>
#include "sun_java2d_SunGraphics2D.h"
#include "jlong.h"
#import "MTLContext.h"
#include "MTLRenderQueue.h"
extern jboolean MTLSD_InitMTLWindow(JNIEnv *env, MTLSDOps *mtlsdo);
static struct TxtVertex verts[PGRAM_VERTEX_COUNT] = {
{{-1.0, 1.0}, {0.0, 0.0}},
{{1.0, 1.0}, {1.0, 0.0}},
{{1.0, -1.0}, {1.0, 1.0}},
{{1.0, -1.0}, {1.0, 1.0}},
{{-1.0, -1.0}, {0.0, 1.0}},
{{-1.0, 1.0}, {0.0, 0.0}}
};
@implementation MTLCommandBufferWrapper {
id<MTLCommandBuffer> _commandBuffer;
NSMutableArray * _pooledTextures;
}
- (id) initWithCommandBuffer:(id<MTLCommandBuffer>)cmdBuf {
self = [super init];
if (self) {
_commandBuffer = [cmdBuf retain];
_pooledTextures = [[NSMutableArray alloc] init];
}
return self;
}
- (id<MTLCommandBuffer>) getCommandBuffer {
return _commandBuffer;
}
- (void) onComplete { // invoked from completion handler in some pooled thread
for (int c = 0; c < [_pooledTextures count]; ++c)
[[_pooledTextures objectAtIndex:c] releaseTexture];
[_pooledTextures removeAllObjects];
}
- (void) registerPooledTexture:(MTLPooledTextureHandle *)handle {
[_pooledTextures addObject:handle];
}
- (void) dealloc {
[self onComplete];
[self->_pooledTextures release];
[self->_commandBuffer release];
[super dealloc];
}
@end
@implementation MTLContext {
MTLCommandBufferWrapper * _commandBufferWrapper;
MTLComposite * _composite;
MTLPaint * _paint;
MTLTransform * _transform;
MTLClip * _clip;
NSObject* _bufImgOp; // TODO: pass as parameter of IsoBlit
EncoderManager * _encoderManager;
}
@synthesize textureFunction,
vertexCacheEnabled, aaEnabled, device, library, pipelineStateStorage,
commandQueue, vertexBuffer,
texturePool;
extern void initSamplers(id<MTLDevice> device);
- (id)initWithDevice:(id<MTLDevice>)d shadersLib:(NSString*)shadersLib {
self = [super init];
if (self) {
// Initialization code here.
device = d;
texturePool = [[MTLTexturePool alloc] initWithDevice:device];
pipelineStateStorage = [[MTLPipelineStatesStorage alloc] initWithDevice:device shaderLibPath:shadersLib];
vertexBuffer = [device newBufferWithBytes:verts
length:sizeof(verts)
options:MTLResourceCPUCacheModeDefaultCache];
NSError *error = nil;
library = [device newLibraryWithFile:shadersLib error:&error];
if (!library) {
NSLog(@"Failed to load library. error %@", error);
exit(0);
}
_encoderManager = [[EncoderManager alloc] init];
[_encoderManager setContext:self];
_composite = [[MTLComposite alloc] init];
_paint = [[MTLPaint alloc] init];
_transform = [[MTLTransform alloc] init];
_clip = [[MTLClip alloc] init];
_bufImgOp = nil;
_commandBufferWrapper = nil;
// Create command queue
commandQueue = [device newCommandQueue];
initSamplers(device);
}
return self;
}
- (void)dealloc {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.dealloc");
self.texturePool = nil;
self.library = nil;
self.vertexBuffer = nil;
self.commandQueue = nil;
self.pipelineStateStorage = nil;
[_encoderManager release];
[_composite release];
[_paint release];
[_transform release];
[_clip release];
[super dealloc];
}
- (MTLCommandBufferWrapper *) getCommandBufferWrapper {
if (_commandBufferWrapper == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLContext : commandBuffer is NULL");
// NOTE: Command queues are thread-safe and allow multiple outstanding command buffers to be encoded simultaneously.
_commandBufferWrapper = [[MTLCommandBufferWrapper alloc] initWithCommandBuffer:[self.commandQueue commandBuffer]];// released in [layer blitTexture]
}
return _commandBufferWrapper;
}
- (MTLCommandBufferWrapper *) pullCommandBufferWrapper {
MTLCommandBufferWrapper * result = _commandBufferWrapper;
_commandBufferWrapper = nil;
return result;
}
+ (MTLContext*) setSurfacesEnv:(JNIEnv*)env src:(jlong)pSrc dst:(jlong)pDst {
BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrc);
BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDst);
MTLContext *mtlc = NULL;
if (srcOps == NULL || dstOps == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext_SetSurfaces: ops are null");
return NULL;
}
J2dTraceLn6(J2D_TRACE_VERBOSE, "MTLContext_SetSurfaces: bsrc=%p (tex=%p type=%d), bdst=%p (tex=%p type=%d)", srcOps, srcOps->pTexture, srcOps->drawableType, dstOps, dstOps->pTexture, dstOps->drawableType);
if (dstOps->drawableType == MTLSD_TEXTURE) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLContext_SetSurfaces: texture cannot be used as destination");
return NULL;
}
if (dstOps->drawableType == MTLSD_UNDEFINED) {
// initialize the surface as an OGLSD_WINDOW
if (!MTLSD_InitMTLWindow(env, dstOps)) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLContext_SetSurfaces: could not init OGL window");
return NULL;
}
}
// make the context current
MTLSDOps *dstCGLOps = (MTLSDOps *)dstOps->privOps;
mtlc = dstCGLOps->configInfo->context;
if (mtlc == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLContext_SetSurfaces: could not make context current");
return NULL;
}
// perform additional one-time initialization, if necessary
if (dstOps->needsInit) {
if (dstOps->isOpaque) {
// in this case we are treating the destination as opaque, but
// to do so, first we need to ensure that the alpha channel
// is filled with fully opaque values (see 6319663)
//MTLContext_InitAlphaChannel();
}
dstOps->needsInit = JNI_FALSE;
}
return mtlc;
}
- (void)resetClip {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.resetClip");
[_clip reset];
}
- (void)setClipRectX1:(jint)x1 Y1:(jint)y1 X2:(jint)x2 Y2:(jint)y2 {
J2dTraceLn4(J2D_TRACE_INFO, "MTLContext.setClipRect: %d,%d - %d,%d", x1, y1, x2, y2);
[_clip setClipRectX1:x1 Y1:y1 X2:x2 Y2:y2];
}
- (void)beginShapeClip:(BMTLSDOps *)dstOps {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.beginShapeClip");
[_clip beginShapeClip:dstOps context:self];
}
- (void)endShapeClip:(BMTLSDOps *)dstOps {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.endShapeClip");
[_clip endShapeClip:dstOps context:self];
}
- (void)resetComposite {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLContext_ResetComposite");
[_composite reset];
}
- (void)setAlphaCompositeRule:(jint)rule extraAlpha:(jfloat)extraAlpha
flags:(jint)flags {
J2dTraceLn3(J2D_TRACE_INFO, "MTLContext_SetAlphaComposite: rule=%d, extraAlpha=%1.2f, flags=%d", rule, extraAlpha, flags);
[_composite setRule:rule extraAlpha:extraAlpha];
}
- (NSString*)getCompositeDescription {
return [_composite getDescription];
}
- (NSString*)getPaintDescription {
return [_paint getDescription];
}
- (void)setXorComposite:(jint)xp {
J2dTraceLn1(J2D_TRACE_INFO, "MTLContext.setXorComposite: xorPixel=%08x", xp);
[_composite setXORComposite:xp];
}
- (jboolean)isBlendingDisabled:(jboolean) isSrcOpaque {
return [_composite isBlendingDisabled:isSrcOpaque];
}
- (void)resetTransform {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_ResetTransform");
[_transform resetTransform];
}
- (void)setTransformM00:(jdouble) m00 M10:(jdouble) m10
M01:(jdouble) m01 M11:(jdouble) m11
M02:(jdouble) m02 M12:(jdouble) m12 {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_SetTransform");
[_transform setTransformM00:m00 M10:m10 M01:m01 M11:m11 M02:m02 M12:m12];
}
- (jboolean)initBlitTileTexture {
//TODO
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_InitBlitTileTexture -- :TODO");
return JNI_TRUE;
}
- (jint)createBlitTextureFormat:(jint)internalFormat pixelFormat:(jint)pixelFormat
width:(jint)width height:(jint)height {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_InitBlitTileTexture -- :TODO");
//TODO
return 0;
}
- (void)resetPaint {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.resetPaint");
[_paint reset];
}
- (void)setColorPaint:(int)pixel {
J2dTraceLn5(J2D_TRACE_INFO, "MTLContext.setColorPaint: pixel=%08x [r=%d g=%d b=%d a=%d]", pixel, (pixel >> 16) & (0xFF), (pixel >> 8) & 0xFF, (pixel) & 0xFF, (pixel >> 24) & 0xFF);
[_paint setColor:pixel];
}
- (void)setGradientPaintUseMask:(jboolean)useMask
cyclic:(jboolean)cyclic
p0:(jdouble)p0
p1:(jdouble)p1
p3:(jdouble)p3
pixel1:(jint)pixel1
pixel2:(jint) pixel2
{
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.setGradientPaintUseMask");
[_paint setGradientUseMask:useMask
cyclic:cyclic
p0:p0
p1:p1
p3:p3
pixel1:pixel1
pixel2:pixel2];
}
- (void)setLinearGradientPaint:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jboolean)cycleMethod
numStops:(jint)numStops
p0:(jfloat)p0
p1:(jfloat)p1
p3:(jfloat)p3
fractions:(void *)fractions
pixels:(void *)pixels
{
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.setLinearGradientPaint");
[_paint setLinearGradient:useMask
linear:linear
cycleMethod:cycleMethod
numStops:numStops
p0:p0
p1:p1
p3:p3
fractions:fractions
pixels:pixels];
}
- (void)setRadialGradientPaint:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jboolean)cycleMethod
numStops:(jint)numStops
m00:(jfloat)m00
m01:(jfloat)m01
m02:(jfloat)m02
m10:(jfloat)m10
m11:(jfloat)m11
m12:(jfloat)m12
focusX:(jfloat)focusX
fractions:(void *)fractions
pixels:(void *)pixels
{
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.setRadialGradientPaint");
[_paint setRadialGradient:useMask
linear:linear
cycleMethod:cycleMethod
numStops:numStops
m00:m00
m01:m01
m02:m02
m10:m10
m11:m11
m12:m12
focusX:focusX
fractions:fractions
pixels:pixels];
}
- (void)setTexturePaint:(jboolean)useMask
pSrcOps:(jlong)pSrcOps
filter:(jboolean)filter
xp0:(jdouble)xp0
xp1:(jdouble)xp1
xp3:(jdouble)xp3
yp0:(jdouble)yp0
yp1:(jdouble)yp1
yp3:(jdouble)yp3
{
BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps);
if (srcOps == NULL || srcOps->pTexture == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext_setTexturePaint: texture paint - texture is null");
return;
}
J2dTraceLn1(J2D_TRACE_INFO, "MTLContext.setTexturePaint [tex=%p]", srcOps->pTexture);
[_paint setTexture:useMask
textureID:srcOps->pTexture
filter:filter
xp0:xp0
xp1:xp1
xp3:xp3
yp0:yp0
yp1:yp1
yp3:yp3];
}
- (id<MTLCommandBuffer>)createBlitCommandBuffer {
return [self.commandQueue commandBuffer];
}
-(void)setBufImgOp:(NSObject*)bufImgOp {
if (_bufImgOp != nil) {
[_bufImgOp release]; // context owns bufImgOp object
}
_bufImgOp = bufImgOp;
}
-(NSObject*)getBufImgOp {
return _bufImgOp;
}
@end
/*
* Class: sun_java2d_metal_MTLContext
* Method: getMTLIdString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_sun_java2d_metal_MTLContext_getMTLIdString
(JNIEnv *env, jclass mtlcc)
{
char *vendor, *renderer, *version;
char *pAdapterId;
jobject ret = NULL;
int len;
return NULL;
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLFuncs_h_Included
#define MTLFuncs_h_Included
#ifdef MACOSX
#include <dlfcn.h>
#endif
#include "jni.h"
#include "Trace.h"
jboolean MTLFuncs_OpenLibrary();
void MTLFuncs_CloseLibrary();
jboolean MTLFuncs_InitPlatformFuncs();
jboolean MTLFuncs_InitBaseFuncs();
jboolean MTLFuncs_InitExtFuncs();
#endif /* MTLFuncs_h_Included */

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include "MTLFuncs.h"
jboolean
MTLFuncs_OpenLibrary()
{
//TODO
J2dRlsTraceLn(J2D_TRACE_INFO, "MTLFuncs_OpenLibrary");
return JNI_TRUE;
}
void
MTLFuncs_CloseLibrary()
{
//TODO
J2dRlsTraceLn(J2D_TRACE_INFO, "MTLFuncs_CloseLibrary");
}
jboolean
MTLFuncs_InitPlatformFuncs()
{
//TODO
J2dRlsTraceLn(J2D_TRACE_INFO, "MTLFuncs_InitPlatformFuncs");
return JNI_TRUE;
}
jboolean
MTLFuncs_InitBaseFuncs()
{
//TODO
J2dRlsTraceLn(J2D_TRACE_INFO, "MTLFuncs_InitBaseFuncs");
return JNI_TRUE;
}
jboolean
MTLFuncs_InitExtFuncs()
{
//TODO
J2dRlsTraceLn(J2D_TRACE_INFO, "MTLFuncs_InitExtFuncs");
return JNI_TRUE;
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,99 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLGlyphCache_h_Included
#define MTLGlyphCache_h_Included
#ifdef __cplusplus
extern "C" {
#endif
#include "jni.h"
#include "fontscalerdefs.h"
#import <Metal/Metal.h>
typedef void (MTLFlushFunc)();
typedef struct _MTLCacheCellInfo MTLCacheCellInfo;
typedef struct {
CacheCellInfo *head;
CacheCellInfo *tail;
id<MTLTexture> texture;
jint width;
jint height;
jint cellWidth;
jint cellHeight;
MTLFlushFunc *Flush;
} MTLGlyphCacheInfo;
struct _MTLCacheCellInfo {
MTLGlyphCacheInfo *cacheInfo;
struct GlyphInfo *glyphInfo;
// next cell info in the cache's list
MTLCacheCellInfo *next;
// REMIND: find better name?
// next cell info in the glyph's cell list (next Glyph Cache Info)
MTLCacheCellInfo *nextGCI;
jint timesRendered;
jint x;
jint y;
// number of pixels from the left or right edge not considered touched
// by the glyph
jint leftOff;
jint rightOff;
jfloat tx1;
jfloat ty1;
jfloat tx2;
jfloat ty2;
};
MTLGlyphCacheInfo *
MTLGlyphCache_Init(jint width, jint height,
jint cellWidth, jint cellHeight,
MTLFlushFunc *func);
MTLCacheCellInfo *
MTLGlyphCache_AddGlyph(MTLGlyphCacheInfo *cache, struct GlyphInfo *glyph);
bool
MTLGlyphCache_IsCacheFull(MTLGlyphCacheInfo *cache, GlyphInfo *glyph);
void
MTLGlyphCache_Invalidate(MTLGlyphCacheInfo *cache);
void
MTLGlyphCache_AddCellInfo(struct GlyphInfo *glyph, MTLCacheCellInfo *cellInfo);
void
MTLGlyphCache_RemoveCellInfo(struct GlyphInfo *glyph, MTLCacheCellInfo *cellInfo);
MTLCacheCellInfo *
MTLGlyphCache_GetCellInfoForCache(struct GlyphInfo *glyph,
MTLGlyphCacheInfo *cache);
JNIEXPORT void
MTLGlyphCache_RemoveAllCellInfos(struct GlyphInfo *glyph);
void
MTLGlyphCache_Free(MTLGlyphCacheInfo *cache);
#ifdef __cplusplus
};
#endif
#endif /* MTLGlyphCache_h_Included */

View File

@@ -0,0 +1,365 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <stdlib.h>
#include "jni.h"
#include "MTLGlyphCache.h"
#include "Trace.h"
/**
* When the cache is full, we will try to reuse the cache cells that have
* been used relatively less than the others (and we will save the cells that
* have been rendered more than the threshold defined here).
*/
#define TIMES_RENDERED_THRESHOLD 5
/**
* Creates a new GlyphCacheInfo structure, fills in the initial values, and
* then returns a pointer to the GlyphCacheInfo record.
*
* Note that this method only sets up a data structure describing a
* rectangular region of accelerated memory, containing "virtual" cells of
* the requested size. The cell information is added lazily to the linked
* list describing the cache as new glyphs are added. Platform specific
* glyph caching code is responsible for actually creating the accelerated
* memory surface that will contain the individual glyph images.
*
* Each glyph contains a reference to a list of cell infos - one per glyph
* cache. There may be multiple glyph caches (for example, one per graphics
* adapter), so if the glyph is cached on two devices its cell list will
* consists of two elements corresponding to different glyph caches.
*
* The platform-specific glyph caching code is supposed to use
* GetCellInfoForCache method for retrieving cache infos from the glyph's list.
*
* Note that if it is guaranteed that there will be only one global glyph
* cache then it one does not have to use AccelGlyphCache_GetCellInfoForCache
* for retrieving cell info for the glyph, but instead just use the struct's
* field directly.
*/
MTLGlyphCacheInfo *
MTLGlyphCache_Init(jint width, jint height,
jint cellWidth, jint cellHeight,
MTLFlushFunc *func)
{
MTLGlyphCacheInfo *gcinfo;
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_Init");
gcinfo = (MTLGlyphCacheInfo *)malloc(sizeof(MTLGlyphCacheInfo));
if (gcinfo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLGlyphCache_Init: could not allocate MTLGlyphCacheInfo");
return NULL;
}
gcinfo->head = NULL;
gcinfo->tail = NULL;
gcinfo->width = width;
gcinfo->height = height;
gcinfo->cellWidth = cellWidth;
gcinfo->cellHeight = cellHeight;
gcinfo->Flush = func;
return gcinfo;
}
/**
* Attempts to add the provided glyph to the specified cache. If the
* operation is successful, a pointer to the newly occupied cache cell is
* stored in the glyph's cellInfo field; otherwise, its cellInfo field is
* set to NULL, indicating that the glyph's original bits should be rendered
* instead. If the cache is full, the least-recently-used glyph is
* invalidated and its cache cell is reassigned to the new glyph being added.
*
* Note that this method only ensures that a rectangular region in the
* "virtual" glyph cache is available for the glyph image. Platform specific
* glyph caching code is responsible for actually caching the glyph image
* in the associated accelerated memory surface.
*
* Returns created cell info if it was successfully created and added to the
* cache and glyph's cell lists, NULL otherwise.
*/
MTLCacheCellInfo *
MTLGlyphCache_AddGlyph(MTLGlyphCacheInfo *cache, GlyphInfo *glyph)
{
MTLCacheCellInfo *cellinfo = NULL;
jint w = glyph->width;
jint h = glyph->height;
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_AddGlyph");
if ((glyph->width > cache->cellWidth) ||
(glyph->height > cache->cellHeight))
{
return NULL;
}
jint x, y;
if (cache->head == NULL) {
x = 0;
y = 0;
} else {
x = cache->tail->x + cache->cellWidth;
y = cache->tail->y;
if ((x + cache->cellWidth) > cache->width) {
x = 0;
y += cache->cellHeight;
}
}
// create new CacheCellInfo
cellinfo = (MTLCacheCellInfo *)malloc(sizeof(MTLCacheCellInfo));
if (cellinfo == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "could not allocate CellInfo");
return NULL;
}
cellinfo->cacheInfo = cache;
cellinfo->glyphInfo = glyph;
cellinfo->timesRendered = 0;
cellinfo->x = x;
cellinfo->y = y;
cellinfo->leftOff = 0;
cellinfo->rightOff = 0;
cellinfo->tx1 = (jfloat)cellinfo->x / cache->width;
cellinfo->ty1 = (jfloat)cellinfo->y / cache->height;
cellinfo->tx2 = cellinfo->tx1 + ((jfloat)w / cache->width);
cellinfo->ty2 = cellinfo->ty1 + ((jfloat)h / cache->height);
if (cache->head == NULL) {
// initialize the head cell
cache->head = cellinfo;
} else {
// update existing tail cell
cache->tail->next = cellinfo;
}
// add the new cell to the end of the list
cache->tail = cellinfo;
cellinfo->next = NULL;
cellinfo->nextGCI = NULL;
// add cache cell to the glyph's cells list
MTLGlyphCache_AddCellInfo(glyph, cellinfo);
return cellinfo;
}
bool
MTLGlyphCache_IsCacheFull(MTLGlyphCacheInfo *cache, GlyphInfo *glyph)
{
jint w = glyph->width;
jint h = glyph->height;
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_IsCacheFull");
jint x, y;
if (cache->head == NULL) {
return JNI_FALSE;
} else {
x = cache->tail->x + cache->cellWidth;
y = cache->tail->y;
if ((x + cache->cellWidth) > cache->width) {
x = 0;
y += cache->cellHeight;
if ((y + cache->cellHeight) > cache->height) {
return JNI_TRUE;
}
}
}
return JNI_FALSE;
}
/**
* Invalidates all cells in the cache. Note that this method does not
* attempt to compact the cache in any way; it just invalidates any cells
* that already exist.
*/
void
MTLGlyphCache_Invalidate(MTLGlyphCacheInfo *cache)
{
MTLCacheCellInfo *cellinfo;
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_Invalidate");
if (cache == NULL) {
return;
}
// flush any pending vertices that may be depending on the current
// glyph cache layout
if (cache->Flush != NULL) {
cache->Flush();
}
cellinfo = cache->head;
while (cellinfo != NULL) {
if (cellinfo->glyphInfo != NULL) {
// if the cell is occupied, notify the base glyph that its
// cached version for this cache is about to be invalidated
MTLGlyphCache_RemoveCellInfo(cellinfo->glyphInfo, cellinfo);
}
cellinfo = cellinfo->next;
}
}
/**
* Invalidates and frees all cells and the cache itself. The "cache" pointer
* becomes invalid after this function returns.
*/
void
MTLGlyphCache_Free(MTLGlyphCacheInfo *cache)
{
MTLCacheCellInfo *cellinfo;
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_Free");
if (cache == NULL) {
return;
}
// flush any pending vertices that may be depending on the current
// glyph cache
if (cache->Flush != NULL) {
cache->Flush();
}
while (cache->head != NULL) {
cellinfo = cache->head;
if (cellinfo->glyphInfo != NULL) {
// if the cell is occupied, notify the base glyph that its
// cached version for this cache is about to be invalidated
MTLGlyphCache_RemoveCellInfo(cellinfo->glyphInfo, cellinfo);
}
cache->head = cellinfo->next;
free(cellinfo);
}
free(cache);
}
/**
* Add cell info to the head of the glyph's list of cached cells.
*/
void
MTLGlyphCache_AddCellInfo(GlyphInfo *glyph, MTLCacheCellInfo *cellInfo)
{
// assert (glyph != NULL && cellInfo != NULL)
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_AddCellInfo");
J2dTraceLn2(J2D_TRACE_VERBOSE, " glyph 0x%x: adding cell 0x%x to the list",
glyph, cellInfo);
cellInfo->glyphInfo = glyph;
cellInfo->nextGCI = glyph->cellInfo;
glyph->cellInfo = cellInfo;
glyph->managed = MANAGED_GLYPH;
}
/**
* Removes cell info from the glyph's list of cached cells.
*/
void
MTLGlyphCache_RemoveCellInfo(GlyphInfo *glyph, MTLCacheCellInfo *cellInfo)
{
MTLCacheCellInfo *currCellInfo = glyph->cellInfo;
MTLCacheCellInfo *prevInfo = NULL;
// assert (glyph!= NULL && glyph->cellInfo != NULL && cellInfo != NULL)
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_RemoveCellInfo");
do {
if (currCellInfo == cellInfo) {
J2dTraceLn2(J2D_TRACE_VERBOSE,
" glyph 0x%x: removing cell 0x%x from glyph's list",
glyph, currCellInfo);
if (prevInfo == NULL) { // it's the head, chop-chop
glyph->cellInfo = currCellInfo->nextGCI;
} else {
prevInfo->nextGCI = currCellInfo->nextGCI;
}
currCellInfo->glyphInfo = NULL;
currCellInfo->nextGCI = NULL;
return;
}
prevInfo = currCellInfo;
currCellInfo = currCellInfo->nextGCI;
} while (currCellInfo != NULL);
J2dTraceLn2(J2D_TRACE_WARNING, "MTLGlyphCache_RemoveCellInfo: "\
"no cell 0x%x in glyph 0x%x's cell list",
cellInfo, glyph);
}
/**
* Removes cell info from the glyph's list of cached cells.
*/
JNIEXPORT void
MTLGlyphCache_RemoveAllCellInfos(GlyphInfo *glyph)
{
MTLCacheCellInfo *currCell, *prevCell;
J2dTraceLn(J2D_TRACE_INFO, "MTLGlyphCache_RemoveAllCellInfos");
if (glyph == NULL || glyph->cellInfo == NULL) {
return;
}
// invalidate all of this glyph's accelerated cache cells
currCell = glyph->cellInfo;
do {
currCell->glyphInfo = NULL;
prevCell = currCell;
currCell = currCell->nextGCI;
prevCell->nextGCI = NULL;
} while (currCell != NULL);
glyph->cellInfo = NULL;
}
/**
* Returns cell info associated with particular cache from the glyph's list of
* cached cells.
*/
MTLCacheCellInfo *
MTLGlyphCache_GetCellInfoForCache(GlyphInfo *glyph, MTLGlyphCacheInfo *cache)
{
// assert (glyph != NULL && cache != NULL)
J2dTraceLn(J2D_TRACE_VERBOSE2, "MTLGlyphCache_GetCellInfoForCache");
if (glyph->cellInfo != NULL) {
MTLCacheCellInfo *cellInfo = glyph->cellInfo;
do {
if (cellInfo->cacheInfo == cache) {
J2dTraceLn3(J2D_TRACE_VERBOSE2,
" glyph 0x%x: found cell 0x%x for cache 0x%x",
glyph, cellInfo, cache);
return cellInfo;
}
cellInfo = cellInfo->nextGCI;
} while (cellInfo != NULL);
}
J2dTraceLn2(J2D_TRACE_VERBOSE2, " glyph 0x%x: no cell for cache 0x%x",
glyph, cache);
return NULL;
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLGraphicsConfig_h_Included
#define MTLGraphicsConfig_h_Included
#import "jni.h"
#import "MTLSurfaceDataBase.h"
#import "MTLContext.h"
#import <Cocoa/Cocoa.h>
#import <Metal/Metal.h>
@interface MTLGraphicsConfigUtil : NSObject {}
+ (void) _getMTLConfigInfo: (NSMutableArray *)argValue;
@end
// REMIND: Using an NSOpenGLPixelBuffer as the scratch surface has been
// problematic thus far (seeing garbage and flickering when switching
// between an NSView and the scratch surface), so the following enables
// an alternate codepath that uses a hidden NSWindow/NSView as the scratch
// surface, for the purposes of making a context current in certain
// situations. It appears that calling [NSOpenGLContext setView] too
// frequently contributes to the bad behavior, so we should try to avoid
// switching to the scratch surface whenever possible.
/* Do we need this if we are using all off-screen drawing ? */
#define USE_NSVIEW_FOR_SCRATCH 1
/* Uncomment to have an additional CAOGLLayer instance tied to
* each instance, which can be used to test remoting the layer
* to an out of process window. The additional layer is needed
* because a layer can only be attached to one context (view/window).
* This is only for testing purposes and can be removed if/when no
* longer needed.
*/
/**
* The MTLGraphicsConfigInfo structure contains information specific to a
* given CGLGraphicsConfig (pixel format).
*
* jint screen;
* The screen and PixelFormat for the associated CGLGraphicsConfig.
*
* NSOpenGLPixelFormat *pixfmt;
* The pixel format of the native NSOpenGL context.
*
* MTLContext *context;
* The context associated with this CGLGraphicsConfig.
*/
typedef struct _MTLGraphicsConfigInfo {
jint screen;
NSOpenGLPixelFormat *pixfmt;
MTLContext *context;
} MTLGraphicsConfigInfo;
#endif /* MTLGraphicsConfig_h_Included */

View File

@@ -0,0 +1,228 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#import "sun_java2d_metal_MTLGraphicsConfig.h"
#import "MTLGraphicsConfig.h"
#import "MTLSurfaceData.h"
#import "ThreadUtilities.h"
#import "awt.h"
#import <stdlib.h>
#import <string.h>
#import <ApplicationServices/ApplicationServices.h>
#import <JavaNativeFoundation/JavaNativeFoundation.h>
#pragma mark -
#pragma mark "--- Mac OS X specific methods for GL pipeline ---"
/**
* Disposes all memory and resources associated with the given
* CGLGraphicsConfigInfo (including its native MTLContext data).
*/
void
MTLGC_DestroyMTLGraphicsConfig(jlong pConfigInfo)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLGC_DestroyMTLGraphicsConfig");
MTLGraphicsConfigInfo *mtlinfo =
(MTLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo);
if (mtlinfo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLGC_DestroyMTLGraphicsConfig: info is null");
return;
}
MTLContext *mtlc = (MTLContext*)mtlinfo->context;
if (mtlc != NULL) {
[mtlinfo->context release];
mtlinfo->context = nil;
}
free(mtlinfo);
}
#pragma mark -
#pragma mark "--- MTLGraphicsConfig methods ---"
/**
* Attempts to initialize CGL and the core OpenGL library.
*/
JNIEXPORT jboolean JNICALL
Java_sun_java2d_metal_MTLGraphicsConfig_initMTL
(JNIEnv *env, jclass cglgc)
{
J2dRlsTraceLn(J2D_TRACE_INFO, "MTLGraphicsConfig_initMTL");
FILE *f = popen("system_profiler SPDisplaysDataType", "r");
bool metalSupport = FALSE;
while (getc(f) != EOF)
{
char str[60];
if (fgets(str, 60, f) != NULL) {
// Check for string
// "Metal: Supported, feature set macOS GPUFamily1 v4"
if (strstr(str, "Metal") != NULL) {
puts(str);
metalSupport = JNI_TRUE;
break;
}
}
}
pclose(f);
if (!metalSupport) {
fprintf(stderr, "Metal support not present\n");
} else {
fprintf(stderr, "Metal support is present\n");
}
if (!MTLFuncs_OpenLibrary()) {
return JNI_FALSE;
}
if (!MTLFuncs_InitPlatformFuncs() ||
!MTLFuncs_InitBaseFuncs() ||
!MTLFuncs_InitExtFuncs())
{
MTLFuncs_CloseLibrary();
return JNI_FALSE;
}
return JNI_TRUE;
}
/**
* Determines whether the CGL pipeline can be used for a given GraphicsConfig
* provided its screen number and visual ID. If the minimum requirements are
* met, the native CGLGraphicsConfigInfo structure is initialized for this
* GraphicsConfig with the necessary information (pixel format, etc.)
* and a pointer to this structure is returned as a jlong. If
* initialization fails at any point, zero is returned, indicating that CGL
* cannot be used for this GraphicsConfig (we should fallback on an existing
* 2D pipeline).
*/
JNIEXPORT jlong JNICALL
Java_sun_java2d_metal_MTLGraphicsConfig_getMTLConfigInfo
(JNIEnv *env, jclass cglgc, jint displayID, jstring mtlShadersLib)
{
jlong ret = 0L;
JNF_COCOA_ENTER(env);
NSMutableArray * retArray = [NSMutableArray arrayWithCapacity:3];
[retArray addObject: [NSNumber numberWithInt: (int)displayID]];
[retArray addObject: [NSString stringWithUTF8String: JNU_GetStringPlatformChars(env, mtlShadersLib, 0)]];
if ([NSThread isMainThread]) {
[MTLGraphicsConfigUtil _getMTLConfigInfo: retArray];
} else {
[MTLGraphicsConfigUtil performSelectorOnMainThread: @selector(_getMTLConfigInfo:) withObject: retArray waitUntilDone: YES];
}
NSNumber * num = (NSNumber *)[retArray objectAtIndex: 0];
ret = (jlong)[num longValue];
JNF_COCOA_EXIT(env);
return ret;
}
@implementation MTLGraphicsConfigUtil
+ (void) _getMTLConfigInfo: (NSMutableArray *)argValue {
AWT_ASSERT_APPKIT_THREAD;
jint displayID = (jint)[(NSNumber *)[argValue objectAtIndex: 0] intValue];
NSString *mtlShadersLib = (NSString *)[argValue objectAtIndex: 1];
JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
[argValue removeAllObjects];
J2dRlsTraceLn(J2D_TRACE_INFO, "MTLGraphicsConfig_getMTLConfigInfo");
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSRect contentRect = NSMakeRect(0, 0, 64, 64);
NSWindow *window =
[[NSWindow alloc]
initWithContentRect: contentRect
styleMask: NSBorderlessWindowMask
backing: NSBackingStoreBuffered
defer: false];
if (window == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLGraphicsConfig_getMTLConfigInfo: NSWindow is NULL");
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
NSView *scratchSurface =
[[NSView alloc]
initWithFrame: contentRect];
if (scratchSurface == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLGraphicsConfig_getMTLConfigInfo: NSView is NULL");
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
[window setContentView: scratchSurface];
MTLContext *mtlc = [[MTLContext alloc] initWithDevice:CGDirectDisplayCopyCurrentMetalDevice(displayID)
shadersLib:mtlShadersLib];
if (mtlc == 0L) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLGC_InitMTLContext: could not allocate memory for mtlc");
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
// create the MTLGraphicsConfigInfo record for this config
MTLGraphicsConfigInfo *mtlinfo = (MTLGraphicsConfigInfo *)malloc(sizeof(MTLGraphicsConfigInfo));
if (mtlinfo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLGraphicsConfig_getMTLConfigInfo: could not allocate memory for mtlinfo");
free(mtlc);
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
memset(mtlinfo, 0, sizeof(MTLGraphicsConfigInfo));
mtlinfo->screen = displayID;
mtlinfo->context = mtlc;
[argValue addObject: [NSNumber numberWithLong:ptr_to_jlong(mtlinfo)]];
[pool drain];
}
@end //GraphicsConfigUtil
JNIEXPORT jint JNICALL
Java_sun_java2d_metal_MTLGraphicsConfig_nativeGetMaxTextureSize
(JNIEnv *env, jclass mtlgc)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLGraphicsConfig_nativeGetMaxTextureSize");
// From "Metal Feature Set Tables"
// There are 2 GPU families for mac - MTLGPUFamilyMac1 and MTLGPUFamilyMac2
// Both of them support "Maximum 2D texture width and height" of 16384 pixels
// Note : there is no API to get this value, hence hardcoding by reading from the table
__block int max = 16384;
return (jint)max;
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLLayer_h_Included
#define MTLLayer_h_Included
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
#import "common.h"
#import <JavaNativeFoundation/JavaNativeFoundation.h>
@interface MTLLayer : CAMetalLayer
{
@private
JNFWeakJObjectWrapper *javaLayer;
// intermediate buffer, used the RQ lock to synchronize
MTLContext* ctx;
float bufferWidth;
float bufferHeight;
id<MTLTexture> buffer;
id<CAMetalDrawable> mtlDrawable;
id<MTLCommandBuffer> blitCommandBuf;
id<MTLBlitCommandEncoder> blitEncoder;
int topInset;
int leftInset;
}
@property (nonatomic, retain) JNFWeakJObjectWrapper *javaLayer;
@property (readwrite, assign) MTLContext* ctx;
@property (readwrite, assign) float bufferWidth;
@property (readwrite, assign) float bufferHeight;
@property (readwrite, assign) id<MTLTexture> buffer;
@property (readwrite, assign) id<CAMetalDrawable> mtlDrawable;
@property (readwrite, assign) id<MTLCommandBuffer> blitCommandBuf;
@property (readwrite, assign) id<MTLBlitCommandEncoder> blitEncoder;
@property (readwrite, assign) int topInset;
@property (readwrite, assign) int leftInset;
- (id) initWithJavaLayer:(JNFWeakJObjectWrapper *)layer;
- (void) blitTexture;
- (void) fillParallelogramCtxX:(jfloat)x
Y:(jfloat)y
DX1:(jfloat)dx1
DY1:(jfloat)dy1
DX2:(jfloat)dx2
DY2:(jfloat)dy2;
- (void) blitCallback;
- (void) display;
- (void) initBlit;
@end
#endif /* CGLLayer_h_Included */

View File

@@ -0,0 +1,239 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#import "MTLGraphicsConfig.h"
#import "MTLLayer.h"
#import "ThreadUtilities.h"
#import "LWCToolkit.h"
#import "MTLSurfaceData.h"
#import "MTLBlitLoops.h"
@implementation MTLLayer
@synthesize javaLayer;
@synthesize ctx;
@synthesize bufferWidth;
@synthesize bufferHeight;
@synthesize buffer;
@synthesize mtlDrawable;
@synthesize blitCommandBuf;
@synthesize blitEncoder;
@synthesize topInset;
@synthesize leftInset;
- (id) initWithJavaLayer:(JNFWeakJObjectWrapper *)layer
{
AWT_ASSERT_APPKIT_THREAD;
// Initialize ourselves
self = [super init];
if (self == nil) return self;
self.javaLayer = layer;
self.contentsGravity = kCAGravityTopLeft;
//Disable CALayer's default animation
NSMutableDictionary * actions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
[NSNull null], @"anchorPoint",
[NSNull null], @"bounds",
[NSNull null], @"contents",
[NSNull null], @"contentsScale",
[NSNull null], @"onOrderIn",
[NSNull null], @"onOrderOut",
[NSNull null], @"position",
[NSNull null], @"sublayers",
nil];
self.actions = actions;
[actions release];
self.topInset = 0;
self.leftInset = 0;
self.framebufferOnly = NO;
return self;
}
- (void) blitTexture {
@autoreleasepool {
[self.blitEncoder
copyFromTexture:self.buffer sourceSlice:0 sourceLevel:0
sourceOrigin:MTLOriginMake((jint)(self.leftInset*self.contentsScale), (jint)(self.topInset*self.contentsScale), 0)
sourceSize:MTLSizeMake(self.buffer.width, self.buffer.height, 1)
toTexture:self.mtlDrawable.texture destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(0, 0, 0)];
[self.blitEncoder endEncoding];
[self.blitCommandBuf presentDrawable:self.mtlDrawable];
[self.blitCommandBuf commit];
}
}
- (void) dealloc {
self.javaLayer = nil;
[super dealloc];
}
- (void) blitCallback {
JNIEnv *env = [ThreadUtilities getJNIEnv];
static JNF_CLASS_CACHE(jc_JavaLayer, "sun/java2d/metal/MTLLayer");
static JNF_MEMBER_CACHE(jm_drawInMTLContext, jc_JavaLayer, "drawInMTLContext", "()V");
jobject javaLayerLocalRef = [self.javaLayer jObjectWithEnv:env];
if ((*env)->IsSameObject(env, javaLayerLocalRef, NULL)) {
return;
}
JNFCallVoidMethod(env, javaLayerLocalRef, jm_drawInMTLContext);
(*env)->DeleteLocalRef(env, javaLayerLocalRef);
}
- (void) initBlit {
if (self.ctx == NULL || self.javaLayer == NULL || self.buffer == nil || self.ctx.device == nil) {
J2dTraceLn4(J2D_TRACE_VERBOSE, "MTLLayer.initBlit: uninitialized (mtlc=%p, javaLayer=%p, buffer=%p, devide=%p)", self.ctx, self.javaLayer, self.buffer, ctx.device);
return;
}
if ((self.buffer.width == 0) || (self.buffer.height == 0)) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.initBlit: cannot create drawable of size 0");
return;
}
self.blitCommandBuf = [self.ctx createBlitCommandBuffer];
if (self.blitCommandBuf == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.initBlit: nothing to do (commandBuf is null)");
return;
}
self.blitEncoder = [self.blitCommandBuf blitCommandEncoder];
if (self.blitEncoder == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.initBlit: blitEncoder is null)");
return;
}
self.mtlDrawable = [self nextDrawable];
if (self.mtlDrawable == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.initBlit: nextDrawable is null)");
return;
}
J2dTraceLn6(J2D_TRACE_VERBOSE, "MTLLayer.initBlit: src tex=%p (w=%d, h=%d), dst tex=%p (w=%d, h=%d)", self.buffer, self.buffer.width, self.buffer.height, self.mtlDrawable.texture, self.mtlDrawable.texture.width, self.mtlDrawable.texture.height);
}
- (void) display {
AWT_ASSERT_APPKIT_THREAD;
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer_display() called");
[self initBlit];
[self blitCallback];
[super display];
}
@end
/*
* Class: sun_java2d_metal_MTLLayer
* Method: nativeCreateLayer
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_sun_java2d_metal_MTLLayer_nativeCreateLayer
(JNIEnv *env, jobject obj)
{
__block MTLLayer *layer = nil;
JNF_COCOA_ENTER(env);
JNFWeakJObjectWrapper *javaLayer = [JNFWeakJObjectWrapper wrapperWithJObject:obj withEnv:env];
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
AWT_ASSERT_APPKIT_THREAD;
layer = [[MTLLayer alloc] initWithJavaLayer: javaLayer];
}];
JNF_COCOA_EXIT(env);
return ptr_to_jlong(layer);
}
// Must be called under the RQ lock.
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLLayer_validate
(JNIEnv *env, jclass cls, jlong layerPtr, jobject surfaceData)
{
MTLLayer *layer = OBJC(layerPtr);
if (surfaceData != NULL) {
BMTLSDOps *bmtlsdo = (BMTLSDOps*) SurfaceData_GetOps(env, surfaceData);
layer.bufferWidth = bmtlsdo->width;
layer.bufferHeight = bmtlsdo->width;
layer.buffer = bmtlsdo->pTexture;
layer.ctx = ((MTLSDOps *)bmtlsdo->privOps)->configInfo->context;
layer.device = layer.ctx.device;
layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
layer.drawableSize =
CGSizeMake(layer.buffer.width,
layer.buffer.height);
} else {
layer.ctx = NULL;
}
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLLayer_nativeSetScale
(JNIEnv *env, jclass cls, jlong layerPtr, jdouble scale)
{
JNF_COCOA_ENTER(env);
MTLLayer *layer = jlong_to_ptr(layerPtr);
// We always call all setXX methods asynchronously, exception is only in
// this method where we need to change native texture size and layer's scale
// in one call on appkit, otherwise we'll get window's contents blinking,
// during screen-2-screen moving.
[ThreadUtilities performOnMainThreadWaiting:[NSThread isMainThread] block:^(){
layer.contentsScale = scale;
}];
JNF_COCOA_EXIT(env);
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLLayer_nativeSetInsets
(JNIEnv *env, jclass cls, jlong layerPtr, jint top, jint left)
{
MTLLayer *layer = jlong_to_ptr(layerPtr);
layer.topInset = top;
layer.leftInset = left;
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLLayer_blitTexture
(JNIEnv *env, jclass cls, jlong layerPtr)
{
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer_blitTexture");
MTLLayer *layer = jlong_to_ptr(layerPtr);
MTLContext * ctx = layer.ctx;
if (layer == NULL || ctx == NULL) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer_blit : Layer or Context is null");
return;
}
[layer blitTexture];
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLMaskBlit_h_Included
#define MTLMaskBlit_h_Included
#include "MTLContext.h"
void MTLMaskBlit_MaskBlit(JNIEnv *env, MTLContext *mtlc, BMTLSDOps * dstOps,
jint dstx, jint dsty,
jint width, jint height,
void *pPixels);
#endif /* MTLMaskBlit_h_Included */

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <stdlib.h>
#include <jlong.h>
#include "MTLMaskBlit.h"
#include "MTLRenderQueue.h"
#include "MTLBlitLoops.h"
/**
* REMIND: This method assumes that the dimensions of the incoming pixel
* array are less than or equal to the cached blit texture tile;
* these are rather fragile assumptions, and should be cleaned up...
*/
void
MTLMaskBlit_MaskBlit(JNIEnv *env, MTLContext *mtlc, BMTLSDOps * dstOps,
jint dstx, jint dsty,
jint width, jint height,
void *pPixels)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLMaskBlit_MaskBlit");
if (width <= 0 || height <= 0) {
J2dTraceLn(J2D_TRACE_WARNING, "MTLMaskBlit_MaskBlit: invalid dimensions");
return;
}
RETURN_IF_NULL(pPixels);
RETURN_IF_NULL(mtlc);
MTLPooledTextureHandle * texHandle = [mtlc.texturePool
getTexture:width
height:height
format:MTLPixelFormatBGRA8Unorm];
if (texHandle == nil) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLMaskBlit_MaskBlit: can't obtain temporary texture object from pool");
return;
}
[[mtlc getCommandBufferWrapper] registerPooledTexture:texHandle];
[texHandle release];
id<MTLTexture> texBuff = texHandle.texture;
MTLRegion region = MTLRegionMake2D(0, 0, width, height);
[texBuff replaceRegion:region mipmapLevel:0 withBytes:pPixels bytesPerRow:4*width];
drawTex2Tex(mtlc, texBuff, dstOps->pTexture, JNI_FALSE, dstOps->isOpaque, 0,
0, 0, width, height, dstx, dsty, dstx + width, dsty + height);
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLMaskFill_h_Included
#define MTLMaskFill_h_Included
#include "MTLContext.h"
void MTLMaskFill_MaskFill(MTLContext *mtlc, BMTLSDOps * dstOps,
jint x, jint y, jint w, jint h,
jint maskoff, jint maskscan, jint masklen,
unsigned char *pMask);
#endif /* MTLMaskFill_h_Included */

View File

@@ -0,0 +1,153 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include "sun_java2d_metal_MTLMaskFill.h"
#include "MTLMaskFill.h"
#include "MTLRenderQueue.h"
#include "MTLVertexCache.h"
/**
* This implementation first copies the alpha tile into a texture and then
* maps that texture to the destination surface. This approach appears to
* offer the best performance despite being a two-step process.
*
* When the source paint is a Color, we can simply use the GL_MODULATE
* function to multiply the current color (already premultiplied with the
* extra alpha value from the AlphaComposite) with the alpha value from
* the mask texture tile. In picture form, this process looks like:
*
* A R G B
* primary color Pa Pr Pg Pb (modulated with...)
* texture unit 0 Ca Ca Ca Ca
* ---------------------------------------
* resulting color Ra Rr Rg Rb
*
* where:
* Px = current color (already premultiplied by extra alpha)
* Cx = coverage value from mask tile
* Rx = resulting color/alpha component
*
* When the source paint is not a Color, it means that we are rendering with
* a complex paint (e.g. GradientPaint, TexturePaint). In this case, we
* rely on the GL_ARB_multitexture extension to effectively multiply the
* paint fragments (autogenerated on texture unit 1, see the
* MTLPaints_Set{Gradient,Texture,etc}Paint() methods for more details)
* with the coverage values from the mask texture tile (provided on texture
* unit 0), all of which is multiplied with the current color value (which
* contains the extra alpha value). In picture form:
*
* A R G B
* primary color Ea Ea Ea Ea (modulated with...)
* texture unit 0 Ca Ca Ca Ca (modulated with...)
* texture unit 1 Pa Pr Pg Pb
* ---------------------------------------
* resulting color Ra Rr Rg Rb
*
* where:
* Ea = extra alpha
* Cx = coverage value from mask tile
* Px = gradient/texture paint color (generated for each fragment)
* Rx = resulting color/alpha component
*
* Here are some descriptions of the many variables used in this method:
* x,y - upper left corner of the tile destination
* w,h - width/height of the mask tile
* x0 - placekeeper for the original destination x location
* tw,th - width/height of the actual texture tile in pixels
* sx1,sy1 - upper left corner of the mask tile source region
* sx2,sy2 - lower left corner of the mask tile source region
* sx,sy - "current" upper left corner of the mask tile region of interest
*/
void
MTLMaskFill_MaskFill(MTLContext *mtlc, BMTLSDOps * dstOps,
jint x, jint y, jint w, jint h,
jint maskoff, jint maskscan, jint masklen,
unsigned char *pMask)
{
J2dTraceLn5(J2D_TRACE_INFO, "MTLMaskFill_MaskFill (x=%d y=%d w=%d h=%d pMask=%p)", x, y, w, h, dstOps->pTexture);
MTLVertexCache_EnableMaskCache(mtlc, dstOps);
jint tw, th, x0;
jint sx1, sy1, sx2, sy2;
jint sx, sy, sw, sh;
x0 = x;
tw = MTLVC_MASK_CACHE_TILE_WIDTH;
th = MTLVC_MASK_CACHE_TILE_HEIGHT;
sx1 = maskoff % maskscan;
sy1 = maskoff / maskscan;
sx2 = sx1 + w;
sy2 = sy1 + h;
for (sy = sy1; sy < sy2; sy += th, y += th) {
x = x0;
sh = ((sy + th) > sy2) ? (sy2 - sy) : th;
for (sx = sx1; sx < sx2; sx += tw, x += tw) {
sw = ((sx + tw) > sx2) ? (sx2 - sx) : tw;
MTLVertexCache_AddMaskQuad(mtlc,
sx, sy, x, y, sw, sh,
MTLVC_MASK_CACHE_TILE_WIDTH, pMask, dstOps,
MTLVC_MASK_CACHE_TILE_WIDTH);
}
}
MTLVertexCache_DisableMaskCache(mtlc);
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLMaskFill_maskFill
(JNIEnv *env, jobject self,
jint x, jint y, jint w, jint h,
jint maskoff, jint maskscan, jint masklen,
jbyteArray maskArray)
{
MTLContext *mtlc = MTLRenderQueue_GetCurrentContext();
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
unsigned char *mask;
J2dTraceLn(J2D_TRACE_ERROR, "MTLMaskFill_maskFill");
if (maskArray != NULL) {
mask = (unsigned char *)
(*env)->GetPrimitiveArrayCritical(env, maskArray, NULL);
} else {
mask = NULL;
}
MTLMaskFill_MaskFill(mtlc, dstOps,
x, y, w, h,
maskoff, maskscan, masklen, mask);
if (mask != NULL) {
(*env)->ReleasePrimitiveArrayCritical(env, maskArray, mask, JNI_ABORT);
}
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,108 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLPaints_h_Included
#define MTLPaints_h_Included
#import <Metal/Metal.h>
#include "RenderOptions.h"
#define sun_java2d_SunGraphics2D_PAINT_UNDEFINED -1
@class MTLContext;
@class MTLComposite;
@class MTLClip;
@class MTLPipelineStatesStorage;
/**
* The MTLPaint class represents paint mode (color, gradient, e.t.c.)
* */
@interface MTLPaint : NSObject
- (id)init;
- (BOOL)isEqual:(MTLPaint *)other; // used to compare requested with cached
- (void)copyFrom:(MTLPaint *)other; // used to save cached
- (NSString *)getDescription;
- (jint)getColor;
- (void)reset;
- (void)setColor:(jint)pixelColor;
- (void)setGradientUseMask:(jboolean)useMask
cyclic:(jboolean)cyclic
p0:(jdouble)p0
p1:(jdouble)p1
p3:(jdouble)p3
pixel1:(jint)pixel1
pixel2:(jint)pixel2;
- (void)setLinearGradient:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jboolean)cycleMethod
numStops:(jint)numStops
p0:(jfloat)p0
p1:(jfloat)p1
p3:(jfloat)p3
fractions:(void *)fractions
pixels:(void *)pixels;
- (void)setRadialGradient:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jboolean)cycleMethod
numStops:(jint)numStops
m00:(jfloat)m00
m01:(jfloat)m01
m02:(jfloat)m02
m10:(jfloat)m10
m11:(jfloat)m11
m12:(jfloat)m12
focusX:(jfloat)focusX
fractions:(void *)fractions
pixels:(void *)pixels;
- (void)setTexture:(jboolean)useMask
textureID:(id<MTLTexture>)textureID
filter:(jboolean)filter
xp0:(jdouble)xp0
xp1:(jdouble)xp1
xp3:(jdouble)xp3
yp0:(jdouble)yp0
yp1:(jdouble)yp1
yp3:(jdouble)yp3;
// For the current paint mode and passed composite (and flags):
// 1. Selects vertex+fragment shader (and corresponding pipelineDesc) and set pipelineState
// 2. Prepares corresponding buffers of vertex and fragment shaders
- (void)setPipelineState:(id <MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage;
- (void)setXorModePipelineState:(id <MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage;
@end
#endif /* MTLPaints_h_Included */

View File

@@ -0,0 +1,903 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include "MTLPaints.h"
#include "MTLClip.h"
#include "common.h"
#include "sun_java2d_SunGraphics2D.h"
#include "sun_java2d_pipe_BufferedPaints.h"
#import "MTLComposite.h"
#import "MTLBufImgOps.h"
#define RGBA_TO_V4(c) \
{ \
(((c) >> 16) & (0xFF))/255.0f, \
(((c) >> 8) & 0xFF)/255.0f, \
((c) & 0xFF)/255.0f, \
(((c) >> 24) & 0xFF)/255.0f \
}
#define FLOAT_ARR_TO_V4(p) \
{ \
p[0], \
p[1], \
p[2], \
p[3] \
}
static MTLRenderPipelineDescriptor * templateRenderPipelineDesc = nil;
static MTLRenderPipelineDescriptor * templateTexturePipelineDesc = nil;
static MTLRenderPipelineDescriptor * templateAATexturePipelineDesc = nil;
static void initTemplatePipelineDescriptors() {
if (templateRenderPipelineDesc != nil && templateTexturePipelineDesc != nil)
return;
MTLVertexDescriptor *vertDesc = [[MTLVertexDescriptor new] autorelease];
vertDesc.attributes[VertexAttributePosition].format = MTLVertexFormatFloat2;
vertDesc.attributes[VertexAttributePosition].offset = 0;
vertDesc.attributes[VertexAttributePosition].bufferIndex = MeshVertexBuffer;
vertDesc.layouts[MeshVertexBuffer].stride = sizeof(struct Vertex);
vertDesc.layouts[MeshVertexBuffer].stepRate = 1;
vertDesc.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;
templateRenderPipelineDesc = [[MTLRenderPipelineDescriptor new] autorelease];
templateRenderPipelineDesc.sampleCount = 1;
templateRenderPipelineDesc.vertexDescriptor = vertDesc;
templateRenderPipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
templateRenderPipelineDesc.label = @"template_render";
templateTexturePipelineDesc = [[templateRenderPipelineDesc copy] autorelease];
templateTexturePipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].format = MTLVertexFormatFloat2;
templateTexturePipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].offset = 2*sizeof(float);
templateTexturePipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].bufferIndex = MeshVertexBuffer;
templateTexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stride = sizeof(struct TxtVertex);
templateTexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepRate = 1;
templateTexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;
templateTexturePipelineDesc.label = @"template_texture";
templateAATexturePipelineDesc = [[templateTexturePipelineDesc copy] autorelease];
templateAATexturePipelineDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOne;
templateAATexturePipelineDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne;
templateAATexturePipelineDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
templateAATexturePipelineDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
templateAATexturePipelineDesc.label = @"template_aa_texture";
}
@implementation MTLPaint {
// TODO: remove paintState, split into heirarchy of Paint-objects (i.e. PaintColor, PaintGrad, e.t.c)
jint _paintState;
// color-mode
jint _color;
// lin-grad-mode
jdouble _p0;
jdouble _p1;
jdouble _p3;
jboolean _cyclic;
jint _pixel1;
jint _pixel2;
jboolean _useMask;
// texture paint
id<MTLTexture> _paintTexture;
struct AnchorData _anchor;
}
- (id)init {
self = [super init];
if (self) {
_paintState = sun_java2d_SunGraphics2D_PAINT_UNDEFINED;
}
return self;
}
- (BOOL)isEqual:(MTLPaint *)other {
if (self == other)
return YES;
if (_paintState == sun_java2d_SunGraphics2D_PAINT_UNDEFINED)
return _paintState == other->_paintState;
if (_paintState != other->_paintState)
return NO;
if (_paintState == sun_java2d_SunGraphics2D_PAINT_GRADIENT) {
return _p0 == other->_p0
&& _p1 == other->_p1
&& _p3 == other->_p3
&& _pixel1 == other->_pixel1
&& _pixel2 == other->_pixel2;
}
if (_paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) {
return _color == other->_color;
}
if (_paintState == sun_java2d_SunGraphics2D_PAINT_TEXTURE) {
return _paintTexture == other->_paintTexture
&& _anchor.xParams[0] == other->_anchor.xParams[0]
&& _anchor.xParams[1] == other->_anchor.xParams[1]
&& _anchor.xParams[2] == other->_anchor.xParams[2]
&& _anchor.yParams[0] == other->_anchor.yParams[0]
&& _anchor.yParams[1] == other->_anchor.yParams[1]
&& _anchor.yParams[2] == other->_anchor.yParams[2];
}
J2dTraceLn1(J2D_TRACE_ERROR, "Unimplemented paint mode %d", _paintState);
return NO;
}
- (void)copyFrom:(MTLPaint *)other {
_paintState = other->_paintState;
if (other->_paintState == sun_java2d_SunGraphics2D_PAINT_UNDEFINED)
return;
if (other->_paintState == sun_java2d_SunGraphics2D_PAINT_GRADIENT) {
_p0 = other->_p0;
_p1 = other->_p1;
_p3 = other->_p3;
_pixel1 = other->_pixel1;
_pixel2 = other->_pixel2;
return;
}
if (_paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) {
_color = other->_color;
return;
}
if (_paintState == sun_java2d_SunGraphics2D_PAINT_TEXTURE) {
_color = other->_color;
_paintTexture = other->_paintTexture;
_anchor = other->_anchor;
return;
}
J2dTraceLn1(J2D_TRACE_ERROR, "Unsupported paint mode %d", _paintState);
}
- (NSString *)getDescription {
if (_paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) {
return [NSString stringWithFormat:@"[r=%d g=%d b=%d a=%d]", (_color >> 16) & (0xFF), (_color >> 8) & 0xFF, (_color) & 0xFF, (_color >> 24) & 0xFF];
}
if (_paintState == sun_java2d_SunGraphics2D_PAINT_GRADIENT) {
return [NSString stringWithFormat:@"gradient"];
}
if (_paintState == sun_java2d_SunGraphics2D_PAINT_TEXTURE) {
return [NSString stringWithFormat:@"texture_paint"];
}
return @"unknown-paint";
}
- (jint)getColor {
return _color;
}
- (void)reset {
_paintState = sun_java2d_SunGraphics2D_PAINT_UNDEFINED;
_paintTexture = nil;
_anchor.xParams[0] = _anchor.xParams[1] = _anchor.xParams[2] = 0.0f;
_anchor.yParams[0] = _anchor.yParams[1] = _anchor.yParams[2] = 0.0f;
}
- (void)setColor:(jint)pixelColor {
_paintState = sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR;
_color = pixelColor;
}
- (void)setGradientUseMask:(jboolean)useMask
cyclic:(jboolean)cyclic
p0:(jdouble)p0
p1:(jdouble)p1
p3:(jdouble)p3
pixel1:(jint)pixel1
pixel2:(jint)pixel2
{
//TODO Resolve gradient distribution problem
//TODO Implement useMask
//TODO Implement cyclic
//fprintf(stderr,
// "MTLPaints_SetGradientPaint useMask=%d cyclic=%d "
// "p0=%f p1=%f p3=%f pix1=%d pix2=%d\n", useMask, cyclic,
// p0, p1, p3, pixel1, pixel2);
_paintState = sun_java2d_SunGraphics2D_PAINT_GRADIENT;
_useMask = useMask;
_pixel1 = pixel1;
_pixel2 = pixel2;
_p0 = p0;
_p1 = p1;
_p3 = p3;
_cyclic = cyclic;
}
- (void)setLinearGradient:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jboolean)cycleMethod
numStops:(jint)numStops
p0:(jfloat)p0
p1:(jfloat)p1
p3:(jfloat)p3
fractions:(void *)fractions
pixels:(void *)pixels
{
J2dTraceLn(J2D_TRACE_ERROR, "setLinearGradient: UNIMPLEMENTED");
[self setColor:0];
}
- (void)setRadialGradient:(jboolean)useMask
linear:(jboolean)linear
cycleMethod:(jboolean)cycleMethod
numStops:(jint)numStops
m00:(jfloat)m00
m01:(jfloat)m01
m02:(jfloat)m02
m10:(jfloat)m10
m11:(jfloat)m11
m12:(jfloat)m12
focusX:(jfloat)focusX
fractions:(void *)fractions
pixels:(void *)pixels
{
J2dTraceLn(J2D_TRACE_ERROR, "setRadialGradient: UNIMPLEMENTED");
[self setColor:0];
}
- (void)setTexture:(jboolean)useMask
textureID:(id<MTLTexture>)textureID
filter:(jboolean)filter
xp0:(jdouble)xp0
xp1:(jdouble)xp1
xp3:(jdouble)xp3
yp0:(jdouble)yp0
yp1:(jdouble)yp1
yp3:(jdouble)yp3
{
_paintState = sun_java2d_SunGraphics2D_PAINT_TEXTURE;
_paintTexture = textureID;
_anchor.xParams[0] = xp0;
_anchor.xParams[1] = xp1;
_anchor.xParams[2] = xp3;
_anchor.yParams[0] = yp0;
_anchor.yParams[1] = yp1;
_anchor.yParams[2] = yp3;
}
static id<MTLSamplerState> samplerNearestClamp = nil;
static id<MTLSamplerState> samplerLinearClamp = nil;
static id<MTLSamplerState> samplerNearestRepeat = nil;
static id<MTLSamplerState> samplerLinearRepeat = nil;
void initSamplers(id<MTLDevice> device) {
// TODO: move this code into SamplerManager (need implement)
if (samplerNearestClamp != nil)
return;
MTLSamplerDescriptor *samplerDescriptor = [MTLSamplerDescriptor new];
samplerDescriptor.rAddressMode = MTLSamplerAddressModeClampToEdge;
samplerDescriptor.sAddressMode = MTLSamplerAddressModeClampToEdge;
samplerDescriptor.tAddressMode = MTLSamplerAddressModeClampToEdge;
samplerDescriptor.minFilter = MTLSamplerMinMagFilterNearest;
samplerDescriptor.magFilter = MTLSamplerMinMagFilterNearest;
samplerNearestClamp = [device newSamplerStateWithDescriptor:samplerDescriptor];
samplerDescriptor.minFilter = MTLSamplerMinMagFilterLinear;
samplerDescriptor.magFilter = MTLSamplerMinMagFilterLinear;
samplerLinearClamp = [device newSamplerStateWithDescriptor:samplerDescriptor];
samplerDescriptor.rAddressMode = MTLSamplerAddressModeRepeat;
samplerDescriptor.sAddressMode = MTLSamplerAddressModeRepeat;
samplerDescriptor.tAddressMode = MTLSamplerAddressModeRepeat;
samplerDescriptor.minFilter = MTLSamplerMinMagFilterNearest;
samplerDescriptor.magFilter = MTLSamplerMinMagFilterNearest;
samplerNearestRepeat = [device newSamplerStateWithDescriptor:samplerDescriptor];
samplerDescriptor.minFilter = MTLSamplerMinMagFilterLinear;
samplerDescriptor.magFilter = MTLSamplerMinMagFilterLinear;
samplerLinearRepeat = [device newSamplerStateWithDescriptor:samplerDescriptor];
}
static void setSampler(id<MTLRenderCommandEncoder> encoder, int interpolation, bool repeat) {
id<MTLSamplerState> sampler;
if (repeat) {
sampler = interpolation == INTERPOLATION_BILINEAR ? samplerLinearRepeat : samplerNearestRepeat;
} else {
sampler = interpolation == INTERPOLATION_BILINEAR ? samplerLinearClamp : samplerNearestClamp;
}
[encoder setFragmentSamplerState:sampler atIndex:0];
}
static void setTxtUniforms(
id<MTLRenderCommandEncoder> encoder, int color, int mode, int interpolation, bool repeat, jfloat extraAlpha,
const SurfaceRasterFlags * srcFlags, const SurfaceRasterFlags * dstFlags
) {
struct TxtFrameUniforms uf = {RGBA_TO_V4(color), mode, srcFlags->isOpaque, dstFlags->isOpaque, extraAlpha};
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
setSampler(encoder, interpolation, repeat);
}
// For the current paint mode:
// 1. Selects vertex+fragment shaders (and corresponding pipelineDesc) and set pipelineState
// 2. Set vertex and fragment buffers
- (void)setPipelineState:(id<MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
{
initTemplatePipelineDescriptors();
NSString * vertShader = @"vert_txt";
NSString * fragShader = @"frag_txt";
MTLRenderPipelineDescriptor * rpDesc = templateTexturePipelineDesc;
if (renderOptions->isTexture) {
NSObject *bufImgOp = [mtlc getBufImgOp];
if (bufImgOp != nil) {
if ([bufImgOp isKindOfClass:[MTLRescaleOp class]]) {
MTLRescaleOp *rescaleOp = bufImgOp;
vertShader = @"vert_txt";
fragShader = @"frag_txt_op_rescale";
struct TxtFrameOpRescaleUniforms uf = {
RGBA_TO_V4(0), [mtlc.composite getExtraAlpha], renderOptions->srcFlags.isOpaque,
rescaleOp.isNonPremult,
FLOAT_ARR_TO_V4([rescaleOp getScaleFactors]), FLOAT_ARR_TO_V4([rescaleOp getOffsets])
};
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
setSampler(encoder, renderOptions->interpolation, NO);
} else if ([bufImgOp isKindOfClass:[MTLConvolveOp class]]) {
MTLConvolveOp * convolveOp = bufImgOp;
vertShader = @"vert_txt";
fragShader = @"frag_txt_op_convolve";
struct TxtFrameOpConvolveUniforms uf = {
[mtlc.composite getExtraAlpha], FLOAT_ARR_TO_V4([convolveOp getImgEdge]),
convolveOp.kernelSize, convolveOp.isEdgeZeroFill,
};
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
setSampler(encoder, renderOptions->interpolation, NO);
[encoder setFragmentBuffer:[convolveOp getBuffer] offset:0 atIndex:2];
} else if ([bufImgOp isKindOfClass:[MTLLookupOp class]]) {
MTLLookupOp * lookupOp = bufImgOp;
vertShader = @"vert_txt";
fragShader = @"frag_txt_op_lookup";
struct TxtFrameOpLookupUniforms uf = {
[mtlc.composite getExtraAlpha], FLOAT_ARR_TO_V4([lookupOp getOffset]),
lookupOp.isUseSrcAlpha, lookupOp.isNonPremult,
};
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
setSampler(encoder, renderOptions->interpolation, NO);
[encoder setFragmentTexture:[lookupOp getLookupTexture] atIndex: 1];
}
} else if (_paintState == sun_java2d_SunGraphics2D_PAINT_TEXTURE) {
vertShader = @"vert_txt_tp";
fragShader = @"frag_txt_tp";
[encoder setVertexBytes:&_anchor length:sizeof(_anchor) atIndex:FrameUniformBuffer];
[encoder setFragmentTexture:_paintTexture atIndex:1];
setTxtUniforms(encoder, 0, 0, renderOptions->interpolation, YES, [mtlc.composite getExtraAlpha],
&renderOptions->srcFlags, &renderOptions->dstFlags);
} else if (_paintState == sun_java2d_SunGraphics2D_PAINT_GRADIENT) {
vertShader = @"vert_txt_grad";
fragShader = @"frag_txt_grad";
struct GradFrameUniforms uf = {
{_p0, _p1, _p3},
RGBA_TO_V4(_pixel1),
RGBA_TO_V4(_pixel2)};
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:0];
} else {
vertShader = @"vert_txt";
fragShader = @"frag_txt";
if (renderOptions->isAA) {
fragShader = @"aa_frag_txt";
rpDesc = templateAATexturePipelineDesc;
}
setTxtUniforms(encoder, _color, _paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR ? 1 : 0,
renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha], &renderOptions->srcFlags,
&renderOptions->dstFlags);
}
} else {
rpDesc = templateRenderPipelineDesc;
if (_paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) {
vertShader = @"vert_col";
fragShader = @"frag_col";
struct FrameUniforms uf = {RGBA_TO_V4(_color)};
[encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
} else if (_paintState == sun_java2d_SunGraphics2D_PAINT_GRADIENT) {
vertShader = @"vert_grad";
fragShader = @"frag_grad";
struct GradFrameUniforms uf = {
{_p0, _p1, _p3},
RGBA_TO_V4(_pixel1),
RGBA_TO_V4(_pixel2)};
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:0];
} else if (_paintState == sun_java2d_SunGraphics2D_PAINT_TEXTURE) {
vertShader = @"vert_tp";
fragShader = @"frag_tp";
[encoder setVertexBytes:&_anchor length:sizeof(_anchor) atIndex:FrameUniformBuffer];
[encoder setFragmentTexture:_paintTexture atIndex:0];
}
}
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
vertexShaderId:vertShader
fragmentShaderId:fragShader
composite:mtlc.composite
renderOptions:renderOptions
stencilNeeded:[mtlc.clip isShape]];
[encoder setRenderPipelineState:pipelineState];
}
// For the current paint mode: and for XOR composite - a separate method is added as fragment shader differ in some cases
// 1. Selects vertex+fragment shaders (and corresponding pipelineDesc) and set pipelineState
// 2. Set vertex and fragment buffers
- (void)setXorModePipelineState:(id<MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
{
initTemplatePipelineDescriptors();
jint xorColor = (jint) [mtlc.composite getXorColor];
NSString * vertShader = @"vert_txt";
NSString * fragShader = @"frag_txt";
MTLRenderPipelineDescriptor * rpDesc = templateTexturePipelineDesc;
if (renderOptions->isTexture) {
const int col = _paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR ? _color ^ xorColor : 0 ^ xorColor;
setTxtUniforms(encoder, col, _paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR ? 1 : 0, renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha], &renderOptions->srcFlags, &renderOptions->dstFlags);
[encoder setFragmentBytes:&xorColor length:sizeof(xorColor) atIndex: 0];
} else {
if (_paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) {
vertShader = @"vert_col";
fragShader = @"frag_col";
rpDesc = templateRenderPipelineDesc;
// Calculate _color ^ xorColor for RGB components
// This color gets XORed with destination framebuffer pixel color
struct FrameUniforms uf = {RGBA_TO_V4(_color ^ xorColor)};
[encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
} else if (_paintState == sun_java2d_SunGraphics2D_PAINT_GRADIENT) {
vertShader = @"vert_grad";
fragShader = @"frag_grad";
rpDesc = templateRenderPipelineDesc;
struct GradFrameUniforms uf = {
{_p0, _p1, _p3},
RGBA_TO_V4(_pixel1 ^ xorColor),
RGBA_TO_V4(_pixel2 ^ xorColor)};
[encoder setFragmentBytes: &uf length:sizeof(uf) atIndex:0];
} else if (_paintState == sun_java2d_SunGraphics2D_PAINT_TEXTURE) {
vertShader = @"vert_tp";
fragShader = @"frag_tp_xorMode";
rpDesc = templateRenderPipelineDesc;
[encoder setVertexBytes:&_anchor length:sizeof(_anchor) atIndex:FrameUniformBuffer];
[encoder setFragmentTexture:_paintTexture atIndex: 0];
[encoder setFragmentBytes:&xorColor length:sizeof(xorColor) atIndex: 0];
}
}
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
vertexShaderId:vertShader
fragmentShaderId:fragShader
composite:mtlc.composite
renderOptions:renderOptions
stencilNeeded:[mtlc.clip isShape]];
[encoder setRenderPipelineState:pipelineState];
}
@end
/************************* GradientPaint support ****************************/
static void
MTLPaints_InitGradientTexture()
{
//TODO
J2dTraceLn(J2D_TRACE_INFO, "MTLPaints_InitGradientTexture -- :TODO");
}
/****************** Shared MultipleGradientPaint support ********************/
/**
* These constants are identical to those defined in the
* MultipleGradientPaint.CycleMethod enum; they are copied here for
* convenience (ideally we would pull them directly from the Java level,
* but that entails more hassle than it is worth).
*/
#define CYCLE_NONE 0
#define CYCLE_REFLECT 1
#define CYCLE_REPEAT 2
/**
* The following constants are flags that can be bitwise-or'ed together
* to control how the MultipleGradientPaint shader source code is generated:
*
* MULTI_CYCLE_METHOD
* Placeholder for the CycleMethod enum constant.
*
* MULTI_LARGE
* If set, use the (slower) shader that supports a larger number of
* gradient colors; otherwise, use the optimized codepath. See
* the MAX_FRACTIONS_SMALL/LARGE constants below for more details.
*
* MULTI_USE_MASK
* If set, apply the alpha mask value from texture unit 0 to the
* final color result (only used in the MaskFill case).
*
* MULTI_LINEAR_RGB
* If set, convert the linear RGB result back into the sRGB color space.
*/
#define MULTI_CYCLE_METHOD (3 << 0)
#define MULTI_LARGE (1 << 2)
#define MULTI_USE_MASK (1 << 3)
#define MULTI_LINEAR_RGB (1 << 4)
/**
* This value determines the size of the array of programs for each
* MultipleGradientPaint type. This value reflects the maximum value that
* can be represented by performing a bitwise-or of all the MULTI_*
* constants defined above.
*/
#define MAX_PROGRAMS 32
/** Evaluates to true if the given bit is set on the local flags variable. */
#define IS_SET(flagbit) \
(((flags) & (flagbit)) != 0)
/** Composes the given parameters as flags into the given flags variable.*/
#define COMPOSE_FLAGS(flags, cycleMethod, large, useMask, linear) \
do { \
flags |= ((cycleMethod) & MULTI_CYCLE_METHOD); \
if (large) flags |= MULTI_LARGE; \
if (useMask) flags |= MULTI_USE_MASK; \
if (linear) flags |= MULTI_LINEAR_RGB; \
} while (0)
/** Extracts the CycleMethod enum value from the given flags variable. */
#define EXTRACT_CYCLE_METHOD(flags) \
((flags) & MULTI_CYCLE_METHOD)
/**
* The maximum number of gradient "stops" supported by the fragment shader
* and related code. When the MULTI_LARGE flag is set, we will use
* MAX_FRACTIONS_LARGE; otherwise, we use MAX_FRACTIONS_SMALL. By having
* two separate values, we can have one highly optimized shader (SMALL) that
* supports only a few fractions/colors, and then another, less optimal
* shader that supports more stops.
*/
#define MAX_FRACTIONS sun_java2d_pipe_BufferedPaints_MULTI_MAX_FRACTIONS
#define MAX_FRACTIONS_LARGE MAX_FRACTIONS
#define MAX_FRACTIONS_SMALL 4
/**
* The maximum number of gradient colors supported by all of the gradient
* fragment shaders. Note that this value must be a power of two, as it
* determines the size of the 1D texture created below. It also must be
* greater than or equal to MAX_FRACTIONS (there is no strict requirement
* that the two values be equal).
*/
#define MAX_COLORS 16
/**
* The handle to the gradient color table texture object used by the shaders.
*/
static jint multiGradientTexID = 0;
/**
* This is essentially a template of the shader source code that can be used
* for either LinearGradientPaint or RadialGradientPaint. It includes the
* structure and some variables that are common to each; the remaining
* code snippets (for CycleMethod, ColorSpaceType, and mask modulation)
* are filled in prior to compiling the shader at runtime depending on the
* paint parameters. See MTLPaints_CreateMultiGradProgram() for more details.
*/
static const char *multiGradientShaderSource =
// gradient texture size (in texels)
"const int TEXTURE_SIZE = %d;"
// maximum number of fractions/colors supported by this shader
"const int MAX_FRACTIONS = %d;"
// size of a single texel
"const float FULL_TEXEL = (1.0 / float(TEXTURE_SIZE));"
// size of half of a single texel
"const float HALF_TEXEL = (FULL_TEXEL / 2.0);"
// texture containing the gradient colors
"uniform sampler1D colors;"
// array of gradient stops/fractions
"uniform float fractions[MAX_FRACTIONS];"
// array of scale factors (one for each interval)
"uniform float scaleFactors[MAX_FRACTIONS-1];"
// (placeholder for mask variable)
"%s"
// (placeholder for Linear/RadialGP-specific variables)
"%s"
""
"void main(void)"
"{"
" float dist;"
// (placeholder for Linear/RadialGradientPaint-specific code)
" %s"
""
" float tc;"
// (placeholder for CycleMethod-specific code)
" %s"
""
// calculate interpolated color
" vec4 result = texture1D(colors, tc);"
""
// (placeholder for ColorSpace conversion code)
" %s"
""
// (placeholder for mask modulation code)
" %s"
""
// modulate with gl_Color in order to apply extra alpha
" gl_FragColor = result * gl_Color;"
"}";
/**
* This code takes a "dist" value as input (as calculated earlier by the
* LGP/RGP-specific code) in the range [0,1] and produces a texture
* coordinate value "tc" that represents the position of the chosen color
* in the one-dimensional gradient texture (also in the range [0,1]).
*
* One naive way to implement this would be to iterate through the fractions
* to figure out in which interval "dist" falls, and then compute the
* relative distance between the two nearest stops. This approach would
* require an "if" check on every iteration, and it is best to avoid
* conditionals in fragment shaders for performance reasons. Also, one might
* be tempted to use a break statement to jump out of the loop once the
* interval was found, but break statements (and non-constant loop bounds)
* are not natively available on most graphics hardware today, so that is
* a non-starter.
*
* The more optimal approach used here avoids these issues entirely by using
* an accumulation function that is equivalent to the process described above.
* The scaleFactors array is pre-initialized at enable time as follows:
* scaleFactors[i] = 1.0 / (fractions[i+1] - fractions[i]);
*
* For each iteration, we subtract fractions[i] from dist and then multiply
* that value by scaleFactors[i]. If we are within the target interval,
* this value will be a fraction in the range [0,1] indicating the relative
* distance between fraction[i] and fraction[i+1]. If we are below the
* target interval, this value will be negative, so we clamp it to zero
* to avoid accumulating any value. If we are above the target interval,
* the value will be greater than one, so we clamp it to one. Upon exiting
* the loop, we will have accumulated zero or more 1.0's and a single
* fractional value. This accumulated value tells us the position of the
* fragment color in the one-dimensional gradient texture, i.e., the
* texcoord called "tc".
*/
static const char *texCoordCalcCode =
"int i;"
"float relFraction = 0.0;"
"for (i = 0; i < MAX_FRACTIONS-1; i++) {"
" relFraction +="
" clamp((dist - fractions[i]) * scaleFactors[i], 0.0, 1.0);"
"}"
// we offset by half a texel so that we find the linearly interpolated
// color between the two texel centers of interest
"tc = HALF_TEXEL + (FULL_TEXEL * relFraction);";
/** Code for NO_CYCLE that gets plugged into the CycleMethod placeholder. */
static const char *noCycleCode =
"if (dist <= 0.0) {"
" tc = 0.0;"
"} else if (dist >= 1.0) {"
" tc = 1.0;"
"} else {"
// (placeholder for texcoord calculation)
" %s"
"}";
/** Code for REFLECT that gets plugged into the CycleMethod placeholder. */
static const char *reflectCode =
"dist = 1.0 - (abs(fract(dist * 0.5) - 0.5) * 2.0);"
// (placeholder for texcoord calculation)
"%s";
/** Code for REPEAT that gets plugged into the CycleMethod placeholder. */
static const char *repeatCode =
"dist = fract(dist);"
// (placeholder for texcoord calculation)
"%s";
static void
MTLPaints_InitMultiGradientTexture()
{
J2dTraceLn(J2D_TRACE_INFO, "MTLPaints_InitMultiGradientTexture -- :TODO");
}
/**
* Compiles and links the MultipleGradientPaint shader program. If
* successful, this function returns a handle to the newly created
* shader program; otherwise returns 0.
*/
static void*
MTLPaints_CreateMultiGradProgram(jint flags,
char *paintVars, char *distCode)
{
//TODO
J2dTraceLn(J2D_TRACE_INFO, "MTLPaints_CreateMultiGradProgram -- :TODO");
return NULL;
}
/**
* Called from the MTLPaints_SetLinear/RadialGradientPaint() methods
* in order to setup the fraction/color values that are common to both.
*/
static void
MTLPaints_SetMultiGradientPaint(void* multiGradProgram,
jint numStops,
void *pFractions, void *pPixels)
{
//TODO
J2dTraceLn(J2D_TRACE_INFO, "MTLPaints_SetMultiGradientPaint -- :TODO");
}
/********************** LinearGradientPaint support *************************/
/**
* The handles to the LinearGradientPaint fragment program objects. The
* index to the array should be a bitwise-or'ing of the MULTI_* flags defined
* above. Note that most applications will likely need to initialize one
* or two of these elements, so the array is usually sparsely populated.
*/
static void* linearGradPrograms[MAX_PROGRAMS];
/**
* Compiles and links the LinearGradientPaint shader program. If successful,
* this function returns a handle to the newly created shader program;
* otherwise returns 0.
*/
static void*
MTLPaints_CreateLinearGradProgram(jint flags)
{
char *paintVars;
char *distCode;
J2dTraceLn1(J2D_TRACE_INFO,
"MTLPaints_CreateLinearGradProgram",
flags);
/*
* To simplify the code and to make it easier to upload a number of
* uniform values at once, we pack a bunch of scalar (float) values
* into vec3 values below. Here's how the values are related:
*
* params.x = p0
* params.y = p1
* params.z = p3
*
* yoff = dstOps->yOffset + dstOps->height
*/
paintVars =
"uniform vec3 params;"
"uniform float yoff;";
distCode =
// note that gl_FragCoord is in window space relative to the
// lower-left corner, so we have to flip the y-coordinate here
"vec3 fragCoord = vec3(gl_FragCoord.x, yoff-gl_FragCoord.y, 1.0);"
"dist = dot(params, fragCoord);";
return MTLPaints_CreateMultiGradProgram(flags, paintVars, distCode);
}
/********************** RadialGradientPaint support *************************/
/**
* The handles to the RadialGradientPaint fragment program objects. The
* index to the array should be a bitwise-or'ing of the MULTI_* flags defined
* above. Note that most applications will likely need to initialize one
* or two of these elements, so the array is usually sparsely populated.
*/
static void* radialGradPrograms[MAX_PROGRAMS];
/**
* Compiles and links the RadialGradientPaint shader program. If successful,
* this function returns a handle to the newly created shader program;
* otherwise returns 0.
*/
static void*
MTLPaints_CreateRadialGradProgram(jint flags)
{
char *paintVars;
char *distCode;
J2dTraceLn1(J2D_TRACE_INFO,
"MTLPaints_CreateRadialGradProgram",
flags);
/*
* To simplify the code and to make it easier to upload a number of
* uniform values at once, we pack a bunch of scalar (float) values
* into vec3 and vec4 values below. Here's how the values are related:
*
* m0.x = m00
* m0.y = m01
* m0.z = m02
*
* m1.x = m10
* m1.y = m11
* m1.z = m12
*
* precalc.x = focusX
* precalc.y = yoff = dstOps->yOffset + dstOps->height
* precalc.z = 1.0 - (focusX * focusX)
* precalc.w = 1.0 / precalc.z
*/
paintVars =
"uniform vec3 m0;"
"uniform vec3 m1;"
"uniform vec4 precalc;";
/*
* The following code is derived from Daniel Rice's whitepaper on
* radial gradient performance (attached to the bug report for 6521533).
* Refer to that document as well as the setup code in the Java-level
* BufferedPaints.setRadialGradientPaint() method for more details.
*/
distCode =
// note that gl_FragCoord is in window space relative to the
// lower-left corner, so we have to flip the y-coordinate here
"vec3 fragCoord ="
" vec3(gl_FragCoord.x, precalc.y - gl_FragCoord.y, 1.0);"
"float x = dot(fragCoord, m0);"
"float y = dot(fragCoord, m1);"
"float xfx = x - precalc.x;"
"dist = (precalc.x*xfx + sqrt(xfx*xfx + y*y*precalc.z))*precalc.w;";
return MTLPaints_CreateMultiGradProgram(flags, paintVars, distCode);
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,44 @@
#ifndef MTLPipelineStatesStorage_h_Included
#define MTLPipelineStatesStorage_h_Included
#import "MTLUtils.h"
#include "RenderOptions.h"
@class MTLComposite;
/**
* The MTLPipelineStatesStorage class used to obtain MTLRenderPipelineState
* */
@interface MTLPipelineStatesStorage : NSObject {
@private
id<MTLDevice> device;
id<MTLLibrary> library;
NSMutableDictionary<NSString*, id<MTLFunction>> * shaders;
}
@property (readwrite, assign) id<MTLDevice> device;
@property (readwrite, retain) id<MTLLibrary> library;
@property (readwrite, retain) NSMutableDictionary<NSString*, id<MTLFunction>> * shaders;
@property (readwrite, retain) NSMutableDictionary<NSString*, NSMutableDictionary *> * states;
- (id) initWithDevice:(id<MTLDevice>)device shaderLibPath:(NSString *)shadersLib;
- (id<MTLRenderPipelineState>) getPipelineState:(MTLRenderPipelineDescriptor *) pipelineDescriptor
vertexShaderId:(NSString *)vertexShaderId
fragmentShaderId:(NSString *)fragmentShaderId;
- (id<MTLRenderPipelineState>) getPipelineState:(MTLRenderPipelineDescriptor *) pipelineDescriptor
vertexShaderId:(NSString *)vertexShaderId
fragmentShaderId:(NSString *)fragmentShaderId
composite:(MTLComposite*)composite
renderOptions:(const RenderOptions *)renderOptions
stencilNeeded:(bool)stencilNeeded;
- (id<MTLFunction>) getShader:(NSString *)name;
@end
#endif // MTLPipelineStatesStorage_h_Included

View File

@@ -0,0 +1,353 @@
#import "MTLPipelineStatesStorage.h"
#include "GraphicsPrimitiveMgr.h"
#import "MTLComposite.h"
#include "sun_java2d_SunGraphics2D.h"
extern const SurfaceRasterFlags defaultRasterFlags;
static void setBlendingFactors(
MTLRenderPipelineColorAttachmentDescriptor * cad,
MTLComposite* composite,
const RenderOptions * renderOptions);
@implementation MTLPipelineStatesStorage
@synthesize device;
@synthesize library;
@synthesize shaders;
@synthesize states;
- (id) initWithDevice:(id<MTLDevice>)dev shaderLibPath:(NSString *)shadersLib {
self = [super init];
if (self == nil) return self;
self.device = dev;
NSError *error = nil;
self.library = [dev newLibraryWithFile:shadersLib error:&error];
if (!self.library) {
NSLog(@"Failed to load library. error %@", error);
exit(0);
}
self.shaders = [NSMutableDictionary dictionaryWithCapacity:10];
self.states = [NSMutableDictionary dictionaryWithCapacity:10];
return self;
}
- (NSPointerArray * ) getSubStates:(NSString *)vertexShaderId fragmentShader:(NSString *)fragmentShaderId {
NSMutableDictionary * vSubStates = states[vertexShaderId];
if (vSubStates == nil) {
@autoreleasepool {
vSubStates = [NSMutableDictionary dictionary];
[states setObject:vSubStates forKey:vertexShaderId];
}
}
NSPointerArray * sSubStates = vSubStates[fragmentShaderId];
if (sSubStates == nil) {
@autoreleasepool {
sSubStates = [NSPointerArray strongObjectsPointerArray];
[vSubStates setObject:sSubStates forKey:fragmentShaderId];
}
}
return sSubStates;
}
- (id<MTLRenderPipelineState>) getPipelineState:(MTLRenderPipelineDescriptor *) pipelineDescriptor
vertexShaderId:(NSString *)vertexShaderId
fragmentShaderId:(NSString *)fragmentShaderId
{
RenderOptions defaultOptions = {JNI_FALSE, JNI_FALSE, 0/*unused*/, {JNI_FALSE, JNI_TRUE}, {JNI_FALSE, JNI_TRUE}};
return [self getPipelineState:pipelineDescriptor
vertexShaderId:vertexShaderId
fragmentShaderId:fragmentShaderId
composite:nil
renderOptions:&defaultOptions
stencilNeeded:NO];
}
// Base method to obtain MTLRenderPipelineState.
// NOTE: parameters compositeRule, srcFlags, dstFlags are used to set MTLRenderPipelineColorAttachmentDescriptor multipliers
- (id<MTLRenderPipelineState>) getPipelineState:(MTLRenderPipelineDescriptor *) pipelineDescriptor
vertexShaderId:(NSString *)vertexShaderId
fragmentShaderId:(NSString *)fragmentShaderId
composite:(MTLComposite*) composite
renderOptions:(const RenderOptions *)renderOptions
stencilNeeded:(bool)stencilNeeded;
{
jint compositeRule = composite != nil ? [composite getRule] : RULE_Src;
const jboolean useXorComposite = composite != nil && [composite getCompositeState] == sun_java2d_SunGraphics2D_COMP_XOR;
const jboolean useComposite = composite != nil && compositeRule >= 0
&& compositeRule < java_awt_AlphaComposite_MAX_RULE;
// Calculate index by flags and compositeRule
// TODO: reimplement, use map with convenient key (calculated by all arguments)
int subIndex = 0;
if (useXorComposite) {
// compositeRule value is already XOR_COMPOSITE_RULE
}
else {
if (useComposite) {
if (!renderOptions->srcFlags.isPremultiplied)
subIndex |= 1;
if (renderOptions->srcFlags.isOpaque)
subIndex |= 1 << 1;
if (!renderOptions->dstFlags.isPremultiplied)
subIndex |= 1 << 2;
if (renderOptions->dstFlags.isOpaque)
subIndex |= 1 << 3;
} else
compositeRule = RULE_Src;
}
if (stencilNeeded) {
subIndex |= 1 << 4;
}
if (renderOptions->isAA) {
subIndex |= 1 << 5;
}
if ((composite != nil && FLT_LT([composite getExtraAlpha], 1.0f))) {
subIndex |= 1 << 6;
}
int index = compositeRule*64 + subIndex;
NSPointerArray * subStates = [self getSubStates:vertexShaderId fragmentShader:fragmentShaderId];
while (index >= [subStates count]) {
[subStates addPointer:NULL]; // obj-c collections haven't resize methods, so do that
}
id<MTLRenderPipelineState> result = [subStates pointerAtIndex:index];
if (result == nil) {
@autoreleasepool {
id <MTLFunction> vertexShader = [self getShader:vertexShaderId];
id <MTLFunction> fragmentShader = [self getShader:fragmentShaderId];
MTLRenderPipelineDescriptor *pipelineDesc = [[pipelineDescriptor copy] autorelease];
pipelineDesc.vertexFunction = vertexShader;
pipelineDesc.fragmentFunction = fragmentShader;
if (useXorComposite) {
pipelineDesc.colorAttachments[0].blendingEnabled = YES;
pipelineDesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
pipelineDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOneMinusDestinationColor;
pipelineDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceColor;
} else if (useComposite ||
(composite != nil &&
FLT_LT([composite getExtraAlpha], 1.0f)))
{
setBlendingFactors(
pipelineDesc.colorAttachments[0],
composite,
renderOptions
);
}
if (stencilNeeded) {
pipelineDesc.stencilAttachmentPixelFormat = MTLPixelFormatStencil8;
}
if (renderOptions->isAA) {
pipelineDesc.sampleCount = MTLAASampleCount;
pipelineDesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
pipelineDesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
pipelineDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
pipelineDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
pipelineDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
pipelineDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
pipelineDesc.colorAttachments[0].blendingEnabled = YES;
}
NSError *error = nil;
result = [[self.device newRenderPipelineStateWithDescriptor:pipelineDesc error:&error] autorelease];
if (result == nil) {
NSLog(@"Failed to create pipeline state, error %@", error);
exit(0);
}
[subStates insertPointer:result atIndex:index];
}
}
return result;
}
- (id<MTLFunction>) getShader:(NSString *)name {
id<MTLFunction> result = [self.shaders valueForKey:name];
if (result == nil) {
result = [[self.library newFunctionWithName:name] autorelease];
[self.shaders setValue:result forKey:name];
}
return result;
}
@end
static void setBlendingFactors(
MTLRenderPipelineColorAttachmentDescriptor * cad,
MTLComposite* composite,
const RenderOptions * renderOptions
) {
const jint compositeRule = composite != nil ? [composite getRule] : RULE_Src;
if (compositeRule == RULE_Src &&
(composite == nil || FLT_GE([composite getExtraAlpha], 1.0f))) {
J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_Src but blending is disabled because src is opaque");
return;
}
cad.blendingEnabled = YES;
// RGB = Source.rgb * SBFc + Dest.rgb * DBFc
// A = Source.a * SBFa + Dest.a * DBFa
//
// default mode == RULE_Src with constants:
// DBFa=0
// DBFc=0
// SBFa=1
// SBFc=1
//
// NOTE: constants MTLBlendFactorBlendAlpha, MTLBlendFactorOneMinusBlendAlpha refers to [encoder setBlendColorRed:green:blue:alpha:] (default value is zero)
//
// TODO: implement alpha-composite via shaders (will be much more simpler and can support all rules and modes)
switch (compositeRule) {
case RULE_SrcOver: {
// Ar = As + Ad*(1-As)
// Cr = Cs + Cd*(1-As)
if (renderOptions->srcFlags.isOpaque &&
(composite == nil ||
FLT_GE([composite getExtraAlpha], 1.0f)))
{
J2dTraceLn(J2D_TRACE_VERBOSE, "rule=RULE_SrcOver, but blending is disabled because src is opaque");
cad.blendingEnabled = NO;
return;
}
if (renderOptions->dstFlags.isOpaque) {
// Ar = 1, can be ignored, so
// Cr = Cs + Cd*(1-As)
// TODO: select any multiplier with best performance
// for example: cad.destinationAlphaBlendFactor = MTLBlendFactorZero;
} else {
cad.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
}
if (!renderOptions->srcFlags.isPremultiplied) {
cad.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
}
if (composite != nil && FLT_LT([composite getExtraAlpha], 1.0f)) {
cad.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
}
cad.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_SrcOver");
break;
}
case RULE_DstOver: {
// Ar = As*(1-Ad) + Ad
// Cr = Cs*(1-Ad) + Cd
if (renderOptions->srcFlags.isOpaque) {
J2dTraceLn(J2D_TRACE_ERROR, "Composite rule RULE_DstOver with opaque src isn't implemented (src alpha won't be ignored)");
}
if (renderOptions->dstFlags.isOpaque) {
J2dTraceLn(J2D_TRACE_ERROR, "Composite rule RULE_DstOver with opaque dest hasn't any sense");
}
if (!renderOptions->srcFlags.isPremultiplied) {
J2dTrace(J2D_TRACE_ERROR, "Composite rule RULE_DstOver with non-premultiplied source isn't implemented (scr alpha will be ignored for rgb-component)");
}
cad.sourceAlphaBlendFactor = MTLBlendFactorOneMinusDestinationAlpha;
cad.sourceRGBBlendFactor = MTLBlendFactorOneMinusDestinationAlpha;
cad.destinationAlphaBlendFactor = MTLBlendFactorOne;
cad.destinationRGBBlendFactor = MTLBlendFactorOne;
J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_DstOver");
break;
}
case RULE_SrcIn: {
// Ar = As*Ad
// Cr = Cs*Ad
if (renderOptions->srcFlags.isOpaque) {
J2dTraceLn(J2D_TRACE_ERROR, "Composite rule RULE_SrcIn with opaque src isn't implemented (src alpha won't be ignored)");
}
if (renderOptions->dstFlags.isOpaque) {
J2dTraceLn(J2D_TRACE_VERBOSE, "rule=RULE_SrcIn, but blending is disabled because dest is opaque");
cad.blendingEnabled = NO;
return;
}
if (!renderOptions->srcFlags.isPremultiplied) {
J2dTrace(J2D_TRACE_ERROR, "Composite rule RULE_SrcIn with non-premultiplied source isn't implemented (scr alpha will be ignored for rgb-component)");
}
cad.sourceAlphaBlendFactor = MTLBlendFactorDestinationAlpha;
cad.sourceRGBBlendFactor = MTLBlendFactorDestinationAlpha;
cad.destinationAlphaBlendFactor = MTLBlendFactorZero;
cad.destinationRGBBlendFactor = MTLBlendFactorZero;
J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_SrcIn");
break;
}
case RULE_DstIn: {
// Ar = Ad*As
// Cr = Cd*As
if (renderOptions->srcFlags.isOpaque) {
J2dTraceLn(J2D_TRACE_ERROR, "Composite rule RULE_DstIn with opaque src isn't implemented (src alpha won't be ignored)");
}
if (renderOptions->dstFlags.isOpaque) {
J2dTraceLn(J2D_TRACE_ERROR, "Composite rule RULE_DstIn with opaque dest isn't implemented (dest alpha won't be ignored)");
}
cad.sourceAlphaBlendFactor = MTLBlendFactorZero;
cad.sourceRGBBlendFactor = MTLBlendFactorZero;
cad.destinationAlphaBlendFactor = MTLBlendFactorSourceAlpha;
cad.destinationRGBBlendFactor = MTLBlendFactorSourceAlpha;
J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_DstIn");
break;
}
case RULE_SrcOut: {
// Ar = As*(1-Ad)
// Cr = Cs*(1-Ad)
if (!renderOptions->srcFlags.isPremultiplied) {
J2dTrace(J2D_TRACE_ERROR, "Composite rule SrcOut with non-premultiplied source isn't implemented (scr alpha will be ignored for rgb-component)");
}
cad.sourceAlphaBlendFactor = MTLBlendFactorOneMinusDestinationAlpha;
cad.sourceRGBBlendFactor = MTLBlendFactorOneMinusDestinationAlpha;
cad.destinationAlphaBlendFactor = MTLBlendFactorZero;
cad.destinationRGBBlendFactor = MTLBlendFactorZero;
J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_SrcOut");
break;
}
case RULE_DstOut: {
// Ar = Ad*(1-As)
// Cr = Cd*(1-As)
cad.sourceAlphaBlendFactor = MTLBlendFactorZero;
cad.sourceRGBBlendFactor = MTLBlendFactorZero;
cad.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
cad.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_DstOut");
break;
}
case RULE_Xor: {
// Ar = As*(1-Ad) + Ad*(1-As)
// Cr = Cs*(1-Ad) + Cd*(1-As)
if (!renderOptions->srcFlags.isPremultiplied) {
J2dTrace(J2D_TRACE_ERROR, "Composite rule Xor with non-premultiplied source isn't implemented (scr alpha will be ignored for rgb-component)");
}
cad.sourceAlphaBlendFactor = MTLBlendFactorOneMinusDestinationAlpha;
cad.sourceRGBBlendFactor = MTLBlendFactorOneMinusDestinationAlpha;
cad.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
cad.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_Xor");
break;
}
case RULE_Clear: {
// Ar = 0
// Cr = 0
cad.sourceAlphaBlendFactor = MTLBlendFactorZero;
cad.sourceRGBBlendFactor = MTLBlendFactorZero;
cad.destinationAlphaBlendFactor = MTLBlendFactorZero;
cad.destinationRGBBlendFactor = MTLBlendFactorZero;
J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_Clear");
break;
}
default: {
J2dTrace1(J2D_TRACE_ERROR, "Unimplemented composite rule %d (will be used Src)", compositeRule);
cad.blendingEnabled = NO;
}
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLRenderQueue_h_Included
#define MTLRenderQueue_h_Included
#include "MTLContext.h"
#include "MTLSurfaceData.h"
#include "MTLVertexCache.h"
/*
* The following macros are used to pick values (of the specified type) off
* the queue.
*/
#define NEXT_VAL(buf, type) (((type *)((buf) += sizeof(type)))[-1])
#define NEXT_BYTE(buf) NEXT_VAL(buf, unsigned char)
#define NEXT_INT(buf) NEXT_VAL(buf, jint)
#define NEXT_FLOAT(buf) NEXT_VAL(buf, jfloat)
#define NEXT_BOOLEAN(buf) (jboolean)NEXT_INT(buf)
#define NEXT_LONG(buf) NEXT_VAL(buf, jlong)
#define NEXT_DOUBLE(buf) NEXT_VAL(buf, jdouble)
// Operations for CheckPreviousOp
enum {
MTL_OP_INIT,
MTL_OP_AA,
MTL_OP_SET_COLOR,
MTL_OP_RESET_PAINT,
MTL_OP_SYNC,
MTL_OP_OTHER
};
/*
* These macros now simply delegate to the CheckPreviousOp() method.
*/
#define CHECK_PREVIOUS_OP(op) MTLRenderQueue_CheckPreviousOp(op)
#define RESET_PREVIOUS_OP() {mtlPreviousOp = MTL_OP_INIT;}
/*
* Increments a pointer (buf) by the given number of bytes.
*/
#define SKIP_BYTES(buf, numbytes) buf += (numbytes)
/*
* Extracts a value at the given offset from the provided packed value.
*/
#define EXTRACT_VAL(packedval, offset, mask) \
(((packedval) >> (offset)) & (mask))
#define EXTRACT_BYTE(packedval, offset) \
(unsigned char)EXTRACT_VAL(packedval, offset, 0xff)
#define EXTRACT_BOOLEAN(packedval, offset) \
(jboolean)EXTRACT_VAL(packedval, offset, 0x1)
/*
* The following macros allow the caller to return (or continue) if the
* provided value is NULL. (The strange else clause is included below to
* allow for a trailing ';' after RETURN/CONTINUE_IF_NULL() invocations.)
*/
#define ACT_IF_NULL(ACTION, value) \
if ((value) == NULL) { \
J2dTraceLn1(J2D_TRACE_ERROR, \
"%s is null", #value); \
ACTION; \
} else do { } while (0)
#define RETURN_IF_NULL(value) ACT_IF_NULL(return, value)
#define CONTINUE_IF_NULL(value) ACT_IF_NULL(continue, value)
MTLContext *MTLRenderQueue_GetCurrentContext();
BMTLSDOps *MTLRenderQueue_GetCurrentDestination();
extern jint mtlPreviousOp;
#endif /* MTLRenderQueue_h_Included */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLRenderer_h_Included
#define MTLRenderer_h_Included
#include "sun_java2d_pipe_BufferedRenderPipe.h"
#include "MTLContext.h"
#include "MTLGraphicsConfig.h"
#import "MTLLayer.h"
#define BYTES_PER_POLY_POINT \
sun_java2d_pipe_BufferedRenderPipe_BYTES_PER_POLY_POINT
#define BYTES_PER_SCANLINE \
sun_java2d_pipe_BufferedRenderPipe_BYTES_PER_SCANLINE
#define BYTES_PER_SPAN \
sun_java2d_pipe_BufferedRenderPipe_BYTES_PER_SPAN
void MTLRenderer_DrawLine(MTLContext *mtlc, BMTLSDOps * dstOps,
jint x1, jint y1, jint x2, jint y2);
void MTLRenderer_DrawRect(MTLContext *mtlc, BMTLSDOps * dstOps,
jint x, jint y, jint w, jint h);
void MTLRenderer_DrawPoly(MTLContext *mtlc, BMTLSDOps * dstOps,
jint nPoints, jint isClosed,
jint transX, jint transY,
jint *xPoints, jint *yPoints);
void MTLRenderer_DrawScanlines(MTLContext *mtlc, BMTLSDOps * dstOps,
jint count, jint *scanlines);
void MTLRenderer_DrawParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12,
jfloat lw21, jfloat lw12);
void MTLRenderer_DrawAAParallelogram(MTLContext *mtlc, BMTLSDOps *dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12,
jfloat lw21, jfloat lw12);
void MTLRenderer_FillRect(MTLContext *mtlc, BMTLSDOps * dstOps,
jint x, jint y, jint w, jint h);
void MTLRenderer_FillSpans(MTLContext *mtlc, BMTLSDOps * dstOps,
jint count, jint *spans);
void MTLRenderer_FillParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12);
void MTLRenderer_FillAAParallelogram(MTLContext *mtlc, BMTLSDOps *dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12);
void MTLRenderer_EnableAAParallelogramProgram();
void MTLRenderer_DisableAAParallelogramProgram();
#endif /* MTLRenderer_h_Included */

View File

@@ -0,0 +1,737 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <jlong.h>
#include <jni_util.h>
#include <math.h>
#include "sun_java2d_metal_MTLRenderer.h"
#include "MTLRenderer.h"
#include "MTLRenderQueue.h"
#include "MTLSurfaceData.h"
#include "MTLUtils.h"
#import "MTLLayer.h"
/**
* Note: Some of the methods in this file apply a "magic number"
* translation to line segments. The OpenGL specification lays out the
* "diamond exit rule" for line rasterization, but it is loose enough to
* allow for a wide range of line rendering hardware. (It appears that
* some hardware, such as the Nvidia GeForce2 series, does not even meet
* the spec in all cases.) As such it is difficult to find a mapping
* between the Java2D and OpenGL line specs that works consistently across
* all hardware combinations.
*
* Therefore the "magic numbers" you see here have been empirically derived
* after testing on a variety of graphics hardware in order to find some
* reasonable middle ground between the two specifications. The general
* approach is to apply a fractional translation to vertices so that they
* hit pixel centers and therefore touch the same pixels as in our other
* pipelines. Emphasis was placed on finding values so that MTL lines with
* a slope of +/- 1 hit all the same pixels as our other (software) loops.
* The stepping in other diagonal lines rendered with MTL may deviate
* slightly from those rendered with our software loops, but the most
* important thing is that these magic numbers ensure that all MTL lines
* hit the same endpoints as our software loops.
*
* If you find it necessary to change any of these magic numbers in the
* future, just be sure that you test the changes across a variety of
* hardware to ensure consistent rendering everywhere.
*/
void MTLRenderer_DrawLine(MTLContext *mtlc, BMTLSDOps * dstOps, jint x1, jint y1, jint x2, jint y2) {
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawLine: dest is null");
return;
}
J2dTraceLn5(J2D_TRACE_INFO, "MTLRenderer_DrawLine (x1=%1.2f y1=%1.2f x2=%1.2f y2=%1.2f), dst tex=%p", x1, y1, x2, y2, dstOps->pTexture);
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
if (mtlEncoder == nil)
return;
struct Vertex verts[2] = {
{{x1, y1}},
{{x2, y2}}
};
[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:2];
}
void MTLRenderer_DrawRect(MTLContext *mtlc, BMTLSDOps * dstOps, jint x, jint y, jint w, jint h) {
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawRect: dest is null");
return;
}
id<MTLTexture> dest = dstOps->pTexture;
J2dTraceLn5(J2D_TRACE_INFO, "MTLRenderer_DrawRect (x=%d y=%d w=%d h=%d), dst tex=%p", x, y, w, h, dest);
// TODO: use DrawParallelogram(x, y, w, h, lw=1, lh=1)
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
if (mtlEncoder == nil)
return;
const int verticesCount = 5;
struct Vertex vertices[5] = {
{{x, y}},
{{x + w, y}},
{{x + w, y + h}},
{{x, y + h}},
{{x, y}},
};
[mtlEncoder setVertexBytes:vertices length:sizeof(vertices) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLineStrip vertexStart:0 vertexCount:verticesCount];
}
const int POLYLINE_BUF_SIZE = 64;
NS_INLINE void fillVertex(struct Vertex * vertex, int x, int y) {
vertex->position[0] = x;
vertex->position[1] = y;
}
void MTLRenderer_DrawPoly(MTLContext *mtlc, BMTLSDOps * dstOps,
jint nPoints, jint isClosed,
jint transX, jint transY,
jint *xPoints, jint *yPoints)
{
// Note that BufferedRenderPipe.drawPoly() has already rejected polys
// with nPoints<2, so we can be certain here that we have nPoints>=2.
if (xPoints == NULL || yPoints == NULL || nPoints < 2) { // just for insurance
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawPoly: points array is empty");
return;
}
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawPoly: dest is null");
return;
}
J2dTraceLn4(J2D_TRACE_INFO, "MTLRenderer_DrawPoly: %d points, transX=%d, transY=%d, dst tex=%p", nPoints, transX, transY, dstOps->pTexture);
__block struct {
struct Vertex verts[POLYLINE_BUF_SIZE];
} pointsChunk;
// We intend to submit draw commands in batches of POLYLINE_BUF_SIZE vertices at a time
// Subsequent batches need to be connected - so end point in one batch is repeated as first point in subsequent batch
// This inflates the total number of points by a factor of number of batches of size POLYLINE_BUF_SIZE
nPoints += (nPoints/POLYLINE_BUF_SIZE);
jint prevX = *(xPoints++);
jint prevY = *(yPoints++);
const jint firstX = prevX;
const jint firstY = prevY;
while (nPoints > 0) {
const bool isLastChunk = nPoints <= POLYLINE_BUF_SIZE;
__block int chunkSize = isLastChunk ? nPoints : POLYLINE_BUF_SIZE;
fillVertex(pointsChunk.verts, prevX + transX, prevY + transY);
for (int i = 1; i < chunkSize; i++) {
prevX = *(xPoints++);
prevY = *(yPoints++);
fillVertex(pointsChunk.verts + i, prevX + transX, prevY + transY);
}
bool drawCloseSegment = false;
if (isClosed && isLastChunk) {
if (chunkSize + 2 <= POLYLINE_BUF_SIZE) {
fillVertex(pointsChunk.verts + chunkSize, firstX + transX, firstY + transY);
++chunkSize;
} else
drawCloseSegment = true;
}
nPoints -= chunkSize;
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
if (mtlEncoder == nil)
return;
[mtlEncoder setVertexBytes:pointsChunk.verts length:sizeof(pointsChunk.verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLineStrip vertexStart:0 vertexCount:chunkSize];
if (drawCloseSegment) {
struct Vertex vertices[2] = {
{{prevX + transX, prevY + transY}},
{{firstX + transX, firstY + transY}},
};
[mtlEncoder setVertexBytes:vertices length:sizeof(vertices) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:2];
}
}
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLRenderer_drawPoly
(JNIEnv *env, jobject mtlr,
jintArray xpointsArray, jintArray ypointsArray,
jint nPoints, jboolean isClosed,
jint transX, jint transY)
{
jint *xPoints, *yPoints;
//TODO
J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_drawPoly -- :TODO");
}
void
MTLRenderer_DrawScanlines(MTLContext *mtlc, BMTLSDOps * dstOps,
jint scanlineCount, jint *scanlines)
{
J2dTraceLn2(J2D_TRACE_INFO, "MTLRenderer_DrawScanlines (scanlineCount=%d), dst tex=%p", scanlineCount, dstOps->pTexture);
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawScanlines: dest is null");
return;
}
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
if (mtlEncoder == nil) return;
struct Vertex verts[2*scanlineCount];
for (int j = 0, i = 0; j < scanlineCount; j++) {
// Translate each vertex by a fraction so
// that we hit pixel centers.
float x1 = ((float)*(scanlines++)) + 0.2f;
float x2 = ((float)*(scanlines++)) + 1.2f;
float y = ((float)*(scanlines++)) + 0.5f;
struct Vertex v1 = {{x1, y}};
struct Vertex v2 = {{x2, y}};
verts[i++] = v1;
verts[i++] = v2;
}
[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:2*scanlineCount];
}
void
MTLRenderer_FillRect(MTLContext *mtlc, BMTLSDOps * dstOps, jint x, jint y, jint w, jint h)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_FillRect");
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillRect: current dest is null");
return;
}
struct Vertex verts[QUAD_VERTEX_COUNT] = {
{ {x, y}},
{ {x, y+h}},
{ {x+w, y}},
{ {x+w, y+h}
}};
id<MTLTexture> dest = dstOps->pTexture;
J2dTraceLn5(J2D_TRACE_INFO, "MTLRenderer_FillRect (x=%d y=%d w=%d h=%d), dst tex=%p", x, y, w, h, dest);
// Encode render command.
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
if (mtlEncoder == nil)
return;
[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount: QUAD_VERTEX_COUNT];
}
void MTLRenderer_FillSpans(MTLContext *mtlc, BMTLSDOps * dstOps, jint spanCount, jint *spans)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_FillSpans");
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillSpans: dest is null");
return;
}
// MTLRenderCommandEncoder setVertexBytes usage is recommended if the data is of 4KB.
// We use a buffer that closely matches the 4KB limit size
// This buffer is resued multiple times to encode draw calls of a triangle list
// NOTE : Due to nature of *spans data - it is not possible to use triangle strip.
// We use triangle list to draw spans
// Destination texture to which render commands are encoded
id<MTLTexture> dest = dstOps->pTexture;
id<MTLTexture> destAA = nil;
BOOL isDestOpaque = dstOps->isOpaque;
if (mtlc.clip.stencilMaskGenerationInProgress == JNI_TRUE) {
dest = dstOps->pStencilData;
isDestOpaque = NO;
}
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dest isDstOpaque:isDestOpaque];
if (mtlEncoder == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillSpans: mtlEncoder is nil");
return;
}
// This is the max no of vertices (of struct Vertex - 8 bytes) we can accomodate in 4KB
const int TOTAL_VERTICES_IN_BLOCK = 510;
struct Vertex vertexList[TOTAL_VERTICES_IN_BLOCK]; // a total of 170 triangles ==> 85 spans
int counter = 0;
jint *aaspans = spans;
for (int i = 0; i < spanCount; i++) {
jfloat x1 = *(spans++);
jfloat y1 = *(spans++);
jfloat x2 = *(spans++);
jfloat y2 = *(spans++);
struct Vertex verts[6] = {
{{x1, y1}},
{{x1, y2}},
{{x2, y1}},
{{x1, y2}},
{{x2, y1}},
{{x2, y2}
}};
memcpy(&vertexList[counter], &verts, sizeof(verts));
counter += 6;
// If vertexList buffer full
if (counter % TOTAL_VERTICES_IN_BLOCK == 0) {
[mtlEncoder setVertexBytes:vertexList length:sizeof(vertexList) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:TOTAL_VERTICES_IN_BLOCK];
counter = 0;
}
}
// Draw triangles using remaining vertices if any
if (counter != 0) {
[mtlEncoder setVertexBytes:vertexList length:sizeof(vertexList) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:counter];
}
}
void
MTLRenderer_FillParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12)
{
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillParallelogram: current dest is null");
return;
}
id<MTLTexture> dest = dstOps->pTexture;
J2dTraceLn7(J2D_TRACE_INFO,
"MTLRenderer_FillParallelogram "
"(x=%6.2f y=%6.2f "
"dx1=%6.2f dy1=%6.2f "
"dx2=%6.2f dy2=%6.2f dst tex=%p)",
fx11, fy11,
dx21, dy21,
dx12, dy12, dest);
struct Vertex verts[QUAD_VERTEX_COUNT] = {
{ {fx11, fy11}},
{ {fx11+dx21, fy11+dy21}},
{ {fx11+dx12, fy11+dy12}},
{ {fx11 + dx21 + dx12, fy11+ dy21 + dy12}
}};
// Encode render command.
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
if (mtlEncoder == nil)
return;
[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount: QUAD_VERTEX_COUNT];
}
void
MTLRenderer_DrawParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12,
jfloat lwr21, jfloat lwr12)
{
// dx,dy for line width in the "21" and "12" directions.
jfloat ldx21 = dx21 * lwr21;
jfloat ldy21 = dy21 * lwr21;
jfloat ldx12 = dx12 * lwr12;
jfloat ldy12 = dy12 * lwr12;
// calculate origin of the outer parallelogram
jfloat ox11 = fx11 - (ldx21 + ldx12) / 2.0f;
jfloat oy11 = fy11 - (ldy21 + ldy12) / 2.0f;
J2dTraceLn8(J2D_TRACE_INFO,
"MTLRenderer_DrawParallelogram "
"(x=%6.2f y=%6.2f "
"dx1=%6.2f dy1=%6.2f lwr1=%6.2f "
"dx2=%6.2f dy2=%6.2f lwr2=%6.2f)",
fx11, fy11,
dx21, dy21, lwr21,
dx12, dy12, lwr12);
// Only need to generate 4 quads if the interior still
// has a hole in it (i.e. if the line width ratio was
// less than 1.0)
if (lwr21 < 1.0f && lwr12 < 1.0f) {
// Note: "TOP", "BOTTOM", "LEFT" and "RIGHT" here are
// relative to whether the dxNN variables are positive
// and negative. The math works fine regardless of
// their signs, but for conceptual simplicity the
// comments will refer to the sides as if the dxNN
// were all positive. "TOP" and "BOTTOM" segments
// are defined by the dxy21 deltas. "LEFT" and "RIGHT"
// segments are defined by the dxy12 deltas.
// Each segment includes its starting corner and comes
// to just short of the following corner. Thus, each
// corner is included just once and the only lengths
// needed are the original parallelogram delta lengths
// and the "line width deltas". The sides will cover
// the following relative territories:
//
// T T T T T R
// L R
// L R
// L R
// L R
// L B B B B B
// Every segment is drawn as a filled Parallelogram quad
// Each quad is encoded using two triangles
// For 4 segments - there are 8 triangles in total
// Each triangle has 3 vertices
const int TOTAL_VERTICES = 8 * 3;
struct Vertex vertexList[TOTAL_VERTICES];
int i = 0;
// TOP segment, to left side of RIGHT edge
// "width" of original pgram, "height" of hor. line size
fx11 = ox11;
fy11 = oy11;
fillVertex(vertexList + (i++), fx11, fy11);
fillVertex(vertexList + (i++), fx11 + dx21, fy11 + dy21);
fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);
fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);
fillVertex(vertexList + (i++), fx11 + ldx12, fy11 + ldy12);
fillVertex(vertexList + (i++), fx11, fy11);
// RIGHT segment, to top of BOTTOM edge
// "width" of vert. line size , "height" of original pgram
fx11 = ox11 + dx21;
fy11 = oy11 + dy21;
fillVertex(vertexList + (i++), fx11, fy11);
fillVertex(vertexList + (i++), fx11 + ldx21, fy11 + ldy21);
fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);
fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);
fillVertex(vertexList + (i++), fx11 + dx12, fy11 + dy12);
fillVertex(vertexList + (i++), fx11, fy11);
// BOTTOM segment, from right side of LEFT edge
// "width" of original pgram, "height" of hor. line size
fx11 = ox11 + dx12 + ldx21;
fy11 = oy11 + dy12 + ldy21;
fillVertex(vertexList + (i++), fx11, fy11);
fillVertex(vertexList + (i++), fx11 + dx21, fy11 + dy21);
fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);
fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);
fillVertex(vertexList + (i++), fx11 + ldx12, fy11 + ldy12);
fillVertex(vertexList + (i++), fx11, fy11);
// LEFT segment, from bottom of TOP edge
// "width" of vert. line size , "height" of inner pgram
fx11 = ox11 + ldx12;
fy11 = oy11 + ldy12;
fillVertex(vertexList + (i++), fx11, fy11);
fillVertex(vertexList + (i++), fx11 + ldx21, fy11 + ldy21);
fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);
fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);
fillVertex(vertexList + (i++), fx11 + dx12, fy11 + dy12);
fillVertex(vertexList + (i++), fx11, fy11);
// Encode render command.
id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
if (mtlEncoder == nil)
return;
[mtlEncoder setVertexBytes:vertexList length:sizeof(vertexList) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:TOTAL_VERTICES];
} else {
// The line width ratios were large enough to consume
// the entire hole in the middle of the parallelogram
// so we can just issue one large quad for the outer
// parallelogram.
dx21 += ldx21;
dy21 += ldy21;
dx12 += ldx12;
dy12 += ldy12;
MTLRenderer_FillParallelogram(mtlc, dstOps, ox11, oy11, dx21, dy21, dx12, dy12);
}
}
static GLhandleARB aaPgramProgram = 0;
/*
* This shader fills the space between an outer and inner parallelogram.
* It can be used to draw an outline by specifying both inner and outer
* values. It fills pixels by estimating what portion falls inside the
* outer shape, and subtracting an estimate of what portion falls inside
* the inner shape. Specifying both inner and outer values produces a
* standard "wide outline". Specifying an inner shape that falls far
* outside the outer shape allows the same shader to fill the outer
* shape entirely since pixels that fall within the outer shape are never
* inside the inner shape and so they are filled based solely on their
* coverage of the outer shape.
*
* The setup code renders this shader over the bounds of the outer
* shape (or the only shape in the case of a fill operation) and
* sets the texture 0 coordinates so that 0,0=>0,1=>1,1=>1,0 in those
* texture coordinates map to the four corners of the parallelogram.
* Similarly the texture 1 coordinates map the inner shape to the
* unit square as well, but in a different coordinate system.
*
* When viewed in the texture coordinate systems the parallelograms
* we are filling are unit squares, but the pixels have then become
* tiny parallelograms themselves. Both of the texture coordinate
* systems are affine transforms so the rate of change in X and Y
* of the texture coordinates are essentially constants and happen
* to correspond to the size and direction of the slanted sides of
* the distorted pixels relative to the "square mapped" boundary
* of the parallelograms.
*
* The shader uses the dFdx() and dFdy() functions to measure the "rate
* of change" of these texture coordinates and thus gets an accurate
* measure of the size and shape of a pixel relative to the two
* parallelograms. It then uses the bounds of the size and shape
* of a pixel to intersect with the unit square to estimate the
* coverage of the pixel. Unfortunately, without a lot more work
* to calculate the exact area of intersection between a unit
* square (the original parallelogram) and a parallelogram (the
* distorted pixel), this shader only approximates the pixel
* coverage, but emperically the estimate is very useful and
* produces visually pleasing results, if not theoretically accurate.
*/
static const char *aaPgramShaderSource =
"void main() {"
// Calculate the vectors for the "legs" of the pixel parallelogram
// for the outer parallelogram.
" vec2 oleg1 = dFdx(gl_TexCoord[0].st);"
" vec2 oleg2 = dFdy(gl_TexCoord[0].st);"
// Calculate the bounds of the distorted pixel parallelogram.
" vec2 corner = gl_TexCoord[0].st - (oleg1+oleg2)/2.0;"
" vec2 omin = min(corner, corner+oleg1);"
" omin = min(omin, corner+oleg2);"
" omin = min(omin, corner+oleg1+oleg2);"
" vec2 omax = max(corner, corner+oleg1);"
" omax = max(omax, corner+oleg2);"
" omax = max(omax, corner+oleg1+oleg2);"
// Calculate the vectors for the "legs" of the pixel parallelogram
// for the inner parallelogram.
" vec2 ileg1 = dFdx(gl_TexCoord[1].st);"
" vec2 ileg2 = dFdy(gl_TexCoord[1].st);"
// Calculate the bounds of the distorted pixel parallelogram.
" corner = gl_TexCoord[1].st - (ileg1+ileg2)/2.0;"
" vec2 imin = min(corner, corner+ileg1);"
" imin = min(imin, corner+ileg2);"
" imin = min(imin, corner+ileg1+ileg2);"
" vec2 imax = max(corner, corner+ileg1);"
" imax = max(imax, corner+ileg2);"
" imax = max(imax, corner+ileg1+ileg2);"
// Clamp the bounds of the parallelograms to the unit square to
// estimate the intersection of the pixel parallelogram with
// the unit square. The ratio of the 2 rectangle areas is a
// reasonable estimate of the proportion of coverage.
" vec2 o1 = clamp(omin, 0.0, 1.0);"
" vec2 o2 = clamp(omax, 0.0, 1.0);"
" float oint = (o2.y-o1.y)*(o2.x-o1.x);"
" float oarea = (omax.y-omin.y)*(omax.x-omin.x);"
" vec2 i1 = clamp(imin, 0.0, 1.0);"
" vec2 i2 = clamp(imax, 0.0, 1.0);"
" float iint = (i2.y-i1.y)*(i2.x-i1.x);"
" float iarea = (imax.y-imin.y)*(imax.x-imin.x);"
// Proportion of pixel in outer shape minus the proportion
// of pixel in the inner shape == the coverage of the pixel
// in the area between the two.
" float coverage = oint/oarea - iint / iarea;"
" gl_FragColor = gl_Color * coverage;"
"}";
#define ADJUST_PGRAM(V1, DV, V2) \
do { \
if ((DV) >= 0) { \
(V2) += (DV); \
} else { \
(V1) += (DV); \
} \
} while (0)
// Invert the following transform:
// DeltaT(0, 0) == (0, 0)
// DeltaT(1, 0) == (DX1, DY1)
// DeltaT(0, 1) == (DX2, DY2)
// DeltaT(1, 1) == (DX1+DX2, DY1+DY2)
// TM00 = DX1, TM01 = DX2, (TM02 = X11)
// TM10 = DY1, TM11 = DY2, (TM12 = Y11)
// Determinant = TM00*TM11 - TM01*TM10
// = DX1*DY2 - DX2*DY1
// Inverse is:
// IM00 = TM11/det, IM01 = -TM01/det
// IM10 = -TM10/det, IM11 = TM00/det
// IM02 = (TM01 * TM12 - TM11 * TM02) / det,
// IM12 = (TM10 * TM02 - TM00 * TM12) / det,
#define DECLARE_MATRIX(MAT) \
jfloat MAT ## 00, MAT ## 01, MAT ## 02, MAT ## 10, MAT ## 11, MAT ## 12
#define GET_INVERTED_MATRIX(MAT, X11, Y11, DX1, DY1, DX2, DY2, RET_CODE) \
do { \
jfloat det = DX1*DY2 - DX2*DY1; \
if (det == 0) { \
RET_CODE; \
} \
MAT ## 00 = DY2/det; \
MAT ## 01 = -DX2/det; \
MAT ## 10 = -DY1/det; \
MAT ## 11 = DX1/det; \
MAT ## 02 = (DX2 * Y11 - DY2 * X11) / det; \
MAT ## 12 = (DY1 * X11 - DX1 * Y11) / det; \
} while (0)
#define TRANSFORM(MAT, TX, TY, X, Y) \
do { \
TX = (X) * MAT ## 00 + (Y) * MAT ## 01 + MAT ## 02; \
TY = (X) * MAT ## 10 + (Y) * MAT ## 11 + MAT ## 12; \
} while (0)
void
MTLRenderer_FillAAParallelogram(MTLContext *mtlc, BMTLSDOps *dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12)
{
if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillParallelogram: current dest is null");
return;
}
J2dTraceLn7(J2D_TRACE_INFO,
"MTLRenderer_FillAAParallelogram "
"(x=%6.2f y=%6.2f "
"dx1=%6.2f dy1=%6.2f "
"dx2=%6.2f dy2=%6.2f dst tex=%p)",
fx11, fy11,
dx21, dy21,
dx12, dy12, dstOps->pTexture);
struct Vertex verts[QUAD_VERTEX_COUNT] = {
{ {fx11, fy11}},
{ {fx11+dx21, fy11+dy21}},
{ {fx11+dx12, fy11+dy12}},
{ {fx11 + dx21 + dx12, fy11+ dy21 + dy12}
}};
id<MTLTexture> dstTxt = dstOps->pTexture;
// Encode render command.
id<MTLRenderCommandEncoder> mtlEncoder =
[mtlc.encoderManager getAARenderEncoder:dstOps];
if (mtlEncoder == nil) {
return;
}
[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount: QUAD_VERTEX_COUNT];
}
void
MTLRenderer_FillAAParallelogramInnerOuter(MTLContext *mtlc, MTLSDOps *dstOps,
jfloat ox11, jfloat oy11,
jfloat ox21, jfloat oy21,
jfloat ox12, jfloat oy12,
jfloat ix11, jfloat iy11,
jfloat ix21, jfloat iy21,
jfloat ix12, jfloat iy12)
{
//TODO
J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillAAParallelogramInnerOuter -- :TODO");
}
void
MTLRenderer_DrawAAParallelogram(MTLContext *mtlc, BMTLSDOps *dstOps,
jfloat fx11, jfloat fy11,
jfloat dx21, jfloat dy21,
jfloat dx12, jfloat dy12,
jfloat lwr21, jfloat lwr12)
{
//TODO
// dx,dy for line width in the "21" and "12" directions.
jfloat ldx21, ldy21, ldx12, ldy12;
// parameters for "outer" parallelogram
jfloat ofx11, ofy11, odx21, ody21, odx12, ody12;
// parameters for "inner" parallelogram
jfloat ifx11, ify11, idx21, idy21, idx12, idy12;
J2dTraceLn8(J2D_TRACE_ERROR,
"MTLRenderer_DrawAAParallelogram -- :TODO"
"(x=%6.2f y=%6.2f "
"dx1=%6.2f dy1=%6.2f lwr1=%6.2f "
"dx2=%6.2f dy2=%6.2f lwr2=%6.2f)",
fx11, fy11,
dx21, dy21, lwr21,
dx12, dy12, lwr12);
}
void
MTLRenderer_EnableAAParallelogramProgram()
{
//TODO
J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_EnableAAParallelogramProgram -- :TODO");
}
void
MTLRenderer_DisableAAParallelogramProgram()
{
//TODO
J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_DisableAAParallelogramProgram -- :TODO");
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLSurfaceData_h_Included
#define MTLSurfaceData_h_Included
#import "MTLSurfaceDataBase.h"
#import "MTLGraphicsConfig.h"
#import "AWTWindow.h"
#import "MTLLayer.h"
/**
* The CGLSDOps structure contains the CGL-specific information for a given
* MTLSurfaceData. It is referenced by the native MTLSDOps structure.
*/
typedef struct _MTLSDOps {
AWTView *peerData;
MTLLayer *layer;
jint argb[4]; // background clear color
MTLGraphicsConfigInfo *configInfo;
} MTLSDOps;
// debug-method
NSString * getSurfaceDescription(const BMTLSDOps * bmtlsdOps);
#endif /* MTLSurfaceData_h_Included */

View File

@@ -0,0 +1,404 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#import <stdlib.h>
#import "sun_java2d_metal_MTLSurfaceData.h"
#import "jni_util.h"
#import "MTLRenderQueue.h"
#import "MTLGraphicsConfig.h"
#import "MTLSurfaceData.h"
#import "ThreadUtilities.h"
#include "jlong.h"
/**
* The following methods are implemented in the windowing system (i.e. GLX
* and WGL) source files.
*/
extern jlong MTLSD_GetNativeConfigInfo(BMTLSDOps *bmtlsdo);
extern jboolean MTLSD_InitMTLWindow(JNIEnv *env, BMTLSDOps *bmtlsdo);
extern void MTLSD_DestroyMTLSurface(JNIEnv *env, BMTLSDOps *bmtlsdo);
void MTLSD_SetNativeDimensions(JNIEnv *env, BMTLSDOps *bmtlsdo, jint w, jint h);
static jboolean MTLSurfaceData_initTexture(BMTLSDOps *bmtlsdo, jboolean isOpaque, jboolean rtt, jint width, jint height) {
@autoreleasepool {
if (bmtlsdo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLSurfaceData_initTexture: ops are null");
return JNI_FALSE;
}
if (width <= 0 || height <= 0) {
J2dRlsTraceLn2(J2D_TRACE_ERROR, "MTLSurfaceData_initTexture: texture dimensions is incorrect, w=%d, h=%d", width, height);
return JNI_FALSE;
}
MTLSDOps *mtlsdo = (MTLSDOps *)bmtlsdo->privOps;
if (mtlsdo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLSurfaceData_initTexture: MTLSDOps are null");
return JNI_FALSE;
}
if (mtlsdo->configInfo == NULL || mtlsdo->configInfo->context == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLSurfaceData_initTexture: MTLSDOps wasn't initialized (context is null)");
return JNI_FALSE;
}
MTLContext* ctx = mtlsdo->configInfo->context;
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: MTLPixelFormatBGRA8Unorm width: width height: height mipmapped: NO];
textureDescriptor.usage = MTLTextureUsageUnknown;
bmtlsdo->pTexture = [ctx.device newTextureWithDescriptor: textureDescriptor];
MTLTextureDescriptor *stencilDataDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Uint width:width height:height mipmapped:NO];
stencilDataDescriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
stencilDataDescriptor.storageMode = MTLStorageModePrivate;
bmtlsdo->pStencilData = [ctx.device newTextureWithDescriptor:stencilDataDescriptor];
bmtlsdo->pAAStencilData = [ctx.device newTextureWithDescriptor:textureDescriptor];
MTLTextureDescriptor *stencilTextureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatStencil8 width:width height:height mipmapped:NO];
stencilTextureDescriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
stencilTextureDescriptor.storageMode = MTLStorageModePrivate;
bmtlsdo->pStencilTexture = [ctx.device newTextureWithDescriptor:stencilTextureDescriptor];
MTLTextureDescriptor *lcdTextureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm width:width height:height mipmapped:NO];
bmtlsdo->textureLCD = [ctx.device newTextureWithDescriptor:lcdTextureDescriptor];
bmtlsdo->isOpaque = isOpaque;
bmtlsdo->xOffset = 0;
bmtlsdo->yOffset = 0;
bmtlsdo->width = width;
bmtlsdo->height = height;
bmtlsdo->textureWidth = width;
bmtlsdo->textureHeight = height;
bmtlsdo->textureTarget = -1;
bmtlsdo->drawableType = rtt ? MTLSD_RT_TEXTURE : MTLSD_TEXTURE;
J2dTraceLn6(J2D_TRACE_VERBOSE, "MTLSurfaceData_initTexture: w=%d h=%d bp=%p [tex=%p] opaque=%d rtt=%d", width, height, bmtlsdo, bmtlsdo->pTexture, isOpaque, rtt);
return JNI_TRUE;
}
}
/**
* Initializes an MTL texture, using the given width and height as
* a guide.
*/
JNIEXPORT jboolean JNICALL
Java_sun_java2d_metal_MTLSurfaceData_initTexture(
JNIEnv *env, jobject mtlsd,
jlong pData, jboolean isOpaque,
jint width, jint height
) {
if (!MTLSurfaceData_initTexture((BMTLSDOps *)pData, isOpaque, JNI_FALSE, width, height))
return JNI_FALSE;
MTLSD_SetNativeDimensions(env, (BMTLSDOps *)pData, width, height);
return JNI_TRUE;
}
/**
* Initializes a framebuffer object, using the given width and height as
* a guide. See MTLSD_InitTextureObject() and MTLSD_initRTexture()
* for more information.
*/
JNIEXPORT jboolean JNICALL
Java_sun_java2d_metal_MTLSurfaceData_initRTexture
(JNIEnv *env, jobject mtlsd,
jlong pData, jboolean isOpaque,
jint width, jint height)
{
if (!MTLSurfaceData_initTexture((BMTLSDOps *)pData, isOpaque, JNI_TRUE, width, height))
return JNI_FALSE;
MTLSD_SetNativeDimensions(env, (BMTLSDOps *)pData, width, height);
return JNI_TRUE;
}
/**
* Initializes a surface in the backbuffer of a given double-buffered
* onscreen window for use in a BufferStrategy.Flip situation. The bounds of
* the backbuffer surface should always be kept in sync with the bounds of
* the underlying native window.
*/
JNIEXPORT jboolean JNICALL
Java_sun_java2d_metal_MTLSurfaceData_initFlipBackbuffer
(JNIEnv *env, jobject mtlsd,
jlong pData)
{
//TODO
MTLSDOps *mtlsdo = (MTLSDOps *)jlong_to_ptr(pData);
J2dTraceLn(J2D_TRACE_INFO, "MTLSurfaceData_initFlipBackbuffer -- :TODO");
return JNI_TRUE;
}
JNIEXPORT jlong JNICALL
Java_sun_java2d_metal_MTLSurfaceData_getMTLTexturePointer(JNIEnv *env, jobject mtlsd, jlong pData) {
if (pData == 0)
return 0;
return ptr_to_jlong(((BMTLSDOps *)pData)->pTexture);
}
/**
* Initializes nativeWidth/Height fields of the surfaceData object with
* passed arguments.
*/
void
MTLSD_SetNativeDimensions(JNIEnv *env, BMTLSDOps *mtlsdo,
jint width, jint height)
{
jobject sdObject;
sdObject = (*env)->NewLocalRef(env, mtlsdo->sdOps.sdObject);
if (sdObject == NULL) {
return;
}
JNU_SetFieldByName(env, NULL, sdObject, "nativeWidth", "I", width);
if (!((*env)->ExceptionOccurred(env))) {
JNU_SetFieldByName(env, NULL, sdObject, "nativeHeight", "I", height);
}
(*env)->DeleteLocalRef(env, sdObject);
}
/**
* Deletes native OpenGL resources associated with this surface.
*/
void
MTLSD_Delete(JNIEnv *env, BMTLSDOps *bmtlsdo)
{
J2dTraceLn3(J2D_TRACE_VERBOSE, "MTLSD_Delete: type=%d %p [tex=%p]", bmtlsdo->drawableType, bmtlsdo, bmtlsdo->pTexture);
if (bmtlsdo->drawableType == MTLSD_WINDOW) {
MTLSD_DestroyMTLSurface(env, bmtlsdo);
} else if (
bmtlsdo->drawableType == MTLSD_RT_TEXTURE
|| bmtlsdo->drawableType == MTLSD_TEXTURE
|| bmtlsdo->drawableType == MTLSD_FLIP_BACKBUFFER
) {
[(NSObject *)bmtlsdo->pTexture release];
bmtlsdo->pTexture = NULL;
bmtlsdo->drawableType = MTLSD_UNDEFINED;
}
}
/**
* This is the implementation of the general DisposeFunc defined in
* SurfaceData.h and used by the Disposer mechanism. It first flushes all
* native OpenGL resources and then frees any memory allocated within the
* native MTLSDOps structure.
*/
void
MTLSD_Dispose(JNIEnv *env, SurfaceDataOps *ops)
{
BMTLSDOps *bmtlsdo = (BMTLSDOps *)ops;
jobject graphicsConfig = bmtlsdo->graphicsConfig;
JNU_CallStaticMethodByName(env, NULL, "sun/java2d/metal/MTLSurfaceData",
"dispose",
"(JLsun/java2d/metal/MTLGraphicsConfig;)V",
ptr_to_jlong(ops), graphicsConfig);
(*env)->DeleteGlobalRef(env, graphicsConfig);
bmtlsdo->graphicsConfig = NULL;
}
/**
* This is the implementation of the general surface LockFunc defined in
* SurfaceData.h.
*/
jint
MTLSD_Lock(JNIEnv *env,
SurfaceDataOps *ops,
SurfaceDataRasInfo *pRasInfo,
jint lockflags)
{
JNU_ThrowInternalError(env, "MTLSD_Lock not implemented!");
return SD_FAILURE;
}
/**
* This is the implementation of the general GetRasInfoFunc defined in
* SurfaceData.h.
*/
void
MTLSD_GetRasInfo(JNIEnv *env,
SurfaceDataOps *ops,
SurfaceDataRasInfo *pRasInfo)
{
JNU_ThrowInternalError(env, "MTLSD_GetRasInfo not implemented!");
}
/**
* This is the implementation of the general surface UnlockFunc defined in
* SurfaceData.h.
*/
void
MTLSD_Unlock(JNIEnv *env,
SurfaceDataOps *ops,
SurfaceDataRasInfo *pRasInfo)
{
JNU_ThrowInternalError(env, "MTLSD_Unlock not implemented!");
}
/**
* This function disposes of any native windowing system resources associated
* with this surface.
*/
void
MTLSD_DestroyMTLSurface(JNIEnv *env, BMTLSDOps * bmtlsdo)
{
J2dTraceLn(J2D_TRACE_ERROR, "MTLSD_DestroyMTLSurface not implemented!");
JNF_COCOA_ENTER(env);
if (bmtlsdo->drawableType == MTLSD_WINDOW) {
// TODO: detach the NSView from the metal context
}
bmtlsdo->drawableType = MTLSD_UNDEFINED;
JNF_COCOA_EXIT(env);
}
/**
* This function initializes a native window surface and caches the window
* bounds in the given BMTLSDOps. Returns JNI_TRUE if the operation was
* successful; JNI_FALSE otherwise.
*/
jboolean
MTLSD_InitMTLWindow(JNIEnv *env, BMTLSDOps *bmtlsdo)
{
if (bmtlsdo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLSD_InitMTLWindow: ops are null");
return JNI_FALSE;
}
MTLSDOps *mtlsdo = (MTLSDOps *)bmtlsdo->privOps;
if (mtlsdo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLSD_InitMTLWindow: priv ops are null");
return JNI_FALSE;
}
AWTView *v = mtlsdo->peerData;
if (v == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLSD_InitMTLWindow: view is invalid");
return JNI_FALSE;
}
JNF_COCOA_ENTER(env);
NSRect surfaceBounds = [v bounds];
bmtlsdo->drawableType = MTLSD_WINDOW;
bmtlsdo->isOpaque = JNI_TRUE;
bmtlsdo->width = surfaceBounds.size.width;
bmtlsdo->height = surfaceBounds.size.height;
JNF_COCOA_EXIT(env);
J2dTraceLn2(J2D_TRACE_VERBOSE, " created window: w=%d h=%d", bmtlsdo->width, bmtlsdo->height);
return JNI_TRUE;
}
void
MTLSD_SwapBuffers(JNIEnv *env, jlong pPeerData)
{
J2dTraceLn(J2D_TRACE_ERROR, "OGLSD_SwapBuffers -- :TODO");
}
#pragma mark -
#pragma mark "--- CGLSurfaceData methods ---"
extern LockFunc MTLSD_Lock;
extern GetRasInfoFunc MTLSD_GetRasInfo;
extern UnlockFunc MTLSD_Unlock;
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLSurfaceData_initOps
(JNIEnv *env, jobject mtlsd, jobject gc,
jlong pConfigInfo, jlong pPeerData, jlong layerPtr,
jint xoff, jint yoff, jboolean isOpaque)
{
BMTLSDOps *bmtlsdo = (BMTLSDOps *)SurfaceData_InitOps(env, mtlsd, sizeof(BMTLSDOps));
MTLSDOps *mtlsdo = (MTLSDOps *)malloc(sizeof(MTLSDOps));
J2dTraceLn1(J2D_TRACE_INFO, "MTLSurfaceData_initOps p=%p", bmtlsdo);
J2dTraceLn1(J2D_TRACE_INFO, " pPeerData=%p", jlong_to_ptr(pPeerData));
J2dTraceLn1(J2D_TRACE_INFO, " layerPtr=%p", jlong_to_ptr(layerPtr));
J2dTraceLn2(J2D_TRACE_INFO, " xoff=%d, yoff=%d", (int)xoff, (int)yoff);
gc = (*env)->NewGlobalRef(env, gc);
if (gc == NULL) {
JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed.");
return;
}
if (mtlsdo == NULL) {
(*env)->DeleteGlobalRef(env, gc);
JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed.");
return;
}
// later the graphicsConfig will be used for deallocation of mtlsdo
bmtlsdo->privOps = mtlsdo;
bmtlsdo->graphicsConfig = gc;
bmtlsdo->sdOps.Lock = MTLSD_Lock;
bmtlsdo->sdOps.GetRasInfo = MTLSD_GetRasInfo;
bmtlsdo->sdOps.Unlock = MTLSD_Unlock;
bmtlsdo->sdOps.Dispose = MTLSD_Dispose;
bmtlsdo->drawableType = MTLSD_UNDEFINED;
bmtlsdo->needsInit = JNI_TRUE;
bmtlsdo->xOffset = xoff;
bmtlsdo->yOffset = yoff;
bmtlsdo->isOpaque = isOpaque;
mtlsdo->peerData = (AWTView *)jlong_to_ptr(pPeerData);
mtlsdo->layer = (MTLLayer *)jlong_to_ptr(layerPtr);
mtlsdo->configInfo = (MTLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo);
if (mtlsdo->configInfo == NULL) {
free(mtlsdo);
JNU_ThrowNullPointerException(env, "Config info is null in initOps");
}
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLSurfaceData_clearWindow
(JNIEnv *env, jobject cglsd)
{
J2dTraceLn(J2D_TRACE_INFO, "CGLSurfaceData_clearWindow");
BMTLSDOps *mtlsdo = (MTLSDOps*) SurfaceData_GetOps(env, cglsd);
MTLSDOps *cglsdo = (MTLSDOps*) mtlsdo->privOps;
cglsdo->peerData = NULL;
cglsdo->layer = NULL;
}
NSString * getSurfaceDescription(const BMTLSDOps * bmtlsdOps) {
if (bmtlsdOps == NULL)
return @"NULL";
return [NSString stringWithFormat:@"%p [tex=%p, %dx%d, O=%d]", bmtlsdOps, bmtlsdOps->pTexture, bmtlsdOps->width, bmtlsdOps->height, bmtlsdOps->isOpaque];
}

View File

@@ -0,0 +1,232 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLSurfaceDataBase_h_Included
#define MTLSurfaceDataBase_h_Included
#include "java_awt_image_AffineTransformOp.h"
#include "sun_java2d_metal_MTLSurfaceData.h"
#include "sun_java2d_pipe_hw_AccelSurface.h"
#include "SurfaceData.h"
#include "Trace.h"
#include "MTLFuncs.h"
/**
* The MTLPixelFormat structure contains all the information OpenGL needs to
* know when copying from or into a particular system memory image buffer (via
* glDrawPixels(), glReadPixels, glTexSubImage2D(), etc).
*
* GLenum format;
* The pixel format parameter used in glDrawPixels() and other similar calls.
* Indicates the component ordering for each pixel (e.g. GL_BGRA).
*
* GLenum type;
* The pixel data type parameter used in glDrawPixels() and other similar
* calls. Indicates the data type for an entire pixel or for each component
* in a pixel (e.g. GL_UNSIGNED_BYTE with GL_BGR means a pixel consists of
* 3 unsigned byte components, blue first, then green, then red;
* GL_UNSIGNED_INT_8_8_8_8_REV with GL_BGRA means a pixel consists of 1
* unsigned integer comprised of four byte components, alpha first, then red,
* then green, then blue).
*
* jint alignment;
* The byte alignment parameter used in glPixelStorei(GL_UNPACK_ALIGNMENT). A
* value of 4 indicates that each pixel starts on a 4-byte aligned region in
* memory, and so on. This alignment parameter helps OpenGL speed up pixel
* transfer operations by transferring memory in aligned blocks.
*
* jboolean hasAlpha;
* If true, indicates that this pixel format contains an alpha component.
*
* jboolean isPremult;
* If true, indicates that this pixel format contains color components that
* have been pre-multiplied by their corresponding alpha component.
*/
typedef struct {
//GLenum format;
//GLenum type;
jint format;
jint type;
jint alignment;
jboolean hasAlpha;
jboolean isPremult;
} MTPixelFormat;
/**
* The MTLSDOps structure describes a native OpenGL surface and contains all
* information pertaining to the native surface. Some information about
* the more important/different fields:
*
* void *privOps;
* Pointer to native-specific (GLX, WGL, etc.) SurfaceData info, such as the
* native Drawable handle and GraphicsConfig data.
*
* jint drawableType;
* The surface type; can be any one of the surface type constants defined
* below (MTLSD_WINDOW, MTLSD_TEXTURE, etc).
*
* GLenum activeBuffer;
* Can be either GL_FRONT if this is the front buffer surface of an onscreen
* window or a pbuffer surface, or GL_BACK if this is the backbuffer surface
* of an onscreen window.
*
* jboolean isOpaque;
* If true, the surface should be treated as being fully opaque. If
* the underlying surface (e.g. pbuffer) has an alpha channel and isOpaque
* is true, then we should take appropriate action (i.e. call glColorMask()
* to disable writes into the alpha channel) to ensure that the surface
* remains fully opaque.
*
* jboolean needsInit;
* If true, the surface requires some one-time initialization, which should
* be performed after a context has been made current to the surface for
* the first time.
*
* jint x/yOffset
* The offset in pixels of the OpenGL viewport origin from the lower-left
* corner of the heavyweight drawable. For example, a top-level frame on
* Windows XP has lower-left insets of (4,4). The OpenGL viewport origin
* would typically begin at the lower-left corner of the client region (inside
* the frame decorations), but AWT/Swing will take the insets into account
* when rendering into that window. So in order to account for this, we
* need to adjust the OpenGL viewport origin by an x/yOffset of (-4,-4). On
* X11, top-level frames typically don't have this insets issue, so their
* x/yOffset would be (0,0) (the same applies to pbuffers).
*
* jint width/height;
* The cached surface bounds. For offscreen surface types (MTLSD_FBOBJECT,
* MTLSD_TEXTURE, etc.) these values must remain constant. Onscreen window
* surfaces (MTLSD_WINDOW, MTLSD_FLIP_BACKBUFFER, etc.) may have their
* bounds changed in response to a programmatic or user-initiated event, so
* these values represent the last known dimensions. To determine the true
* current bounds of this surface, query the native Drawable through the
* privOps field.
*
* GLuint textureID;
* The texture object handle, as generated by glGenTextures(). If this value
* is zero, the texture has not yet been initialized.
*
* jint textureWidth/Height;
* The actual bounds of the texture object for this surface. If the
* GL_ARB_texture_non_power_of_two extension is not present, the dimensions
* of an OpenGL texture object must be a power-of-two (e.g. 64x32 or 128x512).
* The texture image that we care about has dimensions specified by the width
* and height fields in this MTLSDOps structure. For example, if the image
* to be stored in the texture has dimensions 115x47, the actual OpenGL
* texture we allocate will have dimensions 128x64 to meet the pow2
* restriction. The image bounds within the texture can be accessed using
* floating point texture coordinates in the range [0.0,1.0].
*
* GLenum textureTarget;
* The texture target of the texture object for this surface. If this
* surface is not backed by a texture, this value is set to zero. Otherwise,
* this value is GL_TEXTURE_RECTANGLE_ARB when the GL_ARB_texture_rectangle
* extension is in use; if not, it is set to GL_TEXTURE_2D.
*
* GLint textureFilter;
* The current filter state for this texture object (can be either GL_NEAREST
* or GL_LINEAR). We cache this value here and check it before updating
* the filter state to avoid redundant calls to glTexParameteri() when the
* filter state remains constant (see the MTLSD_UPDATE_TEXTURE_FILTER()
* macro below).
*
* GLuint fbobjectID, depthID;
* The object handles for the framebuffer object and depth renderbuffer
* associated with this surface. These fields are only used when
* drawableType is MTLSD_FBOBJECT, otherwise they are zero.
*/
typedef struct {
SurfaceDataOps sdOps;
void *privOps;
jobject graphicsConfig;
jint drawableType;
jint activeBuffer;
jboolean isOpaque;
jboolean needsInit;
jint xOffset;
jint yOffset;
jint width;
jint height;
void* pTexture;
void* pStencilData; // stencil data to be rendered to this buffer
void* pStencilTexture; // stencil texture byte buffer stencil mask used in main rendering
void* pAAStencilData; // stencil data for AA rendering
void* textureLCD;
jint textureWidth;
jint textureHeight;
/* GLenum */ jint textureTarget;
/* GLint */ jint textureFilter;
/* GLuint */ jint fbobjectID;
/* GLuint */ jint depthID;
} BMTLSDOps;
#define MTLSD_UNDEFINED sun_java2d_pipe_hw_AccelSurface_UNDEFINED
#define MTLSD_WINDOW sun_java2d_pipe_hw_AccelSurface_WINDOW
#define MTLSD_TEXTURE sun_java2d_pipe_hw_AccelSurface_TEXTURE
#define MTLSD_FLIP_BACKBUFFER sun_java2d_pipe_hw_AccelSurface_FLIP_BACKBUFFER
#define MTLSD_RT_TEXTURE sun_java2d_pipe_hw_AccelSurface_RT_TEXTURE
/**
* These are shorthand names for the filtering method constants used by
* image transform methods.
*/
#define MTLSD_XFORM_DEFAULT 0
#define MTLSD_XFORM_NEAREST_NEIGHBOR \
java_awt_image_AffineTransformOp_TYPE_NEAREST_NEIGHBOR
#define MTLSD_XFORM_BILINEAR \
java_awt_image_AffineTransformOp_TYPE_BILINEAR
/**
* The SurfaceRasterFlags structure contains information about raster (of some MTLTexture):
*
* jboolean isOpaque;
* If true, indicates that this pixel format hasn't alpha component (and values of this component can contain garbage).
*
* jboolean isPremultiplied;
* If true, indicates that this pixel format contains color components that have been pre-multiplied by their
* corresponding alpha component.
*/
typedef struct {
jboolean isOpaque;
jboolean isPremultiplied;
} SurfaceRasterFlags;
/**
* Exported methods.
*/
jint MTLSD_Lock(JNIEnv *env,
SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo,
jint lockflags);
void MTLSD_GetRasInfo(JNIEnv *env,
SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo);
void MTLSD_Unlock(JNIEnv *env,
SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo);
void MTLSD_Dispose(JNIEnv *env, SurfaceDataOps *ops);
void MTLSD_Delete(JNIEnv *env, BMTLSDOps *mtlsdo);
jint MTLSD_NextPowerOfTwo(jint val, jint max);
#endif /* MTLSurfaceDataBase_h_Included */

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef MTLTextRenderer_h_Included
#define MTLTextRenderer_h_Included
#include <jni.h>
#include <jlong.h>
#include "sun_java2d_pipe_BufferedTextPipe.h"
#include "MTLContext.h"
#include "MTLSurfaceData.h"
#define BYTES_PER_GLYPH_IMAGE \
sun_java2d_pipe_BufferedTextPipe_BYTES_PER_GLYPH_IMAGE
#define BYTES_PER_GLYPH_POSITION \
sun_java2d_pipe_BufferedTextPipe_BYTES_PER_GLYPH_POSITION
#define BYTES_PER_POSITIONED_GLYPH \
(BYTES_PER_GLYPH_IMAGE + BYTES_PER_GLYPH_POSITION)
#define OFFSET_CONTRAST sun_java2d_pipe_BufferedTextPipe_OFFSET_CONTRAST
#define OFFSET_RGBORDER sun_java2d_pipe_BufferedTextPipe_OFFSET_RGBORDER
#define OFFSET_SUBPIXPOS sun_java2d_pipe_BufferedTextPipe_OFFSET_SUBPIXPOS
#define OFFSET_POSITIONS sun_java2d_pipe_BufferedTextPipe_OFFSET_POSITIONS
void MTLTR_EnableGlyphVertexCache(MTLContext *mtlc, BMTLSDOps *dstOps);
void MTLTR_DisableGlyphVertexCache(MTLContext *mtlc);
id<MTLTexture> MTLTR_GetGlyphCacheTexture();
void MTLTR_DrawGlyphList(JNIEnv *env, MTLContext *mtlc, BMTLSDOps *dstOps,
jint totalGlyphs, jboolean usePositions,
jboolean subPixPos, jboolean rgbOrder,
jint lcdContrast,
jfloat glyphListOrigX, jfloat glyphListOrigY,
unsigned char *images, unsigned char *positions);
#endif /* MTLTextRenderer_h_Included */

View File

@@ -0,0 +1,935 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <jlong.h>
#include "sun_java2d_metal_MTLTextRenderer.h"
#include "SurfaceData.h"
#include "MTLContext.h"
#include "MTLRenderQueue.h"
#include "MTLTextRenderer.h"
#include "MTLVertexCache.h"
#include "MTLGlyphCache.h"
#include "MTLBlitLoops.h"
/**
* The following constants define the inner and outer bounds of the
* accelerated glyph cache.
*/
#define MTLTR_CACHE_WIDTH 512
#define MTLTR_CACHE_HEIGHT 512
#define MTLTR_CACHE_CELL_WIDTH 32
#define MTLTR_CACHE_CELL_HEIGHT 32
/**
* The current "glyph mode" state. This variable is used to track the
* codepath used to render a particular glyph. This variable is reset to
* MODE_NOT_INITED at the beginning of every call to MTLTR_DrawGlyphList().
* As each glyph is rendered, the glyphMode variable is updated to reflect
* the current mode, so if the current mode is the same as the mode used
* to render the previous glyph, we can avoid doing costly setup operations
* each time.
*/
typedef enum {
MODE_NOT_INITED,
MODE_USE_CACHE_GRAY,
MODE_USE_CACHE_LCD,
MODE_NO_CACHE_GRAY,
MODE_NO_CACHE_LCD,
MODE_NO_CACHE_COLOR
} GlyphMode;
static GlyphMode glyphMode = MODE_NOT_INITED;
/**
* There are two separate glyph caches: for AA and for LCD.
* Once one of them is initialized as either GRAY or LCD, it
* stays in that mode for the duration of the application. It should
* be safe to use this one glyph cache for all screens in a multimon
* environment, since the glyph cache texture is shared between all contexts,
* and (in theory) OpenGL drivers should be smart enough to manage that
* texture across all screens.
*/
static MTLGlyphCacheInfo *glyphCacheLCD = NULL;
static MTLGlyphCacheInfo *glyphCacheAA = NULL;
/**
* The handle to the LCD text fragment program object.
*/
static GLhandleARB lcdTextProgram = 0;
/**
* This value tracks the previous LCD contrast setting, so if the contrast
* value hasn't changed since the last time the gamma uniforms were
* updated (not very common), then we can skip updating the unforms.
*/
static jint lastLCDContrast = -1;
/**
* This value tracks the previous LCD rgbOrder setting, so if the rgbOrder
* value has changed since the last time, it indicates that we need to
* invalidate the cache, which may already store glyph images in the reverse
* order. Note that in most real world applications this value will not
* change over the course of the application, but tests like Font2DTest
* allow for changing the ordering at runtime, so we need to handle that case.
*/
static jboolean lastRGBOrder = JNI_TRUE;
/**
* This constant defines the size of the tile to use in the
* MTLTR_DrawLCDGlyphNoCache() method. See below for more on why we
* restrict this value to a particular size.
*/
#define MTLTR_NOCACHE_TILE_SIZE 32
/**
* These constants define the size of the "cached destination" texture.
* This texture is only used when rendering LCD-optimized text, as that
* codepath needs direct access to the destination. There is no way to
* access the framebuffer directly from an OpenGL shader, so we need to first
* copy the destination region corresponding to a particular glyph into
* this cached texture, and then that texture will be accessed inside the
* shader. Copying the destination into this cached texture can be a very
* expensive operation (accounting for about half the rendering time for
* LCD text), so to mitigate this cost we try to bulk read a horizontal
* region of the destination at a time. (These values are empirically
* derived for the common case where text runs horizontally.)
*
* Note: It is assumed in various calculations below that:
* (MTLTR_CACHED_DEST_WIDTH >= MTLTR_CACHE_CELL_WIDTH) &&
* (MTLTR_CACHED_DEST_WIDTH >= MTLTR_NOCACHE_TILE_SIZE) &&
* (MTLTR_CACHED_DEST_HEIGHT >= MTLTR_CACHE_CELL_HEIGHT) &&
* (MTLTR_CACHED_DEST_HEIGHT >= MTLTR_NOCACHE_TILE_SIZE)
*/
#define MTLTR_CACHED_DEST_WIDTH 512
#define MTLTR_CACHED_DEST_HEIGHT (MTLTR_CACHE_CELL_HEIGHT * 2)
/**
* The handle to the "cached destination" texture object.
*/
static GLuint cachedDestTextureID = 0;
/**
* The current bounds of the "cached destination" texture, in destination
* coordinate space. The width/height of these bounds will not exceed the
* MTLTR_CACHED_DEST_WIDTH/HEIGHT values defined above. These bounds are
* only considered valid when the isCachedDestValid flag is JNI_TRUE.
*/
static SurfaceDataBounds cachedDestBounds;
/**
* This flag indicates whether the "cached destination" texture contains
* valid data. This flag is reset to JNI_FALSE at the beginning of every
* call to MTLTR_DrawGlyphList(). Once we copy valid destination data
* into the cached texture, this flag is set to JNI_TRUE. This way, we can
* limit the number of times we need to copy destination data, which is a
* very costly operation.
*/
static jboolean isCachedDestValid = JNI_FALSE;
/**
* The bounds of the previously rendered LCD glyph, in destination
* coordinate space. We use these bounds to determine whether the glyph
* currently being rendered overlaps the previously rendered glyph (i.e.
* its bounding box intersects that of the previously rendered glyph). If
* so, we need to re-read the destination area associated with that previous
* glyph so that we can correctly blend with the actual destination data.
*/
static SurfaceDataBounds previousGlyphBounds;
static struct TxtVertex txtVertices[6];
static jint vertexCacheIndex = 0;
#define LCD_ADD_VERTEX(TX, TY, DX, DY, DZ) \
do { \
struct TxtVertex *v = &txtVertices[vertexCacheIndex++]; \
v->txtpos[0] = TX; \
v->txtpos[1] = TY; \
v->position[0]= DX; \
v->position[1] = DY; \
} while (0)
#define LCD_ADD_TRIANGLES(TX1, TY1, TX2, TY2, DX1, DY1, DX2, DY2) \
do { \
LCD_ADD_VERTEX(TX1, TY1, DX1, DY1, 0); \
LCD_ADD_VERTEX(TX2, TY1, DX2, DY1, 0); \
LCD_ADD_VERTEX(TX2, TY2, DX2, DY2, 0); \
LCD_ADD_VERTEX(TX2, TY2, DX2, DY2, 0); \
LCD_ADD_VERTEX(TX1, TY2, DX1, DY2, 0); \
LCD_ADD_VERTEX(TX1, TY1, DX1, DY1, 0); \
} while (0)
/**
* Initializes the one glyph cache (texture and data structure).
* If lcdCache is JNI_TRUE, the texture will contain RGB data,
* otherwise we will simply store the grayscale/monochrome glyph images
* as intensity values (which work well with the GL_MODULATE function).
*/
static jboolean
MTLTR_InitGlyphCache(MTLContext *mtlc, jboolean lcdCache)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_InitGlyphCache");
// TODO : Need to fix RGB order in case of LCD
MTLPixelFormat pixelFormat =
lcdCache ? MTLPixelFormatBGRA8Unorm : MTLPixelFormatA8Unorm;
MTLGlyphCacheInfo *gcinfo;
// init glyph cache data structure
gcinfo = MTLGlyphCache_Init(MTLTR_CACHE_WIDTH,
MTLTR_CACHE_HEIGHT,
MTLTR_CACHE_CELL_WIDTH,
MTLTR_CACHE_CELL_HEIGHT,
MTLVertexCache_FlushGlyphVertexCache);
if (gcinfo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLTR_InitGlyphCache: could not init MTL glyph cache");
return JNI_FALSE;
}
MTLTextureDescriptor *textureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:pixelFormat
width:MTLTR_CACHE_WIDTH
height:MTLTR_CACHE_HEIGHT
mipmapped:NO];
gcinfo->texture = [mtlc.device newTextureWithDescriptor:textureDescriptor];
[textureDescriptor release];
if (lcdCache) {
glyphCacheLCD = gcinfo;
} else {
glyphCacheAA = gcinfo;
}
return JNI_TRUE;
}
id<MTLTexture>
MTLTR_GetGlyphCacheTexture()
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_GetGlyphCacheTexture");
if (glyphCacheAA != NULL) {
return glyphCacheAA->texture;
}
return NULL;
}
/**
* Adds the given glyph to the glyph cache (texture and data structure)
* associated with the given MTLContext.
*/
static void
MTLTR_AddToGlyphCache(GlyphInfo *glyph, MTLContext *mtlc,
MTLPixelFormat pixelFormat)
{
MTLCacheCellInfo *ccinfo;
MTLGlyphCacheInfo *gcinfo;
jboolean isLCD = JNI_FALSE;
jint w = glyph->width;
jint h = glyph->height;
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_AddToGlyphCache");
if (pixelFormat == MTLPixelFormatA8Unorm) {
gcinfo = glyphCacheAA;
} else {
gcinfo = glyphCacheLCD;
isLCD = JNI_TRUE;
}
if ((gcinfo == NULL) || (glyph->image == NULL)) {
return;
}
bool isCacheFull = MTLGlyphCache_IsCacheFull(gcinfo, glyph);
if (isCacheFull) {
MTLGlyphCache_Free(gcinfo);
if (!isLCD) {
MTLTR_InitGlyphCache(mtlc, JNI_FALSE);
gcinfo = glyphCacheAA;
} else {
MTLTR_InitGlyphCache(mtlc, JNI_TRUE);
gcinfo = glyphCacheLCD;
}
}
MTLGlyphCache_AddGlyph(gcinfo, glyph);
ccinfo = (MTLCacheCellInfo *) glyph->cellInfo;
if (ccinfo != NULL) {
// store glyph image in texture cell
MTLRegion region = {
{ccinfo->x, ccinfo->y, 0},
{w, h, 1}
};
if (!isLCD) {
NSUInteger bytesPerRow = 1 * w;
[gcinfo->texture replaceRegion:region
mipmapLevel:0
withBytes:glyph->image
bytesPerRow:bytesPerRow];
} else {
unsigned int imageBytes = w * h * 4;
unsigned char imageData[imageBytes];
memset(&imageData, 0, sizeof(imageData));
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
imageData[(i * w * 4) + j * 4] = glyph->image[(i * w * 3) + j * 3];
imageData[(i * w * 4) + j * 4 + 1] = glyph->image[(i * w * 3) + j * 3 + 1];
imageData[(i * w * 4) + j * 4 + 2] = glyph->image[(i * w * 3) + j * 3 + 2];
imageData[(i * w * 4) + j * 4 + 3] = 0xFF;
}
}
NSUInteger bytesPerRow = 4 * w;
[gcinfo->texture replaceRegion:region
mipmapLevel:0
withBytes:imageData
bytesPerRow:bytesPerRow];
}
}
}
static MTLRenderPipelineDescriptor * templateRenderPipelineDesc = nil;
static MTLRenderPipelineDescriptor * templateLCDPipelineDesc = nil;
/**
* Enables the LCD text shader and updates any related state, such as the
* gamma lookup table textures.
*/
static jboolean
MTLTR_EnableLCDGlyphModeState(id<MTLRenderCommandEncoder> encoder,
MTLContext *mtlc,
MTLSDOps *dstOps,
jint contrast)
{
// create the LCD text shader, if necessary
if (templateLCDPipelineDesc == nil) {
MTLVertexDescriptor *vertDesc = [[MTLVertexDescriptor new] autorelease];
vertDesc.attributes[VertexAttributePosition].format = MTLVertexFormatFloat2;
vertDesc.attributes[VertexAttributePosition].offset = 0;
vertDesc.attributes[VertexAttributePosition].bufferIndex = MeshVertexBuffer;
vertDesc.layouts[MeshVertexBuffer].stride = sizeof(struct Vertex);
vertDesc.layouts[MeshVertexBuffer].stepRate = 1;
vertDesc.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;
templateLCDPipelineDesc = [[MTLRenderPipelineDescriptor new] autorelease];
templateLCDPipelineDesc.sampleCount = 1;
templateLCDPipelineDesc.vertexDescriptor = vertDesc;
templateLCDPipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
templateLCDPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].format = MTLVertexFormatFloat2;
templateLCDPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].offset = 2*sizeof(float);
templateLCDPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].bufferIndex = MeshVertexBuffer;
templateLCDPipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stride = sizeof(struct TxtVertex);
templateLCDPipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepRate = 1;
templateLCDPipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;
templateLCDPipelineDesc.label = @"template_lcd";
}
id<MTLRenderPipelineState> pipelineState =
[mtlc.pipelineStateStorage
getPipelineState:templateLCDPipelineDesc
vertexShaderId:@"vert_txt"
fragmentShaderId:@"lcd_color"
];
[encoder setRenderPipelineState:pipelineState];
// update the current color settings
double gamma = ((double)contrast) / 100.0;
double invgamma = 1.0/gamma;
jfloat radj, gadj, badj;
jfloat clr[4];
jint col = [mtlc.paint getColor];
J2dTraceLn2(J2D_TRACE_INFO, "primary color %x, contrast %d", col, contrast);
J2dTraceLn2(J2D_TRACE_INFO, "gamma %f, invgamma %f", gamma, invgamma);
clr[0] = ((col >> 16) & 0xFF)/255.0f;
clr[1] = ((col >> 8) & 0xFF)/255.0f;
clr[2] = ((col) & 0xFF)/255.0f;
// gamma adjust the primary color
radj = (float)pow(clr[0], gamma);
gadj = (float)pow(clr[1], gamma);
badj = (float)pow(clr[2], gamma);
struct LCDFrameUniforms uf = {
{radj, gadj, badj},
{gamma, gamma, gamma},
{invgamma, invgamma, invgamma}};
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
return JNI_TRUE;
}
void
MTLTR_EnableGlyphVertexCache(MTLContext *mtlc, BMTLSDOps *dstOps)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_EnableGlyphVertexCache");
if (!MTLVertexCache_InitVertexCache()) {
return;
}
if (glyphCacheAA == NULL) {
if (!MTLTR_InitGlyphCache(mtlc, JNI_FALSE)) {
return;
}
}
MTLVertexCache_CreateSamplingEncoder(mtlc, dstOps);
}
void
MTLTR_DisableGlyphVertexCache(MTLContext *mtlc)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DisableGlyphVertexCache");
MTLVertexCache_FlushGlyphVertexCache();
MTLVertexCache_RestoreColorState(mtlc);
MTLVertexCache_FreeVertexCache();
}
/**
* Disables any pending state associated with the current "glyph mode".
*/
void
MTLTR_DisableGlyphModeState()
{
// TODO : This is similar to OpenGL implementation
// When LCD implementation is done weshould make
// more changes.
J2dTraceLn1(J2D_TRACE_VERBOSE,
"MTLTR_DisableGlyphModeState: mode=%d", glyphMode);
switch (glyphMode) {
case MODE_NO_CACHE_LCD:
// TODO : Along with LCD implementation
// changes needs to be made
case MODE_USE_CACHE_LCD:
// TODO : Along with LCD implementation
// changes needs to be made
break;
case MODE_NO_CACHE_GRAY:
case MODE_USE_CACHE_GRAY:
case MODE_NOT_INITED:
default:
break;
}
}
static jboolean
MTLTR_DrawGrayscaleGlyphViaCache(MTLContext *mtlc,
GlyphInfo *ginfo, jint x, jint y, BMTLSDOps *dstOps)
{
MTLCacheCellInfo *cell;
jfloat x1, y1, x2, y2;
if (glyphMode != MODE_USE_CACHE_GRAY) {
if (glyphMode == MODE_NO_CACHE_GRAY) {
MTLVertexCache_DisableMaskCache(mtlc);
}
MTLTR_EnableGlyphVertexCache(mtlc, dstOps);
glyphMode = MODE_USE_CACHE_GRAY;
}
if (ginfo->cellInfo == NULL) {
// attempt to add glyph to accelerated glyph cache
MTLTR_AddToGlyphCache(ginfo, mtlc, MTLPixelFormatA8Unorm);
if (ginfo->cellInfo == NULL) {
// we'll just no-op in the rare case that the cell is NULL
return JNI_TRUE;
}
}
cell = (MTLCacheCellInfo *) (ginfo->cellInfo);
cell->timesRendered++;
x1 = (jfloat)x;
y1 = (jfloat)y;
x2 = x1 + ginfo->width;
y2 = y1 + ginfo->height;
MTLVertexCache_AddGlyphQuad(mtlc,
cell->tx1, cell->ty1,
cell->tx2, cell->ty2,
x1, y1, x2, y2);
return JNI_TRUE;
}
/**
* Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 is
* inside outerBounds.
*/
#define INSIDE(gx1, gy1, gx2, gy2, outerBounds) \
(((gx1) >= outerBounds.x1) && ((gy1) >= outerBounds.y1) && \
((gx2) <= outerBounds.x2) && ((gy2) <= outerBounds.y2))
/**
* Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 intersects
* the rectangle defined by bounds.
*/
#define INTERSECTS(gx1, gy1, gx2, gy2, bounds) \
((bounds.x2 > (gx1)) && (bounds.y2 > (gy1)) && \
(bounds.x1 < (gx2)) && (bounds.y1 < (gy2)))
/**
* This method checks to see if the given LCD glyph bounds fall within the
* cached destination texture bounds. If so, this method can return
* immediately. If not, this method will copy a chunk of framebuffer data
* into the cached destination texture and then update the current cached
* destination bounds before returning.
*/
static void
MTLTR_UpdateCachedDestination(MTLSDOps *dstOps, GlyphInfo *ginfo,
jint gx1, jint gy1, jint gx2, jint gy2,
jint glyphIndex, jint totalGlyphs)
{
//TODO
}
static jboolean
MTLTR_DrawLCDGlyphViaCache(MTLContext *mtlc, BMTLSDOps *dstOps,
GlyphInfo *ginfo, jint x, jint y,
jint rowBytesOffset,
jboolean rgbOrder, jint contrast,
id<MTLTexture> dstTexture)
{
CacheCellInfo *cell;
jfloat tx1, ty1, tx2, ty2;
jint w = ginfo->width;
jint h = ginfo->height;
id<MTLRenderCommandEncoder> encoder = nil;
if (glyphMode != MODE_USE_CACHE_LCD) {
if (glyphMode == MODE_NO_CACHE_GRAY) {
MTLVertexCache_DisableMaskCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_GRAY) {
MTLTR_DisableGlyphVertexCache(mtlc);
}
if (glyphCacheLCD == NULL) {
if (!MTLTR_InitGlyphCache(mtlc, JNI_TRUE)) {
return JNI_FALSE;
}
}
if (rgbOrder != lastRGBOrder) {
// need to invalidate the cache in this case; see comments
// for lastRGBOrder above
MTLGlyphCache_Invalidate(glyphCacheLCD);
lastRGBOrder = rgbOrder;
}
glyphMode = MODE_USE_CACHE_LCD;
}
if (ginfo->cellInfo == NULL) {
// attempt to add glyph to accelerated glyph cache
// TODO : Handle RGB order
MTLTR_AddToGlyphCache(ginfo, mtlc, MTLPixelFormatRGBA8Unorm);
if (ginfo->cellInfo == NULL) {
// we'll just no-op in the rare case that the cell is NULL
return JNI_TRUE;
}
}
cell = (CacheCellInfo *) (ginfo->cellInfo);
cell->timesRendered++;
encoder = [mtlc.encoderManager getTextureEncoder:dstOps->pTexture isSrcOpaque:YES isDstOpaque:YES];
if (!MTLTR_EnableLCDGlyphModeState(encoder, mtlc, dstOps,contrast))
{
return JNI_FALSE;
}
tx1 = cell->tx1;
ty1 = cell->ty1;
tx2 = cell->tx2;
ty2 = cell->ty2;
J2dTraceLn4(J2D_TRACE_INFO, "tx1 %f, ty1 %f, tx2 %f, ty2 %f", tx1, ty1, tx2, ty2);
J2dTraceLn2(J2D_TRACE_INFO, "textureWidth %d textureHeight %d", dstOps->textureWidth, dstOps->textureHeight);
LCD_ADD_TRIANGLES(tx1, ty1, tx2, ty2, x, y, x+w, y+h);
[encoder setVertexBytes:txtVertices length:sizeof(txtVertices) atIndex:MeshVertexBuffer];
[encoder setFragmentTexture:glyphCacheLCD->texture atIndex:0];
[encoder setFragmentTexture:dstOps->pTexture atIndex:1];
[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];
vertexCacheIndex = 0;
[mtlc.encoderManager endEncoder];
return JNI_TRUE;
}
static jboolean
MTLTR_DrawGrayscaleGlyphNoCache(MTLContext *mtlc,
GlyphInfo *ginfo, jint x, jint y, BMTLSDOps *dstOps)
{
jint tw, th;
jint sx, sy, sw, sh;
jint x0;
jint w = ginfo->width;
jint h = ginfo->height;
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGrayscaleGlyphNoCache");
if (glyphMode != MODE_NO_CACHE_GRAY) {
if (glyphMode == MODE_USE_CACHE_GRAY) {
MTLTR_DisableGlyphVertexCache(mtlc);
}
MTLVertexCache_EnableMaskCache(mtlc, dstOps);
glyphMode = MODE_NO_CACHE_GRAY;
}
x0 = x;
tw = MTLVC_MASK_CACHE_TILE_WIDTH;
th = MTLVC_MASK_CACHE_TILE_HEIGHT;
for (sy = 0; sy < h; sy += th, y += th) {
x = x0;
sh = ((sy + th) > h) ? (h - sy) : th;
for (sx = 0; sx < w; sx += tw, x += tw) {
sw = ((sx + tw) > w) ? (w - sx) : tw;
J2dTraceLn7(J2D_TRACE_INFO, "sx = %d sy = %d x = %d y = %d sw = %d sh = %d w = %d", sx, sy, x, y, sw, sh, w);
MTLVertexCache_AddMaskQuad(mtlc,
sx, sy, x, y, sw, sh,
w, ginfo->image,
dstOps,
ginfo->width);
}
}
return JNI_TRUE;
}
static jboolean
MTLTR_DrawLCDGlyphNoCache(MTLContext *mtlc, BMTLSDOps *dstOps,
GlyphInfo *ginfo, jint x, jint y,
jint rowBytesOffset,
jboolean rgbOrder, jint contrast,
id<MTLTexture> dstTexture)
{
jfloat tx1, ty1, tx2, ty2;
jfloat dtx1=0, dty1=0, dtx2=0, dty2=0;
jint tw, th;
jint sx=0, sy=0, sw=0, sh=0, dxadj=0, dyadj=0;
jint x0;
jint w = ginfo->width;
jint h = ginfo->height;
id<MTLTexture> blitTexture = nil;
J2dTraceLn2(J2D_TRACE_INFO, "MTLTR_DrawLCDGlyphNoCache x %d, y%d", x, y);
J2dTraceLn3(J2D_TRACE_INFO, "MTLTR_DrawLCDGlyphNoCache rowBytesOffset=%d, rgbOrder=%d, contrast=%d", rowBytesOffset, rgbOrder, contrast);
id<MTLRenderCommandEncoder> encoder = nil;
MTLTextureDescriptor *textureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:w
height:h
mipmapped:NO];
blitTexture = [mtlc.device newTextureWithDescriptor:textureDescriptor];
[textureDescriptor release];
if (glyphMode != MODE_NO_CACHE_LCD) {
if (glyphMode == MODE_NO_CACHE_GRAY) {
MTLVertexCache_DisableMaskCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_GRAY) {
MTLTR_DisableGlyphVertexCache(mtlc);
}
if (blitTexture == nil) {
J2dTraceLn(J2D_TRACE_ERROR, "can't obtain temporary texture object from pool");
return JNI_FALSE;
}
glyphMode = MODE_NO_CACHE_LCD;
}
encoder = [mtlc.encoderManager getTextureEncoder:dstOps->pTexture isSrcOpaque:YES isDstOpaque:YES];
if (!MTLTR_EnableLCDGlyphModeState(encoder, mtlc, dstOps,contrast))
{
return JNI_FALSE;
}
x0 = x;
tx1 = 0.0f;
ty1 = 0.0f;
dtx1 = 0.0f;
dty2 = 0.0f;
tw = MTLTR_NOCACHE_TILE_SIZE;
th = MTLTR_NOCACHE_TILE_SIZE;
unsigned int imageBytes = w * h *4;
unsigned char imageData[imageBytes];
memset(&imageData, 0, sizeof(imageData));
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
imageData[(i * w * 4) + j * 4] = ginfo->image[(i * w * 3) + j * 3];
imageData[(i * w * 4) + j * 4 + 1] = ginfo->image[(i * w * 3) + j * 3 + 1];
imageData[(i * w * 4) + j * 4 + 2] = ginfo->image[(i * w * 3) + j * 3 + 2];
imageData[(i * w * 4) + j * 4 + 3] = 0xFF;
}
}
// copy LCD mask into glyph texture tile
MTLRegion region = MTLRegionMake2D(0, 0, w, h);
NSUInteger bytesPerRow = 4 * ginfo->width;
[blitTexture replaceRegion:region
mipmapLevel:0
withBytes:imageData
bytesPerRow:bytesPerRow];
J2dTraceLn7(J2D_TRACE_INFO, "sx = %d sy = %d x = %d y = %d sw = %d sh = %d w = %d", sx, sy, x, y, sw, sh, w);
// update the lower-right glyph texture coordinates
tx2 = 1.0f;
ty2 = 1.0f;
J2dTraceLn5(J2D_TRACE_INFO, "xOffset %d yOffset %d, dxadj %d, dyadj %d dstOps->height %d", dstOps->xOffset, dstOps->yOffset, dxadj, dyadj, dstOps->height);
dtx1 = ((jfloat)dxadj) / dstOps->textureWidth;
dtx2 = ((float)dxadj + sw) / dstOps->textureWidth;
dty1 = ((jfloat)dyadj + sh) / dstOps->textureHeight;
dty2 = ((jfloat)dyadj) / dstOps->textureHeight;
J2dTraceLn4(J2D_TRACE_INFO, "tx1 %f, ty1 %f, tx2 %f, ty2 %f", tx1, ty1, tx2, ty2);
J2dTraceLn2(J2D_TRACE_INFO, "textureWidth %d textureHeight %d", dstOps->textureWidth, dstOps->textureHeight);
J2dTraceLn4(J2D_TRACE_INFO, "dtx1 %f, dty1 %f, dtx2 %f, dty2 %f", dtx1, dty1, dtx2, dty2);
LCD_ADD_TRIANGLES(tx1, ty1, tx2, ty2, x, y, x+w, y+h);
[encoder setVertexBytes:txtVertices length:sizeof(txtVertices) atIndex:MeshVertexBuffer];
[encoder setFragmentTexture:blitTexture atIndex:0];
[encoder setFragmentTexture:dstOps->pTexture atIndex:1];
[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];
vertexCacheIndex = 0;
[mtlc.encoderManager endEncoder];
[blitTexture release];
MTLCommandBufferWrapper* cbwrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
[commandbuf waitUntilCompleted];
return JNI_TRUE;
}
// see DrawGlyphList.c for more on this macro...
#define FLOOR_ASSIGN(l, r) \
if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))
void
MTLTR_DrawGlyphList(JNIEnv *env, MTLContext *mtlc, BMTLSDOps *dstOps,
jint totalGlyphs, jboolean usePositions,
jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
jfloat glyphListOrigX, jfloat glyphListOrigY,
unsigned char *images, unsigned char *positions)
{
int glyphCounter;
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList");
RETURN_IF_NULL(mtlc);
RETURN_IF_NULL(dstOps);
RETURN_IF_NULL(images);
if (usePositions) {
RETURN_IF_NULL(positions);
}
glyphMode = MODE_NOT_INITED;
isCachedDestValid = JNI_FALSE;
J2dTraceLn1(J2D_TRACE_INFO, "totalGlyphs = %d", totalGlyphs);
for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {
J2dTraceLn(J2D_TRACE_INFO, "Entered for loop for glyph list");
jint x, y;
jfloat glyphx, glyphy;
jboolean grayscale, ok;
GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));
if (ginfo == NULL) {
// this shouldn't happen, but if it does we'll just break out...
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLTR_DrawGlyphList: glyph info is null");
break;
}
grayscale = (ginfo->rowBytes == ginfo->width);
if (usePositions) {
jfloat posx = NEXT_FLOAT(positions);
jfloat posy = NEXT_FLOAT(positions);
glyphx = glyphListOrigX + posx + ginfo->topLeftX;
glyphy = glyphListOrigY + posy + ginfo->topLeftY;
FLOOR_ASSIGN(x, glyphx);
FLOOR_ASSIGN(y, glyphy);
} else {
glyphx = glyphListOrigX + ginfo->topLeftX;
glyphy = glyphListOrigY + ginfo->topLeftY;
FLOOR_ASSIGN(x, glyphx);
FLOOR_ASSIGN(y, glyphy);
glyphListOrigX += ginfo->advanceX;
glyphListOrigY += ginfo->advanceY;
}
if (ginfo->image == NULL) {
J2dTraceLn(J2D_TRACE_INFO, "Glyph image is null");
continue;
}
J2dTraceLn2(J2D_TRACE_INFO, "Glyph width = %d height = %d", ginfo->width, ginfo->height);
J2dTraceLn1(J2D_TRACE_INFO, "rowBytes = %d", ginfo->rowBytes);
if (grayscale) {
// grayscale or monochrome glyph data
if (ginfo->width <= MTLTR_CACHE_CELL_WIDTH &&
ginfo->height <= MTLTR_CACHE_CELL_HEIGHT)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList Grayscale cache");
ok = MTLTR_DrawGrayscaleGlyphViaCache(mtlc, ginfo, x, y, dstOps);
} else {
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList Grayscale no cache");
ok = MTLTR_DrawGrayscaleGlyphNoCache(mtlc, ginfo, x, y, dstOps);
}
} else {
void* dstTexture = dstOps->textureLCD;
// LCD-optimized glyph data
jint rowBytesOffset = 0;
if (subPixPos) {
jint frac = (jint)((glyphx - x) * 3);
if (frac != 0) {
rowBytesOffset = 3 - frac;
x += 1;
}
}
if (rowBytesOffset == 0 &&
ginfo->width <= MTLTR_CACHE_CELL_WIDTH &&
ginfo->height <= MTLTR_CACHE_CELL_HEIGHT)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList LCD cache");
ok = MTLTR_DrawLCDGlyphViaCache(mtlc, dstOps,
ginfo, x, y,
rowBytesOffset,
rgbOrder, lcdContrast,
dstTexture);
} else {
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList LCD no cache");
ok = MTLTR_DrawLCDGlyphNoCache(mtlc, dstOps,
ginfo, x, y,
rowBytesOffset,
rgbOrder, lcdContrast,
dstTexture);
}
}
if (!ok) {
break;
}
}
/*
* Only in case of grayscale text drawing we need to flush
* cache. Still in case of LCD we are not using any intermediate
* cache.
*/
if (glyphMode == MODE_NO_CACHE_GRAY) {
MTLVertexCache_DisableMaskCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_GRAY) {
MTLTR_DisableGlyphVertexCache(mtlc);
}
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLTextRenderer_drawGlyphList
(JNIEnv *env, jobject self,
jint numGlyphs, jboolean usePositions,
jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
jfloat glyphListOrigX, jfloat glyphListOrigY,
jlongArray imgArray, jfloatArray posArray)
{
unsigned char *images;
J2dTraceLn(J2D_TRACE_INFO, "MTLTextRenderer_drawGlyphList");
images = (unsigned char *)
(*env)->GetPrimitiveArrayCritical(env, imgArray, NULL);
if (images != NULL) {
MTLContext *mtlc = MTLRenderQueue_GetCurrentContext();
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
if (usePositions) {
unsigned char *positions = (unsigned char *)
(*env)->GetPrimitiveArrayCritical(env, posArray, NULL);
if (positions != NULL) {
MTLTR_DrawGlyphList(env, mtlc, dstOps,
numGlyphs, usePositions,
subPixPos, rgbOrder, lcdContrast,
glyphListOrigX, glyphListOrigY,
images, positions);
(*env)->ReleasePrimitiveArrayCritical(env, posArray,
positions, JNI_ABORT);
}
} else {
MTLTR_DrawGlyphList(env, mtlc, dstOps,
numGlyphs, usePositions,
subPixPos, rgbOrder, lcdContrast,
glyphListOrigX, glyphListOrigY,
images, NULL);
}
(*env)->ReleasePrimitiveArrayCritical(env, imgArray,
images, JNI_ABORT);
}
}
#endif /* !HEADLESS */

View File

@@ -0,0 +1,30 @@
#ifndef MTLTexturePool_h_Included
#define MTLTexturePool_h_Included
#import "MTLUtils.h"
@interface MTLTexturePoolItem : NSObject
@property (readwrite, retain) id<MTLTexture> texture;
@property (readwrite) bool isBusy;
@property (readwrite, retain) NSDate * lastUsed;
@property (readwrite) bool isMultiSample;
- (id) initWithTexture:(id<MTLTexture>)tex;
@end
@interface MTLPooledTextureHandle : NSObject
@property (readonly, assign) id<MTLTexture> texture;
@property (readonly) MTLRegion rect;
- (void) releaseTexture;
@end
// NOTE: owns all MTLTexture objects
@interface MTLTexturePool : NSObject
@property (readwrite, retain) id<MTLDevice> device;
- (id) initWithDevice:(id<MTLDevice>)device;
- (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format;
- (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format
isMultiSample:(bool)isMultiSample;
@end
#endif /* MTLTexturePool_h_Included */

View File

@@ -0,0 +1,234 @@
#import "MTLTexturePool.h"
#import "Trace.h"
#define SCREEN_MEMORY_SIZE_4K (4096*2160*4) //~33,7 mb
#define MAX_POOL_MEMORY SCREEN_MEMORY_SIZE_4K/2
#define MAX_POOL_ITEM_LIFETIME_SEC 30
#define CELL_WIDTH_BITS 5 // ~ 32 pixel
#define CELL_HEIGHT_BITS 5 // ~ 32 pixel
@implementation MTLTexturePoolItem
@synthesize texture, isBusy, lastUsed, isMultiSample;
- (id) initWithTexture:(id<MTLTexture>)tex {
self = [super init];
if (self == nil) return self;
self.texture = tex;
isBusy = NO;
return self;
}
- (void) dealloc {
[lastUsed release];
[texture release];
[super dealloc];
}
@end
@implementation MTLPooledTextureHandle
{
MTLRegion _rect;
id<MTLTexture> _texture;
MTLTexturePoolItem * _poolItem;
}
@synthesize texture = _texture, rect = _rect;
- (id) initWithPoolItem:(id<MTLTexture>)texture rect:(MTLRegion)rectangle poolItem:(MTLTexturePoolItem *)poolItem {
self = [super init];
if (self == nil) return self;
self->_rect = rectangle;
self->_texture = texture;
self->_poolItem = poolItem;
return self;
}
- (void) releaseTexture {
self->_poolItem.isBusy = NO;
}
@end
@implementation MTLTexturePool {
int _memoryTotalAllocated;
void ** _cells;
int _poolCellWidth;
int _poolCellHeight;
}
@synthesize device;
- (id) initWithDevice:(id<MTLDevice>)dev {
self = [super init];
if (self == nil) return self;
_memoryTotalAllocated = 0;
_poolCellWidth = 10;
_poolCellHeight = 10;
const int cellsCount = _poolCellWidth * _poolCellHeight;
_cells = (void **)malloc(cellsCount * sizeof(void*));
memset(_cells, 0, cellsCount * sizeof(void*));
self.device = dev;
return self;
}
- (void) dealloc {
for (int c = 0; c < _poolCellWidth * _poolCellHeight; ++c) {
NSMutableArray * cell = _cells[c];
if (cell != NULL) {
[cell release];
}
}
free(_cells);
[super dealloc];
}
// NOTE: called from RQ-thread (on blit operations)
- (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format {
return [self getTexture:width height:height format:format isMultiSample:NO];
}
// NOTE: called from RQ-thread (on blit operations)
- (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format
isMultiSample:(bool)isMultiSample {
@autoreleasepool {
// 1. clean pool if necessary
const int requestedPixels = width*height;
const int requestedBytes = requestedPixels*4;
if (_memoryTotalAllocated + requestedBytes > MAX_POOL_MEMORY) {
[self cleanIfNecessary:0]; // release all free textures
} else if (_memoryTotalAllocated + requestedBytes > MAX_POOL_MEMORY/2) {
[self cleanIfNecessary:MAX_POOL_ITEM_LIFETIME_SEC]; // release only old free textures
}
// 2. find free item
const int cellX0 = width >> CELL_WIDTH_BITS;
const int cellY0 = height >> CELL_HEIGHT_BITS;
const int cellX1 = cellX0 + 1;
const int cellY1 = cellY0 + 1;
if (cellX1 > _poolCellWidth || cellY1 > _poolCellHeight) {
const int newCellWidth = cellX1 <= _poolCellWidth ? _poolCellWidth : cellX1;
const int newCellHeight = cellY1 <= _poolCellHeight ? _poolCellHeight : cellY1;
const int newCellsCount = newCellWidth*newCellHeight;
J2dTraceLn2(J2D_TRACE_VERBOSE, "MTLTexturePool: resize: %d -> %d", _poolCellWidth * _poolCellHeight, newCellsCount);
void ** newcells = malloc(newCellsCount*sizeof(void*));
const int strideBytes = _poolCellWidth * sizeof(void*);
for (int cy = 0; cy < _poolCellHeight; ++cy) {
void ** dst = newcells + cy*newCellWidth;
void ** src = _cells + cy * _poolCellWidth;
memcpy(dst, src, strideBytes);
if (newCellWidth > _poolCellWidth)
memset(dst + _poolCellWidth, 0, (newCellWidth - _poolCellWidth) * sizeof(void*));
}
if (newCellHeight > _poolCellHeight) {
void ** dst = newcells + _poolCellHeight * newCellWidth;
memset(dst, 0, (newCellHeight - _poolCellHeight) * newCellWidth * sizeof(void*));
}
free(_cells);
_cells = newcells;
_poolCellWidth = newCellWidth;
_poolCellHeight = newCellHeight;
}
MTLTexturePoolItem * minDeltaTpi = nil;
for (int cy = cellY0; cy < cellY1; ++cy) {
for (int cx = cellX0; cx < cellX1; ++cx) {
NSMutableArray * cell = _cells[cy * _poolCellWidth + cx];
if (cell == NULL)
continue;
const int count = [cell count];
int minDeltaArea = -1;
int minDeltaAreaIndex = -1;
for (int c = 0; c < count; ++c) {
MTLTexturePoolItem *tpi = [cell objectAtIndex:c];
if (tpi == nil || tpi.isBusy || tpi.texture.pixelFormat != format
|| tpi.isMultiSample != isMultiSample) { // TODO: use swizzle when formats are not equal
continue;
}
if (tpi.texture.width < width || tpi.texture.height < height) {
continue;
}
const int deltaArea = tpi.texture.width*tpi.texture.height - requestedPixels;
if (minDeltaArea < 0 || deltaArea < minDeltaArea) {
minDeltaAreaIndex = c;
minDeltaArea = deltaArea;
minDeltaTpi = tpi;
if (deltaArea == 0) {
// found exact match in current cell
break;
}
}
}
if (minDeltaTpi != nil) {
break;
}
}
if (minDeltaTpi != nil) {
break;
}
}
if (minDeltaTpi == NULL) {
MTLTextureDescriptor *textureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format
width:(NSUInteger) width
height:(NSUInteger) height
mipmapped:NO];
if (isMultiSample) {
textureDescriptor.textureType = MTLTextureType2DMultisample;
textureDescriptor.sampleCount = MTLAASampleCount;
textureDescriptor.storageMode = MTLStorageModePrivate;
}
id <MTLTexture> tex = [[self.device newTextureWithDescriptor:textureDescriptor] autorelease];
minDeltaTpi = [[[MTLTexturePoolItem alloc] initWithTexture:tex] autorelease];
minDeltaTpi.isMultiSample = isMultiSample;
NSMutableArray * cell = _cells[cellY0 * _poolCellWidth + cellX0];
if (cell == NULL) {
cell = [[NSMutableArray arrayWithCapacity:10] retain];
_cells[cellY0 * _poolCellWidth + cellX0] = cell;
}
[cell addObject:minDeltaTpi];
_memoryTotalAllocated += requestedBytes;
J2dTraceLn5(J2D_TRACE_VERBOSE, "MTLTexturePool: created pool item: tex=%p, w=%d h=%d, pf=%d | total memory = %d Kb", tex, width, height, format, _memoryTotalAllocated/1024);
}
minDeltaTpi.isBusy = YES;
minDeltaTpi.lastUsed = [NSDate date];
return [[MTLPooledTextureHandle alloc] initWithPoolItem:minDeltaTpi.texture rect:MTLRegionMake2D(0, 0, minDeltaTpi.texture.width, minDeltaTpi.texture.height) poolItem:minDeltaTpi];
}
}
- (void) cleanIfNecessary:(int)lastUsedTimeThreshold {
for (int cy = 0; cy < _poolCellHeight; ++cy) {
for (int cx = 0; cx < _poolCellWidth; ++cx) {
NSMutableArray * cell = _cells[cy * _poolCellWidth + cx];
if (cell == NULL)
continue;
for (int c = 0; c < [cell count];) {
MTLTexturePoolItem *tpi = [cell objectAtIndex:c];
if (!tpi.isBusy) {
if (
lastUsedTimeThreshold <= 0
|| (int)(-[tpi.lastUsed timeIntervalSinceNow]) > lastUsedTimeThreshold
) {
#ifdef DEBUG
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLTexturePool: remove pool item: tex=%p, w=%d h=%d, elapsed=%d", tpi.texture, tpi.texture.width, tpi.texture.height, (int)(-[tpi.lastUsed timeIntervalSinceNow]));
#endif //DEBUG
_memoryTotalAllocated -= tpi.texture.width * tpi.texture.height * 4;
[cell removeObjectAtIndex:c];
continue;
}
}
++c;
}
}
}
}
@end

Some files were not shown because too many files have changed in this diff Show More