1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
// Copyright 2017 Dasein Phaos aka. Luxko
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! this module contains `Factory`, the API entry point,
//! along with several other fundamental structures such as
//! `Adapter` and `Output`.

use comptr::ComPtr;
use winapi::{IDXGIFactory4, IDXGIAdapter3, IDXGIAdapter1, IDXGISwapChain3, IDXGISwapChain1, IDXGIOutput};
use error::WinError;
use std::os::raw::c_void;
use swapchain::{SwapChain, SwapChainDesc, FullScreenDesc};
use command::CommandQueue;

/// dxgi API entry point
#[derive(Debug, Clone)]
pub struct Factory {
    pub(crate) ptr: ComPtr<IDXGIFactory4>,
}

impl Factory {
    /// try to create a new DXGI factory
    pub fn new() -> Result<Factory, WinError> {
        unsafe {
            let mut ptr: *mut IDXGIFactory4 = ::std::mem::uninitialized();
            let hr = ::dxgi::CreateDXGIFactory1(
                & ::dxguid::IID_IDXGIFactory4,
                &mut ptr as *mut *mut _ as *mut *mut c_void
            );
            WinError::from_hresult_or_ok(hr, || Factory{
                ptr: ComPtr::new(ptr)
            })
        }
    }

    /// enumerate available adapters
    #[inline]
    pub fn enumerate_adapters(&mut self) -> AdapterIter {
        AdapterIter{
            idx: 0, factory: self
        }
    }

    /// create a swap chain
    #[inline]
    pub fn create_swapchain_for_hwnd(
        &mut self, queue: &CommandQueue, // FIXME: this should be a command queue
        hwnd: ::winapi::HWND, // TODO: change?
        desc: &SwapChainDesc,
        fullscreen_desc: Option<&FullScreenDesc>,
        restrict_output: Option<&Output>
    ) -> Result<SwapChain, WinError> {
        let fullscreen_desc = if let Some(desc) = fullscreen_desc {
            desc as *const _ as *const ::winapi::DXGI_SWAP_CHAIN_FULLSCREEN_DESC
        } else {
            ::std::ptr::null()
        };
        let restrict_output = if let Some(output) = restrict_output {
            output.ptr.as_mut_ptr()
        } else {
            ::std::ptr::null_mut()
        };
        unsafe {
            let mut ptr: *mut IDXGISwapChain3 = ::std::mem::uninitialized();
            let hr = self.ptr.CreateSwapChainForHwnd(
                queue.ptr.as_mut_ptr() as *mut _,
                hwnd,
                desc as *const _ as *const ::winapi::DXGI_SWAP_CHAIN_DESC1,
                fullscreen_desc,
                restrict_output,
                &mut ptr as *mut *mut _ as *mut *mut IDXGISwapChain1 // TODO: double check
            );
            WinError::from_hresult_or_ok(hr, || SwapChain{
                ptr: ComPtr::new(ptr)
            })
        }
    }

    #[inline]
    pub fn create_swapchain<Window: HwndProvider+?Sized>(
        &mut self, queue: &CommandQueue,
        window: &Window, desc: &SwapChainDesc,
        fullscreen_desc: Option<&FullScreenDesc>,
        restrict_output: Option<&Output>
    ) -> Result<SwapChain, WinError> {
        self.create_swapchain_for_hwnd(
            queue, <Window as HwndProvider>::get_hwnd(window), desc,
            fullscreen_desc, restrict_output
        )
    }
}

/// a provider for HWND
pub trait HwndProvider {
    fn get_hwnd(&self) -> ::winapi::HWND;
}

/// iterator returned by a factory to retrieve available adapters
pub struct AdapterIter<'a> {
    idx: u32,
    factory: &'a mut Factory,
}

impl<'a> Iterator for AdapterIter<'a> {
    type Item = Adapter;
    
    #[inline]
    fn next(&mut self) -> Option<Adapter> {
        let old_idx = self.idx;
        unsafe {
            let mut padapter: *mut IDXGIAdapter1 = ::std::mem::uninitialized();
            let hr = self.factory.ptr.EnumAdapters1(
                old_idx,
                &mut padapter as *mut *mut _ as *mut *mut IDXGIAdapter1
            );
            if let Err(_) = WinError::from_hresult(hr) {
                return None;
            }
            let mut adapter1 = ComPtr::new(padapter);

            let mut padapter: *mut IDXGIAdapter3 = ::std::mem::uninitialized();
            let hr = adapter1.QueryInterface(
                & ::dxguid::IID_IDXGIAdapter3,
                &mut padapter as *mut *mut _ as *mut *mut _
            );

            WinError::from_hresult(hr).ok().map(|()| {
                self.idx += 1;
                Adapter{
                    ptr: ComPtr::new(padapter)
                }
            })
        }
    }
}

/// a display subsystem
#[derive(Debug, Clone)]
pub struct Adapter {
    pub(crate) ptr: ComPtr<IDXGIAdapter3>,
}

impl Adapter {
    /// get basic descriptions about the adapter
    pub fn get_desc(&mut self) -> Result<AdapterDesc, WinError> {
        unsafe {
            let mut ret = ::std::mem::uninitialized();
            let hr = self.ptr.GetDesc1(&mut ret);
            WinError::from_hresult_or_ok(hr, || ::std::mem::transmute(ret))
        }
    }

    /// enumerate outputs of this adapter
    #[inline]
    pub fn enumerate_outputs(&mut self) -> OutputIter {
        OutputIter{
            idx: 0,
            adapter: self
        }
    }

    /// query adapter memory infos
    #[inline]
    pub fn query_mem_info(&mut self, node_idx: u32, local: bool) -> Result<VideoMemInfo, WinError> {
        let mem_seg_group = if local {
            ::winapi::DXGI_MEMORY_SEGMENT_GROUP_LOCAL
        } else {
            ::winapi::DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL
        };
        unsafe {
            let mut ret: VideoMemInfo = ::std::mem::uninitialized();
            let hr = self.ptr.QueryVideoMemoryInfo(
                node_idx, mem_seg_group, 
                &mut ret as *mut _ as *mut _
            );
            WinError::from_hresult_or_ok(hr, || {
                ret
            })
        }
    }
}

/// video memory informaiton
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct VideoMemInfo {
    pub budget: u64,
    pub current_usage: u64,
    pub available_for_reservation: u64,
    pub current_reservation: u64,
}

/// adapter description
#[repr(C)]
pub struct AdapterDesc {
    /// a string description of the adapter
    pub description: [::winapi::WCHAR; 128],
    /// PCI ID of the hardware vendor
    pub vendor_id: u32,
    /// PCI ID of the hardware device
    pub device_id: u32,
    /// PCI ID of the revision number of the adapter
    pub revision: u32,
    /// dedicated video memory not shared with CPU
    pub dedicated_vmem: usize,
    /// dedicated system memory not shared with CPU
    pub dedicated_smem: usize,
    /// shared system memory
    pub shared_smem: usize,
    /// locally unique id for the adapter
    pub luid: ::winapi::LUID,
    /// misc flags
    pub flags: AdapterFlags,
}

impl ::std::fmt::Debug for AdapterDesc {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
        write!(f, "AdapterDesc {{ description: {:?}, vendor_id: {:?}, device_id: {:?}, revision: {:?}, dedicated_vmem: {:?}, dedicated_smem: {:?}, shared_smem: {:?}, luid: {:?}, flags: {:?} }}", ::format::from_wchar_slice(&self.description), self.vendor_id, self.device_id, self.revision, self.dedicated_vmem, self.dedicated_smem, self.shared_smem, self.luid, self.flags)
    }
}

impl ::std::fmt::Display for AdapterDesc {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
        write!(f, "{:?}", ::format::from_wchar_slice(&self.description))
    }
}

bitflags!{
    /// adapter flags
    #[repr(C)]
    pub struct AdapterFlags: u32 {
        const ADAPTER_FLAG_NONE = 0;
        /// reserved flag
        const ADAPTER_FLAG_REMOTE = 1;
        /// software adapter
        const ADAPTER_FLAG_SOFTWARE = 2;
    }
}


/// iterator returned by an adapter to retrieve available outputs
pub struct OutputIter<'a> {
    idx: u32,
    adapter: &'a mut Adapter,
}

impl<'a> Iterator for OutputIter<'a> {
    type Item = Output;

    fn next(&mut self) -> Option<Output> {
        let oldidx = self.idx;
        unsafe {
            let mut ptr: *mut IDXGIOutput = ::std::mem::uninitialized();
            let hr = self.adapter.ptr.EnumOutputs(
                oldidx, &mut ptr
            );
            WinError::from_hresult(hr).ok().map(|()| {
                self.idx += 1;
                Output{ptr: ComPtr::new(ptr)}
            })
        }
    }
}

/// a display output, such as a monitor
#[derive(Debug, Clone)]
pub struct Output {
    pub(crate) ptr: ComPtr<IDXGIOutput>,
}

impl Output {
    // TODO: add more methods?

    /// get basic description for the output
    #[inline]
    pub fn get_desc(&mut self) -> Result<OutputDesc, WinError> {
        unsafe {
            let mut ret = ::std::mem::uninitialized();
            let hr = self.ptr.GetDesc(&mut ret as *mut _ as *mut _);
            WinError::from_hresult_or_ok(hr, || ret)
        }
    }
}

#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct OutputDesc {
    pub name: [::winapi::WCHAR; 32],
    pub descktop_coordinates: ::format::Rect,
    pub attached_to_desktop: ::format::Bool,
    pub rotation: RotationMode,
    pub hmonitor: ::winapi::HMONITOR,
}

impl ::std::fmt::Display for OutputDesc {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
        write!(f, "{:?}", ::format::from_wchar_slice(&self.name))
    }
}

bitflags!{
    /// rotation mode for the monitor
    #[repr(C)]
    pub struct RotationMode: u32 {
        const ROTATION_MODE_UNSPECIFIED = 0;
        const ROTATION_MODE_IDENTITY = 1;
        const ROTATION_MODE_ROTATE90 = 2;
        const ROTATION_MODE_ROTATE180 = 3;
        const ROTATION_MODE_ROTATE270 = 4;
    }
}