forked from androidx/androidx
-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Hide Metal drawables from Kotlin runtime (#1390)
Explicitly manage lifetime of metal drawables by hiding them from Kotlin runtime. Fixes a memory spike when quickly resizing metal layer as in a scenario reported in [the issue](JetBrains/compose-multiplatform#4850) Otherwise associated drawable pools are retained until next GC, which can happen after inadequate amount of drawables with new sizes are allocated ### Before: <img width="414" alt="Screenshot 2024-06-06 at 13 33 46" src="https://github.com/JetBrains/compose-multiplatform-core/assets/4167681/81d74dfe-91c0-4362-ad71-93416dbe172f"> ### After: <img width="574" alt="Screenshot 2024-06-07 at 10 04 18" src="https://github.com/JetBrains/compose-multiplatform-core/assets/4167681/6574b297-1e81-48db-9aee-d31c2425267c"> ## Testing Resize the popup with `ComposeUIViewController` to different detents inside Demo `IosBugs/PopupStretching` Requires either patch: ``` Index: compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.uikit.kt IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.uikit.kt b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.uikit.kt --- a/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.uikit.kt (revision 39d436a) +++ b/compose/ui/ui/src/uikitMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.uikit.kt (date 1717747754590) @@ -558,6 +558,7 @@ asDpRect().toRect() } } + onComposeSceneInvalidate() scene.size = IntSize( width = boundsInPx.width.roundToInt(), height = boundsInPx.height.roundToInt() ``` Or #1387 ## Release Notes ### iOS - Fixes - Fixed a memory spike when continuously resizing the `ComposeUIViewController` (such as when used in modal sheet presentation context with different detents)
- Loading branch information
1 parent
2d2b9e1
commit 0d4294c
Showing
8 changed files
with
269 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
96 changes: 96 additions & 0 deletions
96
compose/mpp/demo/src/uikitMain/kotlin/androidx/compose/mpp/demo/bugs/PopupStretching.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/* | ||
* Copyright 2024 The Android Open Source Project | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package androidx.compose.mpp.demo.bugs | ||
|
||
import androidx.compose.foundation.background | ||
import androidx.compose.foundation.horizontalScroll | ||
import androidx.compose.foundation.layout.Box | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.Row | ||
import androidx.compose.foundation.layout.Spacer | ||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.foundation.layout.height | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.layout.size | ||
import androidx.compose.foundation.rememberScrollState | ||
import androidx.compose.foundation.verticalScroll | ||
import androidx.compose.material.Text | ||
import androidx.compose.material3.Button | ||
import androidx.compose.mpp.demo.Screen | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.graphics.Color | ||
import androidx.compose.ui.interop.LocalUIViewController | ||
import androidx.compose.ui.unit.dp | ||
import androidx.compose.ui.window.ComposeUIViewController | ||
import platform.UIKit.UIModalPresentationFormSheet | ||
import platform.UIKit.UISheetPresentationControllerDetent | ||
import platform.UIKit.sheetPresentationController | ||
|
||
val PopupStretching = Screen.Example("Popup stretching") { | ||
val viewController = LocalUIViewController.current | ||
|
||
Button(onClick = { | ||
val bottomSheetController = ComposeUIViewController { | ||
VerticalScrollWithIndependentHorizontalRows() | ||
} | ||
|
||
bottomSheetController.modalPresentationStyle = UIModalPresentationFormSheet | ||
bottomSheetController.sheetPresentationController?.setDetents( | ||
listOf( | ||
UISheetPresentationControllerDetent.mediumDetent(), | ||
UISheetPresentationControllerDetent.largeDetent(), | ||
) | ||
) | ||
|
||
viewController.presentViewController(bottomSheetController, animated = true, completion = {}) | ||
}) { | ||
Text("Show popup") | ||
} | ||
} | ||
|
||
|
||
@Composable | ||
fun VerticalScrollWithIndependentHorizontalRows() { | ||
Column( | ||
modifier = | ||
Modifier.fillMaxSize().verticalScroll(rememberScrollState(), enabled = true), | ||
) { | ||
repeat(10) { rowIndex -> | ||
val horizontalScrollState = rememberScrollState() | ||
|
||
Spacer(Modifier.height(30.dp).background(Color.DarkGray)) | ||
Row( | ||
modifier = | ||
Modifier | ||
.padding(start = 16.dp, end = 16.dp) | ||
.horizontalScroll(horizontalScrollState), | ||
) { | ||
repeat(5) { | ||
Box( | ||
modifier = | ||
Modifier | ||
.size(100.dp) | ||
.background(Color.Gray), | ||
) { | ||
Text("Item $it in row $rowIndex") | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
...ose/ui/ui-uikit/src/uikitMain/objc/CMPUIKitUtils/CMPUIKitUtils/CMPMetalDrawablesHandler.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* | ||
* Copyright 2023 The Android Open Source Project | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#import <Foundation/Foundation.h> | ||
#import <QuartzCore/QuartzCore.h> | ||
#import <Metal/Metal.h> | ||
|
||
#import "CMPMacros.h" | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
/** | ||
A handler class for managing Metal drawables explicitly using raw pointers. | ||
This class encapsulates the lifecycle management of drawable objects, | ||
facilitating the use in environments where automatic reference counting (ARC) | ||
mixed with Kotlin/Native memory model that leads to violation of practices enstated by Apple (namely, not releasing drawables as soon as possible), which lead to inadequate memory spikes during drawable size updates across consequent frames. | ||
@see https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/Drawables.html | ||
The class methods handle the acquisition, release, and presentation of | ||
drawable objects associated with a given CAMetalLayer. Usage of raw pointers | ||
helps in explicitly controlling the drawable lifecycle, preventing application from keeping drawables and their pools alive longer, than needed, due to awaiting deallocation by GC. | ||
*/ | ||
@interface CMPMetalDrawablesHandler : NSObject | ||
|
||
/// Initializes the handler with a given Metal layer. | ||
/// @param metalLayer The CAMetalLayer instance to be associated with this handler. | ||
- (instancetype)initWithMetalLayer:(CAMetalLayer *)metalLayer; | ||
|
||
/// Retrieves the next drawable object from the associated Metal layer. | ||
/// @return A raw pointer to the next drawable object, ownership is transferred to the caller. | ||
- (void * CMP_OWNED)nextDrawable; | ||
|
||
/// Releases a drawable object, indicating that it is no longer in use by the caller. | ||
/// @param drawablePtr A raw pointer to the drawable to be released, indicating transfer of ownership back to the handler. | ||
- (void)releaseDrawable:(void * CMP_CONSUMED)drawablePtr; | ||
|
||
/// Retrieves the texture of a drawable without transferring ownership. | ||
/// @param drawablePtr A raw pointer to the drawable from which to get the texture. | ||
/// @return A raw pointer to the texture of the drawable, ownership is not transferred. | ||
- (void * CMP_BORROWED)drawableTexture:(void * CMP_BORROWED)drawablePtr; | ||
|
||
/// Presents a drawable to the screen immediately. | ||
/// @param drawablePtr A raw pointer to the drawable to be presented, indicating transfer of ownership. | ||
- (void)presentDrawable:(void * CMP_CONSUMED)drawablePtr; | ||
|
||
/// Schedules the presentation of a drawable on a specific command buffer. | ||
/// @param drawablePtr A raw pointer to the drawable to be presented, indicating transfer of ownership. | ||
/// @param commandBuffer The command buffer on which the drawable will be scheduled for presentation. | ||
- (void)scheduleDrawablePresentation:(void * CMP_CONSUMED)drawablePtr onCommandBuffer:(id <MTLCommandBuffer>)commandBuffer; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
72 changes: 72 additions & 0 deletions
72
...ose/ui/ui-uikit/src/uikitMain/objc/CMPUIKitUtils/CMPUIKitUtils/CMPMetalDrawablesHandler.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* | ||
* Copyright 2023 The Android Open Source Project | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#import "CMPMetalDrawablesHandler.h" | ||
|
||
@implementation CMPMetalDrawablesHandler { | ||
CAMetalLayer *_metalLayer; | ||
} | ||
|
||
- (instancetype)initWithMetalLayer:(CAMetalLayer *)metalLayer { | ||
self = [super init]; | ||
if (self) { | ||
_metalLayer = metalLayer; | ||
} | ||
return self; | ||
} | ||
|
||
- (void * CMP_OWNED)nextDrawable { | ||
id <CAMetalDrawable> drawable = [_metalLayer nextDrawable]; | ||
|
||
if (drawable) { | ||
void *ptr = (__bridge_retained void *)drawable; | ||
return ptr; | ||
} else { | ||
return NULL; | ||
} | ||
} | ||
|
||
- (void)releaseDrawable:(void * CMP_CONSUMED)drawablePtr { | ||
assert(drawablePtr != NULL); | ||
|
||
id <CAMetalDrawable> drawable __unused = (__bridge_transfer id <CAMetalDrawable>)drawablePtr; | ||
// drawable will be released by ARC | ||
} | ||
|
||
- (void * CMP_BORROWED)drawableTexture:(void * CMP_BORROWED)drawablePtr { | ||
assert(drawablePtr != NULL); | ||
|
||
id <CAMetalDrawable> drawable = (__bridge id <CAMetalDrawable>)drawablePtr; | ||
id <MTLTexture> texture = drawable.texture; | ||
void *texturePtr = (__bridge void *)texture; | ||
return texturePtr; | ||
} | ||
|
||
- (void)presentDrawable:(void * CMP_CONSUMED)drawablePtr { | ||
assert(drawablePtr != NULL); | ||
|
||
id <CAMetalDrawable> drawable = (__bridge_transfer id <CAMetalDrawable>)drawablePtr; | ||
[drawable present]; | ||
} | ||
|
||
- (void)scheduleDrawablePresentation:(void * CMP_CONSUMED)drawablePtr onCommandBuffer:(id <MTLCommandBuffer>)commandBuffer { | ||
assert(drawablePtr != NULL); | ||
|
||
id <CAMetalDrawable> drawable = (__bridge_transfer id <CAMetalDrawable>)drawablePtr; | ||
[commandBuffer presentDrawable:drawable]; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters