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