changeset 56589:a1fc001392fd

8233039: support different raster formats (for SwToTexture blit) * supported flag SurfaceData.isOpaue * added implementation for missing composite rules (some combinations of parameters and extra-alpha can't be supported with CAD-multipliers, need to reimplement via shaders)
author abochkarev
date Fri, 01 Nov 2019 17:25:07 +0300
parents b87de4164e02
children 49c8cbf9d3b9
files src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLBlitLoops.m src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLContext.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/MTLVertexCache.m
diffstat 5 files changed, 340 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLBlitLoops.m	Fri Nov 01 17:23:42 2019 +0300
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLBlitLoops.m	Fri Nov 01 17:25:07 2019 +0300
@@ -39,7 +39,58 @@
 #include <string.h> // memcpy
 #include "IntArgbPre.h"
 
-extern MTLPixelFormat PixelFormats[];
+#import <Accelerate/Accelerate.h>
+
+typedef struct {
+    MTLPixelFormat   format;
+    jboolean hasAlpha;
+    jboolean isPremult;
+    const uint8_t * permuteMap;
+
+} MTLRasterFormatInfo;
+
+// 0 denotes the alpha channel, 1 the red channel, 2 the green channel, and 3 the blue channel.
+const uint8_t permuteMap_rgbx[4] = { 1, 2, 3, 0 };
+const uint8_t permuteMap_bgrx[4] = { 3, 2, 1, 0 };
+
+static uint8_t revertPerm(const uint8_t * perm, uint8_t pos) {
+    for (int c = 0; c < 4; ++c) {
+        if (perm[c] == pos)
+            return c;
+    }
+    return -1;
+}
+
+#define uint2swizzle(channel) (channel == 0 ? MTLTextureSwizzleAlpha : (channel == 1 ? MTLTextureSwizzleRed : (channel == 2 ? MTLTextureSwizzleGreen : (channel == 3 ? MTLTextureSwizzleBlue : MTLTextureSwizzleZero))))
+
+/**
+ * This table contains the "pixel formats" for all system memory surfaces
+ * that Metal is capable of handling, indexed by the "PF_" constants defined
+ * in MTLLSurfaceData.java.  These pixel formats contain information that is
+ * passed to Metal when copying from a system memory ("Sw") surface to
+ * an Metal surface
+ */
+MTLRasterFormatInfo RasterFormatInfos[] = {
+        { MTLPixelFormatBGRA8Unorm, 1, 0, NULL }, /* 0 - IntArgb      */ // Argb (in java notation)
+        { MTLPixelFormatBGRA8Unorm, 1, 1, NULL }, /* 1 - IntArgbPre   */
+        { MTLPixelFormatBGRA8Unorm, 0, 1, NULL }, /* 2 - IntRgb       */ // xrgb
+        { MTLPixelFormatBGRA8Unorm, 0, 1, permuteMap_rgbx }, /* 3 - IntRgbx      */
+        { MTLPixelFormatRGBA8Unorm, 0, 1, NULL }, /* 4 - IntBgr       */ // xbgr
+        { MTLPixelFormatBGRA8Unorm, 0, 1, permuteMap_bgrx }, /* 5 - IntBgrx      */
+
+//        TODO: support 2-byte formats
+//        { GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV,
+//                2, 0, 1,                                     }, /* 7 - Ushort555Rgb */
+//        { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1,
+//                2, 0, 1,                                     }, /* 8 - Ushort555Rgbx*/
+//        { GL_LUMINANCE, GL_UNSIGNED_BYTE,
+//                1, 0, 1,                                     }, /* 9 - ByteGray     */
+//        { GL_LUMINANCE, GL_UNSIGNED_SHORT,
+//                2, 0, 1,                                     }, /*10 - UshortGray   */
+//        { GL_BGR,  GL_UNSIGNED_BYTE,
+//                1, 0, 1,                                     }, /*11 - ThreeByteBgr */
+};
+
 extern void J2dTraceImpl(int level, jboolean cr, const char *string, ...);
 
 void fillTxQuad(
@@ -106,22 +157,23 @@
 
 static void drawTex2Tex(MTLContext *mtlc,
                         id<MTLTexture> src, id<MTLTexture> dst,
-                        jboolean rtt, jint hint,
+                        jboolean isSrcOpaque, jboolean isDstOpaque,
                         jint sx1, jint sy1, jint sx2, jint sy2,
                         jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
 {
     if (mtlc == NULL || src == nil || dst == nil)
         return;
 
-//    J2dTraceLn2(J2D_TRACE_VERBOSE, "_drawTex2Tex: src tex=%p, dst tex=%p", src, dst);
+//    J2dTraceLn2(J2D_TRACE_VERBOSE, "drawTex2Tex: src tex=%p, dst tex=%p", src, dst);
 //    J2dTraceLn4(J2D_TRACE_VERBOSE, "  sw=%d sh=%d dw=%d dh=%d", src.width, src.height, dst.width, dst.height);
 //    J2dTraceLn4(J2D_TRACE_VERBOSE, "  sx1=%d sy1=%d sx2=%d sy2=%d", sx1, sy1, sx2, sy2);
 //    J2dTraceLn4(J2D_TRACE_VERBOSE, "  dx1=%f dy1=%f dx2=%f dy2=%f", dx1, dy1, dx2, dy2);
 
-    id<MTLRenderCommandEncoder> encoder = [mtlc createCommonSamplingEncoderForDest:dst];
+    id<MTLRenderCommandEncoder> encoder = [mtlc createCommonSamplingEncoderForDest:
+                                               dst
+                                               isSrcOpaque:isSrcOpaque
+                                               isDstOpaque:isDstOpaque];
 
-
-    const jboolean normalize = !mtlc.useTransform;
     struct TxtVertex quadTxVerticesBuffer[6];
     fillTxQuad(quadTxVerticesBuffer, sx1, sy1, sx2, sy2, src.width, src.height, dx1, dy1, dx2, dy2, dst.width, dst.height);
 
@@ -154,24 +206,85 @@
     J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_IsoBlit [via sampling]: bsrc=%p [tex=%p], bdst=%p [tex=%p] | s (%dx%d) -> d (%dx%d) | src (%d, %d, %d, %d) -> dst (%1.2f, %1.2f, %1.2f, %1.2f)", srcOps, srcOps->pTexture, dstOps, dstOps->pTexture, srcTex.width, srcTex.height, dstOps->width, dstOps->height, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
 #endif //DEBUG
 
-    drawTex2Tex(mtlc, srcOps->pTexture, dstOps->pTexture, rtt, hint, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
+    drawTex2Tex(mtlc, srcOps->pTexture, dstOps->pTexture, srcOps->isOpaque, dstOps->isOpaque, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
+}
+
+static
+id<MTLTexture> replaceTextureRegion(id<MTLTexture> dest, const SurfaceDataRasInfo * srcInfo, const MTLRasterFormatInfo * rfi, int sx1, int sy1, int sx2, int sy2) {
+    const int sw = sx2 - sx1;
+    const int sh = sy2 - sy1;
+
+    const void * raster = srcInfo->rasBase;
+    if (rfi->permuteMap != NULL) {
+        if (@available(macOS 10.15, *)) {
+            const uint8_t swzRed = revertPerm(rfi->permuteMap, 1);
+            const uint8_t swzGreen = revertPerm(rfi->permuteMap, 2);
+            const uint8_t swzBlue = revertPerm(rfi->permuteMap, 3);
+            const uint8_t swzAlpha = revertPerm(rfi->permuteMap, 0);
+            MTLTextureSwizzleChannels swizzle = MTLTextureSwizzleChannelsMake(
+                    uint2swizzle(swzRed),
+                    uint2swizzle(swzGreen),
+                    uint2swizzle(swzBlue),
+                    rfi->hasAlpha ? uint2swizzle(swzAlpha) : MTLTextureSwizzleOne
+            );
+            dest = [dest
+                    newTextureViewWithPixelFormat:MTLPixelFormatBGRA8Unorm
+                    textureType:MTLTextureType2D
+                    levels:NSMakeRange(0, 1) slices:NSMakeRange(0, 1)
+                    swizzle:swizzle];
+            J2dTraceLn5(J2D_TRACE_VERBOSE, "replaceTextureRegion [use swizzle for pooled]: %d, %d, %d, %d, hasA=%d",
+                        swizzle.red, swizzle.green, swizzle.blue, swizzle.alpha, rfi->hasAlpha);
+        } else {
+            // perform raster conversion
+            // invoked only from rq-thread, so use static buffers
+            // but it's better to use thread-local buffers (or special buffer manager)
+            const int destRasterSize = sw*sh*4;
+
+            static int bufferSize = 0;
+            static void * buffer = NULL;
+            if (buffer == NULL || bufferSize < destRasterSize) {
+                bufferSize = destRasterSize;
+                buffer = realloc(buffer, bufferSize);
+            }
+            if (buffer == NULL) {
+                J2dTraceLn1(J2D_TRACE_ERROR, "replaceTextureRegion: can't alloc buffer for raster conversion, size=%d", bufferSize);
+                bufferSize = 0;
+                return nil;
+            }
+            vImage_Buffer src;
+            src.height = sw;
+            src.width = sh;
+            src.rowBytes = srcInfo->scanStride;
+            src.data = srcInfo->rasBase;
+
+            vImage_Buffer dest;
+            dest.height = sw;
+            dest.width = sh;
+            dest.rowBytes = sw*4;
+            dest.data = buffer;
+
+            vImagePermuteChannels_ARGB8888(&src, &dest, rfi->permuteMap, kvImageNoFlags);
+            raster = buffer;
+
+            J2dTraceLn5(J2D_TRACE_VERBOSE, "replaceTextureRegion [use conversion]: %d, %d, %d, %d, hasA=%d",
+                        rfi->permuteMap[0], rfi->permuteMap[1], rfi->permuteMap[2], rfi->permuteMap[3], rfi->hasAlpha);
+        }
+    }
+
+    MTLRegion region = MTLRegionMake2D(sx1, sy1, sw, sh);
+    [dest replaceRegion:region mipmapLevel:0 withBytes:raster bytesPerRow:srcInfo->scanStride];
+    return dest;
 }
 
 /**
  * Inner loop used for copying a source system memory ("Sw") surface to a
  * destination MTL "Surface".  This method is invoked from
  * MTLBlitLoops_Blit().
- *
- * The standard glDrawPixels() mechanism is used to copy the source region
- * into the destination region.  If the regions have different
- * dimensions, the source will be scaled into the destination
- * as appropriate (only nearest neighbor filtering will be applied for simple
- * scale operations).
  */
 
 static void
 MTLBlitSwToSurfaceViaTexture(MTLContext *ctx, SurfaceDataRasInfo *srcInfo, BMTLSDOps * bmtlsdOps,
-                   MTPixelFormat *pf,
+                   MTLRasterFormatInfo * rfi,
                    jint sx1, jint sy1, jint sx2, jint sy2,
                    jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
 {
@@ -188,15 +301,14 @@
     J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_Blit [via pooled texture]: bdst=%p [tex=%p], sw=%d, sh=%d | src (%d, %d, %d, %d) -> dst (%1.2f, %1.2f, %1.2f, %1.2f)", bmtlsdOps, dest, sw, sh, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
 #endif //DEBUG
 
-    id<MTLTexture> texBuff = [ctx.texturePool getTexture:sw height:sh format:MTLPixelFormatBGRA8Unorm];
+    id<MTLTexture> texBuff = [ctx.texturePool getTexture:sw height:sh format:rfi->format];
     if (texBuff == nil) {
         J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitSwToSurfaceViaTexture: can't obtain temporary texture object from pool");
         return;
     }
-    MTLRegion region = MTLRegionMake2D(0, 0, sw, sh);
-    [texBuff replaceRegion:region mipmapLevel:0 withBytes:srcInfo->rasBase bytesPerRow:srcInfo->scanStride]; // texBuff is locked for current frame
 
-    drawTex2Tex(ctx, texBuff, dest, 0, 0, 0, 0, sw, sh, dx1, dy1, dx2, dy2);
+    texBuff = replaceTextureRegion(texBuff, srcInfo, rfi, sx1, sy1, sx2, sy2); // texBuff is locked for current frame
+    drawTex2Tex(ctx, texBuff, dest, !rfi->hasAlpha, bmtlsdOps->isOpaque, 0, 0, sw, sh, dx1, dy1, dx2, dy2);
 }
 
 /**
@@ -365,12 +477,17 @@
     SurfaceDataOps *srcOps = (SurfaceDataOps *)jlong_to_ptr(pSrcOps);
     BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps);
     SurfaceDataRasInfo srcInfo;
-    MTLPixelFormat pf = MTLPixelFormatBGRA8Unorm;//PixelFormats[srctype];
 
     if (dstOps == NULL || dstOps->pTexture == NULL) {
         J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: dest is null");
         return;
     }
+    if (srctype >= sizeof(RasterFormatInfos)/ sizeof(MTLRasterFormatInfo)) {
+        J2dTraceLn1(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: source pixel format %d isn't supported", srctype);
+        return;
+    }
+
+    MTLRasterFormatInfo rfi = RasterFormatInfos[srctype];
     id<MTLTexture> dest = dstOps->pTexture;
     if (dx1 < 0) {
         sx1 += dx1;
@@ -408,7 +525,7 @@
         return;
     }
 
-    J2dTraceLn5(J2D_TRACE_VERBOSE, "MTLBlitLoops_Blit:  pf=%d texture=%d srctype=%d xform=%d hint=%d", pf, texture, srctype, xform, hint);
+    J2dTraceLn4(J2D_TRACE_VERBOSE, "MTLBlitLoops_Blit: texture=%d srctype=%d xform=%d hint=%d", texture, srctype, xform, hint);
 
     if (srcInfo.bounds.x2 > srcInfo.bounds.x1 && srcInfo.bounds.y2 > srcInfo.bounds.y1) {
         srcOps->GetRasInfo(env, srcOps, &srcInfo);
@@ -436,13 +553,13 @@
                     && fabs(dx2 - dx1 - sx2 + sx1) < 0.001f && fabs(dy2 - dy1 - sy2 + sy1) < 0.001f // dimensions are equal (TODO: check that dx1,dy1 is integer)
                     && !mtlc.useTransform); // TODO: check whether transform is simple translate (and use replaceRegion in this case)
             if (useReplaceRegion) {
-                MTLRegion region = MTLRegionMake2D(dx1, dy1, dx2 - dx1, dy2 - dy1);
 #ifdef DEBUG
                 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_Blit [replaceRegion]: bdst=%p [tex=%p] %dx%d | src (%d, %d, %d, %d) -> dst (%1.2f, %1.2f, %1.2f, %1.2f)", dstOps, dest, dest.width, dest.height, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
 #endif //DEBUG
-                [dest replaceRegion:region mipmapLevel:0 withBytes:srcInfo.rasBase bytesPerRow:srcInfo.scanStride]; // executed at CPU (sync), TODO: lock dest for current frame
+                replaceTextureRegion(dest, &srcInfo, &rfi, (int)dx1, (int)dy1, (int)dx2, (int)dy2);
+                dstOps->isOpaque = !rfi.hasAlpha;
             } else {
-                MTLBlitSwToSurfaceViaTexture(mtlc, &srcInfo, dstOps, &pf, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
+                MTLBlitSwToSurfaceViaTexture(mtlc, &srcInfo, dstOps, &rfi, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
             }
         }
         SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLContext.m	Fri Nov 01 17:23:42 2019 +0300
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLContext.m	Fri Nov 01 17:25:07 2019 +0300
@@ -370,7 +370,12 @@
     [self setEncoderTransform:encoder dest:dest];
 }
 
-- (void) updateSamplingEncoderProperties:(id<MTLRenderCommandEncoder>) encoder dest:(id<MTLTexture>) dest {
+- (void) updateSamplingEncoderProperties:
+     (id<MTLRenderCommandEncoder>) encoder
+     dest:(id<MTLTexture>) dest
+     isSrcOpaque:(bool)isSrcOpaque
+     isDstOpaque:(bool)isDstOpaque
+{
     if (compState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) {
         struct TxtFrameUniforms uf = {RGBA_TO_V4(color), 1};
         [encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
@@ -378,7 +383,11 @@
         struct TxtFrameUniforms uf = {RGBA_TO_V4(0), 0};
         [encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
     }
-    [encoder setRenderPipelineState:[pipelineStateStorage getTexturePipelineState:NO compositeRule:alphaCompositeRule]];
+    [encoder setRenderPipelineState:[pipelineStateStorage getTexturePipelineState:NO
+          isDestPremultiplied:NO
+          isSrcOpaque:isSrcOpaque
+          isDstOpaque:isDstOpaque
+          compositeRule:alphaCompositeRule]];
     [self setEncoderTransform:encoder dest:dest];
 }
 
@@ -394,12 +403,15 @@
     return commonRenderEncoder;
 }
 
-- (id<MTLRenderCommandEncoder>)createCommonSamplingEncoderForDest:(id<MTLTexture>)dest {
+- (id<MTLRenderCommandEncoder>)createCommonSamplingEncoderForDest:(id<MTLTexture>)dest isSrcOpaque:(bool)isSrcOpaque isDstOpaque:(bool)isDstOpaque {
     if (commonRenderEncoder == nil) {
         commonRenderEncoder = [self createEncoderForDest: dest];
     }
     [self updateRenderEncoderProperties:commonRenderEncoder dest:dest];
-    [self updateSamplingEncoderProperties:commonRenderEncoder dest:dest];
+    [self updateSamplingEncoderProperties:commonRenderEncoder
+            dest:dest
+            isSrcOpaque:isSrcOpaque
+            isDstOpaque:isDstOpaque];
 
     return commonRenderEncoder;
 }
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLPipelineStatesStorage.h	Fri Nov 01 17:23:42 2019 +0300
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLPipelineStatesStorage.h	Fri Nov 01 17:25:07 2019 +0300
@@ -23,7 +23,11 @@
 
 - (id) initWithDevice:(id<MTLDevice>)device shaderLibPath:(NSString *)shadersLib;
 - (id<MTLRenderPipelineState>) getRenderPipelineState:(bool)isGradient;
-- (id<MTLRenderPipelineState>) getTexturePipelineState:(bool)isSourcePremultiplied compositeRule:(int)compositeRule;
+- (id<MTLRenderPipelineState>) getTexturePipelineState:(bool)isSourcePremultiplied
+    isDestPremultiplied:(bool)isDestPremultiplied
+    isSrcOpaque:(bool)isSrcOpaque
+    isDstOpaque:(bool)isDstOpaque
+    compositeRule:(int)compositeRule;
 - (id<MTLFunction>) getShader:(NSString *)name;
 @end
 
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLPipelineStatesStorage.m	Fri Nov 01 17:23:42 2019 +0300
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLPipelineStatesStorage.m	Fri Nov 01 17:25:07 2019 +0300
@@ -4,6 +4,13 @@
 #include "GraphicsPrimitiveMgr.h"
 #import "common.h"
 
+static void setBlendingFactors(MTLRenderPipelineColorAttachmentDescriptor * cad,
+    int compositeRule,
+    bool isSourcePremultiplied,
+    bool isDestPremultiplied,
+    bool isSrcOpaque,
+    bool isDstOpaque);
+
 @implementation MTLPipelineStatesStorage
 
 @synthesize device;
@@ -56,8 +63,6 @@
     { // pre-create main states
         [self getRenderPipelineState:YES];
         [self getRenderPipelineState:NO];
-        [self getTexturePipelineState:NO compositeRule:RULE_Src];
-        [self getTexturePipelineState:NO compositeRule:RULE_SrcOver];
     }
 
     return self;
@@ -92,7 +97,12 @@
     return result;
 };
 
-- (id<MTLRenderPipelineState>) getTexturePipelineState:(bool)isSourcePremultiplied compositeRule:(int)compositeRule {
+- (id<MTLRenderPipelineState>) getTexturePipelineState:(bool)isSourcePremultiplied
+    isDestPremultiplied:(bool)isDestPremultiplied
+    isSrcOpaque:(bool)isSrcOpaque
+    isDstOpaque:(bool)isDstOpaque
+    compositeRule:(int)compositeRule
+{
     @autoreleasepool {
         NSString *uid = [NSString stringWithFormat:@"texture_compositeRule[%d]", compositeRule];
 
@@ -104,29 +114,12 @@
             pipelineDesc.vertexFunction = vertexShader;
             pipelineDesc.fragmentFunction = fragmentShader;
 
-            if (compositeRule != RULE_Src) {
-                pipelineDesc.colorAttachments[0].blendingEnabled = YES;
-
-                if (!isSourcePremultiplied)
-                    pipelineDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
-
-                //RGB = Source.rgb * SBF + Dest.rgb * DBF
-                //A = Source.a * SBF + Dest.a * DBF
-                //
-                //default SRC:
-                //DBF=0
-                //SBF=1
-                if (compositeRule == RULE_SrcOver) {
-                    // SRC_OVER (Porter-Duff Source Over Destination rule):
-                    // Ar = As + Ad*(1-As)
-                    // Cr = Cs + Cd*(1-As)
-                    pipelineDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
-                    pipelineDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
-                } else {
-                    J2dTrace1(J2D_TRACE_ERROR, "Unimplemented composite rule %d (will be used Src)", compositeRule);
-                    pipelineDesc.colorAttachments[0].blendingEnabled = NO;
-                }
-            }
+            setBlendingFactors(
+                pipelineDesc.colorAttachments[0],
+                compositeRule,
+                isSourcePremultiplied, isDestPremultiplied,
+                isSrcOpaque, isDstOpaque
+            );
 
             NSError *error = nil;
             result = [[self.device newRenderPipelineStateWithDescriptor:pipelineDesc error:&error] autorelease];
@@ -151,3 +144,162 @@
     return result;
 }
 @end
+
+static void setBlendingFactors(
+        MTLRenderPipelineColorAttachmentDescriptor * cad,
+        int compositeRule,
+        bool isSourcePremultiplied,
+        bool isDestPremultiplied,
+        bool isSrcOpaque,
+        bool isDstOpaque
+) {
+    if (compositeRule == RULE_Src) {
+        J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_Src");
+        return;
+    }
+
+    cad.blendingEnabled = YES;
+
+    // RGB = Source.rgb * SBFc + Dest.rgb * DBFc
+    // A = Source.a * SBFa + Dest.a * DBFa
+    //
+    // default mode == RULE_Src with constants:
+    // DBFa=0
+    // DBFc=0
+    // SBFa=1
+    // SBFc=1
+    //
+    // NOTE: constants MTLBlendFactorBlendAlpha, MTLBlendFactorOneMinusBlendAlpha refers to [encoder setBlendColorRed:green:blue:alpha:] (default value is zero)
+    //
+    // TODO: implement alpha-composite via shaders (will be much more simpler and can support all rules and modes)
+
+    switch (compositeRule) {
+        case RULE_SrcOver: {
+            // Ar = As + Ad*(1-As)
+            // Cr = Cs + Cd*(1-As)
+            if (isSrcOpaque) {
+                J2dTraceLn(J2D_TRACE_VERBOSE, "rule=RULE_Src, but blending is disabled because src is opaque");
+                cad.blendingEnabled = NO;
+                return;
+            }
+            if (isDstOpaque) {
+                J2dTraceLn(J2D_TRACE_ERROR, "Composite rule RULE_SrcOver with opaque dest isn't implemented (dst alpha won't be ignored)");
+            }
+            if (!isSourcePremultiplied) {
+                cad.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
+            }
+            cad.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
+            cad.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
+            J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_SrcOver");
+            break;
+        }
+        case RULE_DstOver: {
+            // Ar = As*(1-Ad) + Ad
+            // Cr = Cs*(1-Ad) + Cd
+            if (isSrcOpaque) {
+                J2dTraceLn(J2D_TRACE_ERROR, "Composite rule RULE_DstOver with opaque src isn't implemented (src alpha won't be ignored)");
+            }
+            if (isDstOpaque) {
+                J2dTraceLn(J2D_TRACE_ERROR, "Composite rule RULE_DstOver with opaque dest hasn't any sense");
+            }
+            if (!isSourcePremultiplied) {
+                J2dTrace(J2D_TRACE_ERROR, "Composite rule RULE_DstOver with non-premultiplied source isn't implemented (scr alpha will be ignored for rgb-component)");
+            }
+            cad.sourceAlphaBlendFactor = MTLBlendFactorOneMinusDestinationAlpha;
+            cad.sourceRGBBlendFactor = MTLBlendFactorOneMinusDestinationAlpha;
+            cad.destinationAlphaBlendFactor = MTLBlendFactorOne;
+            cad.destinationRGBBlendFactor = MTLBlendFactorOne;
+            J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_DstOver");
+            break;
+        }
+        case RULE_SrcIn: {
+            // Ar = As*Ad
+            // Cr = Cs*Ad
+            if (isSrcOpaque) {
+                J2dTraceLn(J2D_TRACE_ERROR, "Composite rule RULE_SrcIn with opaque src isn't implemented (src alpha won't be ignored)");
+            }
+            if (isDstOpaque) {
+                J2dTraceLn(J2D_TRACE_VERBOSE, "rule=RULE_SrcIn, but blending is disabled because dest is opaque");
+                cad.blendingEnabled = NO;
+                return;
+            }
+            if (!isSourcePremultiplied) {
+                J2dTrace(J2D_TRACE_ERROR, "Composite rule RULE_SrcIn with non-premultiplied source isn't implemented (scr alpha will be ignored for rgb-component)");
+            }
+            cad.sourceAlphaBlendFactor = MTLBlendFactorDestinationAlpha;
+            cad.sourceRGBBlendFactor = MTLBlendFactorDestinationAlpha;
+            cad.destinationAlphaBlendFactor = MTLBlendFactorZero;
+            cad.destinationRGBBlendFactor = MTLBlendFactorZero;
+            J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_SrcIn");
+            break;
+        }
+        case RULE_DstIn: {
+            // Ar = Ad*As
+            // Cr = Cd*As
+            if (isSrcOpaque) {
+                J2dTraceLn(J2D_TRACE_ERROR, "Composite rule RULE_DstIn with opaque src isn't implemented (src alpha won't be ignored)");
+            }
+            if (isDstOpaque) {
+                J2dTraceLn(J2D_TRACE_ERROR, "Composite rule RULE_DstIn with opaque dest isn't implemented (dest alpha won't be ignored)");
+            }
+            cad.sourceAlphaBlendFactor = MTLBlendFactorZero;
+            cad.sourceRGBBlendFactor = MTLBlendFactorZero;
+            cad.destinationAlphaBlendFactor = MTLBlendFactorSourceAlpha;
+            cad.destinationRGBBlendFactor = MTLBlendFactorSourceAlpha;
+            J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_DstIn");
+            break;
+        }
+        case RULE_SrcOut: {
+            // Ar = As*(1-Ad)
+            // Cr = Cs*(1-Ad)
+            if (!isSourcePremultiplied) {
+                J2dTrace(J2D_TRACE_ERROR, "Composite rule SrcOut with non-premultiplied source isn't implemented (scr alpha will be ignored for rgb-component)");
+            }
+            cad.sourceAlphaBlendFactor = MTLBlendFactorOneMinusDestinationAlpha;
+            cad.sourceRGBBlendFactor = MTLBlendFactorOneMinusDestinationAlpha;
+            cad.destinationAlphaBlendFactor = MTLBlendFactorZero;
+            cad.destinationRGBBlendFactor = MTLBlendFactorZero;
+            J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_SrcOut");
+            break;
+        }
+        case RULE_DstOut: {
+            // Ar = Ad*(1-As)
+            // Cr = Cd*(1-As)
+            cad.sourceAlphaBlendFactor = MTLBlendFactorZero;
+            cad.sourceRGBBlendFactor = MTLBlendFactorZero;
+            cad.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
+            cad.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
+            J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_DstOut");
+            break;
+        }
+        case RULE_Xor: {
+            // Ar = As*(1-Ad) + Ad*(1-As)
+            // Cr = Cs*(1-Ad) + Cd*(1-As)
+            if (!isSourcePremultiplied) {
+                J2dTrace(J2D_TRACE_ERROR, "Composite rule Xor with non-premultiplied source isn't implemented (scr alpha will be ignored for rgb-component)");
+            }
+            cad.sourceAlphaBlendFactor = MTLBlendFactorOneMinusDestinationAlpha;
+            cad.sourceRGBBlendFactor = MTLBlendFactorOneMinusDestinationAlpha;
+            cad.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
+            cad.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
+            J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_Xor");
+            break;
+        }
+        case RULE_Clear: {
+            // Ar = 0
+            // Cr = 0
+            cad.sourceAlphaBlendFactor = MTLBlendFactorZero;
+            cad.sourceRGBBlendFactor = MTLBlendFactorZero;
+            cad.destinationAlphaBlendFactor = MTLBlendFactorZero;
+            cad.destinationRGBBlendFactor = MTLBlendFactorZero;
+            J2dTraceLn(J2D_TRACE_VERBOSE, "set RULE_Clear");
+            break;
+        }
+
+        default: {
+            J2dTrace1(J2D_TRACE_ERROR, "Unimplemented composite rule %d (will be used Src)", compositeRule);
+            cad.blendingEnabled = NO;
+        }
+    }
+
+}
\ No newline at end of file
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLVertexCache.m	Fri Nov 01 17:23:42 2019 +0300
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLVertexCache.m	Fri Nov 01 17:25:07 2019 +0300
@@ -223,7 +223,7 @@
 void
 MTLVertexCache_CreateSamplingEncoder(MTLContext *mtlc, BMTLSDOps *dstOps) {
     J2dTraceLn(J2D_TRACE_INFO, "MTLVertexCache_CreateSamplingEncoder");
-    encoder = [mtlc createCommonSamplingEncoderForDest:dstOps->pTexture];
+    encoder = [mtlc createCommonSamplingEncoderForDest:dstOps->pTexture isSrcOpaque:NO isDstOpaque:NO];
 }
 
 void