2 * Copyright (C) 2019 OKADA Jun-ichi <okada@abt.jp>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
20 /* This code captures the screen using "Desktop Duplication API".
21 * For more information
22 * https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/desktop-dup-api */
24 #include "dxgicapture.h"
26 #include <d3dcompiler.h>
29 GST_DEBUG_CATEGORY_EXTERN (gst_dxgi_screen_cap_src_debug);
30 #define GST_CAT_DEFAULT gst_dxgi_screen_cap_src_debug
32 #define PTR_RELEASE(p) {if(NULL!=(p)){IUnknown_Release((IUnknown *)(p)); (p) = NULL;}}
33 #define BYTE_PER_PIXEL (4)
35 /* vertex structures */
36 typedef struct _vector3d
43 typedef struct _vector2d
49 typedef struct _vertex
54 #define VERTEX_NUM (6);
56 typedef struct _DxgiCapture
58 GstDXGIScreenCapSrc *src;
60 /*Direct3D pointers */
61 ID3D11Device *d3d11_device;
62 ID3D11DeviceContext *d3d11_context;
63 IDXGIOutput1 *dxgi_output1;
64 IDXGIOutputDuplication *dxgi_dupl;
66 /* Texture that has been rotated and combined fragments. */
67 ID3D11Texture2D *work_texture;
68 D3D11_TEXTURE2D_DESC work_texture_desc;
69 D3D11_VIEWPORT view_port;
70 /* Textures that can be read by the CPU.
71 * CPU-accessible textures are required separately from work_texture
72 * because shaders cannot be executed. */
73 ID3D11Texture2D *readable_texture;
74 ID3D11VertexShader *vertex_shader;
75 ID3D11PixelShader *pixel_shader;
76 ID3D11SamplerState *sampler_state;
77 ID3D11RenderTargetView *target_view;
78 /* Screen output dimensions and rotation status.
79 * The texture acquired by AcquireNextFrame has a non-rotated region. */
80 DXGI_OUTDUPL_DESC dupl_desc;
82 /* mouse pointer image */
83 guint8 *pointer_buffer;
84 gsize pointer_buffer_capacity;
86 /* The movement rectangular regions and the movement
87 * destination position from the previous frame. */
88 DXGI_OUTDUPL_MOVE_RECT *move_rects;
89 gsize move_rects_capacity;
91 /* Array of dirty rectangular region for the desktop frame. */
93 gsize dirty_rects_capacity;
95 /* Vertex buffer created from array of dirty rectangular region. */
96 vertex *dirty_verteces;
97 gsize verteces_capacity;
99 /* Array of rectangular region to copy to readable_texture. */
101 gsize copy_rects_capacity;
103 /* latest mouse pointer info */
104 DXGI_OUTDUPL_POINTER_SHAPE_INFO pointer_shape_info;
105 DXGI_OUTDUPL_POINTER_POSITION last_pointer_position;
109 /* Vertex shader for texture rotation by HLSL. */
110 static const char STR_VERTEX_SHADER[] =
111 "struct vs_input { float4 pos : POSITION; float2 tex : TEXCOORD; }; "
112 "struct vs_output { float4 pos : SV_POSITION; float2 tex : TEXCOORD; }; "
113 "vs_output vs_main(vs_input input){return input;}";
115 /* Pixel shader for texture rotation by HLSL. */
116 static const char STR_PIXEL_SHADER[] =
117 "Texture2D tx : register( t0 ); "
118 "SamplerState samp : register( s0 ); "
119 "struct ps_input { float4 pos : SV_POSITION; float2 tex : TEXCOORD;}; "
120 "float4 ps_main(ps_input input) : "
121 "SV_Target{ return tx.Sample( samp, input.tex ); }";
123 /* initial buffer size */
124 const int INITIAL_POINTER_BUFFER_CAPACITY = 64 * 64 * BYTE_PER_PIXEL;
125 const int INITIAL_MOVE_RECTS_CAPACITY = 100;
126 const int INITIAL_DIRTY_RECTS_CAPACITY = 100;
127 const int INITIAL_VERTICES_CAPACITY = 100 * VERTEX_NUM;
128 const int INITIAL_COPY_RECTS_CAPACITY = 100;
130 static D3D_FEATURE_LEVEL feature_levels[] = {
131 D3D_FEATURE_LEVEL_11_0,
132 D3D_FEATURE_LEVEL_10_1,
133 D3D_FEATURE_LEVEL_10_0,
134 D3D_FEATURE_LEVEL_9_3,
135 D3D_FEATURE_LEVEL_9_2,
136 D3D_FEATURE_LEVEL_9_1,
139 static D3D11_INPUT_ELEMENT_DESC vertex_layout[] = {
140 {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
141 D3D11_INPUT_PER_VERTEX_DATA, 0},
142 {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA,
146 static void _draw_pointer (DxgiCapture * self, LPBYTE buffer, LPRECT dst_rect,
148 static ID3D11Texture2D *_create_texture (DxgiCapture * self,
149 enum D3D11_USAGE usage, UINT bindFlags, UINT cpuAccessFlags);
151 static gboolean _setup_texture (DxgiCapture * self);
153 static HRESULT _update_work_texture (DxgiCapture * self,
154 IDXGIResource * desktop_resource);
156 static HRESULT _copy_dirty_fragment (DxgiCapture * self,
157 ID3D11Texture2D * src_texture, const D3D11_TEXTURE2D_DESC * src_desc,
158 guint move_count, guint dirty_count, RECT ** dst_rect);
160 static void _set_verteces (DxgiCapture * self, vertex * verteces,
161 RECT * dest_rect, const D3D11_TEXTURE2D_DESC * dst_desc, RECT * rect,
162 const D3D11_TEXTURE2D_DESC * src_desc);
164 static GModule *d3d_compiler_module = NULL;
165 static pD3DCompile GstD3DCompileFunc = NULL;
168 gst_dxgicap_shader_init (void)
170 static gsize _init = 0;
171 static const gchar *d3d_compiler_names[] = {
172 "d3dcompiler_47.dll",
173 "d3dcompiler_46.dll",
174 "d3dcompiler_45.dll",
175 "d3dcompiler_44.dll",
176 "d3dcompiler_43.dll",
179 if (g_once_init_enter (&_init)) {
181 for (i = 0; i < G_N_ELEMENTS (d3d_compiler_names); i++) {
182 d3d_compiler_module =
183 g_module_open (d3d_compiler_names[i], G_MODULE_BIND_LAZY);
185 if (d3d_compiler_module) {
186 GST_INFO ("D3D compiler %s is available", d3d_compiler_names[i]);
187 if (!g_module_symbol (d3d_compiler_module, "D3DCompile",
188 (gpointer *) & GstD3DCompileFunc)) {
189 GST_ERROR ("Cannot load D3DCompile symbol from %s",
190 d3d_compiler_names[i]);
191 g_module_close (d3d_compiler_module);
192 d3d_compiler_module = NULL;
193 GstD3DCompileFunc = NULL;
200 if (!GstD3DCompileFunc)
201 GST_WARNING ("D3D11 compiler library is unavailable");
203 g_once_init_leave (&_init, 1);
206 return ! !GstD3DCompileFunc;
210 initialize_output_duplication (DxgiCapture * self)
214 DXGI_OUTDUPL_DESC old_dupl_desc;
215 GstDXGIScreenCapSrc *src = self->src;
217 PTR_RELEASE (self->dxgi_dupl);
219 hdesk = OpenInputDesktop (0, FALSE, GENERIC_ALL);
221 if (!SetThreadDesktop (hdesk)) {
222 GST_WARNING_OBJECT (src, "SetThreadDesktop() failed. Error code: %lu",
226 CloseDesktop (hdesk);
228 GST_WARNING_OBJECT (src, "OpenInputDesktop() failed. Error code: %lu",
232 hr = IDXGIOutput1_DuplicateOutput (self->dxgi_output1,
233 (IUnknown *) (self->d3d11_device), &self->dxgi_dupl);
235 gchar *msg = get_hresult_to_string (hr);
236 GST_WARNING_OBJECT (src, "IDXGIOutput1::DuplicateOutput() failed (%x): %s",
239 if (hr == E_ACCESSDENIED) {
240 /* Happens temporarily during resolution changes. */
243 return GST_FLOW_ERROR;
246 old_dupl_desc = self->dupl_desc;
247 IDXGIOutputDuplication_GetDesc (self->dxgi_dupl, &self->dupl_desc);
249 if (self->readable_texture &&
250 (self->dupl_desc.ModeDesc.Width != old_dupl_desc.ModeDesc.Width ||
251 self->dupl_desc.ModeDesc.Height != old_dupl_desc.ModeDesc.Height ||
252 self->dupl_desc.Rotation != old_dupl_desc.Rotation)) {
253 PTR_RELEASE (self->readable_texture);
254 PTR_RELEASE (self->work_texture);
256 _setup_texture (self);
258 return GST_DXGICAP_FLOW_RESOLUTION_CHANGE;
265 dxgicap_new (HMONITOR monitor, GstDXGIScreenCapSrc * src)
269 IDXGIFactory1 *dxgi_factory1 = NULL;
270 IDXGIAdapter1 *dxgi_adapter1 = NULL;
271 ID3D11InputLayout *vertex_input_layout = NULL;
272 ID3DBlob *vertex_shader_blob = NULL;
273 ID3DBlob *pixel_shader_blob = NULL;
274 D3D11_SAMPLER_DESC sampler_desc;
276 DxgiCapture *self = g_new0 (DxgiCapture, 1);
282 hr = CreateDXGIFactory1 (&IID_IDXGIFactory1, (void **) &dxgi_factory1);
283 HR_FAILED_GOTO (hr, CreateDXGIFactory1, new_error);
286 IDXGIFactory1_EnumAdapters1 (dxgi_factory1, i,
287 &dxgi_adapter1) != DXGI_ERROR_NOT_FOUND; ++i) {
288 IDXGIOutput *dxgi_output = NULL;
289 D3D_FEATURE_LEVEL feature_level;
291 hr = D3D11CreateDevice ((IDXGIAdapter *) dxgi_adapter1,
292 D3D_DRIVER_TYPE_UNKNOWN, NULL, 0,
293 feature_levels, G_N_ELEMENTS (feature_levels),
294 D3D11_SDK_VERSION, &self->d3d11_device, &feature_level,
295 &self->d3d11_context);
297 HR_FAILED_INFO (hr, D3D11CreateDevice);
298 PTR_RELEASE (dxgi_adapter1);
302 for (j = 0; IDXGIAdapter1_EnumOutputs (dxgi_adapter1, j, &dxgi_output) !=
303 DXGI_ERROR_NOT_FOUND; ++j) {
304 DXGI_OUTPUT_DESC output_desc;
305 hr = IDXGIOutput_QueryInterface (dxgi_output, &IID_IDXGIOutput1,
306 (void **) &self->dxgi_output1);
307 PTR_RELEASE (dxgi_output);
308 HR_FAILED_GOTO (hr, IDXGIOutput::QueryInterface, new_error);
310 hr = IDXGIOutput1_GetDesc (self->dxgi_output1, &output_desc);
311 HR_FAILED_GOTO (hr, IDXGIOutput1::GetDesc, new_error);
313 if (output_desc.Monitor == monitor) {
314 GST_DEBUG_OBJECT (src, "found monitor");
318 PTR_RELEASE (self->dxgi_output1);
321 PTR_RELEASE (dxgi_adapter1);
323 if (NULL != self->dxgi_output1) {
327 PTR_RELEASE (self->d3d11_device);
328 PTR_RELEASE (self->d3d11_context);
331 if (NULL == self->dxgi_output1) {
335 PTR_RELEASE (dxgi_factory1);
337 if (initialize_output_duplication (self) == GST_FLOW_ERROR) {
341 self->pointer_buffer_capacity = INITIAL_POINTER_BUFFER_CAPACITY;
342 self->pointer_buffer = g_malloc (self->pointer_buffer_capacity);
343 if (NULL == self->pointer_buffer) {
347 self->move_rects_capacity = INITIAL_MOVE_RECTS_CAPACITY;
348 self->move_rects = g_new0 (DXGI_OUTDUPL_MOVE_RECT, self->move_rects_capacity);
349 if (NULL == self->move_rects) {
353 self->dirty_rects_capacity = INITIAL_DIRTY_RECTS_CAPACITY;
354 self->dirty_rects = g_new0 (RECT, self->dirty_rects_capacity);
355 if (NULL == self->dirty_rects) {
359 self->verteces_capacity = INITIAL_VERTICES_CAPACITY;
360 self->dirty_verteces = g_new0 (vertex, self->verteces_capacity);
361 if (NULL == self->dirty_verteces) {
365 self->copy_rects_capacity = INITIAL_COPY_RECTS_CAPACITY;
366 self->copy_rects = g_new0 (RECT, self->copy_rects_capacity);
367 if (NULL == self->copy_rects) {
371 if (DXGI_MODE_ROTATION_IDENTITY != self->dupl_desc.Rotation) {
372 g_assert (GstD3DCompileFunc);
374 /* For a rotated display, create a shader. */
375 hr = GstD3DCompileFunc (STR_VERTEX_SHADER, sizeof (STR_VERTEX_SHADER),
376 NULL, NULL, NULL, "vs_main", "vs_4_0_level_9_1",
377 0, 0, &vertex_shader_blob, NULL);
378 HR_FAILED_GOTO (hr, D3DCompile, new_error);
380 hr = GstD3DCompileFunc (STR_PIXEL_SHADER, sizeof (STR_PIXEL_SHADER),
381 NULL, NULL, NULL, "ps_main", "ps_4_0_level_9_1",
382 0, 0, &pixel_shader_blob, NULL);
383 HR_FAILED_GOTO (hr, D3DCompile, new_error);
385 hr = ID3D11Device_CreateVertexShader (self->d3d11_device,
386 ID3D10Blob_GetBufferPointer (vertex_shader_blob),
387 ID3D10Blob_GetBufferSize (vertex_shader_blob), NULL,
388 &self->vertex_shader);
389 HR_FAILED_GOTO (hr, ID3D11Device::CreateVertexShader, new_error);
391 hr = ID3D11Device_CreateInputLayout (self->d3d11_device, vertex_layout,
392 G_N_ELEMENTS (vertex_layout),
393 ID3D10Blob_GetBufferPointer (vertex_shader_blob),
394 ID3D10Blob_GetBufferSize (vertex_shader_blob), &vertex_input_layout);
395 PTR_RELEASE (vertex_shader_blob)
396 HR_FAILED_GOTO (hr, ID3D11Device::CreateInputLayout, new_error);
398 ID3D11DeviceContext_IASetInputLayout (self->d3d11_context,
399 vertex_input_layout);
400 PTR_RELEASE (vertex_input_layout);
402 hr = ID3D11Device_CreatePixelShader (self->d3d11_device,
403 ID3D10Blob_GetBufferPointer (pixel_shader_blob),
404 ID3D10Blob_GetBufferSize (pixel_shader_blob), NULL,
405 &self->pixel_shader);
406 PTR_RELEASE (pixel_shader_blob);
407 HR_FAILED_GOTO (hr, ID3D11Device::CreatePixelShader, new_error);
409 memset (&sampler_desc, 0, sizeof (sampler_desc));
410 sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
411 sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
412 sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
413 sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
414 sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
415 sampler_desc.MinLOD = 0;
416 sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
418 hr = ID3D11Device_CreateSamplerState (self->d3d11_device, &sampler_desc,
419 &self->sampler_state);
420 HR_FAILED_GOTO (hr, ID3D11Device::CreateSamplerState, new_error);
426 PTR_RELEASE (vertex_input_layout);
427 PTR_RELEASE (vertex_shader_blob);
428 PTR_RELEASE (pixel_shader_blob);
430 dxgicap_destory (self);
435 dxgicap_destory (DxgiCapture * self)
439 PTR_RELEASE (self->target_view);
440 PTR_RELEASE (self->readable_texture);
441 PTR_RELEASE (self->work_texture);
442 PTR_RELEASE (self->dxgi_output1);
443 PTR_RELEASE (self->dxgi_dupl);
444 PTR_RELEASE (self->d3d11_context);
445 PTR_RELEASE (self->d3d11_device);
446 PTR_RELEASE (self->vertex_shader);
447 PTR_RELEASE (self->pixel_shader);
448 PTR_RELEASE (self->sampler_state);
450 g_free (self->pointer_buffer);
451 g_free (self->move_rects);
452 g_free (self->dirty_rects);
453 g_free (self->dirty_verteces);
454 g_free (self->copy_rects);
460 dxgicap_start (DxgiCapture * self)
462 return _setup_texture (self);
466 dxgicap_stop (DxgiCapture * self)
468 PTR_RELEASE (self->target_view);
469 PTR_RELEASE (self->readable_texture);
470 PTR_RELEASE (self->work_texture);
474 dxgicap_acquire_next_frame (DxgiCapture * self, gboolean show_cursor,
477 GstFlowReturn ret = GST_FLOW_ERROR;
479 GstDXGIScreenCapSrc *src = self->src;
481 DXGI_OUTDUPL_FRAME_INFO frame_info;
482 IDXGIResource *desktop_resource = NULL;
484 if (!self->dxgi_dupl) {
485 /* Desktop duplication interface became invalid due to desktop switch,
486 * UAC prompt popping up, or similar event. Try to reinitialize. */
487 ret = initialize_output_duplication (self);
491 /* Get the latest desktop frames. */
492 hr = IDXGIOutputDuplication_AcquireNextFrame (self->dxgi_dupl,
493 timeout, &frame_info, &desktop_resource);
494 if (hr == DXGI_ERROR_WAIT_TIMEOUT) {
495 /* In case of DXGI_ERROR_WAIT_TIMEOUT,
496 * it has not changed from the last time. */
497 GST_LOG_OBJECT (src, "DXGI_ERROR_WAIT_TIMEOUT");
500 } else if (hr == DXGI_ERROR_ACCESS_LOST) {
501 GST_LOG_OBJECT (src, "DXGI_ERROR_ACCESS_LOST; reinitializing output "
503 PTR_RELEASE (self->dxgi_dupl);
507 HR_FAILED_GOTO (hr, IDXGIOutputDuplication::AcquireNextFrame, end);
509 if (0 != frame_info.LastPresentTime.QuadPart) {
510 /* The desktop frame has changed since last time. */
511 hr = _update_work_texture (self, desktop_resource);
513 GST_DEBUG_OBJECT (src, "failed to _update_work_texture");
518 if (show_cursor && 0 != frame_info.LastMouseUpdateTime.QuadPart) {
519 /* The mouse pointer has changed since last time. */
520 self->last_pointer_position = frame_info.PointerPosition;
522 if (0 < frame_info.PointerShapeBufferSize) {
523 /* A valid mouse cursor shape exists. */
524 DXGI_OUTDUPL_POINTER_SHAPE_INFO pointer_shape_info;
525 guint pointer_shape_size_required;
526 /* Get the mouse cursor shape. */
527 hr = IDXGIOutputDuplication_GetFramePointerShape (self->dxgi_dupl,
528 self->pointer_buffer_capacity,
529 self->pointer_buffer,
530 &pointer_shape_size_required, &pointer_shape_info);
531 if (DXGI_ERROR_MORE_DATA == hr) {
532 /* not enough buffers */
533 self->pointer_buffer_capacity = pointer_shape_size_required * 2;
534 self->pointer_buffer =
535 g_realloc (self->pointer_buffer, self->pointer_buffer_capacity);
537 hr = IDXGIOutputDuplication_GetFramePointerShape (self->dxgi_dupl,
538 self->pointer_buffer_capacity,
539 self->pointer_buffer,
540 &pointer_shape_size_required, &pointer_shape_info);
542 HR_FAILED_GOTO (hr, IDXGIOutputDuplication::GetFramePointerShape, end);
543 self->pointer_shape_info = pointer_shape_info;
552 if (self->dxgi_dupl) {
553 IDXGIOutputDuplication_ReleaseFrame (self->dxgi_dupl);
555 PTR_RELEASE (desktop_resource);
560 dxgicap_copy_buffer (DxgiCapture * self, gboolean show_cursor, LPRECT dst_rect,
561 GstVideoInfo * video_info, GstBuffer * buf)
565 GstDXGIScreenCapSrc *src = self->src;
566 D3D11_MAPPED_SUBRESOURCE readable_map;
567 GstVideoFrame vframe;
568 gint height = RECT_HEIGHT ((*dst_rect));
569 gint width = RECT_WIDTH ((*dst_rect));
571 if (NULL == self->readable_texture) {
572 GST_DEBUG_OBJECT (src, "readable_texture is null");
576 hr = ID3D11DeviceContext_Map (self->d3d11_context,
577 (ID3D11Resource *) self->readable_texture, 0,
578 D3D11_MAP_READ, 0, &readable_map);
579 HR_FAILED_GOTO (hr, IDXGISurface1::Map, flow_error);
580 GST_DEBUG_OBJECT (src, "copy size width:%d height:%d", width, height);
582 /* Copy from readable_texture to GstVideFrame. */
583 if (gst_video_frame_map (&vframe, video_info, buf, GST_MAP_WRITE)) {
590 frame_buffer = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0);
591 p_src = (PBYTE) readable_map.pData +
592 (dst_rect->top * readable_map.RowPitch) +
593 (dst_rect->left * BYTE_PER_PIXEL);
594 p_dst = frame_buffer;
596 line_size = width * BYTE_PER_PIXEL;
597 stride_dst = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
599 if (line_size > stride_dst) {
600 GST_ERROR_OBJECT (src, "not enough stride in video frame");
601 ID3D11DeviceContext_Unmap (self->d3d11_context,
602 (ID3D11Resource *) self->readable_texture, 0);
603 gst_video_frame_unmap (&vframe);
607 for (i = 0; i < height; ++i) {
608 memcpy (p_dst, p_src, line_size);
610 p_src += readable_map.RowPitch;
612 ID3D11DeviceContext_Unmap (self->d3d11_context,
613 (ID3D11Resource *) self->readable_texture, 0);
614 HR_FAILED_GOTO (hr, IDXGISurface1::Unmap, flow_error);
616 if (show_cursor && self->last_pointer_position.Visible) {
617 _draw_pointer (self, frame_buffer, dst_rect, stride_dst);
619 gst_video_frame_unmap (&vframe);
628 _draw_pointer (DxgiCapture * self, PBYTE buffer, LPRECT dst_rect, int stride)
631 RECT clip_pointer_rect;
635 /* For DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME, halve the height. */
637 (DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME ==
638 self->pointer_shape_info.Type)
639 ? self->pointer_shape_info.Height / 2 : self->pointer_shape_info.Height;
641 /* A rectangular area containing the mouse pointer shape */
642 SetRect (&pointer_rect,
643 self->last_pointer_position.Position.x,
644 self->last_pointer_position.Position.y,
645 self->last_pointer_position.Position.x +
646 self->pointer_shape_info.Width,
647 self->last_pointer_position.Position.y + pointer_height);
649 if (!IntersectRect (&clip_pointer_rect, dst_rect, &pointer_rect)) {
653 /* Draw a pointer if it overlaps the destination rectangle range.
654 * There are three ways to draw the mouse cursor.
655 * see https://docs.microsoft.com/ja-jp/windows/win32/api/dxgi1_2/ne-dxgi1_2-dxgi_outdupl_pointer_shape_type */
656 offset_x = clip_pointer_rect.left - pointer_rect.left;
657 offset_y = clip_pointer_rect.top - pointer_rect.top;
659 ((PBYTE) buffer) + ((clip_pointer_rect.top -
660 dst_rect->top) * stride) +
661 ((clip_pointer_rect.left - dst_rect->left) * BYTE_PER_PIXEL);
663 if (DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR ==
664 self->pointer_shape_info.Type
665 || DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR ==
666 self->pointer_shape_info.Type) {
668 DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR ==
669 self->pointer_shape_info.Type;
671 (PBYTE) self->pointer_buffer +
672 (offset_y * self->pointer_shape_info.Pitch) +
673 (offset_x * BYTE_PER_PIXEL);
676 for (y = 0; y < RECT_HEIGHT (clip_pointer_rect); ++y) {
677 for (x = 0; x < RECT_WIDTH (clip_pointer_rect); ++x) {
678 PBYTE p1 = p_dst + (x * BYTE_PER_PIXEL);
679 PBYTE p2 = p_src + (x * BYTE_PER_PIXEL);
680 int alpha = *(p2 + 3);
682 for (i = 0; i < 3; ++i) {
684 /* case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
685 * If the alpha channel of a pixel in the mouse image is 0, copy it.
686 * Otherwise, xor each pixel. */
693 /* case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
694 * Copies the mouse cursor image with alpha channel composition. */
695 *p1 = min (255, max (0, *p1 + ((*p2 - *p1) * alpha / 255)));
702 p_src += self->pointer_shape_info.Pitch;
704 } else if (DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME ==
705 self->pointer_shape_info.Type) {
706 guint mask_bit = 0x80;
708 * It is stored in 1 bit per pixel from the beginning. */
710 (PBYTE) self->pointer_buffer +
711 (offset_y * self->pointer_shape_info.Pitch);
713 * The XOR MASK is stored after the AND mask. */
715 (PBYTE) self->pointer_buffer +
716 ((offset_y + pointer_height) * self->pointer_shape_info.Pitch);
719 for (y = 0; y < RECT_HEIGHT (clip_pointer_rect); ++y) {
720 guint32 *p_dst_32 = ((guint32 *) (p_dst));
721 for (x = offset_x; x < RECT_WIDTH (clip_pointer_rect); ++x) {
724 0 != (*(p_src_and + (x / 8)) & (mask_bit >> bit_pos));
726 0 != (*(p_src_xor + (x / 8)) & (mask_bit >> bit_pos));
730 *p_dst_32 = *p_dst_32 ^ 0x00ffffff;
734 *p_dst_32 = 0xffffffff;
736 *p_dst_32 = 0xff000000;
742 p_src_and += self->pointer_shape_info.Pitch;
743 p_src_xor += self->pointer_shape_info.Pitch;
748 static ID3D11Texture2D *
749 _create_texture (DxgiCapture * self,
750 enum D3D11_USAGE usage, UINT bindFlags, UINT cpuAccessFlags)
753 GstDXGIScreenCapSrc *src = self->src;
754 D3D11_TEXTURE2D_DESC new_desc;
755 ID3D11Texture2D *new_texture = NULL;
757 ZeroMemory (&new_desc, sizeof (new_desc));
758 new_desc.Width = self->dupl_desc.ModeDesc.Width;
759 new_desc.Height = self->dupl_desc.ModeDesc.Height;
760 new_desc.MipLevels = 1;
761 new_desc.ArraySize = 1;
762 new_desc.SampleDesc.Count = 1;
763 new_desc.SampleDesc.Quality = 0;
764 new_desc.Usage = usage;
765 new_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
766 new_desc.BindFlags = bindFlags;
767 new_desc.CPUAccessFlags = cpuAccessFlags;
768 new_desc.MiscFlags = 0;
770 hr = ID3D11Device_CreateTexture2D (self->d3d11_device, &new_desc, NULL,
772 HR_FAILED_RET (hr, ID3D11Device::CreateTexture2D, NULL);
778 _setup_texture (DxgiCapture * self)
781 ID3D11Texture2D *new_texture = NULL;
782 GstDXGIScreenCapSrc *src = self->src;
784 if (NULL == self->readable_texture) {
785 new_texture = _create_texture (self, D3D11_USAGE_STAGING, 0,
786 D3D11_CPU_ACCESS_READ);
787 if (NULL == new_texture) {
790 self->readable_texture = new_texture;
793 if (DXGI_MODE_ROTATION_IDENTITY != self->dupl_desc.Rotation) {
794 /* For rotated displays, create work_texture. */
795 if (NULL == self->work_texture) {
797 _create_texture (self, D3D11_USAGE_DEFAULT,
798 D3D11_BIND_RENDER_TARGET, 0);
799 if (NULL == new_texture) {
803 self->work_texture = new_texture;
804 ID3D11Texture2D_GetDesc (self->work_texture, &self->work_texture_desc);
805 hr = ID3D11Device_CreateRenderTargetView (self->d3d11_device,
806 (ID3D11Resource *) self->work_texture, NULL, &self->target_view);
807 HR_FAILED_RET (hr, ID3D11Device::CreateRenderTargetView, FALSE);
809 self->view_port.Width = (float) self->work_texture_desc.Width;
810 self->view_port.Height = (float) self->work_texture_desc.Height;
811 self->view_port.MinDepth = 0.0f;
812 self->view_port.MaxDepth = 1.0f;
813 self->view_port.TopLeftX = 0.0f;
814 self->view_port.TopLeftY = 0.0f;
821 /* Update work_texture to the latest desktop frame from the update information
822 * that can be obtained from IDXGIOutputDuplication.
823 * Then copy to readable_texture.
826 _update_work_texture (DxgiCapture * self, IDXGIResource * desktop_resource)
829 GstDXGIScreenCapSrc *src = self->src;
831 ID3D11Texture2D *desktop_texture = NULL;
834 guint dirty_rects_capacity_size;
837 D3D11_TEXTURE2D_DESC src_desc;
839 ID3D11Texture2D *work_src;
840 guint move_rects_capacity_size =
841 sizeof (DXGI_OUTDUPL_MOVE_RECT) * self->move_rects_capacity;
843 hr = IDXGIResource_QueryInterface (desktop_resource, &IID_ID3D11Texture2D,
844 (void **) &desktop_texture);
845 HR_FAILED_GOTO (hr, IDXGIResource::QueryInterface, end);
847 /* Get the rectangular regions that was moved from the last time.
848 * However, I have never obtained a valid value in GetFrameMoveRects.
849 * It seems to depend on the implementation of the GPU driver.
850 * see https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_2/nf-dxgi1_2-idxgioutputduplication-getframemoverects
852 hr = IDXGIOutputDuplication_GetFrameMoveRects (self->dxgi_dupl,
853 move_rects_capacity_size, self->move_rects, &required_size);
854 if (DXGI_ERROR_MORE_DATA == hr) {
855 /* not enough buffers */
856 self->move_rects_capacity =
857 (required_size / sizeof (DXGI_OUTDUPL_MOVE_RECT)) * 2;
859 g_renew (DXGI_OUTDUPL_MOVE_RECT, self->move_rects,
860 self->move_rects_capacity);
862 hr = IDXGIOutputDuplication_GetFrameMoveRects (self->dxgi_dupl,
863 required_size, self->move_rects, &required_size);
865 HR_FAILED_GOTO (hr, IDXGIOutputDuplication::GetFrameMoveRects, end);
866 move_count = required_size / sizeof (DXGI_OUTDUPL_MOVE_RECT);
868 dirty_rects_capacity_size = sizeof (RECT) * self->dirty_rects_capacity;
869 /* Gets the rectangular regions that has changed since the last time.
870 see https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_2/nf-dxgi1_2-idxgioutputduplication-getframedirtyrects
872 hr = IDXGIOutputDuplication_GetFrameDirtyRects (self->dxgi_dupl,
873 dirty_rects_capacity_size, self->dirty_rects, &required_size);
875 if (DXGI_ERROR_MORE_DATA == hr) {
876 /* not enough buffers */
877 self->dirty_rects_capacity = (required_size / sizeof (RECT)) * 2;
879 g_renew (RECT, self->dirty_rects, self->dirty_rects_capacity);
881 hr = IDXGIOutputDuplication_GetFrameDirtyRects (self->dxgi_dupl,
882 required_size, self->dirty_rects, &required_size);
884 HR_FAILED_GOTO (hr, IDXGIOutputDuplication::GetFrameDirtyRects, end);
886 dirty_count = required_size / sizeof (RECT);
888 /* The number of rectangular regions to copy to the readable_texture. */
889 copy_count = move_count + dirty_count;
891 if (self->copy_rects_capacity < copy_count) {
892 /* not enough buffers */
893 self->copy_rects_capacity = copy_count * 2;
895 g_renew (RECT, self->copy_rects, self->copy_rects_capacity);
898 if (DXGI_MODE_ROTATION_IDENTITY == self->dupl_desc.Rotation) {
899 /* For a non-rotating display, copy it directly into readable_texture. */
900 RECT *p = self->copy_rects;
901 for (i = 0; i < move_count; ++i) {
902 *p = self->move_rects[i].DestinationRect;
905 for (i = 0; i < dirty_count; ++i) {
906 *p = self->dirty_rects[i];
909 work_src = desktop_texture;
911 /* For rotated displays, rotate to work_texture and copy. */
912 ID3D11Texture2D_GetDesc (desktop_texture, &src_desc);
913 dst_rect = self->copy_rects;
914 /* Copy the dirty rectangular and moved rectangular regions from desktop frame to work_texture. */
915 hr = _copy_dirty_fragment (self, desktop_texture, &src_desc, move_count,
916 dirty_count, &dst_rect);
917 work_src = self->work_texture;
923 /* Copy the updated rectangular regions to readable_texture. */
924 for (i = 0; i < copy_count; ++i) {
925 RECT *p = (self->copy_rects + i);
930 box.right = p->right;
931 box.bottom = p->bottom;
934 ID3D11DeviceContext_CopySubresourceRegion (self->d3d11_context,
935 (ID3D11Resource *) self->readable_texture,
936 0, p->left, p->top, 0, (ID3D11Resource *) work_src, 0, &box);
940 PTR_RELEASE (desktop_texture);
945 _rotate_rect (DXGI_MODE_ROTATION rotation, RECT * dst, const RECT * src,
946 gint dst_width, gint dst_height)
949 case DXGI_MODE_ROTATION_ROTATE90:
950 dst->left = dst_width - src->bottom;
951 dst->top = src->left;
952 dst->right = dst_width - src->top;
953 dst->bottom = src->right;
955 case DXGI_MODE_ROTATION_ROTATE180:
956 dst->left = dst_width - src->right;
957 dst->top = dst_height - src->bottom;
958 dst->right = dst_width - src->left;
959 dst->bottom = dst_height - src->top;
961 case DXGI_MODE_ROTATION_ROTATE270:
962 dst->left = src->top;
963 dst->top = dst_height - src->right;
964 dst->right = src->bottom;
965 dst->bottom = dst_height - src->left;
973 /* Copy the rectangular area specified by dirty_rects and move_rects from src_texture to work_texture. */
975 _copy_dirty_fragment (DxgiCapture * self, ID3D11Texture2D * src_texture,
976 const D3D11_TEXTURE2D_DESC * src_desc, guint move_count, guint dirty_count,
980 GstDXGIScreenCapSrc *src = self->src;
986 guint verteces_count;
987 ID3D11Buffer *verteces_buffer = NULL;
988 ID3D11ShaderResourceView *shader_resource = NULL;
989 D3D11_SUBRESOURCE_DATA subresource_data;
990 D3D11_BUFFER_DESC buffer_desc;
991 D3D11_SHADER_RESOURCE_VIEW_DESC shader_desc;
993 shader_desc.Format = src_desc->Format;
994 shader_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
995 shader_desc.Texture2D.MostDetailedMip = src_desc->MipLevels - 1;
996 shader_desc.Texture2D.MipLevels = src_desc->MipLevels;
997 hr = ID3D11Device_CreateShaderResourceView (self->d3d11_device,
998 (ID3D11Resource *) src_texture, &shader_desc, &shader_resource);
999 HR_FAILED_GOTO (hr, ID3D11Device::CreateShaderResourceView, end);
1001 ID3D11DeviceContext_OMSetRenderTargets (self->d3d11_context, 1,
1002 &self->target_view, NULL);
1004 ID3D11DeviceContext_VSSetShader (self->d3d11_context, self->vertex_shader,
1007 ID3D11DeviceContext_PSSetShader (self->d3d11_context, self->pixel_shader,
1010 ID3D11DeviceContext_PSSetShaderResources (self->d3d11_context, 0, 1,
1013 ID3D11DeviceContext_PSSetSamplers (self->d3d11_context, 0, 1,
1014 &self->sampler_state);
1016 ID3D11DeviceContext_IASetPrimitiveTopology (self->d3d11_context,
1017 D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
1019 verteces_count = (move_count + dirty_count) * VERTEX_NUM;
1020 if (verteces_count > self->verteces_capacity) {
1021 /* not enough buffers */
1022 self->verteces_capacity = verteces_count * 2;
1023 self->dirty_verteces =
1024 g_renew (vertex, self->dirty_verteces, self->verteces_capacity);
1025 if (NULL == self->dirty_verteces) {
1031 dst_rect_p = *dst_rect;
1032 vp = self->dirty_verteces;
1033 /* Create a vertex buffer to move and rotate from the move_rects.
1034 * And set the rectangular region to be copied to readable_texture. */
1035 for (i = 0; i < move_count; ++i) {
1036 /* Copy the area to be moved.
1037 * The source of the move is included in dirty_rects. */
1038 _set_verteces (self, vp, dst_rect_p, &self->work_texture_desc,
1039 &(self->move_rects[i].DestinationRect), src_desc);
1043 /* Create a vertex buffer to move and rotate from the dirty_rects.
1044 * And set the rectangular region to be copied to readable_texture. */
1045 for (i = 0; i < dirty_count; ++i) {
1046 _set_verteces (self, vp, dst_rect_p, &self->work_texture_desc,
1047 &(self->dirty_rects[i]), src_desc);
1051 *dst_rect = dst_rect_p;
1053 memset (&buffer_desc, 0, sizeof (buffer_desc));
1054 buffer_desc.Usage = D3D11_USAGE_IMMUTABLE;
1055 buffer_desc.ByteWidth = verteces_count * sizeof (vertex);
1056 buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
1057 buffer_desc.CPUAccessFlags = 0;
1059 memset (&subresource_data, 0, sizeof (subresource_data));
1060 subresource_data.pSysMem = self->dirty_verteces;
1062 hr = ID3D11Device_CreateBuffer (self->d3d11_device, &buffer_desc,
1063 &subresource_data, &verteces_buffer);
1064 HR_FAILED_GOTO (hr, ID3D11Device::CreateBuffer, end);
1066 stride = sizeof (vertex);
1068 ID3D11DeviceContext_IASetVertexBuffers (self->d3d11_context, 0, 1,
1069 &verteces_buffer, &stride, &offset);
1071 ID3D11DeviceContext_RSSetViewports (self->d3d11_context, 1, &self->view_port);
1073 /* Copy the rectangular region indicated by dirty_rects from the desktop frame to work_texture. */
1074 ID3D11DeviceContext_Draw (self->d3d11_context, verteces_count, 0);
1077 PTR_RELEASE (verteces_buffer);
1078 PTR_RELEASE (shader_resource);
1084 _set_verteces (DxgiCapture * self, vertex * verteces, RECT * dst_rect,
1085 const D3D11_TEXTURE2D_DESC * dst_desc, RECT * rect,
1086 const D3D11_TEXTURE2D_DESC * src_desc)
1091 /* Rectangular area is moved according to the rotation of the display. */
1092 _rotate_rect (self->dupl_desc.Rotation, dst_rect, rect, dst_desc->Width,
1095 /* Set the vertex buffer from the rotation of the display. */
1096 switch (self->dupl_desc.Rotation) {
1097 case DXGI_MODE_ROTATION_ROTATE90:
1098 verteces[0].texcoord = (vector2d) {
1099 (float) rect->right / (float) src_desc->Width,
1100 (float) rect->bottom / (float) src_desc->Height};
1101 verteces[1].texcoord = (vector2d) {
1102 (float) rect->left / (float) src_desc->Width,
1103 (float) rect->bottom / (float) src_desc->Height};
1104 verteces[2].texcoord = (vector2d) {
1105 (float) rect->right / (float) src_desc->Width,
1106 (float) rect->top / (float) src_desc->Height};
1107 verteces[5].texcoord = (vector2d) {
1108 (float) rect->left / (float) src_desc->Width,
1109 (float) rect->top / (float) src_desc->Height};
1111 case DXGI_MODE_ROTATION_ROTATE180:
1112 verteces[0].texcoord = (vector2d) {
1113 (float) rect->right / (float) src_desc->Width,
1114 (float) rect->top / (float) src_desc->Height};
1115 verteces[1].texcoord = (vector2d) {
1116 (float) rect->right / (float) src_desc->Width,
1117 (float) rect->bottom / (float) src_desc->Height};
1118 verteces[2].texcoord = (vector2d) {
1119 (float) rect->left / (float) src_desc->Width,
1120 (float) rect->top / (float) src_desc->Height};
1121 verteces[5].texcoord = (vector2d) {
1122 (float) rect->left / (float) src_desc->Width,
1123 (float) rect->bottom / (float) src_desc->Height};
1125 case DXGI_MODE_ROTATION_ROTATE270:
1126 verteces[0].texcoord = (vector2d) {
1127 (float) rect->left / (float) src_desc->Width,
1128 (float) rect->top / (float) src_desc->Height};
1129 verteces[1].texcoord = (vector2d) {
1130 (float) rect->right / (float) src_desc->Width,
1131 (float) rect->top / (float) src_desc->Height};
1132 verteces[2].texcoord = (vector2d) {
1133 (float) rect->left / (float) src_desc->Width,
1134 (float) rect->bottom / (float) src_desc->Height};
1135 verteces[5].texcoord = (vector2d) {
1136 (float) rect->right / (float) src_desc->Width,
1137 (float) rect->bottom / (float) src_desc->Height};
1140 verteces[0].texcoord = (vector2d) {
1141 (float) rect->left / (float) src_desc->Width,
1142 (float) rect->bottom / (float) src_desc->Height};
1143 verteces[1].texcoord = (vector2d) {
1144 (float) rect->left / (float) src_desc->Width,
1145 (float) rect->top / (float) src_desc->Height};
1146 verteces[2].texcoord = (vector2d) {
1147 (float) rect->right / (float) src_desc->Width,
1148 (float) rect->bottom / (float) src_desc->Height};
1149 verteces[5].texcoord = (vector2d) {
1150 (float) rect->right / (float) src_desc->Width,
1151 (float) rect->top / (float) src_desc->Height};
1154 verteces[3].texcoord = verteces[2].texcoord;
1155 verteces[4].texcoord = verteces[1].texcoord;
1157 center_x = (int) dst_desc->Width / 2;
1158 center_y = (int) dst_desc->Height / 2;
1160 verteces[0].pos = (vector3d) {
1161 (float) (dst_rect->left - center_x) / (float) center_x,
1162 (float) (dst_rect->bottom - center_y) / (float) center_y *-1.0f, 0.0f};
1163 verteces[1].pos = (vector3d) {
1164 (float) (dst_rect->left - center_x) / (float) center_x,
1165 (float) (dst_rect->top - center_y) / (float) center_y *-1.0f, 0.0f};
1166 verteces[2].pos = (vector3d) {
1167 (float) (dst_rect->right - center_x) / (float) center_x,
1168 (float) (dst_rect->bottom - center_y) / (float) center_y *-1.0f, 0.0f};
1169 verteces[3].pos = verteces[2].pos;
1170 verteces[4].pos = verteces[1].pos;
1171 verteces[5].pos = (vector3d) {
1172 (float) (dst_rect->right - center_x) / (float) center_x,
1173 (float) (dst_rect->top - center_y) / (float) center_y *-1.0f, 0.0f};
1176 typedef struct _monitor_param_by_name
1178 const gchar *device_name;
1180 } monitor_param_by_name;
1182 static BOOL CALLBACK
1183 monitor_enum_proc_by_name (HMONITOR hmonitor, HDC hdc, LPRECT rect,
1186 MONITORINFOEXA monitor_info;
1187 monitor_param_by_name *param = (monitor_param_by_name *) lparam;
1189 monitor_info.cbSize = sizeof (monitor_info);
1190 if (GetMonitorInfoA (hmonitor, (MONITORINFO *) & monitor_info)) {
1191 if (0 == g_strcmp0 (monitor_info.szDevice, param->device_name)) {
1192 param->hmonitor = hmonitor;
1200 get_hmonitor_by_device_name (const gchar * device_name)
1202 monitor_param_by_name monitor = { device_name, NULL, };
1203 EnumDisplayMonitors (NULL, NULL, monitor_enum_proc_by_name,
1204 (LPARAM) & monitor);
1205 return monitor.hmonitor;
1208 static BOOL CALLBACK
1209 monitor_enum_proc_primary (HMONITOR hmonitor, HDC hdc, LPRECT rect,
1212 MONITORINFOEXA monitor_info;
1213 monitor_param_by_name *param = (monitor_param_by_name *) lparam;
1215 monitor_info.cbSize = sizeof (monitor_info);
1216 if (GetMonitorInfoA (hmonitor, (MONITORINFO *) & monitor_info)) {
1217 if (MONITORINFOF_PRIMARY == monitor_info.dwFlags) {
1218 param->hmonitor = hmonitor;
1226 get_hmonitor_primary (void)
1228 monitor_param_by_name monitor = { NULL, NULL, };
1229 EnumDisplayMonitors (NULL, NULL, monitor_enum_proc_primary,
1230 (LPARAM) & monitor);
1231 return monitor.hmonitor;
1234 typedef struct _monitor_param_by_index
1239 } monitor_param_by_index;
1241 static BOOL CALLBACK
1242 monitor_enum_proc_by_index (HMONITOR hmonitor, HDC hdc, LPRECT rect,
1245 MONITORINFOEXA monitor_info;
1246 monitor_param_by_index *param = (monitor_param_by_index *) lparam;
1248 monitor_info.cbSize = sizeof (monitor_info);
1249 if (GetMonitorInfoA (hmonitor, (MONITORINFO *) & monitor_info)) {
1250 if (param->target == param->counter) {
1251 param->hmonitor = hmonitor;
1260 get_hmonitor_by_index (int index)
1262 monitor_param_by_index monitor = { index, 0, NULL, };
1263 EnumDisplayMonitors (NULL, NULL, monitor_enum_proc_by_index,
1264 (LPARAM) & monitor);
1265 return monitor.hmonitor;
1270 get_monitor_physical_size (HMONITOR hmonitor, LPRECT rect)
1272 MONITORINFOEXW monitor_info;
1275 monitor_info.cbSize = sizeof (monitor_info);
1276 if (!GetMonitorInfoW (hmonitor, (LPMONITORINFO) & monitor_info)) {
1280 dev_mode.dmSize = sizeof (dev_mode);
1281 dev_mode.dmDriverExtra = sizeof (POINTL);
1282 dev_mode.dmFields = DM_POSITION;
1283 if (!EnumDisplaySettingsW
1284 (monitor_info.szDevice, ENUM_CURRENT_SETTINGS, &dev_mode)) {
1288 SetRect (rect, 0, 0, dev_mode.dmPelsWidth, dev_mode.dmPelsHeight);
1292 static const gchar *
1293 _hresult_to_string_fallback (HRESULT hr)
1295 const gchar *s = "unknown error";
1298 case DXGI_ERROR_ACCESS_DENIED:
1299 s = "DXGI_ERROR_ACCESS_DENIED";
1301 case DXGI_ERROR_ACCESS_LOST:
1302 s = "DXGI_ERROR_ACCESS_LOST";
1304 case DXGI_ERROR_CANNOT_PROTECT_CONTENT:
1305 s = "DXGI_ERROR_CANNOT_PROTECT_CONTENT";
1307 case DXGI_ERROR_DEVICE_HUNG:
1308 s = "DXGI_ERROR_DEVICE_HUNG";
1310 case DXGI_ERROR_DEVICE_REMOVED:
1311 s = "DXGI_ERROR_DEVICE_REMOVED";
1313 case DXGI_ERROR_DEVICE_RESET:
1314 s = "DXGI_ERROR_DEVICE_RESET";
1316 case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
1317 s = "DXGI_ERROR_DRIVER_INTERNAL_ERROR";
1319 case DXGI_ERROR_FRAME_STATISTICS_DISJOINT:
1320 s = "DXGI_ERROR_FRAME_STATISTICS_DISJOINT";
1322 case DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE:
1323 s = "DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE";
1325 case DXGI_ERROR_INVALID_CALL:
1326 s = "DXGI_ERROR_INVALID_CALL";
1328 case DXGI_ERROR_MORE_DATA:
1329 s = "DXGI_ERROR_MORE_DATA";
1331 case DXGI_ERROR_NAME_ALREADY_EXISTS:
1332 s = "DXGI_ERROR_NAME_ALREADY_EXISTS";
1334 case DXGI_ERROR_NONEXCLUSIVE:
1335 s = "DXGI_ERROR_NONEXCLUSIVE";
1337 case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:
1338 s = "DXGI_ERROR_NOT_CURRENTLY_AVAILABLE";
1340 case DXGI_ERROR_NOT_FOUND:
1341 s = "DXGI_ERROR_NOT_FOUND";
1343 case DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED:
1344 s = "DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED";
1346 case DXGI_ERROR_REMOTE_OUTOFMEMORY:
1347 s = "DXGI_ERROR_REMOTE_OUTOFMEMORY";
1349 case DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE:
1350 s = "DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE";
1352 case DXGI_ERROR_SDK_COMPONENT_MISSING:
1353 s = "DXGI_ERROR_SDK_COMPONENT_MISSING";
1355 case DXGI_ERROR_SESSION_DISCONNECTED:
1356 s = "DXGI_ERROR_SESSION_DISCONNECTED";
1358 case DXGI_ERROR_UNSUPPORTED:
1359 s = "DXGI_ERROR_UNSUPPORTED";
1361 case DXGI_ERROR_WAIT_TIMEOUT:
1362 s = "DXGI_ERROR_WAIT_TIMEOUT";
1364 case DXGI_ERROR_WAS_STILL_DRAWING:
1365 s = "DXGI_ERROR_WAS_STILL_DRAWING";
1371 s = "E_OUTOFMEMORY";
1376 case E_ACCESSDENIED:
1377 s = "E_ACCESSDENIED";
1385 #if defined(_MSC_VER) && (_MSC_VER >= 1800)
1386 case DXGI_ERROR_ALREADY_EXISTS:
1387 s = "DXGI_ERROR_ALREADY_EXISTS";
1389 case D3D11_ERROR_FILE_NOT_FOUND:
1390 s = "D3D11_ERROR_FILE_NOT_FOUND";
1392 case D3D11_ERROR_TOO_MANY_UNIQUE_STATE_OBJECTS:
1393 s = "D3D11_ERROR_TOO_MANY_UNIQUE_STATE_OBJECTS";
1395 case D3D11_ERROR_TOO_MANY_UNIQUE_VIEW_OBJECTS:
1396 s = "D3D11_ERROR_TOO_MANY_UNIQUE_VIEW_OBJECTS";
1398 case D3D11_ERROR_DEFERRED_CONTEXT_MAP_WITHOUT_INITIAL_DISCARD:
1399 s = "D3D11_ERROR_DEFERRED_CONTEXT_MAP_WITHOUT_INITIAL_DISCARD";
1407 get_hresult_to_string (HRESULT hr)
1409 gchar *error_text = NULL;
1411 error_text = g_win32_error_message ((gint) hr);
1412 /* g_win32_error_message() doesn't cover all HERESULT return code,
1413 * so it could be empty string, or null if there was an error
1414 * in g_utf16_to_utf8() */
1415 if (!error_text || strlen (error_text) == 0) {
1416 g_free (error_text);
1417 error_text = g_strdup (_hresult_to_string_fallback (hr));