// What both ways of taking the screen share (dxgi_source.jai, wgc_source.jai): a D3D11 device of // their own on the adapter the screen is attached to, the region as last seen, copied out of each // new image on the GPU, and the staging textures it is read back through. Capture_Gpu :: struct { adapter: *IDXGIAdapter1; device: *ID3D11Device; device_context: *ID3D11DeviceContext; box: D3D11_BOX; // the region, in the screen's pixels latest: *ID3D11Texture2D; // the region as last seen staging: [RECORD_STAGING] *ID3D11Texture2D; copy_ticks: s64; // spent taking the region out of images (QueryPerformanceCounter ticks) } RECORD_STAGING :: 3; // staging textures in turn: the one read is two frames behind the one copied // What a source's next() found. Capture_Next :: enum u8 { NONE; IMAGE; POINTER; IMAGE_AND_POINTER; LOST; FAILED; } // The device on the screen's adapter, and the textures for a region (in the screen's pixels). gpu_open :: (g: *Capture_Gpu, screen: Record_Screen, region: RECT) -> ok: bool, error: string { factory: *IDXGIFactory1; hr := CreateDXGIFactory1(*uid(IDXGIFactory1_UUID), xx *factory); if FAILED(hr) return false, hresult_text("CreateDXGIFactory1", hr); defer IUnknown_Release(factory); hr = IDXGIFactory1_EnumAdapters1(factory, screen.adapter_index, *g.adapter); if FAILED(hr) return false, hresult_text("the screen's adapter is gone", hr); levels := D3D_FEATURE_LEVEL.[._11_1, ._11_0, ._10_1, ._10_0]; level: D3D_FEATURE_LEVEL; hr = D3D11CreateDevice(g.adapter, .UNKNOWN, null, .BGRA_SUPPORT, levels.data, levels.count, D3D11_SDK_VERSION, *g.device, *level, *g.device_context); if FAILED(hr) return false, hresult_text("D3D11CreateDevice on the screen's adapter", hr); g.box = .{left = xx region.left, top = xx region.top, right = xx region.right, bottom = xx region.bottom, front = 0, back = 1}; desc: D3D11_TEXTURE2D_DESC; desc.Width = cast(u32) (region.right - region.left); desc.Height = cast(u32) (region.bottom - region.top); desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = .B8G8R8A8_UNORM; desc.SampleDesc.Count = 1; desc.Usage = .DEFAULT; hr = ID3D11Device_CreateTexture2D(g.device, *desc, null, *g.latest); if FAILED(hr) return false, hresult_text("a texture for the region", hr); desc.Usage = .STAGING; desc.CPUAccessFlags = .READ; for * g.staging { hr = ID3D11Device_CreateTexture2D(g.device, *desc, null, it); if FAILED(hr) return false, hresult_text("a staging texture", hr); } return true, ""; } // The region of a whole screen's image into g.latest. gpu_copy_region :: (g: *Capture_Gpu, image: *ID3D11Texture2D) { start := ticks_now(); ID3D11DeviceContext_CopySubresourceRegion(g.device_context, g.latest, 0, 0, 0, 0, image, 0, *g.box); g.copy_ticks += ticks_now() - start; } gpu_close :: (g: *Capture_Gpu) { for * g.staging com_release(it); com_release(*g.latest); com_release(*g.device_context); com_release(*g.device); com_release(*g.adapter); } // The calling thread made aware of each monitor's DPI (Windows 10 1703 on), so the desktop it is told // of is in the screens' own pixels. gpu_thread_dpi_aware :: () { DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 :: cast(HANDLE) -4; SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); } #scope_module user32_dll :: #library,system "user32"; SetThreadDpiAwarenessContext :: (value: HANDLE) -> HANDLE #foreign user32_dll; com_release :: (p: *$T) { if p.* { IUnknown_Release(cast(*IUnknown) p.*); p.* = null; } } ticks_now :: () -> s64 { t: LARGE_INTEGER; QueryPerformanceCounter(*t); return t.QuadPart; } hresult_is :: (hr: HRESULT, code: u32) -> bool { return cast,no_check(u32) hr == code; } // "what failed: 0x887A0004 (DXGI_ERROR_UNSUPPORTED)", temp. hresult_text :: (what: string, hr: HRESULT) -> string { code := cast,no_check(u32) hr; name := ""; if code == { case DXGI_ERROR_UNSUPPORTED; name = " (DXGI_ERROR_UNSUPPORTED)"; case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; name = " (DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)"; case DXGI_ERROR_ACCESS_LOST; name = " (DXGI_ERROR_ACCESS_LOST)"; case 0x80070005; name = " (E_ACCESSDENIED)"; case 0x80004002; name = " (E_NOINTERFACE)"; case 0x80070057; name = " (E_INVALIDARG)"; case 0x80040154; name = " (REGDB_E_CLASSNOTREG)"; case 0x80004001; name = " (E_NOTIMPL)"; } return tprint("%: 0x%%", what, formatInt(code, base = 16, minimum_digits = 8), name); } wide_text :: (wide: *s16) -> string { text, ok := wide_to_utf8(cast(*u16) wide,, temp); return ifx ok then text else ""; }