1#![warn(missing_docs)]
2
3mod texture;
25use texture::TexturePool;
26
27use std::mem;
28
29const fn zeroed<T>() -> T {
30 unsafe { mem::zeroed() }
31}
32
33use egui::{
34 ClippedPrimitive, Pos2,
35 epaint::{ClippedShape, Primitive, Vertex, textures::TexturesDelta},
36};
37
38use windows::Win32::Foundation::RECT;
39use windows::Win32::Graphics::{Direct3D::*, Direct3D11::*, Dxgi::Common::*};
40use windows::core::BOOL;
41use windows::core::{Interface, Result};
42
43pub struct Renderer {
46 device: ID3D11Device,
47 input_layout: ID3D11InputLayout,
48 vertex_shader: ID3D11VertexShader,
49 pixel_shader: ID3D11PixelShader,
50 rasterizer_state: ID3D11RasterizerState,
51 sampler_state: ID3D11SamplerState,
52 blend_state: ID3D11BlendState,
53
54 texture_pool: TexturePool,
55}
56
57#[allow(missing_docs)]
67pub struct RendererOutput {
68 pub textures_delta: TexturesDelta,
69 pub shapes: Vec<ClippedShape>,
70 pub pixels_per_point: f32,
71}
72
73pub fn split_output(
82 full_output: egui::FullOutput,
83) -> (
84 RendererOutput,
85 egui::PlatformOutput,
86 egui::OrderedViewportIdMap<egui::ViewportOutput>,
87) {
88 (
89 RendererOutput {
90 textures_delta: full_output.textures_delta,
91 shapes: full_output.shapes,
92 pixels_per_point: full_output.pixels_per_point,
93 },
94 full_output.platform_output,
95 full_output.viewport_output,
96 )
97}
98
99#[repr(C)]
100struct VertexData {
101 pos: Pos2,
102 uv: Pos2,
103 color: [f32; 4],
104}
105
106struct MeshData {
107 vtx: Vec<VertexData>,
108 idx: Vec<u32>,
109 tex: egui::TextureId,
110 clip_rect: egui::Rect,
111}
112
113impl Renderer {
114 pub fn new(device: &ID3D11Device) -> Result<Self> {
122 let mut input_layout = None;
123 let mut vertex_shader = None;
124 let mut pixel_shader = None;
125 let mut rasterizer_state = None;
126 let mut sampler_state = None;
127 let mut blend_state = None;
128 unsafe {
129 device.CreateInputLayout(
130 &Self::INPUT_ELEMENTS_DESC,
131 Self::VS_BLOB,
132 Some(&mut input_layout),
133 )?;
134 device.CreateVertexShader(
135 Self::VS_BLOB,
136 None,
137 Some(&mut vertex_shader),
138 )?;
139 device.CreatePixelShader(
140 Self::PS_BLOB,
141 None,
142 Some(&mut pixel_shader),
143 )?;
144 device.CreateRasterizerState(
145 &Self::RASTERIZER_DESC,
146 Some(&mut rasterizer_state),
147 )?;
148 device.CreateSamplerState(
149 &Self::SAMPLER_DESC,
150 Some(&mut sampler_state),
151 )?;
152 device
153 .CreateBlendState(&Self::BLEND_DESC, Some(&mut blend_state))?;
154 };
155 Ok(Self {
156 device: device.clone(),
157 input_layout: input_layout.unwrap(),
158 vertex_shader: vertex_shader.unwrap(),
159 pixel_shader: pixel_shader.unwrap(),
160 rasterizer_state: rasterizer_state.unwrap(),
161 sampler_state: sampler_state.unwrap(),
162 blend_state: blend_state.unwrap(),
163 texture_pool: TexturePool::new(device),
164 })
165 }
166
167 pub fn register_user_texture(
189 &mut self,
190 srv: ID3D11ShaderResourceView,
191 ) -> egui::TextureId {
192 self.texture_pool.register_user_texture(srv)
193 }
194
195 pub fn unregister_user_texture(&mut self, tid: egui::TextureId) -> bool {
201 self.texture_pool.unregister_user_texture(tid)
202 }
203
204 pub fn render(
246 &mut self,
247 device_context: &ID3D11DeviceContext,
248 render_target: &ID3D11RenderTargetView,
249 egui_ctx: &egui::Context,
250 egui_output: RendererOutput,
251 ) -> Result<()> {
252 self.texture_pool
253 .update(device_context, egui_output.textures_delta)?;
254
255 if egui_output.shapes.is_empty() {
256 return Ok(());
257 }
258
259 let frame_size = Self::get_render_target_size(render_target)?;
260 let frame_size_scaled = (
261 frame_size.0 as f32 / egui_output.pixels_per_point,
262 frame_size.1 as f32 / egui_output.pixels_per_point,
263 );
264 let zoom_factor = egui_ctx.zoom_factor();
265
266 self.setup(device_context, render_target, frame_size);
267 let meshes = egui_ctx
268 .tessellate(egui_output.shapes, egui_output.pixels_per_point)
269 .into_iter()
270 .filter_map(
271 |ClippedPrimitive {
272 primitive,
273 clip_rect,
274 }| match primitive {
275 Primitive::Mesh(mesh) => Some((mesh, clip_rect)),
276 Primitive::Callback(..) => {
277 log::warn!("paint callbacks are not yet supported.");
278 None
279 },
280 },
281 )
282 .filter_map(|(mesh, clip_rect)| {
283 if mesh.indices.is_empty() {
284 return None;
285 }
286 if mesh.indices.len() % 3 != 0 {
287 log::warn!(concat!(
288 "egui wants to draw a incomplete triangle. ",
289 "this request will be ignored."
290 ));
291 return None;
292 }
293 Some(MeshData {
294 vtx: mesh
295 .vertices
296 .into_iter()
297 .map(|Vertex { pos, uv, color }| VertexData {
298 pos: Pos2::new(
299 pos.x * zoom_factor / frame_size_scaled.0 * 2.0
300 - 1.0,
301 1.0 - pos.y * zoom_factor / frame_size_scaled.1
302 * 2.0,
303 ),
304 uv,
305 color: [
306 color[0] as f32 / 255.0,
307 color[1] as f32 / 255.0,
308 color[2] as f32 / 255.0,
309 color[3] as f32 / 255.0,
310 ],
311 })
312 .collect(),
313 idx: mesh.indices,
314 tex: mesh.texture_id,
315 clip_rect: clip_rect
316 * egui_output.pixels_per_point
317 * zoom_factor,
318 })
319 });
320 for mesh in meshes {
321 Self::draw_mesh(
322 &self.device,
323 device_context,
324 &self.texture_pool,
325 mesh,
326 )?;
327 }
328 Ok(())
329 }
330
331 fn setup(
332 &mut self,
333 ctx: &ID3D11DeviceContext,
334 render_target: &ID3D11RenderTargetView,
335 frame_size: (u32, u32),
336 ) {
337 unsafe {
338 ctx.IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
339 ctx.IASetInputLayout(&self.input_layout);
340 ctx.VSSetShader(&self.vertex_shader, None);
341 ctx.PSSetShader(&self.pixel_shader, None);
342 ctx.RSSetState(&self.rasterizer_state);
343 ctx.RSSetViewports(Some(&[D3D11_VIEWPORT {
344 TopLeftX: 0.,
345 TopLeftY: 0.,
346 Width: frame_size.0 as _,
347 Height: frame_size.1 as _,
348 MinDepth: 0.,
349 MaxDepth: 1.,
350 }]));
351 ctx.PSSetSamplers(0, Some(&[Some(self.sampler_state.clone())]));
352 ctx.OMSetRenderTargets(Some(&[Some(render_target.clone())]), None);
353 ctx.OMSetBlendState(&self.blend_state, Some(&[0.; 4]), u32::MAX);
354 }
355 }
356
357 fn draw_mesh(
358 device: &ID3D11Device,
359 device_context: &ID3D11DeviceContext,
360 texture_pool: &TexturePool,
361 mesh: MeshData,
362 ) -> Result<()> {
363 let vb = Self::create_index_buffer(device, &mesh.idx)?;
364 let ib = Self::create_vertex_buffer(device, &mesh.vtx)?;
365 unsafe {
366 device_context.IASetVertexBuffers(
367 0,
368 1,
369 Some(&Some(ib)),
370 Some(&(mem::size_of::<VertexData>() as _)),
371 Some(&0),
372 );
373 device_context.IASetIndexBuffer(&vb, DXGI_FORMAT_R32_UINT, 0);
374 device_context.RSSetScissorRects(Some(&[RECT {
375 left: mesh.clip_rect.left() as _,
376 top: mesh.clip_rect.top() as _,
377 right: mesh.clip_rect.right() as _,
378 bottom: mesh.clip_rect.bottom() as _,
379 }]));
380 }
381 if let Some(srv) = texture_pool.get_srv(mesh.tex) {
382 unsafe {
383 device_context.PSSetShaderResources(0, Some(&[Some(srv)]))
384 };
385 } else {
386 log::warn!(
387 concat!(
388 "egui wants to sample a non-existing texture {:?}.",
389 "this request will be ignored."
390 ),
391 mesh.tex
392 );
393 };
394 unsafe { device_context.DrawIndexed(mesh.idx.len() as _, 0, 0) };
395 Ok(())
396 }
397}
398
399impl Renderer {
400 const VS_BLOB: &'static [u8] = include_bytes!("../shaders/vs_egui.bin");
401 const PS_BLOB: &'static [u8] = include_bytes!("../shaders/ps_egui.bin");
402
403 const INPUT_ELEMENTS_DESC: [D3D11_INPUT_ELEMENT_DESC; 3] = [
404 D3D11_INPUT_ELEMENT_DESC {
405 SemanticName: windows::core::s!("POSITION"),
406 SemanticIndex: 0,
407 Format: DXGI_FORMAT_R32G32_FLOAT,
408 InputSlot: 0,
409 AlignedByteOffset: 0,
410 InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
411 InstanceDataStepRate: 0,
412 },
413 D3D11_INPUT_ELEMENT_DESC {
414 SemanticName: windows::core::s!("TEXCOORD"),
415 SemanticIndex: 0,
416 Format: DXGI_FORMAT_R32G32_FLOAT,
417 InputSlot: 0,
418 AlignedByteOffset: D3D11_APPEND_ALIGNED_ELEMENT,
419 InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
420 InstanceDataStepRate: 0,
421 },
422 D3D11_INPUT_ELEMENT_DESC {
423 SemanticName: windows::core::s!("COLOR"),
424 SemanticIndex: 0,
425 Format: DXGI_FORMAT_R32G32B32A32_FLOAT,
426 InputSlot: 0,
427 AlignedByteOffset: D3D11_APPEND_ALIGNED_ELEMENT,
428 InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
429 InstanceDataStepRate: 0,
430 },
431 ];
432
433 const RASTERIZER_DESC: D3D11_RASTERIZER_DESC = D3D11_RASTERIZER_DESC {
434 FillMode: D3D11_FILL_SOLID,
435 CullMode: D3D11_CULL_NONE,
436 FrontCounterClockwise: BOOL(0),
437 DepthBias: 0,
438 DepthBiasClamp: 0.,
439 SlopeScaledDepthBias: 0.,
440 DepthClipEnable: BOOL(0),
441 ScissorEnable: BOOL(1),
442 MultisampleEnable: BOOL(0),
443 AntialiasedLineEnable: BOOL(0),
444 };
445
446 const SAMPLER_DESC: D3D11_SAMPLER_DESC = D3D11_SAMPLER_DESC {
447 Filter: D3D11_FILTER_MIN_MAG_MIP_LINEAR,
448 AddressU: D3D11_TEXTURE_ADDRESS_BORDER,
449 AddressV: D3D11_TEXTURE_ADDRESS_BORDER,
450 AddressW: D3D11_TEXTURE_ADDRESS_BORDER,
451 ComparisonFunc: D3D11_COMPARISON_ALWAYS,
452 BorderColor: [1., 1., 1., 1.],
453 ..zeroed()
454 };
455
456 const BLEND_DESC: D3D11_BLEND_DESC = D3D11_BLEND_DESC {
457 RenderTarget: [
458 D3D11_RENDER_TARGET_BLEND_DESC {
459 BlendEnable: BOOL(1),
460 SrcBlend: D3D11_BLEND_ONE,
461 DestBlend: D3D11_BLEND_INV_SRC_ALPHA,
462 BlendOp: D3D11_BLEND_OP_ADD,
463 SrcBlendAlpha: D3D11_BLEND_INV_DEST_ALPHA,
464 DestBlendAlpha: D3D11_BLEND_ONE,
465 BlendOpAlpha: D3D11_BLEND_OP_ADD,
466 RenderTargetWriteMask: D3D11_COLOR_WRITE_ENABLE_ALL.0 as _,
467 },
468 zeroed(),
469 zeroed(),
470 zeroed(),
471 zeroed(),
472 zeroed(),
473 zeroed(),
474 zeroed(),
475 ],
476 ..zeroed()
477 };
478}
479
480impl Renderer {
481 fn create_vertex_buffer(
482 device: &ID3D11Device,
483 data: &[VertexData],
484 ) -> Result<ID3D11Buffer> {
485 let mut vertex_buffer = None;
486 unsafe {
487 device.CreateBuffer(
488 &D3D11_BUFFER_DESC {
489 ByteWidth: mem::size_of_val(data) as _,
490 Usage: D3D11_USAGE_IMMUTABLE,
491 BindFlags: D3D11_BIND_VERTEX_BUFFER.0 as _,
492 ..D3D11_BUFFER_DESC::default()
493 },
494 Some(&D3D11_SUBRESOURCE_DATA {
495 pSysMem: data.as_ptr() as _,
496 ..D3D11_SUBRESOURCE_DATA::default()
497 }),
498 Some(&mut vertex_buffer),
499 )
500 }?;
501 Ok(vertex_buffer.unwrap())
502 }
503
504 fn create_index_buffer(
505 device: &ID3D11Device,
506 data: &[u32],
507 ) -> Result<ID3D11Buffer> {
508 let mut index_buffer = None;
509 unsafe {
510 device.CreateBuffer(
511 &D3D11_BUFFER_DESC {
512 ByteWidth: mem::size_of_val(data) as _,
513 Usage: D3D11_USAGE_IMMUTABLE,
514 BindFlags: D3D11_BIND_INDEX_BUFFER.0 as _,
515 ..D3D11_BUFFER_DESC::default()
516 },
517 Some(&D3D11_SUBRESOURCE_DATA {
518 pSysMem: data.as_ptr() as _,
519 ..D3D11_SUBRESOURCE_DATA::default()
520 }),
521 Some(&mut index_buffer),
522 )
523 }?;
524 Ok(index_buffer.unwrap())
525 }
526
527 fn get_render_target_size(
528 rtv: &ID3D11RenderTargetView,
529 ) -> Result<(u32, u32)> {
530 let tex = unsafe { rtv.GetResource() }?.cast::<ID3D11Texture2D>()?;
531 let mut desc = zeroed();
532 unsafe { tex.GetDesc(&mut desc) };
533 Ok((desc.Width, desc.Height))
534 }
535}