Sunshine v2026.319.132152
Self-hosted game stream host for Moonlight.
display.h
Go to the documentation of this file.
1
5#pragma once
6
7// platform includes
8#include <d3d11.h>
9#include <d3d11_4.h>
10#include <d3dcommon.h>
11#include <dwmapi.h>
12#include <dxgi.h>
13#include <dxgi1_6.h>
14#include <Unknwn.h>
15#include <winrt/windows.graphics.capture.h>
16
17// local includes
18#include "src/platform/common.h"
19#include "src/utility.h"
20#include "src/video.h"
21
22namespace platf::dxgi {
23 extern const char *format_str[];
24
25 // Add D3D11_CREATE_DEVICE_DEBUG here to enable the D3D11 debug runtime.
26 // You should have a debugger like WinDbg attached to receive debug messages.
27 auto constexpr D3D11_CREATE_DEVICE_FLAGS = 0;
28
29 template<class T>
30 void Release(T *dxgi) {
31 dxgi->Release();
32 }
33
64
65 namespace video {
72 } // namespace video
73
74 class hwdevice_t;
75
76 struct cursor_t {
77 std::vector<std::uint8_t> img_data;
78
79 DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info;
80 int x;
81 int y;
82 bool visible;
83 };
84
86 public:
88 cursor_view {0, 0, 0, 0, 0.0f, 1.0f} {};
89
90 void set_pos(LONG topleft_x, LONG topleft_y, LONG display_width, LONG display_height, DXGI_MODE_ROTATION display_rotation, bool visible) {
91 this->topleft_x = topleft_x;
92 this->topleft_y = topleft_y;
93 this->display_width = display_width;
94 this->display_height = display_height;
95 this->display_rotation = display_rotation;
96 this->visible = visible;
97 update_viewport();
98 }
99
100 void 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 update_viewport() {
108 switch (display_rotation) {
109 case DXGI_MODE_ROTATION_UNSPECIFIED:
110 case DXGI_MODE_ROTATION_IDENTITY:
111 cursor_view.TopLeftX = topleft_x;
112 cursor_view.TopLeftY = topleft_y;
113 cursor_view.Width = texture_width;
114 cursor_view.Height = texture_height;
115 break;
116
117 case DXGI_MODE_ROTATION_ROTATE90:
118 cursor_view.TopLeftX = topleft_y;
119 cursor_view.TopLeftY = display_width - texture_width - topleft_x;
120 cursor_view.Width = texture_height;
121 cursor_view.Height = texture_width;
122 break;
123
124 case DXGI_MODE_ROTATION_ROTATE180:
125 cursor_view.TopLeftX = display_width - texture_width - topleft_x;
126 cursor_view.TopLeftY = display_height - texture_height - topleft_y;
127 cursor_view.Width = texture_width;
128 cursor_view.Height = texture_height;
129 break;
130
131 case DXGI_MODE_ROTATION_ROTATE270:
132 cursor_view.TopLeftX = display_height - texture_height - topleft_y;
133 cursor_view.TopLeftY = topleft_x;
134 cursor_view.Width = texture_height;
135 cursor_view.Height = texture_width;
136 break;
137 }
138 }
139
140 texture2d_t texture;
141 LONG texture_width;
142 LONG texture_height;
143
144 LONG topleft_x;
145 LONG topleft_y;
146
147 LONG display_width;
148 LONG display_height;
149 DXGI_MODE_ROTATION display_rotation;
150
151 shader_res_t input_res;
152
153 D3D11_VIEWPORT cursor_view;
154
155 bool visible;
156 };
157
158 class display_base_t: public display_t {
159 public:
160 int init(const ::video::config_t &config, const std::string &display_name);
161
162 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;
163
164 factory1_t factory;
165 adapter_t adapter;
166 output_t output;
167 device_t device;
168 device_ctx_t device_ctx;
169 DXGI_RATIONAL display_refresh_rate;
170 int display_refresh_rate_rounded;
171
172 DXGI_MODE_ROTATION display_rotation = DXGI_MODE_ROTATION_UNSPECIFIED;
173 int width_before_rotation;
174 int height_before_rotation;
175
176 int client_frame_rate;
177 DXGI_RATIONAL client_frame_rate_strict;
178
179 DXGI_FORMAT capture_format;
180 D3D_FEATURE_LEVEL feature_level;
181
182 std::unique_ptr<high_precision_timer> timer = create_high_precision_timer();
183
192
193 typedef UINT D3DKMT_HANDLE;
194
196 LUID AdapterLuid;
197 D3DKMT_HANDLE hAdapter;
199
200 typedef struct _D3DKMT_WDDM_2_7_CAPS {
201 union {
202 struct
203 {
204 UINT HwSchSupported : 1;
205 UINT HwSchEnabled : 1;
206 UINT HwSchEnabledByDefault : 1;
207 UINT IndependentVidPnVSyncControl : 1;
208 UINT Reserved : 28;
209 };
210
211 UINT Value;
212 };
214
216 D3DKMT_HANDLE hAdapter;
217 UINT Type;
218 VOID *pPrivateDriverData;
219 UINT PrivateDriverDataSize;
221
222 const UINT KMTQAITYPE_WDDM_2_7_CAPS = 70;
223
224 typedef struct _D3DKMT_CLOSEADAPTER {
225 D3DKMT_HANDLE hAdapter;
227
228 typedef NTSTATUS(WINAPI *PD3DKMTSetProcessSchedulingPriorityClass)(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS);
229 typedef NTSTATUS(WINAPI *PD3DKMTOpenAdapterFromLuid)(D3DKMT_OPENADAPTERFROMLUID *);
230 typedef NTSTATUS(WINAPI *PD3DKMTQueryAdapterInfo)(D3DKMT_QUERYADAPTERINFO *);
231 typedef NTSTATUS(WINAPI *PD3DKMTCloseAdapter)(D3DKMT_CLOSEADAPTER *);
232
233 virtual bool is_hdr() override;
234 virtual bool get_hdr_metadata(SS_HDR_METADATA &metadata) override;
235
236 const char *dxgi_format_to_string(DXGI_FORMAT format);
237 const char *colorspace_to_string(DXGI_COLOR_SPACE_TYPE type);
238 virtual std::vector<DXGI_FORMAT> get_supported_capture_formats() = 0;
239
240 protected:
241 int get_pixel_pitch() {
242 return (capture_format == DXGI_FORMAT_R16G16B16A16_FLOAT) ? 8 : 4;
243 }
244
245 virtual 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) = 0;
246 virtual capture_e release_snapshot() = 0;
247 virtual int complete_img(img_t *img, bool dummy) = 0;
248 };
249
254 public:
255 std::shared_ptr<img_t> alloc_img() override;
256 int dummy_img(img_t *img) override;
257 int complete_img(img_t *img, bool dummy) override;
258 std::vector<DXGI_FORMAT> get_supported_capture_formats() override;
259
260 std::unique_ptr<avcodec_encode_device_t> make_avcodec_encode_device(pix_fmt_e pix_fmt) override;
261
262 D3D11_MAPPED_SUBRESOURCE img_info;
263 texture2d_t texture;
264 };
265
269 class display_vram_t: public display_base_t, public std::enable_shared_from_this<display_vram_t> {
270 public:
271 std::shared_ptr<img_t> alloc_img() override;
272 int dummy_img(img_t *img_base) override;
273 int complete_img(img_t *img_base, bool dummy) override;
274 std::vector<DXGI_FORMAT> get_supported_capture_formats() override;
275
276 bool is_codec_supported(std::string_view name, const ::video::config_t &config) override;
277
278 std::unique_ptr<avcodec_encode_device_t> make_avcodec_encode_device(pix_fmt_e pix_fmt) override;
279
280 std::unique_ptr<nvenc_encode_device_t> make_nvenc_encode_device(pix_fmt_e pix_fmt) override;
281
282 std::atomic<uint32_t> next_image_id;
283 };
284
289 public:
290 dup_t dup;
291 bool has_frame {};
292 std::chrono::steady_clock::time_point last_protected_content_warning_time {};
293
294 int init(display_base_t *display, const ::video::config_t &config);
295 capture_e next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p);
296 capture_e reset(dup_t::pointer dup_p = dup_t::pointer());
297 capture_e release_frame();
298
300 };
301
306 public:
307 int init(const ::video::config_t &config, const std::string &display_name);
308 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;
309 capture_e release_snapshot() override;
310
311 duplication_t dup;
312 cursor_t cursor;
313 };
314
319 public:
320 int init(const ::video::config_t &config, const std::string &display_name);
321 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;
322 capture_e release_snapshot() override;
323
324 duplication_t dup;
325 sampler_state_t sampler_linear;
326
327 blend_t blend_alpha;
328 blend_t blend_invert;
329 blend_t blend_disable;
330
331 ps_t cursor_ps;
332 vs_t cursor_vs;
333
334 gpu_cursor_t cursor_alpha;
335 gpu_cursor_t cursor_xor;
336
337 texture2d_t old_surface_delayed_destruction;
338 std::chrono::steady_clock::time_point old_surface_timestamp;
339 std::variant<std::monostate, texture2d_t, std::shared_ptr<platf::img_t>> last_frame_variant;
340 };
341
346 winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice uwp_device {nullptr};
347 winrt::Windows::Graphics::Capture::GraphicsCaptureItem item {nullptr};
348 winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool frame_pool {nullptr};
349 winrt::Windows::Graphics::Capture::GraphicsCaptureSession capture_session {nullptr};
350 winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame produced_frame {nullptr};
351 winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame consumed_frame {nullptr};
352 SRWLOCK frame_lock = SRWLOCK_INIT;
353 CONDITION_VARIABLE frame_present_cv;
354
355 void on_frame_arrived(winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender, winrt::Windows::Foundation::IInspectable const &);
356
357 public:
360
361 int init(display_base_t *display, const ::video::config_t &config);
362 capture_e next_frame(std::chrono::milliseconds timeout, ID3D11Texture2D **out, uint64_t &out_time);
363 capture_e release_frame();
364 int set_cursor_visible(bool);
365 };
366
371 wgc_capture_t dup;
372
373 public:
374 int init(const ::video::config_t &config, const std::string &display_name);
375 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;
376 capture_e release_snapshot() override;
377 };
378
383 wgc_capture_t dup;
384
385 public:
386 int init(const ::video::config_t &config, const std::string &display_name);
387 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;
388 capture_e release_snapshot() override;
389 };
390} // namespace platf::dxgi
Definition common.h:467
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:476
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:485
Definition display.h:158
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:206
_D3DKMT_SCHEDULINGPRIORITYCLASS
Definition display.h:184
@ D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL
Normal priority class.
Definition display.h:187
@ D3DKMT_SCHEDULINGPRIORITYCLASS_BELOW_NORMAL
Below normal priority class.
Definition display.h:186
@ D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH
High priority class.
Definition display.h:189
@ D3DKMT_SCHEDULINGPRIORITYCLASS_IDLE
Idle priority class.
Definition display.h:185
@ D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME
Realtime priority class.
Definition display.h:190
@ D3DKMT_SCHEDULINGPRIORITYCLASS_ABOVE_NORMAL
Above normal priority class.
Definition display.h:188
Definition display.h:305
Definition display.h:318
Definition display.h:253
Definition display.h:269
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:370
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:256
Definition display.h:382
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:1646
Definition display.h:288
Definition display.h:85
Definition display.h:345
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:190
Definition utility.h:530
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:985
pix_fmt_e
Definition common.h:238
std::unique_ptr< high_precision_timer > create_high_precision_timer()
Create platform-specific timer capable of high-precision sleep.
Definition misc.cpp:1096
capture_e
Definition common.h:459
@ timeout
Timeout.
Definition display.h:76
Definition display_ram.cpp:15
Declarations for utility functions.
Declarations for video.