Vastly reduce stuttering on the cursor
This commit is contained in:
+1
-1
Submodule moonlight-common-c updated: f5ae5df5d0...e60a7ef75f
@@ -81,9 +81,8 @@ struct x11_img_t : public img_t {
|
|||||||
|
|
||||||
struct shm_img_t : public img_t {
|
struct shm_img_t : public img_t {
|
||||||
~shm_img_t() override {
|
~shm_img_t() override {
|
||||||
if(data) {
|
|
||||||
delete[] data;
|
delete[] data;
|
||||||
}
|
data = nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Regular → Executable
+108
-94
@@ -38,26 +38,12 @@ public:
|
|||||||
bool has_frame {};
|
bool has_frame {};
|
||||||
|
|
||||||
capture_e next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, resource_t::pointer *res_p) {
|
capture_e next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, resource_t::pointer *res_p) {
|
||||||
HRESULT status;
|
auto capture_status = release_frame();
|
||||||
if(has_frame) {
|
if(capture_status != capture_e::ok) {
|
||||||
status = dup->ReleaseFrame();
|
return capture_status;
|
||||||
|
|
||||||
switch(status) {
|
|
||||||
case S_OK:
|
|
||||||
has_frame = false;
|
|
||||||
break;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status = dup->AcquireNextFrame(1000, &frame_info, res_p);
|
auto status = dup->AcquireNextFrame(1000, &frame_info, res_p);
|
||||||
|
|
||||||
switch(status) {
|
switch(status) {
|
||||||
case S_OK:
|
case S_OK:
|
||||||
@@ -75,10 +61,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
capture_e reset(dup_t::pointer dup_p = dup_t::pointer()) {
|
capture_e reset(dup_t::pointer dup_p = dup_t::pointer()) {
|
||||||
auto capture_status = capture_e::ok;
|
auto capture_status = release_frame();
|
||||||
if(has_frame) {
|
|
||||||
capture_status = release_frame();
|
|
||||||
}
|
|
||||||
|
|
||||||
dup.reset(dup_p);
|
dup.reset(dup_p);
|
||||||
|
|
||||||
@@ -86,9 +69,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
capture_e release_frame() {
|
capture_e release_frame() {
|
||||||
|
if(!has_frame) {
|
||||||
|
return capture_e::ok;
|
||||||
|
}
|
||||||
|
|
||||||
auto status = dup->ReleaseFrame();
|
auto status = dup->ReleaseFrame();
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case S_OK:
|
case S_OK:
|
||||||
|
has_frame = false;
|
||||||
return capture_e::ok;
|
return capture_e::ok;
|
||||||
case DXGI_ERROR_WAIT_TIMEOUT:
|
case DXGI_ERROR_WAIT_TIMEOUT:
|
||||||
return capture_e::timeout;
|
return capture_e::timeout;
|
||||||
@@ -102,20 +90,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
~duplication_t() {
|
~duplication_t() {
|
||||||
if(has_frame) {
|
|
||||||
release_frame();
|
release_frame();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class display_t;
|
class display_t;
|
||||||
struct img_t : public ::platf::img_t {
|
struct img_t : public ::platf::img_t {
|
||||||
std::shared_ptr<display_t> owner; //Ensure texture remains valid while img is still needed
|
~img_t() override {
|
||||||
|
delete[] data;
|
||||||
texture2d_t texture;
|
data = nullptr;
|
||||||
D3D11_MAPPED_SUBRESOURCE map {};
|
}
|
||||||
|
|
||||||
~img_t() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cursor_t {
|
struct cursor_t {
|
||||||
@@ -131,21 +115,42 @@ void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) {
|
|||||||
int width = cursor.shape_info.Width;
|
int width = cursor.shape_info.Width;
|
||||||
int pitch = cursor.shape_info.Pitch;
|
int pitch = cursor.shape_info.Pitch;
|
||||||
|
|
||||||
int delta_height = std::min(height, std::max(0, img.height - cursor.y));
|
// img cursor.y < 0, skip parts of the cursor.img_data
|
||||||
int delta_width = std::min(width, std::max(0, img.width - cursor.x));
|
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 pixels_per_byte = width / pitch;
|
||||||
auto bytes_per_row = delta_width / pixels_per_byte;
|
auto bytes_per_row = delta_width / pixels_per_byte;
|
||||||
|
|
||||||
auto img_data = (int*)img.data;
|
auto img_data = (int*)img.data;
|
||||||
for(int i = 0; i < delta_height; ++i) {
|
for(int i = 0; i < delta_height; ++i) {
|
||||||
auto and_mask = &cursor.img_data[i * pitch];
|
auto and_mask = &cursor_img_data[i * pitch];
|
||||||
auto xor_mask = &cursor.img_data[(i + height) * pitch];
|
auto xor_mask = &cursor_img_data[(i + height) * pitch];
|
||||||
|
|
||||||
auto img_pixel_p = &img_data[(i + cursor.y) * img.width + cursor.x];
|
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(int x = 0; x < bytes_per_row; ++x) {
|
||||||
for(auto bit = 0u; bit < 8; ++bit) {
|
for(auto bit = 0u; bit < 8; ++bit) {
|
||||||
|
if(skip_y > 0) {
|
||||||
|
--skip_y;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
int and_ = *and_mask & (1 << (7 - bit)) ? -1 : 0;
|
int and_ = *and_mask & (1 << (7 - bit)) ? -1 : 0;
|
||||||
int xor_ = *xor_mask & (1 << (7 - bit)) ? -1 : 0;
|
int xor_ = *xor_mask & (1 << (7 - bit)) ? -1 : 0;
|
||||||
|
|
||||||
@@ -162,22 +167,36 @@ void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void blend_cursor_color(const cursor_t &cursor, img_t &img) {
|
void blend_cursor_color(const cursor_t &cursor, img_t &img) {
|
||||||
int height = cursor.shape_info.Height;
|
int height = cursor.shape_info.Height / 2;
|
||||||
int width = cursor.shape_info.Width;
|
int width = cursor.shape_info.Width;
|
||||||
int pitch = cursor.shape_info.Pitch;
|
int pitch = cursor.shape_info.Pitch;
|
||||||
|
int stride = cursor.shape_info.Pitch / width;
|
||||||
|
|
||||||
int delta_height = std::min(height, std::max(0, img.height - cursor.y));
|
// img cursor.y < 0, skip parts of the cursor.img_data
|
||||||
int delta_width = std::min(width, std::max(0, img.width - cursor.x));
|
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 img_data = (int*)img.data;
|
auto img_data = (int*)img.data;
|
||||||
auto size_of_pixel = pitch / width;
|
|
||||||
BOOST_LOG(info) << "size_of_pixel ["sv << size_of_pixel << ']';
|
|
||||||
for(int i = 0; i < delta_height; ++i) {
|
|
||||||
auto cursor_begin = &cursor.img_data[i * pitch];
|
|
||||||
auto cursor_end = cursor_begin + delta_width * size_of_pixel;
|
|
||||||
|
|
||||||
auto img_pixel_p = &img_data[(i + cursor.y) * img.width + cursor.x];
|
for(int i = 0; i < delta_height; ++i) {
|
||||||
for(auto cursor_p = cursor_begin; cursor_p != cursor_end; cursor_p += size_of_pixel) {
|
auto cursor_begin = &cursor_img_data[i * pitch + cursor_skip_x * stride];
|
||||||
|
auto cursor_end = &cursor_begin[delta_width * stride];
|
||||||
|
|
||||||
|
auto img_pixel_p = &img_data[(i + img_skip_y) * img.width + img_skip_x];
|
||||||
|
for(auto cursor_p = cursor_begin; cursor_p != cursor_end; cursor_p += stride) {
|
||||||
auto colors_in = (uint8_t*)img_pixel_p;
|
auto colors_in = (uint8_t*)img_pixel_p;
|
||||||
|
|
||||||
//TODO: When use of IDXGIOutput5 is implemented, support different color formats
|
//TODO: When use of IDXGIOutput5 is implemented, support different color formats
|
||||||
@@ -211,7 +230,7 @@ void blend_cursor(const cursor_t &cursor, img_t &img) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class display_t : public ::platf::display_t, public std::enable_shared_from_this<display_t> {
|
class display_t : public ::platf::display_t {
|
||||||
public:
|
public:
|
||||||
capture_e snapshot(std::unique_ptr<::platf::img_t> &img_base, bool cursor_visible) override {
|
capture_e snapshot(std::unique_ptr<::platf::img_t> &img_base, bool cursor_visible) override {
|
||||||
auto img = (img_t *) img_base.get();
|
auto img = (img_t *) img_base.get();
|
||||||
@@ -247,11 +266,8 @@ public:
|
|||||||
cursor.visible = frame_info.PointerPosition.Visible;
|
cursor.visible = frame_info.PointerPosition.Visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If frame has not been updated, skip
|
// If frame has been updated
|
||||||
if (frame_info.LastPresentTime.QuadPart == 0) {
|
if (frame_info.LastPresentTime.QuadPart != 0) {
|
||||||
return capture_e::timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
texture2d_t::pointer src_p {};
|
texture2d_t::pointer src_p {};
|
||||||
status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src_p);
|
status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src_p);
|
||||||
@@ -263,24 +279,41 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Copy from GPU to CPU
|
//Copy from GPU to CPU
|
||||||
device_ctx->CopyResource((ID3D11Resource *) img->texture.get(), (ID3D11Resource *) src.get());
|
device_ctx->CopyResource(texture.get(), src.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
status = device_ctx->Map((ID3D11Resource *) img->texture.get(), 0, D3D11_MAP_READ, 0, &img->map);
|
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)) {
|
if (FAILED(status)) {
|
||||||
if (status == DXGI_ERROR_WAS_STILL_DRAWING) {
|
|
||||||
return capture_e::timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']';
|
BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||||
|
|
||||||
return capture_e::error;
|
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->data = (std::uint8_t *) img->map.pData;
|
|
||||||
img->width = width;
|
img->width = width;
|
||||||
img->height = height;
|
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) {
|
if(cursor_visible && cursor.visible) {
|
||||||
blend_cursor(cursor, *img);
|
blend_cursor(cursor, *img);
|
||||||
}
|
}
|
||||||
@@ -342,7 +375,8 @@ public:
|
|||||||
|
|
||||||
dxgi::texture2d_t::pointer tex_p {};
|
dxgi::texture2d_t::pointer tex_p {};
|
||||||
status = device->CreateTexture2D(&t, nullptr, &tex_p);
|
status = device->CreateTexture2D(&t, nullptr, &tex_p);
|
||||||
dxgi::texture2d_t tex { tex_p };
|
|
||||||
|
texture.reset(tex_p);
|
||||||
|
|
||||||
if(FAILED(status)) {
|
if(FAILED(status)) {
|
||||||
BOOST_LOG(error) << "Failed to create texture [0x"sv << util::hex(status).to_string_view() << ']';
|
BOOST_LOG(error) << "Failed to create texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||||
@@ -350,46 +384,22 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// map the texture simply to get the pitch and stride
|
// map the texture simply to get the pitch and stride
|
||||||
D3D11_MAPPED_SUBRESOURCE mapping;
|
status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, ¤t_img);
|
||||||
|
|
||||||
status = device_ctx->Map((ID3D11Resource *)tex.get(), 0, D3D11_MAP_READ, 0, &mapping);
|
|
||||||
if(FAILED(status)) {
|
if(FAILED(status)) {
|
||||||
BOOST_LOG(error) << "Error: Failed to map the texture [0x"sv << util::hex(status).to_string_view() << ']';
|
BOOST_LOG(error) << "Error: Failed to map the texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pitch = (int)mapping.RowPitch;
|
|
||||||
stride = (int)mapping.RowPitch / 4;
|
|
||||||
|
|
||||||
device_ctx->Unmap((ID3D11Resource *)tex.get(), 0);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<::platf::img_t> alloc_img() override {
|
std::unique_ptr<::platf::img_t> alloc_img() override {
|
||||||
auto img = std::make_unique<img_t>();
|
auto img = std::make_unique<img_t>();
|
||||||
|
|
||||||
D3D11_TEXTURE2D_DESC t {};
|
img->data = nullptr;
|
||||||
t.Width = width;
|
img->height = 0;
|
||||||
t.Height = height;
|
img->width = 0;
|
||||||
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 {};
|
|
||||||
HRESULT status = device->CreateTexture2D(&t, nullptr, &tex_p);
|
|
||||||
dxgi::texture2d_t tex { tex_p };
|
|
||||||
|
|
||||||
if(FAILED(status)) {
|
|
||||||
BOOST_LOG(error) << "Failed to create texture [0x"sv << util::hex(status).to_string_view() << ']';
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
img->texture = std::move(tex);
|
|
||||||
img->owner = shared_from_this();
|
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,6 +420,8 @@ public:
|
|||||||
FreeLibrary(user32);
|
FreeLibrary(user32);
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
|
current_img.pData = nullptr; // current_img is not yet mapped
|
||||||
|
|
||||||
dxgi::factory1_t::pointer factory_p {};
|
dxgi::factory1_t::pointer factory_p {};
|
||||||
dxgi::adapter_t::pointer adapter_p {};
|
dxgi::adapter_t::pointer adapter_p {};
|
||||||
dxgi::output_t::pointer output_p {};
|
dxgi::output_t::pointer output_p {};
|
||||||
@@ -537,6 +549,13 @@ public:
|
|||||||
return reinit();
|
return reinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~display_t() override {
|
||||||
|
if(current_img.pData) {
|
||||||
|
device_ctx->Unmap(texture.get(), 0);
|
||||||
|
current_img.pData = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
factory1_t factory;
|
factory1_t factory;
|
||||||
adapter_t adapter;
|
adapter_t adapter;
|
||||||
output_t output;
|
output_t output;
|
||||||
@@ -544,20 +563,15 @@ public:
|
|||||||
device_ctx_t device_ctx;
|
device_ctx_t device_ctx;
|
||||||
duplication_t dup;
|
duplication_t dup;
|
||||||
cursor_t cursor;
|
cursor_t cursor;
|
||||||
|
texture2d_t texture;
|
||||||
|
|
||||||
int width, height;
|
int width, height;
|
||||||
int pitch, stride;
|
|
||||||
|
|
||||||
DXGI_FORMAT format;
|
DXGI_FORMAT format;
|
||||||
D3D_FEATURE_LEVEL feature_level;
|
D3D_FEATURE_LEVEL feature_level;
|
||||||
|
D3D11_MAPPED_SUBRESOURCE current_img;
|
||||||
};
|
};
|
||||||
|
|
||||||
img_t::~img_t() {
|
|
||||||
if(map.pData) {
|
|
||||||
owner->device_ctx->Unmap((ID3D11Resource *)texture.get(), 0);
|
|
||||||
map.pData = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class dummy_mic_t : public mic_t {
|
class dummy_mic_t : public mic_t {
|
||||||
|
|||||||
Reference in New Issue
Block a user