changeset 57652:f6f2dd18c0f6

8234813: MTLRenderer_FillAAParallelogram is not implemented Use multisampling rendering for AA parallelogram rendering
author avu
date Thu, 23 Jan 2020 17:24:18 +0300
parents 57ff3fcb5045
children c7b41a9b3ca6
files src/demo/share/java2d/RenderPerfTest/src/renderperf/RenderPerfTest.java src/java.desktop/macosx/native/libawt_lwawt/awt/shaders.metal src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/EncoderManager.h src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/EncoderManager.m src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLPaints.m src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLPipelineStatesStorage.h src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLPipelineStatesStorage.m src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLRenderer.m src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLTexturePool.h src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLTexurePool.m src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLUtils.h
diffstat 11 files changed, 252 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/src/demo/share/java2d/RenderPerfTest/src/renderperf/RenderPerfTest.java	Wed Jan 22 22:52:18 2020 +0300
+++ b/src/demo/share/java2d/RenderPerfTest/src/renderperf/RenderPerfTest.java	Thu Jan 23 17:24:18 2020 +0300
@@ -51,8 +51,6 @@
             ignoredTests.add("testWhiteTextBubblesNoAA");
             ignoredTests.add("testWhiteTextBubblesLCD");
             ignoredTests.add("testWhiteTextBubblesGray");
-            ignoredTests.add("testFlatBoxBubblesAA");
-            ignoredTests.add("testFlatBoxRotBubblesAA");
             ignoredTests.add("testLinGradOvalRotBubblesAA");
             ignoredTests.add("testWiredBoxBubblesAA");
             ignoredTests.add("testLinesAA");
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/shaders.metal	Wed Jan 22 22:52:18 2020 +0300
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/shaders.metal	Thu Jan 23 17:24:18 2020 +0300
@@ -118,6 +118,18 @@
     return half4(pixelColor.r, pixelColor.g, pixelColor.b, srcA);
 }
 
+fragment half4 aa_frag_txt(
+        TxtShaderInOut vert [[stage_in]],
+texture2d<float, access::sample> renderTexture [[texture(0)]],
+constant TxtFrameUniforms& uniforms [[buffer(1)]]
+)
+{
+    constexpr sampler textureSampler (mag_filter::linear, min_filter::linear);
+    float pixelColor = renderTexture.sample(textureSampler, vert.texCoords).x;
+    float4 c = pixelColor*uniforms.color;
+    return half4(c.r, c.g, c.b, pixelColor);
+}
+
 fragment half4 frag_grad(GradShaderInOut in [[stage_in]],
                          constant GradFrameUniforms& uniforms [[buffer(0)]]) {
     float3 v = float3(in.position.x, in.position.y, 1);
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/EncoderManager.h	Wed Jan 22 22:52:18 2020 +0300
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/EncoderManager.h	Thu Jan 23 17:24:18 2020 +0300
@@ -30,6 +30,8 @@
 // returns encoder that renders/fills geometry with current paint and composite
 - (id<MTLRenderCommandEncoder> _Nonnull)getRenderEncoder:(const BMTLSDOps * _Nonnull)dstOps;
 
+- (id<MTLRenderCommandEncoder> _Nonnull)getAARenderEncoder:(id<MTLTexture>)dstTxt;
+
 - (id<MTLRenderCommandEncoder> _Nonnull)getRenderEncoder:(id<MTLTexture> _Nonnull)dest
                                              isDstOpaque:(bool)isOpaque;
 
@@ -42,10 +44,16 @@
                                       isSrcOpaque:(bool)isSrcOpaque
                                       isDstOpaque:(bool)isDstOpaque;
 
+- (id<MTLRenderCommandEncoder> _Nonnull)getTextureEncoder:(id<MTLTexture> _Nonnull)dest
+                                              isSrcOpaque:(bool)isSrcOpaque
+                                              isDstOpaque:(bool)isDstOpaque
+                                                     isAA:(jboolean)isAA;
+
 // Base method to obtain any MTLRenderCommandEncoder
 - (id<MTLRenderCommandEncoder> _Nonnull)getEncoder:(id<MTLTexture> _Nonnull)dest
                                   isOpaque:(jboolean)isOpaque
                                  isTexture:(jboolean)isTexture
+                                      isAA:(jboolean)isAA
                                   srcFlags:(const SurfaceRasterFlags * _Nullable)srcFlags;
 
 - (id<MTLBlitCommandEncoder> _Nonnull)createBlitEncoder;
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/EncoderManager.m	Wed Jan 22 22:52:18 2020 +0300
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/EncoderManager.m	Thu Jan 23 17:24:18 2020 +0300
@@ -22,10 +22,12 @@
                 paint:(MTLPaint *)paint
             composite:(MTLComposite *)composite
             isTexture:(jboolean)isTexture
+                 isAA:(jboolean)isAA
              srcFlags:(const SurfaceRasterFlags * _Nullable)srcFlags
                  clip:(MTLClip *)clip
             transform:(MTLTransform *)transform
           forceUpdate:(jboolean)forceUpdate;
+@property jboolean aa;
 @end
 
 @implementation EncoderStates {
@@ -49,6 +51,7 @@
 
     // If true, indicates that encoder is used for texture drawing (user must do [encoder setFragmentTexture:] before drawing)
     jboolean _isTexture;
+    jboolean _isAA;
 
     // Clip rect or stencil
     MTLClip * _clip;
@@ -56,6 +59,7 @@
     // Transform (affects transformation inside vertex shader)
     MTLTransform * _transform;
 }
+@synthesize aa = _isAA;
 
 - (id)init {
     self = [super init];
@@ -85,11 +89,12 @@
 - (void)reset:(id<MTLTexture>)destination
            isDstOpaque:(jboolean)isDstOpaque
     isDstPremultiplied:(jboolean)isDstPremultiplied
+                  isAA:(jboolean)isAA
 {
     _destination = destination;
     _dstFlags.isOpaque = isDstOpaque;
     _dstFlags.isPremultiplied = isDstPremultiplied;
-
+    _isAA = isAA;
     // NOTE: probably it's better to invalidate/reset all cached states now
 }
 
@@ -97,6 +102,7 @@
                 paint:(MTLPaint *)paint
             composite:(MTLComposite *)composite
             isTexture:(jboolean)isTexture
+                 isAA:(jboolean)isAA
              srcFlags:(const SurfaceRasterFlags * _Nullable)srcFlags
                  clip:(MTLClip *)clip
             transform:(MTLTransform *)transform
@@ -123,6 +129,7 @@
                     composite:composite
                 isStencilUsed:[clip isShape]
                     isTexture:isTexture
+                         isAA:isAA
                      srcFlags:srcFlags
                   forceUpdate:forceUpdate];
     [self updateTransform:encoder transform:transform forceUpdate:forceUpdate];
@@ -139,6 +146,7 @@
                   composite:(MTLComposite *)composite
               isStencilUsed:(jboolean)isStencilUsed
                   isTexture:(jboolean)isTexture
+                       isAA:(jboolean)isAA
                    srcFlags:(const SurfaceRasterFlags * _Nullable)srcFlags
                 forceUpdate:(jboolean)forceUpdate
 {
@@ -149,18 +157,21 @@
         && [_paint isEqual:paint]
         && [_composite isEqual:composite]
         && _isTexture == isTexture
+        && _isAA == isAA
         && _srcFlags.isOpaque == srcFlags->isOpaque && _srcFlags.isPremultiplied == srcFlags->isPremultiplied)
         return;
 
     [_paint copyFrom:paint];
     [_composite copyFrom:composite];
     _isTexture = isTexture;
+    _isAA = isAA;
     _srcFlags = *srcFlags;
 
     [paint setPipelineState:encoder
                   composite:_composite
               isStencilUsed:isStencilUsed
                   isTexture:_isTexture
+                       isAA:isAA
                    srcFlags:&_srcFlags
                    dstFlags:&_dstFlags
        pipelineStateStorage:_pipelineStateStorage];
@@ -234,11 +245,16 @@
     return [self getRenderEncoder:dstOps->pTexture isDstOpaque:dstOps->isOpaque];
 }
 
+- (id<MTLRenderCommandEncoder>) getAARenderEncoder:(id<MTLTexture>)dstTxt
+{
+    return [self getEncoder:dstTxt isOpaque:JNI_TRUE isTexture:JNI_FALSE isAA:JNI_TRUE srcFlags:NULL];
+
+}
 
 - (id<MTLRenderCommandEncoder> _Nonnull)getRenderEncoder:(id<MTLTexture> _Nonnull)dest
                                              isDstOpaque:(bool)isOpaque
 {
-    return [self getEncoder:dest isOpaque:isOpaque isTexture:JNI_FALSE srcFlags:NULL];
+    return [self getEncoder:dest isOpaque:isOpaque isTexture:JNI_FALSE isAA:JNI_FALSE srcFlags:NULL];
 }
 
 - (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(const BMTLSDOps * _Nonnull)dstOps
@@ -250,25 +266,35 @@
 - (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest
                                       isSrcOpaque:(bool)isSrcOpaque
                                       isDstOpaque:(bool)isDstOpaque
+                                             isAA:(jboolean)isAA
 {
     SurfaceRasterFlags srcFlags = { isSrcOpaque, JNI_TRUE };
     return [self getEncoder:dest
                    isOpaque:isDstOpaque
                   isTexture:JNI_TRUE
+                       isAA:isAA
                    srcFlags:&srcFlags];
 }
 
+- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest
+                                               isSrcOpaque:(bool)isSrcOpaque
+                                               isDstOpaque:(bool)isDstOpaque
+{
+    return [self getTextureEncoder:dest isSrcOpaque:isSrcOpaque isDstOpaque:isDstOpaque isAA:JNI_FALSE];
+}
+
 - (id<MTLRenderCommandEncoder> _Nonnull) getEncoder:(id<MTLTexture> _Nonnull)dest
                                   isOpaque:(jboolean)isOpaque
                                  isTexture:(jboolean)isTexture
+                                      isAA:(jboolean)isAA
                                   srcFlags:(const SurfaceRasterFlags * _Nullable)srcFlags
 {
     //
     // 1. check whether it's necessary to call endEncoder
     //
     jboolean needEnd = JNI_FALSE;
-    if (_encoder != nil) {
-        if (_destination != dest) {
+    if (_encoder != nil ) {
+        if (_destination != dest || isAA != _encoderStates.aa) {
             J2dTraceLn2(J2D_TRACE_VERBOSE, "end common encoder because of dest change: %p -> %p", _destination, dest);
             needEnd = JNI_TRUE;
         } else if ((_useStencil == NO) != ([_mtlc.clip isShape] == NO)) {
@@ -300,9 +326,21 @@
         MTLCommandBufferWrapper * cbw = [_mtlc getCommandBufferWrapper];
         MTLRenderPassDescriptor * rpd = [MTLRenderPassDescriptor renderPassDescriptor];
         MTLRenderPassColorAttachmentDescriptor * ca = rpd.colorAttachments[0];
-        ca.texture = dest;
-        ca.loadAction = MTLLoadActionLoad;
-        ca.storeAction = MTLStoreActionStore;
+        if (isAA && !isTexture) {
+            MTLTexturePoolItem *ti = [_mtlc.texturePool getTexture:dest.width
+                                      height:dest.height format:dest.pixelFormat isMultiSample:YES];
+            [cbw registerPooledTexture: ti];
+            [ti release];
+            ca.texture = ti.texture;
+            ca.resolveTexture = dest;
+            ca.clearColor = MTLClearColorMake(0.0f, 0.0f, 0.0f, 1.0f);
+            ca.loadAction = MTLLoadActionClear;
+            ca.storeAction =  MTLStoreActionMultisampleResolve;
+        } else {
+            ca.texture = dest;
+            ca.loadAction = MTLLoadActionLoad;
+            ca.storeAction = MTLStoreActionStore;
+        }
 
         if (_useStencil) {
             // If you enable stencil testing or stencil writing, the MTLRenderPassDescriptor must include a stencil attachment.
@@ -317,7 +355,8 @@
 
         [_encoderStates reset:dest
                   isDstOpaque:isOpaque
-           isDstPremultiplied:YES];
+           isDstPremultiplied:YES
+                         isAA:isAA];
     }
 
     //
@@ -327,6 +366,7 @@
                             paint:_mtlc.paint
                         composite:_mtlc.composite
                         isTexture:isTexture
+                             isAA:isAA
                          srcFlags:srcFlags
                              clip:_mtlc.clip
                         transform:_mtlc.transform
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLPaints.m	Wed Jan 22 22:52:18 2020 +0300
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLPaints.m	Thu Jan 23 17:24:18 2020 +0300
@@ -44,6 +44,7 @@
 
 static MTLRenderPipelineDescriptor * templateRenderPipelineDesc = nil;
 static MTLRenderPipelineDescriptor * templateTexturePipelineDesc = nil;
+static MTLRenderPipelineDescriptor * templateAATexturePipelineDesc = nil;
 
 static void initTemplatePipelineDescriptors() {
     if (templateRenderPipelineDesc != nil && templateTexturePipelineDesc != nil)
@@ -71,6 +72,19 @@
     templateTexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepRate = 1;
     templateTexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;
     templateTexturePipelineDesc.label = @"template_texture";
+
+    templateAATexturePipelineDesc = [[MTLRenderPipelineDescriptor new] autorelease];
+    templateAATexturePipelineDesc.sampleCount = 1;
+    templateAATexturePipelineDesc.vertexDescriptor = vertDesc;
+    templateAATexturePipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatR8Unorm;
+    templateAATexturePipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].format = MTLVertexFormatFloat2;
+    templateAATexturePipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].offset = 2*sizeof(float);
+    templateAATexturePipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].bufferIndex = MeshVertexBuffer;
+    templateAATexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stride = sizeof(struct TxtVertex);
+    templateAATexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepRate = 1;
+    templateAATexturePipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;
+    templateAATexturePipelineDesc.label = @"template_aa_texture";
+
 }
 
 @implementation MTLPaint {
@@ -235,6 +249,7 @@
                composite:(MTLComposite *)composite
            isStencilUsed:(jboolean)isStencilUsed
                isTexture:(jboolean)isTexture
+                    isAA:(jboolean)isAA
                 srcFlags:(const SurfaceRasterFlags *)srcFlags
                 dstFlags:(const SurfaceRasterFlags *)dstFlags
     pipelineStateStorage:(MTLPipelineStatesStorage *)pipelineStateStorage
@@ -245,14 +260,26 @@
 
     id<MTLRenderPipelineState> pipelineState = nil;
     if (isTexture) {
-        pipelineState = [pipelineStateStorage getPipelineState:templateTexturePipelineDesc
-                                                vertexShaderId:@"vert_txt"
-                                              fragmentShaderId:@"frag_txt"
-                                                 compositeRule:[composite getRule]
-                                                      srcFlags:srcFlags
-                                                      dstFlags:dstFlags
-                                                 stencilNeeded:stencil];
 
+        if (isAA) {
+            pipelineState = [pipelineStateStorage getPipelineState:templateAATexturePipelineDesc
+                                                    vertexShaderId:@"vert_txt"
+                                                  fragmentShaderId:@"aa_frag_txt"
+                                                     compositeRule:[composite getRule]
+                                                              isAA:JNI_FALSE
+                                                          srcFlags:srcFlags
+                                                          dstFlags:dstFlags
+                                                     stencilNeeded:stencil];
+        } else {
+            pipelineState = [pipelineStateStorage getPipelineState:templateTexturePipelineDesc
+                                                    vertexShaderId:@"vert_txt"
+                                                  fragmentShaderId:@"frag_txt"
+                                                     compositeRule:[composite getRule]
+                                                              isAA:JNI_FALSE
+                                                          srcFlags:srcFlags
+                                                          dstFlags:dstFlags
+                                                     stencilNeeded:stencil];
+        }
         if (_paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) {
             struct TxtFrameUniforms uf = {RGBA_TO_V4(_color), 1, srcFlags->isOpaque, dstFlags->isOpaque };
             [encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
@@ -266,17 +293,24 @@
                                                     vertexShaderId:@"vert_col"
                                                   fragmentShaderId:@"frag_col"
                                                      compositeRule:[composite getRule]
+                                                              isAA:isAA
                                                           srcFlags:srcFlags
                                                           dstFlags:dstFlags
                                                      stencilNeeded:stencil];
 
-            struct FrameUniforms uf = {RGBA_TO_V4(_color)};
-            [encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
+            if (isAA) {
+                struct FrameUniforms uf = {1.0f, 1.0f, 1.0f, 1.0};
+                [encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
+            } else {
+                struct FrameUniforms uf = {RGBA_TO_V4(_color)};
+                [encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
+            }
         } else if (_paintState == sun_java2d_SunGraphics2D_PAINT_GRADIENT) {
             pipelineState = [pipelineStateStorage getPipelineState:templateRenderPipelineDesc
                                                     vertexShaderId:@"vert_grad"
                                                   fragmentShaderId:@"frag_grad"
                                                      compositeRule:[composite getRule]
+                                                              isAA:isAA
                                                           srcFlags:srcFlags
                                                           dstFlags:dstFlags
                                                      stencilNeeded:stencil];
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLPipelineStatesStorage.h	Wed Jan 22 22:52:18 2020 +0300
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLPipelineStatesStorage.h	Thu Jan 23 17:24:18 2020 +0300
@@ -1,7 +1,7 @@
 #ifndef MTLPipelineStatesStorage_h_Included
 #define MTLPipelineStatesStorage_h_Included
 
-#import <Metal/Metal.h>
+#import "MTLUtils.h"
 #include "MTLSurfaceDataBase.h"
 
 /**
@@ -45,6 +45,15 @@
                                        dstFlags:(const SurfaceRasterFlags * )dstFlags
                                   stencilNeeded:(bool)stencilNeeded;
 
+- (id<MTLRenderPipelineState>) getPipelineState:(MTLRenderPipelineDescriptor *) pipelineDescriptor
+                                 vertexShaderId:(NSString *)vertexShaderId
+                               fragmentShaderId:(NSString *)fragmentShaderId
+                                  compositeRule:(jint)compositeRule
+                                           isAA:(jboolean)isAA
+                                       srcFlags:(const SurfaceRasterFlags *)srcFlags
+                                       dstFlags:(const SurfaceRasterFlags *)dstFlags
+                                  stencilNeeded:(bool)stencilNeeded;
+
 - (id<MTLFunction>) getShader:(NSString *)name;
 @end
 
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLPipelineStatesStorage.m	Wed Jan 22 22:52:18 2020 +0300
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLPipelineStatesStorage.m	Thu Jan 23 17:24:18 2020 +0300
@@ -79,12 +79,31 @@
                     stencilNeeded:stencilNeeded];
 }
 
+- (id<MTLRenderPipelineState>) getPipelineState:(MTLRenderPipelineDescriptor *) pipelineDescriptor
+                                 vertexShaderId:(NSString *)vertexShaderId
+                               fragmentShaderId:(NSString *)fragmentShaderId
+                                  compositeRule:(jint)compositeRule
+                                       srcFlags:(const SurfaceRasterFlags *)srcFlags
+                                       dstFlags:(const SurfaceRasterFlags *)dstFlags
+                                  stencilNeeded:(bool)stencilNeeded;
+{
+    return [self getPipelineState:pipelineDescriptor
+                   vertexShaderId:vertexShaderId
+                 fragmentShaderId:fragmentShaderId
+                    compositeRule:compositeRule
+                             isAA:JNI_FALSE
+                         srcFlags:srcFlags
+                         dstFlags:dstFlags
+            stencilNeeded:stencilNeeded];
+}
+
 // Base method to obtain MTLRenderPipelineState.
 // NOTE: parameters compositeRule, srcFlags, dstFlags are used to set MTLRenderPipelineColorAttachmentDescriptor multipliers
 - (id<MTLRenderPipelineState>) getPipelineState:(MTLRenderPipelineDescriptor *) pipelineDescriptor
                                  vertexShaderId:(NSString *)vertexShaderId
                                fragmentShaderId:(NSString *)fragmentShaderId
                                   compositeRule:(jint)compositeRule
+                                           isAA:(jboolean)isAA
                                        srcFlags:(const SurfaceRasterFlags *)srcFlags
                                        dstFlags:(const SurfaceRasterFlags *)dstFlags
                                   stencilNeeded:(bool)stencilNeeded;
@@ -112,7 +131,10 @@
         subIndex |= 1 << 4;
     }
 
-    int index = compositeRule*32 + subIndex;
+    if (isAA) {
+        subIndex |= 1 << 5;
+    }
+    int index = compositeRule*64 + subIndex;
 
     NSPointerArray * subStates = [self getSubStates:vertexShaderId fragmentShader:fragmentShaderId];
     while (index >= [subStates count]) {
@@ -139,6 +161,17 @@
                 pipelineDesc.stencilAttachmentPixelFormat = MTLPixelFormatStencil8;
             }
 
+            if (isAA) {
+                pipelineDesc.sampleCount = MTLAASampleCount;
+                pipelineDesc.colorAttachments[0].rgbBlendOperation =   MTLBlendOperationAdd;
+                pipelineDesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
+                pipelineDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
+                pipelineDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
+                pipelineDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
+                pipelineDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
+                pipelineDesc.colorAttachments[0].blendingEnabled = YES;
+            }
+
             NSError *error = nil;
             result = [[self.device newRenderPipelineStateWithDescriptor:pipelineDesc error:&error] autorelease];
             if (result == nil) {
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLRenderer.m	Wed Jan 22 22:52:18 2020 +0300
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLRenderer.m	Thu Jan 23 17:24:18 2020 +0300
@@ -645,22 +645,83 @@
                                 jfloat dx21, jfloat dy21,
                                 jfloat dx12, jfloat dy12)
 {
-    //TODO
-    DECLARE_MATRIX(om);
-    // parameters for parallelogram bounding box
-    jfloat bx11, by11, bx22, by22;
-    // parameters for uv texture coordinates of parallelogram corners
-    jfloat u11, v11, u12, v12, u21, v21, u22, v22;
+    if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
+        J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillParallelogram: current dest is null");
+        return;
+    }
 
-    J2dTraceLn6(J2D_TRACE_ERROR,
-                "MTLRenderer_FillAAParallelogram -- :TODO"
+    id<MTLTexture> dest = dstOps->pTexture;
+    J2dTraceLn7(J2D_TRACE_INFO,
+                "MTLRenderer_FillAAParallelogram "
                 "(x=%6.2f y=%6.2f "
                 "dx1=%6.2f dy1=%6.2f "
-                "dx2=%6.2f dy2=%6.2f)",
+                "dx2=%6.2f dy2=%6.2f dst tex=%p)",
                 fx11, fy11,
                 dx21, dy21,
-                dx12, dy12);
+                dx12, dy12, dest);
 
+    struct Vertex verts[QUAD_VERTEX_COUNT] = {
+            { {fx11, fy11}},
+            { {fx11+dx21, fy11+dy21}},
+            { {fx11+dx12, fy11+dy12}},
+            { {fx11 + dx21 + dx12, fy11+ dy21 + dy12}
+            }};
+
+    jfloat x0 = verts[0].position[0];
+    jfloat y0 = verts[0].position[1];
+    jfloat x1 = verts[0].position[0];
+    jfloat y1 = verts[0].position[1];
+    for (int i = 1; i < 4; i++) {
+        jfloat x = verts[i].position[0];
+        jfloat y = verts[i].position[1];
+        if (x0 > x) x0 = x;
+        if (y0 > y) y0 = y;
+        if (x1 < x) x1 = x;
+        if (y1 < y) y1 = y;
+    }
+
+
+    jint ox = (jint)floorf(x0);
+    jint oy = (jint)floorf(y0);
+    jint w = (jint)ceilf(x1 - floorf(x0));
+    jint h = (jint)ceilf(y1 - floorf(y0));
+    for (int i = 0; i < 4; i++) {
+        verts[i].position[0] -= ox;
+        verts[i].position[1] -= oy;
+    }
+    id<MTLTexture> dstTxt = dstOps->pTexture;
+    MTLCommandBufferWrapper * cbw = [mtlc getCommandBufferWrapper];
+    MTLTexturePoolItem *ti = [mtlc.texturePool getTexture:w
+                                                   height:h format:MTLPixelFormatR8Unorm];
+    [cbw registerPooledTexture: ti];
+    [ti release];
+    // Encode render command.
+    id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getAARenderEncoder:ti.texture];
+
+    if (mtlEncoder == nil) {
+        return;
+    }
+
+    [mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
+    [mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount: QUAD_VERTEX_COUNT];
+
+    mtlEncoder = [mtlc.encoderManager getTextureEncoder:dstTxt
+                                            isSrcOpaque:JNI_FALSE
+                                            isDstOpaque:JNI_TRUE
+                                                   isAA:JNI_TRUE];
+
+    struct TxtVertex quadTxVerticesBuffer[] = {
+            {{ox, oy}, {0, 0}},
+            {{ox, oy + h}, {0, 1}},
+            {{ox + w, oy},{1, 0}},
+            {{ox, oy + h},{0, 1}},
+            {{ox + w, oy + h}, {1, 1}},
+            {{ox + w, oy}, {1, 0}}
+    };
+
+    [mtlEncoder setVertexBytes:quadTxVerticesBuffer length:sizeof(quadTxVerticesBuffer) atIndex:MeshVertexBuffer];
+    [mtlEncoder setFragmentTexture:ti.texture atIndex: 0];
+    [mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];
 }
 
 void
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLTexturePool.h	Wed Jan 22 22:52:18 2020 +0300
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLTexturePool.h	Thu Jan 23 17:24:18 2020 +0300
@@ -1,11 +1,12 @@
 #ifndef MTLTexturePool_h_Included
 #define MTLTexturePool_h_Included
-#import <Metal/Metal.h>
+#import "MTLUtils.h"
 
 @interface MTLTexturePoolItem : NSObject
 @property (readwrite, retain) id<MTLTexture> texture;
 @property (readwrite) bool isBusy;
 @property (readwrite, retain) NSDate * lastUsed;
+@property (readwrite) bool isMultiSample;
 
 - (id) initWithTexture:(id<MTLTexture>)tex;
 @end
@@ -22,6 +23,8 @@
 
 - (id) initWithDevice:(id<MTLDevice>)device;
 - (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format;
+- (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format
+                          isMultiSample:(bool)isMultiSample;
 @end
 
 #endif /* MTLTexturePool_h_Included */
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLTexurePool.m	Wed Jan 22 22:52:18 2020 +0300
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLTexurePool.m	Thu Jan 23 17:24:18 2020 +0300
@@ -10,7 +10,7 @@
 
 @implementation MTLTexturePoolItem
 
-@synthesize texture, isBusy, lastUsed;
+@synthesize texture, isBusy, lastUsed, isMultiSample;
 
 - (id) initWithTexture:(id<MTLTexture>)tex {
     self = [super init];
@@ -88,6 +88,12 @@
 
 // NOTE: called from RQ-thread (on blit operations)
 - (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format {
+    return [self getTexture:width height:height format:format isMultiSample:NO];
+}
+
+// NOTE: called from RQ-thread (on blit operations)
+- (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format
+                          isMultiSample:(bool)isMultiSample {
     @autoreleasepool {
         // 1. clean pool if necessary
         const int requestedPixels = width*height;
@@ -139,7 +145,8 @@
                 int minDeltaAreaIndex = -1;
                 for (int c = 0; c < count; ++c) {
                     MTLTexturePoolItem *tpi = [cell objectAtIndex:c];
-                    if (tpi == nil || tpi.isBusy || tpi.texture.pixelFormat != format) { // TODO: use swizzle when formats are not equal
+                    if (tpi == nil || tpi.isBusy || tpi.texture.pixelFormat != format
+                        || tpi.isMultiSample != isMultiSample) { // TODO: use swizzle when formats are not equal
                         continue;
                     }
                     if (tpi.texture.width < width || tpi.texture.height < height) {
@@ -166,7 +173,17 @@
         }
 
         if (minDeltaTpi == NULL) {
-            MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format width:width height:height mipmapped:NO];
+            MTLTextureDescriptor *textureDescriptor =
+                    [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format
+                                                                       width:(NSUInteger) width
+                                                                      height:(NSUInteger) height
+                                                                   mipmapped:NO];
+            if (isMultiSample) {
+                textureDescriptor.textureType = MTLTextureType2DMultisample;
+                textureDescriptor.sampleCount = MTLAASampleCount;
+                textureDescriptor.storageMode = MTLStorageModePrivate;
+            }
+
             id <MTLTexture> tex = [[self.device newTextureWithDescriptor:textureDescriptor] autorelease];
             minDeltaTpi = [[[MTLTexturePoolItem alloc] initWithTexture:tex] autorelease];
             NSMutableArray * cell = _cells[cellY0 * _poolCellWidth + cellX0];
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLUtils.h	Wed Jan 22 22:52:18 2020 +0300
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLUtils.h	Thu Jan 23 17:24:18 2020 +0300
@@ -3,4 +3,6 @@
 
 #import <Metal/Metal.h>
 
+#define MTLAASampleCount 4
+
 #endif /* MTLUtils_h_Included */