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