diff --git a/CMakeLists.txt b/CMakeLists.txt index bca991e4..27551cba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,8 @@ if(WIN32) set(PLATFORM_LIBRARIES winmm wsock32 - ws2_32) + ws2_32 + d3d11 dxgi) else() find_package(X11 REQUIRED) set(PLATFORM_TARGET_FILES diff --git a/README.txt b/README.txt index 6566dd53..332807c7 100644 --- a/README.txt +++ b/README.txt @@ -42,6 +42,7 @@ Note: Credits: * Simple-Web-Server [https://gitlab.com/eidheim/Simple-Web-Server] * Moonlight [https://github.com/moonlight-stream] + * Looking-Glass [https://github.com/gnif/LookingGlass] (For showing me how to properly capture frames on Windows, saving me a lot of time :) diff --git a/sunshine/platform/windows.cpp b/sunshine/platform/windows.cpp index bde0f880..0ccf6d13 100644 --- a/sunshine/platform/windows.cpp +++ b/sunshine/platform/windows.cpp @@ -1,16 +1,295 @@ +#include +#include + +#include +#include +#include +#include + #include "common.h" namespace platf { using namespace std::literals; std::string get_local_ip() { return "192.168.0.119"s; } -class dxi_display_t : public display_t { +namespace dxgi { +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 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 dup_t = util::safe_ptr>; +using texture2d_t = util::safe_ptr>; + +extern const char *format_str[]; + +struct texture_t { + enum state_e { + UNUSED, + PENDING_MAP, + MAPPED + } state; + + texture2d_t tex; +}; + +class display_t : public ::platf::display_t { +public: std::unique_ptr snapshot(bool cursor) override { return nullptr; } + + int init() { + /* Uncomment when use of IDXGIOutput5 is implemented + std::call_once(windows_cpp_once_flag, []() { + DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); + const auto DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = ((DPI_AWARENESS_CONTEXT)-4); + + typedef BOOL (*User32_SetProcessDpiAwarenessContext)(DPI_AWARENESS_CONTEXT value); + + auto user32 = LoadLibraryA("user32.dll"); + auto f = (User32_SetProcessDpiAwarenessContext)GetProcAddress(user32, "SetProcessDpiAwarenessContext"); + if(f) { + f(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + } + + FreeLibrary(user32); + }); +*/ + dxgi::factory1_t::pointer factory_p {}; + dxgi::adapter_t::pointer adapter_p {}; + dxgi::output_t::pointer output_p {}; + dxgi::device_t::pointer device_p {}; + dxgi::device_ctx_t::pointer device_ctx_p {}; + + HRESULT status; + + status = CreateDXGIFactory1(IID_IDXGIFactory1, (void**)&factory_p); + factory.reset(factory_p); + if(FAILED(status)) { + std::cout << "Error: Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; + return -1; + } + + + for(int x = 0; factory_p->EnumAdapters1(x, &adapter_p) != DXGI_ERROR_NOT_FOUND; ++x) { + dxgi::adapter_t adapter { adapter_p }; + + for(int y = 0; adapter->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) { + dxgi::output_t output_tmp {output_p }; + + DXGI_OUTPUT_DESC desc; + output_tmp->GetDesc(&desc); + + if(desc.AttachedToDesktop) { + output = std::move(output_tmp); + + width = desc.DesktopCoordinates.right - desc.DesktopCoordinates.left; + height = desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top; + } + } + + if(output) { + adapter = std::move(adapter); + break; + } + } + + if(!output) { + std::cout << "Error: Failed to locate an output device"sv << std::endl; + return -1; + } + + D3D_FEATURE_LEVEL featureLevels[] { + D3D_FEATURE_LEVEL_12_1, + D3D_FEATURE_LEVEL_12_0, + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1 + }; + + status = adapter->QueryInterface(IID_IDXGIAdapter, (void**)&adapter_p); + if(FAILED(status)) { + std::cout << "Error: Failed to query IDXGIAdapter interface"sv <Release(); + + device.reset(device_p); + device_ctx.reset(device_ctx_p); + if(FAILED(status)) { + std::cout << "Error: Failed to create D3D11 device [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; + + return -1; + } + + DXGI_ADAPTER_DESC adapter_desc; + adapter->GetDesc(&adapter_desc); + std::cout + << "Device Description : "sv << adapter_desc.Description << std::endl + << "Device Vendor ID : 0x"sv << util::hex(adapter_desc.VendorId).to_string_view() << std::endl + << "Device Device ID : 0x"sv << util::hex(adapter_desc.DeviceId).to_string_view() << std::endl + << "Device Video Mem : "sv << adapter_desc.DedicatedVideoMemory / 1048576 << " MiB"sv << std::endl + << "Device Sys Mem : "sv << adapter_desc.DedicatedSystemMemory / 1048576 << " MiB"sv << std::endl + << "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl + << "Feature Level : 0x"sv << util::hex(feature_level).to_string_view() << std::endl + << "Capture size : "sv << width << 'x' << height << std::endl; + + // Bump up thread priority + { + dxgi::dxgi_t::pointer dxgi_p; + status = device->QueryInterface(IID_IDXGIDevice, (void**)&dxgi_p); + dxgi::dxgi_t dxgi { dxgi_p }; + + if(FAILED(status)) { + std::cout << "Error: Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; + return -1; + } + + dxgi->SetGPUThreadPriority(7); + } + + // Try to reduce latency + { + dxgi::dxgi1_t::pointer dxgi_p; + status = device->QueryInterface(IID_IDXGIDevice, (void**)&dxgi_p); + dxgi::dxgi1_t dxgi { dxgi_p }; + + if(FAILED(status)) { + std::cout << "Error: Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; + return -1; + } + + dxgi->SetMaximumFrameLatency(1); + } + + //TODO: Use IDXGIOutput5 for improved performance + { + dxgi::output1_t::pointer output1_p {}; + status = output->QueryInterface(IID_IDXGIOutput1, (void**)&output1_p); + dxgi::output1_t output1 {output1_p }; + + if(FAILED(status)) { + std::cout << "Error: Failed to query IDXGIOutput1 from the output"sv << std::endl; + return -1; + } + + // We try this twice, in case we still get an error on reinitialization + for(int x = 0; x < 2; ++x) { + dxgi::dup_t::pointer dup_p {}; + status = output1->DuplicateOutput((IUnknown*)device.get(), &dup_p); + if(SUCCEEDED(status)) { + dup.reset(dup_p); + break; + } + std::this_thread::sleep_for(200ms); + } + + if(FAILED(status)) { + std::cout << "Error: DuplicateOutput Failed [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; + return -1; + } + } + + DXGI_OUTDUPL_DESC dup_desc; + dup->GetDesc(&dup_desc); + + std::cout << "Source format ["sv << format_str[dup_desc.ModeDesc.Format] << ']' << std::endl; + + D3D11_TEXTURE2D_DESC t {}; + t.Width = width; + t.Height = height; + t.MipLevels = 1; + t.ArraySize = 1; + t.SampleDesc.Count = 1; + t.Usage = D3D11_USAGE_STAGING; + t.Format = dup_desc.ModeDesc.Format; + t.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + + textures.resize(3); + for(auto &texture : textures) { + dxgi::texture2d_t::pointer tex_p {}; + status = device->CreateTexture2D(&t, nullptr, &tex_p); + texture.tex.reset(tex_p); + + if(FAILED(status)) { + std::cout << "Error: Failed to create texture [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; + return -1; + } + } + + // map the texture simply to get the pitch and stride + D3D11_MAPPED_SUBRESOURCE mapping; + + status = device_ctx->Map((ID3D11Resource *)textures[0].tex.get(), 0, D3D11_MAP_READ, 0, &mapping); + if(FAILED(status)) { + std::cout << "Error: Failed to map the texture [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; + return -1; + } + + pitch = (int)mapping.RowPitch; + stride = (int)mapping.RowPitch / 4; + + return 0; + } + + int deinit() { + for(auto &texture : textures) { + texture.state = texture_t::UNUSED; + texture.tex.reset(); + } + + dup.reset(); + device_ctx.reset(); + device.reset(); + output.reset(); + adapter.reset(); + factory.reset(); + } + + factory1_t factory; + adapter_t adapter; + output_t output; + device_t device; + device_ctx_t device_ctx; + dup_t dup; + + std::vector textures; + + int width, height; + int pitch, stride; + + D3D_FEATURE_LEVEL feature_level; }; +} class dummy_mic_t : public mic_t { +public: std::vector sample(std::size_t sample_size) override { std::vector sample_buf; sample_buf.resize(sample_size); @@ -22,8 +301,16 @@ class dummy_mic_t : public mic_t { std::unique_ptr microphone() { return std::unique_ptr { new dummy_mic_t {} }; } + +//std::once_flag windows_cpp_once_flag; std::unique_ptr display() { - return std::unique_ptr { new dxi_display_t {} }; + auto disp = std::make_unique(); + + if(disp->init()) { + return nullptr; + } + + return disp; } input_t input() { @@ -59,4 +346,131 @@ void sync(input_t &input) {} } void freeInput(void*) {} + +namespace dxgi { +const char *format_str[] = { + "DXGI_FORMAT_UNKNOWN", + "DXGI_FORMAT_R32G32B32A32_TYPELESS", + "DXGI_FORMAT_R32G32B32A32_FLOAT", + "DXGI_FORMAT_R32G32B32A32_UINT", + "DXGI_FORMAT_R32G32B32A32_SINT", + "DXGI_FORMAT_R32G32B32_TYPELESS", + "DXGI_FORMAT_R32G32B32_FLOAT", + "DXGI_FORMAT_R32G32B32_UINT", + "DXGI_FORMAT_R32G32B32_SINT", + "DXGI_FORMAT_R16G16B16A16_TYPELESS", + "DXGI_FORMAT_R16G16B16A16_FLOAT", + "DXGI_FORMAT_R16G16B16A16_UNORM", + "DXGI_FORMAT_R16G16B16A16_UINT", + "DXGI_FORMAT_R16G16B16A16_SNORM", + "DXGI_FORMAT_R16G16B16A16_SINT", + "DXGI_FORMAT_R32G32_TYPELESS", + "DXGI_FORMAT_R32G32_FLOAT", + "DXGI_FORMAT_R32G32_UINT", + "DXGI_FORMAT_R32G32_SINT", + "DXGI_FORMAT_R32G8X24_TYPELESS", + "DXGI_FORMAT_D32_FLOAT_S8X24_UINT", + "DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS", + "DXGI_FORMAT_X32_TYPELESS_G8X24_UINT", + "DXGI_FORMAT_R10G10B10A2_TYPELESS", + "DXGI_FORMAT_R10G10B10A2_UNORM", + "DXGI_FORMAT_R10G10B10A2_UINT", + "DXGI_FORMAT_R11G11B10_FLOAT", + "DXGI_FORMAT_R8G8B8A8_TYPELESS", + "DXGI_FORMAT_R8G8B8A8_UNORM", + "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "DXGI_FORMAT_R8G8B8A8_UINT", + "DXGI_FORMAT_R8G8B8A8_SNORM", + "DXGI_FORMAT_R8G8B8A8_SINT", + "DXGI_FORMAT_R16G16_TYPELESS", + "DXGI_FORMAT_R16G16_FLOAT", + "DXGI_FORMAT_R16G16_UNORM", + "DXGI_FORMAT_R16G16_UINT", + "DXGI_FORMAT_R16G16_SNORM", + "DXGI_FORMAT_R16G16_SINT", + "DXGI_FORMAT_R32_TYPELESS", + "DXGI_FORMAT_D32_FLOAT", + "DXGI_FORMAT_R32_FLOAT", + "DXGI_FORMAT_R32_UINT", + "DXGI_FORMAT_R32_SINT", + "DXGI_FORMAT_R24G8_TYPELESS", + "DXGI_FORMAT_D24_UNORM_S8_UINT", + "DXGI_FORMAT_R24_UNORM_X8_TYPELESS", + "DXGI_FORMAT_X24_TYPELESS_G8_UINT", + "DXGI_FORMAT_R8G8_TYPELESS", + "DXGI_FORMAT_R8G8_UNORM", + "DXGI_FORMAT_R8G8_UINT", + "DXGI_FORMAT_R8G8_SNORM", + "DXGI_FORMAT_R8G8_SINT", + "DXGI_FORMAT_R16_TYPELESS", + "DXGI_FORMAT_R16_FLOAT", + "DXGI_FORMAT_D16_UNORM", + "DXGI_FORMAT_R16_UNORM", + "DXGI_FORMAT_R16_UINT", + "DXGI_FORMAT_R16_SNORM", + "DXGI_FORMAT_R16_SINT", + "DXGI_FORMAT_R8_TYPELESS", + "DXGI_FORMAT_R8_UNORM", + "DXGI_FORMAT_R8_UINT", + "DXGI_FORMAT_R8_SNORM", + "DXGI_FORMAT_R8_SINT", + "DXGI_FORMAT_A8_UNORM", + "DXGI_FORMAT_R1_UNORM", + "DXGI_FORMAT_R9G9B9E5_SHAREDEXP", + "DXGI_FORMAT_R8G8_B8G8_UNORM", + "DXGI_FORMAT_G8R8_G8B8_UNORM", + "DXGI_FORMAT_BC1_TYPELESS", + "DXGI_FORMAT_BC1_UNORM", + "DXGI_FORMAT_BC1_UNORM_SRGB", + "DXGI_FORMAT_BC2_TYPELESS", + "DXGI_FORMAT_BC2_UNORM", + "DXGI_FORMAT_BC2_UNORM_SRGB", + "DXGI_FORMAT_BC3_TYPELESS", + "DXGI_FORMAT_BC3_UNORM", + "DXGI_FORMAT_BC3_UNORM_SRGB", + "DXGI_FORMAT_BC4_TYPELESS", + "DXGI_FORMAT_BC4_UNORM", + "DXGI_FORMAT_BC4_SNORM", + "DXGI_FORMAT_BC5_TYPELESS", + "DXGI_FORMAT_BC5_UNORM", + "DXGI_FORMAT_BC5_SNORM", + "DXGI_FORMAT_B5G6R5_UNORM", + "DXGI_FORMAT_B5G5R5A1_UNORM", + "DXGI_FORMAT_B8G8R8A8_UNORM", + "DXGI_FORMAT_B8G8R8X8_UNORM", + "DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM", + "DXGI_FORMAT_B8G8R8A8_TYPELESS", + "DXGI_FORMAT_B8G8R8A8_UNORM_SRGB", + "DXGI_FORMAT_B8G8R8X8_TYPELESS", + "DXGI_FORMAT_B8G8R8X8_UNORM_SRGB", + "DXGI_FORMAT_BC6H_TYPELESS", + "DXGI_FORMAT_BC6H_UF16", + "DXGI_FORMAT_BC6H_SF16", + "DXGI_FORMAT_BC7_TYPELESS", + "DXGI_FORMAT_BC7_UNORM", + "DXGI_FORMAT_BC7_UNORM_SRGB", + "DXGI_FORMAT_AYUV", + "DXGI_FORMAT_Y410", + "DXGI_FORMAT_Y416", + "DXGI_FORMAT_NV12", + "DXGI_FORMAT_P010", + "DXGI_FORMAT_P016", + "DXGI_FORMAT_420_OPAQUE", + "DXGI_FORMAT_YUY2", + "DXGI_FORMAT_Y210", + "DXGI_FORMAT_Y216", + "DXGI_FORMAT_NV11", + "DXGI_FORMAT_AI44", + "DXGI_FORMAT_IA44", + "DXGI_FORMAT_P8", + "DXGI_FORMAT_A8P8", + "DXGI_FORMAT_B4G4R4A4_UNORM", + + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + "DXGI_FORMAT_P208", + "DXGI_FORMAT_V208", + "DXGI_FORMAT_V408" +}; +} }