d3d11 0.3.3

Safe(r) bindings to the D3D11 API
Documentation
use std::rc::Rc;

use anyhow::Context as _;
use d3d11::dxgi::*;
use d3d11::*;
#[cfg(target_os = "windows")]
use raw_window_handle::HasWindowHandle;

#[cfg(feature = "fxc")]
fn compile_shaders() -> anyhow::Result<(Vec<u8>, Vec<u8>)> {
    use d3d11::fxc::ShaderTarget;

    const SHADER_SRC: &str = r#"
    struct VSOutput {
        float4 position : SV_POSITION;
        float4 color : COLOR;
    };

    static const float4 vertices[3] = {
        float4(0.0, 0.5, 0.0, 1.0),
        float4(0.5, -0.5, 0.0, 1.0),
        float4(-0.5, -0.5, 0.0, 1.0)
    };

    static const float4 colors[3] = {
        float4(1.0, 0.0, 0.0, 1.0),
        float4(0.0, 1.0, 0.0, 1.0),
        float4(0.0, 0.0, 1.0, 1.0)
    };

    VSOutput VSMain(uint vertexID : SV_VertexID) {
        VSOutput output;
        output.position = vertices[vertexID];
        output.color = colors[vertexID];
        return output;
    }

    float4 PSMain(VSOutput input) : SV_Target {
        return input.color;
    }
    "#;

    let vs_data = d3d11::fxc::compile(
        SHADER_SRC.as_bytes(),
        Some("triangle_vs"),
        &[],
        "VSMain",
        ShaderTarget::Vertex,
    )?;

    let ps_data = d3d11::fxc::compile(
        SHADER_SRC.as_bytes(),
        Some("triangle_ps"),
        &[],
        "PSMain",
        ShaderTarget::Pixel,
    )?;

    Ok((vs_data, ps_data))
}

#[cfg(not(feature = "fxc"))]
fn compile_shaders() -> anyhow::Result<(Vec<u8>, Vec<u8>)> {
    let vs_data = include_bytes!("shaders/triangle_vs.cso").to_vec();
    let ps_data = include_bytes!("shaders/triangle_ps.cso").to_vec();
    Ok((vs_data, ps_data))
}

fn main() -> anyhow::Result<()> {
    let sdl_context = Rc::new(sdl2::init().expect("Failed to initialize SDL"));
    let video_subsystem = sdl_context
        .video()
        .expect("Failed to initialize video subsystem");

    let window = {
        let mut builder = video_subsystem.window("Hello this is window", 800, 600);

        let mut builder_ref = builder.position_centered().resizable();

        if cfg!(not(target_os = "windows")) {
            builder_ref = builder_ref.vulkan();
        }

        builder_ref.build().expect("Failed to create window")
    };

    // TODO(cohae): We should support passing raw-window-handle windows directly to SwapChain::create
    #[cfg(target_os = "windows")]
    let output_window = {
        let rwh = window
            .window_handle()
            .expect("Failed to get window handle")
            .as_raw();
        HWND(match rwh {
            raw_window_handle::RawWindowHandle::Win32(handle) => handle.hwnd.get(),
            _ => anyhow::bail!("Invalid window handle type"),
        } as _)
    };
    #[cfg(not(target_os = "windows"))]
    let output_window = &window;

    let device = Device::create(None, true).context("Failed to create device")?;
    let ictx = device.get_immediate_context();

    let swapchain = match SwapChain::create(
        &device,
        output_window,
        &SwapChainDesc::builder()
            .buffer_count(1)
            .buffer_desc(
                ModeDesc::builder()
                    .width(1024)
                    .height(1024)
                    .refresh_rate(Rational::default())
                    .format(Format::R8g8b8a8UnormSrgb)
                    .build(),
            )
            .buffer_usage(DxgiUsage::RENDER_TARGET_OUTPUT)
            .swap_effect(SwapEffect::Discard)
            .build(),
    ) {
        Ok(o) => o,
        Err(e) => {
            anyhow::bail!("Failed to create swapchain: {} ({})", e, sdl2::get_error());
        }
    };

    let (vs_data, ps_data) = compile_shaders()?;

    let vs = device.create_vertex_shader(&vs_data)?;
    let ps = device.create_pixel_shader(&ps_data)?;

    let target: Texture2D = swapchain
        .get_buffer(0)
        .context("Failed to get swapchain buffer")?;

    let rtv = device.create_render_target_view(&target, None)?;

    let dctx = device.create_deferred_context()?;

    let mut event_pump = sdl_context.event_pump().unwrap();
    'app: loop {
        for event in event_pump.poll_iter() {
            if let sdl2::event::Event::Quit { .. } = event {
                break 'app;
            }
        }

        dctx.clear_render_target_view(&rtv, &[0.0, 0.0, 0.0, 1.0]);
        dctx.output_merger_set_render_targets(&[Some(&rtv)], None);
        dctx.rasterizer_set_viewports(&[Viewport::builder().width(1024.0).height(1024.0).build()]);

        dctx.vertex_set_shader(Some(&vs));
        dctx.pixel_set_shader(Some(&ps));
        dctx.input_assembler_set_primitive_topology(PrimitiveTopology::TriangleList);
        dctx.draw(3, 0);

        ictx.execute_command_list(&dctx.finish_command_list(false)?, false);

        swapchain.present(0, PresentFlags::empty());
    }

    Ok(())
}