diff --git a/CMakeLists.txt b/CMakeLists.txt index 68fb7c09..79c46d81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,8 @@ find_package(Boost COMPONENTS log filesystem REQUIRED) if(WIN32) set(PLATFORM_TARGET_FILES - sunshine/platform/windows.cpp) + sunshine/platform/windows.cpp + sunshine/platform/windows_dxgi.cpp) set(PLATFORM_LIBRARIES winmm wsock32 diff --git a/sunshine/platform/windows.cpp b/sunshine/platform/windows.cpp index 300524d5..50bc1a44 100755 --- a/sunshine/platform/windows.cpp +++ b/sunshine/platform/windows.cpp @@ -1,577 +1,11 @@ #include -#include -#include -#include -#include - -#include "sunshine/main.h" #include "common.h" namespace platf { using namespace std::literals; std::string get_local_ip() { return "192.168.0.119"s; } -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>; -using resource_t = util::safe_ptr>; - -extern const char *format_str[]; - -class duplication_t { -public: - dup_t dup; - bool has_frame {}; - - capture_e next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, resource_t::pointer *res_p) { - auto capture_status = release_frame(); - if(capture_status != capture_e::ok) { - return capture_status; - } - - auto status = dup->AcquireNextFrame(1000, &frame_info, res_p); - - switch(status) { - case S_OK: - has_frame = true; - return capture_e::ok; - case DXGI_ERROR_WAIT_TIMEOUT: - return capture_e::timeout; - case WAIT_ABANDONED: - case DXGI_ERROR_ACCESS_DENIED: - return capture_e::reinit; - default: - BOOST_LOG(error) << "Couldn't acquire next frame [0x"sv << util::hex(status).to_string_view(); - return capture_e::error; - } - } - - capture_e reset(dup_t::pointer dup_p = dup_t::pointer()) { - auto capture_status = release_frame(); - - dup.reset(dup_p); - - return capture_status; - } - - capture_e release_frame() { - if(!has_frame) { - return capture_e::ok; - } - - auto status = dup->ReleaseFrame(); - switch (status) { - case S_OK: - has_frame = false; - return capture_e::ok; - case DXGI_ERROR_WAIT_TIMEOUT: - return capture_e::timeout; - case WAIT_ABANDONED: - case DXGI_ERROR_ACCESS_DENIED: - return capture_e::reinit; - default: - BOOST_LOG(error) << "Couldn't release frame [0x"sv << util::hex(status).to_string_view(); - return capture_e::error; - } - } - - ~duplication_t() { - release_frame(); - } -}; - -class display_t; -struct img_t : public ::platf::img_t { - ~img_t() override { - delete[] data; - data = nullptr; - } -}; - -struct cursor_t { - std::vector img_data; - - DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info; - int x, y; - bool visible; -}; - -void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) { - int height = cursor.shape_info.Height / 2; - int width = cursor.shape_info.Width; - int pitch = cursor.shape_info.Pitch; - - // img cursor.y < 0, skip parts of the cursor.img_data - auto cursor_skip_y = std::min(0, cursor.y); - auto cursor_skip_x = std::min(0, cursor.x); - auto img_skip_y = std::max(0, cursor.y); - auto img_skip_x = std::max(0, cursor.x); - - if(cursor_skip_y > height || cursor_skip_x > width) { - return; - } - - height -= cursor_skip_y; - width -= cursor_skip_x; - auto cursor_img_data = cursor.img_data.data() + cursor_skip_y * pitch; - - int delta_height = std::min(height, std::max(0, img.height - img_skip_y)); - int delta_width = std::min(width, std::max(0, img.width - img_skip_x)); - - auto pixels_per_byte = width / pitch; - auto bytes_per_row = delta_width / pixels_per_byte; - - auto img_data = (int*)img.data; - for(int i = 0; i < delta_height; ++i) { - auto and_mask = &cursor_img_data[i * pitch]; - auto xor_mask = &cursor_img_data[(i + height) * pitch]; - - auto img_pixel_p = &img_data[(i + img_skip_y) * img.width + img_skip_x]; - - auto skip_y = cursor_skip_y; - for(int x = 0; x < bytes_per_row; ++x) { - for(auto bit = 0u; bit < 8; ++bit) { - if(skip_y > 0) { - --skip_y; - - continue; - } - - int and_ = *and_mask & (1 << (7 - bit)) ? -1 : 0; - int xor_ = *xor_mask & (1 << (7 - bit)) ? -1 : 0; - - *img_pixel_p &= and_; - *img_pixel_p ^= xor_; - - ++img_pixel_p; - } - - ++and_mask; - ++xor_mask; - } - } -} - -void blend_cursor_color(const cursor_t &cursor, img_t &img) { - int height = cursor.shape_info.Height; - int width = cursor.shape_info.Width; - int pitch = cursor.shape_info.Pitch; - - // img cursor.y < 0, skip parts of the cursor.img_data - auto cursor_skip_y = std::min(0, cursor.y); - auto cursor_skip_x = std::min(0, cursor.x); - auto img_skip_y = std::max(0, cursor.y); - auto img_skip_x = std::max(0, cursor.x); - - if(cursor_skip_y > height || cursor_skip_x > width) { - return; - } - - height -= cursor_skip_y; - width -= cursor_skip_x; - auto cursor_img_data = (int*)&cursor.img_data[cursor_skip_y * pitch]; - - int delta_height = std::min(height, std::max(0, img.height - img_skip_y)); - int delta_width = std::min(width, std::max(0, img.width - img_skip_x)); - - auto img_data = (int*)img.data; - - for(int i = 0; i < delta_height; ++i) { - auto cursor_begin = &cursor_img_data[i * width + cursor_skip_x]; - auto cursor_end = &cursor_begin[delta_width]; - - auto img_pixel_p = &img_data[(i + img_skip_y) * img.width + img_skip_x]; - std::for_each(cursor_begin, cursor_end, [&](int cursor_pixel) { - auto colors_out = (std::uint8_t*)&cursor_pixel; - auto colors_in = (std::uint8_t*)img_pixel_p; - - //TODO: When use of IDXGIOutput5 is implemented, support different color formats - auto alpha = colors_out[3]; - if(alpha == 255) { - *img_pixel_p = cursor_pixel; - } - else { - colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255/2) / 255; - colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255/2) / 255; - colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255/2) / 255; - } - ++img_pixel_p; - }); - } -} - -void blend_cursor(const cursor_t &cursor, img_t &img) { - switch(cursor.shape_info.Type) { - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: - blend_cursor_color(cursor, img); - break; - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: - blend_cursor_monochrome(cursor, img); - break; - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: - default: - BOOST_LOG(warning) << "Unsupported cursor format ["sv << cursor.shape_info.Type << ']'; - } -} - -class display_t : public ::platf::display_t { -public: - capture_e snapshot(std::unique_ptr<::platf::img_t> &img_base, bool cursor_visible) override { - auto img = (img_t *) img_base.get(); - HRESULT status; - - DXGI_OUTDUPL_FRAME_INFO frame_info; - - resource_t::pointer res_p {}; - auto capture_status = dup.next_frame(frame_info, &res_p); - resource_t res{res_p}; - - if (capture_status != capture_e::ok) { - return capture_status; - } - - if (frame_info.PointerShapeBufferSize > 0) { - auto &img_data = cursor.img_data; - - img_data.resize(frame_info.PointerShapeBufferSize); - - UINT dummy; - status = dup.dup->GetFramePointerShape(img_data.size(), img_data.data(), &dummy, &cursor.shape_info); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']'; - - return capture_e::error; - } - } - - if(frame_info.LastMouseUpdateTime.QuadPart) { - cursor.x = frame_info.PointerPosition.Position.x; - cursor.y = frame_info.PointerPosition.Position.y; - cursor.visible = frame_info.PointerPosition.Visible; - } - - // If frame has been updated - if (frame_info.LastPresentTime.QuadPart != 0) { - { - texture2d_t::pointer src_p {}; - status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src_p); - texture2d_t src{src_p}; - - if (FAILED(status)) { - BOOST_LOG(error) << "Couldn't query interface [0x"sv << util::hex(status).to_string_view() << ']'; - return capture_e::error; - } - - //Copy from GPU to CPU - device_ctx->CopyResource(texture.get(), src.get()); - } - - if(current_img.pData) { - device_ctx->Unmap(texture.get(), 0); - current_img.pData = nullptr; - } - - status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, ¤t_img); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']'; - - return capture_e::error; - } - } - - const bool update_flag = - frame_info.LastMouseUpdateTime.QuadPart || - frame_info.LastPresentTime.QuadPart != 0 || - frame_info.PointerShapeBufferSize > 0; - - - if(!update_flag) { - return capture_e::timeout; - } - - if(img->width != width || img->height != height) { - delete[] img->data; - img->data = new std::uint8_t[height * current_img.RowPitch]; - - img->width = width; - img->height = height; - } - - std::copy_n((std::uint8_t*)current_img.pData, height * current_img.RowPitch, (std::uint8_t*)img->data); - if(cursor_visible && cursor.visible) { - blend_cursor(cursor, *img); - } - - return capture_e::ok; - } - - /* - * Called when access is lost. Dup must be reinitialized - */ - int reinit() override { - HRESULT status; - - dup.reset(); - //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)) { - BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv; - 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)) { - BOOST_LOG(error) << "DuplicateOutput Failed [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - } - - DXGI_OUTDUPL_DESC dup_desc; - dup.dup->GetDesc(&dup_desc); - - format = dup_desc.ModeDesc.Format; - - BOOST_LOG(debug) << "Source format ["sv << format_str[dup_desc.ModeDesc.Format] << ']'; - - 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 = format; - t.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - - dxgi::texture2d_t::pointer tex_p {}; - status = device->CreateTexture2D(&t, nullptr, &tex_p); - - texture.reset(tex_p); - - if(FAILED(status)) { - BOOST_LOG(error) << "Failed to create texture [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - // map the texture simply to get the pitch and stride - status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, ¤t_img); - if(FAILED(status)) { - BOOST_LOG(error) << "Error: Failed to map the texture [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - return 0; - } - - std::unique_ptr<::platf::img_t> alloc_img() override { - auto img = std::make_unique(); - - img->data = nullptr; - img->height = 0; - img->width = 0; - - return img; - } - - 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); - }); -*/ - current_img.pData = nullptr; // current_img is not yet mapped - - 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)) { - BOOST_LOG(error) << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - - for(int x = 0; factory_p->EnumAdapters1(x, &adapter_p) != DXGI_ERROR_NOT_FOUND; ++x) { - dxgi::adapter_t adapter_tmp { adapter_p }; - - for(int y = 0; adapter_tmp->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_tmp); - break; - } - } - - if(!output) { - BOOST_LOG(error) << "Failed to locate an output device"sv; - 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)) { - BOOST_LOG(error) << "Failed to query IDXGIAdapter interface"sv; - - return -1; - } - - status = D3D11CreateDevice( - adapter_p, - D3D_DRIVER_TYPE_UNKNOWN, - nullptr, - D3D11_CREATE_DEVICE_VIDEO_SUPPORT, - featureLevels, sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), - D3D11_SDK_VERSION, - &device_p, - &feature_level, - &device_ctx_p); - - adapter_p->Release(); - - device.reset(device_p); - device_ctx.reset(device_ctx_p); - if(FAILED(status)) { - BOOST_LOG(error) << "Failed to create D3D11 device [0x"sv << util::hex(status).to_string_view() << ']'; - - return -1; - } - - DXGI_ADAPTER_DESC adapter_desc; - adapter->GetDesc(&adapter_desc); - BOOST_LOG(info) - << "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; - - // 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)) { - BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']'; - 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)) { - BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - dxgi->SetMaximumFrameLatency(1); - } - - return reinit(); - } - - ~display_t() override { - if(current_img.pData) { - device_ctx->Unmap(texture.get(), 0); - current_img.pData = nullptr; - } - } - - factory1_t factory; - adapter_t adapter; - output_t output; - device_t device; - device_ctx_t device_ctx; - duplication_t dup; - cursor_t cursor; - texture2d_t texture; - - int width, height; - - DXGI_FORMAT format; - D3D_FEATURE_LEVEL feature_level; - D3D11_MAPPED_SUBRESOURCE current_img; -}; - -} - class dummy_mic_t : public mic_t { public: std::vector sample(std::size_t sample_size) override { @@ -586,17 +20,6 @@ std::unique_ptr microphone() { return std::unique_ptr { new dummy_mic_t {} }; } -//std::once_flag windows_cpp_once_flag; -std::shared_ptr display() { - auto disp = std::make_unique(); - - if(disp->init()) { - return nullptr; - } - - return disp; -} - input_t input() { return nullptr; } @@ -630,131 +53,4 @@ 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" -}; -} } diff --git a/sunshine/platform/windows_dxgi.cpp b/sunshine/platform/windows_dxgi.cpp new file mode 100644 index 00000000..14d6f3a1 --- /dev/null +++ b/sunshine/platform/windows_dxgi.cpp @@ -0,0 +1,711 @@ +// +// Created by loki on 1/12/20. +// + +#include +#include +#include +#include + +#include "sunshine/main.h" +#include "common.h" + +namespace platf { +using namespace std::literals; +} +namespace platf::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>; +using resource_t = util::safe_ptr>; + +extern const char *format_str[]; + +class duplication_t { +public: + dup_t dup; + bool has_frame {}; + + capture_e next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, resource_t::pointer *res_p) { + auto capture_status = release_frame(); + if(capture_status != capture_e::ok) { + return capture_status; + } + + auto status = dup->AcquireNextFrame(1000, &frame_info, res_p); + + switch(status) { + case S_OK: + has_frame = true; + return capture_e::ok; + case DXGI_ERROR_WAIT_TIMEOUT: + return capture_e::timeout; + case WAIT_ABANDONED: + case DXGI_ERROR_ACCESS_DENIED: + return capture_e::reinit; + default: + BOOST_LOG(error) << "Couldn't acquire next frame [0x"sv << util::hex(status).to_string_view(); + return capture_e::error; + } + } + + capture_e reset(dup_t::pointer dup_p = dup_t::pointer()) { + auto capture_status = release_frame(); + + dup.reset(dup_p); + + return capture_status; + } + + capture_e release_frame() { + if(!has_frame) { + return capture_e::ok; + } + + auto status = dup->ReleaseFrame(); + switch (status) { + case S_OK: + has_frame = false; + return capture_e::ok; + case DXGI_ERROR_WAIT_TIMEOUT: + return capture_e::timeout; + case WAIT_ABANDONED: + case DXGI_ERROR_ACCESS_DENIED: + return capture_e::reinit; + default: + BOOST_LOG(error) << "Couldn't release frame [0x"sv << util::hex(status).to_string_view(); + return capture_e::error; + } + } + + ~duplication_t() { + release_frame(); + } +}; + +class display_t; +struct img_t : public ::platf::img_t { + ~img_t() override { + delete[] data; + data = nullptr; + } +}; + +struct cursor_t { + std::vector img_data; + + DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info; + int x, y; + bool visible; +}; + +void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) { + int height = cursor.shape_info.Height / 2; + int width = cursor.shape_info.Width; + int pitch = cursor.shape_info.Pitch; + + // img cursor.y < 0, skip parts of the cursor.img_data + auto cursor_skip_y = std::min(0, cursor.y); + auto cursor_skip_x = std::min(0, cursor.x); + auto img_skip_y = std::max(0, cursor.y); + auto img_skip_x = std::max(0, cursor.x); + + if(cursor_skip_y > height || cursor_skip_x > width) { + return; + } + + height -= cursor_skip_y; + width -= cursor_skip_x; + auto cursor_img_data = cursor.img_data.data() + cursor_skip_y * pitch; + + int delta_height = std::min(height, std::max(0, img.height - img_skip_y)); + int delta_width = std::min(width, std::max(0, img.width - img_skip_x)); + + auto pixels_per_byte = width / pitch; + auto bytes_per_row = delta_width / pixels_per_byte; + + auto img_data = (int*)img.data; + for(int i = 0; i < delta_height; ++i) { + auto and_mask = &cursor_img_data[i * pitch]; + auto xor_mask = &cursor_img_data[(i + height) * pitch]; + + auto img_pixel_p = &img_data[(i + img_skip_y) * img.width + img_skip_x]; + + auto skip_y = cursor_skip_y; + for(int x = 0; x < bytes_per_row; ++x) { + for(auto bit = 0u; bit < 8; ++bit) { + if(skip_y > 0) { + --skip_y; + + continue; + } + + int and_ = *and_mask & (1 << (7 - bit)) ? -1 : 0; + int xor_ = *xor_mask & (1 << (7 - bit)) ? -1 : 0; + + *img_pixel_p &= and_; + *img_pixel_p ^= xor_; + + ++img_pixel_p; + } + + ++and_mask; + ++xor_mask; + } + } +} + +void blend_cursor_color(const cursor_t &cursor, img_t &img) { + int height = cursor.shape_info.Height; + int width = cursor.shape_info.Width; + int pitch = cursor.shape_info.Pitch; + + // img cursor.y < 0, skip parts of the cursor.img_data + auto cursor_skip_y = std::min(0, cursor.y); + auto cursor_skip_x = std::min(0, cursor.x); + auto img_skip_y = std::max(0, cursor.y); + auto img_skip_x = std::max(0, cursor.x); + + if(cursor_skip_y > height || cursor_skip_x > width) { + return; + } + + height -= cursor_skip_y; + width -= cursor_skip_x; + auto cursor_img_data = (int*)&cursor.img_data[cursor_skip_y * pitch]; + + int delta_height = std::min(height, std::max(0, img.height - img_skip_y)); + int delta_width = std::min(width, std::max(0, img.width - img_skip_x)); + + auto img_data = (int*)img.data; + + for(int i = 0; i < delta_height; ++i) { + auto cursor_begin = &cursor_img_data[i * width + cursor_skip_x]; + auto cursor_end = &cursor_begin[delta_width]; + + auto img_pixel_p = &img_data[(i + img_skip_y) * img.width + img_skip_x]; + std::for_each(cursor_begin, cursor_end, [&](int cursor_pixel) { + auto colors_out = (std::uint8_t*)&cursor_pixel; + auto colors_in = (std::uint8_t*)img_pixel_p; + + //TODO: When use of IDXGIOutput5 is implemented, support different color formats + auto alpha = colors_out[3]; + if(alpha == 255) { + *img_pixel_p = cursor_pixel; + } + else { + colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255/2) / 255; + colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255/2) / 255; + colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255/2) / 255; + } + ++img_pixel_p; + }); + } +} + +void blend_cursor(const cursor_t &cursor, img_t &img) { + switch(cursor.shape_info.Type) { + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: + blend_cursor_color(cursor, img); + break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: + blend_cursor_monochrome(cursor, img); + break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: + default: + BOOST_LOG(warning) << "Unsupported cursor format ["sv << cursor.shape_info.Type << ']'; + } +} + +class display_t : public ::platf::display_t { +public: + capture_e snapshot(std::unique_ptr<::platf::img_t> &img_base, bool cursor_visible) override { + auto img = (img_t *) img_base.get(); + HRESULT status; + + DXGI_OUTDUPL_FRAME_INFO frame_info; + + resource_t::pointer res_p {}; + auto capture_status = dup.next_frame(frame_info, &res_p); + resource_t res{res_p}; + + if (capture_status != capture_e::ok) { + return capture_status; + } + + if (frame_info.PointerShapeBufferSize > 0) { + auto &img_data = cursor.img_data; + + img_data.resize(frame_info.PointerShapeBufferSize); + + UINT dummy; + status = dup.dup->GetFramePointerShape(img_data.size(), img_data.data(), &dummy, &cursor.shape_info); + if (FAILED(status)) { + BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']'; + + return capture_e::error; + } + } + + if(frame_info.LastMouseUpdateTime.QuadPart) { + cursor.x = frame_info.PointerPosition.Position.x; + cursor.y = frame_info.PointerPosition.Position.y; + cursor.visible = frame_info.PointerPosition.Visible; + } + + // If frame has been updated + if (frame_info.LastPresentTime.QuadPart != 0) { + { + texture2d_t::pointer src_p {}; + status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src_p); + texture2d_t src{src_p}; + + if (FAILED(status)) { + BOOST_LOG(error) << "Couldn't query interface [0x"sv << util::hex(status).to_string_view() << ']'; + return capture_e::error; + } + + //Copy from GPU to CPU + device_ctx->CopyResource(texture.get(), src.get()); + } + + if(current_img.pData) { + device_ctx->Unmap(texture.get(), 0); + current_img.pData = nullptr; + } + + status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, ¤t_img); + if (FAILED(status)) { + BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']'; + + return capture_e::error; + } + } + + const bool update_flag = + frame_info.LastMouseUpdateTime.QuadPart || + frame_info.LastPresentTime.QuadPart != 0 || + frame_info.PointerShapeBufferSize > 0; + + + if(!update_flag) { + return capture_e::timeout; + } + + if(img->width != width || img->height != height) { + delete[] img->data; + img->data = new std::uint8_t[height * current_img.RowPitch]; + + img->width = width; + img->height = height; + } + + std::copy_n((std::uint8_t*)current_img.pData, height * current_img.RowPitch, (std::uint8_t*)img->data); + if(cursor_visible && cursor.visible) { + blend_cursor(cursor, *img); + } + + return capture_e::ok; + } + + /* + * Called when access is lost. Dup must be reinitialized + */ + int reinit() override { + HRESULT status; + + dup.reset(); + //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)) { + BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv; + 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)) { + BOOST_LOG(error) << "DuplicateOutput Failed [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + } + + DXGI_OUTDUPL_DESC dup_desc; + dup.dup->GetDesc(&dup_desc); + + format = dup_desc.ModeDesc.Format; + + BOOST_LOG(debug) << "Source format ["sv << format_str[dup_desc.ModeDesc.Format] << ']'; + + 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 = format; + t.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + + dxgi::texture2d_t::pointer tex_p {}; + status = device->CreateTexture2D(&t, nullptr, &tex_p); + + texture.reset(tex_p); + + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to create texture [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + // map the texture simply to get the pitch and stride + status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, ¤t_img); + if(FAILED(status)) { + BOOST_LOG(error) << "Error: Failed to map the texture [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + return 0; + } + + std::unique_ptr<::platf::img_t> alloc_img() override { + auto img = std::make_unique(); + + img->data = nullptr; + img->height = 0; + img->width = 0; + + return img; + } + + 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); + }); +*/ + current_img.pData = nullptr; // current_img is not yet mapped + + 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)) { + BOOST_LOG(error) << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + + for(int x = 0; factory_p->EnumAdapters1(x, &adapter_p) != DXGI_ERROR_NOT_FOUND; ++x) { + dxgi::adapter_t adapter_tmp { adapter_p }; + + for(int y = 0; adapter_tmp->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_tmp); + break; + } + } + + if(!output) { + BOOST_LOG(error) << "Failed to locate an output device"sv; + 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)) { + BOOST_LOG(error) << "Failed to query IDXGIAdapter interface"sv; + + return -1; + } + + status = D3D11CreateDevice( + adapter_p, + D3D_DRIVER_TYPE_UNKNOWN, + nullptr, + D3D11_CREATE_DEVICE_VIDEO_SUPPORT, + featureLevels, sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), + D3D11_SDK_VERSION, + &device_p, + &feature_level, + &device_ctx_p); + + adapter_p->Release(); + + device.reset(device_p); + device_ctx.reset(device_ctx_p); + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to create D3D11 device [0x"sv << util::hex(status).to_string_view() << ']'; + + return -1; + } + + DXGI_ADAPTER_DESC adapter_desc; + adapter->GetDesc(&adapter_desc); + BOOST_LOG(info) + << "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; + + // 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)) { + BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']'; + 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)) { + BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + dxgi->SetMaximumFrameLatency(1); + } + + return reinit(); + } + + ~display_t() override { + if(current_img.pData) { + device_ctx->Unmap(texture.get(), 0); + current_img.pData = nullptr; + } + } + + factory1_t factory; + adapter_t adapter; + output_t output; + device_t device; + device_ctx_t device_ctx; + duplication_t dup; + cursor_t cursor; + texture2d_t texture; + + int width, height; + + DXGI_FORMAT format; + D3D_FEATURE_LEVEL feature_level; + D3D11_MAPPED_SUBRESOURCE current_img; +}; + +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" +}; + +} + +namespace platf { +std::shared_ptr display() { + auto disp = std::make_unique(); + + if (disp->init()) { + return nullptr; + } + + return disp; +} +} \ No newline at end of file