1//! Submission through Direct3D 11 from feature level 10_0, or WARP where no device has it:
2//! the Direct3D Windows 7 has. Frames draw into an sRGB texture, copied to a DXGI 1.1 swap
3//! chain that blits into the window's redirection surface, as Windows 7 has no flip model,
4//! or where the window has none, to a flip-model one that DirectComposition shows.
5use super::*;
6use std::{ffi::c_void, ptr};
7use windows::{
8 Win32::{
9 Foundation::{HMODULE, HWND, RECT},
10 Graphics::{
11 Direct3D::*,
12 Direct3D11::*,
13 DirectComposition::{IDCompositionDevice, IDCompositionTarget, IDCompositionVisual},
14 Dxgi::{Common::*, *},
15 },
16 System::LibraryLoader::{GetProcAddress, LoadLibraryW},
17 UI::WindowsAndMessaging::{
18 GWL_EXSTYLE, GetClientRect, GetWindowLongPtrW, WS_EX_NOREDIRECTIONBITMAP,
19 },
20 },
21 core::{Error, GUID, HRESULT, Interface, PCSTR, Result, s, w},
22};
23
24// Shader model 4.0 bytecode from `dxbc/compile.ps1`: the build host has no HLSL compiler, and
25// Windows 7 has a runtime one only with an optional update.
26const DRAW_VERTEX: &[u8] = include_bytes!("dxbc/draw.vertex.dxbc");
27const DRAW_FRAGMENT: &[u8] = include_bytes!("dxbc/draw.fragment.dxbc");
28const TRANSLUCENT_VERTEX: &[u8] = include_bytes!("dxbc/translucent.vertex.dxbc");
29const TRANSLUCENT_FRAGMENT: &[u8] = include_bytes!("dxbc/translucent.fragment.dxbc");
30
31const FORMAT: DXGI_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
32
33/// What a successful creating call leaves in its out-parameter.
34fn made<T>(create: impl FnOnce(&mut Option<T>) -> Result<()>) -> Result<T> {
35 let mut made = None;
36 create(&mut made)?;
37 Ok(made.expect("A successful call returns what it made"))
38}
39
40/// An sRGB RGBA texture, `pixels` its rows if given.
41fn texture(
42 device: &ID3D11Device,
43 size: [u32; 2],
44 bind: D3D11_BIND_FLAG,
45 pixels: Option<&[u8]>,
46) -> Result<ID3D11Texture2D> {
47 let description = D3D11_TEXTURE2D_DESC {
48 Width: size[0],
49 Height: size[1],
50 MipLevels: 1,
51 ArraySize: 1,
52 Format: FORMAT,
53 SampleDesc: DXGI_SAMPLE_DESC {
54 Count: 1,
55 Quality: 0,
56 },
57 Usage: D3D11_USAGE_DEFAULT,
58 BindFlags: bind.0 as u32,
59 ..Default::default()
60 };
61 let data = pixels.map(|pixels| D3D11_SUBRESOURCE_DATA {
62 pSysMem: pixels.as_ptr().cast(),
63 SysMemPitch: size[0] * 4,
64 SysMemSlicePitch: 0,
65 });
66 made(|out| unsafe {
67 device.CreateTexture2D(&description, data.as_ref().map(ptr::from_ref), Some(out))
68 })
69}
70
71fn description(texture: &ID3D11Texture2D) -> D3D11_TEXTURE2D_DESC {
72 let mut description = D3D11_TEXTURE2D_DESC::default();
73 unsafe { texture.GetDesc(&mut description) };
74 description
75}
76
77fn resource(device: &ID3D11Device, texture: &ID3D11Texture2D) -> Result<ID3D11ShaderResourceView> {
78 made(|out| unsafe { device.CreateShaderResourceView(texture, None, Some(out)) })
79}
80
81/// A texture as the fragment shader samples it, or none where Direct3D made none, as when
82/// the device is lost.
83pub(super) struct Image(Option<ID3D11ShaderResourceView>);
84
85/// An offscreen frame, which `Renderer::present` shows in the window.
86pub struct Target {
87 texture: ID3D11Texture2D,
88 view: ID3D11RenderTargetView,
89 resource: ID3D11ShaderResourceView,
90}
91
92impl Target {
93 fn new(device: &ID3D11Device, size: [u32; 2]) -> Result<Self> {
94 let bind = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
95 let texture = texture(device, size, bind, None)?;
96 let view = made(|out| unsafe { device.CreateRenderTargetView(&texture, None, Some(out)) })?;
97 let resource = resource(device, &texture)?;
98 Ok(Self {
99 texture,
100 view,
101 resource,
102 })
103 }
104
105 pub fn size(&self) -> [u32; 2] {
106 let description = description(&self.texture);
107 [description.Width, description.Height]
108 }
109}
110
111pub(super) struct Gpu {
112 device: ID3D11Device,
113 context: ID3D11DeviceContext,
114 swap_chain: IDXGISwapChain,
115 /// What shows the swap chain in a window without a redirection surface.
116 _composition: Option<Composition>,
117 layout: ID3D11InputLayout,
118 vertex: ID3D11VertexShader,
119 fragment: ID3D11PixelShader,
120 translucent: (ID3D11VertexShader, ID3D11PixelShader),
121 /// One for each of `Blend::STATES`.
122 blends: [ID3D11BlendState; 4],
123 rasterizer: ID3D11RasterizerState,
124 nearest: ID3D11SamplerState,
125 linear: ID3D11SamplerState,
126 buffer: ID3D11Buffer,
127 atlas: (ID3D11Texture2D, ID3D11ShaderResourceView),
128 /// Offscreen pictures for groups, the target's size.
129 groups: Vec<Target>,
130}
131
132impl Gpu {
133 /// A device and a swap chain on `window`, an `HWND`, and the adapter's description.
134 pub(super) fn new(window: *mut c_void) -> Result<(Self, String)> {
135 let levels = [
136 D3D_FEATURE_LEVEL_11_0,
137 D3D_FEATURE_LEVEL_10_1,
138 D3D_FEATURE_LEVEL_10_0,
139 ];
140 let create = |driver| {
141 let (mut device, mut context) = (None, None);
142 unsafe {
143 D3D11CreateDevice(
144 None,
145 driver,
146 HMODULE::default(),
147 D3D11_CREATE_DEVICE_FLAG(0),
148 Some(&levels),
149 D3D11_SDK_VERSION,
150 Some(&mut device),
151 None,
152 Some(&mut context),
153 )
154 }
155 .map(|()| (device.expect("A device"), context.expect("A context")))
156 };
157 let (device, context) =
158 create(D3D_DRIVER_TYPE_HARDWARE).or_else(|_| create(D3D_DRIVER_TYPE_WARP))?;
159 let adapter = unsafe { device.cast::<IDXGIDevice>()?.GetAdapter()? };
160 let name = unsafe { adapter.GetDesc()? }.Description;
161 let name = String::from_utf16_lossy(name.split(|&c| c == 0).next().unwrap_or(&[]));
162 let level = unsafe { device.GetFeatureLevel() }.0;
163 let [major, minor] = [level >> 12, (level >> 8) & 0xf];
164 let description = format!("{name}, feature level {major}_{minor}");
165 let hwnd = HWND(window);
166 let factory: IDXGIFactory = unsafe { adapter.GetParent()? };
167 // Windows 10's window has no redirection surface, made for wgpu's DirectComposition.
168 let style = unsafe { GetWindowLongPtrW(hwnd, GWL_EXSTYLE) } as u32;
169 let (swap_chain, composition) = if style & WS_EX_NOREDIRECTIONBITMAP.0 != 0 {
170 let (swap_chain, composition) = compose(&device, &factory, hwnd)?;
171 (swap_chain, Some(composition))
172 } else {
173 let description = DXGI_SWAP_CHAIN_DESC {
174 // Windows 7's desktop composes a blit swap chain's alpha only from BGRA.
175 BufferDesc: DXGI_MODE_DESC {
176 Format: DXGI_FORMAT_B8G8R8A8_UNORM,
177 ..Default::default()
178 },
179 SampleDesc: DXGI_SAMPLE_DESC {
180 Count: 1,
181 Quality: 0,
182 },
183 BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
184 BufferCount: 1,
185 OutputWindow: hwnd,
186 Windowed: true.into(),
187 SwapEffect: DXGI_SWAP_EFFECT_DISCARD,
188 Flags: 0,
189 };
190 let swap_chain =
191 made(|out| unsafe { factory.CreateSwapChain(&device, &description, out).ok() })?;
192 (swap_chain, None)
193 };
194 // DXGI would otherwise watch the window's messages and take Alt+Enter to full screen.
195 unsafe {
196 factory
197 .MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER)?;
198 }
199
200 let elements: [_; 9] = std::array::from_fn(|index| {
201 let (_, floats, offset) = ATTRIBUTES[index];
202 D3D11_INPUT_ELEMENT_DESC {
203 SemanticName: PCSTR(c"TEXCOORD".as_ptr().cast()),
204 SemanticIndex: index as u32,
205 Format: [
206 DXGI_FORMAT_R32_FLOAT,
207 DXGI_FORMAT_R32G32_FLOAT,
208 DXGI_FORMAT_R32G32B32_FLOAT,
209 DXGI_FORMAT_R32G32B32A32_FLOAT,
210 ][floats - 1],
211 InputSlot: 0,
212 AlignedByteOffset: offset as u32,
213 InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
214 InstanceDataStepRate: 0,
215 }
216 });
217 let layout =
218 made(|out| unsafe { device.CreateInputLayout(&elements, DRAW_VERTEX, Some(out)) })?;
219 let vertex_shader =
220 |code| made(|out| unsafe { device.CreateVertexShader(code, None, Some(out)) });
221 let pixel_shader =
222 |code| made(|out| unsafe { device.CreatePixelShader(code, None, Some(out)) });
223
224 let blend = |factors: [Factor; 4]| {
225 let [src, dst, src_alpha, dst_alpha] = factors.map(|factor| match factor {
226 Factor::Zero => D3D11_BLEND_ZERO,
227 Factor::One => D3D11_BLEND_ONE,
228 Factor::SourceAlpha => D3D11_BLEND_SRC_ALPHA,
229 Factor::OneMinusSourceAlpha => D3D11_BLEND_INV_SRC_ALPHA,
230 Factor::Destination => D3D11_BLEND_DEST_COLOR,
231 });
232 let mut description = D3D11_BLEND_DESC::default();
233 description.RenderTarget[0] = D3D11_RENDER_TARGET_BLEND_DESC {
234 BlendEnable: true.into(),
235 SrcBlend: src,
236 DestBlend: dst,
237 BlendOp: D3D11_BLEND_OP_ADD,
238 SrcBlendAlpha: src_alpha,
239 DestBlendAlpha: dst_alpha,
240 BlendOpAlpha: D3D11_BLEND_OP_ADD,
241 RenderTargetWriteMask: D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8,
242 };
243 made(|out| unsafe { device.CreateBlendState(&description, Some(out)) })
244 };
245 let sampler = |filter| {
246 let description = D3D11_SAMPLER_DESC {
247 Filter: filter,
248 AddressU: D3D11_TEXTURE_ADDRESS_CLAMP,
249 AddressV: D3D11_TEXTURE_ADDRESS_CLAMP,
250 AddressW: D3D11_TEXTURE_ADDRESS_CLAMP,
251 ComparisonFunc: D3D11_COMPARISON_NEVER,
252 MaxLOD: f32::MAX,
253 ..Default::default()
254 };
255 made(|out| unsafe { device.CreateSamplerState(&description, Some(out)) })
256 };
257 let rasterizer = D3D11_RASTERIZER_DESC {
258 FillMode: D3D11_FILL_SOLID,
259 CullMode: D3D11_CULL_NONE,
260 DepthClipEnable: true.into(),
261 ScissorEnable: true.into(),
262 ..Default::default()
263 };
264 let buffer = D3D11_BUFFER_DESC {
265 ByteWidth: VERTEX_BUFFER_BYTES as u32,
266 Usage: D3D11_USAGE_DYNAMIC,
267 BindFlags: D3D11_BIND_VERTEX_BUFFER.0 as u32,
268 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
269 ..Default::default()
270 };
271 let [over, picture, erase, multiply] = Blend::STATES;
272 let gpu = Self {
273 layout,
274 vertex: vertex_shader(DRAW_VERTEX)?,
275 fragment: pixel_shader(DRAW_FRAGMENT)?,
276 translucent: (
277 vertex_shader(TRANSLUCENT_VERTEX)?,
278 pixel_shader(TRANSLUCENT_FRAGMENT)?,
279 ),
280 blends: [
281 blend(over)?,
282 blend(picture)?,
283 blend(erase)?,
284 blend(multiply)?,
285 ],
286 rasterizer: made(|out| unsafe {
287 device.CreateRasterizerState(&rasterizer, Some(out))
288 })?,
289 nearest: sampler(D3D11_FILTER_MIN_MAG_MIP_POINT)?,
290 linear: sampler(D3D11_FILTER_MIN_MAG_MIP_LINEAR)?,
291 buffer: made(|out| unsafe { device.CreateBuffer(&buffer, None, Some(out)) })?,
292 atlas: atlas(&device, ATLAS_SIZE)?,
293 groups: Vec::new(),
294 device,
295 context,
296 swap_chain,
297 _composition: composition,
298 };
299 Ok((gpu, description))
300 }
301
302 pub(super) fn target(&self, size: [u32; 2]) -> Result<Target> {
303 Target::new(&self.device, size)
304 }
305
306 /// Copies `target` to the window, premultiplying each pixel again in sRGB, as the
307 /// desktop compositing the window over its backdrop needs, which leaves opaque ones be.
308 pub(super) fn present(&self, target: &Target) -> Result<()> {
309 let context = &self.context;
310 let size = target.size();
311 let [width, height] = size;
312 unsafe {
313 let buffers = self.swap_chain.GetDesc()?.BufferDesc;
314 if [buffers.Width, buffers.Height] != size {
315 let flags = DXGI_SWAP_CHAIN_FLAG(0);
316 (self.swap_chain).ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, flags)?;
317 }
318 let buffer: ID3D11Texture2D = self.swap_chain.GetBuffer(0)?;
319 let view = made(|out| self.device.CreateRenderTargetView(&buffer, None, Some(out)))?;
320 let (vertex, fragment) = &self.translucent;
321 self.begin(&view, size);
322 context.OMSetBlendState(None, None, u32::MAX);
323 context.VSSetShader(vertex, None);
324 context.PSSetShader(fragment, None);
325 context.PSSetShaderResources(0, Some(&[Some(target.resource.clone())]));
326 context.Draw(3, 0);
327 context.PSSetShaderResources(0, Some(&[None]));
328 context.OMSetRenderTargets(None, None);
329 self.swap_chain.Present(1, DXGI_PRESENT(0)).ok()
330 }
331 }
332
333 /// sRGB-encoded RGBA rows of `target`, top first.
334 pub(super) fn read_pixels(&self, target: &Target) -> Result<Vec<u8>> {
335 let mut description = description(&target.texture);
336 let [width, height] = [description.Width, description.Height];
337 description.Usage = D3D11_USAGE_STAGING;
338 description.BindFlags = 0;
339 description.CPUAccessFlags = D3D11_CPU_ACCESS_READ.0 as u32;
340 let row = width as usize * 4;
341 let mut pixels = Vec::with_capacity(row * height as usize);
342 unsafe {
343 let staging = made(|out| self.device.CreateTexture2D(&description, None, Some(out)))?;
344 self.context.CopyResource(&staging, &target.texture);
345 let mut mapped = D3D11_MAPPED_SUBRESOURCE::default();
346 self.context
347 .Map(&staging, 0, D3D11_MAP_READ, 0, Some(&mut mapped))?;
348 for y in 0..height as usize {
349 let start = mapped.pData.cast::<u8>().add(y * mapped.RowPitch as usize);
350 pixels.extend_from_slice(std::slice::from_raw_parts(start, row));
351 }
352 self.context.Unmap(&staging, 0);
353 }
354 Ok(pixels)
355 }
356
357 /// Blends in linear light, its targets being sRGB.
358 pub(super) fn blends_linear(&self) -> bool {
359 true
360 }
361
362 /// Offscreen pictures' rows run top first.
363 pub(super) fn flipped(&self) -> bool {
364 false
365 }
366
367 pub(super) fn max_texture_dimension(&self) -> u32 {
368 if unsafe { self.device.GetFeatureLevel() }.0 >= D3D_FEATURE_LEVEL_11_0.0 {
369 D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION
370 } else {
371 // Feature level 10's.
372 8192
373 }
374 }
375
376 pub(super) fn atlas_side(&self) -> u32 {
377 description(&self.atlas.0).Width
378 }
379
380 pub(super) fn new_atlas(&mut self, side: u32) {
381 match atlas(&self.device, side) {
382 Ok(atlas) => self.atlas = atlas,
383 Err(error) => eprintln!("No glyph atlas {side} pixels square: {error}"),
384 }
385 }
386
387 pub(super) fn write_atlas(&self, origin: [u32; 2], size: [u32; 2], rgba: &[u8]) {
388 let area = D3D11_BOX {
389 left: origin[0],
390 top: origin[1],
391 front: 0,
392 right: origin[0] + size[0],
393 bottom: origin[1] + size[1],
394 back: 1,
395 };
396 unsafe {
397 self.context.UpdateSubresource(
398 &self.atlas.0,
399 0,
400 Some(&area),
401 rgba.as_ptr().cast(),
402 size[0] * 4,
403 0,
404 );
405 }
406 }
407
408 pub(super) fn upload_image(&self, image: &RasterImage) -> Image {
409 let texture = texture(
410 &self.device,
411 image.size,
412 D3D11_BIND_SHADER_RESOURCE,
413 Some(image.pixels()),
414 );
415 let view = texture.and_then(|texture| resource(&self.device, &texture));
416 Image(
417 view.inspect_err(|error| eprintln!("No image texture: {error}"))
418 .ok(),
419 )
420 }
421
422 /// Binds `view`, `size` pixels, as the render target, with the pipeline's state.
423 fn begin(&self, view: &ID3D11RenderTargetView, size: [u32; 2]) {
424 let context = &self.context;
425 unsafe {
426 context.PSSetShaderResources(0, Some(&[None]));
427 context.OMSetRenderTargets(Some(&[Some(view.clone())]), None);
428 context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
429 context.RSSetState(&self.rasterizer);
430 context.RSSetViewports(Some(&[D3D11_VIEWPORT {
431 TopLeftX: 0.0,
432 TopLeftY: 0.0,
433 Width: size[0] as f32,
434 Height: size[1] as f32,
435 MinDepth: 0.0,
436 MaxDepth: 1.0,
437 }]));
438 context.RSSetScissorRects(Some(&[RECT {
439 left: 0,
440 top: 0,
441 right: size[0] as i32,
442 bottom: size[1] as i32,
443 }]));
444 }
445 }
446
447 /// Clears `target`, `size` device pixels, to linear `clear` and draws the prepared
448 /// batches, each group's offscreen first.
449 pub(super) fn submit(
450 &mut self,
451 frame: &Frame<'_>,
452 target: &Target,
453 size: [u32; 2],
454 clear: [f32; 4],
455 ) {
456 if self
457 .groups
458 .first()
459 .is_some_and(|picture| picture.size() != size)
460 {
461 self.groups.clear();
462 }
463 while self.groups.len() < frame.groups.len() {
464 match Target::new(&self.device, size) {
465 Ok(picture) => self.groups.push(picture),
466 Err(error) => {
467 eprintln!("No offscreen picture: {error}");
468 return;
469 }
470 }
471 }
472 let gpu = &*self;
473 let context = &gpu.context;
474 let bytes: &[u8] = bytemuck::cast_slice(frame.vertices);
475 let bytes = &bytes[..bytes.len().min(VERTEX_BUFFER_BYTES as usize)];
476 unsafe {
477 let mut mapped = D3D11_MAPPED_SUBRESOURCE::default();
478 if let Err(error) = context.Map(
479 &gpu.buffer,
480 0,
481 D3D11_MAP_WRITE_DISCARD,
482 0,
483 Some(&mut mapped),
484 ) {
485 eprintln!("No vertex buffer: {error}");
486 return;
487 }
488 ptr::copy_nonoverlapping(bytes.as_ptr(), mapped.pData.cast(), bytes.len());
489 context.Unmap(&gpu.buffer, 0);
490 context.IASetInputLayout(&gpu.layout);
491 let buffer = Some(gpu.buffer.clone());
492 let stride = size_of::<Vertex>() as u32;
493 context.IASetVertexBuffers(0, 1, Some(&buffer), Some(&stride), Some(&0));
494 }
495 let paint = |target: &Target, clear: [f32; 4], batches: &[Batch]| unsafe {
496 gpu.begin(&target.view, size);
497 context.VSSetShader(&gpu.vertex, None);
498 context.PSSetShader(&gpu.fragment, None);
499 context.ClearRenderTargetView(&target.view, &clear);
500 for batch in batches {
501 let [x, y, w, h] = batch.scissor.map(|value| value as i32);
502 let rect = RECT {
503 left: x,
504 top: y,
505 right: x + w,
506 bottom: y + h,
507 };
508 context.RSSetScissorRects(Some(&[rect]));
509 let (resource, sampler) = match batch.blend {
510 Blend::Image(id) => (frame.image::<Image>(id).0.as_ref(), &gpu.linear),
511 Blend::Group(index) => (Some(&gpu.groups[index].resource), &gpu.linear),
512 Blend::Over | Blend::Erase | Blend::Multiply => {
513 (Some(&gpu.atlas.1), &gpu.nearest)
514 }
515 };
516 context.OMSetBlendState(&gpu.blends[batch.blend.state()], None, u32::MAX);
517 context.PSSetSamplers(0, Some(&[Some(sampler.clone())]));
518 context.PSSetShaderResources(0, Some(&[resource.cloned()]));
519 context.Draw(batch.vertices.len() as u32, batch.vertices.start);
520 }
521 };
522 for (group, picture) in frame.groups.iter().zip(&gpu.groups) {
523 paint(picture, [0.0; 4], &group.batches);
524 }
525 paint(target, clear, frame.batches);
526 unsafe {
527 context.PSSetShaderResources(0, Some(&[None]));
528 context.OMSetRenderTargets(None, None);
529 }
530 }
531}
532
533type Composition = (
534 IDCompositionDevice,
535 IDCompositionTarget,
536 IDCompositionVisual,
537);
538
539/// A flip-model swap chain with premultiplied alpha, which DirectComposition shows over
540/// all of `hwnd`.
541fn compose(
542 device: &ID3D11Device,
543 factory: &IDXGIFactory,
544 hwnd: HWND,
545) -> Result<(IDXGISwapChain, Composition)> {
546 let mut client = RECT::default();
547 unsafe { GetClientRect(hwnd, &mut client)? };
548 let description = DXGI_SWAP_CHAIN_DESC1 {
549 Width: client.right.max(1) as u32,
550 Height: client.bottom.max(1) as u32,
551 Format: DXGI_FORMAT_B8G8R8A8_UNORM,
552 SampleDesc: DXGI_SAMPLE_DESC {
553 Count: 1,
554 Quality: 0,
555 },
556 BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
557 BufferCount: 2,
558 Scaling: DXGI_SCALING_STRETCH,
559 SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
560 AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED,
561 ..Default::default()
562 };
563 // Windows 7 has no dcomp.dll, so it is looked up rather than linked.
564 type Create = unsafe extern "system" fn(*mut c_void, *const GUID, *mut *mut c_void) -> HRESULT;
565 unsafe {
566 let factory: IDXGIFactory2 = factory.cast()?;
567 let swap_chain = factory.CreateSwapChainForComposition(device, &description, None)?;
568 let library = LoadLibraryW(w!("dcomp.dll"))?;
569 let create = GetProcAddress(library, s!("DCompositionCreateDevice"))
570 .ok_or_else(Error::from_thread)?;
571 let create = std::mem::transmute::<unsafe extern "system" fn() -> isize, Create>(create);
572 let mut composition = ptr::null_mut();
573 let dxgi: IDXGIDevice = device.cast()?;
574 create(dxgi.as_raw(), &IDCompositionDevice::IID, &mut composition).ok()?;
575 let composition = IDCompositionDevice::from_raw(composition);
576 let target = composition.CreateTargetForHwnd(hwnd, true)?;
577 let visual = composition.CreateVisual()?;
578 visual.SetContent(&swap_chain)?;
579 target.SetRoot(&visual)?;
580 composition.Commit()?;
581 Ok((swap_chain.cast()?, (composition, target, visual)))
582 }
583}
584
585/// An empty atlas `side` texels square.
586fn atlas(device: &ID3D11Device, side: u32) -> Result<(ID3D11Texture2D, ID3D11ShaderResourceView)> {
587 let texture = texture(device, [side; 2], D3D11_BIND_SHADER_RESOURCE, None)?;
588 let view = resource(device, &texture)?;
589 Ok((texture, view))
590}