Path: blob/master/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/EncoderManager.m
41159 views
/*1* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include "EncoderManager.h"26#include "MTLContext.h"27#include "sun_java2d_SunGraphics2D.h"28#import "common.h"2930// NOTE: uncomment to disable comparing cached encoder states with requested (for debugging)31// #define ALWAYS_UPDATE_ENCODER_STATES3233const SurfaceRasterFlags defaultRasterFlags = { JNI_FALSE, JNI_TRUE };3435// Internal utility class that represents the set of 'mutable' encoder properties36@interface EncoderStates : NSObject37@property (readonly) MTLClip * clip;3839- (id)init;40- (void)dealloc;4142- (void)reset:(id<MTLTexture>)destination43isDstOpaque:(jboolean)isDstOpaque44isDstPremultiplied:(jboolean)isDstPremultiplied45isAA:(jboolean)isAA46isText:(jboolean)isText47isLCD:(jboolean)isLCD;4849- (void)updateEncoder:(id<MTLRenderCommandEncoder>)encoder50context:(MTLContext *)mtlc51renderOptions:(const RenderOptions *)renderOptions52forceUpdate:(jboolean)forceUpdate;53@property (assign) jboolean aa;54@property (assign) jboolean text;55@property (assign) jboolean lcd;56@property (assign) jboolean aaShader;57@property (retain) MTLPaint* paint;58@end5960@implementation EncoderStates {61MTLPipelineStatesStorage * _pipelineStateStorage;62id<MTLDevice> _device;6364// Persistent encoder properties65id<MTLTexture> _destination;66SurfaceRasterFlags _dstFlags;6768jboolean _isAA;69jboolean _isText;70jboolean _isLCD;71jboolean _isAAShader;7273//74// Cached 'mutable' states of encoder75//7677// Composite rule and source raster flags (it affects the CAD-multipliers (of pipelineState))78MTLComposite * _composite;79SurfaceRasterFlags _srcFlags;8081// Paint mode (it affects shaders (of pipelineState) and corresponding buffers)82MTLPaint * _paint;8384// If true, indicates that encoder is used for texture drawing (user must do [encoder setFragmentTexture:] before drawing)85jboolean _isTexture;86int _interpolationMode;8788// Clip rect or stencil89MTLClip * _clip;9091// Transform (affects transformation inside vertex shader)92MTLTransform * _transform;93}94@synthesize aa = _isAA;95@synthesize text = _isText;96@synthesize lcd = _isLCD;97@synthesize aaShader = _isAAShader;98@synthesize paint = _paint;99100- (id)init {101self = [super init];102if (self) {103_destination = nil;104_composite = [[MTLComposite alloc] init];105_paint = [[MTLPaint alloc] init];106_transform = [[MTLTransform alloc] init];107_clip = [[MTLClip alloc] init];108}109return self;110}111112- (void)dealloc {113[_composite release];114[_paint release];115[_transform release];116[super dealloc];117}118119- (void)setContext:(MTLContext * _Nonnull)mtlc {120self->_pipelineStateStorage = mtlc.pipelineStateStorage;121self->_device = mtlc.device;122}123124- (void)reset:(id<MTLTexture>)destination125isDstOpaque:(jboolean)isDstOpaque126isDstPremultiplied:(jboolean)isDstPremultiplied127isAA:(jboolean)isAA128isText:(jboolean)isText129isLCD:(jboolean)isLCD {130_destination = destination;131_dstFlags.isOpaque = isDstOpaque;132_dstFlags.isPremultiplied = isDstPremultiplied;133_isAA = isAA;134_isText = isText;135_isLCD = isLCD;136// NOTE: probably it's better to invalidate/reset all cached states now137}138139- (void)updateEncoder:(id<MTLRenderCommandEncoder>)encoder140context:(MTLContext *)mtlc141renderOptions:(const RenderOptions *)renderOptions142forceUpdate:(jboolean)forceUpdate143{144// 1. Process special case for stencil mask generation145if (mtlc.clip.stencilMaskGenerationInProgress == JNI_TRUE) {146// use separate pipeline state for stencil generation147if (forceUpdate || (_clip.stencilMaskGenerationInProgress != JNI_TRUE)) {148[_clip copyFrom:mtlc.clip];149[_clip setMaskGenerationPipelineState:encoder150destWidth:_destination.width151destHeight:_destination.height152pipelineStateStorage:_pipelineStateStorage];153}154155[self updateTransform:encoder transform:mtlc.transform forceUpdate:forceUpdate];156return;157}158159// 2. Otherwise update all 'mutable' properties of encoder160[self updatePipelineState:encoder161context:mtlc162renderOptions:renderOptions163forceUpdate:forceUpdate];164[self updateTransform:encoder transform:mtlc.transform forceUpdate:forceUpdate];165[self updateClip:encoder clip:mtlc.clip forceUpdate:forceUpdate];166}167168//169// Internal methods that update states when necessary (compare with cached states)170//171172// Updates pipelineState (and corresponding buffers) with use of paint+composite+flags173- (void)updatePipelineState:(id<MTLRenderCommandEncoder>)encoder174context:(MTLContext *)mtlc175renderOptions:(const RenderOptions *)renderOptions176forceUpdate:(jboolean)forceUpdate177{178if (!forceUpdate179&& [_paint isEqual:mtlc.paint]180&& [_composite isEqual:mtlc.composite]181&& (_isTexture == renderOptions->isTexture && (!renderOptions->isTexture || _interpolationMode == renderOptions->interpolation)) // interpolation is used only in texture mode182&& _isAA == renderOptions->isAA183&& _isAAShader == renderOptions->isAAShader184&& _isText == renderOptions->isText185&& _isLCD == renderOptions->isLCD186&& _srcFlags.isOpaque == renderOptions->srcFlags.isOpaque && _srcFlags.isPremultiplied == renderOptions->srcFlags.isPremultiplied)187return;188189self.paint = mtlc.paint;190[_composite copyFrom:mtlc.composite];191_isTexture = renderOptions->isTexture;192_interpolationMode = renderOptions->interpolation;193_isAA = renderOptions->isAA;194_isAAShader = renderOptions->isAAShader;195_isText = renderOptions->isText;196_isLCD = renderOptions->isLCD;197_srcFlags = renderOptions->srcFlags;198199if ((jint)[mtlc.composite getCompositeState] == sun_java2d_SunGraphics2D_COMP_XOR) {200201[mtlc.paint setXorModePipelineState:encoder202context:mtlc203renderOptions:renderOptions204pipelineStateStorage:_pipelineStateStorage];205} else {206[mtlc.paint setPipelineState:encoder207context:mtlc208renderOptions:renderOptions209pipelineStateStorage:_pipelineStateStorage];210}211}212213- (void) updateClip:(id<MTLRenderCommandEncoder>)encoder clip:(MTLClip *)clip forceUpdate:(jboolean)forceUpdate214{215if (clip.stencilMaskGenerationInProgress == JNI_TRUE) {216// don't set setScissorOrStencil when generation in progress217return;218}219220if (!forceUpdate && [_clip isEqual:clip])221return;222223[_clip copyFrom:clip];224[_clip setScissorOrStencil:encoder225destWidth:_destination.width226destHeight:_destination.height227device:_device];228}229230- (void)updateTransform:(id <MTLRenderCommandEncoder>)encoder231transform:(MTLTransform *)transform232forceUpdate:(jboolean)forceUpdate233{234if (!forceUpdate235&& [_transform isEqual:transform])236return;237238[_transform copyFrom:transform];239[_transform setVertexMatrix:encoder240destWidth:_destination.width241destHeight:_destination.height];242}243244@end245246@implementation EncoderManager {247MTLContext * _mtlc; // used to obtain CommandBufferWrapper and Composite/Paint/Transform248249id<MTLRenderCommandEncoder> _encoder;250251// 'Persistent' properties of encoder252id<MTLTexture> _destination;253id<MTLTexture> _aaDestination;254BOOL _useStencil;255256// 'Mutable' states of encoder257EncoderStates * _encoderStates;258}259260- (id _Nonnull)init {261self = [super init];262if (self) {263_encoder = nil;264_destination = nil;265_aaDestination = nil;266_useStencil = NO;267_encoderStates = [[EncoderStates alloc] init];268269}270return self;271}272273- (void)dealloc {274[_encoderStates release];275[super dealloc];276}277278- (void)setContext:(MTLContex * _Nonnull)mtlc {279self->_mtlc = mtlc;280[self->_encoderStates setContext:mtlc];281}282283- (id<MTLRenderCommandEncoder> _Nonnull) getRenderEncoder:(const BMTLSDOps * _Nonnull)dstOps284{285return [self getRenderEncoder:dstOps->pTexture isDstOpaque:dstOps->isOpaque];286}287288- (id<MTLRenderCommandEncoder> _Nonnull)getAARenderEncoder:(const BMTLSDOps * _Nonnull)dstOps {289id<MTLTexture> dstTxt = dstOps->pTexture;290RenderOptions roptions = {JNI_FALSE, JNI_TRUE, INTERPOLATION_NEAREST_NEIGHBOR, defaultRasterFlags, {dstOps->isOpaque, JNI_TRUE}, JNI_FALSE, JNI_FALSE, JNI_FALSE};291return [self getEncoder:dstTxt renderOptions:&roptions];292}293294- (id<MTLRenderCommandEncoder> _Nonnull)getAAShaderRenderEncoder:(const BMTLSDOps * _Nonnull)dstOps295{296RenderOptions roptions = {JNI_FALSE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, defaultRasterFlags, {dstOps->isOpaque, JNI_TRUE}, JNI_FALSE, JNI_FALSE, JNI_TRUE};297return [self getEncoder:dstOps->pTexture renderOptions:&roptions];298}299300- (id<MTLRenderCommandEncoder> _Nonnull)getRenderEncoder:(id<MTLTexture> _Nonnull)dest301isDstOpaque:(bool)isOpaque302{303RenderOptions roptions = {JNI_FALSE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, defaultRasterFlags, {isOpaque, JNI_TRUE}, JNI_FALSE, JNI_FALSE, JNI_FALSE};304return [self getEncoder:dest renderOptions:&roptions];305}306307- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(const BMTLSDOps * _Nonnull)dstOps308isSrcOpaque:(bool)isSrcOpaque309{310return [self getTextureEncoder:dstOps->pTexture311isSrcOpaque:isSrcOpaque312isDstOpaque:dstOps->isOpaque313interpolation:INTERPOLATION_NEAREST_NEIGHBOR];314}315316- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest317isSrcOpaque:(bool)isSrcOpaque318isDstOpaque:(bool)isDstOpaque319{320return [self getTextureEncoder:dest321isSrcOpaque:isSrcOpaque322isDstOpaque:isDstOpaque323interpolation:INTERPOLATION_NEAREST_NEIGHBOR324isAA:JNI_FALSE];325}326327- (id<MTLRenderCommandEncoder> _Nonnull) getLCDEncoder:(id<MTLTexture> _Nonnull)dest328isSrcOpaque:(bool)isSrcOpaque329isDstOpaque:(bool)isDstOpaque330{331RenderOptions roptions = {JNI_TRUE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, {isSrcOpaque, JNI_TRUE }, {isDstOpaque, JNI_TRUE}, JNI_FALSE, JNI_TRUE, JNI_FALSE};332return [self getEncoder:dest renderOptions:&roptions];333}334335- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest336isSrcOpaque:(bool)isSrcOpaque337isDstOpaque:(bool)isDstOpaque338interpolation:(int)interpolation339isAA:(jboolean)isAA340{341RenderOptions roptions = {JNI_TRUE, isAA, interpolation, { isSrcOpaque, JNI_TRUE }, {isDstOpaque, JNI_TRUE}, JNI_FALSE, JNI_FALSE, JNI_FALSE};342return [self getEncoder:dest renderOptions:&roptions];343}344345- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest346isSrcOpaque:(bool)isSrcOpaque347isDstOpaque:(bool)isDstOpaque348interpolation:(int)interpolation349{350return [self getTextureEncoder:dest isSrcOpaque:isSrcOpaque isDstOpaque:isDstOpaque interpolation:interpolation isAA:JNI_FALSE];351}352353- (id<MTLRenderCommandEncoder> _Nonnull) getTextEncoder:(const BMTLSDOps * _Nonnull)dstOps354isSrcOpaque:(bool)isSrcOpaque355{356RenderOptions roptions = {JNI_TRUE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, { isSrcOpaque, JNI_TRUE }, {dstOps->isOpaque, JNI_TRUE}, JNI_TRUE, JNI_FALSE, JNI_FALSE};357return [self getEncoder:dstOps->pTexture renderOptions:&roptions];358}359360- (id<MTLRenderCommandEncoder> _Nonnull) getEncoder:(id <MTLTexture> _Nonnull)dest361renderOptions:(const RenderOptions * _Nonnull)renderOptions362{363//364// 1. check whether it's necessary to call endEncoder365//366jboolean needEnd = JNI_FALSE;367if (_encoder != nil) {368if (_destination != dest || renderOptions->isAA != _encoderStates.aa) {369J2dTraceLn2(J2D_TRACE_VERBOSE,370"end common encoder because of dest change: %p -> %p",371_destination, dest);372needEnd = JNI_TRUE;373} else if ((_useStencil == NO) != ([_mtlc.clip isShape] == NO)) {374// 1. When mode changes RECT -> SHAPE we must recreate encoder with375// stencilAttachment (todo: consider the case when current encoder already376// has stencil)377//378// 2. When mode changes SHAPE -> RECT it seems that we can use the same379// encoder with disabled stencil test, but [encoder380// setDepthStencilState:nil] causes crash, so we have to recreate encoder381// in such case382J2dTraceLn2(J2D_TRACE_VERBOSE,383"end common encoder because toggle stencil: %d -> %d",384(int)_useStencil, (int)[_mtlc.clip isShape]);385needEnd = JNI_TRUE;386}387}388if (needEnd)389[self endEncoder];390391//392// 2. recreate encoder if necessary393//394jboolean forceUpdate = JNI_FALSE;395#ifdef ALWAYS_UPDATE_ENCODER_STATES396forceUpdate = JNI_TRUE;397#endif // ALWAYS_UPDATE_ENCODER_STATES398399if (_encoder == nil) {400_destination = dest;401_useStencil = [_mtlc.clip isShape] && !_mtlc.clip.stencilMaskGenerationInProgress;402forceUpdate = JNI_TRUE;403404MTLCommandBufferWrapper *cbw = [_mtlc getCommandBufferWrapper];405MTLRenderPassDescriptor *rpd =406[MTLRenderPassDescriptor renderPassDescriptor];407MTLRenderPassColorAttachmentDescriptor *ca = rpd.colorAttachments[0];408ca.texture = dest;409410// TODO: Find out why we cannot use411// if (_mtlc.clip.stencilMaskGenerationInProgress == YES) {412// ca.loadAction = MTLLoadActionClear;413// ca.clearColor = MTLClearColorMake(0.0f, 0.0f,0.0f, 0.0f);414// }415// here to avoid creation of clearEncoder in beginShapeClip416417ca.loadAction = MTLLoadActionLoad;418ca.storeAction = MTLStoreActionStore;419420if (_useStencil && !renderOptions->isAA) {421// If you enable stencil testing or stencil writing, the422// MTLRenderPassDescriptor must include a stencil attachment.423rpd.stencilAttachment.loadAction = MTLLoadActionLoad;424rpd.stencilAttachment.storeAction = MTLStoreActionStore;425rpd.stencilAttachment.texture = _mtlc.clip.stencilTextureRef;426} else if (_mtlc.clip.stencilMaskGenerationInProgress == YES) {427rpd.stencilAttachment.texture = _mtlc.clip.dstOps->pStencilTexture;428rpd.stencilAttachment.clearStencil = 0;429rpd.stencilAttachment.loadAction = _mtlc.clip.stencilMaskGenerationStarted? MTLLoadActionLoad : MTLLoadActionClear;430_mtlc.clip.stencilMaskGenerationStarted = YES;431rpd.stencilAttachment.storeAction = MTLStoreActionStore;432}433434// J2dTraceLn1(J2D_TRACE_VERBOSE, "created render encoder to draw on435// tex=%p", dest);436_encoder = [[cbw getCommandBuffer] renderCommandEncoderWithDescriptor:rpd];437438[_encoderStates reset:dest439isDstOpaque:renderOptions->dstFlags.isOpaque440isDstPremultiplied:YES441isAA:renderOptions->isAA442isText:renderOptions->isText443isLCD:renderOptions->isLCD];444}445446//447// 3. update encoder states448//449[_encoderStates updateEncoder:_encoder450context:_mtlc451renderOptions:renderOptions452forceUpdate:forceUpdate];453454return _encoder;455}456457- (id<MTLBlitCommandEncoder> _Nonnull) createBlitEncoder {458[self endEncoder];459return [[[_mtlc getCommandBufferWrapper] getCommandBuffer] blitCommandEncoder];460}461462- (void) endEncoder {463if (_encoder != nil) {464[_encoder endEncoding];465_encoder = nil;466_destination = nil;467}468}469470@end471472473