skia_safe/gpu/ganesh/
backend_surface.rs

1use std::fmt;
2
3use skia_bindings::{self as sb, GrBackendFormat, GrBackendRenderTarget, GrBackendTexture};
4
5use crate::gpu;
6use crate::{interop::AsStr, prelude::*, ISize};
7#[cfg(feature = "d3d")]
8use gpu::d3d;
9#[cfg(feature = "gl")]
10use gpu::gl;
11#[cfg(feature = "metal")]
12use gpu::mtl;
13#[cfg(feature = "vulkan")]
14use gpu::vk;
15use gpu::{BackendAPI, Mipmapped, MutableTextureState};
16
17pub type BackendFormat = Handle<GrBackendFormat>;
18unsafe_send_sync!(BackendFormat);
19
20impl NativeDrop for GrBackendFormat {
21    fn drop(&mut self) {
22        unsafe { sb::C_GrBackendFormat_destruct(self) }
23    }
24}
25
26impl NativeClone for GrBackendFormat {
27    fn clone(&self) -> Self {
28        unsafe { GrBackendFormat::new1(self) }
29    }
30}
31
32impl fmt::Debug for BackendFormat {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        let mut d = f.debug_struct("BackendFormat");
35        d.field("backend", &self.backend());
36        d.field("channel_mask", &self.channel_mask());
37        #[cfg(feature = "gl")]
38        d.field("gl_format", &self.as_gl_format());
39        #[cfg(feature = "vulkan")]
40        d.field("vk_format", &self.as_vk_format());
41        #[cfg(feature = "metal")]
42        d.field("mtl_format", &self.as_mtl_format());
43        #[cfg(feature = "d3d")]
44        d.field("dxgi_format", &self.as_dxgi_format());
45        d.finish()
46    }
47}
48
49impl BackendFormat {
50    #[deprecated(
51        note = "The creation of invalid BackendFormats isn't supported anymore",
52        since = "0.37.0"
53    )]
54    pub fn new() -> Self {
55        Self::new_invalid()
56    }
57
58    pub(crate) fn new_invalid() -> Self {
59        Self::construct(|bf| unsafe { sb::C_GrBackendFormat_Construct(bf) })
60    }
61
62    #[cfg(feature = "gl")]
63    pub fn new_gl(format: gl::Enum, target: gl::Enum) -> Self {
64        Self::construct(|bf| unsafe { sb::C_GrBackendFormats_ConstructGL(bf, format, target) })
65            .assert_valid()
66    }
67
68    #[cfg(feature = "vulkan")]
69    #[deprecated(since = "0.67.0", note = "use gpu::backend_formats::make_vk()")]
70    pub fn new_vulkan(
71        format: vk::Format,
72        will_use_drm_format_modifiers: impl Into<Option<bool>>,
73    ) -> Self {
74        gpu::backend_formats::make_vk(format, will_use_drm_format_modifiers)
75    }
76
77    #[cfg(feature = "vulkan")]
78    #[deprecated(since = "0.67.0", note = "use gpu::backend_formats::make_vk_ycbcr()")]
79    pub fn new_vulkan_ycbcr(
80        conversion_info: &vk::YcbcrConversionInfo,
81        will_use_drm_format_modifiers: impl Into<Option<bool>>,
82    ) -> Self {
83        gpu::backend_formats::make_vk_ycbcr(conversion_info, will_use_drm_format_modifiers)
84    }
85
86    #[cfg(feature = "metal")]
87    #[deprecated(since = "0.74.0", note = "use gpu::backend_formats::make_mtl()")]
88    pub fn new_metal(format: mtl::PixelFormat) -> Self {
89        gpu::backend_formats::make_mtl(format)
90    }
91
92    #[cfg(feature = "d3d")]
93    pub fn new_dxgi(format: d3d::DXGI_FORMAT) -> Self {
94        Self::construct(|bf| unsafe {
95            sb::C_GrBackendFormat_ConstructDxgi(bf, format.into_native())
96        })
97        .assert_valid()
98    }
99
100    pub fn backend(&self) -> BackendAPI {
101        self.native().fBackend
102    }
103
104    pub fn channel_mask(&self) -> u32 {
105        unsafe { self.native().channelMask() }
106    }
107
108    // m117: Even though Skia did, we won't deprecate these functions here for convenience.
109
110    #[cfg(feature = "gl")]
111    pub fn as_gl_format(&self) -> gl::Format {
112        gpu::backend_formats::as_gl_format(self)
113    }
114
115    #[cfg(feature = "gl")]
116    pub fn as_gl_format_enum(&self) -> gl::Enum {
117        gpu::backend_formats::as_gl_format_enum(self)
118    }
119
120    // Deprecated in Skia
121    #[cfg(feature = "vulkan")]
122    pub fn as_vk_format(&self) -> Option<vk::Format> {
123        gpu::backend_formats::as_vk_format(self)
124    }
125
126    #[cfg(feature = "metal")]
127    pub fn as_mtl_format(&self) -> Option<mtl::PixelFormat> {
128        gpu::backend_formats::as_mtl_format(self)
129    }
130
131    #[cfg(feature = "d3d")]
132    pub fn as_dxgi_format(&self) -> Option<d3d::DXGI_FORMAT> {
133        let mut f = sb::DXGI_FORMAT::DXGI_FORMAT_UNKNOWN;
134        unsafe { self.native().asDxgiFormat(&mut f) }
135            .if_true_some(d3d::DXGI_FORMAT::from_native_c(f))
136    }
137
138    #[must_use]
139    pub fn to_texture_2d(&self) -> Self {
140        let mut new = Self::new_invalid();
141        unsafe { sb::C_GrBackendFormat_makeTexture2D(self.native(), new.native_mut()) };
142        assert!(Self::native_is_valid(new.native()));
143        new
144    }
145
146    #[deprecated(
147        note = "Invalid BackendFormats are not supported anymore",
148        since = "0.37.0"
149    )]
150    pub fn is_valid(&self) -> bool {
151        self.native().fValid
152    }
153
154    pub(crate) fn native_is_valid(format: &GrBackendFormat) -> bool {
155        format.fValid
156    }
157
158    pub(crate) fn assert_valid(self) -> Self {
159        assert!(Self::native_is_valid(self.native()));
160        self
161    }
162}
163
164// GrBackendTexture contains a string `fLabel`, and with SSO on some platforms, it can't be moved.
165// See <https://github.com/rust-skia/rust-skia/issues/750>.
166pub type BackendTexture = RefHandle<GrBackendTexture>;
167unsafe_send_sync!(BackendTexture);
168
169impl NativeDrop for GrBackendTexture {
170    fn drop(&mut self) {
171        unsafe { sb::C_GrBackendTexture_delete(self) }
172    }
173}
174
175impl Clone for BackendTexture {
176    fn clone(&self) -> Self {
177        unsafe { Self::from_ptr(sb::C_GrBackendTexture_Clone(self.native())) }.unwrap()
178    }
179}
180
181impl fmt::Debug for BackendTexture {
182    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183        let mut d = f.debug_struct("BackendTexture");
184        d.field("dimensions", &self.dimensions());
185        d.field("label", &self.label());
186        d.field("mipmapped", &self.mipmapped());
187        d.field("backend", &self.backend());
188        #[cfg(feature = "gl")]
189        d.field("gl_texture_info", &self.gl_texture_info());
190        #[cfg(feature = "vulkan")]
191        d.field("vulkan_image_info", &self.vulkan_image_info());
192        #[cfg(feature = "metal")]
193        d.field("metal_texture_info", &self.metal_texture_info());
194        #[cfg(feature = "d3d")]
195        d.field(
196            "d3d_texture_resource_info",
197            &self.d3d_texture_resource_info(),
198        );
199        d.field("backend_format", &self.backend_format());
200        d.field("is_protected", &self.is_protected());
201        d.finish()
202    }
203}
204
205impl BackendTexture {
206    pub(crate) fn new_invalid() -> Self {
207        Self::from_ptr(unsafe { sb::C_GrBackendTexture_new() }).unwrap()
208    }
209
210    #[cfg(feature = "gl")]
211    #[allow(clippy::missing_safety_doc)]
212    #[deprecated(since = "0.67.0", note = "use gpu::backend_textures::make_gl()")]
213    pub unsafe fn new_gl(
214        (width, height): (i32, i32),
215        mipmapped: gpu::Mipmapped,
216        gl_info: gl::TextureInfo,
217    ) -> Self {
218        gpu::backend_textures::make_gl((width, height), mipmapped, gl_info, "")
219    }
220
221    #[cfg(feature = "gl")]
222    #[allow(clippy::missing_safety_doc)]
223    #[deprecated(since = "0.67.0", note = "use gpu::backend_textures::make_gl()")]
224    pub unsafe fn new_gl_with_label(
225        (width, height): (i32, i32),
226        mipmapped: gpu::Mipmapped,
227        gl_info: gl::TextureInfo,
228        label: impl AsRef<str>,
229    ) -> Self {
230        gpu::backend_textures::make_gl((width, height), mipmapped, gl_info, label)
231    }
232
233    #[cfg(feature = "vulkan")]
234    #[allow(clippy::missing_safety_doc)]
235    #[deprecated(since = "0.67.0", note = "use gpu::backend_textures::make_vk()")]
236    pub unsafe fn new_vulkan((width, height): (i32, i32), vk_info: &vk::ImageInfo) -> Self {
237        gpu::backend_textures::make_vk((width, height), vk_info, "")
238    }
239
240    #[cfg(feature = "vulkan")]
241    #[allow(clippy::missing_safety_doc)]
242    #[deprecated(since = "0.67.0", note = "use gpu::backend_textures::make_vk()")]
243    pub unsafe fn new_vulkan_with_label(
244        (width, height): (i32, i32),
245        vk_info: &vk::ImageInfo,
246        label: impl AsRef<str>,
247    ) -> Self {
248        gpu::backend_textures::make_vk((width, height), vk_info, label)
249    }
250
251    #[cfg(feature = "metal")]
252    #[allow(clippy::missing_safety_doc)]
253    #[deprecated(since = "0.74.0", note = "use gpu::backend_textures::make_mtl()")]
254    pub unsafe fn new_metal(
255        (width, height): (i32, i32),
256        mipmapped: gpu::Mipmapped,
257        mtl_info: &mtl::TextureInfo,
258    ) -> Self {
259        gpu::backend_textures::make_mtl((width, height), mipmapped, mtl_info, "")
260    }
261
262    #[cfg(feature = "metal")]
263    #[allow(clippy::missing_safety_doc)]
264    #[deprecated(since = "0.74.0", note = "use gpu::backend_textures::make_mtl()")]
265    pub unsafe fn new_metal_with_label(
266        (width, height): (i32, i32),
267        mipmapped: gpu::Mipmapped,
268        mtl_info: &mtl::TextureInfo,
269        label: impl AsRef<str>,
270    ) -> Self {
271        gpu::backend_textures::make_mtl((width, height), mipmapped, mtl_info, label)
272    }
273
274    #[cfg(feature = "d3d")]
275    pub fn new_d3d((width, height): (i32, i32), d3d_info: &d3d::TextureResourceInfo) -> Self {
276        Self::new_d3d_with_label((width, height), d3d_info, "")
277    }
278
279    #[cfg(feature = "d3d")]
280    pub fn new_d3d_with_label(
281        (width, height): (i32, i32),
282        d3d_info: &d3d::TextureResourceInfo,
283        label: impl AsRef<str>,
284    ) -> Self {
285        let label = label.as_ref().as_bytes();
286        unsafe {
287            Self::from_native_if_valid(sb::C_GrBackendTexture_newD3D(
288                width,
289                height,
290                d3d_info.native(),
291                label.as_ptr() as _,
292                label.len(),
293            ))
294        }
295        .unwrap()
296    }
297
298    pub(crate) unsafe fn from_native_if_valid(
299        backend_texture: *mut GrBackendTexture,
300    ) -> Option<BackendTexture> {
301        Self::native_is_valid(backend_texture)
302            .if_true_then_some(|| BackendTexture::from_ptr(backend_texture).unwrap())
303    }
304
305    pub fn dimensions(&self) -> ISize {
306        ISize::new(self.width(), self.height())
307    }
308
309    pub fn width(&self) -> i32 {
310        self.native().fWidth
311    }
312
313    pub fn height(&self) -> i32 {
314        self.native().fHeight
315    }
316
317    pub fn label(&self) -> &str {
318        self.native().fLabel.as_str()
319    }
320
321    pub fn mipmapped(&self) -> Mipmapped {
322        self.native().fMipmapped
323    }
324
325    #[deprecated(since = "0.35.0", note = "Use has_mipmaps()")]
326    pub fn has_mip_maps(&self) -> bool {
327        self.has_mipmaps()
328    }
329
330    pub fn has_mipmaps(&self) -> bool {
331        self.native().fMipmapped == Mipmapped::Yes
332    }
333
334    pub fn backend(&self) -> BackendAPI {
335        self.native().fBackend
336    }
337
338    // Deprecated in Skia
339    #[cfg(feature = "gl")]
340    pub fn gl_texture_info(&self) -> Option<gl::TextureInfo> {
341        gpu::backend_textures::get_gl_texture_info(self)
342    }
343
344    // Deprecated in Skia
345    #[cfg(feature = "gl")]
346    pub fn gl_texture_parameters_modified(&mut self) {
347        gpu::backend_textures::gl_texture_parameters_modified(self)
348    }
349
350    // Deprecated in Skia
351    #[cfg(feature = "vulkan")]
352    pub fn vulkan_image_info(&self) -> Option<vk::ImageInfo> {
353        gpu::backend_textures::get_vk_image_info(self)
354    }
355
356    // Deprecated in Skia
357    #[cfg(feature = "vulkan")]
358    pub fn set_vulkan_image_layout(&mut self, layout: vk::ImageLayout) -> &mut Self {
359        gpu::backend_textures::set_vk_image_layout(self, layout)
360    }
361
362    #[cfg(feature = "metal")]
363    pub fn metal_texture_info(&self) -> Option<mtl::TextureInfo> {
364        gpu::backend_textures::get_mtl_texture_info(self)
365    }
366
367    #[cfg(feature = "d3d")]
368    pub fn d3d_texture_resource_info(&self) -> Option<d3d::TextureResourceInfo> {
369        unsafe {
370            let mut info = sb::GrD3DTextureResourceInfo::default();
371            self.native()
372                .getD3DTextureResourceInfo(&mut info)
373                .if_true_then_some(|| {
374                    assert!(!info.fResource.fObject.is_null());
375                    d3d::TextureResourceInfo::from_native_c(info)
376                })
377        }
378    }
379
380    #[cfg(feature = "d3d")]
381    pub fn set_d3d_resource_state(&mut self, resource_state: d3d::ResourceStateEnum) -> &mut Self {
382        unsafe { self.native_mut().setD3DResourceState(resource_state) }
383        self
384    }
385
386    pub fn backend_format(&self) -> BackendFormat {
387        let mut format = BackendFormat::new_invalid();
388        unsafe { sb::C_GrBackendTexture_getBackendFormat(self.native(), format.native_mut()) };
389        assert!(BackendFormat::native_is_valid(format.native()));
390        format
391    }
392
393    pub fn set_mutable_state(&mut self, state: &MutableTextureState) {
394        unsafe { self.native_mut().setMutableState(state.native()) }
395    }
396
397    pub fn is_protected(&self) -> bool {
398        unsafe { self.native().isProtected() }
399    }
400
401    #[deprecated(note = "Invalid BackendTextures aren't supported", since = "0.37.0")]
402    pub fn is_valid(&self) -> bool {
403        self.native().fIsValid
404    }
405
406    pub(crate) unsafe fn native_is_valid(texture: *const GrBackendTexture) -> bool {
407        (*texture).fIsValid
408    }
409
410    #[allow(clippy::wrong_self_convention)]
411    pub fn is_same_texture(&mut self, texture: &BackendTexture) -> bool {
412        unsafe { self.native_mut().isSameTexture(texture.native()) }
413    }
414}
415
416pub type BackendRenderTarget = Handle<GrBackendRenderTarget>;
417unsafe_send_sync!(BackendRenderTarget);
418
419impl NativeDrop for GrBackendRenderTarget {
420    fn drop(&mut self) {
421        unsafe { sb::C_GrBackendRenderTarget_destruct(self) }
422    }
423}
424
425impl NativeClone for GrBackendRenderTarget {
426    fn clone(&self) -> Self {
427        construct(|render_target| unsafe {
428            sb::C_GrBackendRenderTarget_CopyConstruct(render_target, self)
429        })
430    }
431}
432
433impl fmt::Debug for BackendRenderTarget {
434    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435        let mut d = f.debug_struct("BackendRenderTarget");
436        d.field("dimensions", &self.dimensions());
437        d.field("sample_count", &self.sample_count());
438        d.field("stencil_bits", &self.stencil_bits());
439        d.field("backend", &self.backend());
440        d.field("is_framebuffer_only", &self.is_framebuffer_only());
441        #[cfg(feature = "gl")]
442        d.field("gl_framebuffer_info", &self.gl_framebuffer_info());
443        #[cfg(feature = "vulkan")]
444        d.field("vulkan_image_info", &self.vulkan_image_info());
445        #[cfg(feature = "metal")]
446        d.field("metal_texture_info", &self.metal_texture_info());
447        #[cfg(feature = "d3d")]
448        d.field(
449            "d3d_texture_resource_info",
450            &self.d3d_texture_resource_info(),
451        );
452        d.field("backend_format", &self.backend_format());
453        d.field("is_protected", &self.is_protected());
454        d.finish()
455    }
456}
457
458impl BackendRenderTarget {
459    #[cfg(feature = "gl")]
460    #[deprecated(since = "0.67.0", note = "use gpu::backend_render_targets::make_gl()")]
461    pub fn new_gl(
462        (width, height): (i32, i32),
463        sample_count: impl Into<Option<usize>>,
464        stencil_bits: usize,
465        info: gl::FramebufferInfo,
466    ) -> Self {
467        gpu::backend_render_targets::make_gl((width, height), sample_count, stencil_bits, info)
468    }
469
470    #[cfg(feature = "vulkan")]
471    #[deprecated(since = "0.67.0", note = "use gpu::backend_render_targets::make_vk()")]
472    pub fn new_vulkan((width, height): (i32, i32), info: &vk::ImageInfo) -> Self {
473        gpu::backend_render_targets::make_vk((width, height), info)
474    }
475
476    #[cfg(feature = "metal")]
477    #[deprecated(since = "0.74.0", note = "use gpu::backend_render_targets::make_mtl()")]
478    pub fn new_metal((width, height): (i32, i32), mtl_info: &mtl::TextureInfo) -> Self {
479        gpu::backend_render_targets::make_mtl((width, height), mtl_info)
480    }
481
482    #[cfg(feature = "d3d")]
483    pub fn new_d3d((width, height): (i32, i32), d3d_info: &d3d::TextureResourceInfo) -> Self {
484        Self::construct(|brt| unsafe {
485            sb::C_GrBackendRenderTarget_ConstructD3D(brt, width, height, d3d_info.native())
486        })
487    }
488
489    pub(crate) fn from_native_c_if_valid(
490        native: GrBackendRenderTarget,
491    ) -> Option<BackendRenderTarget> {
492        let backend_render_target = BackendRenderTarget::from_native_c(native);
493        Self::native_is_valid(backend_render_target.native()).if_true_some(backend_render_target)
494    }
495
496    pub fn dimensions(&self) -> ISize {
497        ISize::new(self.width(), self.height())
498    }
499
500    pub fn width(&self) -> i32 {
501        self.native().fWidth
502    }
503
504    pub fn height(&self) -> i32 {
505        self.native().fHeight
506    }
507
508    pub fn sample_count(&self) -> usize {
509        self.native().fSampleCnt.try_into().unwrap()
510    }
511
512    pub fn stencil_bits(&self) -> usize {
513        self.native().fStencilBits.try_into().unwrap()
514    }
515
516    pub fn backend(&self) -> BackendAPI {
517        self.native().fBackend
518    }
519
520    pub fn is_framebuffer_only(&self) -> bool {
521        self.native().fFramebufferOnly
522    }
523
524    // Deprecated in Skia
525    #[cfg(feature = "gl")]
526    pub fn gl_framebuffer_info(&self) -> Option<gl::FramebufferInfo> {
527        gpu::backend_render_targets::get_gl_framebuffer_info(self)
528    }
529
530    // Deprecated in Skia
531    #[cfg(feature = "vulkan")]
532    pub fn vulkan_image_info(&self) -> Option<vk::ImageInfo> {
533        gpu::backend_render_targets::get_vk_image_info(self)
534    }
535
536    // Deprecated in Skia
537    #[cfg(feature = "vulkan")]
538    pub fn set_vulkan_image_layout(&mut self, layout: vk::ImageLayout) -> &mut Self {
539        gpu::backend_render_targets::set_vk_image_layout(self, layout)
540    }
541
542    #[cfg(feature = "metal")]
543    pub fn metal_texture_info(&self) -> Option<mtl::TextureInfo> {
544        gpu::backend_render_targets::get_mtl_texture_info(self)
545    }
546
547    #[cfg(feature = "d3d")]
548    pub fn d3d_texture_resource_info(&self) -> Option<d3d::TextureResourceInfo> {
549        let mut info = sb::GrD3DTextureResourceInfo::default();
550        unsafe { self.native().getD3DTextureResourceInfo(&mut info) }.if_true_then_some(|| {
551            assert!(!info.fResource.fObject.is_null());
552            d3d::TextureResourceInfo::from_native_c(info)
553        })
554    }
555
556    #[cfg(feature = "d3d")]
557    pub fn set_d3d_resource_state(&mut self, resource_state: d3d::ResourceStateEnum) -> &mut Self {
558        unsafe { self.native_mut().setD3DResourceState(resource_state) }
559        self
560    }
561
562    pub fn backend_format(&self) -> BackendFormat {
563        BackendFormat::construct(|format| unsafe {
564            sb::C_GrBackendRenderTarget_getBackendFormat(self.native(), format)
565        })
566    }
567
568    pub fn set_mutable_state(&mut self, state: &MutableTextureState) {
569        unsafe { self.native_mut().setMutableState(state.native()) }
570    }
571
572    pub fn is_protected(&self) -> bool {
573        unsafe { self.native().isProtected() }
574    }
575
576    #[deprecated(
577        since = "0.37.0",
578        note = "Exposed BackendRenderTargets are always valid."
579    )]
580    pub fn is_valid(&self) -> bool {
581        self.native().fIsValid
582    }
583
584    pub(crate) fn native_is_valid(rt: &GrBackendRenderTarget) -> bool {
585        rt.fIsValid
586    }
587}
588
589#[cfg(test)]
590mod tests {
591    use super::BackendTexture;
592    use std::hint::black_box;
593
594    // Regression test for <https://github.com/rust-skia/rust-skia/issues/750>
595    #[test]
596    fn create_move_and_drop_backend_texture() {
597        let texture = force_move(BackendTexture::new_invalid());
598        drop(texture);
599    }
600
601    fn force_move<V>(src: V) -> V {
602        let src = black_box(src);
603        *black_box(Box::new(src))
604    }
605}