1 #include "EncoderManager.h"
2 #include "MTLContext.h"
3 #include "sun_java2d_SunGraphics2D.h"
4 #import "common.h"
5
6 // NOTE: uncomment to disable comparing cached encoder states with requested (for debugging)
7 // #define ALWAYS_UPDATE_ENCODER_STATES
8
9 const SurfaceRasterFlags defaultRasterFlags = { JNI_FALSE, JNI_TRUE };
10
11 // Internal utility class that represents the set of 'mutable' encoder properties
12 @interface EncoderStates : NSObject
13 @property (readonly) MTLClip * clip;
14
15 - (id)init;
16 - (void)dealloc;
17
18 - (void)reset:(id<MTLTexture>)destination
19 isDstOpaque:(jboolean)isDstOpaque
20 isDstPremultiplied:(jboolean)isDstPremultiplied
21 isAA:(jboolean)isAA;
22
23 - (void)updateEncoder:(id<MTLRenderCommandEncoder>)encoder
24 paint:(MTLPaint *)paint
25 composite:(MTLComposite *)composite
26 isTexture:(jboolean)isTexture
27 isAA:(jboolean)isAA
28 srcFlags:(const SurfaceRasterFlags * _Nullable)srcFlags
29 clip:(MTLClip *)clip
30 transform:(MTLTransform *)transform
31 forceUpdate:(jboolean)forceUpdate;
32 @property jboolean aa;
33 @end
34
35 @implementation EncoderStates {
36 MTLPipelineStatesStorage * _pipelineStateStorage;
37 id<MTLDevice> _device;
38
39 // Persistent encoder properties
40 id<MTLTexture> _destination;
41 SurfaceRasterFlags _dstFlags;
42
43 jboolean _isAA;
44
45 //
46 // Cached 'mutable' states of encoder
47 //
48
49 // Composite rule and source raster flags (it affects the CAD-multipliers (of pipelineState))
50 MTLComposite * _composite;
51 SurfaceRasterFlags _srcFlags;
52
53 // Paint mode (it affects shaders (of pipelineState) and corresponding buffers)
54 MTLPaint * _paint;
55
56 // If true, indicates that encoder is used for texture drawing (user must do [encoder setFragmentTexture:] before drawing)
57 jboolean _isTexture;
58 int _interpolationMode;
59
60 // Clip rect or stencil
61 MTLClip * _clip;
62
63 // Transform (affects transformation inside vertex shader)
64 MTLTransform * _transform;
65 }
66 @synthesize aa = _isAA;
67
68 - (id)init {
69 self = [super init];
70 if (self) {
71 _destination = nil;
72 _composite = [[MTLComposite alloc] init];
73 _paint = [[MTLPaint alloc] init];
74 _transform = [[MTLTransform alloc] init];
75 _clip = [[MTLClip alloc] init];
76 }
77 return self;
78 }
79
80 - (void)dealloc {
81 [_composite release];
82 [_paint release];
83 [_transform release];
84 [super dealloc];
85 }
86
87 - (void)setContext:(MTLContext * _Nonnull)mtlc {
88 self->_pipelineStateStorage = mtlc.pipelineStateStorage;
89 self->_device = mtlc.device;
90 }
91
92 - (void)reset:(id<MTLTexture>)destination
93 isDstOpaque:(jboolean)isDstOpaque
94 isDstPremultiplied:(jboolean)isDstPremultiplied
95 isAA:(jboolean)isAA {
96 _destination = destination;
97 _dstFlags.isOpaque = isDstOpaque;
98 _dstFlags.isPremultiplied = isDstPremultiplied;
99 _isAA = isAA;
100 // NOTE: probably it's better to invalidate/reset all cached states now
101 }
102
103 - (void)updateEncoder:(id<MTLRenderCommandEncoder>)encoder
104 paint:(MTLPaint *)paint
105 composite:(MTLComposite *)composite
106 isTexture:(jboolean)isTexture
107 interpolation:(int)interpolation
108 isAA:(jboolean)isAA
109 srcFlags:(const SurfaceRasterFlags * _Nullable)srcFlags
110 clip:(MTLClip *)clip
111 transform:(MTLTransform *)transform
112 forceUpdate:(jboolean)forceUpdate
113 {
114 // 1. Process special case for stencil mask generation
115 if (clip.stencilMaskGenerationInProgress == JNI_TRUE) {
116 // use separate pipeline state for stencil generation
117 if (forceUpdate || (_clip.stencilMaskGenerationInProgress != JNI_TRUE)) {
118 [_clip copyFrom:clip];
119 [_clip setMaskGenerationPipelineState:encoder
120 destWidth:_destination.width
121 destHeight:_destination.height
122 pipelineStateStorage:_pipelineStateStorage];
123 }
124
125 [self updateTransform:encoder transform:transform forceUpdate:forceUpdate];
126 return;
127 }
128
129 // 2. Otherwise update all 'mutable' properties of encoder
130 [self updatePipelineState:encoder
131 paint:paint
132 composite:composite
133 isStencilUsed:[clip isShape]
134 isTexture:isTexture
135 interpolation:interpolation
136 isAA:isAA
137 srcFlags:srcFlags
138 forceUpdate:forceUpdate];
139 [self updateTransform:encoder transform:transform forceUpdate:forceUpdate];
140 [self updateClip:encoder clip:clip forceUpdate:forceUpdate];
141 }
142
143 //
144 // Internal methods that update states when necessary (compare with cached states)
145 //
146
147 // Updates pipelineState (and corresponding buffers) with use of paint+composite+flags
148 - (void)updatePipelineState:(id<MTLRenderCommandEncoder>)encoder
149 paint:(MTLPaint *)paint
150 composite:(MTLComposite *)composite
151 isStencilUsed:(jboolean)isStencilUsed
152 isTexture:(jboolean)isTexture
153 interpolation:(int)interpolation
154 isAA:(jboolean)isAA
155 srcFlags:(const SurfaceRasterFlags * _Nullable)srcFlags
156 forceUpdate:(jboolean)forceUpdate
157 {
158 if (srcFlags == NULL)
159 srcFlags = &defaultRasterFlags;
160
161 if (!forceUpdate
162 && [_paint isEqual:paint]
163 && [_composite isEqual:composite]
164 && (_isTexture == isTexture && (!isTexture || _interpolationMode == interpolation)) // interpolation is used only in texture mode
165 && _isAA == isAA
166 && _srcFlags.isOpaque == srcFlags->isOpaque && _srcFlags.isPremultiplied == srcFlags->isPremultiplied)
167 return;
168
169 [_paint copyFrom:paint];
170 [_composite copyFrom:composite];
171 _isTexture = isTexture;
172 _interpolationMode = interpolation;
173 _isAA = isAA;
174 _srcFlags = *srcFlags;
175
176 if ((jint)[composite getCompositeState] == sun_java2d_SunGraphics2D_COMP_XOR) {
177 [paint setXorModePipelineState:encoder
178 composite:_composite
179 isStencilUsed:isStencilUsed
180 isTexture:_isTexture
181 interpolation:interpolation
182 srcFlags:&_srcFlags
183 dstFlags:&_dstFlags
184 pipelineStateStorage:_pipelineStateStorage];
185 } else {
186 [paint setPipelineState:encoder
187 composite:_composite
188 isStencilUsed:isStencilUsed
189 isTexture:_isTexture
190 interpolation:interpolation
191 isAA:isAA
192 srcFlags:&_srcFlags
193 dstFlags:&_dstFlags
194 pipelineStateStorage:_pipelineStateStorage];
195 }
196 }
197
198 - (void) updateClip:(id<MTLRenderCommandEncoder>)encoder clip:(MTLClip *)clip forceUpdate:(jboolean)forceUpdate
199 {
200 if (clip.stencilMaskGenerationInProgress == JNI_TRUE) {
201 // don't set setScissorOrStencil when generateion in progress
202 return;
203 }
204
205 if (!forceUpdate && [_clip isEqual:clip])
206 return;
207
208 [_clip copyFrom:clip];
209 [_clip setScissorOrStencil:encoder
210 destWidth:_destination.width
211 destHeight:_destination.height
212 device:_device];
213 }
214
215 - (void)updateTransform:(id <MTLRenderCommandEncoder>)encoder
216 transform:(MTLTransform *)transform
217 forceUpdate:(jboolean)forceUpdate
218 {
219 if (!forceUpdate
220 && [_transform isEqual:transform])
221 return;
222
223 [_transform copyFrom:transform];
224 [_transform setVertexMatrix:encoder
225 destWidth:_destination.width
226 destHeight:_destination.height];
227 }
228
229 @end
230
231 @implementation EncoderManager {
232 MTLContext * _mtlc; // used to obtain CommandBufferWrapper and Composite/Paint/Transform
233
234 id<MTLRenderCommandEncoder> _encoder;
235
236 // 'Persistent' properties of encoder
237 id<MTLTexture> _destination;
238 id<MTLTexture> _aaDestination;
239 BOOL _useStencil;
240
241 // 'Mutable' states of encoder
242 EncoderStates * _encoderStates;
243 }
244
245 - (id _Nonnull)init {
246 self = [super init];
247 if (self) {
248 _encoder = nil;
249 _destination = nil;
250 _aaDestination = nil;
251 _useStencil = NO;
252 _encoderStates = [[EncoderStates alloc] init];
253
254 }
255 return self;
256 }
257
258 - (void)dealloc {
259 [_encoderStates release];
260 [super dealloc];
261 }
262
263 - (void)setContext:(MTLContex * _Nonnull)mtlc {
264 self->_mtlc = mtlc;
265 [self->_encoderStates setContext:mtlc];
266 }
267
268 - (id<MTLRenderCommandEncoder> _Nonnull) getRenderEncoder:(const BMTLSDOps * _Nonnull)dstOps
269 {
270 return [self getRenderEncoder:dstOps->pTexture isDstOpaque:dstOps->isOpaque];
271 }
272
273 - (id<MTLRenderCommandEncoder> _Nonnull)getAARenderEncoder:(const BMTLSDOps * _Nonnull)dstOps {
274 id<MTLTexture> dstTxt = dstOps->pTexture;
275 return [self getEncoder:dstTxt
276 isOpaque:dstOps->isOpaque
277 isTexture:JNI_FALSE
278 interpolation:INTERPOLATION_NEAREST_NEIGHBOR
279 isAA:JNI_TRUE
280 srcFlags:NULL];
281 }
282
283 - (id<MTLRenderCommandEncoder> _Nonnull)getRenderEncoder:(id<MTLTexture> _Nonnull)dest
284 isDstOpaque:(bool)isOpaque
285 {
286 return [self getEncoder:dest
287 isOpaque:isOpaque
288 isTexture:JNI_FALSE
289 interpolation:INTERPOLATION_NEAREST_NEIGHBOR
290 isAA:JNI_FALSE
291 srcFlags:NULL];
292 }
293
294 - (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(const BMTLSDOps * _Nonnull)dstOps
295 isSrcOpaque:(bool)isSrcOpaque
296 {
297 return [self getTextureEncoder:dstOps->pTexture
298 isSrcOpaque:isSrcOpaque
299 isDstOpaque:dstOps->isOpaque
300 interpolation:INTERPOLATION_NEAREST_NEIGHBOR];
301 }
302
303 - (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest
304 isSrcOpaque:(bool)isSrcOpaque
305 isDstOpaque:(bool)isDstOpaque
306 {
307 return [self getTextureEncoder:dest
308 isSrcOpaque:isSrcOpaque
309 isDstOpaque:isDstOpaque
310 interpolation:INTERPOLATION_NEAREST_NEIGHBOR
311 isAA:JNI_FALSE];
312 }
313
314 - (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest
315 isSrcOpaque:(bool)isSrcOpaque
316 isDstOpaque:(bool)isDstOpaque
317 interpolation:(int)interpolation
318 isAA:(jboolean)isAA
319 {
320 SurfaceRasterFlags srcFlags = { isSrcOpaque, JNI_TRUE };
321 return [self getEncoder:dest
322 isOpaque:isDstOpaque
323 isTexture:JNI_TRUE
324 interpolation:interpolation
325 isAA:isAA
326 srcFlags:&srcFlags];
327 }
328
329 - (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest
330 isSrcOpaque:(bool)isSrcOpaque
331 isDstOpaque:(bool)isDstOpaque
332 interpolation:(int)interpolation
333 {
334 return [self getTextureEncoder:dest isSrcOpaque:isSrcOpaque isDstOpaque:isDstOpaque interpolation:interpolation isAA:JNI_FALSE];
335 }
336
337 - (id<MTLRenderCommandEncoder> _Nonnull)
338 getEncoder:(id<MTLTexture> _Nonnull)dest
339 isOpaque:(jboolean)isOpaque
340 isTexture:(jboolean)isTexture
341 interpolation:(int)interpolation
342 isAA:(jboolean)isAA
343 srcFlags:(const SurfaceRasterFlags *_Nullable)srcFlags {
344 //
345 // 1. check whether it's necessary to call endEncoder
346 //
347 jboolean needEnd = JNI_FALSE;
348 if (_encoder != nil) {
349 if (_destination != dest || isAA != _encoderStates.aa) {
350 J2dTraceLn2(J2D_TRACE_VERBOSE,
351 "end common encoder because of dest change: %p -> %p",
352 _destination, dest);
353 needEnd = JNI_TRUE;
354 } else if ((_useStencil == NO) != ([_mtlc.clip isShape] == NO)) {
355 // 1. When mode changes RECT -> SHAPE we must recreate encoder with
356 // stencilAttachment (todo: consider the case when current encoder already
357 // has stencil)
358 //
359 // 2. When mode changes SHAPE -> RECT it seems that we can use the same
360 // encoder with disabled stencil test, but [encoder
361 // setDepthStencilState:nil] causes crash, so we have to recreate encoder
362 // in such case
363 J2dTraceLn2(J2D_TRACE_VERBOSE,
364 "end common encoder because toggle stencil: %d -> %d",
365 (int)_useStencil, (int)[_mtlc.clip isShape]);
366 needEnd = JNI_TRUE;
367 }
368 }
369 if (needEnd)
370 [self endEncoder];
371
372 //
373 // 2. recreate encoder if necessary
374 //
375 jboolean forceUpdate = JNI_FALSE;
376 #ifdef ALWAYS_UPDATE_ENCODER_STATES
377 forceUpdate = JNI_TRUE;
378 #endif // ALWAYS_UPDATE_ENCODER_STATES
379
380 if (_encoder == nil) {
381 _destination = dest;
382 _useStencil = [_mtlc.clip isShape];
383 forceUpdate = JNI_TRUE;
384
385 MTLCommandBufferWrapper *cbw = [_mtlc getCommandBufferWrapper];
386 MTLRenderPassDescriptor *rpd =
387 [MTLRenderPassDescriptor renderPassDescriptor];
388 MTLRenderPassColorAttachmentDescriptor *ca = rpd.colorAttachments[0];
389 if (isAA && !isTexture) {
390 MTLTexturePoolItem *tiBuf = [_mtlc.texturePool getTexture:dest.width
391 height:dest.height
392 format:MTLPixelFormatBGRA8Unorm];
393 [cbw registerPooledTexture:tiBuf];
394 [tiBuf release];
395 _aaDestination = tiBuf.texture;
396
397 MTLTexturePoolItem *ti = [_mtlc.texturePool getTexture:dest.width
398 height:dest.height
399 format:_aaDestination.pixelFormat
400 isMultiSample:YES];
401 [cbw registerPooledTexture:ti];
402 [ti release];
403 ca.texture = ti.texture;
404 ca.resolveTexture = _aaDestination;
405 ca.clearColor = MTLClearColorMake(0.0f, 0.0f, 0.0f, 0.0f);
406 ca.loadAction = MTLLoadActionClear;
407 ca.storeAction = MTLStoreActionMultisampleResolve;
408 } else {
409 ca.texture = dest;
410 ca.loadAction = MTLLoadActionLoad;
411 ca.storeAction = MTLStoreActionStore;
412 }
413
414 if (_useStencil) {
415 // If you enable stencil testing or stencil writing, the
416 // MTLRenderPassDescriptor must include a stencil attachment.
417 rpd.stencilAttachment.texture = _mtlc.clip.stencilTextureRef;
418 rpd.stencilAttachment.loadAction = MTLLoadActionLoad;
419 rpd.stencilAttachment.storeAction = MTLStoreActionDontCare;
420 }
421
422 // J2dTraceLn1(J2D_TRACE_VERBOSE, "created render encoder to draw on
423 // tex=%p", dest);
424 _encoder = [[cbw getCommandBuffer] renderCommandEncoderWithDescriptor:rpd];
425 [rpd release];
426
427 [_encoderStates reset:dest
428 isDstOpaque:isOpaque
429 isDstPremultiplied:YES
430 isAA:isAA];
431 }
432
433 //
434 // 3. update encoder states
435 //
436 [_encoderStates updateEncoder:_encoder
437 paint:_mtlc.paint
438 composite:_mtlc.composite
439 isTexture:isTexture
440 interpolation:interpolation
441 isAA:isAA
442 srcFlags:srcFlags
443 clip:_mtlc.clip
444 transform:_mtlc.transform
445 forceUpdate:forceUpdate];
446
447 return _encoder;
448 }
449
450 - (id<MTLBlitCommandEncoder> _Nonnull) createBlitEncoder {
451 [self endEncoder];
452 return [[[_mtlc getCommandBufferWrapper] getCommandBuffer] blitCommandEncoder];
453 }
454
455 - (void) endEncoder {
456 if (_encoder != nil) {
457 [_encoder endEncoding];
458 [_encoder release];
459 _encoder = nil;
460 if (_aaDestination != nil) {
461 id<MTLTexture> aaDest = _aaDestination;
462 _aaDestination = nil;
463 NSUInteger _w = _destination.width;
464 NSUInteger _h = _destination.height;
465 _encoder = [self getTextureEncoder:_destination
466 isSrcOpaque:JNI_FALSE
467 isDstOpaque:JNI_TRUE
468 interpolation:INTERPOLATION_NEAREST_NEIGHBOR
469 isAA:JNI_TRUE];
470
471 struct TxtVertex quadTxVerticesBuffer[] = {
472 {{0, 0}, {0, 0}},
473 {{0,_h}, {0, 1}},
474 {{_w, 0},{1, 0}},
475 {{0, _h},{0, 1}},
476 {{_w, _h}, {1, 1}},
477 {{_w, 0}, {1, 0}}
478 };
479
480 [_encoder setVertexBytes:quadTxVerticesBuffer length:sizeof(quadTxVerticesBuffer) atIndex:MeshVertexBuffer];
481 [_encoder setFragmentTexture:aaDest atIndex: 0];
482 [_encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];
483 [_encoder endEncoding];
484 [_encoder release];
485 }
486
487 _encoder = nil;
488 _destination = nil;
489 }
490 }
491
492 @end