--- /dev/null 2019-06-14 14:38:23.000000000 +0530 +++ new/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLBlitLoops.m 2019-06-14 14:38:22.000000000 +0530 @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2019, 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. + */ + +#ifndef HEADLESS + +#include +#include + +#include "SurfaceData.h" +#include "MTLBlitLoops.h" +#include "MTLRenderQueue.h" +#include "MTLSurfaceData.h" +#include "MTLUtils.h" +#include "GraphicsPrimitiveMgr.h" + +#include // malloc +#include // memcpy +#include "IntArgbPre.h" + +extern MTLPixelFormat PixelFormats[]; +extern void J2dTraceImpl(int level, jboolean cr, const char *string, ...); + +void fillTxQuad( + struct TxtVertex * txQuadVerts, + jint sx1, jint sy1, jint sx2, jint sy2, jint sw, jint sh, + jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2, jdouble dw, jdouble dh +) { + const float nsx1 = sx1/(float)sw; + const float nsy1 = sy1/(float)sh; + const float nsx2 = sx2/(float)sw; + const float nsy2 = sy2/(float)sh; + + txQuadVerts[0].position[0] = dx1; + txQuadVerts[0].position[1] = dy1; + txQuadVerts[0].position[2] = 0; + txQuadVerts[0].txtpos[0] = nsx1; + txQuadVerts[0].txtpos[1] = nsy1; + + txQuadVerts[1].position[0] = dx2; + txQuadVerts[1].position[1] = dy1; + txQuadVerts[1].position[2] = 0; + txQuadVerts[1].txtpos[0] = nsx2; + txQuadVerts[1].txtpos[1] = nsy1; + + txQuadVerts[2].position[0] = dx2; + txQuadVerts[2].position[1] = dy2; + txQuadVerts[2].position[2] = 0; + txQuadVerts[2].txtpos[0] = nsx2; + txQuadVerts[2].txtpos[1] = nsy2; + + txQuadVerts[3].position[0] = dx2; + txQuadVerts[3].position[1] = dy2; + txQuadVerts[3].position[2] = 0; + txQuadVerts[3].txtpos[0] = nsx2; + txQuadVerts[3].txtpos[1] = nsy2; + + txQuadVerts[4].position[0] = dx1; + txQuadVerts[4].position[1] = dy2; + txQuadVerts[4].position[2] = 0; + txQuadVerts[4].txtpos[0] = nsx1; + txQuadVerts[4].txtpos[1] = nsy2; + + txQuadVerts[5].position[0] = dx1; + txQuadVerts[5].position[1] = dy1; + txQuadVerts[5].position[2] = 0; + txQuadVerts[5].txtpos[0] = nsx1; + txQuadVerts[5].txtpos[1] = nsy1; +} + +/** + * Inner loop used for copying a source MTL "Surface" (window, pbuffer, + * etc.) to a destination OpenGL "Surface". Note that the same surface can + * be used as both the source and destination, as is the case in a copyArea() + * operation. This method is invoked from MTLBlitLoops_IsoBlit() as well as + * MTLBlitLoops_CopyArea(). + * + * The standard glCopyPixels() 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 +MTLBlitSurfaceToSurface(MTLContext *mtlc, BMTLSDOps *srcOps, BMTLSDOps *dstOps, + jint sx1, jint sy1, jint sx2, jint sy2, + jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) +{ + //TODO + //J2dTraceNotImplPrimitive("MTLBlitSurfaceToSurface"); +} + +static void drawTex2Tex(MTLContext *mtlc, + id src, id dst, + jboolean rtt, jint hint, + 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); +// 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 encoder = [mtlc createSamplingEncoderForDest:dst]; + + + 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); + + [encoder setVertexBytes:quadTxVerticesBuffer length:sizeof(quadTxVerticesBuffer) atIndex:MeshVertexBuffer]; + [encoder setFragmentTexture:src atIndex: 0]; + [encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6]; + [encoder endEncoding]; +} + +/** + * Inner loop used for copying a source MTL "Texture" to a destination + * MTL "Surface". This method is invoked from MTLBlitLoops_IsoBlit(). + * + * This method will copy, scale, or transform the source texture into the + * destination depending on the transform state, as established in + * and MTLContext_SetTransform(). If the source texture is + * transformed in any way when rendered into the destination, the filtering + * method applied is determined by the hint parameter (can be GL_NEAREST or + * GL_LINEAR). + */ +static void +MTLBlitTextureToSurface(MTLContext *mtlc, + BMTLSDOps *srcOps, BMTLSDOps *dstOps, + jboolean rtt, jint hint, + jint sx1, jint sy1, jint sx2, jint sy2, + jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) +{ + id srcTex = srcOps->pTexture; + +#ifdef DEBUG + 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); +} + +/** + * 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, + jint sx1, jint sy1, jint sx2, jint sy2, + jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) +{ + if (bmtlsdOps == NULL || bmtlsdOps->pTexture == NULL) { + J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitSwToSurfaceViaTexture: dest is null"); + return; + } + + const int sw = sx2 - sx1; + const int sh = sy2 - sy1; + id dest = bmtlsdOps->pTexture; + +#ifdef DEBUG + 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 texBuff = [ctx.texturePool getTexture:sw height:sh format:MTLPixelFormatBGRA8Unorm]; + 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); +} + +/** + * Inner loop used for copying a source system memory ("Sw") surface or + * MTL "Surface" to a destination OpenGL "Surface", using an MTL texture + * tile as an intermediate surface. This method is invoked from + * MTLBlitLoops_Blit() for "Sw" surfaces and MTLBlitLoops_IsoBlit() for + * "Surface" surfaces. + * + * This method is used to transform the source surface into the destination. + * Pixel rectangles cannot be arbitrarily transformed (without the + * GL_EXT_pixel_transform extension, which is not supported on most modern + * hardware). However, texture mapped quads do respect the GL_MODELVIEW + * transform matrix, so we use textures here to perform the transform + * operation. This method uses a tile-based approach in which a small + * subregion of the source surface is copied into a cached texture tile. The + * texture tile is then mapped into the appropriate location in the + * destination surface. + * + * REMIND: this only works well using GL_NEAREST for the filtering mode + * (GL_LINEAR causes visible stitching problems between tiles, + * but this can be fixed by making use of texture borders) + */ +static void +MTLBlitToSurfaceViaTexture(MTLContext *mtlc, SurfaceDataRasInfo *srcInfo, + MTPixelFormat *pf, MTLSDOps *srcOps, + jboolean swsurface, jint hint, + jint sx1, jint sy1, jint sx2, jint sy2, + jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) +{ + //TODO + //J2dTraceNotImplPrimitive("MTLBlitToSurfaceViaTexture"); +} + +/** + * Inner loop used for copying a source system memory ("Sw") surface to a + * destination OpenGL "Texture". This method is invoked from + * MTLBlitLoops_Blit(). + * + * The source surface is effectively loaded into the MTL texture object, + * which must have already been initialized by MTLSD_initTexture(). Note + * that this method is only capable of copying the source surface into the + * destination surface (i.e. no scaling or general transform is allowed). + * This restriction should not be an issue as this method is only used + * currently to cache a static system memory image into an MTL texture in + * a hidden-acceleration situation. + */ +static void +MTLBlitSwToTexture(SurfaceDataRasInfo *srcInfo, MTPixelFormat *pf, + MTLSDOps *dstOps, + jint dx1, jint dy1, jint dx2, jint dy2) +{ + //TODO + //J2dTraceNotImplPrimitive("MTLBlitSwToTexture"); +} + +/** + * General blit method for copying a native MTL surface (of type "Surface" + * or "Texture") to another MTL "Surface". If texture is JNI_TRUE, this + * method will invoke the Texture->Surface inner loop; otherwise, one of the + * Surface->Surface inner loops will be invoked, depending on the transform + * state. + * + * REMIND: we can trick these blit methods into doing XOR simply by passing + * in the (pixel ^ xorpixel) as the pixel value and preceding the + * blit with a fillrect... + */ +void +MTLBlitLoops_IsoBlit(JNIEnv *env, + MTLContext *mtlc, jlong pSrcOps, jlong pDstOps, + jboolean xform, jint hint, + jboolean texture, jboolean rtt, + jint sx1, jint sy1, jint sx2, jint sy2, + jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) +{ + BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps); + BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps); + + RETURN_IF_NULL(srcOps); + RETURN_IF_NULL(dstOps); + + id srcTex = srcOps->pTexture; + id dstTex = dstOps->pTexture; + if (mtlc == NULL || srcTex == nil || srcTex == nil) { + J2dTraceLn2(J2D_TRACE_ERROR, "MTLBlitLoops_IsoBlit: surface is null (stex=%p, dtex=%p)", srcTex, dstTex); + return; + } + + const jint sw = sx2 - sx1; + const jint sh = sy2 - sy1; + const jdouble dw = dx2 - dx1; + const jdouble dh = dy2 - dy1; + + if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) { + J2dTraceLn4(J2D_TRACE_WARNING, "MTLBlitLoops_IsoBlit: invalid dimensions: sw=%d, sh%d, dw=%d, dh=%d", sw, sh, dw, dh); + return; + } + + SurfaceDataRasInfo srcInfo; + srcInfo.bounds.x1 = sx1; + srcInfo.bounds.y1 = sy1; + srcInfo.bounds.x2 = sx2; + srcInfo.bounds.y2 = sy2; + SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds, 0, 0, srcOps->width, srcOps->height); + + if (srcInfo.bounds.x2 <= srcInfo.bounds.x1 || srcInfo.bounds.y2 <= srcInfo.bounds.y1) { + J2dTraceLn(J2D_TRACE_VERBOSE, "MTLBlitLoops_IsoBlit: source rectangle doesn't intersect with source surface bounds"); + J2dTraceLn6(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d sw=%d sh=%d", sx1, sy1, sx2, sy2, srcOps->width, srcOps->height); + J2dTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f", dx1, dy1, dx2, dy2); + return; + } + + if (srcInfo.bounds.x1 != sx1) { + dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw); + sx1 = srcInfo.bounds.x1; + } + if (srcInfo.bounds.y1 != sy1) { + dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh); + sy1 = srcInfo.bounds.y1; + } + if (srcInfo.bounds.x2 != sx2) { + dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw); + sx2 = srcInfo.bounds.x2; + } + if (srcInfo.bounds.y2 != sy2) { + dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh); + sy2 = srcInfo.bounds.y2; + } + + const jboolean useBlitEncoder = + mtlc.isBlendingDisabled + && 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 blitEncoder in this case) + if (useBlitEncoder) { +#ifdef DEBUG + J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_IsoBlit [via blitEncoder]: bdst=%p [tex=%p] %dx%d | src (%d, %d, %d, %d) -> dst (%1.2f, %1.2f, %1.2f, %1.2f)", dstOps, dstTex, dstTex.width, dstTex.height, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); +#endif //DEBUG + id blitEncoder = [mtlc createBlitEncoder]; + [blitEncoder copyFromTexture:srcTex sourceSlice:0 sourceLevel:0 sourceOrigin:MTLOriginMake(sx1, sy1, 0) sourceSize:MTLSizeMake(sx2 - sx1, sy2 - sy1, 1) toTexture:dstTex destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(dx1, dy1, 0)]; + [blitEncoder endEncoding]; + } else { + // TODO: support other flags + MTLBlitTextureToSurface(mtlc, srcOps, dstOps, rtt, hint, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } +} + +/** + * General blit method for copying a system memory ("Sw") surface to a native + * MTL surface (of type "Surface" or "Texture"). If texture is JNI_TRUE, + * this method will invoke the Sw->Texture inner loop; otherwise, one of the + * Sw->Surface inner loops will be invoked, depending on the transform state. + */ +void +MTLBlitLoops_Blit(JNIEnv *env, + MTLContext *mtlc, jlong pSrcOps, jlong pDstOps, + jboolean xform, jint hint, + jint srctype, jboolean texture, + jint sx1, jint sy1, jint sx2, jint sy2, + jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) +{ + RETURN_IF_NULL(jlong_to_ptr(pSrcOps)); + RETURN_IF_NULL(jlong_to_ptr(pDstOps)); + + 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; + } + id dest = dstOps->pTexture; + if (dx1 < 0) { + sx1 += dx1; + dx1 = 0; + } + if (dx2 > dest.width) { + sx2 -= dx2 - dest.width; + dx2 = dest.width; + } + if (dy1 < 0) { + sy1 += dy1; + dy1 = 0; + } + if (dy2 > dest.height) { + sy2 -= dy2 - dest.height; + dy2 = dest.height; + } + jint sw = sx2 - sx1; + jint sh = sy2 - sy1; + jdouble dw = dx2 - dx1; + jdouble dh = dy2 - dy1; + + if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0 || srctype < 0) { + J2dTraceLn(J2D_TRACE_WARNING, "MTLBlitLoops_Blit: invalid dimensions or srctype"); + return; + } + + srcInfo.bounds.x1 = sx1; + srcInfo.bounds.y1 = sy1; + srcInfo.bounds.x2 = sx2; + srcInfo.bounds.y2 = sy2; + + if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) != SD_SUCCESS) { + J2dTraceLn(J2D_TRACE_WARNING, "MTLBlitLoops_Blit: could not acquire lock"); + return; + } + + J2dTraceLn5(J2D_TRACE_VERBOSE, "MTLBlitLoops_Blit: pf=%d texture=%d srctype=%d xform=%d hint=%d", pf, texture, srctype, xform, hint); + + if (srcInfo.bounds.x2 > srcInfo.bounds.x1 && srcInfo.bounds.y2 > srcInfo.bounds.y1) { + srcOps->GetRasInfo(env, srcOps, &srcInfo); + if (srcInfo.rasBase) { + if (srcInfo.bounds.x1 != sx1) { + dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw); + sx1 = srcInfo.bounds.x1; + } + if (srcInfo.bounds.y1 != sy1) { + dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh); + sy1 = srcInfo.bounds.y1; + } + if (srcInfo.bounds.x2 != sx2) { + dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw); + sx2 = srcInfo.bounds.x2; + } + if (srcInfo.bounds.y2 != sy2) { + dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh); + sy2 = srcInfo.bounds.y2; + } + + // NOTE: if (texture) => dest coordinates will always be integers since we only ever do a straight copy from sw to texture. + const jboolean useReplaceRegion = texture || + (mtlc.isBlendingDisabled + && 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 + } else { + MTLBlitSwToSurfaceViaTexture(mtlc, &srcInfo, dstOps, &pf, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } + } + SurfaceData_InvokeRelease(env, srcOps, &srcInfo); + } + SurfaceData_InvokeUnlock(env, srcOps, &srcInfo); +} + +/** + * Specialized blit method for copying a native MTL "Surface" (pbuffer, + * window, etc.) to a system memory ("Sw") surface. + */ +void +MTLBlitLoops_SurfaceToSwBlit(JNIEnv *env, MTLContext *mtlc, + jlong pSrcOps, jlong pDstOps, jint dsttype, + jint srcx, jint srcy, jint dstx, jint dsty, + jint width, jint height) +{ + //TODO + //J2dTraceNotImplPrimitive("MTLBlitLoops_SurfaceToSwBlit"); +} + +void +MTLBlitLoops_CopyArea(JNIEnv *env, + MTLContext *mtlc, BMTLSDOps *dstOps, + jint x, jint y, jint width, jint height, + jint dx, jint dy) +{ + //TODO + //J2dTraceNotImplPrimitive("MTLBlitLoops_CopyArea"); +} + +#endif /* !HEADLESS */