1 /* 2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #ifndef HEADLESS 27 28 #include <jni.h> 29 #include <jlong.h> 30 31 #include "SurfaceData.h" 32 #include "MTLBlitLoops.h" 33 #include "MTLRenderQueue.h" 34 #include "MTLSurfaceData.h" 35 #include "MTLUtils.h" 36 #include "GraphicsPrimitiveMgr.h" 37 38 #include <stdlib.h> // malloc 39 #include <string.h> // memcpy 40 #include "IntArgbPre.h" 41 42 #import <Accelerate/Accelerate.h> 43 44 //#define TRACE_ISOBLIT 45 //#define TRACE_BLIT 46 //#define DEBUG_ISOBLIT 47 //#define DEBUG_BLIT 48 49 typedef struct { 50 MTLPixelFormat format; 51 jboolean hasAlpha; 52 jboolean isPremult; 53 const uint8_t * permuteMap; 54 } MTLRasterFormatInfo; 55 56 // 0 denotes the alpha channel, 1 the red channel, 2 the green channel, and 3 the blue channel. 57 const uint8_t permuteMap_rgbx[4] = { 1, 2, 3, 0 }; 58 const uint8_t permuteMap_bgrx[4] = { 3, 2, 1, 0 }; 59 60 static uint8_t revertPerm(const uint8_t * perm, uint8_t pos) { 61 for (int c = 0; c < 4; ++c) { 62 if (perm[c] == pos) 63 return c; 64 } 65 return -1; 66 } 67 68 #define uint2swizzle(channel) (channel == 0 ? MTLTextureSwizzleAlpha : (channel == 1 ? MTLTextureSwizzleRed : (channel == 2 ? MTLTextureSwizzleGreen : (channel == 3 ? MTLTextureSwizzleBlue : MTLTextureSwizzleZero)))) 69 70 /** 71 * This table contains the "pixel formats" for all system memory surfaces 72 * that Metal is capable of handling, indexed by the "PF_" constants defined 73 * in MTLLSurfaceData.java. These pixel formats contain information that is 74 * passed to Metal when copying from a system memory ("Sw") surface to 75 * an Metal surface 76 */ 77 MTLRasterFormatInfo RasterFormatInfos[] = { 78 { MTLPixelFormatBGRA8Unorm, 1, 0, NULL }, /* 0 - IntArgb */ // Argb (in java notation) 79 { MTLPixelFormatBGRA8Unorm, 1, 1, NULL }, /* 1 - IntArgbPre */ 80 { MTLPixelFormatBGRA8Unorm, 0, 1, NULL }, /* 2 - IntRgb */ // xrgb 81 { MTLPixelFormatBGRA8Unorm, 0, 1, permuteMap_rgbx }, /* 3 - IntRgbx */ 82 { MTLPixelFormatRGBA8Unorm, 0, 1, NULL }, /* 4 - IntBgr */ // xbgr 83 { MTLPixelFormatBGRA8Unorm, 0, 1, permuteMap_bgrx }, /* 5 - IntBgrx */ 84 85 // TODO: support 2-byte formats 86 // { GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 87 // 2, 0, 1, }, /* 7 - Ushort555Rgb */ 88 // { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 89 // 2, 0, 1, }, /* 8 - Ushort555Rgbx*/ 90 // { GL_LUMINANCE, GL_UNSIGNED_BYTE, 91 // 1, 0, 1, }, /* 9 - ByteGray */ 92 // { GL_LUMINANCE, GL_UNSIGNED_SHORT, 93 // 2, 0, 1, }, /*10 - UshortGray */ 94 // { GL_BGR, GL_UNSIGNED_BYTE, 95 // 1, 0, 1, }, /*11 - ThreeByteBgr */ 96 }; 97 98 extern void J2dTraceImpl(int level, jboolean cr, const char *string, ...); 99 100 void fillTxQuad( 101 struct TxtVertex * txQuadVerts, 102 jint sx1, jint sy1, jint sx2, jint sy2, jint sw, jint sh, 103 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2, jdouble dw, jdouble dh 104 ) { 105 const float nsx1 = sx1/(float)sw; 106 const float nsy1 = sy1/(float)sh; 107 const float nsx2 = sx2/(float)sw; 108 const float nsy2 = sy2/(float)sh; 109 110 txQuadVerts[0].position[0] = dx1; 111 txQuadVerts[0].position[1] = dy1; 112 txQuadVerts[0].txtpos[0] = nsx1; 113 txQuadVerts[0].txtpos[1] = nsy1; 114 115 txQuadVerts[1].position[0] = dx2; 116 txQuadVerts[1].position[1] = dy1; 117 txQuadVerts[1].txtpos[0] = nsx2; 118 txQuadVerts[1].txtpos[1] = nsy1; 119 120 txQuadVerts[2].position[0] = dx2; 121 txQuadVerts[2].position[1] = dy2; 122 txQuadVerts[2].txtpos[0] = nsx2; 123 txQuadVerts[2].txtpos[1] = nsy2; 124 125 txQuadVerts[3].position[0] = dx2; 126 txQuadVerts[3].position[1] = dy2; 127 txQuadVerts[3].txtpos[0] = nsx2; 128 txQuadVerts[3].txtpos[1] = nsy2; 129 130 txQuadVerts[4].position[0] = dx1; 131 txQuadVerts[4].position[1] = dy2; 132 txQuadVerts[4].txtpos[0] = nsx1; 133 txQuadVerts[4].txtpos[1] = nsy2; 134 135 txQuadVerts[5].position[0] = dx1; 136 txQuadVerts[5].position[1] = dy1; 137 txQuadVerts[5].txtpos[0] = nsx1; 138 txQuadVerts[5].txtpos[1] = nsy1; 139 } 140 141 //#define TRACE_drawTex2Tex 142 143 void drawTex2Tex(MTLContext *mtlc, 144 id<MTLTexture> src, id<MTLTexture> dst, 145 jboolean isSrcOpaque, jboolean isDstOpaque, jint hint, 146 jint sx1, jint sy1, jint sx2, jint sy2, 147 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) 148 { 149 #ifdef TRACE_drawTex2Tex 150 J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "drawTex2Tex: src tex=%p, dst tex=%p", src, dst); 151 J2dRlsTraceLn4(J2D_TRACE_VERBOSE, " sw=%d sh=%d dw=%d dh=%d", src.width, src.height, dst.width, dst.height); 152 J2dRlsTraceLn4(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d", sx1, sy1, sx2, sy2); 153 J2dRlsTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f", dx1, dy1, dx2, dy2); 154 #endif //TRACE_drawTex2Tex 155 156 id<MTLRenderCommandEncoder> encoder = [mtlc.encoderManager getTextureEncoder:dst 157 isSrcOpaque:isSrcOpaque 158 isDstOpaque:isDstOpaque 159 interpolation:hint 160 ]; 161 162 struct TxtVertex quadTxVerticesBuffer[6]; 163 fillTxQuad(quadTxVerticesBuffer, sx1, sy1, sx2, sy2, src.width, src.height, dx1, dy1, dx2, dy2, dst.width, dst.height); 164 165 [encoder setVertexBytes:quadTxVerticesBuffer length:sizeof(quadTxVerticesBuffer) atIndex:MeshVertexBuffer]; 166 [encoder setFragmentTexture:src atIndex: 0]; 167 [encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6]; 168 } 169 170 static 171 id<MTLTexture> replaceTextureRegion(id<MTLTexture> dest, const SurfaceDataRasInfo * srcInfo, const MTLRasterFormatInfo * rfi, int dx1, int dy1, int dx2, int dy2) { 172 const int dw = dx2 - dx1; 173 const int dh = dy2 - dy1; 174 175 const void * raster = srcInfo->rasBase; 176 id<MTLTexture> result = nil; 177 if (rfi->permuteMap != NULL) { 178 #if defined(__MAC_10_15) && __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_15 179 if (@available(macOS 10.15, *)) { 180 @autoreleasepool { 181 const uint8_t swzRed = revertPerm(rfi->permuteMap, 1); 182 const uint8_t swzGreen = revertPerm(rfi->permuteMap, 2); 183 const uint8_t swzBlue = revertPerm(rfi->permuteMap, 3); 184 const uint8_t swzAlpha = revertPerm(rfi->permuteMap, 0); 185 MTLTextureSwizzleChannels swizzle = MTLTextureSwizzleChannelsMake( 186 uint2swizzle(swzRed), 187 uint2swizzle(swzGreen), 188 uint2swizzle(swzBlue), 189 rfi->hasAlpha ? uint2swizzle(swzAlpha) : MTLTextureSwizzleOne 190 ); 191 result = [dest 192 newTextureViewWithPixelFormat:MTLPixelFormatBGRA8Unorm 193 textureType:MTLTextureType2D 194 levels:NSMakeRange(0, 1) slices:NSMakeRange(0, 1) 195 swizzle:swizzle]; 196 J2dTraceLn5(J2D_TRACE_VERBOSE, "replaceTextureRegion [use swizzle for pooled]: %d, %d, %d, %d, hasA=%d", 197 swizzle.red, swizzle.green, swizzle.blue, swizzle.alpha, rfi->hasAlpha); 198 } 199 } else 200 #endif // __MAC_10_15 && __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_15 201 { 202 // perform raster conversion 203 // invoked only from rq-thread, so use static buffers 204 // but it's better to use thread-local buffers (or special buffer manager) 205 const int destRasterSize = dw*dh*4; 206 207 static int bufferSize = 0; 208 static void * buffer = NULL; 209 if (buffer == NULL || bufferSize < destRasterSize) { 210 bufferSize = destRasterSize; 211 buffer = realloc(buffer, bufferSize); 212 } 213 if (buffer == NULL) { 214 J2dTraceLn1(J2D_TRACE_ERROR, "replaceTextureRegion: can't alloc buffer for raster conversion, size=%d", bufferSize); 215 bufferSize = 0; 216 return nil; 217 } 218 vImage_Buffer srcBuf; 219 srcBuf.height = dh; 220 srcBuf.width = dw; 221 srcBuf.rowBytes = srcInfo->scanStride; 222 srcBuf.data = srcInfo->rasBase; 223 224 vImage_Buffer destBuf; 225 destBuf.height = dh; 226 destBuf.width = dw; 227 destBuf.rowBytes = dw*4; 228 destBuf.data = buffer; 229 230 vImagePermuteChannels_ARGB8888(&srcBuf, &destBuf, rfi->permuteMap, kvImageNoFlags); 231 raster = buffer; 232 233 J2dTraceLn5(J2D_TRACE_VERBOSE, "replaceTextureRegion [use conversion]: %d, %d, %d, %d, hasA=%d", 234 rfi->permuteMap[0], rfi->permuteMap[1], rfi->permuteMap[2], rfi->permuteMap[3], rfi->hasAlpha); 235 } 236 } 237 238 MTLRegion region = MTLRegionMake2D(dx1, dy1, dw, dh); 239 if (result != nil) 240 dest = result; 241 [dest replaceRegion:region mipmapLevel:0 withBytes:raster bytesPerRow:srcInfo->scanStride]; 242 return result; 243 } 244 245 /** 246 * Inner loop used for copying a source system memory ("Sw") surface to a 247 * destination MTL "Surface". This method is invoked from 248 * MTLBlitLoops_Blit(). 249 */ 250 251 static void 252 MTLBlitSwToTextureViaPooledTexture( 253 MTLContext *mtlc, SurfaceDataRasInfo *srcInfo, BMTLSDOps * bmtlsdOps, 254 MTLRasterFormatInfo * rfi, jboolean useBlitEncoder, jint hint, 255 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) 256 { 257 const int sw = srcInfo->bounds.x2 - srcInfo->bounds.x1; 258 const int sh = srcInfo->bounds.y2 - srcInfo->bounds.y1; 259 id<MTLTexture> dest = bmtlsdOps->pTexture; 260 261 MTLPooledTextureHandle * texHandle = [mtlc.texturePool getTexture:sw height:sh format:rfi->format]; 262 if (texHandle == nil) { 263 J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitSwToTextureViaPooledTexture: can't obtain temporary texture object from pool"); 264 return; 265 } 266 [[mtlc getCommandBufferWrapper] registerPooledTexture:texHandle]; 267 [texHandle release]; 268 269 id<MTLTexture> texBuff = texHandle.texture; 270 id<MTLTexture> swizzledTexture = replaceTextureRegion(texBuff, srcInfo, rfi, 0, 0, sw, sh); 271 if (useBlitEncoder) { 272 id <MTLBlitCommandEncoder> blitEncoder = [mtlc.encoderManager createBlitEncoder]; 273 [blitEncoder copyFromTexture:swizzledTexture != nil ? swizzledTexture : texBuff 274 sourceSlice:0 275 sourceLevel:0 276 sourceOrigin:MTLOriginMake(0, 0, 0) 277 sourceSize:MTLSizeMake(sw, sh, 1) 278 toTexture:dest 279 destinationSlice:0 280 destinationLevel:0 281 destinationOrigin:MTLOriginMake(dx1, dy1, 0)]; 282 [blitEncoder endEncoding]; 283 } else { 284 drawTex2Tex(mtlc, swizzledTexture != nil ? swizzledTexture : texBuff, dest, !rfi->hasAlpha, bmtlsdOps->isOpaque, hint, 285 0, 0, sw, sh, dx1, dy1, dx2, dy2); 286 } 287 288 if (swizzledTexture != nil) { 289 [swizzledTexture release]; 290 } 291 } 292 293 static 294 jboolean isIntegerAndUnscaled( 295 jint sx1, jint sy1, jint sx2, jint sy2, 296 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2 297 ) { 298 const jdouble epsilon = 0.0001f; 299 300 // check that dx1,dy1 is integer 301 if (fabs(dx1 - (int)dx1) > epsilon || fabs(dy1 - (int)dy1) > epsilon) { 302 return JNI_FALSE; 303 } 304 // check that destSize equals srcSize 305 if (fabs(dx2 - dx1 - sx2 + sx1) > epsilon || fabs(dy2 - dy1 - sy2 + sy1) > epsilon) { 306 return JNI_FALSE; 307 } 308 return JNI_TRUE; 309 } 310 311 static 312 jboolean clipDestCoords( 313 jdouble *dx1, jdouble *dy1, jdouble *dx2, jdouble *dy2, 314 jint *sx1, jint *sy1, jint *sx2, jint *sy2, 315 jint destW, jint destH, const MTLScissorRect * clipRect 316 ) { 317 // Trim destination rect by clip-rect (or dest.bounds) 318 const jint sw = *sx2 - *sx1; 319 const jint sh = *sy2 - *sy1; 320 const jdouble dw = *dx2 - *dx1; 321 const jdouble dh = *dy2 - *dy1; 322 323 jdouble dcx1 = 0; 324 jdouble dcx2 = destW; 325 jdouble dcy1 = 0; 326 jdouble dcy2 = destH; 327 if (clipRect != NULL) { 328 if (clipRect->x > dcx1) 329 dcx1 = clipRect->x; 330 const int maxX = clipRect->x + clipRect->width; 331 if (dcx2 > maxX) 332 dcx2 = maxX; 333 if (clipRect->y > dcy1) 334 dcy1 = clipRect->y; 335 const int maxY = clipRect->y + clipRect->height; 336 if (dcy2 > maxY) 337 dcy2 = maxY; 338 339 if (dcx1 >= dcx2) { 340 J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcx1=%1.2f, dcx2=%1.2f", dcx1, dcx2); 341 dcx1 = dcx2; 342 } 343 if (dcy1 >= dcy2) { 344 J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcy1=%1.2f, dcy2=%1.2f", dcy1, dcy2); 345 dcy1 = dcy2; 346 } 347 } 348 if (*dx2 <= dcx1 || *dx1 >= dcx2 || *dy2 <= dcy1 || *dy1 >= dcy2) { 349 J2dTraceLn(J2D_TRACE_INFO, "\tclipDestCoords: dest rect doesn't intersect clip area"); 350 return JNI_FALSE; 351 } 352 if (*dx1 < dcx1) { 353 J2dTraceLn2(J2D_TRACE_VERBOSE, "\t\tdx1=%1.2f, will be clipped to %1.2f", *dx1, dcx1); 354 *sx1 += (jint)((dcx1 - *dx1) * (sw/dw)); 355 *dx1 = dcx1; 356 } 357 if (*dx2 > dcx2) { 358 J2dTraceLn2(J2D_TRACE_VERBOSE, "\t\tdx2=%1.2f, will be clipped to %1.2f", *dx2, dcx2); 359 *sx2 -= (jint)((*dx2 - dcx2) * (sw/dw)); 360 *dx2 = dcx2; 361 } 362 if (*dy1 < dcy1) { 363 J2dTraceLn2(J2D_TRACE_VERBOSE, "\t\tdy1=%1.2f, will be clipped to %1.2f", *dy1, dcy1); 364 *sy1 += (jint)((dcy1 - *dy1) * (sh/dh)); 365 *dy1 = dcy1; 366 } 367 if (*dy2 > dcy2) { 368 J2dTraceLn2(J2D_TRACE_VERBOSE, "\t\tdy2=%1.2f, will be clipped to %1.2f", *dy2, dcy2); 369 *sy2 -= (jint)((*dy2 - dcy2) * (sh/dh)); 370 *dy2 = dcy2; 371 } 372 return JNI_TRUE; 373 } 374 375 /** 376 * General blit method for copying a native MTL surface to another MTL "Surface". 377 * Parameter texture == true forces to use 'texture' codepath (dest coordinates will always be integers). 378 * Parameter xform == true only when AffineTransform is used (invoked only from TransformBlit, dest coordinates will always be integers). 379 */ 380 void 381 MTLBlitLoops_IsoBlit(JNIEnv *env, 382 MTLContext *mtlc, jlong pSrcOps, jlong pDstOps, 383 jboolean xform, jint hint, jboolean texture, 384 jint sx1, jint sy1, jint sx2, jint sy2, 385 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) 386 { 387 BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps); 388 BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps); 389 390 RETURN_IF_NULL(mtlc); 391 RETURN_IF_NULL(srcOps); 392 RETURN_IF_NULL(dstOps); 393 394 id<MTLTexture> srcTex = srcOps->pTexture; 395 id<MTLTexture> dstTex = dstOps->pTexture; 396 if (srcTex == nil || srcTex == nil) { 397 J2dTraceLn2(J2D_TRACE_ERROR, "MTLBlitLoops_IsoBlit: surface is null (stex=%p, dtex=%p)", srcTex, dstTex); 398 return; 399 } 400 401 const jint sw = sx2 - sx1; 402 const jint sh = sy2 - sy1; 403 const jdouble dw = dx2 - dx1; 404 const jdouble dh = dy2 - dy1; 405 406 if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) { 407 J2dTraceLn4(J2D_TRACE_WARNING, "MTLBlitLoops_IsoBlit: invalid dimensions: sw=%d, sh%d, dw=%d, dh=%d", sw, sh, dw, dh); 408 return; 409 } 410 411 #ifdef DEBUG_ISOBLIT 412 if ((xform == JNI_TRUE) != (mtlc.useTransform == JNI_TRUE)) { 413 J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE, 414 "MTLBlitLoops_IsoBlit state error: xform=%d, mtlc.useTransform=%d, texture=%d", 415 xform, mtlc.useTransform, texture); 416 } 417 #endif // DEBUG_ISOBLIT 418 419 clipDestCoords( 420 &dx1, &dy1, &dx2, &dy2, 421 &sx1, &sy1, &sx2, &sy2, 422 dstTex.width, dstTex.height, [mtlc.clip getRect] 423 ); 424 425 SurfaceDataBounds bounds; 426 bounds.x1 = sx1; 427 bounds.y1 = sy1; 428 bounds.x2 = sx2; 429 bounds.y2 = sy2; 430 SurfaceData_IntersectBoundsXYXY(&bounds, 0, 0, srcOps->width, srcOps->height); 431 432 if (bounds.x2 <= bounds.x1 || bounds.y2 <= bounds.y1) { 433 J2dTraceLn(J2D_TRACE_VERBOSE, "MTLBlitLoops_IsoBlit: source rectangle doesn't intersect with source surface bounds"); 434 J2dTraceLn6(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d sw=%d sh=%d", sx1, sy1, sx2, sy2, srcOps->width, srcOps->height); 435 J2dTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f", dx1, dy1, dx2, dy2); 436 return; 437 } 438 439 if (bounds.x1 != sx1) { 440 dx1 += (bounds.x1 - sx1) * (dw / sw); 441 sx1 = bounds.x1; 442 } 443 if (bounds.y1 != sy1) { 444 dy1 += (bounds.y1 - sy1) * (dh / sh); 445 sy1 = bounds.y1; 446 } 447 if (bounds.x2 != sx2) { 448 dx2 += (bounds.x2 - sx2) * (dw / sw); 449 sx2 = bounds.x2; 450 } 451 if (bounds.y2 != sy2) { 452 dy2 += (bounds.y2 - sy2) * (dh / sh); 453 sy2 = bounds.y2; 454 } 455 456 #ifdef TRACE_ISOBLIT 457 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE, 458 "MTLBlitLoops_IsoBlit [tx=%d, xf=%d, AC=%s]: src=%s, dst=%s | (%d, %d, %d, %d)->(%1.2f, %1.2f, %1.2f, %1.2f)", 459 texture, xform, [mtlc getCompositeDescription].cString, 460 getSurfaceDescription(srcOps).cString, getSurfaceDescription(dstOps).cString, 461 sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); 462 #endif //TRACE_ISOBLIT 463 464 if (!texture && !xform 465 && [mtlc isBlendingDisabled:srcOps->isOpaque] 466 && isIntegerAndUnscaled(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2) 467 && (dstOps->isOpaque || !srcOps->isOpaque) 468 ) { 469 #ifdef TRACE_ISOBLIT 470 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via blitEncoder]"); 471 #endif //TRACE_ISOBLIT 472 473 id <MTLBlitCommandEncoder> blitEncoder = [mtlc.encoderManager createBlitEncoder]; 474 [blitEncoder copyFromTexture:srcTex 475 sourceSlice:0 476 sourceLevel:0 477 sourceOrigin:MTLOriginMake(sx1, sy1, 0) 478 sourceSize:MTLSizeMake(sx2 - sx1, sy2 - sy1, 1) 479 toTexture:dstTex 480 destinationSlice:0 481 destinationLevel:0 482 destinationOrigin:MTLOriginMake(dx1, dy1, 0)]; 483 [blitEncoder endEncoding]; 484 return; 485 } 486 487 #ifdef TRACE_ISOBLIT 488 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via sampling]"); 489 #endif //TRACE_ISOBLIT 490 drawTex2Tex(mtlc, srcTex, dstTex, srcOps->isOpaque, dstOps->isOpaque, hint, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); 491 } 492 493 /** 494 * General blit method for copying a system memory ("Sw") surface to a native MTL surface. 495 * Parameter texture == true only in SwToTextureBlit (straight copy from sw to texture), dest coordinates will always be integers. 496 * Parameter xform == true only when AffineTransform is used (invoked only from TransformBlit, dest coordinates will always be integers). 497 */ 498 void 499 MTLBlitLoops_Blit(JNIEnv *env, 500 MTLContext *mtlc, jlong pSrcOps, jlong pDstOps, 501 jboolean xform, jint hint, 502 jint srctype, jboolean texture, 503 jint sx1, jint sy1, jint sx2, jint sy2, 504 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) 505 { 506 SurfaceDataOps *srcOps = (SurfaceDataOps *)jlong_to_ptr(pSrcOps); 507 BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps); 508 509 RETURN_IF_NULL(mtlc); 510 RETURN_IF_NULL(srcOps); 511 RETURN_IF_NULL(dstOps); 512 513 id<MTLTexture> dest = dstOps->pTexture; 514 if (dest == NULL) { 515 J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: dest is null"); 516 return; 517 } 518 if (srctype < 0 || srctype >= sizeof(RasterFormatInfos)/ sizeof(MTLRasterFormatInfo)) { 519 J2dTraceLn1(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: source pixel format %d isn't supported", srctype); 520 return; 521 } 522 const jint sw = sx2 - sx1; 523 const jint sh = sy2 - sy1; 524 const jdouble dw = dx2 - dx1; 525 const jdouble dh = dy2 - dy1; 526 527 if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) { 528 J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: invalid dimensions"); 529 return; 530 } 531 532 #ifdef DEBUG_BLIT 533 if ( 534 (xform == JNI_TRUE) != (mtlc.useTransform == JNI_TRUE) 535 || (xform && texture) 536 ) { 537 J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE, 538 "MTLBlitLoops_Blit state error: xform=%d, mtlc.useTransform=%d, texture=%d", 539 xform, mtlc.useTransform, texture); 540 } 541 if (texture) { 542 if (!isIntegerAndUnscaled(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)) { 543 J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE, 544 "MTLBlitLoops_Blit state error: texture=true, but src and dst dimensions aren't equal or dest coords aren't integers"); 545 } 546 if (!dstOps->isOpaque && !RasterFormatInfos[srctype].hasAlpha) { 547 J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE, 548 "MTLBlitLoops_Blit state error: texture=true, but dest has alpha and source hasn't alpha, can't use texture-codepath"); 549 } 550 } 551 #endif // DEBUG_BLIT 552 553 clipDestCoords( 554 &dx1, &dy1, &dx2, &dy2, 555 &sx1, &sy1, &sx2, &sy2, 556 dest.width, dest.height, texture ? NULL : [mtlc.clip getRect] 557 ); 558 559 SurfaceDataRasInfo srcInfo; 560 srcInfo.bounds.x1 = sx1; 561 srcInfo.bounds.y1 = sy1; 562 srcInfo.bounds.x2 = sx2; 563 srcInfo.bounds.y2 = sy2; 564 565 // NOTE: This function will modify the contents of the bounds field to represent the maximum available raster data. 566 if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) != SD_SUCCESS) { 567 J2dTraceLn(J2D_TRACE_WARNING, "MTLBlitLoops_Blit: could not acquire lock"); 568 return; 569 } 570 571 if (srcInfo.bounds.x2 > srcInfo.bounds.x1 && srcInfo.bounds.y2 > srcInfo.bounds.y1) { 572 srcOps->GetRasInfo(env, srcOps, &srcInfo); 573 if (srcInfo.rasBase) { 574 if (srcInfo.bounds.x1 != sx1) { 575 const int dx = srcInfo.bounds.x1 - sx1; 576 dx1 += dx * (dw / sw); 577 } 578 if (srcInfo.bounds.y1 != sy1) { 579 const int dy = srcInfo.bounds.y1 - sy1; 580 dy1 += dy * (dh / sh); 581 } 582 if (srcInfo.bounds.x2 != sx2) { 583 const int dx = srcInfo.bounds.x2 - sx2; 584 dx2 += dx * (dw / sw); 585 } 586 if (srcInfo.bounds.y2 != sy2) { 587 const int dy = srcInfo.bounds.y2 - sy2; 588 dy2 += dy * (dh / sh); 589 } 590 591 #ifdef TRACE_BLIT 592 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE, 593 "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)", 594 texture, xform, [mtlc getCompositeDescription].cString, 595 getSurfaceDescription(dstOps).cString, srcOps, 596 sx2 - sx1, sy2 - sy1, 597 RasterFormatInfos[srctype].hasAlpha ? 0 : 1, RasterFormatInfos[srctype].isPremult ? 1 : 0, 598 sx1, sy1, sx2, sy2, 599 dx1, dy1, dx2, dy2); 600 #endif //TRACE_BLIT 601 602 MTLRasterFormatInfo rfi = RasterFormatInfos[srctype]; 603 const jboolean useReplaceRegion = texture || 604 ([mtlc isBlendingDisabled:!rfi.hasAlpha] 605 && !xform 606 && isIntegerAndUnscaled(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)); 607 608 if (useReplaceRegion) { 609 if (dstOps->isOpaque || rfi.hasAlpha) { 610 #ifdef TRACE_BLIT 611 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [replaceTextureRegion]"); 612 #endif //TRACE_BLIT 613 replaceTextureRegion(dest, &srcInfo, &rfi, (int) dx1, (int) dy1, (int) dx2, (int) dy2); 614 } else { 615 #ifdef TRACE_BLIT 616 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via pooled + blit]"); 617 #endif //TRACE_BLIT 618 MTLBlitSwToTextureViaPooledTexture(mtlc, &srcInfo, dstOps, &rfi, true, hint, dx1, dy1, dx2, dy2); 619 } 620 } else { // !useReplaceRegion 621 #ifdef TRACE_BLIT 622 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via pooled texture]"); 623 #endif //TRACE_BLIT 624 MTLBlitSwToTextureViaPooledTexture(mtlc, &srcInfo, dstOps, &rfi, false, hint, dx1, dy1, dx2, dy2); 625 } 626 } 627 SurfaceData_InvokeRelease(env, srcOps, &srcInfo); 628 } 629 SurfaceData_InvokeUnlock(env, srcOps, &srcInfo); 630 } 631 632 /** 633 * Specialized blit method for copying a native MTL "Surface" (pbuffer, 634 * window, etc.) to a system memory ("Sw") surface. 635 */ 636 void 637 MTLBlitLoops_SurfaceToSwBlit(JNIEnv *env, MTLContext *mtlc, 638 jlong pSrcOps, jlong pDstOps, jint dsttype, 639 jint srcx, jint srcy, jint dstx, jint dsty, 640 jint width, jint height) 641 { 642 J2dTraceLn6(J2D_TRACE_VERBOSE, "MTLBlitLoops_SurfaceToSwBlit: sx=%d sy=%d w=%d h=%d dx=%d dy=%d", srcx, srcy, width, height, dstx, dsty); 643 644 BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps); 645 SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps); 646 SurfaceDataRasInfo srcInfo, dstInfo; 647 648 if (dsttype < 0 || dsttype >= sizeof(RasterFormatInfos)/ sizeof(MTLRasterFormatInfo)) { 649 J2dTraceLn1(J2D_TRACE_ERROR, "MTLBlitLoops_SurfaceToSwBlit: destination pixel format %d isn't supported", dsttype); 650 return; 651 } 652 653 if (width <= 0 || height <= 0) { 654 J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_SurfaceToSwBlit: dimensions are non-positive"); 655 return; 656 } 657 658 RETURN_IF_NULL(srcOps); 659 RETURN_IF_NULL(dstOps); 660 RETURN_IF_NULL(mtlc); 661 662 srcInfo.bounds.x1 = srcx; 663 srcInfo.bounds.y1 = srcy; 664 srcInfo.bounds.x2 = srcx + width; 665 srcInfo.bounds.y2 = srcy + height; 666 dstInfo.bounds.x1 = dstx; 667 dstInfo.bounds.y1 = dsty; 668 dstInfo.bounds.x2 = dstx + width; 669 dstInfo.bounds.y2 = dsty + height; 670 671 if (dstOps->Lock(env, dstOps, &dstInfo, SD_LOCK_WRITE) != SD_SUCCESS) { 672 J2dTraceLn(J2D_TRACE_WARNING,"MTLBlitLoops_SurfaceToSwBlit: could not acquire dst lock"); 673 return; 674 } 675 676 SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds, 677 0, 0, srcOps->width, srcOps->height); 678 SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds, 679 srcx - dstx, srcy - dsty); 680 681 if (srcInfo.bounds.x2 > srcInfo.bounds.x1 && 682 srcInfo.bounds.y2 > srcInfo.bounds.y1) 683 { 684 dstOps->GetRasInfo(env, dstOps, &dstInfo); 685 if (dstInfo.rasBase) { 686 void *pDst = dstInfo.rasBase; 687 688 srcx = srcInfo.bounds.x1; 689 srcy = srcInfo.bounds.y1; 690 dstx = dstInfo.bounds.x1; 691 dsty = dstInfo.bounds.y1; 692 width = srcInfo.bounds.x2 - srcInfo.bounds.x1; 693 height = srcInfo.bounds.y2 - srcInfo.bounds.y1; 694 695 pDst = PtrAddBytes(pDst, dstx * dstInfo.pixelStride); 696 pDst = PtrPixelsRow(pDst, dsty, dstInfo.scanStride); 697 698 // this accounts for lower-left origin of the source region 699 srcx = srcOps->xOffset + srcx; 700 srcy = srcOps->yOffset + srcOps->height - srcy - height; 701 const int srcLength = width * height * 4; // NOTE: assume that src format is MTLPixelFormatBGRA8Unorm 702 703 #ifdef DEBUG 704 void *pDstEnd = dstInfo.rasBase + (height - 1)*dstInfo.scanStride + width*dstInfo.pixelStride; 705 if (pDst + srcLength > pDstEnd) { 706 J2dTraceLn6(J2D_TRACE_ERROR, "MTLBlitLoops_SurfaceToSwBlit: length mismatch: dstx=%d, dsty=%d, w=%d, h=%d, pixStride=%d, scanStride=%d", 707 dstx, dsty, width, height, dstInfo.pixelStride, dstInfo.scanStride); 708 return; 709 } 710 #endif //DEBUG 711 712 // Create MTLBuffer (or use static) 713 MTLRasterFormatInfo rfi = RasterFormatInfos[dsttype]; 714 const jboolean directCopy = rfi.permuteMap == NULL; 715 716 id<MTLBuffer> mtlbuf; 717 #ifdef USE_STATIC_BUFFER 718 if (directCopy) { 719 // NOTE: theoretically we can use newBufferWithBytesNoCopy, but pDst must be allocated with special API 720 // mtlbuf = [mtlc.device 721 // newBufferWithBytesNoCopy:pDst 722 // length:(NSUInteger) srcLength 723 // options:MTLResourceCPUCacheModeDefaultCache 724 // deallocator:nil]; 725 // 726 // see https://developer.apple.com/documentation/metal/mtldevice/1433382-newbufferwithbytesnocopy?language=objc 727 // 728 // The storage allocation of the returned new MTLBuffer object is the same as the pointer input value. 729 // The existing memory allocation must be covered by a single VM region, typically allocated with vm_allocate or mmap. 730 // Memory allocated by malloc is specifically disallowed. 731 } 732 733 static id<MTLBuffer> mtlIntermediateBuffer = nil; // need to reimplement with MTLBufferManager 734 if (mtlIntermediateBuffer == nil || mtlIntermediateBuffer.length < srcLength) { 735 if (mtlIntermediateBuffer != nil) { 736 [mtlIntermediateBuffer release]; 737 } 738 mtlIntermediateBuffer = [mtlc.device newBufferWithLength:srcLength options:MTLResourceCPUCacheModeDefaultCache]; 739 } 740 mtlbuf = mtlIntermediateBuffer; 741 #else // USE_STATIC_BUFFER 742 mtlbuf = [mtlc.device newBufferWithLength:width*height*4 options:MTLResourceStorageModeShared]; 743 #endif // USE_STATIC_BUFFER 744 745 // Read from surface into MTLBuffer 746 // NOTE: using of separate blitCommandBuffer can produce errors (draw into surface (with general cmd-buf) 747 // can be unfinished when reading raster from blit cmd-buf). 748 // Consider to use [mtlc.encoderManager createBlitEncoder] and [mtlc commitCommandBuffer:JNI_TRUE]; 749 J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLBlitLoops_SurfaceToSwBlit: source texture %p", srcOps->pTexture); 750 751 id<MTLCommandBuffer> cb = [mtlc createBlitCommandBuffer]; 752 id<MTLBlitCommandEncoder> blitEncoder = [cb blitCommandEncoder]; 753 [blitEncoder synchronizeTexture:srcOps->pTexture slice:0 level:0]; 754 [blitEncoder copyFromTexture:srcOps->pTexture 755 sourceSlice:0 756 sourceLevel:0 757 sourceOrigin:MTLOriginMake(srcx, srcy, 0) 758 sourceSize:MTLSizeMake(width, height, 1) 759 toBuffer:mtlbuf 760 destinationOffset:0 /*offset already taken in: pDst = PtrAddBytes(pDst, dstx * dstInfo.pixelStride)*/ 761 destinationBytesPerRow:width*4 762 destinationBytesPerImage:width * height*4]; 763 [blitEncoder endEncoding]; 764 765 // Commit and wait for reading complete 766 [cb commit]; 767 [cb waitUntilCompleted]; 768 769 // Perform conversion if necessary 770 if (directCopy) { 771 memcpy(pDst, mtlbuf.contents, srcLength); 772 } else { 773 J2dTraceLn6(J2D_TRACE_VERBOSE,"MTLBlitLoops_SurfaceToSwBlit: dsttype=%d, raster conversion will be performed, dest rfi: %d, %d, %d, %d, hasA=%d", 774 dsttype, rfi.permuteMap[0], rfi.permuteMap[1], rfi.permuteMap[2], rfi.permuteMap[3], rfi.hasAlpha); 775 776 // perform raster conversion: mtlIntermediateBuffer(8888) -> pDst(rfi) 777 // invoked only from rq-thread, so use static buffers 778 // but it's better to use thread-local buffers (or special buffer manager) 779 vImage_Buffer srcBuf; 780 srcBuf.height = height; 781 srcBuf.width = width; 782 srcBuf.rowBytes = 4*width; 783 srcBuf.data = mtlbuf.contents; 784 785 vImage_Buffer destBuf; 786 destBuf.height = height; 787 destBuf.width = width; 788 destBuf.rowBytes = dstInfo.scanStride; 789 destBuf.data = pDst; 790 791 vImagePermuteChannels_ARGB8888(&srcBuf, &destBuf, rfi.permuteMap, kvImageNoFlags); 792 } 793 #ifndef USE_STATIC_BUFFER 794 [mtlbuf release]; 795 #endif // USE_STATIC_BUFFER 796 } 797 SurfaceData_InvokeRelease(env, dstOps, &dstInfo); 798 } 799 SurfaceData_InvokeUnlock(env, dstOps, &dstInfo); 800 } 801 802 void 803 MTLBlitLoops_CopyArea(JNIEnv *env, 804 MTLContext *mtlc, BMTLSDOps *dstOps, 805 jint x, jint y, jint width, jint height, 806 jint dx, jint dy) 807 { 808 #ifdef DEBUG 809 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_CopyArea: bdst=%p [tex=%p] %dx%d | src (%d, %d), %dx%d -> dst (%d, %d)", 810 dstOps, dstOps->pTexture, ((id<MTLTexture>)dstOps->pTexture).width, ((id<MTLTexture>)dstOps->pTexture).height, x, y, width, height, dx, dy); 811 #endif //DEBUG 812 id <MTLBlitCommandEncoder> blitEncoder = [mtlc.encoderManager createBlitEncoder]; 813 [blitEncoder 814 copyFromTexture:dstOps->pTexture 815 sourceSlice:0 sourceLevel:0 sourceOrigin:MTLOriginMake(x, y, 0) sourceSize:MTLSizeMake(width, height, 1) 816 toTexture:dstOps->pTexture destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(x + dx, y + dy, 0)]; 817 [blitEncoder endEncoding]; 818 819 // TODO: 820 // 1. check rect bounds 821 // 2. support CopyArea with extra-alpha (and with custom Composite if necessary) 822 } 823 824 #endif /* !HEADLESS */