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/MTLTexurePool.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
#import "MTLTexturePool.h"
27
#import "Trace.h"
28
29
#define SCREEN_MEMORY_SIZE_4K (4096*2160*4) //~33,7 mb
30
#define MAX_POOL_MEMORY SCREEN_MEMORY_SIZE_4K/2
31
#define MAX_POOL_ITEM_LIFETIME_SEC 30
32
33
#define CELL_WIDTH_BITS 5 // ~ 32 pixel
34
#define CELL_HEIGHT_BITS 5 // ~ 32 pixel
35
36
@implementation MTLTexturePoolItem
37
38
@synthesize texture, isBusy, lastUsed, isMultiSample, next, cell;
39
40
- (id) initWithTexture:(id<MTLTexture>)tex cell:(MTLPoolCell*)c{
41
self = [super init];
42
if (self == nil) return self;
43
self.texture = tex;
44
isBusy = NO;
45
self.next = nil;
46
self.prev = nil;
47
self.cell = c;
48
return self;
49
}
50
51
- (void) dealloc {
52
[texture release];
53
[super dealloc];
54
}
55
56
@end
57
58
@implementation MTLPooledTextureHandle
59
{
60
MTLRegion _rect;
61
id<MTLTexture> _texture;
62
MTLTexturePoolItem * _poolItem;
63
}
64
@synthesize texture = _texture, rect = _rect;
65
66
- (id) initWithPoolItem:(id<MTLTexture>)texture rect:(MTLRegion)rectangle poolItem:(MTLTexturePoolItem *)poolItem {
67
self = [super init];
68
if (self == nil) return self;
69
70
_rect = rectangle;
71
_texture = texture;
72
_poolItem = poolItem;
73
return self;
74
}
75
76
- (void) releaseTexture {
77
[_poolItem.cell releaseItem:_poolItem];
78
}
79
80
@end
81
82
@implementation MTLPoolCell {
83
NSLock* _lock;
84
}
85
@synthesize available, availableTail, occupied;
86
87
- (instancetype)init {
88
self = [super init];
89
if (self) {
90
self.available = nil;
91
self.availableTail = nil;
92
self.occupied = nil;
93
_lock = [[NSLock alloc] init];
94
}
95
return self;
96
}
97
98
- (void)occupyItem:(MTLTexturePoolItem *)item {
99
if (item.isBusy) return;
100
[item retain];
101
if (item.prev == nil) {
102
self.available = item.next;
103
if (item.next) {
104
item.next.prev = nil;
105
} else {
106
self.availableTail = item.prev;
107
}
108
} else {
109
item.prev.next = item.next;
110
if (item.next) {
111
item.next.prev = item.prev;
112
} else {
113
self.availableTail = item.prev;
114
}
115
item.prev = nil;
116
}
117
if (occupied) occupied.prev = item;
118
item.next = occupied;
119
self.occupied = item;
120
[item release];
121
item.isBusy = YES;
122
}
123
124
- (void)releaseItem:(MTLTexturePoolItem *)item {
125
[_lock lock];
126
@try {
127
if (!item.isBusy) return;
128
[item retain];
129
if (item.prev == nil) {
130
self.occupied = item.next;
131
if (item.next) item.next.prev = nil;
132
} else {
133
item.prev.next = item.next;
134
if (item.next) item.next.prev = item.prev;
135
item.prev = nil;
136
}
137
if (self.available) {
138
self.available.prev = item;
139
} else {
140
self.availableTail = item;
141
}
142
item.next = self.available;
143
self.available = item;
144
item.isBusy = NO;
145
[item release];
146
} @finally {
147
[_lock unlock];
148
}
149
}
150
151
- (void)addOccupiedItem:(MTLTexturePoolItem *)item {
152
if (self.occupied) self.occupied.prev = item;
153
item.next = self.occupied;
154
item.isBusy = YES;
155
self.occupied = item;
156
}
157
158
- (void)removeAvailableItem:(MTLTexturePoolItem*)item {
159
[item retain];
160
if (item.prev == nil) {
161
self.available = item.next;
162
if (item.next) {
163
item.next.prev = nil;
164
item.next = nil;
165
} else {
166
self.availableTail = item.prev;
167
}
168
} else {
169
item.prev.next = item.next;
170
if (item.next) {
171
item.next.prev = item.prev;
172
item.next = nil;
173
} else {
174
self.availableTail = item.prev;
175
}
176
}
177
[item release];
178
}
179
180
- (void)removeAllItems {
181
MTLTexturePoolItem *cur = self.available;
182
while (cur != nil) {
183
cur = cur.next;
184
self.available = cur;
185
}
186
cur = self.occupied;
187
while (cur != nil) {
188
cur = cur.next;
189
self.occupied = cur;
190
}
191
self.availableTail = nil;
192
}
193
194
- (MTLTexturePoolItem *)createItem:(id<MTLDevice>)dev
195
width:(int)width
196
height:(int)height
197
format:(MTLPixelFormat)format
198
isMultiSample:(bool)isMultiSample
199
{
200
MTLTextureDescriptor *textureDescriptor =
201
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format
202
width:(NSUInteger) width
203
height:(NSUInteger) height
204
mipmapped:NO];
205
textureDescriptor.usage = MTLTextureUsageRenderTarget |
206
MTLTextureUsageShaderRead;
207
if (isMultiSample) {
208
textureDescriptor.textureType = MTLTextureType2DMultisample;
209
textureDescriptor.sampleCount = MTLAASampleCount;
210
textureDescriptor.storageMode = MTLStorageModePrivate;
211
}
212
213
id <MTLTexture> tex = (id <MTLTexture>) [[dev newTextureWithDescriptor:textureDescriptor] autorelease];
214
MTLTexturePoolItem* item = [[[MTLTexturePoolItem alloc] initWithTexture:tex cell:self] autorelease];
215
item.isMultiSample = isMultiSample;
216
[_lock lock];
217
@try {
218
[self addOccupiedItem:item];
219
} @finally {
220
[_lock unlock];
221
}
222
return item;
223
}
224
225
226
- (NSUInteger)cleanIfBefore:(time_t)lastUsedTimeToRemove {
227
NSUInteger deallocMem = 0;
228
[_lock lock];
229
MTLTexturePoolItem *cur = availableTail;
230
@try {
231
while (cur != nil) {
232
MTLTexturePoolItem *prev = cur.prev;
233
if (lastUsedTimeToRemove <= 0 ||
234
cur.lastUsed < lastUsedTimeToRemove) {
235
#ifdef DEBUG
236
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE,
237
"MTLTexturePool: remove pool item: tex=%p, w=%d h=%d, elapsed=%d",
238
cur.texture, cur.texture.width, cur.texture.height,
239
time(NULL) - cur.lastUsed);
240
#endif //DEBUG
241
deallocMem += cur.texture.width * cur.texture.height * 4;
242
[self removeAvailableItem:cur];
243
} else {
244
if (lastUsedTimeToRemove > 0) break;
245
}
246
cur = prev;
247
}
248
} @finally {
249
[_lock unlock];
250
}
251
return deallocMem;
252
}
253
254
- (MTLTexturePoolItem *)occupyItem:(int)width height:(int)height format:(MTLPixelFormat)format
255
isMultiSample:(bool)isMultiSample {
256
int minDeltaArea = -1;
257
const int requestedPixels = width*height;
258
MTLTexturePoolItem *minDeltaTpi = nil;
259
[_lock lock];
260
@try {
261
for (MTLTexturePoolItem *cur = available; cur != nil; cur = cur.next) {
262
if (cur.texture.pixelFormat != format
263
|| cur.isMultiSample != isMultiSample) { // TODO: use swizzle when formats are not equal
264
continue;
265
}
266
if (cur.texture.width < width || cur.texture.height < height) {
267
continue;
268
}
269
const int deltaArea = (const int) (cur.texture.width * cur.texture.height - requestedPixels);
270
if (minDeltaArea < 0 || deltaArea < minDeltaArea) {
271
minDeltaArea = deltaArea;
272
minDeltaTpi = cur;
273
if (deltaArea == 0) {
274
// found exact match in current cell
275
break;
276
}
277
}
278
}
279
280
if (minDeltaTpi) {
281
[self occupyItem:minDeltaTpi];
282
}
283
} @finally {
284
[_lock unlock];
285
}
286
return minDeltaTpi;
287
}
288
289
- (void) dealloc {
290
[_lock lock];
291
@try {
292
[self removeAllItems];
293
} @finally {
294
[_lock unlock];
295
}
296
[_lock release];
297
[super dealloc];
298
}
299
300
@end
301
302
@implementation MTLTexturePool {
303
int _memoryTotalAllocated;
304
305
void ** _cells;
306
int _poolCellWidth;
307
int _poolCellHeight;
308
}
309
310
@synthesize device;
311
312
- (id) initWithDevice:(id<MTLDevice>)dev {
313
self = [super init];
314
if (self == nil) return self;
315
316
_memoryTotalAllocated = 0;
317
_poolCellWidth = 10;
318
_poolCellHeight = 10;
319
const int cellsCount = _poolCellWidth * _poolCellHeight;
320
_cells = (void **)malloc(cellsCount * sizeof(void*));
321
memset(_cells, 0, cellsCount * sizeof(void*));
322
self.device = dev;
323
return self;
324
}
325
326
- (void) dealloc {
327
for (int c = 0; c < _poolCellWidth * _poolCellHeight; ++c) {
328
MTLPoolCell * cell = _cells[c];
329
if (cell != NULL) {
330
[cell release];
331
}
332
}
333
free(_cells);
334
[super dealloc];
335
}
336
337
// NOTE: called from RQ-thread (on blit operations)
338
- (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format {
339
return [self getTexture:width height:height format:format isMultiSample:NO];
340
}
341
342
// NOTE: called from RQ-thread (on blit operations)
343
- (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format
344
isMultiSample:(bool)isMultiSample {
345
// 1. clean pool if necessary
346
const int requestedPixels = width*height;
347
const int requestedBytes = requestedPixels*4;
348
if (_memoryTotalAllocated + requestedBytes > MAX_POOL_MEMORY) {
349
[self cleanIfNecessary:0]; // release all free textures
350
} else if (_memoryTotalAllocated + requestedBytes > MAX_POOL_MEMORY/2) {
351
[self cleanIfNecessary:MAX_POOL_ITEM_LIFETIME_SEC]; // release only old free textures
352
}
353
354
// 2. find free item
355
const int cellX0 = width >> CELL_WIDTH_BITS;
356
const int cellY0 = height >> CELL_HEIGHT_BITS;
357
const int cellX1 = cellX0 + 1;
358
const int cellY1 = cellY0 + 1;
359
if (cellX1 > _poolCellWidth || cellY1 > _poolCellHeight) {
360
const int newCellWidth = cellX1 <= _poolCellWidth ? _poolCellWidth : cellX1;
361
const int newCellHeight = cellY1 <= _poolCellHeight ? _poolCellHeight : cellY1;
362
const int newCellsCount = newCellWidth*newCellHeight;
363
#ifdef DEBUG
364
J2dTraceLn2(J2D_TRACE_VERBOSE, "MTLTexturePool: resize: %d -> %d", _poolCellWidth * _poolCellHeight, newCellsCount);
365
#endif
366
void ** newcells = malloc(newCellsCount*sizeof(void*));
367
const int strideBytes = _poolCellWidth * sizeof(void*);
368
for (int cy = 0; cy < _poolCellHeight; ++cy) {
369
void ** dst = newcells + cy*newCellWidth;
370
void ** src = _cells + cy * _poolCellWidth;
371
memcpy(dst, src, strideBytes);
372
if (newCellWidth > _poolCellWidth)
373
memset(dst + _poolCellWidth, 0, (newCellWidth - _poolCellWidth) * sizeof(void*));
374
}
375
if (newCellHeight > _poolCellHeight) {
376
void ** dst = newcells + _poolCellHeight * newCellWidth;
377
memset(dst, 0, (newCellHeight - _poolCellHeight) * newCellWidth * sizeof(void*));
378
}
379
free(_cells);
380
_cells = newcells;
381
_poolCellWidth = newCellWidth;
382
_poolCellHeight = newCellHeight;
383
}
384
385
MTLTexturePoolItem * minDeltaTpi = nil;
386
int minDeltaArea = -1;
387
for (int cy = cellY0; cy < cellY1; ++cy) {
388
for (int cx = cellX0; cx < cellX1; ++cx) {
389
MTLPoolCell * cell = _cells[cy * _poolCellWidth + cx];
390
if (cell != NULL) {
391
MTLTexturePoolItem* tpi = [cell occupyItem:width height:height
392
format:format isMultiSample:isMultiSample];
393
if (!tpi) continue;
394
const int deltaArea = (const int) (tpi.texture.width * tpi.texture.height - requestedPixels);
395
if (minDeltaArea < 0 || deltaArea < minDeltaArea) {
396
minDeltaArea = deltaArea;
397
minDeltaTpi = tpi;
398
if (deltaArea == 0) {
399
// found exact match in current cell
400
break;
401
}
402
}
403
}
404
}
405
if (minDeltaTpi != nil) {
406
break;
407
}
408
}
409
410
if (minDeltaTpi == NULL) {
411
MTLPoolCell* cell = _cells[cellY0 * _poolCellWidth + cellX0];
412
if (cell == NULL) {
413
cell = [[MTLPoolCell alloc] init];
414
_cells[cellY0 * _poolCellWidth + cellX0] = cell;
415
}
416
minDeltaTpi = [cell createItem:device width:width height:height format:format isMultiSample:isMultiSample];
417
_memoryTotalAllocated += requestedBytes;
418
J2dTraceLn5(J2D_TRACE_VERBOSE, "MTLTexturePool: created pool item: tex=%p, w=%d h=%d, pf=%d | total memory = %d Kb", minDeltaTpi.texture, width, height, format, _memoryTotalAllocated/1024);
419
}
420
421
minDeltaTpi.isBusy = YES;
422
minDeltaTpi.lastUsed = time(NULL);
423
return [[[MTLPooledTextureHandle alloc] initWithPoolItem:minDeltaTpi.texture
424
rect:MTLRegionMake2D(0, 0,
425
minDeltaTpi.texture.width,
426
minDeltaTpi.texture.height)
427
poolItem:minDeltaTpi] autorelease];
428
}
429
430
- (void) cleanIfNecessary:(int)lastUsedTimeThreshold {
431
time_t lastUsedTimeToRemove =
432
lastUsedTimeThreshold > 0 ?
433
time(NULL) - lastUsedTimeThreshold :
434
lastUsedTimeThreshold;
435
for (int cy = 0; cy < _poolCellHeight; ++cy) {
436
for (int cx = 0; cx < _poolCellWidth; ++cx) {
437
MTLPoolCell * cell = _cells[cy * _poolCellWidth + cx];
438
if (cell != NULL) {
439
_memoryTotalAllocated -= [cell cleanIfBefore:lastUsedTimeToRemove];
440
}
441
}
442
}
443
}
444
445
@end
446
447