librashader-cli 0.11.4

RetroArch shaders for all.
Documentation
use crate::render::{CommonFrameOptions, RenderTest};
use anyhow::anyhow;
use image::RgbaImage;
use librashader::runtime::d3d11::*;
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
use librashader::runtime::{Size, Viewport};
use std::io::{Cursor, Write};
use std::ops::DerefMut;
use std::path::Path;

impl RenderTest for Direct3D11 {
    fn new(path: &Path) -> anyhow::Result<Self>
    where
        Self: Sized,
    {
        Direct3D11::new(path)
    }

    fn image_size(&self) -> Size<u32> {
        self.image_bytes.size
    }

    fn render_with_preset_and_params(
        &mut self,
        preset: ShaderPreset,
        frame_count: usize,
        output_size: Option<Size<u32>>,
        param_setter: Option<&dyn Fn(&RuntimeParameters)>,
        frame_options: Option<CommonFrameOptions>,
    ) -> anyhow::Result<image::RgbaImage> {
        let output_size = output_size.unwrap_or(self.image_bytes.size);
        let (renderbuffer, rtv) = self.create_renderbuffer(output_size)?;

        unsafe {
            let mut filter_chain = FilterChain::load_from_preset(
                preset,
                &self.device,
                Some(&FilterChainOptions {
                    force_no_mipmaps: false,
                    disable_cache: false,
                    ..Default::default()
                }),
            )?;

            if let Some(setter) = param_setter {
                setter(filter_chain.parameters());
            }
            let viewport = Viewport::new_render_target_sized_origin(&rtv, None)?;
            let options = frame_options.map(|options| FrameOptions {
                clear_history: options.clear_history,
                frame_direction: options.frame_direction,
                rotation: options.rotation,
                total_subframes: options.total_subframes,
                current_subframe: options.current_subframe,
                aspect_ratio: options.aspect_ratio,
                frametime_delta: options.frametime_delta,
                frames_per_second: options.frames_per_second,
                ..Default::default()
            });

            for frame in 0..=frame_count {
                filter_chain.frame(None, &self.image_srv, &viewport, frame, options.as_ref())?;
            }

            let mut renderbuffer_desc = Default::default();
            self.immediate_context.Flush();
            renderbuffer.GetDesc(&mut renderbuffer_desc);

            let mut staging = None;
            self.device.CreateTexture2D(
                &D3D11_TEXTURE2D_DESC {
                    MipLevels: 1,
                    BindFlags: 0,
                    MiscFlags: 0,
                    Usage: D3D11_USAGE_STAGING,
                    CPUAccessFlags: D3D11_CPU_ACCESS_READ.0 as u32,
                    ..renderbuffer_desc
                },
                None,
                Some(&mut staging),
            )?;

            let staging = staging.ok_or(anyhow!("Unable to create staging texture"))?;

            self.immediate_context.CopyResource(&staging, &renderbuffer);

            let mut pixels: Vec<u8> = Vec::new();

            let mut map_info = Default::default();
            self.immediate_context
                .Map(&staging, 0, D3D11_MAP_READ, 0, Some(&mut map_info))?;

            let slice = std::slice::from_raw_parts(
                map_info.pData as *const u8,
                (renderbuffer_desc.Height * map_info.RowPitch) as usize,
            );
            pixels.resize(
                (renderbuffer_desc.Height * renderbuffer_desc.Width * 4) as usize,
                0,
            );

            let mut cursor = Cursor::new(pixels.deref_mut());
            for chunk in slice.chunks(map_info.RowPitch as usize) {
                cursor.write_all(&chunk[..(renderbuffer_desc.Width * 4) as usize])?
            }

            let image = RgbaImage::from_raw(output_size.width, output_size.height, pixels)
                .ok_or(anyhow!("Unable to create image from data"))?;
            self.immediate_context.Unmap(&staging, 0);

            Ok(image)
        }
    }
}

use librashader::presets::ShaderPreset;
use librashader_runtime::image::{Image, UVDirection};
use windows::{
    Win32::Foundation::*, Win32::Graphics::Direct3D::*, Win32::Graphics::Direct3D11::*,
    Win32::Graphics::Dxgi::Common::*, Win32::Graphics::Dxgi::*,
};

pub struct Direct3D11 {
    device: ID3D11Device,
    immediate_context: ID3D11DeviceContext,
    _image_tex: ID3D11Texture2D,
    image_srv: ID3D11ShaderResourceView,
    image_bytes: Image,
}

impl Direct3D11 {
    fn create_device() -> anyhow::Result<(IDXGIFactory4, ID3D11Device, ID3D11DeviceContext)> {
        let dxgi_factory: IDXGIFactory4 = unsafe { CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG) }?;
        let feature_levels = vec![D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1];

        let mut out_device = None;
        let mut out_context = None;
        let mut _out_feature_level = D3D_FEATURE_LEVEL_11_0;

        unsafe {
            D3D11CreateDevice(
                None,
                D3D_DRIVER_TYPE_HARDWARE,
                HMODULE::default(),
                D3D11_CREATE_DEVICE_DEBUG,
                Some(&feature_levels),
                D3D11_SDK_VERSION,
                Some(&mut out_device),
                Some(&mut _out_feature_level),
                Some(&mut out_context),
            )
        }?;
        Ok((dxgi_factory, out_device.unwrap(), out_context.unwrap()))
    }

    pub fn new(image_path: &Path) -> anyhow::Result<Self> {
        let (_factory, device, imm_context) = Self::create_device()?;

        let (image, image_tex, srv) = Self::load_image(&device, image_path)?;
        Ok(Self {
            device,
            immediate_context: imm_context,
            image_bytes: image,
            _image_tex: image_tex,
            image_srv: srv,
        })
    }

    fn load_image(
        device: &ID3D11Device,
        image_path: &Path,
    ) -> anyhow::Result<(Image, ID3D11Texture2D, ID3D11ShaderResourceView)> {
        let image = Image::load(image_path, UVDirection::TopLeft)?;
        let desc = D3D11_TEXTURE2D_DESC {
            Width: image.size.width,
            Height: image.size.height,
            // todo: set this to 0
            MipLevels: 1,
            ArraySize: 1,
            SampleDesc: DXGI_SAMPLE_DESC {
                Count: 1,
                Quality: 0,
            },
            CPUAccessFlags: 0,
            Format: DXGI_FORMAT_R8G8B8A8_UNORM,
            Usage: D3D11_USAGE_DEFAULT,
            BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
            ..Default::default()
        };

        unsafe {
            let mut resource = None;
            device.CreateTexture2D(
                &desc,
                Some(&D3D11_SUBRESOURCE_DATA {
                    pSysMem: image.bytes.as_ptr().cast(),
                    SysMemPitch: image.pitch as u32,
                    SysMemSlicePitch: 0,
                }),
                Some(&mut resource),
            )?;
            let resource = resource.ok_or(anyhow!("Failed to create texture"))?;

            let mut srv = None;
            device.CreateShaderResourceView(
                &resource,
                Some(&D3D11_SHADER_RESOURCE_VIEW_DESC {
                    Format: desc.Format,
                    ViewDimension: D3D_SRV_DIMENSION_TEXTURE2D,
                    Anonymous: D3D11_SHADER_RESOURCE_VIEW_DESC_0 {
                        Texture2D: D3D11_TEX2D_SRV {
                            MostDetailedMip: 0,
                            MipLevels: u32::MAX,
                        },
                    },
                }),
                Some(&mut srv),
            )?;

            let srv = srv.ok_or(anyhow!("Failed to create SRV"))?;

            Ok((image, resource, srv))
        }
    }

    fn create_renderbuffer(
        &self,
        size: Size<u32>,
    ) -> anyhow::Result<(ID3D11Texture2D, ID3D11RenderTargetView)> {
        let desc = D3D11_TEXTURE2D_DESC {
            Width: size.width,
            Height: size.height,
            // todo: set this to 0
            MipLevels: 1,
            ArraySize: 1,
            SampleDesc: DXGI_SAMPLE_DESC {
                Count: 1,
                Quality: 0,
            },
            CPUAccessFlags: 0,
            Format: DXGI_FORMAT_R8G8B8A8_UNORM,
            Usage: D3D11_USAGE_DEFAULT,
            BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
            ..Default::default()
        };

        unsafe {
            let mut resource = None;
            self.device
                .CreateTexture2D(&desc, None, Some(&mut resource))?;
            let resource = resource.ok_or(anyhow!("Failed to create texture"))?;

            let mut rtv = None;
            self.device
                .CreateRenderTargetView(&resource, None, Some(&mut rtv))?;

            let rtv = rtv.ok_or(anyhow!("Failed to create RTV"))?;

            Ok((resource, rtv))
        }
    }
}