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