Path: blob/master/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLBlitLoops.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 <jni.h>26#include <jlong.h>2728#include "SurfaceData.h"29#include "MTLBlitLoops.h"30#include "MTLRenderQueue.h"31#include "MTLSurfaceData.h"32#include "MTLUtils.h"33#include "GraphicsPrimitiveMgr.h"3435#include <string.h> // memcpy36#include "IntArgbPre.h"3738#import <Accelerate/Accelerate.h>3940#ifdef DEBUG41#define TRACE_ISOBLIT42#define TRACE_BLIT43#endif //DEBUG44//#define DEBUG_ISOBLIT45//#define DEBUG_BLIT4647typedef struct {48// Consider deleting this field, since it's always MTLPixelFormatBGRA8Unorm49jboolean hasAlpha;50jboolean isPremult;51const uint8_t* swizzleMap;52} MTLRasterFormatInfo;535455const uint8_t rgb_to_rgba[4] = {0, 1, 2, 3};56const uint8_t xrgb_to_rgba[4] = {1, 2, 3, 0};57const uint8_t bgr_to_rgba[4] = {2, 1, 0, 3};58const uint8_t xbgr_to_rgba[4] = {3, 2, 1, 0};5960/**61* This table contains the "pixel formats" for all system memory surfaces62* that Metal is capable of handling, indexed by the "PF_" constants defined63* in MTLLSurfaceData.java. These pixel formats contain information that is64* passed to Metal when copying from a system memory ("Sw") surface to65* an Metal surface66*/67MTLRasterFormatInfo RasterFormatInfos[] = {68{ 1, 0, nil }, /* 0 - IntArgb */ // Argb (in java notation)69{ 1, 1, nil }, /* 1 - IntArgbPre */70{ 0, 1, rgb_to_rgba }, /* 2 - IntRgb */71{ 0, 1, xrgb_to_rgba }, /* 3 - IntRgbx */72{ 0, 1, bgr_to_rgba }, /* 4 - IntBgr */73{ 0, 1, xbgr_to_rgba }, /* 5 - IntBgrx */7475// TODO: support 2-byte formats76// { GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV,77// 2, 0, 1, }, /* 7 - Ushort555Rgb */78// { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1,79// 2, 0, 1, }, /* 8 - Ushort555Rgbx*/80// { GL_LUMINANCE, GL_UNSIGNED_BYTE,81// 1, 0, 1, }, /* 9 - ByteGray */82// { GL_LUMINANCE, GL_UNSIGNED_SHORT,83// 2, 0, 1, }, /*10 - UshortGray */84// { GL_BGR, GL_UNSIGNED_BYTE,85// 1, 0, 1, }, /*11 - ThreeByteBgr */86};8788extern void J2dTraceImpl(int level, jboolean cr, const char *string, ...);8990void fillTxQuad(91struct TxtVertex * txQuadVerts,92jint sx1, jint sy1, jint sx2, jint sy2, jint sw, jint sh,93jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2, jdouble dw, jdouble dh94) {95const float nsx1 = sx1/(float)sw;96const float nsy1 = sy1/(float)sh;97const float nsx2 = sx2/(float)sw;98const float nsy2 = sy2/(float)sh;99100txQuadVerts[0].position[0] = dx1;101txQuadVerts[0].position[1] = dy1;102txQuadVerts[0].txtpos[0] = nsx1;103txQuadVerts[0].txtpos[1] = nsy1;104105txQuadVerts[1].position[0] = dx2;106txQuadVerts[1].position[1] = dy1;107txQuadVerts[1].txtpos[0] = nsx2;108txQuadVerts[1].txtpos[1] = nsy1;109110txQuadVerts[2].position[0] = dx2;111txQuadVerts[2].position[1] = dy2;112txQuadVerts[2].txtpos[0] = nsx2;113txQuadVerts[2].txtpos[1] = nsy2;114115txQuadVerts[3].position[0] = dx2;116txQuadVerts[3].position[1] = dy2;117txQuadVerts[3].txtpos[0] = nsx2;118txQuadVerts[3].txtpos[1] = nsy2;119120txQuadVerts[4].position[0] = dx1;121txQuadVerts[4].position[1] = dy2;122txQuadVerts[4].txtpos[0] = nsx1;123txQuadVerts[4].txtpos[1] = nsy2;124125txQuadVerts[5].position[0] = dx1;126txQuadVerts[5].position[1] = dy1;127txQuadVerts[5].txtpos[0] = nsx1;128txQuadVerts[5].txtpos[1] = nsy1;129}130131//#define TRACE_drawTex2Tex132133void drawTex2Tex(MTLContext *mtlc,134id<MTLTexture> src, id<MTLTexture> dst,135jboolean isSrcOpaque, jboolean isDstOpaque, jint hint,136jint sx1, jint sy1, jint sx2, jint sy2,137jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)138{139#ifdef TRACE_drawTex2Tex140J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "drawTex2Tex: src tex=%p, dst tex=%p", src, dst);141J2dRlsTraceLn4(J2D_TRACE_VERBOSE, " sw=%d sh=%d dw=%d dh=%d", src.width, src.height, dst.width, dst.height);142J2dRlsTraceLn4(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d", sx1, sy1, sx2, sy2);143J2dRlsTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f", dx1, dy1, dx2, dy2);144#endif //TRACE_drawTex2Tex145146id<MTLRenderCommandEncoder> encoder = [mtlc.encoderManager getTextureEncoder:dst147isSrcOpaque:isSrcOpaque148isDstOpaque:isDstOpaque149interpolation:hint150];151152struct TxtVertex quadTxVerticesBuffer[6];153fillTxQuad(quadTxVerticesBuffer, sx1, sy1, sx2, sy2, src.width, src.height, dx1, dy1, dx2, dy2, dst.width, dst.height);154155[encoder setVertexBytes:quadTxVerticesBuffer length:sizeof(quadTxVerticesBuffer) atIndex:MeshVertexBuffer];156[encoder setFragmentTexture:src atIndex: 0];157[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];158}159160static void fillSwizzleUniforms(struct SwizzleUniforms *uniforms, const MTLRasterFormatInfo *rfi) {161const size_t SWIZZLE_MAP_SIZE = 4;162memcpy(&uniforms->swizzle, rfi->swizzleMap, SWIZZLE_MAP_SIZE);163uniforms->hasAlpha = rfi->hasAlpha;164}165166static void167replaceTextureRegion(MTLContext *mtlc, id<MTLTexture> dest, const SurfaceDataRasInfo *srcInfo,168const MTLRasterFormatInfo *rfi,169int dx1, int dy1, int dx2, int dy2) {170const int sw = MIN(srcInfo->bounds.x2 - srcInfo->bounds.x1, MTL_GPU_FAMILY_MAC_TXT_SIZE);171const int sh = MIN(srcInfo->bounds.y2 - srcInfo->bounds.y1, MTL_GPU_FAMILY_MAC_TXT_SIZE);172const int dw = MIN(dx2 - dx1, MTL_GPU_FAMILY_MAC_TXT_SIZE);173const int dh = MIN(dy2 - dy1, MTL_GPU_FAMILY_MAC_TXT_SIZE);174175if (dw < sw || dh < sh) {176J2dTraceLn4(J2D_TRACE_ERROR, "replaceTextureRegion: dest size: (%d, %d) less than source size: (%d, %d)", dw, dh, sw, sh);177return;178}179180const void *raster = srcInfo->rasBase;181raster += (NSUInteger)srcInfo->bounds.y1 * (NSUInteger)srcInfo->scanStride + (NSUInteger)srcInfo->bounds.x1 * (NSUInteger)srcInfo->pixelStride;182183@autoreleasepool {184J2dTraceLn4(J2D_TRACE_VERBOSE, "replaceTextureRegion src (dw, dh) : [%d, %d] dest (dx1, dy1) =[%d, %d]",185dw, dh, dx1, dy1);186id<MTLBuffer> buff = [[mtlc.device newBufferWithLength:(sw * sh * srcInfo->pixelStride) options:MTLResourceStorageModeManaged] autorelease];187188// copy src pixels inside src bounds to buff189for (int row = 0; row < sh; row++) {190memcpy(buff.contents + (row * sw * srcInfo->pixelStride), raster, sw * srcInfo->pixelStride);191raster += (NSUInteger)srcInfo->scanStride;192}193[buff didModifyRange:NSMakeRange(0, buff.length)];194195if (rfi->swizzleMap != nil) {196id <MTLBuffer> swizzled = [[mtlc.device newBufferWithLength:(sw * sh * srcInfo->pixelStride) options:MTLResourceStorageModeManaged] autorelease];197198// this should be cheap, since data is already on GPU199id<MTLCommandBuffer> cb = [mtlc createCommandBuffer];200id<MTLComputeCommandEncoder> computeEncoder = [cb computeCommandEncoder];201id<MTLComputePipelineState> computePipelineState = [mtlc.pipelineStateStorage202getComputePipelineState:@"swizzle_to_rgba"];203[computeEncoder setComputePipelineState:computePipelineState];204205[computeEncoder setBuffer:buff offset:0 atIndex:0];206[computeEncoder setBuffer:swizzled offset:0 atIndex:1];207208struct SwizzleUniforms uniforms;209fillSwizzleUniforms(&uniforms, rfi);210[computeEncoder setBytes:&uniforms length:sizeof(struct SwizzleUniforms) atIndex:2];211212NSUInteger pixelCount = buff.length / srcInfo->pixelStride;213[computeEncoder setBytes:&pixelCount length:sizeof(NSUInteger) atIndex:3];214215NSUInteger threadGroupSize = computePipelineState.maxTotalThreadsPerThreadgroup;216if (threadGroupSize == 0) {217threadGroupSize = 1;218}219MTLSize threadsPerGroup = MTLSizeMake(threadGroupSize, 1, 1);220MTLSize threadGroups = MTLSizeMake((pixelCount + threadGroupSize - 1) / threadGroupSize,2211, 1);222[computeEncoder dispatchThreadgroups:threadGroups223threadsPerThreadgroup:threadsPerGroup];224[computeEncoder endEncoding];225[cb commit];226227buff = swizzled;228}229230id<MTLBlitCommandEncoder> blitEncoder = [mtlc.encoderManager createBlitEncoder];231[blitEncoder copyFromBuffer:buff232sourceOffset:0 sourceBytesPerRow:(sw * srcInfo->pixelStride)233sourceBytesPerImage:(sw * sh * srcInfo->pixelStride) sourceSize:MTLSizeMake(sw, sh, 1)234toTexture:dest235destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(dx1, dy1, 0)];236[blitEncoder endEncoding];237[mtlc.encoderManager endEncoder];238239MTLCommandBufferWrapper * cbwrapper = [mtlc pullCommandBufferWrapper];240id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];241[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {242[cbwrapper release];243}];244[commandbuf commit];245}246}247248/**249* Inner loop used for copying a source system memory ("Sw") surface to a250* destination MTL "Surface". This method is invoked from251* MTLBlitLoops_Blit().252*/253254static void255MTLBlitSwToTextureViaPooledTexture(256MTLContext *mtlc, SurfaceDataRasInfo *srcInfo, BMTLSDOps * bmtlsdOps,257MTLRasterFormatInfo *rfi, jint hint,258jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)259{260int sw = srcInfo->bounds.x2 - srcInfo->bounds.x1;261int sh = srcInfo->bounds.y2 - srcInfo->bounds.y1;262263sw = MIN(sw, MTL_GPU_FAMILY_MAC_TXT_SIZE);264sh = MIN(sh, MTL_GPU_FAMILY_MAC_TXT_SIZE);265266id<MTLTexture> dest = bmtlsdOps->pTexture;267268MTLPooledTextureHandle * texHandle = [mtlc.texturePool getTexture:sw height:sh format:MTLPixelFormatBGRA8Unorm];269if (texHandle == nil) {270J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitSwToTextureViaPooledTexture: can't obtain temporary texture object from pool");271return;272}273[[mtlc getCommandBufferWrapper] registerPooledTexture:texHandle];274275id<MTLTexture> texBuff = texHandle.texture;276replaceTextureRegion(mtlc, texBuff, srcInfo, rfi, 0, 0, sw, sh);277278drawTex2Tex(mtlc, texBuff, dest, !rfi->hasAlpha, bmtlsdOps->isOpaque, hint,2790, 0, sw, sh, dx1, dy1, dx2, dy2);280}281282static283jboolean isIntegerAndUnscaled(284jint sx1, jint sy1, jint sx2, jint sy2,285jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2286) {287const jdouble epsilon = 0.0001f;288289// check that dx1,dy1 is integer290if (fabs(dx1 - (int)dx1) > epsilon || fabs(dy1 - (int)dy1) > epsilon) {291return JNI_FALSE;292}293// check that destSize equals srcSize294if (fabs(dx2 - dx1 - sx2 + sx1) > epsilon || fabs(dy2 - dy1 - sy2 + sy1) > epsilon) {295return JNI_FALSE;296}297return JNI_TRUE;298}299300static301jboolean clipDestCoords(302jdouble *dx1, jdouble *dy1, jdouble *dx2, jdouble *dy2,303jint *sx1, jint *sy1, jint *sx2, jint *sy2,304jint destW, jint destH, const MTLScissorRect * clipRect305) {306// Trim destination rect by clip-rect (or dest.bounds)307const jint sw = *sx2 - *sx1;308const jint sh = *sy2 - *sy1;309const jdouble dw = *dx2 - *dx1;310const jdouble dh = *dy2 - *dy1;311312jdouble dcx1 = 0;313jdouble dcx2 = destW;314jdouble dcy1 = 0;315jdouble dcy2 = destH;316if (clipRect != NULL) {317if (clipRect->x > dcx1)318dcx1 = clipRect->x;319const int maxX = clipRect->x + clipRect->width;320if (dcx2 > maxX)321dcx2 = maxX;322if (clipRect->y > dcy1)323dcy1 = clipRect->y;324const int maxY = clipRect->y + clipRect->height;325if (dcy2 > maxY)326dcy2 = maxY;327328if (dcx1 >= dcx2) {329J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcx1=%1.2f, dcx2=%1.2f", dcx1, dcx2);330dcx1 = dcx2;331}332if (dcy1 >= dcy2) {333J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcy1=%1.2f, dcy2=%1.2f", dcy1, dcy2);334dcy1 = dcy2;335}336}337if (*dx2 <= dcx1 || *dx1 >= dcx2 || *dy2 <= dcy1 || *dy1 >= dcy2) {338J2dTraceLn(J2D_TRACE_INFO, "\tclipDestCoords: dest rect doesn't intersect clip area");339J2dTraceLn4(J2D_TRACE_INFO, "\tdx2=%1.4f <= dcx1=%1.4f || *dx1=%1.4f >= dcx2=%1.4f", *dx2, dcx1, *dx1, dcx2);340J2dTraceLn4(J2D_TRACE_INFO, "\t*dy2=%1.4f <= dcy1=%1.4f || *dy1=%1.4f >= dcy2=%1.4f", *dy2, dcy1, *dy1, dcy2);341return JNI_FALSE;342}343if (*dx1 < dcx1) {344J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdx1=%1.2f, will be clipped to %1.2f | sx1+=%d", *dx1, dcx1, (jint)((dcx1 - *dx1) * (sw/dw)));345*sx1 += (jint)((dcx1 - *dx1) * (sw/dw));346*dx1 = dcx1;347}348if (*dx2 > dcx2) {349J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdx2=%1.2f, will be clipped to %1.2f | sx2-=%d", *dx2, dcx2, (jint)((*dx2 - dcx2) * (sw/dw)));350*sx2 -= (jint)((*dx2 - dcx2) * (sw/dw));351*dx2 = dcx2;352}353if (*dy1 < dcy1) {354J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdy1=%1.2f, will be clipped to %1.2f | sy1+=%d", *dy1, dcy1, (jint)((dcy1 - *dy1) * (sh/dh)));355*sy1 += (jint)((dcy1 - *dy1) * (sh/dh));356*dy1 = dcy1;357}358if (*dy2 > dcy2) {359J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdy2=%1.2f, will be clipped to %1.2f | sy2-=%d", *dy2, dcy2, (jint)((*dy2 - dcy2) * (sh/dh)));360*sy2 -= (jint)((*dy2 - dcy2) * (sh/dh));361*dy2 = dcy2;362}363return JNI_TRUE;364}365366/**367* General blit method for copying a native MTL surface to another MTL "Surface".368* Parameter texture == true forces to use 'texture' codepath (dest coordinates will always be integers).369* Parameter xform == true only when AffineTransform is used (invoked only from TransformBlit, dest coordinates will always be integers).370*/371void372MTLBlitLoops_IsoBlit(JNIEnv *env,373MTLContext *mtlc, jlong pSrcOps, jlong pDstOps,374jboolean xform, jint hint, jboolean texture,375jint sx1, jint sy1, jint sx2, jint sy2,376jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)377{378BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps);379BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps);380381RETURN_IF_NULL(mtlc);382RETURN_IF_NULL(srcOps);383RETURN_IF_NULL(dstOps);384// Verify if we use a valid MTLContext385MTLSDOps *dstMTLOps = (MTLSDOps *)dstOps->privOps;386RETURN_IF_TRUE(dstMTLOps->configInfo != NULL && mtlc != dstMTLOps->configInfo->context);387388MTLSDOps *srcMTLOps = (MTLSDOps *)srcOps->privOps;389RETURN_IF_TRUE(srcMTLOps->configInfo != NULL && mtlc != srcMTLOps->configInfo->context);390391id<MTLTexture> srcTex = srcOps->pTexture;392id<MTLTexture> dstTex = dstOps->pTexture;393if (srcTex == nil || srcTex == nil) {394J2dTraceLn2(J2D_TRACE_ERROR, "MTLBlitLoops_IsoBlit: surface is null (stex=%p, dtex=%p)", srcTex, dstTex);395return;396}397398const jint sw = sx2 - sx1;399const jint sh = sy2 - sy1;400const jdouble dw = dx2 - dx1;401const jdouble dh = dy2 - dy1;402403if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) {404J2dTraceLn4(J2D_TRACE_WARNING, "MTLBlitLoops_IsoBlit: invalid dimensions: sw=%d, sh%d, dw=%d, dh=%d", sw, sh, dw, dh);405return;406}407408#ifdef DEBUG_ISOBLIT409if ((xform == JNI_TRUE) != (mtlc.useTransform == JNI_TRUE)) {410J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE,411"MTLBlitLoops_IsoBlit state error: xform=%d, mtlc.useTransform=%d, texture=%d",412xform, mtlc.useTransform, texture);413}414#endif // DEBUG_ISOBLIT415416if (!xform) {417clipDestCoords(418&dx1, &dy1, &dx2, &dy2,419&sx1, &sy1, &sx2, &sy2,420dstTex.width, dstTex.height, texture ? NULL : [mtlc.clip getRect]421);422}423424SurfaceDataBounds bounds;425bounds.x1 = sx1;426bounds.y1 = sy1;427bounds.x2 = sx2;428bounds.y2 = sy2;429SurfaceData_IntersectBoundsXYXY(&bounds, 0, 0, srcOps->width, srcOps->height);430431if (bounds.x2 <= bounds.x1 || bounds.y2 <= bounds.y1) {432J2dTraceLn(J2D_TRACE_VERBOSE, "MTLBlitLoops_IsoBlit: source rectangle doesn't intersect with source surface bounds");433J2dTraceLn6(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d sw=%d sh=%d", sx1, sy1, sx2, sy2, srcOps->width, srcOps->height);434J2dTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f", dx1, dy1, dx2, dy2);435return;436}437438if (bounds.x1 != sx1) {439dx1 += (bounds.x1 - sx1) * (dw / sw);440sx1 = bounds.x1;441}442if (bounds.y1 != sy1) {443dy1 += (bounds.y1 - sy1) * (dh / sh);444sy1 = bounds.y1;445}446if (bounds.x2 != sx2) {447dx2 += (bounds.x2 - sx2) * (dw / sw);448sx2 = bounds.x2;449}450if (bounds.y2 != sy2) {451dy2 += (bounds.y2 - sy2) * (dh / sh);452sy2 = bounds.y2;453}454455#ifdef TRACE_ISOBLIT456J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE,457"MTLBlitLoops_IsoBlit [tx=%d, xf=%d, AC=%s]: src=%s, dst=%s | (%d, %d, %d, %d)->(%1.2f, %1.2f, %1.2f, %1.2f)",458texture, xform, [mtlc getCompositeDescription].cString,459getSurfaceDescription(srcOps).cString, getSurfaceDescription(dstOps).cString,460sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);461#endif //TRACE_ISOBLIT462463if (!texture && !xform464&& srcOps->isOpaque465&& isIntegerAndUnscaled(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)466&& (dstOps->isOpaque || !srcOps->isOpaque)467) {468#ifdef TRACE_ISOBLIT469J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via blitEncoder]");470#endif //TRACE_ISOBLIT471472id <MTLBlitCommandEncoder> blitEncoder = [mtlc.encoderManager createBlitEncoder];473[blitEncoder copyFromTexture:srcTex474sourceSlice:0475sourceLevel:0476sourceOrigin:MTLOriginMake(sx1, sy1, 0)477sourceSize:MTLSizeMake(sx2 - sx1, sy2 - sy1, 1)478toTexture:dstTex479destinationSlice:0480destinationLevel:0481destinationOrigin:MTLOriginMake(dx1, dy1, 0)];482[blitEncoder endEncoding];483return;484}485486#ifdef TRACE_ISOBLIT487J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via sampling]");488#endif //TRACE_ISOBLIT489drawTex2Tex(mtlc, srcTex, dstTex,490srcOps->isOpaque, dstOps->isOpaque,491hint, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);492}493494/**495* General blit method for copying a system memory ("Sw") surface to a native MTL surface.496* Parameter texture == true only in SwToTextureBlit (straight copy from sw to texture), dest coordinates will always be integers.497* Parameter xform == true only when AffineTransform is used (invoked only from TransformBlit, dest coordinates will always be integers).498*/499void500MTLBlitLoops_Blit(JNIEnv *env,501MTLContext *mtlc, jlong pSrcOps, jlong pDstOps,502jboolean xform, jint hint,503jint srctype, jboolean texture,504jint sx1, jint sy1, jint sx2, jint sy2,505jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)506{507SurfaceDataOps *srcOps = (SurfaceDataOps *)jlong_to_ptr(pSrcOps);508BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps);509510RETURN_IF_NULL(mtlc);511RETURN_IF_NULL(srcOps);512RETURN_IF_NULL(dstOps);513// Verify if we use a valid MTLContext514MTLSDOps *dstMTLOps = (MTLSDOps *)dstOps->privOps;515RETURN_IF_TRUE(dstMTLOps->configInfo != NULL && mtlc != dstMTLOps->configInfo->context);516517id<MTLTexture> dest = dstOps->pTexture;518if (dest == NULL) {519J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: dest is null");520return;521}522if (srctype < 0 || srctype >= sizeof(RasterFormatInfos)/ sizeof(MTLRasterFormatInfo)) {523J2dTraceLn1(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: source pixel format %d isn't supported", srctype);524return;525}526const jint sw = sx2 - sx1;527const jint sh = sy2 - sy1;528const jdouble dw = dx2 - dx1;529const jdouble dh = dy2 - dy1;530531if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) {532J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: invalid dimensions");533return;534}535536#ifdef DEBUG_BLIT537if (538(xform == JNI_TRUE) != (mtlc.useTransform == JNI_TRUE)539|| (xform && texture)540) {541J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE,542"MTLBlitLoops_Blit state error: xform=%d, mtlc.useTransform=%d, texture=%d",543xform, mtlc.useTransform, texture);544}545if (texture) {546if (!isIntegerAndUnscaled(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)) {547J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE,548"MTLBlitLoops_Blit state error: texture=true, but src and dst dimensions aren't equal or dest coords aren't integers");549}550if (!dstOps->isOpaque && !RasterFormatInfos[srctype].hasAlpha) {551J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE,552"MTLBlitLoops_Blit state error: texture=true, but dest has alpha and source hasn't alpha, can't use texture-codepath");553}554}555#endif // DEBUG_BLIT556if (!xform) {557clipDestCoords(558&dx1, &dy1, &dx2, &dy2,559&sx1, &sy1, &sx2, &sy2,560dest.width, dest.height, texture ? NULL : [mtlc.clip getRect]561);562}563564SurfaceDataRasInfo srcInfo;565srcInfo.bounds.x1 = sx1;566srcInfo.bounds.y1 = sy1;567srcInfo.bounds.x2 = sx2;568srcInfo.bounds.y2 = sy2;569570// NOTE: This function will modify the contents of the bounds field to represent the maximum available raster data.571if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) != SD_SUCCESS) {572J2dTraceLn(J2D_TRACE_WARNING, "MTLBlitLoops_Blit: could not acquire lock");573return;574}575576if (srcInfo.bounds.x2 > srcInfo.bounds.x1 && srcInfo.bounds.y2 > srcInfo.bounds.y1) {577srcOps->GetRasInfo(env, srcOps, &srcInfo);578if (srcInfo.rasBase) {579if (srcInfo.bounds.x1 != sx1) {580const int dx = srcInfo.bounds.x1 - sx1;581dx1 += dx * (dw / sw);582}583if (srcInfo.bounds.y1 != sy1) {584const int dy = srcInfo.bounds.y1 - sy1;585dy1 += dy * (dh / sh);586}587if (srcInfo.bounds.x2 != sx2) {588const int dx = srcInfo.bounds.x2 - sx2;589dx2 += dx * (dw / sw);590}591if (srcInfo.bounds.y2 != sy2) {592const int dy = srcInfo.bounds.y2 - sy2;593dy2 += dy * (dh / sh);594}595596#ifdef TRACE_BLIT597J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE,598"MTLBlitLoops_Blit [tx=%d, xf=%d, AC=%s]: bdst=%s, src=%p (%dx%d) O=%d premul=%d | (%d, %d, %d, %d)->(%1.2f, %1.2f, %1.2f, %1.2f)",599texture, xform, [mtlc getCompositeDescription].cString,600getSurfaceDescription(dstOps).cString, srcOps,601sx2 - sx1, sy2 - sy1,602RasterFormatInfos[srctype].hasAlpha ? 0 : 1, RasterFormatInfos[srctype].isPremult ? 1 : 0,603sx1, sy1, sx2, sy2,604dx1, dy1, dx2, dy2);605#endif //TRACE_BLIT606607MTLRasterFormatInfo rfi = RasterFormatInfos[srctype];608609if (texture) {610replaceTextureRegion(mtlc, dest, &srcInfo, &rfi, (int) dx1, (int) dy1, (int) dx2, (int) dy2);611} else {612MTLBlitSwToTextureViaPooledTexture(mtlc, &srcInfo, dstOps, &rfi, hint, dx1, dy1, dx2, dy2);613}614}615SurfaceData_InvokeRelease(env, srcOps, &srcInfo);616}617SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);618}619620void copyFromMTLBuffer(void *pDst, id<MTLBuffer> srcBuf, NSUInteger offset, NSUInteger len, BOOL convertFromArgbPre) {621char *pSrc = (char*)srcBuf.contents + offset;622if (convertFromArgbPre) {623NSUInteger pixelLen = len >> 2;624for (NSUInteger i = 0; i < pixelLen; i++) {625LoadIntArgbPreTo1IntArgb((jint*)pSrc, 0, i, ((jint*)pDst)[i]);626}627} else {628memcpy(pDst, pSrc, len);629}630}631632/**633* Specialized blit method for copying a native MTL "Surface" (pbuffer,634* window, etc.) to a system memory ("Sw") surface.635*/636void637MTLBlitLoops_SurfaceToSwBlit(JNIEnv *env, MTLContext *mtlc,638jlong pSrcOps, jlong pDstOps, jint dsttype,639jint srcx, jint srcy, jint dstx, jint dsty,640jint width, jint height)641{642J2dTraceLn6(J2D_TRACE_VERBOSE, "MTLBlitLoops_SurfaceToSwBlit: sx=%d sy=%d w=%d h=%d dx=%d dy=%d", srcx, srcy, width, height, dstx, dsty);643644BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps);645SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps);646SurfaceDataRasInfo srcInfo, dstInfo;647648if (dsttype < 0 || dsttype >= sizeof(RasterFormatInfos)/ sizeof(MTLRasterFormatInfo)) {649J2dTraceLn1(J2D_TRACE_ERROR, "MTLBlitLoops_SurfaceToSwBlit: destination pixel format %d isn't supported", dsttype);650return;651}652653if (width <= 0 || height <= 0) {654J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_SurfaceToSwBlit: dimensions are non-positive");655return;656}657658RETURN_IF_NULL(srcOps);659RETURN_IF_NULL(dstOps);660RETURN_IF_NULL(mtlc);661RETURN_IF_TRUE(width < 0);662RETURN_IF_TRUE(height < 0);663NSUInteger w = (NSUInteger)width;664NSUInteger h = (NSUInteger)height;665666srcInfo.bounds.x1 = srcx;667srcInfo.bounds.y1 = srcy;668srcInfo.bounds.x2 = srcx + width;669srcInfo.bounds.y2 = srcy + height;670dstInfo.bounds.x1 = dstx;671dstInfo.bounds.y1 = dsty;672dstInfo.bounds.x2 = dstx + width;673dstInfo.bounds.y2 = dsty + height;674675if (dstOps->Lock(env, dstOps, &dstInfo, SD_LOCK_WRITE) != SD_SUCCESS) {676J2dTraceLn(J2D_TRACE_WARNING,"MTLBlitLoops_SurfaceToSwBlit: could not acquire dst lock");677return;678}679680SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds,6810, 0, srcOps->width, srcOps->height);682683SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds,684srcx - dstx, srcy - dsty);685686if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&687srcInfo.bounds.y2 > srcInfo.bounds.y1)688{689dstOps->GetRasInfo(env, dstOps, &dstInfo);690if (dstInfo.rasBase) {691void *pDst = dstInfo.rasBase;692693srcx = srcInfo.bounds.x1;694srcy = srcInfo.bounds.y1;695dstx = dstInfo.bounds.x1;696dsty = dstInfo.bounds.y1;697width = srcInfo.bounds.x2 - srcInfo.bounds.x1;698height = srcInfo.bounds.y2 - srcInfo.bounds.y1;699700pDst = PtrPixelsRow(pDst, dstx, dstInfo.pixelStride);701pDst = PtrPixelsRow(pDst, dsty, dstInfo.scanStride);702703NSUInteger byteLength = w * h * 4; // NOTE: assume that src format is MTLPixelFormatBGRA8Unorm704705// Create MTLBuffer (or use static)706id<MTLBuffer> mtlbuf;707#ifdef USE_STATIC_BUFFER708// NOTE: theoretically we can use newBufferWithBytesNoCopy, but pDst must be allocated with special API709// mtlbuf = [mtlc.device710// newBufferWithBytesNoCopy:pDst711// length:(NSUInteger) srcLength712// options:MTLResourceCPUCacheModeDefaultCache713// deallocator:nil];714//715// see https://developer.apple.com/documentation/metal/mtldevice/1433382-newbufferwithbytesnocopy?language=objc716//717// The storage allocation of the returned new MTLBuffer object is the same as the pointer input value.718// The existing memory allocation must be covered by a single VM region, typically allocated with vm_allocate or mmap.719// Memory allocated by malloc is specifically disallowed.720721static id<MTLBuffer> mtlIntermediateBuffer = nil; // need to reimplement with MTLBufferManager722if (mtlIntermediateBuffer == nil || mtlIntermediateBuffer.length < srcLength) {723if (mtlIntermediateBuffer != nil) {724[mtlIntermediateBuffer release];725}726mtlIntermediateBuffer = [mtlc.device newBufferWithLength:srcLength options:MTLResourceCPUCacheModeDefaultCache];727}728mtlbuf = mtlIntermediateBuffer;729#else // USE_STATIC_BUFFER730mtlbuf = [mtlc.device newBufferWithLength:byteLength options:MTLResourceStorageModeShared];731#endif // USE_STATIC_BUFFER732733// Read from surface into MTLBuffer734// NOTE: using of separate blitCommandBuffer can produce errors (draw into surface (with general cmd-buf)735// can be unfinished when reading raster from blit cmd-buf).736// Consider to use [mtlc.encoderManager createBlitEncoder] and [mtlc commitCommandBuffer:JNI_TRUE];737J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLBlitLoops_SurfaceToSwBlit: source texture %p", srcOps->pTexture);738739id<MTLCommandBuffer> cb = [mtlc createCommandBuffer];740id<MTLBlitCommandEncoder> blitEncoder = [cb blitCommandEncoder];741[blitEncoder copyFromTexture:srcOps->pTexture742sourceSlice:0743sourceLevel:0744sourceOrigin:MTLOriginMake(srcx, srcy, 0)745sourceSize:MTLSizeMake(w, h, 1)746toBuffer:mtlbuf747destinationOffset:0 /*offset already taken in: pDst = PtrPixelsRow(pDst, dstx, dstInfo.pixelStride)*/748destinationBytesPerRow:w*4749destinationBytesPerImage:byteLength];750[blitEncoder endEncoding];751752// Commit and wait for reading complete753[cb commit];754[cb waitUntilCompleted];755756// Perform conversion if necessary757BOOL convertFromPre = !RasterFormatInfos[dsttype].isPremult && !srcOps->isOpaque;758759if ((dstInfo.scanStride == w * dstInfo.pixelStride) &&760(height == (dstInfo.bounds.y2 - dstInfo.bounds.y1))) {761// mtlbuf.contents have same dimensions as of pDst762copyFromMTLBuffer(pDst, mtlbuf, 0, byteLength, convertFromPre);763} else {764// mtlbuf.contents have smaller dimensions than pDst765// copy each row from mtlbuf.contents at appropriate position in pDst766// Note : pDst is already addjusted for offsets using PtrAddBytes above767768NSUInteger rowSize = w * dstInfo.pixelStride;769for (int y = 0; y < height; y++) {770copyFromMTLBuffer(pDst, mtlbuf, y * rowSize, rowSize, convertFromPre);771pDst = PtrAddBytes(pDst, dstInfo.scanStride);772}773}774775#ifndef USE_STATIC_BUFFER776[mtlbuf release];777#endif // USE_STATIC_BUFFER778}779SurfaceData_InvokeRelease(env, dstOps, &dstInfo);780}781SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);782}783784void785MTLBlitLoops_CopyArea(JNIEnv *env,786MTLContext *mtlc, BMTLSDOps *dstOps,787jint x, jint y, jint width, jint height,788jint dx, jint dy)789{790#ifdef DEBUG791J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_CopyArea: bdst=%p [tex=%p] %dx%d | src (%d, %d), %dx%d -> dst (%d, %d)",792dstOps, dstOps->pTexture, ((id<MTLTexture>)dstOps->pTexture).width, ((id<MTLTexture>)dstOps->pTexture).height, x, y, width, height, dx, dy);793#endif //DEBUG794jint texWidth = ((id<MTLTexture>)dstOps->pTexture).width;795jint texHeight = ((id<MTLTexture>)dstOps->pTexture).height;796797SurfaceDataBounds srcBounds, dstBounds;798srcBounds.x1 = x;799srcBounds.y1 = y;800srcBounds.x2 = srcBounds.x1 + width;801srcBounds.y2 = srcBounds.y1 + height;802dstBounds.x1 = x + dx;803dstBounds.y1 = y + dy;804dstBounds.x2 = dstBounds.x1 + width;805dstBounds.y2 = dstBounds.y1 + height;806807SurfaceData_IntersectBoundsXYXY(&srcBounds, 0, 0, texWidth, texHeight);808SurfaceData_IntersectBoundsXYXY(&dstBounds, 0, 0, texWidth, texHeight);809SurfaceData_IntersectBlitBounds(&dstBounds, &srcBounds, -dx, -dy);810811int srcWidth = (srcBounds.x2 - srcBounds.x1);812int srcHeight = (srcBounds.y2 - srcBounds.y1);813814if ((srcBounds.x1 < srcBounds.x2 && srcBounds.y1 < srcBounds.y2) &&815(dstBounds.x1 < dstBounds.x2 && dstBounds.y1 < dstBounds.y2))816{817@autoreleasepool {818struct TxtVertex quadTxVerticesBuffer[6];819MTLPooledTextureHandle * interHandle =820[mtlc.texturePool getTexture:texWidth821height:texHeight822format:MTLPixelFormatBGRA8Unorm];823if (interHandle == nil) {824J2dTraceLn(J2D_TRACE_ERROR,825"MTLBlitLoops_CopyArea: texture handle is null");826return;827}828[[mtlc getCommandBufferWrapper] registerPooledTexture:interHandle];829830id<MTLTexture> interTexture = interHandle.texture;831832/*833* We need to consider common states like clipping while834* performing copyArea, thats why we use drawTex2Tex and835* get encoder with appropriate state from EncoderManager836* and not directly use MTLBlitCommandEncoder for texture copy.837*/838839// copy content to intermediate texture840drawTex2Tex(mtlc, dstOps->pTexture, interTexture, dstOps->isOpaque,841JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR,8420, 0, texWidth, texHeight, 0, 0, texWidth, texHeight);843844// copy content with appropriate bounds to destination texture845drawTex2Tex(mtlc, interTexture, dstOps->pTexture, JNI_FALSE,846dstOps->isOpaque, INTERPOLATION_NEAREST_NEIGHBOR,847srcBounds.x1, srcBounds.y1, srcBounds.x2, srcBounds.y2,848dstBounds.x1, dstBounds.y1, dstBounds.x2, dstBounds.y2);849[mtlc.encoderManager endEncoder];850MTLCommandBufferWrapper * cbwrapper =851[mtlc pullCommandBufferWrapper];852id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];853[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {854[cbwrapper release];855}];856[commandbuf commit];857}858}859}860861862