Path: blob/master/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLTexurePool.m
41159 views
/*1* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#import "MTLTexturePool.h"26#import "Trace.h"2728#define SCREEN_MEMORY_SIZE_4K (4096*2160*4) //~33,7 mb29#define MAX_POOL_MEMORY SCREEN_MEMORY_SIZE_4K/230#define MAX_POOL_ITEM_LIFETIME_SEC 303132#define CELL_WIDTH_BITS 5 // ~ 32 pixel33#define CELL_HEIGHT_BITS 5 // ~ 32 pixel3435@implementation MTLTexturePoolItem3637@synthesize texture, isBusy, lastUsed, isMultiSample, next, cell;3839- (id) initWithTexture:(id<MTLTexture>)tex cell:(MTLPoolCell*)c{40self = [super init];41if (self == nil) return self;42self.texture = tex;43isBusy = NO;44self.next = nil;45self.prev = nil;46self.cell = c;47return self;48}4950- (void) dealloc {51[texture release];52[super dealloc];53}5455@end5657@implementation MTLPooledTextureHandle58{59MTLRegion _rect;60id<MTLTexture> _texture;61MTLTexturePoolItem * _poolItem;62}63@synthesize texture = _texture, rect = _rect;6465- (id) initWithPoolItem:(id<MTLTexture>)texture rect:(MTLRegion)rectangle poolItem:(MTLTexturePoolItem *)poolItem {66self = [super init];67if (self == nil) return self;6869_rect = rectangle;70_texture = texture;71_poolItem = poolItem;72return self;73}7475- (void) releaseTexture {76[_poolItem.cell releaseItem:_poolItem];77}7879@end8081@implementation MTLPoolCell {82NSLock* _lock;83}84@synthesize available, availableTail, occupied;8586- (instancetype)init {87self = [super init];88if (self) {89self.available = nil;90self.availableTail = nil;91self.occupied = nil;92_lock = [[NSLock alloc] init];93}94return self;95}9697- (void)occupyItem:(MTLTexturePoolItem *)item {98if (item.isBusy) return;99[item retain];100if (item.prev == nil) {101self.available = item.next;102if (item.next) {103item.next.prev = nil;104} else {105self.availableTail = item.prev;106}107} else {108item.prev.next = item.next;109if (item.next) {110item.next.prev = item.prev;111} else {112self.availableTail = item.prev;113}114item.prev = nil;115}116if (occupied) occupied.prev = item;117item.next = occupied;118self.occupied = item;119[item release];120item.isBusy = YES;121}122123- (void)releaseItem:(MTLTexturePoolItem *)item {124[_lock lock];125@try {126if (!item.isBusy) return;127[item retain];128if (item.prev == nil) {129self.occupied = item.next;130if (item.next) item.next.prev = nil;131} else {132item.prev.next = item.next;133if (item.next) item.next.prev = item.prev;134item.prev = nil;135}136if (self.available) {137self.available.prev = item;138} else {139self.availableTail = item;140}141item.next = self.available;142self.available = item;143item.isBusy = NO;144[item release];145} @finally {146[_lock unlock];147}148}149150- (void)addOccupiedItem:(MTLTexturePoolItem *)item {151if (self.occupied) self.occupied.prev = item;152item.next = self.occupied;153item.isBusy = YES;154self.occupied = item;155}156157- (void)removeAvailableItem:(MTLTexturePoolItem*)item {158[item retain];159if (item.prev == nil) {160self.available = item.next;161if (item.next) {162item.next.prev = nil;163item.next = nil;164} else {165self.availableTail = item.prev;166}167} else {168item.prev.next = item.next;169if (item.next) {170item.next.prev = item.prev;171item.next = nil;172} else {173self.availableTail = item.prev;174}175}176[item release];177}178179- (void)removeAllItems {180MTLTexturePoolItem *cur = self.available;181while (cur != nil) {182cur = cur.next;183self.available = cur;184}185cur = self.occupied;186while (cur != nil) {187cur = cur.next;188self.occupied = cur;189}190self.availableTail = nil;191}192193- (MTLTexturePoolItem *)createItem:(id<MTLDevice>)dev194width:(int)width195height:(int)height196format:(MTLPixelFormat)format197isMultiSample:(bool)isMultiSample198{199MTLTextureDescriptor *textureDescriptor =200[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format201width:(NSUInteger) width202height:(NSUInteger) height203mipmapped:NO];204textureDescriptor.usage = MTLTextureUsageRenderTarget |205MTLTextureUsageShaderRead;206if (isMultiSample) {207textureDescriptor.textureType = MTLTextureType2DMultisample;208textureDescriptor.sampleCount = MTLAASampleCount;209textureDescriptor.storageMode = MTLStorageModePrivate;210}211212id <MTLTexture> tex = (id <MTLTexture>) [[dev newTextureWithDescriptor:textureDescriptor] autorelease];213MTLTexturePoolItem* item = [[[MTLTexturePoolItem alloc] initWithTexture:tex cell:self] autorelease];214item.isMultiSample = isMultiSample;215[_lock lock];216@try {217[self addOccupiedItem:item];218} @finally {219[_lock unlock];220}221return item;222}223224225- (NSUInteger)cleanIfBefore:(time_t)lastUsedTimeToRemove {226NSUInteger deallocMem = 0;227[_lock lock];228MTLTexturePoolItem *cur = availableTail;229@try {230while (cur != nil) {231MTLTexturePoolItem *prev = cur.prev;232if (lastUsedTimeToRemove <= 0 ||233cur.lastUsed < lastUsedTimeToRemove) {234#ifdef DEBUG235J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE,236"MTLTexturePool: remove pool item: tex=%p, w=%d h=%d, elapsed=%d",237cur.texture, cur.texture.width, cur.texture.height,238time(NULL) - cur.lastUsed);239#endif //DEBUG240deallocMem += cur.texture.width * cur.texture.height * 4;241[self removeAvailableItem:cur];242} else {243if (lastUsedTimeToRemove > 0) break;244}245cur = prev;246}247} @finally {248[_lock unlock];249}250return deallocMem;251}252253- (MTLTexturePoolItem *)occupyItem:(int)width height:(int)height format:(MTLPixelFormat)format254isMultiSample:(bool)isMultiSample {255int minDeltaArea = -1;256const int requestedPixels = width*height;257MTLTexturePoolItem *minDeltaTpi = nil;258[_lock lock];259@try {260for (MTLTexturePoolItem *cur = available; cur != nil; cur = cur.next) {261if (cur.texture.pixelFormat != format262|| cur.isMultiSample != isMultiSample) { // TODO: use swizzle when formats are not equal263continue;264}265if (cur.texture.width < width || cur.texture.height < height) {266continue;267}268const int deltaArea = (const int) (cur.texture.width * cur.texture.height - requestedPixels);269if (minDeltaArea < 0 || deltaArea < minDeltaArea) {270minDeltaArea = deltaArea;271minDeltaTpi = cur;272if (deltaArea == 0) {273// found exact match in current cell274break;275}276}277}278279if (minDeltaTpi) {280[self occupyItem:minDeltaTpi];281}282} @finally {283[_lock unlock];284}285return minDeltaTpi;286}287288- (void) dealloc {289[_lock lock];290@try {291[self removeAllItems];292} @finally {293[_lock unlock];294}295[_lock release];296[super dealloc];297}298299@end300301@implementation MTLTexturePool {302int _memoryTotalAllocated;303304void ** _cells;305int _poolCellWidth;306int _poolCellHeight;307}308309@synthesize device;310311- (id) initWithDevice:(id<MTLDevice>)dev {312self = [super init];313if (self == nil) return self;314315_memoryTotalAllocated = 0;316_poolCellWidth = 10;317_poolCellHeight = 10;318const int cellsCount = _poolCellWidth * _poolCellHeight;319_cells = (void **)malloc(cellsCount * sizeof(void*));320memset(_cells, 0, cellsCount * sizeof(void*));321self.device = dev;322return self;323}324325- (void) dealloc {326for (int c = 0; c < _poolCellWidth * _poolCellHeight; ++c) {327MTLPoolCell * cell = _cells[c];328if (cell != NULL) {329[cell release];330}331}332free(_cells);333[super dealloc];334}335336// NOTE: called from RQ-thread (on blit operations)337- (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format {338return [self getTexture:width height:height format:format isMultiSample:NO];339}340341// NOTE: called from RQ-thread (on blit operations)342- (MTLPooledTextureHandle *) getTexture:(int)width height:(int)height format:(MTLPixelFormat)format343isMultiSample:(bool)isMultiSample {344// 1. clean pool if necessary345const int requestedPixels = width*height;346const int requestedBytes = requestedPixels*4;347if (_memoryTotalAllocated + requestedBytes > MAX_POOL_MEMORY) {348[self cleanIfNecessary:0]; // release all free textures349} else if (_memoryTotalAllocated + requestedBytes > MAX_POOL_MEMORY/2) {350[self cleanIfNecessary:MAX_POOL_ITEM_LIFETIME_SEC]; // release only old free textures351}352353// 2. find free item354const int cellX0 = width >> CELL_WIDTH_BITS;355const int cellY0 = height >> CELL_HEIGHT_BITS;356const int cellX1 = cellX0 + 1;357const int cellY1 = cellY0 + 1;358if (cellX1 > _poolCellWidth || cellY1 > _poolCellHeight) {359const int newCellWidth = cellX1 <= _poolCellWidth ? _poolCellWidth : cellX1;360const int newCellHeight = cellY1 <= _poolCellHeight ? _poolCellHeight : cellY1;361const int newCellsCount = newCellWidth*newCellHeight;362#ifdef DEBUG363J2dTraceLn2(J2D_TRACE_VERBOSE, "MTLTexturePool: resize: %d -> %d", _poolCellWidth * _poolCellHeight, newCellsCount);364#endif365void ** newcells = malloc(newCellsCount*sizeof(void*));366const int strideBytes = _poolCellWidth * sizeof(void*);367for (int cy = 0; cy < _poolCellHeight; ++cy) {368void ** dst = newcells + cy*newCellWidth;369void ** src = _cells + cy * _poolCellWidth;370memcpy(dst, src, strideBytes);371if (newCellWidth > _poolCellWidth)372memset(dst + _poolCellWidth, 0, (newCellWidth - _poolCellWidth) * sizeof(void*));373}374if (newCellHeight > _poolCellHeight) {375void ** dst = newcells + _poolCellHeight * newCellWidth;376memset(dst, 0, (newCellHeight - _poolCellHeight) * newCellWidth * sizeof(void*));377}378free(_cells);379_cells = newcells;380_poolCellWidth = newCellWidth;381_poolCellHeight = newCellHeight;382}383384MTLTexturePoolItem * minDeltaTpi = nil;385int minDeltaArea = -1;386for (int cy = cellY0; cy < cellY1; ++cy) {387for (int cx = cellX0; cx < cellX1; ++cx) {388MTLPoolCell * cell = _cells[cy * _poolCellWidth + cx];389if (cell != NULL) {390MTLTexturePoolItem* tpi = [cell occupyItem:width height:height391format:format isMultiSample:isMultiSample];392if (!tpi) continue;393const int deltaArea = (const int) (tpi.texture.width * tpi.texture.height - requestedPixels);394if (minDeltaArea < 0 || deltaArea < minDeltaArea) {395minDeltaArea = deltaArea;396minDeltaTpi = tpi;397if (deltaArea == 0) {398// found exact match in current cell399break;400}401}402}403}404if (minDeltaTpi != nil) {405break;406}407}408409if (minDeltaTpi == NULL) {410MTLPoolCell* cell = _cells[cellY0 * _poolCellWidth + cellX0];411if (cell == NULL) {412cell = [[MTLPoolCell alloc] init];413_cells[cellY0 * _poolCellWidth + cellX0] = cell;414}415minDeltaTpi = [cell createItem:device width:width height:height format:format isMultiSample:isMultiSample];416_memoryTotalAllocated += requestedBytes;417J2dTraceLn5(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);418}419420minDeltaTpi.isBusy = YES;421minDeltaTpi.lastUsed = time(NULL);422return [[[MTLPooledTextureHandle alloc] initWithPoolItem:minDeltaTpi.texture423rect:MTLRegionMake2D(0, 0,424minDeltaTpi.texture.width,425minDeltaTpi.texture.height)426poolItem:minDeltaTpi] autorelease];427}428429- (void) cleanIfNecessary:(int)lastUsedTimeThreshold {430time_t lastUsedTimeToRemove =431lastUsedTimeThreshold > 0 ?432time(NULL) - lastUsedTimeThreshold :433lastUsedTimeThreshold;434for (int cy = 0; cy < _poolCellHeight; ++cy) {435for (int cx = 0; cx < _poolCellWidth; ++cx) {436MTLPoolCell * cell = _cells[cy * _poolCellWidth + cx];437if (cell != NULL) {438_memoryTotalAllocated -= [cell cleanIfBefore:lastUsedTimeToRemove];439}440}441}442}443444@end445446447