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