1use super::{CacheDomain, CacheScope, ImageCachePolicy, MemoryAction, MemoryEvent};
2
3#[cfg_attr(feature = "diagnostics-serde", derive(serde::Serialize))]
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub struct MemoryBudget {
6 pub cache_soft_bytes: usize,
7 pub cache_hard_bytes: usize,
8 pub transient_hard_bytes: usize,
9 pub persistent_bytes: u64,
10 pub max_encoded_resource_bytes: usize,
11 pub max_decoded_resource_bytes: usize,
12 pub max_parallel_large_tasks: usize,
13}
14
15impl MemoryBudget {
16 pub const fn new(
17 cache_soft_bytes: usize,
18 cache_hard_bytes: usize,
19 transient_hard_bytes: usize,
20 persistent_bytes: u64,
21 max_encoded_resource_bytes: usize,
22 max_decoded_resource_bytes: usize,
23 max_parallel_large_tasks: usize,
24 ) -> Self {
25 Self {
26 cache_soft_bytes,
27 cache_hard_bytes,
28 transient_hard_bytes,
29 persistent_bytes,
30 max_encoded_resource_bytes,
31 max_decoded_resource_bytes,
32 max_parallel_large_tasks,
33 }
34 }
35}
36
37#[cfg_attr(feature = "diagnostics-serde", derive(serde::Serialize))]
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39pub struct MemoryDomainBudgets {
40 pub encoded_image_bytes: usize,
41 pub decoded_image_bytes: usize,
42 pub svg_bytes: usize,
43 pub blur_bytes: usize,
44 pub text_bytes: usize,
45 pub static_layer_bytes: usize,
46 pub scroll_raster_bytes: usize,
47 pub gdi_bytes: usize,
48 pub d2d_bytes: usize,
49 pub skia_bytes: usize,
50 pub component_output_bytes: usize,
51 pub host_scene_bytes: usize,
52 pub diagnostics_bytes: usize,
53}
54
55impl MemoryDomainBudgets {
56 pub const fn budget_for(self, domain: CacheDomain) -> usize {
57 match domain {
58 CacheDomain::EncodedImage => self.encoded_image_bytes,
59 CacheDomain::DecodedImage => self.decoded_image_bytes,
60 CacheDomain::Svg => self.svg_bytes,
61 CacheDomain::Blur => self.blur_bytes,
62 CacheDomain::Text => self.text_bytes,
63 CacheDomain::StaticLayer => self.static_layer_bytes,
64 CacheDomain::ScrollRaster => self.scroll_raster_bytes,
65 CacheDomain::Gdi => self.gdi_bytes,
66 CacheDomain::D2d => self.d2d_bytes,
67 CacheDomain::Skia => self.skia_bytes,
68 CacheDomain::ComponentOutput => self.component_output_bytes,
69 CacheDomain::HostScene => self.host_scene_bytes,
70 CacheDomain::Diagnostics => self.diagnostics_bytes,
71 CacheDomain::Persistent => 0,
72 }
73 }
74
75 pub const fn unbounded() -> Self {
76 Self {
77 encoded_image_bytes: usize::MAX,
78 decoded_image_bytes: usize::MAX,
79 svg_bytes: usize::MAX,
80 blur_bytes: usize::MAX,
81 text_bytes: usize::MAX,
82 static_layer_bytes: usize::MAX,
83 scroll_raster_bytes: usize::MAX,
84 gdi_bytes: usize::MAX,
85 d2d_bytes: usize::MAX,
86 skia_bytes: usize::MAX,
87 component_output_bytes: usize::MAX,
88 host_scene_bytes: usize::MAX,
89 diagnostics_bytes: usize::MAX,
90 }
91 }
92}
93
94#[cfg_attr(feature = "diagnostics-serde", derive(serde::Serialize))]
95#[derive(Clone, Copy, Debug, PartialEq, Eq)]
96pub struct MemoryEventPolicy {
97 pub frame_committed: MemoryAction,
98 pub window_hidden: MemoryAction,
99 pub window_shown: MemoryAction,
100 pub all_windows_hidden: MemoryAction,
101 pub session_unmounted: MemoryAction,
102 pub renderer_device_lost: MemoryAction,
103 pub theme_or_scale_changed: MemoryAction,
104 pub moderate_pressure: MemoryAction,
105 pub critical_pressure: MemoryAction,
106 pub explicit_trim: MemoryAction,
107 pub application_shutdown: MemoryAction,
108}
109
110impl MemoryEventPolicy {
111 pub const fn action_for(self, event: MemoryEvent) -> MemoryAction {
112 match event {
113 MemoryEvent::FrameCommitted => self.frame_committed,
114 MemoryEvent::WindowHidden => self.window_hidden,
115 MemoryEvent::WindowShown => self.window_shown,
116 MemoryEvent::AllWindowsHidden => self.all_windows_hidden,
117 MemoryEvent::SessionUnmounted => self.session_unmounted,
118 MemoryEvent::RendererDeviceLost => self.renderer_device_lost,
119 MemoryEvent::ThemeOrScaleChanged => self.theme_or_scale_changed,
120 MemoryEvent::ModeratePressure => self.moderate_pressure,
121 MemoryEvent::CriticalPressure => self.critical_pressure,
122 MemoryEvent::ExplicitTrim => self.explicit_trim,
123 MemoryEvent::ApplicationShutdown => self.application_shutdown,
124 }
125 }
126
127 pub const fn ignore_all() -> Self {
128 Self {
129 frame_committed: MemoryAction::None,
130 window_hidden: MemoryAction::None,
131 window_shown: MemoryAction::None,
132 all_windows_hidden: MemoryAction::None,
133 session_unmounted: MemoryAction::None,
134 renderer_device_lost: MemoryAction::None,
135 theme_or_scale_changed: MemoryAction::None,
136 moderate_pressure: MemoryAction::None,
137 critical_pressure: MemoryAction::None,
138 explicit_trim: MemoryAction::None,
139 application_shutdown: MemoryAction::None,
140 }
141 }
142}
143
144#[cfg_attr(feature = "diagnostics-serde", derive(serde::Serialize))]
145#[derive(Clone, Copy, Debug, PartialEq, Eq)]
146pub struct MemoryOptions {
147 pub policy_name: &'static str,
148 pub budget: MemoryBudget,
149 pub domains: MemoryDomainBudgets,
150 pub events: MemoryEventPolicy,
151 pub default_image_cache_policy: ImageCachePolicy,
152 pub persistent_cache_enabled: bool,
153}
154
155impl MemoryOptions {
156 pub const fn new(
157 policy_name: &'static str,
158 budget: MemoryBudget,
159 domains: MemoryDomainBudgets,
160 events: MemoryEventPolicy,
161 default_image_cache_policy: ImageCachePolicy,
162 persistent_cache_enabled: bool,
163 ) -> Self {
164 Self {
165 policy_name,
166 budget,
167 domains,
168 events,
169 default_image_cache_policy,
170 persistent_cache_enabled,
171 }
172 }
173
174 pub const fn unbounded(
175 default_image_cache_policy: ImageCachePolicy,
176 persistent_cache_enabled: bool,
177 ) -> Self {
178 Self::new(
179 "unbounded",
180 MemoryBudget::new(
181 usize::MAX,
182 usize::MAX,
183 usize::MAX,
184 u64::MAX,
185 usize::MAX,
186 usize::MAX,
187 usize::MAX,
188 ),
189 MemoryDomainBudgets::unbounded(),
190 MemoryEventPolicy::ignore_all(),
191 default_image_cache_policy,
192 persistent_cache_enabled,
193 )
194 }
195
196 pub const fn persistent_cache(mut self, enabled: bool) -> Self {
197 self.persistent_cache_enabled = enabled;
198 self
199 }
200
201 pub const fn persistent_budget(mut self, bytes: u64) -> Self {
202 self.budget.persistent_bytes = bytes;
203 self
204 }
205
206 pub fn validate(self) -> Result<(), &'static str> {
207 if self.policy_name.is_empty() {
208 return Err("memory policy name must not be empty");
209 }
210 if self.budget.cache_soft_bytes > self.budget.cache_hard_bytes {
211 return Err("memory cache soft budget exceeds hard budget");
212 }
213 if self.budget.max_parallel_large_tasks == 0 {
214 return Err("memory policy must allow at least one large task");
215 }
216 if self.budget.max_encoded_resource_bytes > self.budget.transient_hard_bytes {
217 return Err("encoded resource limit exceeds transient hard budget");
218 }
219 if self.budget.max_decoded_resource_bytes > self.budget.transient_hard_bytes {
220 return Err("decoded resource limit exceeds transient hard budget");
221 }
222 if self.domains.encoded_image_bytes != 0
223 && self.domains.encoded_image_bytes < self.budget.max_encoded_resource_bytes
224 {
225 return Err("encoded image domain budget is below the resource limit");
226 }
227 if self.domains.decoded_image_bytes != 0
228 && self.domains.decoded_image_bytes < self.budget.max_decoded_resource_bytes
229 {
230 return Err("decoded image domain budget is below the resource limit");
231 }
232 if self.default_image_cache_policy == ImageCachePolicy::ApplicationDefault {
233 return Err("application default image policy must be concrete");
234 }
235 Ok(())
236 }
237
238 pub const fn event_action(self, event: MemoryEvent) -> MemoryAction {
239 self.events.action_for(event)
240 }
241
242 pub fn domain_budget(self, domain: CacheDomain) -> usize {
243 match domain {
244 CacheDomain::Persistent => self.budget.persistent_bytes.min(usize::MAX as u64) as usize,
245 domain => self.domains.budget_for(domain),
246 }
247 }
248}
249
250impl MemoryAction {
251 pub const fn trim(scope: CacheScope, target_bytes: usize) -> Self {
252 Self::Trim {
253 scope,
254 target_bytes,
255 }
256 }
257}