Sunshine v2025.118.151840
Self-hosted game stream host for Moonlight.
display.h
Go to the documentation of this file.
1
5#pragma once
6
7#include <d3d11.h>
8#include <d3d11_4.h>
9#include <d3dcommon.h>
10#include <dwmapi.h>
11#include <dxgi.h>
12#include <dxgi1_6.h>
13
14#include <Unknwn.h>
15#include <winrt/Windows.Graphics.Capture.h>
16
17#include "src/platform/common.h"
18#include "src/utility.h"
19#include "src/video.h"
20
21namespace platf::dxgi {
22 extern const char *format_str[];
23
24 // Add D3D11_CREATE_DEVICE_DEBUG here to enable the D3D11 debug runtime.
25 // You should have a debugger like WinDbg attached to receive debug messages.
26 auto constexpr D3D11_CREATE_DEVICE_FLAGS = 0;
27
28 template <class T>
29 void
30 Release(T *dxgi) {
31 dxgi->Release();
32 }
33
64
65 namespace video {
72 } // namespace video
73
74 class hwdevice_t;
75 struct cursor_t {
76 std::vector<std::uint8_t> img_data;
77
78 DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info;
79 int x, y;
80 bool visible;
81 };
82
84 public:
86 cursor_view { 0, 0, 0, 0, 0.0f, 1.0f } {};
87
88 void
89 set_pos(LONG topleft_x, LONG topleft_y, LONG display_width, LONG display_height, DXGI_MODE_ROTATION display_rotation, bool visible) {
90 this->topleft_x = topleft_x;
91 this->topleft_y = topleft_y;
92 this->display_width = display_width;
93 this->display_height = display_height;
94 this->display_rotation = display_rotation;
95 this->visible = visible;
96 update_viewport();
97 }
98
99 void
100 set_texture(LONG texture_width, LONG texture_height, texture2d_t &&texture) {
101 this->texture = std::move(texture);
102 this->texture_width = texture_width;
103 this->texture_height = texture_height;
104 update_viewport();
105 }
106
107 void
108 update_viewport() {
109 switch (display_rotation) {
110 case DXGI_MODE_ROTATION_UNSPECIFIED:
111 case DXGI_MODE_ROTATION_IDENTITY:
112 cursor_view.TopLeftX = topleft_x;
113 cursor_view.TopLeftY = topleft_y;
114 cursor_view.Width = texture_width;
115 cursor_view.Height = texture_height;
116 break;
117
118 case DXGI_MODE_ROTATION_ROTATE90:
119 cursor_view.TopLeftX = topleft_y;
120 cursor_view.TopLeftY = display_width - texture_width - topleft_x;
121 cursor_view.Width = texture_height;
122 cursor_view.Height = texture_width;
123 break;
124
125 case DXGI_MODE_ROTATION_ROTATE180:
126 cursor_view.TopLeftX = display_width - texture_width - topleft_x;
127 cursor_view.TopLeftY = display_height - texture_height - topleft_y;
128 cursor_view.Width = texture_width;
129 cursor_view.Height = texture_height;
130 break;
131
132 case DXGI_MODE_ROTATION_ROTATE270:
133 cursor_view.TopLeftX = display_height - texture_height - topleft_y;
134 cursor_view.TopLeftY = topleft_x;
135 cursor_view.Width = texture_height;
136 cursor_view.Height = texture_width;
137 break;
138 }
139 }
140
141 texture2d_t texture;
142 LONG texture_width;
143 LONG texture_height;
144
145 LONG topleft_x;
146 LONG topleft_y;
147
148 LONG display_width;
149 LONG display_height;
150 DXGI_MODE_ROTATION display_rotation;
151
152 shader_res_t input_res;
153
154 D3D11_VIEWPORT cursor_view;
155
156 bool visible;
157 };
158
159 class display_base_t: public display_t {
160 public:
161 int
162 init(const ::video::config_t &config, const std::string &display_name);
163
165 capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override;
166
167 factory1_t factory;
168 adapter_t adapter;
169 output_t output;
170 device_t device;
171 device_ctx_t device_ctx;
172 DXGI_RATIONAL display_refresh_rate;
173 int display_refresh_rate_rounded;
174
175 DXGI_MODE_ROTATION display_rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
176 int width_before_rotation;
177 int height_before_rotation;
178
179 int client_frame_rate;
180
181 DXGI_FORMAT capture_format;
182 D3D_FEATURE_LEVEL feature_level;
183
184 std::unique_ptr<high_precision_timer> timer = create_high_precision_timer();
185
194
195 typedef UINT D3DKMT_HANDLE;
196
198 LUID AdapterLuid;
199 D3DKMT_HANDLE hAdapter;
201
202 typedef struct _D3DKMT_WDDM_2_7_CAPS {
203 union {
204 struct
205 {
206 UINT HwSchSupported : 1;
207 UINT HwSchEnabled : 1;
208 UINT HwSchEnabledByDefault : 1;
209 UINT IndependentVidPnVSyncControl : 1;
210 UINT Reserved : 28;
211 };
212 UINT Value;
213 };
215
217 D3DKMT_HANDLE hAdapter;
218 UINT Type;
219 VOID *pPrivateDriverData;
220 UINT PrivateDriverDataSize;
222
223 const UINT KMTQAITYPE_WDDM_2_7_CAPS = 70;
224
225 typedef struct _D3DKMT_CLOSEADAPTER {
226 D3DKMT_HANDLE hAdapter;
228
229 typedef NTSTATUS(WINAPI *PD3DKMTSetProcessSchedulingPriorityClass)(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS);
230 typedef NTSTATUS(WINAPI *PD3DKMTOpenAdapterFromLuid)(D3DKMT_OPENADAPTERFROMLUID *);
231 typedef NTSTATUS(WINAPI *PD3DKMTQueryAdapterInfo)(D3DKMT_QUERYADAPTERINFO *);
232 typedef NTSTATUS(WINAPI *PD3DKMTCloseAdapter)(D3DKMT_CLOSEADAPTER *);
233
234 virtual bool
235 is_hdr() override;
236 virtual bool
237 get_hdr_metadata(SS_HDR_METADATA &metadata) override;
238
239 const char *
240 dxgi_format_to_string(DXGI_FORMAT format);
241 const char *
242 colorspace_to_string(DXGI_COLOR_SPACE_TYPE type);
243 virtual std::vector<DXGI_FORMAT>
244 get_supported_capture_formats() = 0;
245
246 protected:
247 int
248 get_pixel_pitch() {
249 return (capture_format == DXGI_FORMAT_R16G16B16A16_FLOAT) ? 8 : 4;
250 }
251
252 virtual capture_e
253 snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) = 0;
254 virtual capture_e
255 release_snapshot() = 0;
256 virtual int
257 complete_img(img_t *img, bool dummy) = 0;
258 };
259
264 public:
265 std::shared_ptr<img_t>
266 alloc_img() override;
267 int
268 dummy_img(img_t *img) override;
269 int
270 complete_img(img_t *img, bool dummy) override;
271 std::vector<DXGI_FORMAT>
272 get_supported_capture_formats() override;
273
274 std::unique_ptr<avcodec_encode_device_t>
275 make_avcodec_encode_device(pix_fmt_e pix_fmt) override;
276
277 D3D11_MAPPED_SUBRESOURCE img_info;
278 texture2d_t texture;
279 };
280
284 class display_vram_t: public display_base_t, public std::enable_shared_from_this<display_vram_t> {
285 public:
286 std::shared_ptr<img_t>
287 alloc_img() override;
288 int
289 dummy_img(img_t *img_base) override;
290 int
291 complete_img(img_t *img_base, bool dummy) override;
292 std::vector<DXGI_FORMAT>
293 get_supported_capture_formats() override;
294
295 bool
296 is_codec_supported(std::string_view name, const ::video::config_t &config) override;
297
298 std::unique_ptr<avcodec_encode_device_t>
299 make_avcodec_encode_device(pix_fmt_e pix_fmt) override;
300
301 std::unique_ptr<nvenc_encode_device_t>
302 make_nvenc_encode_device(pix_fmt_e pix_fmt) override;
303
304 std::atomic<uint32_t> next_image_id;
305 };
306
311 public:
312 dup_t dup;
313 bool has_frame {};
314 std::chrono::steady_clock::time_point last_protected_content_warning_time {};
315
316 int
317 init(display_base_t *display, const ::video::config_t &config);
319 next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p);
321 reset(dup_t::pointer dup_p = dup_t::pointer());
323 release_frame();
324
326 };
327
332 public:
333 int
334 init(const ::video::config_t &config, const std::string &display_name);
336 snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override;
338 release_snapshot() override;
339
340 duplication_t dup;
341 cursor_t cursor;
342 };
343
348 public:
349 int
350 init(const ::video::config_t &config, const std::string &display_name);
352 snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override;
354 release_snapshot() override;
355
356 duplication_t dup;
357 sampler_state_t sampler_linear;
358
359 blend_t blend_alpha;
360 blend_t blend_invert;
361 blend_t blend_disable;
362
363 ps_t cursor_ps;
364 vs_t cursor_vs;
365
366 gpu_cursor_t cursor_alpha;
367 gpu_cursor_t cursor_xor;
368
369 texture2d_t old_surface_delayed_destruction;
370 std::chrono::steady_clock::time_point old_surface_timestamp;
371 std::variant<std::monostate, texture2d_t, std::shared_ptr<platf::img_t>> last_frame_variant;
372 };
373
378 winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice uwp_device { nullptr };
379 winrt::Windows::Graphics::Capture::GraphicsCaptureItem item { nullptr };
380 winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool frame_pool { nullptr };
381 winrt::Windows::Graphics::Capture::GraphicsCaptureSession capture_session { nullptr };
382 winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame produced_frame { nullptr }, consumed_frame { nullptr };
383 SRWLOCK frame_lock = SRWLOCK_INIT;
384 CONDITION_VARIABLE frame_present_cv;
385
386 void
387 on_frame_arrived(winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender, winrt::Windows::Foundation::IInspectable const &);
388
389 public:
392
393 int
394 init(display_base_t *display, const ::video::config_t &config);
396 next_frame(std::chrono::milliseconds timeout, ID3D11Texture2D **out, uint64_t &out_time);
398 release_frame();
399 int
400 set_cursor_visible(bool);
401 };
402
407 wgc_capture_t dup;
408
409 public:
410 int
411 init(const ::video::config_t &config, const std::string &display_name);
413 snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override;
415 release_snapshot() override;
416 };
417
422 wgc_capture_t dup;
423
424 public:
425 int
426 init(const ::video::config_t &config, const std::string &display_name);
428 snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override;
430 release_snapshot() override;
431 };
432} // namespace platf::dxgi
Definition common.h:448
std::function< bool(std::shared_ptr< img_t > &&img, bool frame_captured)> push_captured_image_cb_t
Callback for when a new image is ready. When display has a new image ready or a timeout occurs,...
Definition common.h:457
std::function< bool(std::shared_ptr< img_t > &img_out)> pull_free_image_cb_t
Get free image from pool. Calls must be synchronized. Blocks until there is free image in the pool or...
Definition common.h:466
Definition display.h:159
capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override
Capture a frame.
Definition display_base.cpp:199
_D3DKMT_SCHEDULINGPRIORITYCLASS
Definition display.h:186
@ D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL
Normal priority class.
Definition display.h:189
@ D3DKMT_SCHEDULINGPRIORITYCLASS_BELOW_NORMAL
Below normal priority class.
Definition display.h:188
@ D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH
High priority class.
Definition display.h:191
@ D3DKMT_SCHEDULINGPRIORITYCLASS_IDLE
Idle priority class.
Definition display.h:187
@ D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME
Realtime priority class.
Definition display.h:192
@ D3DKMT_SCHEDULINGPRIORITYCLASS_ABOVE_NORMAL
Above normal priority class.
Definition display.h:190
Definition display.h:331
Definition display.h:347
Definition display.h:263
Definition display.h:284
bool is_codec_supported(std::string_view name, const ::video::config_t &config) override
Check that a given codec is supported by the display device.
Definition display_vram.cpp:1841
Definition display.h:406
capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr< platf::img_t > &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override
Get the next frame from the Windows.Graphics.Capture API and copy it into a new snapshot texture.
Definition display_wgc.cpp:248
Definition display.h:421
capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr< platf::img_t > &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override
Definition display_vram.cpp:1640
Definition display.h:310
Definition display.h:83
Definition display.h:377
capture_e next_frame(std::chrono::milliseconds timeout, ID3D11Texture2D **out, uint64_t &out_time)
Get the next frame from the producer thread. If not available, the capture thread blocks until one is...
Definition display_wgc.cpp:182
Definition utility.h:496
Declarations for common platform specific utilities.
std::shared_ptr< display_t > display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config)
Get the display_t instance for the given hwdevice_type. If display_name is empty, use the first monit...
Definition misc.cpp:909
pix_fmt_e
Definition common.h:213
std::unique_ptr< high_precision_timer > create_high_precision_timer()
Create platform-specific timer capable of high-precision sleep.
Definition misc.cpp:1019
capture_e
Definition common.h:440
@ timeout
Timeout.
Definition display.h:75
Definition display_ram.cpp:15
Declarations for utility functions.
Declarations for video.