diff --git a/imgui-examples/Cargo.lock b/imgui-examples/Cargo.lock index 0be9df1..aa01a69 100644 --- a/imgui-examples/Cargo.lock +++ b/imgui-examples/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "andrew" version = "0.2.1" @@ -211,6 +213,16 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "gfx_device_dx11" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gfx_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "gfx_device_gl" version = "0.16.0" @@ -229,6 +241,18 @@ dependencies = [ "gl_generator 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "gfx_window_dxgi" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gfx_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx_device_dx11 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winit 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "gfx_window_glutin" version = "0.30.0" @@ -279,7 +303,7 @@ dependencies = [ "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "glutin_egl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "glutin_gles2_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "glutin_glx_sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin_glx_sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "glutin_wgl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", @@ -312,7 +336,7 @@ dependencies = [ [[package]] name = "glutin_glx_sys" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -339,6 +363,7 @@ name = "imgui-examples" version = "0.0.24-pre" dependencies = [ "gfx 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx_window_dxgi 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "gfx_window_glutin 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", "glutin 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "imgui 0.0.24-pre", @@ -353,6 +378,7 @@ dependencies = [ "gfx 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", "imgui 0.0.24-pre", "imgui-sys 0.0.24-pre", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -932,8 +958,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum gfx 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1caca0dbbdae14bd51f7770b1fa9ae73f02ac313b4bb33b9d1747803bb81466f" "checksum gfx_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "559a7b438248707675721519065be39ae47756cea1324d68c90fbd63adcf64ac" +"checksum gfx_device_dx11 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc60429072cfafbed1b149ff1acc4541233e88e4fa744f78d8d2fbd5ff4f6beb" "checksum gfx_device_gl 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2229b46c5630717e9aeb330561374594ffb30749a314c1c2f5a6514eab5ed53f" "checksum gfx_gl 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e8a920f8f6c1025a7ddf9dd25502bf059506fd3cd765dfbe8dba0b56b7eeecb" +"checksum gfx_window_dxgi 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "105fc97238370ede5b9ad4de9f05f480bdbbffebf36707306a2dc08679d9f0ba" "checksum gfx_window_glutin 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f415d3534e6ea92f3ded1683e9e00592a76e2e67050e33244bb507719a211eb2" "checksum gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39a23d5e872a275135d66895d954269cf5e8661d234eb1c2480f4ce0d586acbd" "checksum gl_generator 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a795170cbd85b5a7baa58d6d7525cae6a03e486859860c220f7ebbbdd379d0a" @@ -941,7 +969,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum glutin 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff663466cd51f6fda5976e8a6f02a9fd65b8dde0b9b11db8344585174d015b2c" "checksum glutin_egl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "23f48987ab6cb2b61ad903b59e54a2fd0c380a7baff68cffd6826b69a73dd326" "checksum glutin_gles2_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "89996c30857ae1b4de4b5189abf1ea822a20a9fe9e1c93e5e7b862ff0bdd5cdf" -"checksum glutin_glx_sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5c3b6d06f860d5fae9986a01c36805ef1b1457a7e6df466f86d275d7133011cc" +"checksum glutin_glx_sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1290a5ca5e46fcfa7f66f949cc9d9194b2cb6f2ed61892c8c2b82343631dba57" "checksum glutin_wgl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f801bbc91efc22dd1c4818a47814fc72bf74d024510451b119381579bfa39021" "checksum khronos_api 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "037ab472c33f67b5fbd3e9163a2645319e5356fcd355efa6d4eb7fff4bbcb554" "checksum khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" diff --git a/imgui-examples/Cargo.toml b/imgui-examples/Cargo.toml index c6712d1..21dbc32 100644 --- a/imgui-examples/Cargo.toml +++ b/imgui-examples/Cargo.toml @@ -9,6 +9,11 @@ repository = "https://github.com/Gekkio/imgui-rs" license = "MIT/Apache-2.0" publish = false +[features] +opengl = ["imgui-gfx-renderer/opengl"] +directx = ["imgui-gfx-renderer/directx"] +default = ["opengl"] + [dev-dependencies] gfx = "0.18" gfx_window_glutin = "0.30" @@ -16,3 +21,6 @@ glutin = "0.20" imgui = { version = "0.0.24-pre", path = "../" } imgui-gfx-renderer = { version = "0.0.24-pre", path = "../imgui-gfx-renderer" } imgui-winit-support = { version = "0.0.24-pre", path = "../imgui-winit-support" } + +[target.'cfg(windows)'.dev-dependencies] +gfx_window_dxgi = "0.19" diff --git a/imgui-examples/README.md b/imgui-examples/README.md new file mode 100644 index 0000000..d53f497 --- /dev/null +++ b/imgui-examples/README.md @@ -0,0 +1,4 @@ +# `imgui-examples` + +- Run with OpenGL backend: `cargo run --example hello_gfx` +- Run with DirectX backend: `cargo run --example hello_gfx --features directx --no-default-features` \ No newline at end of file diff --git a/imgui-examples/examples/hello_gfx.rs b/imgui-examples/examples/hello_gfx.rs index 162f32d..e425e17 100644 --- a/imgui-examples/examples/hello_gfx.rs +++ b/imgui-examples/examples/hello_gfx.rs @@ -9,7 +9,13 @@ fn main() { } fn hello_world<'a>(ui: &Ui<'a>) -> bool { - ui.window(im_str!("Hello world")) + #[cfg(feature = "opengl")] + let window_title = im_str!("Hello world (OpenGL)"); + + #[cfg(feature = "directx")] + let window_title = im_str!("Hello world (DirectX)"); + + ui.window(window_title) .size((300.0, 100.0), ImGuiCond::FirstUseEver) .build(|| { ui.text(im_str!("Hello world!")); diff --git a/imgui-examples/examples/support_gfx/mod.rs b/imgui-examples/examples/support_gfx/mod.rs index d59cec6..ba7d9d0 100644 --- a/imgui-examples/examples/support_gfx/mod.rs +++ b/imgui-examples/examples/support_gfx/mod.rs @@ -3,6 +3,7 @@ use imgui_gfx_renderer::{Renderer, Shaders}; use imgui_winit_support; use std::time::Instant; +#[cfg(feature = "opengl")] pub fn run bool>(title: String, clear_color: [f32; 4], mut run_ui: F) { use gfx::{self, Device}; use gfx_window_glutin; @@ -145,3 +146,131 @@ pub fn run bool>(title: String, clear_color: [f32; 4], mut run_ device.cleanup(); } } + +#[cfg(feature = "directx")] +pub fn run bool>(title: String, clear_color: [f32; 4], mut run_ui: F) { + use gfx::{self, Device}; + use gfx_window_dxgi; + use glutin; + + type ColorFormat = gfx::format::Rgba8; + type DepthFormat = gfx::format::DepthStencil; + + let mut events_loop = glutin::EventsLoop::new(); + let window = glutin::WindowBuilder::new() + .with_title(title) + .with_dimensions(glutin::dpi::LogicalSize::new(1024f64, 768f64)); + let (window, mut device, mut factory, mut main_color) = + gfx_window_dxgi::init::(window, &events_loop) + .expect("Failed to initalize graphics"); + let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into(); + + let mut imgui = ImGui::init(); + { + // Fix incorrect colors with sRGB framebuffer + fn imgui_gamma_to_linear(col: ImVec4) -> ImVec4 { + let x = col.x.powf(2.2); + let y = col.y.powf(2.2); + let z = col.z.powf(2.2); + let w = 1.0 - (1.0 - col.w).powf(2.2); + ImVec4::new(x, y, z, w) + } + + let style = imgui.style_mut(); + for col in 0..style.colors.len() { + style.colors[col] = imgui_gamma_to_linear(style.colors[col]); + } + } + imgui.set_ini_filename(None); + + // In the examples we only use integer DPI factors, because the UI can get very blurry + // otherwise. This might or might not be what you want in a real application. + let hidpi_factor = window.inner.get_hidpi_factor().round(); + + let font_size = (13.0 * hidpi_factor) as f32; + + imgui.fonts().add_default_font_with_config( + ImFontConfig::new() + .oversample_h(1) + .pixel_snap_h(true) + .size_pixels(font_size), + ); + + imgui.fonts().add_font_with_config( + include_bytes!("../../../resources/mplus-1p-regular.ttf"), + ImFontConfig::new() + .merge_mode(true) + .oversample_h(1) + .pixel_snap_h(true) + .size_pixels(font_size) + .rasterizer_multiply(1.75), + &FontGlyphRange::japanese(), + ); + + imgui.set_font_global_scale((1.0 / hidpi_factor) as f32); + + let mut renderer = Renderer::init( + &mut imgui, + &mut factory, + Shaders::HlslSm40, + main_color.clone(), + ) + .expect("Failed to initialize renderer"); + + imgui_winit_support::configure_keys(&mut imgui); + + let mut last_frame = Instant::now(); + let mut quit = false; + + loop { + events_loop.poll_events(|event| { + use glutin::{ + Event, + WindowEvent::{CloseRequested, Resized}, + }; + + imgui_winit_support::handle_event( + &mut imgui, + &event, + window.inner.get_hidpi_factor(), + hidpi_factor, + ); + + if let Event::WindowEvent { event, .. } = event { + match event { + Resized(size) => { + // gfx_window_dxgi::update_views(&window, &mut factory, &mut device, ); + renderer.update_render_target(main_color.clone()); + } + CloseRequested => quit = true, + _ => (), + } + } + }); + if quit { + break; + } + + let now = Instant::now(); + let delta = now - last_frame; + let delta_s = delta.as_secs() as f32 + delta.subsec_nanos() as f32 / 1_000_000_000.0; + last_frame = now; + + imgui_winit_support::update_mouse_cursor(&imgui, &window.inner); + + let frame_size = imgui_winit_support::get_frame_size(&window.inner, hidpi_factor).unwrap(); + + let ui = imgui.frame(frame_size, delta_s); + if !run_ui(&ui) { + break; + } + + encoder.clear(&main_color, clear_color); + renderer + .render(ui, &mut factory, &mut encoder) + .expect("Rendering failed"); + encoder.flush(&mut device); + window.swap_buffers(1); + device.cleanup(); + } +} diff --git a/imgui-gfx-renderer/Cargo.toml b/imgui-gfx-renderer/Cargo.toml index b694823..495036b 100644 --- a/imgui-gfx-renderer/Cargo.toml +++ b/imgui-gfx-renderer/Cargo.toml @@ -9,6 +9,11 @@ repository = "https://github.com/Gekkio/imgui-rs" license = "MIT/Apache-2.0" categories = ["gui", "rendering"] +[features] +opengl = [] +directx = [] +default = ["opengl"] + [badges] travis-ci = { repository = "Gekkio/imgui-rs" } @@ -16,3 +21,6 @@ travis-ci = { repository = "Gekkio/imgui-rs" } gfx = "0.18" imgui = { version = "0.0.24-pre", path = "../" } imgui-sys = { version = "0.0.24-pre", path = "../imgui-sys", features = ["gfx"] } + +[target.'cfg(windows)'.build-dependencies] +winapi = { version = "0.3", features = ["d3dcompiler"] } diff --git a/imgui-gfx-renderer/build.rs b/imgui-gfx-renderer/build.rs new file mode 100644 index 0000000..8730d98 --- /dev/null +++ b/imgui-gfx-renderer/build.rs @@ -0,0 +1,124 @@ +fn main() { + #[cfg(windows)] + { + // Note: When building on Windows, this build script will automatically recompile the HLSL shaders. + + use std::env; + use std::path::PathBuf; + + let src_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("src"); + + hlsl_build::update_hlsl_shaders( + &src_dir.join("shader").join("sm_40.hlsl"), + &src_dir.join("data").join("vertex.fx"), + &src_dir.join("data").join("pixel.fx"), + ); + } +} + +#[cfg(windows)] +mod hlsl_build { + use std::ffi::CString; + use std::fs; + use std::path::Path; + use std::ptr; + use std::slice; + use std::str; + + pub fn update_hlsl_shaders( + source_path: &Path, + vertex_destination: &Path, + pixel_destination: &Path, + ) { + println!("cargo:rerun-if-changed={}", source_path.display()); + + let src_data = fs::read_to_string(&source_path).unwrap(); + + if vertex_destination.exists() { + fs::remove_file(vertex_destination).unwrap(); + } + fs::write( + vertex_destination, + compile_shader(&src_data, &source_path, "VertexMain", "vs_4_0").unwrap_or_else( + |error_message| { + eprintln!("{}", error_message); + panic!("Vertex shader failed to compile"); + }, + ), + ) + .unwrap(); + + if pixel_destination.exists() { + fs::remove_file(pixel_destination).unwrap(); + } + fs::write( + pixel_destination, + compile_shader(&src_data, &source_path, "PixelMain", "ps_4_0").unwrap_or_else( + |error_message| { + eprintln!("{}", error_message); + panic!("Pixel shader failed to compile"); + }, + ), + ) + .unwrap(); + } + + fn compile_shader( + src_data: &str, + source_path: &Path, + entry_point: &str, + target: &str, + ) -> Result, String> { + use winapi::shared::minwindef::LPCVOID; + use winapi::um::d3dcommon::ID3DBlob; + use winapi::um::d3dcompiler; + + unsafe { + let mut code: *mut ID3DBlob = ptr::null_mut(); + let mut error_msgs: *mut ID3DBlob = ptr::null_mut(); + + let hr = d3dcompiler::D3DCompile( + src_data.as_bytes().as_ptr() as LPCVOID, + src_data.as_bytes().len(), + CString::new(source_path.to_string_lossy().to_string()) + .unwrap() + .as_ptr(), + ptr::null(), + ptr::null_mut(), + CString::new(entry_point).unwrap().as_ptr(), + CString::new(target).unwrap().as_ptr(), + 0, + 0, + &mut code, + &mut error_msgs, + ); + + if hr < 0 { + if !error_msgs.is_null() { + let error_msgs = error_msgs.as_ref().unwrap(); + + let error_msgs = str::from_utf8(slice::from_raw_parts( + error_msgs.GetBufferPointer() as *const u8, + error_msgs.GetBufferSize(), + )) + .expect("error messages from D3DCompile not valid UTF-8"); + + Err(error_msgs.to_string()) + } else { + Err(format!("hresult: {}", hr)) + } + } else { + let code = code + .as_ref() + .expect("null code blob returned from D3DCompile"); + + Ok(slice::from_raw_parts( + code.GetBufferPointer() as *const u8, + code.GetBufferSize(), + ) + .to_vec()) + } + } + } + +} diff --git a/imgui-gfx-renderer/src/data/pixel.fx b/imgui-gfx-renderer/src/data/pixel.fx new file mode 100644 index 0000000..697cba0 Binary files /dev/null and b/imgui-gfx-renderer/src/data/pixel.fx differ diff --git a/imgui-gfx-renderer/src/data/vertex.fx b/imgui-gfx-renderer/src/data/vertex.fx new file mode 100644 index 0000000..2fccc8a Binary files /dev/null and b/imgui-gfx-renderer/src/data/vertex.fx differ diff --git a/imgui-gfx-renderer/src/lib.rs b/imgui-gfx-renderer/src/lib.rs index bbfb794..2cd5b33 100644 --- a/imgui-gfx-renderer/src/lib.rs +++ b/imgui-gfx-renderer/src/lib.rs @@ -76,6 +76,21 @@ macro_rules! extended_defines { } } +#[cfg(feature = "directx")] +mod constants { + use gfx::gfx_constant_struct_meta; + use gfx::gfx_impl_struct_meta; + + gfx::gfx_constant_struct! { + Constants { + // `matrix` is a reserved keyword in HLSL + matrix: [[f32; 4]; 4] = "matrix_", + } + } +} + +// This version of `pipe` uses a single uniform for `matrix`, used in GLSL shaders +#[cfg(not(feature = "directx"))] extended_defines! { pipeline pipe { vertex_buffer: gfx::VertexBuffer = (), @@ -90,14 +105,38 @@ extended_defines! { } } +// This version of `pipe` uses a constant buffer containing `matrix`, used in the HLSL shader +#[cfg(feature = "directx")] +extended_defines! { + pipeline pipe { + vertex_buffer: gfx::VertexBuffer = (), + constants: gfx::ConstantBuffer = "Constants", + tex: gfx::TextureSampler<[f32; 4]> = "tex", + out: gfx::BlendTarget = ( + "Target0", + gfx::state::ColorMask::all(), + gfx::preset::blend::ALPHA, + ), + scissor: gfx::Scissor = (), + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Shaders { - GlSl400, // OpenGL 4.0+ - GlSl150, // OpenGL 3.2+ - GlSl130, // OpenGL 3.0+ - GlSl110, // OpenGL 2.0+ - GlSlEs300, // OpenGL ES 3.0+ - GlSlEs100, // OpenGL ES 2.0+ + /// OpenGL 4.0+ + GlSl400, + /// OpenGL 3.2+ + GlSl150, + /// OpenGL 3.0+ + GlSl130, + /// OpenGL 2.0+ + GlSl110, + /// OpenGL ES 3.0+ + GlSlEs300, + /// OpenGL ES 2.0+ + GlSlEs100, + /// HLSL Shader Model 4.0+ + HlslSm40, } impl Shaders { @@ -128,6 +167,10 @@ impl Shaders { include_bytes!("shader/glsles_100.vert"), include_bytes!("shader/glsles_100.frag"), ), + HlslSm40 => ( + include_bytes!("data/vertex.fx"), + include_bytes!("data/pixel.fx"), + ), } } } @@ -141,6 +184,8 @@ pub struct Renderer { bundle: Bundle>, index_buffer: Buffer, textures: Textures>, + #[cfg(feature = "directx")] + constants: Buffer, } impl Renderer { @@ -197,6 +242,8 @@ impl Renderer { }, index_buffer, textures, + #[cfg(feature = "directx")] + constants: factory.create_constant_buffer(1), }) } @@ -276,9 +323,19 @@ impl Renderer { .min(fb_height) .round() as u16, }; + + #[cfg(feature = "directx")] + { + let constants = constants::Constants { matrix: *matrix }; + encoder.update_constant_buffer(&self.constants, &constants); + } + let data = pipe::BorrowedData { vertex_buffer: &self.bundle.vertex_buffer, + #[cfg(not(feature = "directx"))] matrix, + #[cfg(feature = "directx")] + constants: &self.constants, tex, out: &self.bundle.out, scissor: &scissor, diff --git a/imgui-gfx-renderer/src/shader/sm_40.hlsl b/imgui-gfx-renderer/src/shader/sm_40.hlsl new file mode 100644 index 0000000..bdfb4ca --- /dev/null +++ b/imgui-gfx-renderer/src/shader/sm_40.hlsl @@ -0,0 +1,34 @@ +cbuffer Constants : register(b0) { + float4x4 matrix_; +} + +Texture2D tex; +SamplerState tex_; + +struct VIn { + float2 position : pos; + float2 uv : uv; + float4 color : col; +}; + +struct VOut +{ + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + float4 color : COLOR; +}; + +VOut VertexMain(VIn vertex) +{ + VOut output; + output.position = mul(matrix_, float4(vertex.position, 0.0, 1.0)); + output.uv = vertex.uv; + output.color = vertex.color; + + return output; +} + +float4 PixelMain(VOut vout) : SV_TARGET +{ + return vout.color * tex.Sample(tex_, vout.uv); +} \ No newline at end of file