librashader-runtime-d3d9 0.10.1

RetroArch shaders for all.
Documentation
use crate::error;
use crate::error::{assume_d3d_init, FilterChainError};

use librashader_common::{FilterMode, GetSize, ImageFormat, Size, WrapMode};
use librashader_presets::Scale2D;
use librashader_runtime::binding::TextureInput;
use librashader_runtime::scaling::{ScaleFramebuffer, ViewportSize};
use windows::Win32::Graphics::Direct3D9::{
    IDirect3DDevice9, IDirect3DSurface9, IDirect3DTexture9, D3DCLEAR_TARGET, D3DFORMAT,
    D3DPOOL_DEFAULT, D3DTEXF_LINEAR, D3DUSAGE_RENDERTARGET,
};

/// An image view for use as a shader resource.
///
/// Contains an `ID3D11ShaderResourceView`, and a size.
#[derive(Debug, Clone)]
pub struct D3D9Texture {
    /// A handle to the shader resource view.
    pub handle: IDirect3DTexture9,
    pub mipmap: bool,
    pub original_format: ImageFormat,
}

impl TextureInput for D3D9InputTexture {
    fn size(&self) -> Size<u32> {
        GetSize::size(&self.handle).unwrap_or_else(|_| Size::new(0, 0))
    }
}

impl AsRef<D3D9InputTexture> for D3D9InputTexture {
    fn as_ref(&self) -> &D3D9InputTexture {
        &self
    }
}

#[derive(Debug, Clone)]
pub struct D3D9InputTexture {
    /// A handle to the shader resource view.
    pub handle: IDirect3DTexture9,
    pub filter: FilterMode,
    pub wrap: WrapMode,
    pub mipmode: FilterMode,
    pub is_srgb: bool,
}

impl D3D9Texture {
    pub fn new(
        device: &IDirect3DDevice9,
        size: Size<u32>,
        format: ImageFormat,
        mipmap: bool,
    ) -> error::Result<Self> {
        let mut texture = None;
        // eprintln!("creating texture");
        unsafe {
            device.CreateTexture(
                size.width,
                size.height,
                if mipmap { 0 } else { 1 },
                D3DUSAGE_RENDERTARGET as u32,
                format.into(),
                D3DPOOL_DEFAULT,
                &mut texture,
                std::ptr::null_mut(),
            )?;
        }

        // eprintln!("creating texture ok");

        assume_d3d_init!(texture, "CreateTexture");

        Ok(Self {
            handle: texture,
            mipmap,
            original_format: format,
        })
    }

    pub fn init(&mut self, size: Size<u32>, format: ImageFormat) -> error::Result<()> {
        // let format = format.into();

        unsafe {
            let mut texture = None;
            self.handle.GetDevice()?.CreateTexture(
                size.width,
                size.height,
                if self.mipmap { 0 } else { 1 },
                D3DUSAGE_RENDERTARGET as u32,
                format.into(),
                D3DPOOL_DEFAULT,
                &mut texture,
                std::ptr::null_mut(),
            )?;
            assume_d3d_init!(mut texture, "CreateTexture2D");
            std::mem::swap(&mut self.handle, &mut texture);
            drop(texture)
        }

        Ok(())
    }

    pub fn size(&self) -> error::Result<Size<u32>> {
        let mut desc = Default::default();
        unsafe {
            self.handle.GetLevelDesc(0, &mut desc)?;
        }

        Ok(Size {
            height: desc.Height,
            width: desc.Width,
        })
    }

    pub fn as_output(&self) -> error::Result<IDirect3DSurface9> {
        unsafe { Ok(self.handle.GetSurfaceLevel(0)?) }
    }

    pub(crate) fn scale(
        &mut self,
        scaling: Scale2D,
        format: ImageFormat,
        viewport_size: &Size<u32>,
        source_size: &Size<u32>,
        original_size: &Size<u32>,
        should_mipmap: bool,
    ) -> error::Result<Size<u32>> {
        let size = source_size.scale_viewport(scaling, *viewport_size, *original_size, None);

        if self.size()? != size || should_mipmap != self.mipmap {
            self.mipmap = should_mipmap;
            self.init(
                size,
                if format == ImageFormat::Unknown {
                    ImageFormat::R8G8B8A8Unorm
                } else {
                    format
                },
            )?;
        }
        Ok(size)
    }

    pub fn clear(&mut self, device: &IDirect3DDevice9) -> error::Result<()> {
        unsafe {
            let surface = self.handle.GetSurfaceLevel(0)?;
            device.SetRenderTarget(0, &surface)?;
            device.Clear(0, std::ptr::null_mut(), D3DCLEAR_TARGET as u32, 0x0, 0.0, 0)?;
        }
        Ok(())
    }

    pub fn copy_from(
        &mut self,
        device: &IDirect3DDevice9,
        input: &IDirect3DTexture9,
    ) -> error::Result<()> {
        let mut desc = Default::default();
        unsafe {
            input.GetLevelDesc(0, &mut desc)?;
        }

        let size = Size {
            width: desc.Width,
            height: desc.Height,
        };

        if self.size()? != size || D3DFORMAT::from(self.original_format) != desc.Format {
            eprintln!("[history] resizing");
            self.init(size, ImageFormat::from(desc.Format))?;
        }

        unsafe {
            let dest = self.handle.GetSurfaceLevel(0)?;
            let source = input.GetSurfaceLevel(0)?;

            device.StretchRect(
                &source,
                std::ptr::null(),
                &dest,
                std::ptr::null(),
                D3DTEXF_LINEAR,
            )?;
        }

        Ok(())
    }

    pub fn as_input(
        &self,
        filter: FilterMode,
        mipmode: FilterMode,
        wrap: WrapMode,
    ) -> D3D9InputTexture {
        D3D9InputTexture {
            handle: self.handle.clone(),
            filter,
            wrap,
            mipmode,
            is_srgb: self.original_format == ImageFormat::R8G8B8A8Srgb,
        }
    }
}

impl ScaleFramebuffer for D3D9Texture {
    type Error = FilterChainError;
    type Context = ();

    fn scale(
        &mut self,
        scaling: Scale2D,
        format: ImageFormat,
        viewport_size: &Size<u32>,
        source_size: &Size<u32>,
        original_size: &Size<u32>,
        should_mipmap: bool,
        _context: &Self::Context,
    ) -> Result<Size<u32>, Self::Error> {
        self.scale(
            scaling,
            format,
            viewport_size,
            source_size,
            original_size,
            should_mipmap,
        )
    }
}