const VERTEX_SRC: &str = r#"
#version 450 core
layout (location = 0) in vec2 v_pos;
layout (location = 1) in vec2 v_uv;
layout (location = 2) in vec4 v_color;
layout (location = 0) out vec2 a_uv;
layout (location = 1) out vec4 a_color;
layout (location = 0) uniform mat4 u_transform;
void main() {
a_uv = v_uv;
a_color = v_color;
gl_Position = u_transform * vec4(v_pos, 0.0, 1.0);
}
"#;
const FRAGMENT_SRC: &str = r#"
#version 450 core
layout (location = 0) in vec2 a_uv;
layout (location = 1) in vec4 a_color;
layout (location = 0) out vec4 f_color;
layout (binding = 0) uniform sampler2D u_texture;
void main() {
f_color = a_color * texture(u_texture, a_uv);
}
"#;
pub struct Renderer<'grr> {
device: &'grr grr::Device,
pipeline: grr::Pipeline,
textures: imgui::Textures<(grr::Image, grr::ImageView, grr::Sampler)>,
vertex_array: grr::VertexArray,
}
impl<'grr> Renderer<'grr> {
pub unsafe fn new(
imgui: &mut imgui::Context,
grr: &'grr grr::Device,
) -> Result<Self, grr::Error> {
{
fn imgui_gamma_to_linear(col: [f32; 4]) -> [f32; 4] {
let x = col[0].powf(2.2);
let y = col[1].powf(2.2);
let z = col[2].powf(2.2);
let w = 1.0 - (1.0 - col[3]).powf(2.2);
[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]);
}
}
let vs = grr.create_shader(
grr::ShaderStage::Vertex,
VERTEX_SRC.as_bytes(),
grr::ShaderFlags::empty(),
)?;
let fs = grr.create_shader(
grr::ShaderStage::Fragment,
FRAGMENT_SRC.as_bytes(),
grr::ShaderFlags::empty(),
)?;
let pipeline = grr.create_graphics_pipeline(
grr::VertexPipelineDesc {
vertex_shader: vs,
tessellation_control_shader: None,
tessellation_evaluation_shader: None,
geometry_shader: None,
fragment_shader: Some(fs),
},
grr::PipelineFlags::empty(),
)?;
let mut textures = imgui::Textures::new();
let mut fonts = imgui.fonts();
let image = {
let texture = fonts.build_rgba32_texture();
let image = grr
.create_image(
grr::ImageType::D2 {
width: texture.width,
height: texture.height,
layers: 1,
samples: 1,
},
grr::Format::R8G8B8A8_SRGB,
1,
)
.unwrap();
grr.object_name(image, "imgui-texture");
grr.copy_host_to_image(
&texture.data,
image,
grr::HostImageCopy {
host_layout: grr::MemoryLayout {
base_format: grr::BaseFormat::RGBA,
format_layout: grr::FormatLayout::U8,
row_length: texture.width,
image_height: texture.height,
alignment: 4,
},
image_subresource: grr::SubresourceLayers {
level: 0,
layers: 0..1,
},
image_offset: grr::Offset { x: 0, y: 0, z: 0 },
image_extent: grr::Extent {
width: texture.width,
height: texture.height,
depth: 1,
},
},
);
image
};
let image_view = grr.create_image_view(
image,
grr::ImageViewType::D2,
grr::Format::R8G8B8A8_SRGB,
grr::SubresourceRange {
layers: 0..1,
levels: 0..1,
},
)?;
let sampler = grr.create_sampler(grr::SamplerDesc {
min_filter: grr::Filter::Linear,
mag_filter: grr::Filter::Linear,
mip_map: None,
address: (
grr::SamplerAddress::ClampEdge,
grr::SamplerAddress::ClampEdge,
grr::SamplerAddress::ClampEdge,
),
lod_bias: 0.0,
lod: 0.0..10.0,
compare: None,
border_color: [0.0, 0.0, 0.0, 1.0],
})?;
fonts.tex_id = textures.insert((image, image_view, sampler));
let vertex_array = grr.create_vertex_array(&[
grr::VertexAttributeDesc {
location: 0,
binding: 0,
format: grr::VertexFormat::Xy32Float,
offset: 0,
},
grr::VertexAttributeDesc {
location: 1,
binding: 0,
format: grr::VertexFormat::Xy32Float,
offset: (2 * std::mem::size_of::<f32>()) as _,
},
grr::VertexAttributeDesc {
location: 2,
binding: 0,
format: grr::VertexFormat::Xyzw8Unorm,
offset: (4 * std::mem::size_of::<f32>()) as _,
},
])?;
Ok(Renderer {
device: grr,
pipeline,
textures,
vertex_array,
})
}
pub unsafe fn render(&self, draw_data: &imgui::DrawData) -> Result<(), grr::Error> {
let fb_width = draw_data.display_size[0] * draw_data.framebuffer_scale[0];
let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1];
if fb_width <= 0.0 || fb_height <= 0.0 {
return Ok(());
}
let left = draw_data.display_pos[0];
let right = draw_data.display_pos[0] + draw_data.display_size[0];
let top = draw_data.display_pos[1];
let bottom = draw_data.display_pos[1] + draw_data.display_size[1];
let transform = [
[2.0 / draw_data.display_size[0] as f32, 0.0, 0.0, 0.0],
[0.0, -2.0 / draw_data.display_size[1] as f32, 0.0, 0.0],
[0.0, 0.0, -1.0, 0.0],
[
-(right + left) / draw_data.display_size[0],
(top + bottom) / draw_data.display_size[1],
0.0,
1.0,
],
];
let clip_off = draw_data.display_pos;
let clip_scale = draw_data.framebuffer_scale;
for draw_list in draw_data.draw_lists() {
self.render_draw_list(
&draw_list,
(fb_width, fb_height),
&transform,
clip_off,
clip_scale,
)?;
}
Ok(())
}
unsafe fn render_draw_list(
&self,
draw_list: &imgui::DrawList,
fb_size: (f32, f32),
matrix: &[[f32; 4]; 4],
clip_off: [f32; 2],
clip_scale: [f32; 2],
) -> Result<(), grr::Error> {
let vertex_buffer = self.device.create_buffer_from_host(
grr::as_u8_slice(&draw_list.vtx_buffer()),
grr::MemoryFlags::empty(),
)?;
let index_buffer = self.device.create_buffer_from_host(
grr::as_u8_slice(&draw_list.idx_buffer()),
grr::MemoryFlags::empty(),
)?;
self.device.bind_pipeline(self.pipeline);
self.device.bind_vertex_array(self.vertex_array);
self.device
.bind_index_buffer(self.vertex_array, index_buffer);
self.device.bind_vertex_buffers(
self.vertex_array,
0,
&[grr::VertexBufferView {
buffer: vertex_buffer,
offset: 0,
stride: std::mem::size_of::<imgui::DrawVert>() as _,
input_rate: grr::InputRate::Vertex,
}],
);
let color_blend = grr::ColorBlend {
attachments: vec![grr::ColorBlendAttachment {
blend_enable: true,
color: grr::BlendChannel {
src_factor: grr::BlendFactor::SrcAlpha,
dst_factor: grr::BlendFactor::OneMinusSrcAlpha,
blend_op: grr::BlendOp::Add,
},
alpha: grr::BlendChannel {
src_factor: grr::BlendFactor::One,
dst_factor: grr::BlendFactor::One,
blend_op: grr::BlendOp::Add,
},
}],
};
self.device.bind_color_blend_state(&color_blend);
self.device
.bind_uniform_constants(self.pipeline, 0, &[grr::Constant::Mat4x4(*matrix)]);
self.device.set_viewport(
0,
&[grr::Viewport {
x: 0.0,
y: 0.0,
w: fb_size.0,
h: fb_size.1,
n: 0.0,
f: 1.0,
}],
);
let mut index_start = 0;
for cmd in draw_list.commands() {
match cmd {
imgui::DrawCmd::Elements {
count,
cmd_params:
imgui::DrawCmdParams {
clip_rect,
texture_id,
..
},
} => {
let clip_rect = [
(clip_rect[0] - clip_off[0]) * clip_scale[0],
(clip_rect[1] - clip_off[1]) * clip_scale[1],
(clip_rect[2] - clip_off[0]) * clip_scale[0],
(clip_rect[3] - clip_off[1]) * clip_scale[1],
];
if clip_rect[0] < fb_size.0
&& clip_rect[1] < fb_size.1
&& clip_rect[2] >= 0.0
&& clip_rect[3] >= 0.0
{
let (_, image_view, sampler) = self.textures.get(texture_id).unwrap(); self.device.bind_image_views(0, &[*image_view]);
self.device.bind_samplers(0, &[*sampler]);
self.device.set_scissor(
0,
&[grr::Region {
x: clip_rect[0] as _,
y: (fb_size.1 - clip_rect[3]) as _,
w: (clip_rect[2] - clip_rect[0]).abs().ceil() as _,
h: (clip_rect[3] - clip_rect[1]).abs().ceil() as _,
}],
);
self.device.draw_indexed(
grr::Primitive::Triangles,
grr::IndexTy::U16,
index_start..index_start + count as u32,
0..1,
0,
);
}
index_start += count as u32;
}
_ => unimplemented!(),
}
}
self.device.delete_buffer(vertex_buffer);
self.device.delete_buffer(index_buffer);
Ok(())
}
}