/** * @file src/platform/windows/display.h * @brief Declarations for the Windows display backend. */ #pragma once #include #include #include #include #include #include #include #include #include "src/platform/common.h" #include "src/utility.h" #include "src/video.h" namespace platf::dxgi { extern const char *format_str[]; // Add D3D11_CREATE_DEVICE_DEBUG here to enable the D3D11 debug runtime. // You should have a debugger like WinDbg attached to receive debug messages. auto constexpr D3D11_CREATE_DEVICE_FLAGS = 0; template void Release(T *dxgi) { dxgi->Release(); } using factory1_t = util::safe_ptr>; using dxgi_t = util::safe_ptr>; using dxgi1_t = util::safe_ptr>; using device_t = util::safe_ptr>; using device1_t = util::safe_ptr>; using device_ctx_t = util::safe_ptr>; using adapter_t = util::safe_ptr>; using output_t = util::safe_ptr>; using output1_t = util::safe_ptr>; using output5_t = util::safe_ptr>; using output6_t = util::safe_ptr>; using dup_t = util::safe_ptr>; using texture2d_t = util::safe_ptr>; using texture1d_t = util::safe_ptr>; using resource_t = util::safe_ptr>; using resource1_t = util::safe_ptr>; using multithread_t = util::safe_ptr>; using vs_t = util::safe_ptr>; using ps_t = util::safe_ptr>; using blend_t = util::safe_ptr>; using input_layout_t = util::safe_ptr>; using render_target_t = util::safe_ptr>; using shader_res_t = util::safe_ptr>; using buf_t = util::safe_ptr>; using raster_state_t = util::safe_ptr>; using sampler_state_t = util::safe_ptr>; using blob_t = util::safe_ptr>; using depth_stencil_state_t = util::safe_ptr>; using depth_stencil_view_t = util::safe_ptr>; using keyed_mutex_t = util::safe_ptr>; namespace video { using device_t = util::safe_ptr>; using ctx_t = util::safe_ptr>; using processor_t = util::safe_ptr>; using processor_out_t = util::safe_ptr>; using processor_in_t = util::safe_ptr>; using processor_enum_t = util::safe_ptr>; } // namespace video class hwdevice_t; struct cursor_t { std::vector img_data; DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info; int x, y; bool visible; }; class gpu_cursor_t { public: gpu_cursor_t(): cursor_view { 0, 0, 0, 0, 0.0f, 1.0f } {}; void set_pos(LONG topleft_x, LONG topleft_y, LONG display_width, LONG display_height, DXGI_MODE_ROTATION display_rotation, bool visible) { this->topleft_x = topleft_x; this->topleft_y = topleft_y; this->display_width = display_width; this->display_height = display_height; this->display_rotation = display_rotation; this->visible = visible; update_viewport(); } void set_texture(LONG texture_width, LONG texture_height, texture2d_t &&texture) { this->texture = std::move(texture); this->texture_width = texture_width; this->texture_height = texture_height; update_viewport(); } void update_viewport() { switch (display_rotation) { case DXGI_MODE_ROTATION_UNSPECIFIED: case DXGI_MODE_ROTATION_IDENTITY: cursor_view.TopLeftX = topleft_x; cursor_view.TopLeftY = topleft_y; cursor_view.Width = texture_width; cursor_view.Height = texture_height; break; case DXGI_MODE_ROTATION_ROTATE90: cursor_view.TopLeftX = topleft_y; cursor_view.TopLeftY = display_width - texture_width - topleft_x; cursor_view.Width = texture_height; cursor_view.Height = texture_width; break; case DXGI_MODE_ROTATION_ROTATE180: cursor_view.TopLeftX = display_width - texture_width - topleft_x; cursor_view.TopLeftY = display_height - texture_height - topleft_y; cursor_view.Width = texture_width; cursor_view.Height = texture_height; break; case DXGI_MODE_ROTATION_ROTATE270: cursor_view.TopLeftX = display_height - texture_height - topleft_y; cursor_view.TopLeftY = topleft_x; cursor_view.Width = texture_height; cursor_view.Height = texture_width; break; } } texture2d_t texture; LONG texture_width; LONG texture_height; LONG topleft_x; LONG topleft_y; LONG display_width; LONG display_height; DXGI_MODE_ROTATION display_rotation; shader_res_t input_res; D3D11_VIEWPORT cursor_view; bool visible; }; class display_base_t: public display_t { public: int init(const ::video::config_t &config, const std::string &display_name); 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; factory1_t factory; adapter_t adapter; output_t output; device_t device; device_ctx_t device_ctx; DXGI_RATIONAL display_refresh_rate; int display_refresh_rate_rounded; DXGI_MODE_ROTATION display_rotation = DXGI_MODE_ROTATION_UNSPECIFIED; int width_before_rotation; int height_before_rotation; int client_frame_rate; DXGI_FORMAT capture_format; D3D_FEATURE_LEVEL feature_level; std::unique_ptr timer = create_high_precision_timer(); typedef enum _D3DKMT_SCHEDULINGPRIORITYCLASS { D3DKMT_SCHEDULINGPRIORITYCLASS_IDLE, ///< Idle priority class D3DKMT_SCHEDULINGPRIORITYCLASS_BELOW_NORMAL, ///< Below normal priority class D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL, ///< Normal priority class D3DKMT_SCHEDULINGPRIORITYCLASS_ABOVE_NORMAL, ///< Above normal priority class D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH, ///< High priority class D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME ///< Realtime priority class } D3DKMT_SCHEDULINGPRIORITYCLASS; typedef UINT D3DKMT_HANDLE; typedef struct _D3DKMT_OPENADAPTERFROMLUID { LUID AdapterLuid; D3DKMT_HANDLE hAdapter; } D3DKMT_OPENADAPTERFROMLUID; typedef struct _D3DKMT_WDDM_2_7_CAPS { union { struct { UINT HwSchSupported : 1; UINT HwSchEnabled : 1; UINT HwSchEnabledByDefault : 1; UINT IndependentVidPnVSyncControl : 1; UINT Reserved : 28; }; UINT Value; }; } D3DKMT_WDDM_2_7_CAPS; typedef struct _D3DKMT_QUERYADAPTERINFO { D3DKMT_HANDLE hAdapter; UINT Type; VOID *pPrivateDriverData; UINT PrivateDriverDataSize; } D3DKMT_QUERYADAPTERINFO; const UINT KMTQAITYPE_WDDM_2_7_CAPS = 70; typedef struct _D3DKMT_CLOSEADAPTER { D3DKMT_HANDLE hAdapter; } D3DKMT_CLOSEADAPTER; typedef NTSTATUS(WINAPI *PD3DKMTSetProcessSchedulingPriorityClass)(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS); typedef NTSTATUS(WINAPI *PD3DKMTOpenAdapterFromLuid)(D3DKMT_OPENADAPTERFROMLUID *); typedef NTSTATUS(WINAPI *PD3DKMTQueryAdapterInfo)(D3DKMT_QUERYADAPTERINFO *); typedef NTSTATUS(WINAPI *PD3DKMTCloseAdapter)(D3DKMT_CLOSEADAPTER *); virtual bool is_hdr() override; virtual bool get_hdr_metadata(SS_HDR_METADATA &metadata) override; const char * dxgi_format_to_string(DXGI_FORMAT format); const char * colorspace_to_string(DXGI_COLOR_SPACE_TYPE type); virtual std::vector get_supported_capture_formats() = 0; protected: int get_pixel_pitch() { return (capture_format == DXGI_FORMAT_R16G16B16A16_FLOAT) ? 8 : 4; } virtual capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) = 0; virtual capture_e release_snapshot() = 0; virtual int complete_img(img_t *img, bool dummy) = 0; }; /** * Display component for devices that use software encoders. */ class display_ram_t: public display_base_t { public: std::shared_ptr alloc_img() override; int dummy_img(img_t *img) override; int complete_img(img_t *img, bool dummy) override; std::vector get_supported_capture_formats() override; std::unique_ptr make_avcodec_encode_device(pix_fmt_e pix_fmt) override; D3D11_MAPPED_SUBRESOURCE img_info; texture2d_t texture; }; /** * Display component for devices that use hardware encoders. */ class display_vram_t: public display_base_t, public std::enable_shared_from_this { public: std::shared_ptr alloc_img() override; int dummy_img(img_t *img_base) override; int complete_img(img_t *img_base, bool dummy) override; std::vector get_supported_capture_formats() override; bool is_codec_supported(std::string_view name, const ::video::config_t &config) override; std::unique_ptr make_avcodec_encode_device(pix_fmt_e pix_fmt) override; std::unique_ptr make_nvenc_encode_device(pix_fmt_e pix_fmt) override; std::atomic next_image_id; }; /** * Display duplicator that uses the DirectX Desktop Duplication API. */ class duplication_t { public: dup_t dup; bool has_frame {}; std::chrono::steady_clock::time_point last_protected_content_warning_time {}; int init(display_base_t *display, const ::video::config_t &config); capture_e next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p); capture_e reset(dup_t::pointer dup_p = dup_t::pointer()); capture_e release_frame(); ~duplication_t(); }; /** * Display backend that uses DDAPI with a software encoder. */ class display_ddup_ram_t: public display_ram_t { public: int init(const ::video::config_t &config, const std::string &display_name); capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override; capture_e release_snapshot() override; duplication_t dup; cursor_t cursor; }; /** * Display backend that uses DDAPI with a hardware encoder. */ class display_ddup_vram_t: public display_vram_t { public: int init(const ::video::config_t &config, const std::string &display_name); capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override; capture_e release_snapshot() override; duplication_t dup; sampler_state_t sampler_linear; blend_t blend_alpha; blend_t blend_invert; blend_t blend_disable; ps_t cursor_ps; vs_t cursor_vs; gpu_cursor_t cursor_alpha; gpu_cursor_t cursor_xor; texture2d_t old_surface_delayed_destruction; std::chrono::steady_clock::time_point old_surface_timestamp; std::variant> last_frame_variant; }; /** * Display duplicator that uses the Windows.Graphics.Capture API. */ class wgc_capture_t { winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice uwp_device { nullptr }; winrt::Windows::Graphics::Capture::GraphicsCaptureItem item { nullptr }; winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool frame_pool { nullptr }; winrt::Windows::Graphics::Capture::GraphicsCaptureSession capture_session { nullptr }; winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame produced_frame { nullptr }, consumed_frame { nullptr }; SRWLOCK frame_lock = SRWLOCK_INIT; CONDITION_VARIABLE frame_present_cv; void on_frame_arrived(winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender, winrt::Windows::Foundation::IInspectable const &); public: wgc_capture_t(); ~wgc_capture_t(); int init(display_base_t *display, const ::video::config_t &config); capture_e next_frame(std::chrono::milliseconds timeout, ID3D11Texture2D **out, uint64_t &out_time); capture_e release_frame(); int set_cursor_visible(bool); }; /** * Display backend that uses Windows.Graphics.Capture with a software encoder. */ class display_wgc_ram_t: public display_ram_t { wgc_capture_t dup; public: int init(const ::video::config_t &config, const std::string &display_name); capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override; capture_e release_snapshot() override; }; /** * Display backend that uses Windows.Graphics.Capture with a hardware encoder. */ class display_wgc_vram_t: public display_vram_t { wgc_capture_t dup; public: int init(const ::video::config_t &config, const std::string &display_name); capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override; capture_e release_snapshot() override; }; } // namespace platf::dxgi