1#[macro_use]
2pub extern crate gfx;
3pub extern crate imgui;
4
5use gfx::format::BlendFormat;
6use gfx::handle::{Buffer, RenderTargetView};
7use gfx::memory::Bind;
8use gfx::pso::PipelineState;
9use gfx::texture::{FilterMethod, SamplerInfo, WrapMode};
10use gfx::traits::FactoryExt;
11use gfx::{CommandBuffer, Encoder, Factory, IntoIndexBuffer, Rect, Resources, Slice};
12use imgui::internal::RawWrapper;
13use imgui::{BackendFlags, DrawCmd, DrawCmdParams, DrawData, DrawIdx, TextureId, Textures};
14use std::error::Error;
15use std::fmt;
16use std::usize;
17
18#[derive(Clone, Debug)]
19pub enum RendererError {
20 Update(gfx::UpdateError<usize>),
21 Buffer(gfx::buffer::CreationError),
22 Pipeline(gfx::PipelineStateError<String>),
23 Combined(gfx::CombinedError),
24 BadTexture(TextureId),
25}
26
27impl fmt::Display for RendererError {
28 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29 use self::RendererError::*;
30 match *self {
31 Update(ref e) => write!(f, "{}", e),
32 Buffer(ref e) => write!(f, "{}", e),
33 Pipeline(ref e) => write!(f, "{}", e),
34 Combined(ref e) => write!(f, "{}", e),
35 BadTexture(ref t) => write!(f, "Bad texture ID: {}", t.id()),
36 }
37 }
38}
39
40impl Error for RendererError {
41 fn source(&self) -> Option<&(dyn Error + 'static)> {
42 use self::RendererError::*;
43 match *self {
44 Update(ref e) => Some(e),
45 Buffer(ref e) => Some(e),
46 Pipeline(ref e) => Some(e),
47 Combined(ref e) => Some(e),
48 BadTexture(_) => None,
49 }
50 }
51}
52
53impl From<gfx::UpdateError<usize>> for RendererError {
54 fn from(e: gfx::UpdateError<usize>) -> RendererError {
55 RendererError::Update(e)
56 }
57}
58
59impl From<gfx::buffer::CreationError> for RendererError {
60 fn from(e: gfx::buffer::CreationError) -> RendererError {
61 RendererError::Buffer(e)
62 }
63}
64
65impl From<gfx::PipelineStateError<String>> for RendererError {
66 fn from(e: gfx::PipelineStateError<String>) -> RendererError {
67 RendererError::Pipeline(e)
68 }
69}
70
71impl From<gfx::CombinedError> for RendererError {
72 fn from(e: gfx::CombinedError) -> RendererError {
73 RendererError::Combined(e)
74 }
75}
76
77#[derive(Copy, Clone, Debug, PartialEq, Eq)]
78pub enum Shaders {
79 GlSl400,
81 GlSl150,
83 GlSl130,
85 GlSl110,
87 GlSlEs300,
89 GlSlEs100,
91 HlslSm40,
93}
94
95impl Shaders {
96 fn get_program_code(self) -> (&'static [u8], &'static [u8]) {
97 use self::Shaders::*;
98 match self {
99 GlSl400 => (
100 include_bytes!("shader/glsl_400.vert"),
101 include_bytes!("shader/glsl_400.frag"),
102 ),
103 GlSl150 => (
104 include_bytes!("shader/glsl_150.vert"),
105 include_bytes!("shader/glsl_150.frag"),
106 ),
107 GlSl130 => (
108 include_bytes!("shader/glsl_130.vert"),
109 include_bytes!("shader/glsl_130.frag"),
110 ),
111 GlSl110 => (
112 include_bytes!("shader/glsl_110.vert"),
113 include_bytes!("shader/glsl_110.frag"),
114 ),
115 GlSlEs300 => (
116 include_bytes!("shader/glsles_300.vert"),
117 include_bytes!("shader/glsles_300.frag"),
118 ),
119 GlSlEs100 => (
120 include_bytes!("shader/glsles_100.vert"),
121 include_bytes!("shader/glsles_100.frag"),
122 ),
123 HlslSm40 => (
124 include_bytes!("data/vertex.fx"),
125 include_bytes!("data/pixel.fx"),
126 ),
127 }
128 }
129}
130
131pub type Texture<R> = (
132 gfx::handle::ShaderResourceView<R, [f32; 4]>,
133 gfx::handle::Sampler<R>,
134);
135
136pub struct Renderer<Cf: BlendFormat, R: Resources> {
137 vertex_buffer: Buffer<R, GfxDrawVert>,
138 index_buffer: Buffer<R, DrawIdx>,
139 slice: Slice<R>,
140 pso: PipelineState<R, pipeline::Meta<Cf>>,
141 font_texture: Texture<R>,
142 textures: Textures<Texture<R>>,
143 #[cfg(feature = "directx")]
144 constants: Buffer<R, constants::Constants>,
145}
146
147impl<Cf, R> Renderer<Cf, R>
148where
149 Cf: BlendFormat,
150 R: Resources,
151{
152 pub fn init<F: Factory<R>>(
153 ctx: &mut imgui::Context,
154 factory: &mut F,
155 shaders: Shaders,
156 ) -> Result<Renderer<Cf, R>, RendererError> {
157 let (vs_code, ps_code) = shaders.get_program_code();
158 let pso = factory.create_pipeline_simple(vs_code, ps_code, pipeline::new::<Cf>())?;
159 let vertex_buffer = factory.create_buffer::<GfxDrawVert>(
160 256,
161 gfx::buffer::Role::Vertex,
162 gfx::memory::Usage::Dynamic,
163 Bind::empty(),
164 )?;
165 let index_buffer = factory.create_buffer::<DrawIdx>(
166 256,
167 gfx::buffer::Role::Index,
168 gfx::memory::Usage::Dynamic,
169 Bind::empty(),
170 )?;
171 let font_texture = upload_font_texture(ctx.fonts(), factory)?;
172 let slice = Slice {
173 start: 0,
174 end: 0,
175 base_vertex: 0,
176 instances: None,
177 buffer: index_buffer.clone().into_index_buffer(factory),
178 };
179 ctx.set_renderer_name(Some(format!(
180 "imgui-gfx-renderer {}",
181 env!("CARGO_PKG_VERSION")
182 )));
183 ctx.io_mut()
184 .backend_flags
185 .insert(BackendFlags::RENDERER_HAS_VTX_OFFSET);
186 Ok(Renderer {
187 vertex_buffer,
188 index_buffer,
189 slice,
190 pso,
191 font_texture,
192 textures: Textures::new(),
193 #[cfg(feature = "directx")]
194 constants: factory.create_constant_buffer(1),
195 })
196 }
197 pub fn reload_font_texture<F: Factory<R>>(
198 &mut self,
199 ctx: &mut imgui::Context,
200 factory: &mut F,
201 ) -> Result<(), RendererError> {
202 self.font_texture = upload_font_texture(ctx.fonts(), factory)?;
203 Ok(())
204 }
205 pub fn textures(&mut self) -> &mut Textures<Texture<R>> {
206 &mut self.textures
207 }
208 pub fn render<F: Factory<R>, C: CommandBuffer<R>>(
209 &mut self,
210 factory: &mut F,
211 encoder: &mut Encoder<R, C>,
212 target: &mut RenderTargetView<R, Cf>,
213 draw_data: &DrawData,
214 ) -> Result<(), RendererError> {
215 let fb_width = draw_data.display_size[0] * draw_data.framebuffer_scale[0];
216 let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1];
217 if !(fb_width > 0.0 && fb_height > 0.0) {
218 return Ok(());
219 }
220 let left = draw_data.display_pos[0];
221 let right = draw_data.display_pos[0] + draw_data.display_size[0];
222 let top = draw_data.display_pos[1];
223 let bottom = draw_data.display_pos[1] + draw_data.display_size[1];
224 let matrix = [
225 [(2.0 / (right - left)), 0.0, 0.0, 0.0],
226 [0.0, (2.0 / (top - bottom)), 0.0, 0.0],
227 [0.0, 0.0, -1.0, 0.0],
228 [
229 (right + left) / (left - right),
230 (top + bottom) / (bottom - top),
231 0.0,
232 1.0,
233 ],
234 ];
235 let clip_off = draw_data.display_pos;
236 let clip_scale = draw_data.framebuffer_scale;
237 for draw_list in draw_data.draw_lists() {
238 self.upload_vertex_buffer(factory, encoder, unsafe {
239 draw_list.transmute_vtx_buffer::<GfxDrawVert>()
240 })?;
241 self.upload_index_buffer(factory, encoder, draw_list.idx_buffer())?;
242 self.slice.start = 0;
243 for cmd in draw_list.commands() {
244 match cmd {
245 DrawCmd::Elements {
246 count,
247 cmd_params:
248 DrawCmdParams {
249 clip_rect,
250 texture_id,
251 vtx_offset,
252 idx_offset,
253 ..
254 },
255 } => {
256 let clip_rect = [
257 (clip_rect[0] - clip_off[0]) * clip_scale[0],
258 (clip_rect[1] - clip_off[1]) * clip_scale[1],
259 (clip_rect[2] - clip_off[0]) * clip_scale[0],
260 (clip_rect[3] - clip_off[1]) * clip_scale[1],
261 ];
262
263 self.slice.start = idx_offset as u32;
264 self.slice.end = self.slice.start + count as u32;
265 self.slice.base_vertex = vtx_offset as u32;
266
267 if clip_rect[0] < fb_width
268 && clip_rect[1] < fb_height
269 && clip_rect[2] >= 0.0
270 && clip_rect[3] >= 0.0
271 {
272 let scissor = Rect {
273 x: f32::max(0.0, clip_rect[0]).floor() as u16,
274 y: f32::max(0.0, clip_rect[1]).floor() as u16,
275 w: (clip_rect[2] - clip_rect[0]).abs().ceil() as u16,
276 h: (clip_rect[3] - clip_rect[1]).abs().ceil() as u16,
277 };
278 let tex = self.lookup_texture(texture_id)?;
279 #[cfg(feature = "directx")]
280 {
281 let constants = constants::Constants { matrix };
282 encoder.update_constant_buffer(&self.constants, &constants);
283 }
284 let data = pipeline::Data {
285 vertex_buffer: &self.vertex_buffer,
286 #[cfg(not(feature = "directx"))]
287 matrix: &matrix,
288 #[cfg(feature = "directx")]
289 constants: &self.constants,
290 tex,
291 scissor: &scissor,
292 target,
293 };
294 encoder.draw(&self.slice, &self.pso, &data);
295 }
296 }
297 DrawCmd::ResetRenderState => (), DrawCmd::RawCallback { callback, raw_cmd } => unsafe {
299 callback(draw_list.raw(), raw_cmd)
300 },
301 }
302 }
303 }
304 Ok(())
305 }
306 fn upload_vertex_buffer<F: Factory<R>, C: CommandBuffer<R>>(
307 &mut self,
308 factory: &mut F,
309 encoder: &mut Encoder<R, C>,
310 vtx_buffer: &[GfxDrawVert],
311 ) -> Result<(), RendererError> {
312 if self.vertex_buffer.len() < vtx_buffer.len() {
313 self.vertex_buffer = factory.create_buffer::<GfxDrawVert>(
314 vtx_buffer.len(),
315 gfx::buffer::Role::Vertex,
316 gfx::memory::Usage::Dynamic,
317 Bind::empty(),
318 )?;
319 }
320 encoder.update_buffer(&self.vertex_buffer, vtx_buffer, 0)?;
321 Ok(())
322 }
323 fn upload_index_buffer<F: Factory<R>, C: CommandBuffer<R>>(
324 &mut self,
325 factory: &mut F,
326 encoder: &mut Encoder<R, C>,
327 idx_buffer: &[DrawIdx],
328 ) -> Result<(), RendererError> {
329 if self.index_buffer.len() < idx_buffer.len() {
330 self.index_buffer = factory.create_buffer::<DrawIdx>(
331 idx_buffer.len(),
332 gfx::buffer::Role::Index,
333 gfx::memory::Usage::Dynamic,
334 Bind::empty(),
335 )?;
336 self.slice.buffer = self.index_buffer.clone().into_index_buffer(factory);
337 }
338 encoder.update_buffer(&self.index_buffer, idx_buffer, 0)?;
339 Ok(())
340 }
341 fn lookup_texture(&self, texture_id: TextureId) -> Result<&Texture<R>, RendererError> {
342 if texture_id.id() == usize::MAX {
343 Ok(&self.font_texture)
344 } else if let Some(texture) = self.textures.get(texture_id) {
345 Ok(texture)
346 } else {
347 Err(RendererError::BadTexture(texture_id))
348 }
349 }
350}
351
352fn upload_font_texture<R: Resources, F: Factory<R>>(
353 mut fonts: imgui::FontAtlasRefMut,
354 factory: &mut F,
355) -> Result<Texture<R>, RendererError> {
356 let texture = fonts.build_rgba32_texture();
357 let (_, texture_view) = factory.create_texture_immutable_u8::<gfx::format::Srgba8>(
358 gfx::texture::Kind::D2(
359 texture.width as u16,
360 texture.height as u16,
361 gfx::texture::AaMode::Single,
362 ),
363 gfx::texture::Mipmap::Provided,
364 &[texture.data],
365 )?;
366 fonts.tex_id = TextureId::from(usize::MAX);
367 let sampler = factory.create_sampler(SamplerInfo::new(FilterMethod::Bilinear, WrapMode::Tile));
368 let font_texture = (texture_view, sampler);
369 Ok(font_texture)
370}
371
372#[cfg(feature = "directx")]
373mod constants {
374 use gfx::gfx_constant_struct_meta;
375 use gfx::gfx_impl_struct_meta;
376
377 gfx::gfx_constant_struct! {
378 Constants {
379 matrix: [[f32; 4]; 4] = "matrix_",
381 }
382 }
383}
384
385mod pipeline {
391 use super::*;
392 use gfx::format::BlendFormat;
393 use gfx::handle::Manager;
394 use gfx::preset::blend;
395 use gfx::pso::{
396 AccessInfo, DataBind, DataLink, Descriptor, InitError, PipelineData, PipelineInit,
397 RawDataSet,
398 };
399 use gfx::state::ColorMask;
400 use gfx::{ProgramInfo, Resources};
401
402 #[derive(Clone, Debug, PartialEq)]
403 pub struct Data<'a, R: Resources, Cf: BlendFormat + 'a> {
404 pub vertex_buffer: &'a <gfx::VertexBuffer<GfxDrawVert> as DataBind<R>>::Data,
405 #[cfg(not(feature = "directx"))]
406 pub matrix: &'a <gfx::Global<[[f32; 4]; 4]> as DataBind<R>>::Data,
407 #[cfg(feature = "directx")]
408 pub constants: &'a <gfx::ConstantBuffer<super::constants::Constants> as DataBind<R>>::Data,
409 pub tex: &'a <gfx::TextureSampler<[f32; 4]> as DataBind<R>>::Data,
410 pub target: &'a <gfx::BlendTarget<Cf> as DataBind<R>>::Data,
411 pub scissor: &'a <gfx::Scissor as DataBind<R>>::Data,
412 }
413
414 #[derive(Clone, Debug, Hash, PartialEq)]
415 pub struct Meta<Cf: BlendFormat> {
416 vertex_buffer: gfx::VertexBuffer<GfxDrawVert>,
417 #[cfg(not(feature = "directx"))]
418 matrix: gfx::Global<[[f32; 4]; 4]>,
419 #[cfg(feature = "directx")]
420 constants: gfx::ConstantBuffer<super::constants::Constants>,
421 tex: gfx::TextureSampler<[f32; 4]>,
422 target: gfx::BlendTarget<Cf>,
423 scissor: gfx::Scissor,
424 }
425
426 #[derive(Clone, Debug, PartialEq)]
427 pub struct Init<'a, Cf: BlendFormat> {
428 vertex_buffer: <gfx::VertexBuffer<GfxDrawVert> as DataLink<'a>>::Init,
429 #[cfg(not(feature = "directx"))]
430 matrix: <gfx::Global<[[f32; 4]; 4]> as DataLink<'a>>::Init,
431 #[cfg(feature = "directx")]
432 constants: <gfx::ConstantBuffer<super::constants::Constants> as DataLink<'a>>::Init,
433 tex: <gfx::TextureSampler<[f32; 4]> as DataLink<'a>>::Init,
434 target: <gfx::BlendTarget<Cf> as DataLink<'a>>::Init,
435 scissor: <gfx::Scissor as DataLink<'a>>::Init,
436 }
437
438 impl<'a, Cf: BlendFormat> PipelineInit for Init<'a, Cf> {
439 type Meta = Meta<Cf>;
440 fn link_to<'s>(
441 &self,
442 desc: &mut Descriptor,
443 info: &'s ProgramInfo,
444 ) -> Result<Meta<Cf>, InitError<&'s str>> {
445 let mut meta = Meta {
446 vertex_buffer: DataLink::new(),
447 #[cfg(not(feature = "directx"))]
448 matrix: DataLink::new(),
449 #[cfg(feature = "directx")]
450 constants: DataLink::new(),
451 tex: DataLink::new(),
452 target: DataLink::new(),
453 scissor: DataLink::new(),
454 };
455 if let Some(d) = meta
456 .vertex_buffer
457 .link_vertex_buffer(0, &self.vertex_buffer)
458 {
459 assert!(meta.vertex_buffer.is_active());
460 desc.vertex_buffers[0] = Some(d);
461 }
462 for at in &info.vertex_attributes {
463 match meta.vertex_buffer.link_input(at, &self.vertex_buffer) {
464 Some(Ok(d)) => {
465 assert!(meta.vertex_buffer.is_active());
466 desc.attributes[at.slot as usize] = Some(d);
467 continue;
468 }
469 Some(Err(fm)) => return Err(InitError::VertexImport(&at.name, Some(fm))),
470 None => return Err(InitError::VertexImport(&at.name, None)),
471 }
472 }
473 #[cfg(feature = "directx")]
474 for cb in &info.constant_buffers {
475 match meta.constants.link_constant_buffer(cb, &self.constants) {
476 Some(Ok(d)) => {
477 assert!(meta.constants.is_active());
478 desc.constant_buffers[cb.slot as usize] = Some(d);
479 }
480 Some(Err(e)) => return Err(InitError::ConstantBuffer(&cb.name, Some(e))),
481 None => return Err(InitError::ConstantBuffer(&cb.name, None)),
482 }
483 }
484 #[cfg(not(feature = "directx"))]
485 for gc in &info.globals {
486 match meta.matrix.link_global_constant(gc, &self.matrix) {
487 Some(Ok(())) => assert!(meta.matrix.is_active()),
488 Some(Err(e)) => return Err(InitError::GlobalConstant(&gc.name, Some(e))),
489 None => return Err(InitError::GlobalConstant(&gc.name, None)),
490 }
491 }
492 for srv in &info.textures {
493 match meta.tex.link_resource_view(srv, &self.tex) {
494 Some(Ok(d)) => {
495 assert!(meta.tex.is_active());
496 desc.resource_views[srv.slot as usize] = Some(d);
497 }
498 Some(Err(_)) => return Err(InitError::ResourceView(&srv.name, Some(()))),
499 None => return Err(InitError::ResourceView(&srv.name, None)),
500 }
501 }
502 for sm in &info.samplers {
503 match meta.tex.link_sampler(sm, &self.tex) {
504 Some(d) => {
505 assert!(meta.tex.is_active());
506 desc.samplers[sm.slot as usize] = Some(d);
507 }
508 None => return Err(InitError::Sampler(&sm.name, None)),
509 }
510 }
511 for out in &info.outputs {
512 match meta.target.link_output(out, &self.target) {
513 Some(Ok(d)) => {
514 assert!(meta.target.is_active());
515 desc.color_targets[out.slot as usize] = Some(d);
516 }
517 Some(Err(fm)) => return Err(InitError::PixelExport(&out.name, Some(fm))),
518 None => return Err(InitError::PixelExport(&out.name, None)),
519 }
520 }
521 if !info.knows_outputs {
522 use gfx::shade::core::*;
523 let mut out = OutputVar {
524 name: String::new(),
525 slot: 0,
526 base_type: BaseType::F32,
527 container: ContainerType::Vector(4),
528 };
529 match meta.target.link_output(&out, &self.target) {
530 Some(Ok(d)) => {
531 assert!(meta.target.is_active());
532 desc.color_targets[out.slot as usize] = Some(d);
533 out.slot += 1;
534 }
535 Some(Err(fm)) => return Err(InitError::PixelExport("!known", Some(fm))),
536 None => (),
537 }
538 }
539 if meta.scissor.link_scissor() {
540 assert!(meta.scissor.is_active());
541 desc.scissor = true;
542 }
543 Ok(meta)
544 }
545 }
546
547 impl<'a, R: Resources, Cf: BlendFormat> PipelineData<R> for Data<'a, R, Cf> {
548 type Meta = Meta<Cf>;
549 fn bake_to(
550 &self,
551 out: &mut RawDataSet<R>,
552 meta: &Meta<Cf>,
553 man: &mut Manager<R>,
554 access: &mut AccessInfo<R>,
555 ) {
556 meta.vertex_buffer
557 .bind_to(out, self.vertex_buffer, man, access);
558 #[cfg(not(feature = "directx"))]
559 {
560 meta.matrix.bind_to(out, self.matrix, man, access);
561 }
562 #[cfg(feature = "directx")]
563 {
564 meta.constants.bind_to(out, self.constants, man, access);
565 }
566 meta.tex.bind_to(out, self.tex, man, access);
567 meta.target.bind_to(out, self.target, man, access);
568 meta.scissor.bind_to(out, self.scissor, man, access);
569 }
570 }
571
572 pub fn new<Cf: BlendFormat>() -> Init<'static, Cf> {
573 Init {
574 vertex_buffer: (),
575 #[cfg(not(feature = "directx"))]
576 matrix: "matrix",
577 #[cfg(feature = "directx")]
578 constants: "Constants",
579 tex: "tex",
580 target: ("Target0", ColorMask::all(), blend::ALPHA),
581 scissor: (),
582 }
583 }
584}
585
586gfx_vertex_struct! {
587 GfxDrawVert {
588 pos: [f32; 2] = "pos",
589 uv: [f32; 2] = "uv",
590 col: [gfx::format::U8Norm; 4] = "col",
591 }
592}