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) }.then_some(d3d::DXGI_FORMAT::from_native_c(f))
135    }
136
137    #[must_use]
138    pub fn to_texture_2d(&self) -> Self {
139        let mut new = Self::new_invalid();
140        unsafe { sb::C_GrBackendFormat_makeTexture2D(self.native(), new.native_mut()) };
141        assert!(Self::native_is_valid(new.native()));
142        new
143    }
144
145    #[deprecated(
146        note = "Invalid BackendFormats are not supported anymore",
147        since = "0.37.0"
148    )]
149    pub fn is_valid(&self) -> bool {
150        self.native().fValid
151    }
152
153    pub(crate) fn native_is_valid(format: &GrBackendFormat) -> bool {
154        format.fValid
155    }
156
157    pub(crate) fn assert_valid(self) -> Self {
158        assert!(Self::native_is_valid(self.native()));
159        self
160    }
161}
162
163// GrBackendTexture contains a string `fLabel`, and with SSO on some platforms, it can't be moved.
164// See <https://github.com/rust-skia/rust-skia/issues/750>.
165pub type BackendTexture = RefHandle<GrBackendTexture>;
166unsafe_send_sync!(BackendTexture);
167
168impl NativeDrop for GrBackendTexture {
169    fn drop(&mut self) {
170        unsafe { sb::C_GrBackendTexture_delete(self) }
171    }
172}
173
174impl Clone for BackendTexture {
175    fn clone(&self) -> Self {
176        unsafe { Self::from_ptr(sb::C_GrBackendTexture_Clone(self.native())) }.unwrap()
177    }
178}
179
180impl fmt::Debug for BackendTexture {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        let mut d = f.debug_struct("BackendTexture");
183        d.field("dimensions", &self.dimensions());
184        d.field("label", &self.label());
185        d.field("mipmapped", &self.mipmapped());
186        d.field("backend", &self.backend());
187        #[cfg(feature = "gl")]
188        d.field("gl_texture_info", &self.gl_texture_info());
189        #[cfg(feature = "vulkan")]
190        d.field("vulkan_image_info", &self.vulkan_image_info());
191        #[cfg(feature = "metal")]
192        d.field("metal_texture_info", &self.metal_texture_info());
193        #[cfg(feature = "d3d")]
194        d.field(
195            "d3d_texture_resource_info",
196            &self.d3d_texture_resource_info(),
197        );
198        d.field("backend_format", &self.backend_format());
199        d.field("is_protected", &self.is_protected());
200        d.finish()
201    }
202}
203
204impl BackendTexture {
205    pub(crate) fn new_invalid() -> Self {
206        Self::from_ptr(unsafe { sb::C_GrBackendTexture_new() }).unwrap()
207    }
208
209    #[cfg(feature = "gl")]
210    #[allow(clippy::missing_safety_doc)]
211    #[deprecated(since = "0.67.0", note = "use gpu::backend_textures::make_gl()")]
212    pub unsafe fn new_gl(
213        (width, height): (i32, i32),
214        mipmapped: gpu::Mipmapped,
215        gl_info: gl::TextureInfo,
216    ) -> Self {
217        gpu::backend_textures::make_gl((width, height), mipmapped, gl_info, "")
218    }
219
220    #[cfg(feature = "gl")]
221    #[allow(clippy::missing_safety_doc)]
222    #[deprecated(since = "0.67.0", note = "use gpu::backend_textures::make_gl()")]
223    pub unsafe fn new_gl_with_label(
224        (width, height): (i32, i32),
225        mipmapped: gpu::Mipmapped,
226        gl_info: gl::TextureInfo,
227        label: impl AsRef<str>,
228    ) -> Self {
229        gpu::backend_textures::make_gl((width, height), mipmapped, gl_info, label)
230    }
231
232    #[cfg(feature = "vulkan")]
233    #[allow(clippy::missing_safety_doc)]
234    #[deprecated(since = "0.67.0", note = "use gpu::backend_textures::make_vk()")]
235    pub unsafe fn new_vulkan((width, height): (i32, i32), vk_info: &vk::ImageInfo) -> Self {
236        gpu::backend_textures::make_vk((width, height), vk_info, "")
237    }
238
239    #[cfg(feature = "vulkan")]
240    #[allow(clippy::missing_safety_doc)]
241    #[deprecated(since = "0.67.0", note = "use gpu::backend_textures::make_vk()")]
242    pub unsafe fn new_vulkan_with_label(
243        (width, height): (i32, i32),
244        vk_info: &vk::ImageInfo,
245        label: impl AsRef<str>,
246    ) -> Self {
247        gpu::backend_textures::make_vk((width, height), vk_info, label)
248    }
249
250    #[cfg(feature = "metal")]
251    #[allow(clippy::missing_safety_doc)]
252    #[deprecated(since = "0.74.0", note = "use gpu::backend_textures::make_mtl()")]
253    pub unsafe fn new_metal(
254        (width, height): (i32, i32),
255        mipmapped: gpu::Mipmapped,
256        mtl_info: &mtl::TextureInfo,
257    ) -> Self {
258        gpu::backend_textures::make_mtl((width, height), mipmapped, mtl_info, "")
259    }
260
261    #[cfg(feature = "metal")]
262    #[allow(clippy::missing_safety_doc)]
263    #[deprecated(since = "0.74.0", note = "use gpu::backend_textures::make_mtl()")]
264    pub unsafe fn new_metal_with_label(
265        (width, height): (i32, i32),
266        mipmapped: gpu::Mipmapped,
267        mtl_info: &mtl::TextureInfo,
268        label: impl AsRef<str>,
269    ) -> Self {
270        gpu::backend_textures::make_mtl((width, height), mipmapped, mtl_info, label)
271    }
272
273    #[cfg(feature = "d3d")]
274    pub fn new_d3d((width, height): (i32, i32), d3d_info: &d3d::TextureResourceInfo) -> Self {
275        Self::new_d3d_with_label((width, height), d3d_info, "")
276    }
277
278    #[cfg(feature = "d3d")]
279    pub fn new_d3d_with_label(
280        (width, height): (i32, i32),
281        d3d_info: &d3d::TextureResourceInfo,
282        label: impl AsRef<str>,
283    ) -> Self {
284        let label = label.as_ref().as_bytes();
285        unsafe {
286            Self::from_native_if_valid(sb::C_GrBackendTexture_newD3D(
287                width,
288                height,
289                d3d_info.native(),
290                label.as_ptr() as _,
291                label.len(),
292            ))
293        }
294        .unwrap()
295    }
296
297    pub(crate) unsafe fn from_native_if_valid(
298        backend_texture: *mut GrBackendTexture,
299    ) -> Option<BackendTexture> {
300        Self::native_is_valid(backend_texture)
301            .then(|| BackendTexture::from_ptr(backend_texture).unwrap())
302    }
303
304    pub fn dimensions(&self) -> ISize {
305        ISize::new(self.width(), self.height())
306    }
307
308    pub fn width(&self) -> i32 {
309        self.native().fWidth
310    }
311
312    pub fn height(&self) -> i32 {
313        self.native().fHeight
314    }
315
316    pub fn label(&self) -> &str {
317        self.native().fLabel.as_str()
318    }
319
320    pub fn mipmapped(&self) -> Mipmapped {
321        self.native().fMipmapped
322    }
323
324    #[deprecated(since = "0.35.0", note = "Use has_mipmaps()")]
325    pub fn has_mip_maps(&self) -> bool {
326        self.has_mipmaps()
327    }
328
329    pub fn has_mipmaps(&self) -> bool {
330        self.native().fMipmapped == Mipmapped::Yes
331    }
332
333    pub fn backend(&self) -> BackendAPI {
334        self.native().fBackend
335    }
336
337    // Deprecated in Skia
338    #[cfg(feature = "gl")]
339    pub fn gl_texture_info(&self) -> Option<gl::TextureInfo> {
340        gpu::backend_textures::get_gl_texture_info(self)
341    }
342
343    // Deprecated in Skia
344    #[cfg(feature = "gl")]
345    pub fn gl_texture_parameters_modified(&mut self) {
346        gpu::backend_textures::gl_texture_parameters_modified(self)
347    }
348
349    // Deprecated in Skia
350    #[cfg(feature = "vulkan")]
351    pub fn vulkan_image_info(&self) -> Option<vk::ImageInfo> {
352        gpu::backend_textures::get_vk_image_info(self)
353    }
354
355    // Deprecated in Skia
356    #[cfg(feature = "vulkan")]
357    pub fn set_vulkan_image_layout(&mut self, layout: vk::ImageLayout) -> &mut Self {
358        gpu::backend_textures::set_vk_image_layout(self, layout)
359    }
360
361    #[cfg(feature = "metal")]
362    pub fn metal_texture_info(&self) -> Option<mtl::TextureInfo> {
363        gpu::backend_textures::get_mtl_texture_info(self)
364    }
365
366    #[cfg(feature = "d3d")]
367    pub fn d3d_texture_resource_info(&self) -> Option<d3d::TextureResourceInfo> {
368        unsafe {
369            let mut info = sb::GrD3DTextureResourceInfo::default();
370            self.native().getD3DTextureResourceInfo(&mut info).then(|| {
371                assert!(!info.fResource.fObject.is_null());
372                d3d::TextureResourceInfo::from_native_c(info)
373            })
374        }
375    }
376
377    #[cfg(feature = "d3d")]
378    pub fn set_d3d_resource_state(&mut self, resource_state: d3d::ResourceStateEnum) -> &mut Self {
379        unsafe { self.native_mut().setD3DResourceState(resource_state) }
380        self
381    }
382
383    pub fn backend_format(&self) -> BackendFormat {
384        let mut format = BackendFormat::new_invalid();
385        unsafe { sb::C_GrBackendTexture_getBackendFormat(self.native(), format.native_mut()) };
386        assert!(BackendFormat::native_is_valid(format.native()));
387        format
388    }
389
390    pub fn set_mutable_state(&mut self, state: &MutableTextureState) {
391        unsafe { self.native_mut().setMutableState(state.native()) }
392    }
393
394    pub fn is_protected(&self) -> bool {
395        unsafe { self.native().isProtected() }
396    }
397
398    #[deprecated(note = "Invalid BackendTextures aren't supported", since = "0.37.0")]
399    pub fn is_valid(&self) -> bool {
400        self.native().fIsValid
401    }
402
403    pub(crate) unsafe fn native_is_valid(texture: *const GrBackendTexture) -> bool {
404        (*texture).fIsValid
405    }
406
407    #[allow(clippy::wrong_self_convention)]
408    pub fn is_same_texture(&mut self, texture: &BackendTexture) -> bool {
409        unsafe { self.native_mut().isSameTexture(texture.native()) }
410    }
411}
412
413pub type BackendRenderTarget = Handle<GrBackendRenderTarget>;
414unsafe_send_sync!(BackendRenderTarget);
415
416impl NativeDrop for GrBackendRenderTarget {
417    fn drop(&mut self) {
418        unsafe { sb::C_GrBackendRenderTarget_destruct(self) }
419    }
420}
421
422impl NativeClone for GrBackendRenderTarget {
423    fn clone(&self) -> Self {
424        construct(|render_target| unsafe {
425            sb::C_GrBackendRenderTarget_CopyConstruct(render_target, self)
426        })
427    }
428}
429
430impl fmt::Debug for BackendRenderTarget {
431    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432        let mut d = f.debug_struct("BackendRenderTarget");
433        d.field("dimensions", &self.dimensions());
434        d.field("sample_count", &self.sample_count());
435        d.field("stencil_bits", &self.stencil_bits());
436        d.field("backend", &self.backend());
437        d.field("is_framebuffer_only", &self.is_framebuffer_only());
438        #[cfg(feature = "gl")]
439        d.field("gl_framebuffer_info", &self.gl_framebuffer_info());
440        #[cfg(feature = "vulkan")]
441        d.field("vulkan_image_info", &self.vulkan_image_info());
442        #[cfg(feature = "metal")]
443        d.field("metal_texture_info", &self.metal_texture_info());
444        #[cfg(feature = "d3d")]
445        d.field(
446            "d3d_texture_resource_info",
447            &self.d3d_texture_resource_info(),
448        );
449        d.field("backend_format", &self.backend_format());
450        d.field("is_protected", &self.is_protected());
451        d.finish()
452    }
453}
454
455impl BackendRenderTarget {
456    #[cfg(feature = "gl")]
457    #[deprecated(since = "0.67.0", note = "use gpu::backend_render_targets::make_gl()")]
458    pub fn new_gl(
459        (width, height): (i32, i32),
460        sample_count: impl Into<Option<usize>>,
461        stencil_bits: usize,
462        info: gl::FramebufferInfo,
463    ) -> Self {
464        gpu::backend_render_targets::make_gl((width, height), sample_count, stencil_bits, info)
465    }
466
467    #[cfg(feature = "vulkan")]
468    #[deprecated(since = "0.67.0", note = "use gpu::backend_render_targets::make_vk()")]
469    pub fn new_vulkan((width, height): (i32, i32), info: &vk::ImageInfo) -> Self {
470        gpu::backend_render_targets::make_vk((width, height), info)
471    }
472
473    #[cfg(feature = "metal")]
474    #[deprecated(since = "0.74.0", note = "use gpu::backend_render_targets::make_mtl()")]
475    pub fn new_metal((width, height): (i32, i32), mtl_info: &mtl::TextureInfo) -> Self {
476        gpu::backend_render_targets::make_mtl((width, height), mtl_info)
477    }
478
479    #[cfg(feature = "d3d")]
480    pub fn new_d3d((width, height): (i32, i32), d3d_info: &d3d::TextureResourceInfo) -> Self {
481        Self::construct(|brt| unsafe {
482            sb::C_GrBackendRenderTarget_ConstructD3D(brt, width, height, d3d_info.native())
483        })
484    }
485
486    pub(crate) fn from_native_c_if_valid(
487        native: GrBackendRenderTarget,
488    ) -> Option<BackendRenderTarget> {
489        let backend_render_target = BackendRenderTarget::from_native_c(native);
490        Self::native_is_valid(backend_render_target.native()).then_some(backend_render_target)
491    }
492
493    pub fn dimensions(&self) -> ISize {
494        ISize::new(self.width(), self.height())
495    }
496
497    pub fn width(&self) -> i32 {
498        self.native().fWidth
499    }
500
501    pub fn height(&self) -> i32 {
502        self.native().fHeight
503    }
504
505    pub fn sample_count(&self) -> usize {
506        self.native().fSampleCnt.try_into().unwrap()
507    }
508
509    pub fn stencil_bits(&self) -> usize {
510        self.native().fStencilBits.try_into().unwrap()
511    }
512
513    pub fn backend(&self) -> BackendAPI {
514        self.native().fBackend
515    }
516
517    pub fn is_framebuffer_only(&self) -> bool {
518        self.native().fFramebufferOnly
519    }
520
521    // Deprecated in Skia
522    #[cfg(feature = "gl")]
523    pub fn gl_framebuffer_info(&self) -> Option<gl::FramebufferInfo> {
524        gpu::backend_render_targets::get_gl_framebuffer_info(self)
525    }
526
527    // Deprecated in Skia
528    #[cfg(feature = "vulkan")]
529    pub fn vulkan_image_info(&self) -> Option<vk::ImageInfo> {
530        gpu::backend_render_targets::get_vk_image_info(self)
531    }
532
533    // Deprecated in Skia
534    #[cfg(feature = "vulkan")]
535    pub fn set_vulkan_image_layout(&mut self, layout: vk::ImageLayout) -> &mut Self {
536        gpu::backend_render_targets::set_vk_image_layout(self, layout)
537    }
538
539    #[cfg(feature = "metal")]
540    pub fn metal_texture_info(&self) -> Option<mtl::TextureInfo> {
541        gpu::backend_render_targets::get_mtl_texture_info(self)
542    }
543
544    #[cfg(feature = "d3d")]
545    pub fn d3d_texture_resource_info(&self) -> Option<d3d::TextureResourceInfo> {
546        let mut info = sb::GrD3DTextureResourceInfo::default();
547        unsafe { self.native().getD3DTextureResourceInfo(&mut info) }.then(|| {
548            assert!(!info.fResource.fObject.is_null());
549            d3d::TextureResourceInfo::from_native_c(info)
550        })
551    }
552
553    #[cfg(feature = "d3d")]
554    pub fn set_d3d_resource_state(&mut self, resource_state: d3d::ResourceStateEnum) -> &mut Self {
555        unsafe { self.native_mut().setD3DResourceState(resource_state) }
556        self
557    }
558
559    pub fn backend_format(&self) -> BackendFormat {
560        BackendFormat::construct(|format| unsafe {
561            sb::C_GrBackendRenderTarget_getBackendFormat(self.native(), format)
562        })
563    }
564
565    pub fn set_mutable_state(&mut self, state: &MutableTextureState) {
566        unsafe { self.native_mut().setMutableState(state.native()) }
567    }
568
569    pub fn is_protected(&self) -> bool {
570        unsafe { self.native().isProtected() }
571    }
572
573    #[deprecated(
574        since = "0.37.0",
575        note = "Exposed BackendRenderTargets are always valid."
576    )]
577    pub fn is_valid(&self) -> bool {
578        self.native().fIsValid
579    }
580
581    pub(crate) fn native_is_valid(rt: &GrBackendRenderTarget) -> bool {
582        rt.fIsValid
583    }
584}
585
586#[cfg(test)]
587mod tests {
588    use super::BackendTexture;
589    use std::hint::black_box;
590
591    // Regression test for <https://github.com/rust-skia/rust-skia/issues/750>
592    #[test]
593    fn create_move_and_drop_backend_texture() {
594        let texture = force_move(BackendTexture::new_invalid());
595        drop(texture);
596    }
597
598    fn force_move<V>(src: V) -> V {
599        let src = black_box(src);
600        *black_box(Box::new(src))
601    }
602}