Skip to main content

skia_safe/gpu/ganesh/
direct_context.rs

1use std::{
2    fmt,
3    ops::{Deref, DerefMut},
4    ptr,
5    time::Duration,
6};
7
8use crate::{
9    Data, Image, Surface, TextureCompressionType,
10    gpu::{
11        BackendFormat, BackendRenderTarget, BackendTexture, ContextOptions, FlushInfo,
12        GpuStatsFlags, MutableTextureState, PurgeResourceOptions, RecordingContext,
13        SemaphoresSubmitted, SubmitInfo, SyncCpu,
14    },
15    prelude::*,
16    surfaces,
17};
18use skia_bindings::{self as sb, GrDirectContext, GrDirectContext_DirectContextID, SkRefCntBase};
19
20#[repr(C)]
21#[derive(Copy, Clone, PartialEq, Eq, Debug)]
22pub struct DirectContextId {
23    id: u32,
24}
25
26native_transmutable!(GrDirectContext_DirectContextID, DirectContextId);
27
28pub type DirectContext = RCHandle<GrDirectContext>;
29
30impl NativeRefCountedBase for GrDirectContext {
31    type Base = SkRefCntBase;
32}
33
34impl Deref for DirectContext {
35    type Target = RecordingContext;
36
37    fn deref(&self) -> &Self::Target {
38        unsafe { transmute_ref(self) }
39    }
40}
41
42impl DerefMut for DirectContext {
43    fn deref_mut(&mut self) -> &mut Self::Target {
44        unsafe { transmute_ref_mut(self) }
45    }
46}
47
48#[derive(Copy, Clone, PartialEq, Eq, Debug)]
49pub struct ResourceCacheLimits {
50    pub max_resources: usize,
51    pub max_resource_bytes: usize,
52}
53
54#[derive(Copy, Clone, PartialEq, Eq, Debug)]
55pub struct ResourceCacheUsage {
56    pub resource_count: usize,
57    pub resource_bytes: usize,
58}
59
60impl fmt::Debug for DirectContext {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        f.debug_struct("DirectContext")
63            .field("base", self as &RecordingContext)
64            .field("resource_cache_limit", &self.resource_cache_limit())
65            .field("resource_cache_usage", &self.resource_cache_usage())
66            .field(
67                "resource_cache_purgeable_bytes",
68                &self.resource_cache_purgeable_bytes(),
69            )
70            .field(
71                "supports_distance_field_text",
72                &self.supports_distance_field_text(),
73            )
74            .finish()
75    }
76}
77
78impl DirectContext {
79    // Removed from Skia
80    #[cfg(feature = "gl")]
81    #[deprecated(since = "0.74.0", note = "use gpu::direct_contexts::make_gl()")]
82    pub fn new_gl<'a>(
83        interface: impl Into<crate::gpu::gl::Interface>,
84        options: impl Into<Option<&'a ContextOptions>>,
85    ) -> Option<DirectContext> {
86        crate::gpu::direct_contexts::make_gl(interface, options)
87    }
88
89    // Removed from Skia
90    #[cfg(feature = "vulkan")]
91    #[deprecated(since = "0.74.0", note = "use gpu::direct_contexts::make_vulkan()")]
92    pub fn new_vulkan<'a>(
93        backend_context: &crate::gpu::vk::BackendContext,
94        options: impl Into<Option<&'a ContextOptions>>,
95    ) -> Option<DirectContext> {
96        crate::gpu::direct_contexts::make_vulkan(backend_context, options)
97    }
98
99    #[cfg(feature = "metal")]
100    #[deprecated(since = "0.74.0", note = "use gpu::direct_contexts::make_metal()")]
101    pub fn new_metal<'a>(
102        backend_context: &crate::gpu::mtl::BackendContext,
103        options: impl Into<Option<&'a ContextOptions>>,
104    ) -> Option<DirectContext> {
105        crate::gpu::direct_contexts::make_metal(backend_context, options)
106    }
107
108    #[cfg(feature = "d3d")]
109    #[deprecated(since = "0.95.0", note = "use gpu::direct_contexts::make_d3d()")]
110    #[allow(clippy::missing_safety_doc)]
111    pub unsafe fn new_d3d<'a>(
112        backend_context: &crate::gpu::d3d::BackendContext,
113        options: impl Into<Option<&'a ContextOptions>>,
114    ) -> Option<DirectContext> {
115        unsafe { crate::gpu::direct_contexts::make_d3d(backend_context, options) }
116    }
117
118    pub fn reset(&mut self, backend_state: Option<u32>) -> &mut Self {
119        unsafe {
120            self.native_mut()
121                .resetContext(backend_state.unwrap_or(sb::kAll_GrBackendState))
122        }
123        self
124    }
125
126    pub fn reset_gl_texture_bindings(&mut self) -> &mut Self {
127        unsafe { self.native_mut().resetGLTextureBindings() }
128        self
129    }
130
131    pub fn abandon(&mut self) -> &mut Self {
132        unsafe {
133            // self.native_mut().abandonContext()
134            sb::GrDirectContext_abandonContext(self.native_mut() as *mut _ as _)
135        }
136        self
137    }
138
139    pub fn is_device_lost(&mut self) -> bool {
140        unsafe { self.native_mut().isDeviceLost() }
141    }
142
143    // TODO: threadSafeProxy()
144
145    pub fn oomed(&mut self) -> bool {
146        unsafe { self.native_mut().oomed() }
147    }
148
149    pub fn release_resources_and_abandon(&mut self) -> &mut Self {
150        unsafe {
151            sb::GrDirectContext_releaseResourcesAndAbandonContext(self.native_mut() as *mut _ as _)
152        }
153        self
154    }
155
156    pub fn resource_cache_limit(&self) -> usize {
157        unsafe { self.native().getResourceCacheLimit() }
158    }
159
160    pub fn resource_cache_usage(&self) -> ResourceCacheUsage {
161        let mut resource_count = 0;
162        let mut resource_bytes = 0;
163        unsafe {
164            self.native()
165                .getResourceCacheUsage(&mut resource_count, &mut resource_bytes)
166        }
167        ResourceCacheUsage {
168            resource_count: resource_count.try_into().unwrap(),
169            resource_bytes,
170        }
171    }
172
173    pub fn resource_cache_purgeable_bytes(&self) -> usize {
174        unsafe { self.native().getResourceCachePurgeableBytes() }
175    }
176
177    pub fn set_resource_cache_limits(&mut self, limits: ResourceCacheLimits) {
178        unsafe {
179            self.native_mut().setResourceCacheLimits(
180                limits.max_resources.try_into().unwrap(),
181                limits.max_resource_bytes,
182            )
183        }
184    }
185
186    pub fn set_resource_cache_limit(&mut self, max_resource_bytes: usize) {
187        unsafe { self.native_mut().setResourceCacheLimit(max_resource_bytes) }
188    }
189
190    pub fn free_gpu_resources(&mut self) -> &mut Self {
191        unsafe { sb::GrDirectContext_freeGpuResources(self.native_mut() as *mut _ as _) }
192        self
193    }
194
195    pub fn perform_deferred_cleanup(
196        &mut self,
197        not_used: Duration,
198        opts: impl Into<Option<PurgeResourceOptions>>,
199    ) -> &mut Self {
200        unsafe {
201            sb::C_GrDirectContext_performDeferredCleanup(
202                self.native_mut(),
203                not_used.as_millis().try_into().unwrap(),
204                opts.into().unwrap_or(PurgeResourceOptions::AllResources),
205            )
206        }
207        self
208    }
209
210    pub fn purge_unlocked_resource_bytes(
211        &mut self,
212        bytes_to_purge: usize,
213        prefer_scratch_resources: bool,
214    ) -> &mut Self {
215        unsafe {
216            self.native_mut()
217                .purgeUnlockedResources(bytes_to_purge, prefer_scratch_resources)
218        }
219        self
220    }
221
222    pub fn purge_unlocked_resources(&mut self, opts: PurgeResourceOptions) -> &mut Self {
223        unsafe { self.native_mut().purgeUnlockedResources1(opts) }
224        self
225    }
226
227    pub fn supported_gpu_stats(&self) -> GpuStatsFlags {
228        GpuStatsFlags::from_bits_truncate(unsafe { self.native().supportedGpuStats() })
229    }
230
231    // TODO: wait()
232
233    pub fn flush_and_submit(&mut self) -> &mut Self {
234        unsafe { sb::C_GrDirectContext_flushAndSubmit(self.native_mut()) }
235        self
236    }
237
238    pub fn flush_submit_and_sync_cpu(&mut self) -> &mut Self {
239        self.flush(&FlushInfo::default());
240        self.submit(SyncCpu::Yes);
241        self
242    }
243
244    #[deprecated(since = "0.37.0", note = "Use flush()")]
245    pub fn flush_with_info(&mut self, info: &FlushInfo) -> SemaphoresSubmitted {
246        self.flush(info)
247    }
248
249    pub fn flush<'a>(&mut self, info: impl Into<Option<&'a FlushInfo>>) -> SemaphoresSubmitted {
250        let n = self.native_mut();
251        if let Some(info) = info.into() {
252            unsafe { n.flush(info.native()) }
253        } else {
254            let info = FlushInfo::default();
255            unsafe { n.flush(info.native()) }
256        }
257    }
258
259    pub fn flush_image_with_info(
260        &mut self,
261        image: &Image,
262        info: &FlushInfo,
263    ) -> SemaphoresSubmitted {
264        unsafe {
265            sb::C_GrDirectContext_flushImageWithInfo(
266                self.native_mut(),
267                image.clone().into_ptr(),
268                info.native(),
269            )
270        }
271    }
272
273    pub fn flush_image(&mut self, image: &Image) {
274        unsafe { sb::C_GrDirectContext_flushImage(self.native_mut(), image.clone().into_ptr()) }
275    }
276
277    pub fn flush_and_submit_image(&mut self, image: &Image) {
278        unsafe {
279            sb::C_GrDirectContext_flushAndSubmitImage(self.native_mut(), image.clone().into_ptr())
280        }
281    }
282
283    pub fn flush_surface_with_access(
284        &mut self,
285        surface: &mut Surface,
286        access: surfaces::BackendSurfaceAccess,
287        info: &FlushInfo,
288    ) -> SemaphoresSubmitted {
289        unsafe {
290            self.native_mut()
291                .flush3(surface.native_mut(), access, info.native())
292        }
293    }
294
295    pub fn flush_surface_with_texture_state(
296        &mut self,
297        surface: &mut Surface,
298        info: &FlushInfo,
299        new_state: Option<&MutableTextureState>,
300    ) -> SemaphoresSubmitted {
301        unsafe {
302            self.native_mut().flush4(
303                surface.native_mut(),
304                info.native(),
305                new_state.native_ptr_or_null(),
306            )
307        }
308    }
309
310    pub fn flush_and_submit_surface(
311        &mut self,
312        surface: &mut Surface,
313        sync_cpu: impl Into<Option<SyncCpu>>,
314    ) {
315        unsafe {
316            self.native_mut()
317                .flushAndSubmit1(surface.native_mut(), sync_cpu.into().unwrap_or(SyncCpu::No))
318        }
319    }
320
321    pub fn flush_surface(&mut self, surface: &mut Surface) {
322        unsafe { self.native_mut().flush5(surface.native_mut()) }
323    }
324
325    pub fn submit(&mut self, submit_info: impl Into<SubmitInfo>) -> bool {
326        unsafe { self.native_mut().submit(&submit_info.into().into_native()) }
327    }
328
329    pub fn check_async_work_completion(&mut self) {
330        unsafe { self.native_mut().checkAsyncWorkCompletion() }
331    }
332
333    // TODO: dumpMemoryStatistics()
334
335    pub fn supports_distance_field_text(&self) -> bool {
336        unsafe { self.native().supportsDistanceFieldText() }
337    }
338}
339
340#[cfg(feature = "vulkan")]
341impl DirectContext {
342    pub fn can_detect_new_vk_pipeline_cache_data(&self) -> bool {
343        unsafe { self.native().canDetectNewVkPipelineCacheData() }
344    }
345
346    pub fn has_new_vk_pipeline_cache_data(&self) -> bool {
347        unsafe { self.native().hasNewVkPipelineCacheData() }
348    }
349
350    pub fn store_vk_pipeline_cache_data(&mut self) -> &mut Self {
351        unsafe {
352            self.native_mut().storeVkPipelineCacheData();
353        }
354        self
355    }
356
357    pub fn store_vk_pipeline_cache_data_with_max_size(&mut self, max_size: usize) -> &mut Self {
358        unsafe {
359            self.native_mut().storeVkPipelineCacheData1(max_size);
360        }
361        self
362    }
363}
364
365impl DirectContext {
366    // TODO: wrap createBackendTexture (several variants)
367    //       introduced in m76, m77, and m79
368    //       extended in m84 with finishedProc and finishedContext
369    //       extended in m107 with label
370
371    // TODO: wrap updateBackendTexture (several variants)
372    //       introduced in m84
373
374    pub fn compressed_backend_format(&self, compression: TextureCompressionType) -> BackendFormat {
375        let mut backend_format = BackendFormat::new_invalid();
376        unsafe {
377            sb::C_GrDirectContext_compressedBackendFormat(
378                self.native(),
379                compression,
380                backend_format.native_mut(),
381            )
382        };
383        backend_format
384    }
385
386    // TODO: wrap createCompressedBackendTexture (several variants)
387    //       introduced in m81
388    //       extended in m84 with finishedProc and finishedContext
389
390    // TODO: wrap updateCompressedBackendTexture (two variants)
391    //       introduced in m86
392
393    // TODO: add variant with GpuFinishedProc / GpuFinishedContext
394    pub fn set_backend_texture_state(
395        &mut self,
396        backend_texture: &BackendTexture,
397        state: &MutableTextureState,
398    ) -> bool {
399        self.set_backend_texture_state_and_return_previous(backend_texture, state)
400            .is_some()
401    }
402
403    pub fn set_backend_texture_state_and_return_previous(
404        &mut self,
405        backend_texture: &BackendTexture,
406        state: &MutableTextureState,
407    ) -> Option<MutableTextureState> {
408        let mut previous = MutableTextureState::default();
409        unsafe {
410            self.native_mut().setBackendTextureState(
411                backend_texture.native(),
412                state.native(),
413                previous.native_mut(),
414                None,
415                ptr::null_mut(),
416            )
417        }
418        .then_some(previous)
419    }
420
421    // TODO: add variant with GpuFinishedProc / GpuFinishedContext
422    pub fn set_backend_render_target_state(
423        &mut self,
424        target: &BackendRenderTarget,
425        state: &MutableTextureState,
426    ) -> bool {
427        self.set_backend_render_target_state_and_return_previous(target, state)
428            .is_some()
429    }
430
431    pub fn set_backend_render_target_state_and_return_previous(
432        &mut self,
433        target: &BackendRenderTarget,
434        state: &MutableTextureState,
435    ) -> Option<MutableTextureState> {
436        let mut previous = MutableTextureState::default();
437        unsafe {
438            self.native_mut().setBackendRenderTargetState(
439                target.native(),
440                state.native(),
441                previous.native_mut(),
442                None,
443                ptr::null_mut(),
444            )
445        }
446        .then_some(previous)
447    }
448
449    pub fn delete_backend_texture(&mut self, texture: &BackendTexture) {
450        unsafe { self.native_mut().deleteBackendTexture(texture.native()) }
451    }
452
453    pub fn precompile_shader(&mut self, key: &Data, data: &Data) -> bool {
454        unsafe {
455            self.native_mut()
456                .precompileShader(key.native(), data.native())
457        }
458    }
459
460    pub fn id(&self) -> DirectContextId {
461        let mut id = DirectContextId { id: 0 };
462        unsafe { sb::C_GrDirectContext_directContextId(self.native(), id.native_mut()) }
463        id
464    }
465}
466
467#[cfg(test)]
468mod tests {
469    use super::DirectContext;
470    use crate::gpu::{SubmitInfo, SyncCpu};
471
472    #[allow(unused)]
473    fn submit_invocation(direct_context: &mut DirectContext) {
474        direct_context.submit(SyncCpu::Yes);
475        direct_context.submit(None);
476        direct_context.submit(Some(SyncCpu::Yes));
477        direct_context.submit(SubmitInfo::default());
478    }
479}