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