-
Notifications
You must be signed in to change notification settings - Fork 197
HARP-12700: Disposal of POI resources by reference counting. #1946
Conversation
atomicsulfate
commented
Nov 3, 2020
•
edited
Loading
edited
- Share material and texture across pois with same image even if they have different render order.
- Do reference counting on resources to dispose of them when they're not used any longer.
bf51876
to
73c71e3
Compare
- Share material and texture across pois with same image even if they have different render order. - Do reference counting on resources to dispose of them when they're not used any longer.
73c71e3
to
591b8df
Compare
this.textElementGroups.forEach((element: TextElement) => { | ||
element.dispose(); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call to TextElement.dispose() on Tile disposal to ensure that it releases it's poi buffer (if any).
dispose() { | ||
const poiBuffer = this.poiInfo?.buffer; | ||
if (poiBuffer) { | ||
poiBuffer.decreaseRefCount(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Decrease ref counting on TextElement disposal to ensure the poi buffer and associated resources are released when ref count reaches 0.
const imageWidth = imageItem.imageData.width; | ||
const imageHeight = imageItem.imageData.height; | ||
const paddedSize = MipMapGenerator.getPaddedSize(imageWidth, imageHeight); | ||
const trilinearFiltering = PoiRenderBufferBatch.trilinear && imageItem.mipMaps; | ||
const trilinearFiltering = PoiBatch.trilinear && imageItem.mipMaps; | ||
const paddedImageWidth = trilinearFiltering ? paddedSize.width : imageWidth; | ||
const paddedImageHeight = trilinearFiltering ? paddedSize.height : imageHeight; | ||
|
||
const iconWidth = imageTexture.width !== undefined ? imageTexture.width : imageWidth; | ||
const iconHeight = imageTexture.height !== undefined ? imageTexture.height : imageHeight; | ||
const iconWidth = imageTexture?.width !== undefined ? imageTexture.width : imageWidth; | ||
const iconHeight = imageTexture?.height !== undefined ? imageTexture.height : imageHeight; | ||
|
||
let minS = 0; | ||
let maxS = 1; | ||
let minT = 0; | ||
let maxT = 1; | ||
const width = imageTexture?.width !== undefined ? imageTexture.width : imageWidth; | ||
const height = imageTexture?.height !== undefined ? imageTexture.height : imageHeight; | ||
const xOffset = imageTexture?.xOffset !== undefined ? imageTexture.xOffset : 0; | ||
const yOffset = imageTexture?.yOffset !== undefined ? imageTexture.yOffset : 0; | ||
|
||
const minS = xOffset / paddedImageWidth; | ||
const maxS = (xOffset + width) / paddedImageWidth; | ||
const minT = yOffset / paddedImageHeight; | ||
const maxT = (yOffset + height) / paddedImageHeight; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no actual change, just removing unused code and simplifying.
if (imageItem.loadingPromise) { | ||
// already being loaded, will be rendered once available | ||
return; | ||
} | ||
|
||
const result = imageCache.loadImage(imageItem); | ||
assert(result instanceof Promise); | ||
const loadPromise = result as Promise<ImageItem | undefined>; | ||
loadPromise | ||
.then(loadedImageItem => { | ||
if (loadedImageItem === undefined) { | ||
logger.error(`preparePoi: Failed to load imageItem: '${imageItem.url}`); | ||
return; | ||
} | ||
this.setupPoiInfo(poiInfo, loadedImageItem, env, imageTexture); | ||
}) | ||
.catch(error => { | ||
logger.error(`preparePoi: Failed to load imageItem: '${imageItem.url}`, error); | ||
poiInfo.isValid = false; | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just rearranging the code to reduce nesting level.
} else { | ||
// No image texture found. Either this is a user image or it's missing. | ||
const image = this.mapView.userImageCache.findImageByName(imageTextureName); | ||
|
||
if (!image) { | ||
// Warn about a missing texture, but only once. | ||
if (PoiRenderer.m_missingTextureName.get(imageTextureName) === undefined) { | ||
PoiRenderer.m_missingTextureName.set(imageTextureName, true); | ||
logger.error( | ||
`preparePoi: No imageTexture with name '${imageTextureName}' found` | ||
); | ||
} | ||
poiInfo.isValid = false; | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now the user images don't need to be registered both in MapView's userImageCache and PoiManager. This only makes sense internally for theme atlases.
boxBuffer: BoxBuffer | undefined; | ||
|
||
private m_material?: THREE.Material | THREE.Material[]; | ||
export class PoiBuffer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the class where we do reference counting and propagate disposal of any resources when ref count reaches 0.
* There is a `PoiBatch` for every icon in a texture atlas, since the size of the icon in the atlas | ||
* as well as the texture coordinates are specified in the `PoiBatch`. | ||
*/ | ||
class PoiBatch { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the old PoiRenderBufferBatch, but I've moved in all PoiBuffers with same image but different render order, which now share same material and texture (before we didn't share them for different render orders of the same image).
pickBoxes( | ||
screenPosition: THREE.Vector2, | ||
pickCallback: (pickData: any | undefined) => void, | ||
imageData?: ImageBitmap | ImageData | ||
) { | ||
for (const poiBuffer of this.m_poiBuffers.values()) { | ||
poiBuffer.buffer.pickBoxes(screenPosition, pickCallback, imageData); | ||
} | ||
} | ||
|
||
/** | ||
* Update the info with the memory footprint caused by objects owned by the `PoiBatch`. | ||
* | ||
* @param info - The info object to increment with the values from this `PoiBatch`. | ||
*/ | ||
updateMemoryUsage(info: MemoryUsage) { | ||
if (this.imageItem.imageData !== undefined) { | ||
const imageBytes = this.imageItem.imageData.width * this.imageItem.imageData.height * 4; | ||
info.heapSize += imageBytes; | ||
info.gpuSize += imageBytes; | ||
} | ||
for (const poiBuffer of this.m_poiBuffers.values()) { | ||
poiBuffer.buffer.updateMemoryUsage(info); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing new here, just moved.
this.m_poiBuffers.clear(); | ||
this.m_material.map.dispose(); | ||
this.m_material.dispose(); | ||
this.m_onDispose(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we dispose of material and texture, which we never diposed of before.
|
||
if (batch === undefined) { | ||
batch = new PoiBatch(this.mapView, this.textCanvas, imageItem, () => { | ||
this.deleteBatch(batchKey); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This callback deletes a batch when it's empty.
@@ -707,11 +707,13 @@ export interface MarkerTechniqueParams extends BaseTechniqueParams { | |||
*/ | |||
poiNameField?: string; | |||
/** | |||
* Name of [[ImageTexture]] definition to use. | |||
* Name of [[ImageTexture]] definition or image in {@link @here/harp-mapview#userImageCache} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the imageTextures from the theme go to some internal cache in MapViewThemeManager, not to MapView.userImageCache
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant, it's the name of either a theme's image or a user image in userImageCache. I'll rewrite the documentation to make it clearer.
Codecov Report
@@ Coverage Diff @@
## master #1946 +/- ##
==========================================
+ Coverage 65.25% 65.78% +0.52%
==========================================
Files 293 293
Lines 26329 26327 -2
Branches 5951 5936 -15
==========================================
+ Hits 17181 17319 +138
+ Misses 9148 9008 -140
Continue to review full report at Codecov.
|