Path: blob/master/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLClip.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 "MTLClip.h"2627#include "MTLContext.h"28#include "MTLStencilManager.h"29#include "common.h"3031static MTLRenderPipelineDescriptor * templateStencilPipelineDesc = nil;3233static void initTemplatePipelineDescriptors() {34if (templateStencilPipelineDesc != nil)35return;3637MTLVertexDescriptor *vertDesc = [[MTLVertexDescriptor new] autorelease];38vertDesc.attributes[VertexAttributePosition].format = MTLVertexFormatFloat2;39vertDesc.attributes[VertexAttributePosition].offset = 0;40vertDesc.attributes[VertexAttributePosition].bufferIndex = MeshVertexBuffer;41vertDesc.layouts[MeshVertexBuffer].stride = sizeof(struct Vertex);42vertDesc.layouts[MeshVertexBuffer].stepRate = 1;43vertDesc.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;4445templateStencilPipelineDesc = [MTLRenderPipelineDescriptor new];46templateStencilPipelineDesc.sampleCount = 1;47templateStencilPipelineDesc.vertexDescriptor = vertDesc;48templateStencilPipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatR8Uint; // A byte buffer format49templateStencilPipelineDesc.stencilAttachmentPixelFormat = MTLPixelFormatStencil8;50templateStencilPipelineDesc.label = @"template_stencil";51}5253@implementation MTLClip {54jint _clipType;55MTLScissorRect _clipRect;56MTLContext* _mtlc;57BMTLSDOps* _dstOps;58BOOL _stencilMaskGenerationInProgress;59BOOL _stencilMaskGenerationStarted;60BOOL _clipReady;61MTLOrigin _clipShapeOrigin;62MTLSize _clipShapeSize;63}6465@synthesize dstOps = _dstOps;6667- (id)init {68self = [super init];69if (self) {70_clipType = NO_CLIP;71_mtlc = nil;72_dstOps = NULL;73_stencilMaskGenerationInProgress = NO;74_stencilMaskGenerationStarted = NO;75_clipReady = NO;76}77return self;78}7980- (BOOL)isEqual:(MTLClip *)other {81if (self == other)82return YES;83if (_stencilMaskGenerationInProgress == JNI_TRUE)84return other->_stencilMaskGenerationInProgress == JNI_TRUE;85if (_clipType != other->_clipType)86return NO;87if (_clipType == NO_CLIP)88return YES;89if (_clipType == RECT_CLIP) {90return _clipRect.x == other->_clipRect.x && _clipRect.y == other->_clipRect.y91&& _clipRect.width == other->_clipRect.width && _clipRect.height == other->_clipRect.height;92}9394// NOTE: can compare stencil-data pointers here95return YES;96}9798- (BOOL)isShape {99return _clipType == SHAPE_CLIP;100}101102- (BOOL)isRect __unused {103return _clipType == RECT_CLIP;104}105106- (const MTLScissorRect * _Nullable) getRect {107return _clipType == RECT_CLIP ? &_clipRect : NULL;108}109110- (void)copyFrom:(MTLClip *)other {111_clipType = other->_clipType;112_stencilMaskGenerationInProgress = other->_stencilMaskGenerationInProgress;113_dstOps = other->_dstOps;114_mtlc = other->_mtlc;115if (other->_clipType == RECT_CLIP) {116_clipRect = other->_clipRect;117}118}119120- (void)reset {121_clipType = NO_CLIP;122_stencilMaskGenerationInProgress = JNI_FALSE;123}124125126- (void)setClipRectX1:(jint)x1 Y1:(jint)y1 X2:(jint)x2 Y2:(jint)y2 {127if (_clipType == SHAPE_CLIP) {128_dstOps = NULL;129}130131if (x1 >= x2 || y1 >= y2) {132J2dTraceLn4(J2D_TRACE_ERROR, "MTLClip.setClipRect: invalid rect: x1=%d y1=%d x2=%d y2=%d", x1, y1, x2, y2);133_clipType = NO_CLIP;134}135136const jint width = x2 - x1;137const jint height = y2 - y1;138139J2dTraceLn4(J2D_TRACE_INFO, "MTLClip.setClipRect: x=%d y=%d w=%d h=%d", x1, y1, width, height);140141_clipRect.x = (NSUInteger)((x1 >= 0) ? x1 : 0);142_clipRect.y = (NSUInteger)((y1 >= 0) ? y1 : 0);143_clipRect.width = (NSUInteger)((width >= 0) ? width : 0);144_clipRect.height = (NSUInteger)((height >= 0) ? height : 0);145_clipType = RECT_CLIP;146}147148- (void)beginShapeClip:(BMTLSDOps *)dstOps context:(MTLContext *)mtlc {149_stencilMaskGenerationInProgress = YES;150_mtlc = mtlc;151if ((dstOps == NULL) || (dstOps->pStencilData == NULL) || (dstOps->pStencilTexture == NULL)) {152J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext_beginShapeClip: stencil render target or stencil texture is NULL");153return;154}155156// Clear the stencil render buffer & stencil texture157@autoreleasepool {158if (dstOps->width <= 0 || dstOps->height <= 0) {159return;160}161162_clipShapeSize = MTLSizeMake(0, 0, 1);163// Use out of bounds origin to correctly calculate shape boundaries164_clipShapeOrigin = MTLOriginMake((NSUInteger) dstOps->width, (NSUInteger) dstOps->height, 0);165_dstOps = dstOps;166}167}168169- (void)endShapeClip:(BMTLSDOps *)dstOps context:(MTLContext *)mtlc {170171if ((dstOps == NULL) || (dstOps->pStencilData == NULL) || (dstOps->pStencilTexture == NULL)) {172J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext_endShapeClip: stencil render target or stencil texture is NULL");173return;174}175176// Complete the rendering to the stencil buffer ------------177[mtlc.encoderManager endEncoder];178179MTLCommandBufferWrapper* cbWrapper = [mtlc pullCommandBufferWrapper];180181id<MTLCommandBuffer> commandBuffer = [cbWrapper getCommandBuffer];182[commandBuffer addCompletedHandler:^(id <MTLCommandBuffer> c) {183[cbWrapper release];184}];185186[commandBuffer commit];187_stencilMaskGenerationInProgress = NO;188_stencilMaskGenerationStarted = NO;189_dstOps = dstOps;190_clipType = SHAPE_CLIP;191_clipReady = NO;192}193194- (void)setMaskGenerationPipelineState:(id<MTLRenderCommandEncoder>)encoder195destWidth:(NSUInteger)dw196destHeight:(NSUInteger)dh197pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage198{199initTemplatePipelineDescriptors();200201// A PipelineState for rendering to a byte-buffered texture that will be used as a stencil202id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:templateStencilPipelineDesc203vertexShaderId:@"vert_stencil"204fragmentShaderId:@"frag_stencil"205stencilNeeded:YES];206[encoder setRenderPipelineState:pipelineState];207208struct FrameUniforms uf; // color is ignored while writing to stencil buffer209memset(&uf, 0, sizeof(uf));210[encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];211212_clipRect.x = 0;213_clipRect.y = 0;214_clipRect.width = dw;215_clipRect.height = dh;216217[encoder setDepthStencilState: _mtlc.stencilManager.genStencilState];218[encoder setStencilReferenceValue:255];219[encoder setScissorRect:_clipRect]; // just for insurance (to reset possible clip from previous drawing)220}221222- (void)setScissorOrStencil:(id<MTLRenderCommandEncoder>)encoder223destWidth:(NSUInteger)dw224destHeight:(NSUInteger)dh225device:(id<MTLDevice>)device226{227if (_clipType == NO_CLIP || _clipType == SHAPE_CLIP) {228_clipRect.x = 0;229_clipRect.y = 0;230_clipRect.width = dw;231_clipRect.height = dh;232}233234// Clamping clip rect to the destination area235MTLScissorRect rect = _clipRect;236237if (rect.x > dw) {238rect.x = dw;239}240241if (rect.y > dh) {242rect.y = dh;243}244245if (rect.x + rect.width > dw) {246rect.width = dw - rect.x;247}248249if (rect.y + rect.height > dh) {250rect.height = dh - rect.y;251}252253[encoder setScissorRect:rect];254if (_clipType == NO_CLIP || _clipType == RECT_CLIP) {255// NOTE: It seems that we can use the same encoder (with disabled stencil test) when mode changes from SHAPE to RECT.256// But [encoder setDepthStencilState:nil] causes crash, so we have to recreate encoder in such case.257// So we can omit [encoder setDepthStencilState:nil] here.258return;259}260261if (_clipType == SHAPE_CLIP) {262// Enable stencil test263[encoder setDepthStencilState:_mtlc.stencilManager.stencilState];264[encoder setStencilReferenceValue:0xFF];265}266}267268- (NSString *)getDescription __unused {269if (_clipType == NO_CLIP) {270return @"NO_CLIP";271}272if (_clipType == RECT_CLIP) {273return [NSString stringWithFormat:@"RECT_CLIP [%lu,%lu - %lux%lu]", _clipRect.x, _clipRect.y, _clipRect.width, _clipRect.height];274}275return [NSString stringWithFormat:@"SHAPE_CLIP"];276}277278- (id<MTLTexture>) stencilTextureRef {279if (_dstOps == NULL) return nil;280281return _dstOps->pStencilTexture;;282}283284- (NSUInteger)shapeX {285return _clipShapeOrigin.x;286}287288- (void)setShapeX:(NSUInteger)shapeX {289_clipShapeOrigin.x = shapeX;290}291292- (NSUInteger)shapeY {293return _clipShapeOrigin.y;294}295296- (void)setShapeY:(NSUInteger)shapeY {297_clipShapeOrigin.y = shapeY;298}299300- (NSUInteger)shapeWidth {301return _clipShapeSize.width;302}303304- (void)setShapeWidth:(NSUInteger)shapeWidth {305_clipShapeSize.width = shapeWidth;306}307308- (NSUInteger)shapeHeight {309return _clipShapeSize.height;310}311312- (void)setShapeHeight:(NSUInteger)shapeHeight {313_clipShapeSize.height = shapeHeight;314}315316317@end318319320