Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLBlitLoops.m
41159 views
1
/*
2
* Copyright (c) 2019, 2021, 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
#include <jni.h>
27
#include <jlong.h>
28
29
#include "SurfaceData.h"
30
#include "MTLBlitLoops.h"
31
#include "MTLRenderQueue.h"
32
#include "MTLSurfaceData.h"
33
#include "MTLUtils.h"
34
#include "GraphicsPrimitiveMgr.h"
35
36
#include <string.h> // memcpy
37
#include "IntArgbPre.h"
38
39
#import <Accelerate/Accelerate.h>
40
41
#ifdef DEBUG
42
#define TRACE_ISOBLIT
43
#define TRACE_BLIT
44
#endif //DEBUG
45
//#define DEBUG_ISOBLIT
46
//#define DEBUG_BLIT
47
48
typedef struct {
49
// Consider deleting this field, since it's always MTLPixelFormatBGRA8Unorm
50
jboolean hasAlpha;
51
jboolean isPremult;
52
const uint8_t* swizzleMap;
53
} MTLRasterFormatInfo;
54
55
56
const uint8_t rgb_to_rgba[4] = {0, 1, 2, 3};
57
const uint8_t xrgb_to_rgba[4] = {1, 2, 3, 0};
58
const uint8_t bgr_to_rgba[4] = {2, 1, 0, 3};
59
const uint8_t xbgr_to_rgba[4] = {3, 2, 1, 0};
60
61
/**
62
* This table contains the "pixel formats" for all system memory surfaces
63
* that Metal is capable of handling, indexed by the "PF_" constants defined
64
* in MTLLSurfaceData.java. These pixel formats contain information that is
65
* passed to Metal when copying from a system memory ("Sw") surface to
66
* an Metal surface
67
*/
68
MTLRasterFormatInfo RasterFormatInfos[] = {
69
{ 1, 0, nil }, /* 0 - IntArgb */ // Argb (in java notation)
70
{ 1, 1, nil }, /* 1 - IntArgbPre */
71
{ 0, 1, rgb_to_rgba }, /* 2 - IntRgb */
72
{ 0, 1, xrgb_to_rgba }, /* 3 - IntRgbx */
73
{ 0, 1, bgr_to_rgba }, /* 4 - IntBgr */
74
{ 0, 1, xbgr_to_rgba }, /* 5 - IntBgrx */
75
76
// TODO: support 2-byte formats
77
// { GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV,
78
// 2, 0, 1, }, /* 7 - Ushort555Rgb */
79
// { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1,
80
// 2, 0, 1, }, /* 8 - Ushort555Rgbx*/
81
// { GL_LUMINANCE, GL_UNSIGNED_BYTE,
82
// 1, 0, 1, }, /* 9 - ByteGray */
83
// { GL_LUMINANCE, GL_UNSIGNED_SHORT,
84
// 2, 0, 1, }, /*10 - UshortGray */
85
// { GL_BGR, GL_UNSIGNED_BYTE,
86
// 1, 0, 1, }, /*11 - ThreeByteBgr */
87
};
88
89
extern void J2dTraceImpl(int level, jboolean cr, const char *string, ...);
90
91
void fillTxQuad(
92
struct TxtVertex * txQuadVerts,
93
jint sx1, jint sy1, jint sx2, jint sy2, jint sw, jint sh,
94
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2, jdouble dw, jdouble dh
95
) {
96
const float nsx1 = sx1/(float)sw;
97
const float nsy1 = sy1/(float)sh;
98
const float nsx2 = sx2/(float)sw;
99
const float nsy2 = sy2/(float)sh;
100
101
txQuadVerts[0].position[0] = dx1;
102
txQuadVerts[0].position[1] = dy1;
103
txQuadVerts[0].txtpos[0] = nsx1;
104
txQuadVerts[0].txtpos[1] = nsy1;
105
106
txQuadVerts[1].position[0] = dx2;
107
txQuadVerts[1].position[1] = dy1;
108
txQuadVerts[1].txtpos[0] = nsx2;
109
txQuadVerts[1].txtpos[1] = nsy1;
110
111
txQuadVerts[2].position[0] = dx2;
112
txQuadVerts[2].position[1] = dy2;
113
txQuadVerts[2].txtpos[0] = nsx2;
114
txQuadVerts[2].txtpos[1] = nsy2;
115
116
txQuadVerts[3].position[0] = dx2;
117
txQuadVerts[3].position[1] = dy2;
118
txQuadVerts[3].txtpos[0] = nsx2;
119
txQuadVerts[3].txtpos[1] = nsy2;
120
121
txQuadVerts[4].position[0] = dx1;
122
txQuadVerts[4].position[1] = dy2;
123
txQuadVerts[4].txtpos[0] = nsx1;
124
txQuadVerts[4].txtpos[1] = nsy2;
125
126
txQuadVerts[5].position[0] = dx1;
127
txQuadVerts[5].position[1] = dy1;
128
txQuadVerts[5].txtpos[0] = nsx1;
129
txQuadVerts[5].txtpos[1] = nsy1;
130
}
131
132
//#define TRACE_drawTex2Tex
133
134
void drawTex2Tex(MTLContext *mtlc,
135
id<MTLTexture> src, id<MTLTexture> dst,
136
jboolean isSrcOpaque, jboolean isDstOpaque, jint hint,
137
jint sx1, jint sy1, jint sx2, jint sy2,
138
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
139
{
140
#ifdef TRACE_drawTex2Tex
141
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "drawTex2Tex: src tex=%p, dst tex=%p", src, dst);
142
J2dRlsTraceLn4(J2D_TRACE_VERBOSE, " sw=%d sh=%d dw=%d dh=%d", src.width, src.height, dst.width, dst.height);
143
J2dRlsTraceLn4(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d", sx1, sy1, sx2, sy2);
144
J2dRlsTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f", dx1, dy1, dx2, dy2);
145
#endif //TRACE_drawTex2Tex
146
147
id<MTLRenderCommandEncoder> encoder = [mtlc.encoderManager getTextureEncoder:dst
148
isSrcOpaque:isSrcOpaque
149
isDstOpaque:isDstOpaque
150
interpolation:hint
151
];
152
153
struct TxtVertex quadTxVerticesBuffer[6];
154
fillTxQuad(quadTxVerticesBuffer, sx1, sy1, sx2, sy2, src.width, src.height, dx1, dy1, dx2, dy2, dst.width, dst.height);
155
156
[encoder setVertexBytes:quadTxVerticesBuffer length:sizeof(quadTxVerticesBuffer) atIndex:MeshVertexBuffer];
157
[encoder setFragmentTexture:src atIndex: 0];
158
[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];
159
}
160
161
static void fillSwizzleUniforms(struct SwizzleUniforms *uniforms, const MTLRasterFormatInfo *rfi) {
162
const size_t SWIZZLE_MAP_SIZE = 4;
163
memcpy(&uniforms->swizzle, rfi->swizzleMap, SWIZZLE_MAP_SIZE);
164
uniforms->hasAlpha = rfi->hasAlpha;
165
}
166
167
static void
168
replaceTextureRegion(MTLContext *mtlc, id<MTLTexture> dest, const SurfaceDataRasInfo *srcInfo,
169
const MTLRasterFormatInfo *rfi,
170
int dx1, int dy1, int dx2, int dy2) {
171
const int sw = MIN(srcInfo->bounds.x2 - srcInfo->bounds.x1, MTL_GPU_FAMILY_MAC_TXT_SIZE);
172
const int sh = MIN(srcInfo->bounds.y2 - srcInfo->bounds.y1, MTL_GPU_FAMILY_MAC_TXT_SIZE);
173
const int dw = MIN(dx2 - dx1, MTL_GPU_FAMILY_MAC_TXT_SIZE);
174
const int dh = MIN(dy2 - dy1, MTL_GPU_FAMILY_MAC_TXT_SIZE);
175
176
if (dw < sw || dh < sh) {
177
J2dTraceLn4(J2D_TRACE_ERROR, "replaceTextureRegion: dest size: (%d, %d) less than source size: (%d, %d)", dw, dh, sw, sh);
178
return;
179
}
180
181
const void *raster = srcInfo->rasBase;
182
raster += (NSUInteger)srcInfo->bounds.y1 * (NSUInteger)srcInfo->scanStride + (NSUInteger)srcInfo->bounds.x1 * (NSUInteger)srcInfo->pixelStride;
183
184
@autoreleasepool {
185
J2dTraceLn4(J2D_TRACE_VERBOSE, "replaceTextureRegion src (dw, dh) : [%d, %d] dest (dx1, dy1) =[%d, %d]",
186
dw, dh, dx1, dy1);
187
id<MTLBuffer> buff = [[mtlc.device newBufferWithLength:(sw * sh * srcInfo->pixelStride) options:MTLResourceStorageModeManaged] autorelease];
188
189
// copy src pixels inside src bounds to buff
190
for (int row = 0; row < sh; row++) {
191
memcpy(buff.contents + (row * sw * srcInfo->pixelStride), raster, sw * srcInfo->pixelStride);
192
raster += (NSUInteger)srcInfo->scanStride;
193
}
194
[buff didModifyRange:NSMakeRange(0, buff.length)];
195
196
if (rfi->swizzleMap != nil) {
197
id <MTLBuffer> swizzled = [[mtlc.device newBufferWithLength:(sw * sh * srcInfo->pixelStride) options:MTLResourceStorageModeManaged] autorelease];
198
199
// this should be cheap, since data is already on GPU
200
id<MTLCommandBuffer> cb = [mtlc createCommandBuffer];
201
id<MTLComputeCommandEncoder> computeEncoder = [cb computeCommandEncoder];
202
id<MTLComputePipelineState> computePipelineState = [mtlc.pipelineStateStorage
203
getComputePipelineState:@"swizzle_to_rgba"];
204
[computeEncoder setComputePipelineState:computePipelineState];
205
206
[computeEncoder setBuffer:buff offset:0 atIndex:0];
207
[computeEncoder setBuffer:swizzled offset:0 atIndex:1];
208
209
struct SwizzleUniforms uniforms;
210
fillSwizzleUniforms(&uniforms, rfi);
211
[computeEncoder setBytes:&uniforms length:sizeof(struct SwizzleUniforms) atIndex:2];
212
213
NSUInteger pixelCount = buff.length / srcInfo->pixelStride;
214
[computeEncoder setBytes:&pixelCount length:sizeof(NSUInteger) atIndex:3];
215
216
NSUInteger threadGroupSize = computePipelineState.maxTotalThreadsPerThreadgroup;
217
if (threadGroupSize == 0) {
218
threadGroupSize = 1;
219
}
220
MTLSize threadsPerGroup = MTLSizeMake(threadGroupSize, 1, 1);
221
MTLSize threadGroups = MTLSizeMake((pixelCount + threadGroupSize - 1) / threadGroupSize,
222
1, 1);
223
[computeEncoder dispatchThreadgroups:threadGroups
224
threadsPerThreadgroup:threadsPerGroup];
225
[computeEncoder endEncoding];
226
[cb commit];
227
228
buff = swizzled;
229
}
230
231
id<MTLBlitCommandEncoder> blitEncoder = [mtlc.encoderManager createBlitEncoder];
232
[blitEncoder copyFromBuffer:buff
233
sourceOffset:0 sourceBytesPerRow:(sw * srcInfo->pixelStride)
234
sourceBytesPerImage:(sw * sh * srcInfo->pixelStride) sourceSize:MTLSizeMake(sw, sh, 1)
235
toTexture:dest
236
destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(dx1, dy1, 0)];
237
[blitEncoder endEncoding];
238
[mtlc.encoderManager endEncoder];
239
240
MTLCommandBufferWrapper * cbwrapper = [mtlc pullCommandBufferWrapper];
241
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
242
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
243
[cbwrapper release];
244
}];
245
[commandbuf commit];
246
}
247
}
248
249
/**
250
* Inner loop used for copying a source system memory ("Sw") surface to a
251
* destination MTL "Surface". This method is invoked from
252
* MTLBlitLoops_Blit().
253
*/
254
255
static void
256
MTLBlitSwToTextureViaPooledTexture(
257
MTLContext *mtlc, SurfaceDataRasInfo *srcInfo, BMTLSDOps * bmtlsdOps,
258
MTLRasterFormatInfo *rfi, jint hint,
259
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
260
{
261
int sw = srcInfo->bounds.x2 - srcInfo->bounds.x1;
262
int sh = srcInfo->bounds.y2 - srcInfo->bounds.y1;
263
264
sw = MIN(sw, MTL_GPU_FAMILY_MAC_TXT_SIZE);
265
sh = MIN(sh, MTL_GPU_FAMILY_MAC_TXT_SIZE);
266
267
id<MTLTexture> dest = bmtlsdOps->pTexture;
268
269
MTLPooledTextureHandle * texHandle = [mtlc.texturePool getTexture:sw height:sh format:MTLPixelFormatBGRA8Unorm];
270
if (texHandle == nil) {
271
J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitSwToTextureViaPooledTexture: can't obtain temporary texture object from pool");
272
return;
273
}
274
[[mtlc getCommandBufferWrapper] registerPooledTexture:texHandle];
275
276
id<MTLTexture> texBuff = texHandle.texture;
277
replaceTextureRegion(mtlc, texBuff, srcInfo, rfi, 0, 0, sw, sh);
278
279
drawTex2Tex(mtlc, texBuff, dest, !rfi->hasAlpha, bmtlsdOps->isOpaque, hint,
280
0, 0, sw, sh, dx1, dy1, dx2, dy2);
281
}
282
283
static
284
jboolean isIntegerAndUnscaled(
285
jint sx1, jint sy1, jint sx2, jint sy2,
286
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2
287
) {
288
const jdouble epsilon = 0.0001f;
289
290
// check that dx1,dy1 is integer
291
if (fabs(dx1 - (int)dx1) > epsilon || fabs(dy1 - (int)dy1) > epsilon) {
292
return JNI_FALSE;
293
}
294
// check that destSize equals srcSize
295
if (fabs(dx2 - dx1 - sx2 + sx1) > epsilon || fabs(dy2 - dy1 - sy2 + sy1) > epsilon) {
296
return JNI_FALSE;
297
}
298
return JNI_TRUE;
299
}
300
301
static
302
jboolean clipDestCoords(
303
jdouble *dx1, jdouble *dy1, jdouble *dx2, jdouble *dy2,
304
jint *sx1, jint *sy1, jint *sx2, jint *sy2,
305
jint destW, jint destH, const MTLScissorRect * clipRect
306
) {
307
// Trim destination rect by clip-rect (or dest.bounds)
308
const jint sw = *sx2 - *sx1;
309
const jint sh = *sy2 - *sy1;
310
const jdouble dw = *dx2 - *dx1;
311
const jdouble dh = *dy2 - *dy1;
312
313
jdouble dcx1 = 0;
314
jdouble dcx2 = destW;
315
jdouble dcy1 = 0;
316
jdouble dcy2 = destH;
317
if (clipRect != NULL) {
318
if (clipRect->x > dcx1)
319
dcx1 = clipRect->x;
320
const int maxX = clipRect->x + clipRect->width;
321
if (dcx2 > maxX)
322
dcx2 = maxX;
323
if (clipRect->y > dcy1)
324
dcy1 = clipRect->y;
325
const int maxY = clipRect->y + clipRect->height;
326
if (dcy2 > maxY)
327
dcy2 = maxY;
328
329
if (dcx1 >= dcx2) {
330
J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcx1=%1.2f, dcx2=%1.2f", dcx1, dcx2);
331
dcx1 = dcx2;
332
}
333
if (dcy1 >= dcy2) {
334
J2dTraceLn2(J2D_TRACE_ERROR, "\tclipDestCoords: dcy1=%1.2f, dcy2=%1.2f", dcy1, dcy2);
335
dcy1 = dcy2;
336
}
337
}
338
if (*dx2 <= dcx1 || *dx1 >= dcx2 || *dy2 <= dcy1 || *dy1 >= dcy2) {
339
J2dTraceLn(J2D_TRACE_INFO, "\tclipDestCoords: dest rect doesn't intersect clip area");
340
J2dTraceLn4(J2D_TRACE_INFO, "\tdx2=%1.4f <= dcx1=%1.4f || *dx1=%1.4f >= dcx2=%1.4f", *dx2, dcx1, *dx1, dcx2);
341
J2dTraceLn4(J2D_TRACE_INFO, "\t*dy2=%1.4f <= dcy1=%1.4f || *dy1=%1.4f >= dcy2=%1.4f", *dy2, dcy1, *dy1, dcy2);
342
return JNI_FALSE;
343
}
344
if (*dx1 < dcx1) {
345
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdx1=%1.2f, will be clipped to %1.2f | sx1+=%d", *dx1, dcx1, (jint)((dcx1 - *dx1) * (sw/dw)));
346
*sx1 += (jint)((dcx1 - *dx1) * (sw/dw));
347
*dx1 = dcx1;
348
}
349
if (*dx2 > dcx2) {
350
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdx2=%1.2f, will be clipped to %1.2f | sx2-=%d", *dx2, dcx2, (jint)((*dx2 - dcx2) * (sw/dw)));
351
*sx2 -= (jint)((*dx2 - dcx2) * (sw/dw));
352
*dx2 = dcx2;
353
}
354
if (*dy1 < dcy1) {
355
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdy1=%1.2f, will be clipped to %1.2f | sy1+=%d", *dy1, dcy1, (jint)((dcy1 - *dy1) * (sh/dh)));
356
*sy1 += (jint)((dcy1 - *dy1) * (sh/dh));
357
*dy1 = dcy1;
358
}
359
if (*dy2 > dcy2) {
360
J2dTraceLn3(J2D_TRACE_VERBOSE, "\t\tdy2=%1.2f, will be clipped to %1.2f | sy2-=%d", *dy2, dcy2, (jint)((*dy2 - dcy2) * (sh/dh)));
361
*sy2 -= (jint)((*dy2 - dcy2) * (sh/dh));
362
*dy2 = dcy2;
363
}
364
return JNI_TRUE;
365
}
366
367
/**
368
* General blit method for copying a native MTL surface to another MTL "Surface".
369
* Parameter texture == true forces to use 'texture' codepath (dest coordinates will always be integers).
370
* Parameter xform == true only when AffineTransform is used (invoked only from TransformBlit, dest coordinates will always be integers).
371
*/
372
void
373
MTLBlitLoops_IsoBlit(JNIEnv *env,
374
MTLContext *mtlc, jlong pSrcOps, jlong pDstOps,
375
jboolean xform, jint hint, jboolean texture,
376
jint sx1, jint sy1, jint sx2, jint sy2,
377
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
378
{
379
BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps);
380
BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps);
381
382
RETURN_IF_NULL(mtlc);
383
RETURN_IF_NULL(srcOps);
384
RETURN_IF_NULL(dstOps);
385
// Verify if we use a valid MTLContext
386
MTLSDOps *dstMTLOps = (MTLSDOps *)dstOps->privOps;
387
RETURN_IF_TRUE(dstMTLOps->configInfo != NULL && mtlc != dstMTLOps->configInfo->context);
388
389
MTLSDOps *srcMTLOps = (MTLSDOps *)srcOps->privOps;
390
RETURN_IF_TRUE(srcMTLOps->configInfo != NULL && mtlc != srcMTLOps->configInfo->context);
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
if (!xform) {
418
clipDestCoords(
419
&dx1, &dy1, &dx2, &dy2,
420
&sx1, &sy1, &sx2, &sy2,
421
dstTex.width, dstTex.height, texture ? NULL : [mtlc.clip getRect]
422
);
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
&& 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,
491
srcOps->isOpaque, dstOps->isOpaque,
492
hint, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
493
}
494
495
/**
496
* General blit method for copying a system memory ("Sw") surface to a native MTL surface.
497
* Parameter texture == true only in SwToTextureBlit (straight copy from sw to texture), dest coordinates will always be integers.
498
* Parameter xform == true only when AffineTransform is used (invoked only from TransformBlit, dest coordinates will always be integers).
499
*/
500
void
501
MTLBlitLoops_Blit(JNIEnv *env,
502
MTLContext *mtlc, jlong pSrcOps, jlong pDstOps,
503
jboolean xform, jint hint,
504
jint srctype, jboolean texture,
505
jint sx1, jint sy1, jint sx2, jint sy2,
506
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
507
{
508
SurfaceDataOps *srcOps = (SurfaceDataOps *)jlong_to_ptr(pSrcOps);
509
BMTLSDOps *dstOps = (BMTLSDOps *)jlong_to_ptr(pDstOps);
510
511
RETURN_IF_NULL(mtlc);
512
RETURN_IF_NULL(srcOps);
513
RETURN_IF_NULL(dstOps);
514
// Verify if we use a valid MTLContext
515
MTLSDOps *dstMTLOps = (MTLSDOps *)dstOps->privOps;
516
RETURN_IF_TRUE(dstMTLOps->configInfo != NULL && mtlc != dstMTLOps->configInfo->context);
517
518
id<MTLTexture> dest = dstOps->pTexture;
519
if (dest == NULL) {
520
J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: dest is null");
521
return;
522
}
523
if (srctype < 0 || srctype >= sizeof(RasterFormatInfos)/ sizeof(MTLRasterFormatInfo)) {
524
J2dTraceLn1(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: source pixel format %d isn't supported", srctype);
525
return;
526
}
527
const jint sw = sx2 - sx1;
528
const jint sh = sy2 - sy1;
529
const jdouble dw = dx2 - dx1;
530
const jdouble dh = dy2 - dy1;
531
532
if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) {
533
J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_Blit: invalid dimensions");
534
return;
535
}
536
537
#ifdef DEBUG_BLIT
538
if (
539
(xform == JNI_TRUE) != (mtlc.useTransform == JNI_TRUE)
540
|| (xform && texture)
541
) {
542
J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE,
543
"MTLBlitLoops_Blit state error: xform=%d, mtlc.useTransform=%d, texture=%d",
544
xform, mtlc.useTransform, texture);
545
}
546
if (texture) {
547
if (!isIntegerAndUnscaled(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)) {
548
J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE,
549
"MTLBlitLoops_Blit state error: texture=true, but src and dst dimensions aren't equal or dest coords aren't integers");
550
}
551
if (!dstOps->isOpaque && !RasterFormatInfos[srctype].hasAlpha) {
552
J2dTraceImpl(J2D_TRACE_ERROR, JNI_TRUE,
553
"MTLBlitLoops_Blit state error: texture=true, but dest has alpha and source hasn't alpha, can't use texture-codepath");
554
}
555
}
556
#endif // DEBUG_BLIT
557
if (!xform) {
558
clipDestCoords(
559
&dx1, &dy1, &dx2, &dy2,
560
&sx1, &sy1, &sx2, &sy2,
561
dest.width, dest.height, texture ? NULL : [mtlc.clip getRect]
562
);
563
}
564
565
SurfaceDataRasInfo srcInfo;
566
srcInfo.bounds.x1 = sx1;
567
srcInfo.bounds.y1 = sy1;
568
srcInfo.bounds.x2 = sx2;
569
srcInfo.bounds.y2 = sy2;
570
571
// NOTE: This function will modify the contents of the bounds field to represent the maximum available raster data.
572
if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) != SD_SUCCESS) {
573
J2dTraceLn(J2D_TRACE_WARNING, "MTLBlitLoops_Blit: could not acquire lock");
574
return;
575
}
576
577
if (srcInfo.bounds.x2 > srcInfo.bounds.x1 && srcInfo.bounds.y2 > srcInfo.bounds.y1) {
578
srcOps->GetRasInfo(env, srcOps, &srcInfo);
579
if (srcInfo.rasBase) {
580
if (srcInfo.bounds.x1 != sx1) {
581
const int dx = srcInfo.bounds.x1 - sx1;
582
dx1 += dx * (dw / sw);
583
}
584
if (srcInfo.bounds.y1 != sy1) {
585
const int dy = srcInfo.bounds.y1 - sy1;
586
dy1 += dy * (dh / sh);
587
}
588
if (srcInfo.bounds.x2 != sx2) {
589
const int dx = srcInfo.bounds.x2 - sx2;
590
dx2 += dx * (dw / sw);
591
}
592
if (srcInfo.bounds.y2 != sy2) {
593
const int dy = srcInfo.bounds.y2 - sy2;
594
dy2 += dy * (dh / sh);
595
}
596
597
#ifdef TRACE_BLIT
598
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_FALSE,
599
"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)",
600
texture, xform, [mtlc getCompositeDescription].cString,
601
getSurfaceDescription(dstOps).cString, srcOps,
602
sx2 - sx1, sy2 - sy1,
603
RasterFormatInfos[srctype].hasAlpha ? 0 : 1, RasterFormatInfos[srctype].isPremult ? 1 : 0,
604
sx1, sy1, sx2, sy2,
605
dx1, dy1, dx2, dy2);
606
#endif //TRACE_BLIT
607
608
MTLRasterFormatInfo rfi = RasterFormatInfos[srctype];
609
610
if (texture) {
611
replaceTextureRegion(mtlc, dest, &srcInfo, &rfi, (int) dx1, (int) dy1, (int) dx2, (int) dy2);
612
} else {
613
MTLBlitSwToTextureViaPooledTexture(mtlc, &srcInfo, dstOps, &rfi, hint, dx1, dy1, dx2, dy2);
614
}
615
}
616
SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
617
}
618
SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
619
}
620
621
void copyFromMTLBuffer(void *pDst, id<MTLBuffer> srcBuf, NSUInteger offset, NSUInteger len, BOOL convertFromArgbPre) {
622
char *pSrc = (char*)srcBuf.contents + offset;
623
if (convertFromArgbPre) {
624
NSUInteger pixelLen = len >> 2;
625
for (NSUInteger i = 0; i < pixelLen; i++) {
626
LoadIntArgbPreTo1IntArgb((jint*)pSrc, 0, i, ((jint*)pDst)[i]);
627
}
628
} else {
629
memcpy(pDst, pSrc, len);
630
}
631
}
632
633
/**
634
* Specialized blit method for copying a native MTL "Surface" (pbuffer,
635
* window, etc.) to a system memory ("Sw") surface.
636
*/
637
void
638
MTLBlitLoops_SurfaceToSwBlit(JNIEnv *env, MTLContext *mtlc,
639
jlong pSrcOps, jlong pDstOps, jint dsttype,
640
jint srcx, jint srcy, jint dstx, jint dsty,
641
jint width, jint height)
642
{
643
J2dTraceLn6(J2D_TRACE_VERBOSE, "MTLBlitLoops_SurfaceToSwBlit: sx=%d sy=%d w=%d h=%d dx=%d dy=%d", srcx, srcy, width, height, dstx, dsty);
644
645
BMTLSDOps *srcOps = (BMTLSDOps *)jlong_to_ptr(pSrcOps);
646
SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps);
647
SurfaceDataRasInfo srcInfo, dstInfo;
648
649
if (dsttype < 0 || dsttype >= sizeof(RasterFormatInfos)/ sizeof(MTLRasterFormatInfo)) {
650
J2dTraceLn1(J2D_TRACE_ERROR, "MTLBlitLoops_SurfaceToSwBlit: destination pixel format %d isn't supported", dsttype);
651
return;
652
}
653
654
if (width <= 0 || height <= 0) {
655
J2dTraceLn(J2D_TRACE_ERROR, "MTLBlitLoops_SurfaceToSwBlit: dimensions are non-positive");
656
return;
657
}
658
659
RETURN_IF_NULL(srcOps);
660
RETURN_IF_NULL(dstOps);
661
RETURN_IF_NULL(mtlc);
662
RETURN_IF_TRUE(width < 0);
663
RETURN_IF_TRUE(height < 0);
664
NSUInteger w = (NSUInteger)width;
665
NSUInteger h = (NSUInteger)height;
666
667
srcInfo.bounds.x1 = srcx;
668
srcInfo.bounds.y1 = srcy;
669
srcInfo.bounds.x2 = srcx + width;
670
srcInfo.bounds.y2 = srcy + height;
671
dstInfo.bounds.x1 = dstx;
672
dstInfo.bounds.y1 = dsty;
673
dstInfo.bounds.x2 = dstx + width;
674
dstInfo.bounds.y2 = dsty + height;
675
676
if (dstOps->Lock(env, dstOps, &dstInfo, SD_LOCK_WRITE) != SD_SUCCESS) {
677
J2dTraceLn(J2D_TRACE_WARNING,"MTLBlitLoops_SurfaceToSwBlit: could not acquire dst lock");
678
return;
679
}
680
681
SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds,
682
0, 0, srcOps->width, srcOps->height);
683
684
SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds,
685
srcx - dstx, srcy - dsty);
686
687
if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&
688
srcInfo.bounds.y2 > srcInfo.bounds.y1)
689
{
690
dstOps->GetRasInfo(env, dstOps, &dstInfo);
691
if (dstInfo.rasBase) {
692
void *pDst = dstInfo.rasBase;
693
694
srcx = srcInfo.bounds.x1;
695
srcy = srcInfo.bounds.y1;
696
dstx = dstInfo.bounds.x1;
697
dsty = dstInfo.bounds.y1;
698
width = srcInfo.bounds.x2 - srcInfo.bounds.x1;
699
height = srcInfo.bounds.y2 - srcInfo.bounds.y1;
700
701
pDst = PtrPixelsRow(pDst, dstx, dstInfo.pixelStride);
702
pDst = PtrPixelsRow(pDst, dsty, dstInfo.scanStride);
703
704
NSUInteger byteLength = w * h * 4; // NOTE: assume that src format is MTLPixelFormatBGRA8Unorm
705
706
// Create MTLBuffer (or use static)
707
id<MTLBuffer> mtlbuf;
708
#ifdef USE_STATIC_BUFFER
709
// NOTE: theoretically we can use newBufferWithBytesNoCopy, but pDst must be allocated with special API
710
// mtlbuf = [mtlc.device
711
// newBufferWithBytesNoCopy:pDst
712
// length:(NSUInteger) srcLength
713
// options:MTLResourceCPUCacheModeDefaultCache
714
// deallocator:nil];
715
//
716
// see https://developer.apple.com/documentation/metal/mtldevice/1433382-newbufferwithbytesnocopy?language=objc
717
//
718
// The storage allocation of the returned new MTLBuffer object is the same as the pointer input value.
719
// The existing memory allocation must be covered by a single VM region, typically allocated with vm_allocate or mmap.
720
// Memory allocated by malloc is specifically disallowed.
721
722
static id<MTLBuffer> mtlIntermediateBuffer = nil; // need to reimplement with MTLBufferManager
723
if (mtlIntermediateBuffer == nil || mtlIntermediateBuffer.length < srcLength) {
724
if (mtlIntermediateBuffer != nil) {
725
[mtlIntermediateBuffer release];
726
}
727
mtlIntermediateBuffer = [mtlc.device newBufferWithLength:srcLength options:MTLResourceCPUCacheModeDefaultCache];
728
}
729
mtlbuf = mtlIntermediateBuffer;
730
#else // USE_STATIC_BUFFER
731
mtlbuf = [mtlc.device newBufferWithLength:byteLength options:MTLResourceStorageModeShared];
732
#endif // USE_STATIC_BUFFER
733
734
// Read from surface into MTLBuffer
735
// NOTE: using of separate blitCommandBuffer can produce errors (draw into surface (with general cmd-buf)
736
// can be unfinished when reading raster from blit cmd-buf).
737
// Consider to use [mtlc.encoderManager createBlitEncoder] and [mtlc commitCommandBuffer:JNI_TRUE];
738
J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLBlitLoops_SurfaceToSwBlit: source texture %p", srcOps->pTexture);
739
740
id<MTLCommandBuffer> cb = [mtlc createCommandBuffer];
741
id<MTLBlitCommandEncoder> blitEncoder = [cb blitCommandEncoder];
742
[blitEncoder copyFromTexture:srcOps->pTexture
743
sourceSlice:0
744
sourceLevel:0
745
sourceOrigin:MTLOriginMake(srcx, srcy, 0)
746
sourceSize:MTLSizeMake(w, h, 1)
747
toBuffer:mtlbuf
748
destinationOffset:0 /*offset already taken in: pDst = PtrPixelsRow(pDst, dstx, dstInfo.pixelStride)*/
749
destinationBytesPerRow:w*4
750
destinationBytesPerImage:byteLength];
751
[blitEncoder endEncoding];
752
753
// Commit and wait for reading complete
754
[cb commit];
755
[cb waitUntilCompleted];
756
757
// Perform conversion if necessary
758
BOOL convertFromPre = !RasterFormatInfos[dsttype].isPremult && !srcOps->isOpaque;
759
760
if ((dstInfo.scanStride == w * dstInfo.pixelStride) &&
761
(height == (dstInfo.bounds.y2 - dstInfo.bounds.y1))) {
762
// mtlbuf.contents have same dimensions as of pDst
763
copyFromMTLBuffer(pDst, mtlbuf, 0, byteLength, convertFromPre);
764
} else {
765
// mtlbuf.contents have smaller dimensions than pDst
766
// copy each row from mtlbuf.contents at appropriate position in pDst
767
// Note : pDst is already addjusted for offsets using PtrAddBytes above
768
769
NSUInteger rowSize = w * dstInfo.pixelStride;
770
for (int y = 0; y < height; y++) {
771
copyFromMTLBuffer(pDst, mtlbuf, y * rowSize, rowSize, convertFromPre);
772
pDst = PtrAddBytes(pDst, dstInfo.scanStride);
773
}
774
}
775
776
#ifndef USE_STATIC_BUFFER
777
[mtlbuf release];
778
#endif // USE_STATIC_BUFFER
779
}
780
SurfaceData_InvokeRelease(env, dstOps, &dstInfo);
781
}
782
SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);
783
}
784
785
void
786
MTLBlitLoops_CopyArea(JNIEnv *env,
787
MTLContext *mtlc, BMTLSDOps *dstOps,
788
jint x, jint y, jint width, jint height,
789
jint dx, jint dy)
790
{
791
#ifdef DEBUG
792
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_CopyArea: bdst=%p [tex=%p] %dx%d | src (%d, %d), %dx%d -> dst (%d, %d)",
793
dstOps, dstOps->pTexture, ((id<MTLTexture>)dstOps->pTexture).width, ((id<MTLTexture>)dstOps->pTexture).height, x, y, width, height, dx, dy);
794
#endif //DEBUG
795
jint texWidth = ((id<MTLTexture>)dstOps->pTexture).width;
796
jint texHeight = ((id<MTLTexture>)dstOps->pTexture).height;
797
798
SurfaceDataBounds srcBounds, dstBounds;
799
srcBounds.x1 = x;
800
srcBounds.y1 = y;
801
srcBounds.x2 = srcBounds.x1 + width;
802
srcBounds.y2 = srcBounds.y1 + height;
803
dstBounds.x1 = x + dx;
804
dstBounds.y1 = y + dy;
805
dstBounds.x2 = dstBounds.x1 + width;
806
dstBounds.y2 = dstBounds.y1 + height;
807
808
SurfaceData_IntersectBoundsXYXY(&srcBounds, 0, 0, texWidth, texHeight);
809
SurfaceData_IntersectBoundsXYXY(&dstBounds, 0, 0, texWidth, texHeight);
810
SurfaceData_IntersectBlitBounds(&dstBounds, &srcBounds, -dx, -dy);
811
812
int srcWidth = (srcBounds.x2 - srcBounds.x1);
813
int srcHeight = (srcBounds.y2 - srcBounds.y1);
814
815
if ((srcBounds.x1 < srcBounds.x2 && srcBounds.y1 < srcBounds.y2) &&
816
(dstBounds.x1 < dstBounds.x2 && dstBounds.y1 < dstBounds.y2))
817
{
818
@autoreleasepool {
819
struct TxtVertex quadTxVerticesBuffer[6];
820
MTLPooledTextureHandle * interHandle =
821
[mtlc.texturePool getTexture:texWidth
822
height:texHeight
823
format:MTLPixelFormatBGRA8Unorm];
824
if (interHandle == nil) {
825
J2dTraceLn(J2D_TRACE_ERROR,
826
"MTLBlitLoops_CopyArea: texture handle is null");
827
return;
828
}
829
[[mtlc getCommandBufferWrapper] registerPooledTexture:interHandle];
830
831
id<MTLTexture> interTexture = interHandle.texture;
832
833
/*
834
* We need to consider common states like clipping while
835
* performing copyArea, thats why we use drawTex2Tex and
836
* get encoder with appropriate state from EncoderManager
837
* and not directly use MTLBlitCommandEncoder for texture copy.
838
*/
839
840
// copy content to intermediate texture
841
drawTex2Tex(mtlc, dstOps->pTexture, interTexture, dstOps->isOpaque,
842
JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR,
843
0, 0, texWidth, texHeight, 0, 0, texWidth, texHeight);
844
845
// copy content with appropriate bounds to destination texture
846
drawTex2Tex(mtlc, interTexture, dstOps->pTexture, JNI_FALSE,
847
dstOps->isOpaque, INTERPOLATION_NEAREST_NEIGHBOR,
848
srcBounds.x1, srcBounds.y1, srcBounds.x2, srcBounds.y2,
849
dstBounds.x1, dstBounds.y1, dstBounds.x2, dstBounds.y2);
850
[mtlc.encoderManager endEncoder];
851
MTLCommandBufferWrapper * cbwrapper =
852
[mtlc pullCommandBufferWrapper];
853
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
854
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
855
[cbwrapper release];
856
}];
857
[commandbuf commit];
858
}
859
}
860
}
861
862