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