blob: 3919d97c53ad98df8a8cb75296c1411e2392e61b [file]
/*
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "MTLClip.h"
#include "MTLContext.h"
#include "MTLStencilManager.h"
#include "common.h"
static MTLRenderPipelineDescriptor * templateStencilPipelineDesc = nil;
static void initTemplatePipelineDescriptors() {
if (templateStencilPipelineDesc != nil)
return;
MTLVertexDescriptor *vertDesc = [[MTLVertexDescriptor new] autorelease];
vertDesc.attributes[VertexAttributePosition].format = MTLVertexFormatFloat2;
vertDesc.attributes[VertexAttributePosition].offset = 0;
vertDesc.attributes[VertexAttributePosition].bufferIndex = MeshVertexBuffer;
vertDesc.layouts[MeshVertexBuffer].stride = sizeof(struct Vertex);
vertDesc.layouts[MeshVertexBuffer].stepRate = 1;
vertDesc.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;
templateStencilPipelineDesc = [MTLRenderPipelineDescriptor new];
templateStencilPipelineDesc.sampleCount = 1;
templateStencilPipelineDesc.vertexDescriptor = vertDesc;
templateStencilPipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatR8Uint; // A byte buffer format
templateStencilPipelineDesc.stencilAttachmentPixelFormat = MTLPixelFormatStencil8;
templateStencilPipelineDesc.label = @"template_stencil";
}
@implementation MTLClip {
jint _clipType;
MTLScissorRect _clipRect;
MTLContext* _mtlc;
BMTLSDOps* _dstOps;
BOOL _stencilMaskGenerationInProgress;
BOOL _stencilMaskGenerationStarted;
MTLOrigin _clipShapeOrigin;
MTLSize _clipShapeSize;
}
@synthesize dstOps = _dstOps;
- (id)init {
self = [super init];
if (self) {
_clipType = NO_CLIP;
_mtlc = nil;
_dstOps = NULL;
_stencilMaskGenerationInProgress = NO;
_stencilMaskGenerationStarted = NO;
}
return self;
}
- (BOOL)isEqual:(MTLClip *)other {
if (self == other)
return YES;
if (_stencilMaskGenerationInProgress == JNI_TRUE)
return other->_stencilMaskGenerationInProgress == JNI_TRUE;
if (_clipType != other->_clipType)
return NO;
if (_clipType == NO_CLIP)
return YES;
if (_clipType == RECT_CLIP) {
return _clipRect.x == other->_clipRect.x && _clipRect.y == other->_clipRect.y
&& _clipRect.width == other->_clipRect.width && _clipRect.height == other->_clipRect.height;
}
// NOTE: can compare stencil-data pointers here
return YES;
}
- (BOOL)isShape {
return _clipType == SHAPE_CLIP;
}
- (BOOL)isRect __unused {
return _clipType == RECT_CLIP;
}
- (const MTLScissorRect * _Nullable) getRect {
return _clipType == RECT_CLIP ? &_clipRect : NULL;
}
- (void)copyFrom:(MTLClip *)other {
_clipType = other->_clipType;
_stencilMaskGenerationInProgress = other->_stencilMaskGenerationInProgress;
_dstOps = other->_dstOps;
_mtlc = other->_mtlc;
if (other->_clipType == RECT_CLIP) {
_clipRect = other->_clipRect;
}
}
- (void)reset {
_clipType = NO_CLIP;
_stencilMaskGenerationInProgress = JNI_FALSE;
}
- (void)setClipRectX1:(jint)x1 Y1:(jint)y1 X2:(jint)x2 Y2:(jint)y2 {
if (_clipType == SHAPE_CLIP) {
_dstOps = NULL;
}
if (x1 >= x2 || y1 >= y2) {
J2dTraceLn4(J2D_TRACE_ERROR, "MTLClip.setClipRect: invalid rect: x1=%d y1=%d x2=%d y2=%d", x1, y1, x2, y2);
_clipType = NO_CLIP;
}
const jint width = x2 - x1;
const jint height = y2 - y1;
J2dTraceLn4(J2D_TRACE_INFO, "MTLClip.setClipRect: x=%d y=%d w=%d h=%d", x1, y1, width, height);
_clipRect.x = (NSUInteger)((x1 >= 0) ? x1 : 0);
_clipRect.y = (NSUInteger)((y1 >= 0) ? y1 : 0);
_clipRect.width = (NSUInteger)((width >= 0) ? width : 0);
_clipRect.height = (NSUInteger)((height >= 0) ? height : 0);
_clipType = RECT_CLIP;
}
- (void)beginShapeClip:(BMTLSDOps *)dstOps context:(MTLContext *)mtlc {
_stencilMaskGenerationInProgress = YES;
_mtlc = mtlc;
if ((dstOps == NULL) || (dstOps->pStencilData == NULL) || (dstOps->pStencilTexture == NULL)) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext_beginShapeClip: stencil render target or stencil texture is NULL");
return;
}
// Clear the stencil render buffer & stencil texture
@autoreleasepool {
if (dstOps->width <= 0 || dstOps->height <= 0) {
return;
}
_clipShapeSize = MTLSizeMake(0, 0, 1);
// Use out of bounds origin to correctly calculate shape boundaries
_clipShapeOrigin = MTLOriginMake((NSUInteger) dstOps->width, (NSUInteger) dstOps->height, 0);
_dstOps = dstOps;
}
}
- (void)endShapeClip:(BMTLSDOps *)dstOps context:(MTLContext *)mtlc {
if ((dstOps == NULL) || (dstOps->pStencilData == NULL) || (dstOps->pStencilTexture == NULL)) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext_endShapeClip: stencil render target or stencil texture is NULL");
return;
}
// Complete the rendering to the stencil buffer ------------
[mtlc.encoderManager endEncoder];
MTLCommandBufferWrapper* cbWrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandBuffer = [cbWrapper getCommandBuffer];
[commandBuffer addCompletedHandler:^(id <MTLCommandBuffer> c) {
[cbWrapper release];
}];
[commandBuffer commit];
_stencilMaskGenerationInProgress = NO;
_stencilMaskGenerationStarted = NO;
_dstOps = dstOps;
_clipType = SHAPE_CLIP;
}
- (void)setMaskGenerationPipelineState:(id<MTLRenderCommandEncoder>)encoder
destWidth:(NSUInteger)dw
destHeight:(NSUInteger)dh
pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
{
initTemplatePipelineDescriptors();
// A PipelineState for rendering to a byte-buffered texture that will be used as a stencil
id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:templateStencilPipelineDesc
vertexShaderId:@"vert_stencil"
fragmentShaderId:@"frag_stencil"
stencilNeeded:YES];
[encoder setRenderPipelineState:pipelineState];
struct FrameUniforms uf; // color is ignored while writing to stencil buffer
memset(&uf, 0, sizeof(uf));
[encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
_clipRect.x = 0;
_clipRect.y = 0;
_clipRect.width = dw;
_clipRect.height = dh;
[encoder setDepthStencilState: _mtlc.stencilManager.genStencilState];
[encoder setStencilReferenceValue:255];
[encoder setScissorRect:_clipRect]; // just for insurance (to reset possible clip from previous drawing)
}
- (void)setScissorOrStencil:(id<MTLRenderCommandEncoder>)encoder
destWidth:(NSUInteger)dw
destHeight:(NSUInteger)dh
device:(id<MTLDevice>)device
{
if (_clipType == NO_CLIP || _clipType == SHAPE_CLIP) {
_clipRect.x = 0;
_clipRect.y = 0;
_clipRect.width = dw;
_clipRect.height = dh;
}
// Clamping clip rect to the destination area
MTLScissorRect rect = _clipRect;
if (rect.x > dw) {
rect.x = dw;
}
if (rect.y > dh) {
rect.y = dh;
}
if (rect.x + rect.width > dw) {
rect.width = dw - rect.x;
}
if (rect.y + rect.height > dh) {
rect.height = dh - rect.y;
}
[encoder setScissorRect:rect];
if (_clipType == NO_CLIP || _clipType == RECT_CLIP) {
// NOTE: It seems that we can use the same encoder (with disabled stencil test) when mode changes from SHAPE to RECT.
// But [encoder setDepthStencilState:nil] causes crash, so we have to recreate encoder in such case.
// So we can omit [encoder setDepthStencilState:nil] here.
return;
}
if (_clipType == SHAPE_CLIP) {
// Enable stencil test
[encoder setDepthStencilState:_mtlc.stencilManager.stencilState];
[encoder setStencilReferenceValue:0xFF];
}
}
- (NSString *)getDescription __unused {
if (_clipType == NO_CLIP) {
return @"NO_CLIP";
}
if (_clipType == RECT_CLIP) {
return [NSString stringWithFormat:@"RECT_CLIP [%lu,%lu - %lux%lu]", _clipRect.x, _clipRect.y, _clipRect.width, _clipRect.height];
}
return [NSString stringWithFormat:@"SHAPE_CLIP"];
}
- (id<MTLTexture>) stencilTextureRef {
if (_dstOps == NULL) return nil;
return _dstOps->pStencilTexture;
}
- (NSUInteger)shapeX {
return _clipShapeOrigin.x;
}
- (void)setShapeX:(NSUInteger)shapeX {
_clipShapeOrigin.x = shapeX;
}
- (NSUInteger)shapeY {
return _clipShapeOrigin.y;
}
- (void)setShapeY:(NSUInteger)shapeY {
_clipShapeOrigin.y = shapeY;
}
- (NSUInteger)shapeWidth {
return _clipShapeSize.width;
}
- (void)setShapeWidth:(NSUInteger)shapeWidth {
_clipShapeSize.width = shapeWidth;
}
- (NSUInteger)shapeHeight {
return _clipShapeSize.height;
}
- (void)setShapeHeight:(NSUInteger)shapeHeight {
_clipShapeSize.height = shapeHeight;
}
@end