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