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, 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 160 struct TxtVertex quadTxVerticesBuffer[6]; 161 fillTxQuad(quadTxVerticesBuffer, sx1, sy1, sx2, sy2, src.width, src.height, dx1, dy1, dx2, dy2, dst.width, dst.height); 162 163 [encoder setVertexBytes:quadTxVerticesBuffer length:sizeof(quadTxVerticesBuffer) atIndex:MeshVertexBuffer]; 164 [encoder setFragmentTexture:src atIndex: 0]; 165 [encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6]; 166 } 167 168 static 169 id<MTLTexture> replaceTextureRegion(id<MTLTexture> dest, const SurfaceDataRasInfo * srcInfo, const MTLRasterFormatInfo * rfi, int dx1, int dy1, int dx2, int dy2) { 170 const int dw = dx2 - dx1; 171 const int dh = dy2 - dy1; 172 173 const void * raster = srcInfo->rasBase; 174 id<MTLTexture> result = nil; 175 if (rfi->permuteMap != NULL) { 176 #if defined(__MAC_10_15) && __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_15 177 if (@available(macOS 10.15, *)) { 178 @autoreleasepool { 179 const uint8_t swzRed = revertPerm(rfi->permuteMap, 1); 180 const uint8_t swzGreen = revertPerm(rfi->permuteMap, 2); 181 const uint8_t swzBlue = revertPerm(rfi->permuteMap, 3); 182 const uint8_t swzAlpha = revertPerm(rfi->permuteMap, 0); 183 MTLTextureSwizzleChannels swizzle = MTLTextureSwizzleChannelsMake( 184 uint2swizzle(swzRed), 185 uint2swizzle(swzGreen), 186 uint2swizzle(swzBlue), 187 rfi->hasAlpha ? uint2swizzle(swzAlpha) : MTLTextureSwizzleOne 188 ); 189 result = [dest 190 newTextureViewWithPixelFormat:MTLPixelFormatBGRA8Unorm 191 textureType:MTLTextureType2D 192 levels:NSMakeRange(0, 1) slices:NSMakeRange(0, 1) 193 swizzle:swizzle]; 194 J2dTraceLn5(J2D_TRACE_VERBOSE, "replaceTextureRegion [use swizzle for pooled]: %d, %d, %d, %d, hasA=%d", 195 swizzle.red, swizzle.green, swizzle.blue, swizzle.alpha, rfi->hasAlpha); 196 } 197 } else 198 #endif // __MAC_10_15 && __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_15 199 { 200 // perform raster conversion 201 // invoked only from rq-thread, so use static buffers 202 // but it's better to use thread-local buffers (or special buffer manager) 203 const int destRasterSize = dw*dh*4; 204 205 static int bufferSize = 0; 206 static void * buffer = NULL; 207 if (buffer == NULL || bufferSize < destRasterSize) { 208 bufferSize = destRasterSize; 209 buffer = realloc(buffer, bufferSize); 210 } 211 if (buffer == NULL) { 212 J2dTraceLn1(J2D_TRACE_ERROR, "replaceTextureRegion: can't alloc buffer for raster conversion, size=%d", bufferSize); 213 bufferSize = 0; 214 return nil; 215 } 216 vImage_Buffer srcBuf; 217 srcBuf.height = dh; 218 srcBuf.width = dw; 219 srcBuf.rowBytes = srcInfo->scanStride; 220 srcBuf.data = srcInfo->rasBase; 221 222 vImage_Buffer destBuf; 223 destBuf.height = dh; 224 destBuf.width = dw; 225 destBuf.rowBytes = dw*4; 226 destBuf.data = buffer; 227 228 vImagePermuteChannels_ARGB8888(&srcBuf, &destBuf, rfi->permuteMap, kvImageNoFlags); 229 raster = buffer; 230 231 J2dTraceLn5(J2D_TRACE_VERBOSE, "replaceTextureRegion [use conversion]: %d, %d, %d, %d, hasA=%d", 232 rfi->permuteMap[0], rfi->permuteMap[1], rfi->permuteMap[2], rfi->permuteMap[3], rfi->hasAlpha); 233 } 234 } 235 236 MTLRegion region = MTLRegionMake2D(dx1, dy1, dw, dh); 237 if (result != nil) 238 dest = result; 239 [dest replaceRegion:region mipmapLevel:0 withBytes:raster bytesPerRow:srcInfo->scanStride]; 240 return result; 241 } 242 243 /** 244 * Inner loop used for copying a source system memory ("Sw") surface to a 245 * destination MTL "Surface". This method is invoked from 246 * MTLBlitLoops_Blit(). 247 */ 248 249 static void 250 MTLBlitSwToTextureViaPooledTexture( 251 MTLContext *mtlc, SurfaceDataRasInfo *srcInfo, BMTLSDOps * bmtlsdOps, 252 MTLRasterFormatInfo * rfi, jboolean useBlitEncoder, 253 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) 254 { 255 const int sw = srcInfo->bounds.x2 - srcInfo->bounds.x1; 256 const int sh = srcInfo->bounds.y2 - srcInfo->bounds.y1; 257 id<MTLTexture> dest = bmtlsdOps->pTexture; 258 259 MTLPooledTextureHandle * texHandle = [mtlc.texturePool getTexture:sw height:sh format:rfi->format]; 260 if (texHandle == nil) { 261 J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitSwToTextureViaPooledTexture: can't obtain temporary texture object from pool"); 262 return; 263 } 264 [[mtlc getCommandBufferWrapper] registerPooledTexture:texHandle]; 265 [texHandle release]; 266 267 id<MTLTexture> texBuff = texHandle.texture; 268 id<MTLTexture> swizzledTexture = replaceTextureRegion(texBuff, srcInfo, rfi, 0, 0, sw, sh); 269 if (useBlitEncoder) { 270 id <MTLBlitCommandEncoder> blitEncoder = [mtlc.encoderManager createBlitEncoder]; 271 [blitEncoder copyFromTexture:swizzledTexture != nil ? swizzledTexture : texBuff 272 sourceSlice:0 273 sourceLevel:0 274 sourceOrigin:MTLOriginMake(0, 0, 0) 275 sourceSize:MTLSizeMake(sw, sh, 1) 276 toTexture:dest 277 destinationSlice:0 278 destinationLevel:0 279 destinationOrigin:MTLOriginMake(dx1, dy1, 0)]; 280 [blitEncoder endEncoding]; 281 } else { 282 drawTex2Tex(mtlc, swizzledTexture != nil ? swizzledTexture : texBuff, dest, !rfi->hasAlpha, bmtlsdOps->isOpaque, 283 0, 0, sw, sh, dx1, dy1, dx2, dy2); 284 } 285 286 if (swizzledTexture != nil) { 287 [swizzledTexture release]; 288 } 289 } 290 291 static 292 jboolean isIntegerAndUnscaled( 293 jint sx1, jint sy1, jint sx2, jint sy2, 294 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2 295 ) { 296 const jdouble epsilon = 0.0001f; 297 298 // check that dx1,dy1 is integer 299 if (fabs(dx1 - (int)dx1) > epsilon || fabs(dy1 - (int)dy1) > epsilon) { 300 return JNI_FALSE; 301 } 302 // check that destSize equals srcSize 303 if (fabs(dx2 - dx1 - sx2 + sx1) > epsilon || fabs(dy2 - dy1 - sy2 + sy1) > epsilon) { 304 return JNI_FALSE; 305 } 306 return JNI_TRUE; 307 } 308 309 static 310 jboolean clipDestCoords( 311 jdouble *dx1, jdouble *dy1, jdouble *dx2, jdouble *dy2, 312 jint *sx1, jint *sy1, jint *sx2, jint *sy2, 313 jint destW, jint destH, const MTLScissorRect * clipRect 314 ) { 315 // Trim destination rect by clip-rect (or dest.bounds) 316 const jint sw = *sx2 - *sx1; 317 const jint sh = *sy2 - *sy1; 318 const jdouble dw = *dx2 - *dx1; 319 const jdouble dh = *dy2 - *dy1; 320 321 jdouble dcx1 = 0; 322 jdouble dcx2 = destW; 323 jdouble dcy1 = 0; 324 jdouble dcy2 = destH; 325 if (clipRect != NULL) { 326 if (clipRect->x > dcx1) 327 dcx1 = clipRect->x; 328 const int maxX = clipRect->x + clipRect->width; 329 if (dcx2 > maxX) 330 dcx2 = maxX; 331 if (clipRect->y > dcy1) 332 dcy1 = clipRect->y; 333 const int maxY = clipRect->y + clipRect->height; 334 if (dcy2 > maxY) 335 dcy2 = maxY; 336 337 if (dcx1 >= dcx2) { 338 J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcx1=%1.2f, dcx2=%1.2f", dcx1, dcx2); 339 dcx1 = dcx2; 340 } 341 if (dcy1 >= dcy2) { 342 J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcy1=%1.2f, dcy2=%1.2f", dcy1, dcy2); 343 dcy1 = dcy2; 344 } 345 } 346 if (*dx2 <= dcx1 || *dx1 >= dcx2 || *dy2 <= dcy1 || *dy1 >= dcy2) { 347 J2dTraceLn(J2D_TRACE_INFO, "\tclipDestCoords: dest rect doesn't intersect clip area"); 348 return JNI_FALSE; 349 } 350 if (*dx1 < dcx1) { 351 J2dTraceLn2(J2D_TRACE_VERBOSE, "\t\tdx1=%1.2f, will be clipped to %1.2f", *dx1, dcx1); 352 *sx1 += (jint)((dcx1 - *dx1) * (sw/dw)); 353 *dx1 = dcx1; 354 } 355 if (*dx2 > dcx2) { 356 J2dTraceLn2(J2D_TRACE_VERBOSE, "\t\tdx2=%1.2f, will be clipped to %1.2f", *dx2, dcx2); 357 *sx2 -= (jint)((*dx2 - dcx2) * (sw/dw)); 358 *dx2 = dcx2; 359 } 360 if (*dy1 < dcy1) { 361 J2dTraceLn2(J2D_TRACE_VERBOSE, "\t\tdy1=%1.2f, will be clipped to %1.2f", *dy1, dcy1); 362 *sy1 += (jint)((dcy1 - *dy1) * (sh/dh)); 363 *dy1 = dcy1; 364 } 365 if (*dy2 > dcy2) { 366 J2dTraceLn2(J2D_TRACE_VERBOSE, "\t\tdy2=%1.2f, will be clipped to %1.2f", *dy2, dcy2); 367 *sy2 -= (jint)((*dy2 - dcy2) * (sh/dh)); 368 *dy2 = dcy2; 369 } 370 return JNI_TRUE; 371 } 372 373 /** 374 * General blit method for copying a native MTL surface to another MTL "Surface". 375 * Parameter texture == true forces to use 'texture' codepath (dest coordinates will always be integers). 376 * Parameter xform == true only when AffineTransform is used (invoked only from TransformBlit, dest coordinates will always be integers). 377 */ 378 void 379 MTLBlitLoops_IsoBlit(JNIEnv *env, 380 MTLContext *mtlc, jlong pSrcOps, jlong pDstOps, 381 jboolean xform, jint hint, jboolean texture, 382 jint sx1, jint sy1, jint sx2, jint sy2, 383 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) 384 { 385 BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps); 386 BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps); 387 388 RETURN_IF_NULL(mtlc); 389 RETURN_IF_NULL(srcOps); 390 RETURN_IF_NULL(dstOps); 391 392 id<MTLTexture> srcTex = srcOps->pTexture; 393 id<MTLTexture> dstTex = dstOps->pTexture; 394 if (srcTex == nil || srcTex == nil) { 395 J2dTraceLn2(J2D_TRACE_ERROR, "MTLBlitLoops_IsoBlit: surface is null (stex=%p, dtex=%p)", srcTex, dstTex); 396 return; 397 } 398 399 const jint sw = sx2 - sx1; 400 const jint sh = sy2 - sy1; 401 const jdouble dw = dx2 - dx1; 402 const jdouble dh = dy2 - dy1; 403 404 if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) { 405 J2dTraceLn4(J2D_TRACE_WARNING, "MTLBlitLoops_IsoBlit: invalid dimensions: sw=%d, sh%d, dw=%d, dh=%d", sw, sh, dw, dh); 406 return; 407 } 408 409 #ifdef DEBUG_ISOBLIT 410 if ((xform == JNI_TRUE) != (mtlc.useTransform == JNI_TRUE)) { 411 J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE, 412 "MTLBlitLoops_IsoBlit state error: xform=%d, mtlc.useTransform=%d, texture=%d", 413 xform, mtlc.useTransform, texture); 414 } 415 #endif // DEBUG_ISOBLIT 416 417 clipDestCoords( 418 &dx1, &dy1, &dx2, &dy2, 419 &sx1, &sy1, &sx2, &sy2, 420 dstTex.width, dstTex.height, [mtlc.clip getRect] 421 ); 422 423 SurfaceDataBounds bounds; 424 bounds.x1 = sx1; 425 bounds.y1 = sy1; 426 bounds.x2 = sx2; 427 bounds.y2 = sy2; 428 SurfaceData_IntersectBoundsXYXY(&bounds, 0, 0, srcOps->width, srcOps->height); 429 430 if (bounds.x2 <= bounds.x1 || bounds.y2 <= bounds.y1) { 431 J2dTraceLn(J2D_TRACE_VERBOSE, "MTLBlitLoops_IsoBlit: source rectangle doesn't intersect with source surface bounds"); 432 J2dTraceLn6(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d sw=%d sh=%d", sx1, sy1, sx2, sy2, srcOps->width, srcOps->height); 433 J2dTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f", dx1, dy1, dx2, dy2); 434 return; 435 } 436 437 if (bounds.x1 != sx1) { 438 dx1 += (bounds.x1 - sx1) * (dw / sw); 439 sx1 = bounds.x1; 440 } 441 if (bounds.y1 != sy1) { 442 dy1 += (bounds.y1 - sy1) * (dh / sh); 443 sy1 = bounds.y1; 444 } 445 if (bounds.x2 != sx2) { 446 dx2 += (bounds.x2 - sx2) * (dw / sw); 447 sx2 = bounds.x2; 448 } 449 if (bounds.y2 != sy2) { 450 dy2 += (bounds.y2 - sy2) * (dh / sh); 451 sy2 = bounds.y2; 452 } 453 454 #ifdef TRACE_ISOBLIT 455 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE, 456 "MTLBlitLoops_IsoBlit [tx=%d, xf=%d, AC=%s]: src=%s, dst=%s | (%d, %d, %d, %d)->(%1.2f, %1.2f, %1.2f, %1.2f)", 457 texture, xform, [mtlc getCompositeDescription].cString, 458 getSurfaceDescription(srcOps).cString, getSurfaceDescription(dstOps).cString, 459 sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); 460 #endif //TRACE_ISOBLIT 461 462 if (!texture && !xform 463 && [mtlc isBlendingDisabled:srcOps->isOpaque] 464 && isIntegerAndUnscaled(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2) 465 && (dstOps->isOpaque || !srcOps->isOpaque) 466 ) { 467 #ifdef TRACE_ISOBLIT 468 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via blitEncoder]"); 469 #endif //TRACE_ISOBLIT 470 471 id <MTLBlitCommandEncoder> blitEncoder = [mtlc.encoderManager createBlitEncoder]; 472 [blitEncoder copyFromTexture:srcTex 473 sourceSlice:0 474 sourceLevel:0 475 sourceOrigin:MTLOriginMake(sx1, sy1, 0) 476 sourceSize:MTLSizeMake(sx2 - sx1, sy2 - sy1, 1) 477 toTexture:dstTex 478 destinationSlice:0 479 destinationLevel:0 480 destinationOrigin:MTLOriginMake(dx1, dy1, 0)]; 481 [blitEncoder endEncoding]; 482 return; 483 } 484 485 #ifdef TRACE_ISOBLIT 486 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via sampling]"); 487 #endif //TRACE_ISOBLIT 488 drawTex2Tex(mtlc, srcTex, dstTex, srcOps->isOpaque, dstOps->isOpaque, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); 489 } 490 491 /** 492 * General blit method for copying a system memory ("Sw") surface to a native MTL surface. 493 * Parameter texture == true only in SwToTextureBlit (straight copy from sw to texture), dest coordinates will always be integers. 494 * Parameter xform == true only when AffineTransform is used (invoked only from TransformBlit, dest coordinates will always be integers). 495 */ 496 void 497 MTLBlitLoops_Blit(JNIEnv *env, 498 MTLContext *mtlc, jlong pSrcOps, jlong pDstOps, 499 jboolean xform, jint hint, 500 jint srctype, jboolean texture, 501 jint sx1, jint sy1, jint sx2, jint sy2, 502 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) 503 { 504 SurfaceDataOps *srcOps = (SurfaceDataOps *)jlong_to_ptr(pSrcOps); 505 BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps); 506 507 RETURN_IF_NULL(mtlc); 508 RETURN_IF_NULL(srcOps); 509 RETURN_IF_NULL(dstOps); 510 511 id<MTLTexture> dest = dstOps->pTexture; 512 if (dest == NULL) { 513 J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: dest is null"); 514 return; 515 } 516 if (srctype < 0 || srctype >= sizeof(RasterFormatInfos)/ sizeof(MTLRasterFormatInfo)) { 517 J2dTraceLn1(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: source pixel format %d isn't supported", srctype); 518 return; 519 } 520 const jint sw = sx2 - sx1; 521 const jint sh = sy2 - sy1; 522 const jdouble dw = dx2 - dx1; 523 const jdouble dh = dy2 - dy1; 524 525 if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) { 526 J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: invalid dimensions"); 527 return; 528 } 529 530 #ifdef DEBUG_BLIT 531 if ( 532 (xform == JNI_TRUE) != (mtlc.useTransform == JNI_TRUE) 533 || (xform && texture) 534 ) { 535 J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE, 536 "MTLBlitLoops_Blit state error: xform=%d, mtlc.useTransform=%d, texture=%d", 537 xform, mtlc.useTransform, texture); 538 } 539 if (texture) { 540 if (!isIntegerAndUnscaled(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)) { 541 J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE, 542 "MTLBlitLoops_Blit state error: texture=true, but src and dst dimensions aren't equal or dest coords aren't integers"); 543 } 544 if (!dstOps->isOpaque && !RasterFormatInfos[srctype].hasAlpha) { 545 J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE, 546 "MTLBlitLoops_Blit state error: texture=true, but dest has alpha and source hasn't alpha, can't use texture-codepath"); 547 } 548 } 549 #endif // DEBUG_BLIT 550 551 clipDestCoords( 552 &dx1, &dy1, &dx2, &dy2, 553 &sx1, &sy1, &sx2, &sy2, 554 dest.width, dest.height, texture ? NULL : [mtlc.clip getRect] 555 ); 556 557 SurfaceDataRasInfo srcInfo; 558 srcInfo.bounds.x1 = sx1; 559 srcInfo.bounds.y1 = sy1; 560 srcInfo.bounds.x2 = sx2; 561 srcInfo.bounds.y2 = sy2; 562 563 // NOTE: This function will modify the contents of the bounds field to represent the maximum available raster data. 564 if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) != SD_SUCCESS) { 565 J2dTraceLn(J2D_TRACE_WARNING, "MTLBlitLoops_Blit: could not acquire lock"); 566 return; 567 } 568 569 if (srcInfo.bounds.x2 > srcInfo.bounds.x1 && srcInfo.bounds.y2 > srcInfo.bounds.y1) { 570 srcOps->GetRasInfo(env, srcOps, &srcInfo); 571 if (srcInfo.rasBase) { 572 if (srcInfo.bounds.x1 != sx1) { 573 const int dx = srcInfo.bounds.x1 - sx1; 574 dx1 += dx * (dw / sw); 575 } 576 if (srcInfo.bounds.y1 != sy1) { 577 const int dy = srcInfo.bounds.y1 - sy1; 578 dy1 += dy * (dh / sh); 579 } 580 if (srcInfo.bounds.x2 != sx2) { 581 const int dx = srcInfo.bounds.x2 - sx2; 582 dx2 += dx * (dw / sw); 583 } 584 if (srcInfo.bounds.y2 != sy2) { 585 const int dy = srcInfo.bounds.y2 - sy2; 586 dy2 += dy * (dh / sh); 587 } 588 589 #ifdef TRACE_BLIT 590 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE, 591 "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)", 592 texture, xform, [mtlc getCompositeDescription].cString, 593 getSurfaceDescription(dstOps).cString, srcOps, 594 sx2 - sx1, sy2 - sy1, 595 RasterFormatInfos[srctype].hasAlpha ? 0 : 1, RasterFormatInfos[srctype].isPremult ? 1 : 0, 596 sx1, sy1, sx2, sy2, 597 dx1, dy1, dx2, dy2); 598 #endif //TRACE_BLIT 599 600 MTLRasterFormatInfo rfi = RasterFormatInfos[srctype]; 601 const jboolean useReplaceRegion = texture || 602 ([mtlc isBlendingDisabled:!rfi.hasAlpha] 603 && !xform 604 && isIntegerAndUnscaled(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)); 605 606 if (useReplaceRegion) { 607 if (dstOps->isOpaque || rfi.hasAlpha) { 608 #ifdef TRACE_BLIT 609 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [replaceTextureRegion]"); 610 #endif //TRACE_BLIT 611 replaceTextureRegion(dest, &srcInfo, &rfi, (int) dx1, (int) dy1, (int) dx2, (int) dy2); 612 } else { 613 #ifdef TRACE_BLIT 614 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via pooled + blit]"); 615 #endif //TRACE_BLIT 616 MTLBlitSwToTextureViaPooledTexture(mtlc, &srcInfo, dstOps, &rfi, true, dx1, dy1, dx2, dy2); 617 } 618 } else { // !useReplaceRegion 619 #ifdef TRACE_BLIT 620 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE," [via pooled texture]"); 621 #endif //TRACE_BLIT 622 MTLBlitSwToTextureViaPooledTexture(mtlc, &srcInfo, dstOps, &rfi, false, dx1, dy1, dx2, dy2); 623 } 624 } 625 SurfaceData_InvokeRelease(env, srcOps, &srcInfo); 626 } 627 SurfaceData_InvokeUnlock(env, srcOps, &srcInfo); 628 } 629 630 /** 631 * Specialized blit method for copying a native MTL "Surface" (pbuffer, 632 * window, etc.) to a system memory ("Sw") surface. 633 */ 634 void 635 MTLBlitLoops_SurfaceToSwBlit(JNIEnv *env, MTLContext *mtlc, 636 jlong pSrcOps, jlong pDstOps, jint dsttype, 637 jint srcx, jint srcy, jint dstx, jint dsty, 638 jint width, jint height) 639 { 640 J2dTraceLn6(J2D_TRACE_VERBOSE, "MTLBlitLoops_SurfaceToSwBlit: sx=%d sy=%d w=%d h=%d dx=%d dy=%d", srcx, srcy, width, height, dstx, dsty); 641 642 BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps); 643 SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps); 644 SurfaceDataRasInfo srcInfo, dstInfo; 645 646 if (dsttype < 0 || dsttype >= sizeof(RasterFormatInfos)/ sizeof(MTLRasterFormatInfo)) { 647 J2dTraceLn1(J2D_TRACE_ERROR, "MTLBlitLoops_SurfaceToSwBlit: destination pixel format %d isn't supported", dsttype); 648 return; 649 } 650 651 if (width <= 0 || height <= 0) { 652 J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_SurfaceToSwBlit: dimensions are non-positive"); 653 return; 654 } 655 656 RETURN_IF_NULL(srcOps); 657 RETURN_IF_NULL(dstOps); 658 RETURN_IF_NULL(mtlc); 659 660 srcInfo.bounds.x1 = srcx; 661 srcInfo.bounds.y1 = srcy; 662 srcInfo.bounds.x2 = srcx + width; 663 srcInfo.bounds.y2 = srcy + height; 664 dstInfo.bounds.x1 = dstx; 665 dstInfo.bounds.y1 = dsty; 666 dstInfo.bounds.x2 = dstx + width; 667 dstInfo.bounds.y2 = dsty + height; 668 669 if (dstOps->Lock(env, dstOps, &dstInfo, SD_LOCK_WRITE) != SD_SUCCESS) { 670 J2dTraceLn(J2D_TRACE_WARNING,"MTLBlitLoops_SurfaceToSwBlit: could not acquire dst lock"); 671 return; 672 } 673 674 SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds, 675 0, 0, srcOps->width, srcOps->height); 676 SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds, 677 srcx - dstx, srcy - dsty); 678 679 if (srcInfo.bounds.x2 > srcInfo.bounds.x1 && 680 srcInfo.bounds.y2 > srcInfo.bounds.y1) 681 { 682 dstOps->GetRasInfo(env, dstOps, &dstInfo); 683 if (dstInfo.rasBase) { 684 void *pDst = dstInfo.rasBase; 685 686 srcx = srcInfo.bounds.x1; 687 srcy = srcInfo.bounds.y1; 688 dstx = dstInfo.bounds.x1; 689 dsty = dstInfo.bounds.y1; 690 width = srcInfo.bounds.x2 - srcInfo.bounds.x1; 691 height = srcInfo.bounds.y2 - srcInfo.bounds.y1; 692 693 pDst = PtrAddBytes(pDst, dstx * dstInfo.pixelStride); 694 pDst = PtrPixelsRow(pDst, dsty, dstInfo.scanStride); 695 696 // this accounts for lower-left origin of the source region 697 srcx = srcOps->xOffset + srcx; 698 srcy = srcOps->yOffset + srcOps->height - srcy - height; 699 const int srcLength = width * height * 4; // NOTE: assume that src format is MTLPixelFormatBGRA8Unorm 700 701 #ifdef DEBUG 702 void *pDstEnd = dstInfo.rasBase + (height - 1)*dstInfo.scanStride + width*dstInfo.pixelStride; 703 if (pDst + srcLength > pDstEnd) { 704 J2dTraceLn6(J2D_TRACE_ERROR, "MTLBlitLoops_SurfaceToSwBlit: length mismatch: dstx=%d, dsty=%d, w=%d, h=%d, pixStride=%d, scanStride=%d", 705 dstx, dsty, width, height, dstInfo.pixelStride, dstInfo.scanStride); 706 return; 707 } 708 #endif //DEBUG 709 710 // Create MTLBuffer (or use static) 711 MTLRasterFormatInfo rfi = RasterFormatInfos[dsttype]; 712 const jboolean directCopy = rfi.permuteMap == NULL; 713 714 id<MTLBuffer> mtlbuf; 715 #ifdef USE_STATIC_BUFFER 716 if (directCopy) { 717 // NOTE: theoretically we can use newBufferWithBytesNoCopy, but pDst must be allocated with special API 718 // mtlbuf = [mtlc.device 719 // newBufferWithBytesNoCopy:pDst 720 // length:(NSUInteger) srcLength 721 // options:MTLResourceCPUCacheModeDefaultCache 722 // deallocator:nil]; 723 // 724 // see https://developer.apple.com/documentation/metal/mtldevice/1433382-newbufferwithbytesnocopy?language=objc 725 // 726 // The storage allocation of the returned new MTLBuffer object is the same as the pointer input value. 727 // The existing memory allocation must be covered by a single VM region, typically allocated with vm_allocate or mmap. 728 // Memory allocated by malloc is specifically disallowed. 729 } 730 731 static id<MTLBuffer> mtlIntermediateBuffer = nil; // need to reimplement with MTLBufferManager 732 if (mtlIntermediateBuffer == nil || mtlIntermediateBuffer.length < srcLength) { 733 if (mtlIntermediateBuffer != nil) { 734 [mtlIntermediateBuffer release]; 735 } 736 mtlIntermediateBuffer = [mtlc.device newBufferWithLength:srcLength options:MTLResourceCPUCacheModeDefaultCache]; 737 } 738 mtlbuf = mtlIntermediateBuffer; 739 #else // USE_STATIC_BUFFER 740 mtlbuf = [mtlc.device newBufferWithLength:width*height*4 options:MTLResourceStorageModeShared]; 741 #endif // USE_STATIC_BUFFER 742 743 // Read from surface into MTLBuffer 744 // NOTE: using of separate blitCommandBuffer can produce errors (draw into surface (with general cmd-buf) 745 // can be unfinished when reading raster from blit cmd-buf). 746 // Consider to use [mtlc.encoderManager createBlitEncoder] and [mtlc commitCommandBuffer:JNI_TRUE]; 747 J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLBlitLoops_SurfaceToSwBlit: source texture %p", srcOps->pTexture); 748 749 id<MTLCommandBuffer> cb = [mtlc createBlitCommandBuffer]; 750 id<MTLBlitCommandEncoder> blitEncoder = [cb blitCommandEncoder]; 751 [blitEncoder synchronizeTexture:srcOps->pTexture slice:0 level:0]; 752 [blitEncoder copyFromTexture:srcOps->pTexture 753 sourceSlice:0 754 sourceLevel:0 755 sourceOrigin:MTLOriginMake(srcx, srcy, 0) 756 sourceSize:MTLSizeMake(width, height, 1) 757 toBuffer:mtlbuf 758 destinationOffset:0 /*offset already taken in: pDst = PtrAddBytes(pDst, dstx * dstInfo.pixelStride)*/ 759 destinationBytesPerRow:width*4 760 destinationBytesPerImage:width * height*4]; 761 [blitEncoder endEncoding]; 762 763 // Commit and wait for reading complete 764 [cb commit]; 765 [cb waitUntilCompleted]; 766 767 // Perform conversion if necessary 768 if (directCopy) { 769 memcpy(pDst, mtlbuf.contents, srcLength); 770 } else { 771 J2dTraceLn6(J2D_TRACE_VERBOSE,"MTLBlitLoops_SurfaceToSwBlit: dsttype=%d, raster conversion will be performed, dest rfi: %d, %d, %d, %d, hasA=%d", 772 dsttype, rfi.permuteMap[0], rfi.permuteMap[1], rfi.permuteMap[2], rfi.permuteMap[3], rfi.hasAlpha); 773 774 // perform raster conversion: mtlIntermediateBuffer(8888) -> pDst(rfi) 775 // invoked only from rq-thread, so use static buffers 776 // but it's better to use thread-local buffers (or special buffer manager) 777 vImage_Buffer srcBuf; 778 srcBuf.height = height; 779 srcBuf.width = width; 780 srcBuf.rowBytes = 4*width; 781 srcBuf.data = mtlbuf.contents; 782 783 vImage_Buffer destBuf; 784 destBuf.height = height; 785 destBuf.width = width; 786 destBuf.rowBytes = dstInfo.scanStride; 787 destBuf.data = pDst; 788 789 vImagePermuteChannels_ARGB8888(&srcBuf, &destBuf, rfi.permuteMap, kvImageNoFlags); 790 } 791 #ifndef USE_STATIC_BUFFER 792 [mtlbuf release]; 793 #endif // USE_STATIC_BUFFER 794 } 795 SurfaceData_InvokeRelease(env, dstOps, &dstInfo); 796 } 797 SurfaceData_InvokeUnlock(env, dstOps, &dstInfo); 798 } 799 800 void 801 MTLBlitLoops_CopyArea(JNIEnv *env, 802 MTLContext *mtlc, BMTLSDOps *dstOps, 803 jint x, jint y, jint width, jint height, 804 jint dx, jint dy) 805 { 806 #ifdef DEBUG 807 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_CopyArea: bdst=%p [tex=%p] %dx%d | src (%d, %d), %dx%d -> dst (%d, %d)", 808 dstOps, dstOps->pTexture, ((id<MTLTexture>)dstOps->pTexture).width, ((id<MTLTexture>)dstOps->pTexture).height, x, y, width, height, dx, dy); 809 #endif //DEBUG 810 id <MTLBlitCommandEncoder> blitEncoder = [mtlc.encoderManager createBlitEncoder]; 811 [blitEncoder 812 copyFromTexture:dstOps->pTexture 813 sourceSlice:0 sourceLevel:0 sourceOrigin:MTLOriginMake(x, y, 0) sourceSize:MTLSizeMake(width, height, 1) 814 toTexture:dstOps->pTexture destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(x + dx, y + dy, 0)]; 815 [blitEncoder endEncoding]; 816 817 // TODO: 818 // 1. check rect bounds 819 // 2. support CopyArea with extra-alpha (and with custom Composite if necessary) 820 } 821 822 #endif /* !HEADLESS */