diff --git a/sunshine/platform/windows_dxgi.cpp b/sunshine/platform/windows_dxgi.cpp index 7832d127..4417bf2f 100644 --- a/sunshine/platform/windows_dxgi.cpp +++ b/sunshine/platform/windows_dxgi.cpp @@ -138,6 +138,12 @@ struct cursor_t { bool visible; }; +struct gpu_cursor_t { + texture2d_t texture; + + LONG width, height; +}; + void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) { int height = cursor.shape_info.Height / 2; int width = cursor.shape_info.Width; @@ -290,8 +296,63 @@ void blend_cursor(const cursor_t &cursor, img_t &img) { } } +std::vector make_cursor_image(std::vector &&img_data, DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info) { + switch(shape_info.Type) { + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: + return std::move(img_data); + default: + break; + } + + shape_info.Height /= 2; + + std::vector cursor_img; + cursor_img.resize(shape_info.Width * shape_info.Height * 4); + std::fill_n((std::uint32_t*)cursor_img.data(), cursor_img.size() / sizeof(std::uint32_t), 0x99888888); + + return cursor_img; +} + class hwdevice_t : public platf::hwdevice_t { public: + hwdevice_t(std::vector *hwdevices_p) : hwdevices_p { hwdevices_p } {} + hwdevice_t() = delete; + + void set_cursor_pos(LONG rel_x, LONG rel_y, bool visible) { + LONG x = ((float)rel_x) / in_width * out_width; + LONG y = ((float)rel_y) / in_height * out_height; + + // Ensure it's within bounds + auto left = std::min(out_width, std::max(0, x)); + auto top = std::min(out_height, std::max(0, y)); + auto right = std::max(0, std::min(out_width, x + cursor_width)); + auto bottom = std::max(0, std::min(out_height, y + cursor_height)); + + RECT rect { left, top, right, bottom }; + ctx->VideoProcessorSetStreamDestRect(processor.get(), 1, TRUE, &rect); + + cursor_visible = visible; + } + + int set_cursor_texture(texture2d_t::pointer texture, LONG width, LONG height) { + D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC input_desc = { 0, (D3D11_VPIV_DIMENSION)D3D11_VPIV_DIMENSION_TEXTURE2D, { 0, 0 } }; + + video::processor_in_t::pointer processor_in_p; + auto status = device->CreateVideoProcessorInputView(texture, processor_e.get(), &input_desc, &processor_in_p); + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to create cursor VideoProcessorInputView [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + cursor_in.reset(processor_in_p); + + cursor_width = ((float)width) / in_width * out_width; + cursor_height = ((float)height) / in_height * out_height; + + return 0; + } + int convert(platf::img_t &img_base) override { auto &img = (img_d3d_t&)img_base; @@ -302,17 +363,19 @@ public: video::processor_in_t::pointer processor_in_p; auto status = device->CreateVideoProcessorInputView(img.texture.get(), processor_e.get(), &input_desc, &processor_in_p); if(FAILED(status)) { - BOOST_LOG(error) << "Failed to create VideoProcessorInputView [0x"sv - << util::hex(status).to_string_view() << ']'; + BOOST_LOG(error) << "Failed to create VideoProcessorInputView [0x"sv << util::hex(status).to_string_view() << ']'; return -1; } it = texture_to_processor_in.emplace(img.texture.get(), processor_in_p).first; } auto &processor_in = it->second; - D3D11_VIDEO_PROCESSOR_STREAM stream { TRUE, 0, 0, 0, 0, nullptr, processor_in.get(), nullptr }; + D3D11_VIDEO_PROCESSOR_STREAM stream[] { + { TRUE, 0, 0, 0, 0, nullptr, processor_in.get(), nullptr }, + { TRUE, 0, 0, 0, 0, nullptr, cursor_in.get(), nullptr } + }; - auto status = ctx->VideoProcessorBlt(processor.get(), processor_out.get(), 0, 1, &stream); + auto status = ctx->VideoProcessorBlt(processor.get(), processor_out.get(), 0, cursor_visible ? 2 : 1, stream); if(FAILED(status)) { BOOST_LOG(error) << "Failed size and color conversion [0x"sv << util::hex(status).to_string_view() << ']'; return -1; @@ -333,8 +396,15 @@ public: ) { HRESULT status; + cursor_visible = false; + platf::hwdevice_t::img = &img; + this->out_width = out_width; + this->out_height = out_height; + this->in_width = in_width; + this->in_height = in_height; + video::device_t::pointer vdevice_p; status = device_p->QueryInterface(IID_ID3D11VideoDevice, (void**)&vdevice_p); if(FAILED(status)) { @@ -401,13 +471,16 @@ public: D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC output_desc { D3D11_VPOV_DIMENSION_TEXTURE2D, 0 }; video::processor_out_t::pointer processor_out_p; - status = device->CreateVideoProcessorOutputView(tex_p, processor_e.get(), &output_desc, &processor_out_p); + status = device->CreateVideoProcessorOutputView(img.texture.get(), processor_e.get(), &output_desc, &processor_out_p); if(FAILED(status)) { BOOST_LOG(error) << "Failed to create VideoProcessorOutputView [0x"sv << util::hex(status).to_string_view() << ']'; return -1; } processor_out.reset(processor_out_p); + // Tell video processor alpha values need to be enabled + ctx->VideoProcessorSetStreamAlpha(processor.get(), 1, TRUE, 1.0f); + device_p->AddRef(); data = device_p; return 0; @@ -417,6 +490,11 @@ public: if(data) { ((ID3D11Device*)data)->Release(); } + + auto it = std::find(std::begin(*hwdevices_p), std::end(*hwdevices_p), this); + if(it != std::end(*hwdevices_p)) { + hwdevices_p->erase(it); + } } img_d3d_t img; @@ -426,6 +504,16 @@ public: video::processor_t processor; video::processor_out_t processor_out; std::unordered_map texture_to_processor_in; + + video::processor_in_t cursor_in; + + bool cursor_visible; + LONG cursor_width, cursor_height; + + float out_width, out_height; + float in_width, in_height; + + std::vector *hwdevices_p; }; class display_base_t : public ::platf::display_t { @@ -797,11 +885,72 @@ public: return capture_status; } - const bool update_flag = frame_info.AccumulatedFrames != 0 || frame_info.LastPresentTime.QuadPart != 0; + const bool update_flag = + frame_info.AccumulatedFrames != 0 || frame_info.LastPresentTime.QuadPart != 0 || + frame_info.LastMouseUpdateTime.QuadPart != 0 || frame_info.PointerShapeBufferSize > 0; + if(!update_flag) { return capture_e::timeout; } + if(frame_info.PointerShapeBufferSize > 0) { + DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info {}; + + std::vector img_data; + img_data.resize(frame_info.PointerShapeBufferSize); + + UINT dummy; + status = dup.dup->GetFramePointerShape(img_data.size(), img_data.data(), &dummy, &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; + } + + auto cursor_img = make_cursor_image(std::move(img_data), shape_info); + + D3D11_SUBRESOURCE_DATA data { + cursor_img.data(), + 4 * shape_info.Width, + 0 + }; + + // Create texture for cursor + D3D11_TEXTURE2D_DESC t {}; + t.Width = shape_info.Width; + t.Height = cursor_img.size() / data.SysMemPitch; + t.MipLevels = 1; + t.ArraySize = 1; + t.SampleDesc.Count = 1; + t.Usage = D3D11_USAGE_DEFAULT; + t.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + t.BindFlags = D3D11_BIND_RENDER_TARGET; + + dxgi::texture2d_t::pointer tex_p {}; + auto status = device->CreateTexture2D(&t, &data, &tex_p); + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to create dummy texture [0x"sv << util::hex(status).to_string_view() << ']'; + return capture_e::error; + } + texture2d_t texture { tex_p }; + + for(auto *hwdevice : hwdevices) { + if(hwdevice->set_cursor_texture(tex_p, t.Width, t.Height)) { + return capture_e::error; + } + } + + cursor.texture = std::move(texture); + cursor.width = t.Width; + cursor.height = t.Height; + } + + if(frame_info.LastMouseUpdateTime.QuadPart) { + for(auto *hwdevice : hwdevices) { + hwdevice->set_cursor_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, frame_info.PointerPosition.Visible && cursor_visible); + } + } + texture2d_t::pointer src_p {}; status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src_p); @@ -891,7 +1040,7 @@ public: return nullptr; } - auto hwdevice = std::make_shared(); + auto hwdevice = std::make_shared(&hwdevices); auto ret = hwdevice->init( shared_from_this(), @@ -905,8 +1054,17 @@ public: return nullptr; } + if(cursor.texture && hwdevice->set_cursor_texture(cursor.texture.get(), cursor.width, cursor.height)) { + return nullptr; + } + + hwdevices.emplace_back(hwdevice.get()); + return hwdevice; } + + gpu_cursor_t cursor; + std::vector hwdevices; }; const char *format_str[] = {