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 extern MTLPixelFormat PixelFormats[]; 43 extern void J2dTraceImpl(int level, jboolean cr, const char *string, ...); 44 45 void fillTxQuad( 46 struct TxtVertex * txQuadVerts, 47 jint sx1, jint sy1, jint sx2, jint sy2, jint sw, jint sh, 48 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2, jdouble dw, jdouble dh 49 ) { 50 const float nsx1 = sx1/(float)sw; 51 const float nsy1 = sy1/(float)sh; 52 const float nsx2 = sx2/(float)sw; 53 const float nsy2 = sy2/(float)sh; 54 55 txQuadVerts[0].position[0] = dx1; 56 txQuadVerts[0].position[1] = dy1; 57 txQuadVerts[0].position[2] = 0; 58 txQuadVerts[0].txtpos[0] = nsx1; 59 txQuadVerts[0].txtpos[1] = nsy1; 60 61 txQuadVerts[1].position[0] = dx2; 62 txQuadVerts[1].position[1] = dy1; 63 txQuadVerts[1].position[2] = 0; 64 txQuadVerts[1].txtpos[0] = nsx2; 65 txQuadVerts[1].txtpos[1] = nsy1; 66 67 txQuadVerts[2].position[0] = dx2; 68 txQuadVerts[2].position[1] = dy2; 69 txQuadVerts[2].position[2] = 0; 70 txQuadVerts[2].txtpos[0] = nsx2; 71 txQuadVerts[2].txtpos[1] = nsy2; 72 73 txQuadVerts[3].position[0] = dx2; 74 txQuadVerts[3].position[1] = dy2; 75 txQuadVerts[3].position[2] = 0; 76 txQuadVerts[3].txtpos[0] = nsx2; 77 txQuadVerts[3].txtpos[1] = nsy2; 78 79 txQuadVerts[4].position[0] = dx1; 80 txQuadVerts[4].position[1] = dy2; 81 txQuadVerts[4].position[2] = 0; 82 txQuadVerts[4].txtpos[0] = nsx1; 83 txQuadVerts[4].txtpos[1] = nsy2; 84 85 txQuadVerts[5].position[0] = dx1; 86 txQuadVerts[5].position[1] = dy1; 87 txQuadVerts[5].position[2] = 0; 88 txQuadVerts[5].txtpos[0] = nsx1; 89 txQuadVerts[5].txtpos[1] = nsy1; 90 } 91 92 /** 93 * Inner loop used for copying a source MTL "Surface" (window, pbuffer, 94 * etc.) to a destination OpenGL "Surface". Note that the same surface can 95 * be used as both the source and destination, as is the case in a copyArea() 96 * operation. This method is invoked from MTLBlitLoops_IsoBlit() as well as 97 * MTLBlitLoops_CopyArea(). 98 * 99 * The standard glCopyPixels() mechanism is used to copy the source region 100 * into the destination region. If the regions have different dimensions, 101 * the source will be scaled into the destination as appropriate (only 102 * nearest neighbor filtering will be applied for simple scale operations). 103 */ 104 static void 105 MTLBlitSurfaceToSurface(MTLContext *mtlc, BMTLSDOps *srcOps, BMTLSDOps *dstOps, 106 jint sx1, jint sy1, jint sx2, jint sy2, 107 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) 108 { 109 //TODO 110 //J2dTraceNotImplPrimitive("MTLBlitSurfaceToSurface"); 111 } 112 113 static void drawTex2Tex(MTLContext *mtlc, 114 id<MTLTexture> src, id<MTLTexture> dst, 115 jboolean rtt, jint hint, 116 jint sx1, jint sy1, jint sx2, jint sy2, 117 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) 118 { 119 if (mtlc == NULL || src == nil || dst == nil) 120 return; 121 122 // J2dTraceLn2(J2D_TRACE_VERBOSE, "_drawTex2Tex: src tex=%p, dst tex=%p", src, dst); 123 // J2dTraceLn4(J2D_TRACE_VERBOSE, " sw=%d sh=%d dw=%d dh=%d", src.width, src.height, dst.width, dst.height); 124 // J2dTraceLn4(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d", sx1, sy1, sx2, sy2); 125 // J2dTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f", dx1, dy1, dx2, dy2); 126 127 id<MTLRenderCommandEncoder> encoder = [mtlc createSamplingEncoderForDest:dst]; 128 129 130 const jboolean normalize = !mtlc.useTransform; 131 struct TxtVertex quadTxVerticesBuffer[6]; 132 fillTxQuad(quadTxVerticesBuffer, sx1, sy1, sx2, sy2, src.width, src.height, dx1, dy1, dx2, dy2, dst.width, dst.height); 133 134 [encoder setVertexBytes:quadTxVerticesBuffer length:sizeof(quadTxVerticesBuffer) atIndex:MeshVertexBuffer]; 135 [encoder setFragmentTexture:src atIndex: 0]; 136 [encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6]; 137 [encoder endEncoding]; 138 } 139 140 /** 141 * Inner loop used for copying a source MTL "Texture" to a destination 142 * MTL "Surface". This method is invoked from MTLBlitLoops_IsoBlit(). 143 * 144 * This method will copy, scale, or transform the source texture into the 145 * destination depending on the transform state, as established in 146 * and MTLContext_SetTransform(). If the source texture is 147 * transformed in any way when rendered into the destination, the filtering 148 * method applied is determined by the hint parameter (can be GL_NEAREST or 149 * GL_LINEAR). 150 */ 151 static void 152 MTLBlitTextureToSurface(MTLContext *mtlc, 153 BMTLSDOps *srcOps, BMTLSDOps *dstOps, 154 jboolean rtt, jint hint, 155 jint sx1, jint sy1, jint sx2, jint sy2, 156 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) 157 { 158 id<MTLTexture> srcTex = srcOps->pTexture; 159 160 #ifdef DEBUG 161 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_IsoBlit [via sampling]: bsrc=%p [tex=%p], bdst=%p [tex=%p] | s (%dx%d) -> d (%dx%d) | src (%d, %d, %d, %d) -> dst (%1.2f, %1.2f, %1.2f, %1.2f)", srcOps, srcOps->pTexture, dstOps, dstOps->pTexture, srcTex.width, srcTex.height, dstOps->width, dstOps->height, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); 162 #endif //DEBUG 163 164 drawTex2Tex(mtlc, srcOps->pTexture, dstOps->pTexture, rtt, hint, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); 165 } 166 167 /** 168 * Inner loop used for copying a source system memory ("Sw") surface to a 169 * destination MTL "Surface". This method is invoked from 170 * MTLBlitLoops_Blit(). 171 * 172 * The standard glDrawPixels() mechanism is used to copy the source region 173 * into the destination region. If the regions have different 174 * dimensions, the source will be scaled into the destination 175 * as appropriate (only nearest neighbor filtering will be applied for simple 176 * scale operations). 177 */ 178 179 static void 180 MTLBlitSwToSurfaceViaTexture(MTLContext *ctx, SurfaceDataRasInfo *srcInfo, BMTLSDOps * bmtlsdOps, 181 MTPixelFormat *pf, 182 jint sx1, jint sy1, jint sx2, jint sy2, 183 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) 184 { 185 if (bmtlsdOps == NULL || bmtlsdOps->pTexture == NULL) { 186 J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitSwToSurfaceViaTexture: dest is null"); 187 return; 188 } 189 190 const int sw = sx2 - sx1; 191 const int sh = sy2 - sy1; 192 id<MTLTexture> dest = bmtlsdOps->pTexture; 193 194 #ifdef DEBUG 195 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_Blit [via pooled texture]: bdst=%p [tex=%p], sw=%d, sh=%d | src (%d, %d, %d, %d) -> dst (%1.2f, %1.2f, %1.2f, %1.2f)", bmtlsdOps, dest, sw, sh, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); 196 #endif //DEBUG 197 198 id<MTLTexture> texBuff = [ctx.texturePool getTexture:sw height:sh format:MTLPixelFormatBGRA8Unorm]; 199 if (texBuff == nil) { 200 J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitSwToSurfaceViaTexture: can't obtain temporary texture object from pool"); 201 return; 202 } 203 MTLRegion region = MTLRegionMake2D(0, 0, sw, sh); 204 [texBuff replaceRegion:region mipmapLevel:0 withBytes:srcInfo->rasBase bytesPerRow:srcInfo->scanStride]; // texBuff is locked for current frame 205 206 drawTex2Tex(ctx, texBuff, dest, 0, 0, 0, 0, sw, sh, dx1, dy1, dx2, dy2); 207 } 208 209 /** 210 * Inner loop used for copying a source system memory ("Sw") surface or 211 * MTL "Surface" to a destination OpenGL "Surface", using an MTL texture 212 * tile as an intermediate surface. This method is invoked from 213 * MTLBlitLoops_Blit() for "Sw" surfaces and MTLBlitLoops_IsoBlit() for 214 * "Surface" surfaces. 215 * 216 * This method is used to transform the source surface into the destination. 217 * Pixel rectangles cannot be arbitrarily transformed (without the 218 * GL_EXT_pixel_transform extension, which is not supported on most modern 219 * hardware). However, texture mapped quads do respect the GL_MODELVIEW 220 * transform matrix, so we use textures here to perform the transform 221 * operation. This method uses a tile-based approach in which a small 222 * subregion of the source surface is copied into a cached texture tile. The 223 * texture tile is then mapped into the appropriate location in the 224 * destination surface. 225 * 226 * REMIND: this only works well using GL_NEAREST for the filtering mode 227 * (GL_LINEAR causes visible stitching problems between tiles, 228 * but this can be fixed by making use of texture borders) 229 */ 230 static void 231 MTLBlitToSurfaceViaTexture(MTLContext *mtlc, SurfaceDataRasInfo *srcInfo, 232 MTPixelFormat *pf, MTLSDOps *srcOps, 233 jboolean swsurface, jint hint, 234 jint sx1, jint sy1, jint sx2, jint sy2, 235 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) 236 { 237 //TODO 238 //J2dTraceNotImplPrimitive("MTLBlitToSurfaceViaTexture"); 239 } 240 241 /** 242 * Inner loop used for copying a source system memory ("Sw") surface to a 243 * destination OpenGL "Texture". This method is invoked from 244 * MTLBlitLoops_Blit(). 245 * 246 * The source surface is effectively loaded into the MTL texture object, 247 * which must have already been initialized by MTLSD_initTexture(). Note 248 * that this method is only capable of copying the source surface into the 249 * destination surface (i.e. no scaling or general transform is allowed). 250 * This restriction should not be an issue as this method is only used 251 * currently to cache a static system memory image into an MTL texture in 252 * a hidden-acceleration situation. 253 */ 254 static void 255 MTLBlitSwToTexture(SurfaceDataRasInfo *srcInfo, MTPixelFormat *pf, 256 MTLSDOps *dstOps, 257 jint dx1, jint dy1, jint dx2, jint dy2) 258 { 259 //TODO 260 //J2dTraceNotImplPrimitive("MTLBlitSwToTexture"); 261 } 262 263 /** 264 * General blit method for copying a native MTL surface (of type "Surface" 265 * or "Texture") to another MTL "Surface". If texture is JNI_TRUE, this 266 * method will invoke the Texture->Surface inner loop; otherwise, one of the 267 * Surface->Surface inner loops will be invoked, depending on the transform 268 * state. 269 * 270 * REMIND: we can trick these blit methods into doing XOR simply by passing 271 * in the (pixel ^ xorpixel) as the pixel value and preceding the 272 * blit with a fillrect... 273 */ 274 void 275 MTLBlitLoops_IsoBlit(JNIEnv *env, 276 MTLContext *mtlc, jlong pSrcOps, jlong pDstOps, 277 jboolean xform, jint hint, 278 jboolean texture, jboolean rtt, 279 jint sx1, jint sy1, jint sx2, jint sy2, 280 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) 281 { 282 BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps); 283 BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps); 284 285 RETURN_IF_NULL(srcOps); 286 RETURN_IF_NULL(dstOps); 287 288 id<MTLTexture> srcTex = srcOps->pTexture; 289 id<MTLTexture> dstTex = dstOps->pTexture; 290 if (mtlc == NULL || srcTex == nil || srcTex == nil) { 291 J2dTraceLn2(J2D_TRACE_ERROR, "MTLBlitLoops_IsoBlit: surface is null (stex=%p, dtex=%p)", srcTex, dstTex); 292 return; 293 } 294 295 const jint sw = sx2 - sx1; 296 const jint sh = sy2 - sy1; 297 const jdouble dw = dx2 - dx1; 298 const jdouble dh = dy2 - dy1; 299 300 if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) { 301 J2dTraceLn4(J2D_TRACE_WARNING, "MTLBlitLoops_IsoBlit: invalid dimensions: sw=%d, sh%d, dw=%d, dh=%d", sw, sh, dw, dh); 302 return; 303 } 304 305 SurfaceDataRasInfo srcInfo; 306 srcInfo.bounds.x1 = sx1; 307 srcInfo.bounds.y1 = sy1; 308 srcInfo.bounds.x2 = sx2; 309 srcInfo.bounds.y2 = sy2; 310 SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds, 0, 0, srcOps->width, srcOps->height); 311 312 if (srcInfo.bounds.x2 <= srcInfo.bounds.x1 || srcInfo.bounds.y2 <= srcInfo.bounds.y1) { 313 J2dTraceLn(J2D_TRACE_VERBOSE, "MTLBlitLoops_IsoBlit: source rectangle doesn't intersect with source surface bounds"); 314 J2dTraceLn6(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d sw=%d sh=%d", sx1, sy1, sx2, sy2, srcOps->width, srcOps->height); 315 J2dTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f", dx1, dy1, dx2, dy2); 316 return; 317 } 318 319 if (srcInfo.bounds.x1 != sx1) { 320 dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw); 321 sx1 = srcInfo.bounds.x1; 322 } 323 if (srcInfo.bounds.y1 != sy1) { 324 dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh); 325 sy1 = srcInfo.bounds.y1; 326 } 327 if (srcInfo.bounds.x2 != sx2) { 328 dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw); 329 sx2 = srcInfo.bounds.x2; 330 } 331 if (srcInfo.bounds.y2 != sy2) { 332 dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh); 333 sy2 = srcInfo.bounds.y2; 334 } 335 336 const jboolean useBlitEncoder = 337 mtlc.isBlendingDisabled 338 && fabs(dx2 - dx1 - sx2 + sx1) < 0.001f && fabs(dy2 - dy1 - sy2 + sy1) < 0.001f // dimensions are equal (TODO: check that dx1,dy1 is integer) 339 && !mtlc.useTransform; // TODO: check whether transform is simple translate (and use blitEncoder in this case) 340 if (useBlitEncoder) { 341 #ifdef DEBUG 342 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_IsoBlit [via blitEncoder]: bdst=%p [tex=%p] %dx%d | src (%d, %d, %d, %d) -> dst (%1.2f, %1.2f, %1.2f, %1.2f)", dstOps, dstTex, dstTex.width, dstTex.height, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); 343 #endif //DEBUG 344 id <MTLBlitCommandEncoder> blitEncoder = [mtlc createBlitEncoder]; 345 [blitEncoder copyFromTexture:srcTex sourceSlice:0 sourceLevel:0 sourceOrigin:MTLOriginMake(sx1, sy1, 0) sourceSize:MTLSizeMake(sx2 - sx1, sy2 - sy1, 1) toTexture:dstTex destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(dx1, dy1, 0)]; 346 [blitEncoder endEncoding]; 347 } else { 348 // TODO: support other flags 349 MTLBlitTextureToSurface(mtlc, srcOps, dstOps, rtt, hint, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); 350 } 351 } 352 353 /** 354 * General blit method for copying a system memory ("Sw") surface to a native 355 * MTL surface (of type "Surface" or "Texture"). If texture is JNI_TRUE, 356 * this method will invoke the Sw->Texture inner loop; otherwise, one of the 357 * Sw->Surface inner loops will be invoked, depending on the transform state. 358 */ 359 void 360 MTLBlitLoops_Blit(JNIEnv *env, 361 MTLContext *mtlc, jlong pSrcOps, jlong pDstOps, 362 jboolean xform, jint hint, 363 jint srctype, jboolean texture, 364 jint sx1, jint sy1, jint sx2, jint sy2, 365 jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2) 366 { 367 RETURN_IF_NULL(jlong_to_ptr(pSrcOps)); 368 RETURN_IF_NULL(jlong_to_ptr(pDstOps)); 369 370 SurfaceDataOps *srcOps = (SurfaceDataOps *)jlong_to_ptr(pSrcOps); 371 BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps); 372 SurfaceDataRasInfo srcInfo; 373 MTLPixelFormat pf = MTLPixelFormatBGRA8Unorm;//PixelFormats[srctype]; 374 375 if (dstOps == NULL || dstOps->pTexture == NULL) { 376 J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: dest is null"); 377 return; 378 } 379 id<MTLTexture> dest = dstOps->pTexture; 380 if (dx1 < 0) { 381 sx1 += dx1; 382 dx1 = 0; 383 } 384 if (dx2 > dest.width) { 385 sx2 -= dx2 - dest.width; 386 dx2 = dest.width; 387 } 388 if (dy1 < 0) { 389 sy1 += dy1; 390 dy1 = 0; 391 } 392 if (dy2 > dest.height) { 393 sy2 -= dy2 - dest.height; 394 dy2 = dest.height; 395 } 396 jint sw = sx2 - sx1; 397 jint sh = sy2 - sy1; 398 jdouble dw = dx2 - dx1; 399 jdouble dh = dy2 - dy1; 400 401 if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0 || srctype < 0) { 402 J2dTraceLn(J2D_TRACE_WARNING, "MTLBlitLoops_Blit: invalid dimensions or srctype"); 403 return; 404 } 405 406 srcInfo.bounds.x1 = sx1; 407 srcInfo.bounds.y1 = sy1; 408 srcInfo.bounds.x2 = sx2; 409 srcInfo.bounds.y2 = sy2; 410 411 if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) != SD_SUCCESS) { 412 J2dTraceLn(J2D_TRACE_WARNING, "MTLBlitLoops_Blit: could not acquire lock"); 413 return; 414 } 415 416 J2dTraceLn5(J2D_TRACE_VERBOSE, "MTLBlitLoops_Blit: pf=%d texture=%d srctype=%d xform=%d hint=%d", pf, texture, srctype, xform, hint); 417 418 if (srcInfo.bounds.x2 > srcInfo.bounds.x1 && srcInfo.bounds.y2 > srcInfo.bounds.y1) { 419 srcOps->GetRasInfo(env, srcOps, &srcInfo); 420 if (srcInfo.rasBase) { 421 if (srcInfo.bounds.x1 != sx1) { 422 dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw); 423 sx1 = srcInfo.bounds.x1; 424 } 425 if (srcInfo.bounds.y1 != sy1) { 426 dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh); 427 sy1 = srcInfo.bounds.y1; 428 } 429 if (srcInfo.bounds.x2 != sx2) { 430 dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw); 431 sx2 = srcInfo.bounds.x2; 432 } 433 if (srcInfo.bounds.y2 != sy2) { 434 dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh); 435 sy2 = srcInfo.bounds.y2; 436 } 437 438 // NOTE: if (texture) => dest coordinates will always be integers since we only ever do a straight copy from sw to texture. 439 const jboolean useReplaceRegion = texture || 440 (mtlc.isBlendingDisabled 441 && fabs(dx2 - dx1 - sx2 + sx1) < 0.001f && fabs(dy2 - dy1 - sy2 + sy1) < 0.001f // dimensions are equal (TODO: check that dx1,dy1 is integer) 442 && !mtlc.useTransform); // TODO: check whether transform is simple translate (and use replaceRegion in this case) 443 if (useReplaceRegion) { 444 MTLRegion region = MTLRegionMake2D(dx1, dy1, dx2 - dx1, dy2 - dy1); 445 #ifdef DEBUG 446 J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_Blit [replaceRegion]: bdst=%p [tex=%p] %dx%d | src (%d, %d, %d, %d) -> dst (%1.2f, %1.2f, %1.2f, %1.2f)", dstOps, dest, dest.width, dest.height, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); 447 #endif //DEBUG 448 [dest replaceRegion:region mipmapLevel:0 withBytes:srcInfo.rasBase bytesPerRow:srcInfo.scanStride]; // executed at CPU (sync), TODO: lock dest for current frame 449 } else { 450 MTLBlitSwToSurfaceViaTexture(mtlc, &srcInfo, dstOps, &pf, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); 451 } 452 } 453 SurfaceData_InvokeRelease(env, srcOps, &srcInfo); 454 } 455 SurfaceData_InvokeUnlock(env, srcOps, &srcInfo); 456 } 457 458 /** 459 * Specialized blit method for copying a native MTL "Surface" (pbuffer, 460 * window, etc.) to a system memory ("Sw") surface. 461 */ 462 void 463 MTLBlitLoops_SurfaceToSwBlit(JNIEnv *env, MTLContext *mtlc, 464 jlong pSrcOps, jlong pDstOps, jint dsttype, 465 jint srcx, jint srcy, jint dstx, jint dsty, 466 jint width, jint height) 467 { 468 //TODO 469 //J2dTraceNotImplPrimitive("MTLBlitLoops_SurfaceToSwBlit"); 470 } 471 472 void 473 MTLBlitLoops_CopyArea(JNIEnv *env, 474 MTLContext *mtlc, BMTLSDOps *dstOps, 475 jint x, jint y, jint width, jint height, 476 jint dx, jint dy) 477 { 478 //TODO 479 //J2dTraceNotImplPrimitive("MTLBlitLoops_CopyArea"); 480 } 481 482 #endif /* !HEADLESS */