Support capturing rotated displays on Windows (#1602)
This commit is contained in:
@@ -81,23 +81,71 @@ namespace platf::dxgi {
|
||||
public:
|
||||
gpu_cursor_t():
|
||||
cursor_view { 0, 0, 0, 0, 0.0f, 1.0f } {};
|
||||
void
|
||||
set_pos(LONG rel_x, LONG rel_y, bool visible) {
|
||||
cursor_view.TopLeftX = rel_x;
|
||||
cursor_view.TopLeftY = rel_y;
|
||||
|
||||
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 width, LONG height, texture2d_t &&texture) {
|
||||
cursor_view.Width = width;
|
||||
cursor_view.Height = height;
|
||||
|
||||
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;
|
||||
@@ -141,6 +189,10 @@ namespace platf::dxgi {
|
||||
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;
|
||||
@@ -277,8 +329,8 @@ namespace platf::dxgi {
|
||||
blend_t blend_invert;
|
||||
blend_t blend_disable;
|
||||
|
||||
ps_t scene_ps;
|
||||
vs_t scene_vs;
|
||||
ps_t cursor_ps;
|
||||
vs_t cursor_vs;
|
||||
|
||||
gpu_cursor_t cursor_alpha;
|
||||
gpu_cursor_t cursor_xor;
|
||||
|
||||
@@ -461,6 +461,17 @@ namespace platf::dxgi {
|
||||
width = desc.DesktopCoordinates.right - offset_x;
|
||||
height = desc.DesktopCoordinates.bottom - offset_y;
|
||||
|
||||
display_rotation = desc.Rotation;
|
||||
if (display_rotation == DXGI_MODE_ROTATION_ROTATE90 ||
|
||||
display_rotation == DXGI_MODE_ROTATION_ROTATE270) {
|
||||
width_before_rotation = height;
|
||||
height_before_rotation = width;
|
||||
}
|
||||
else {
|
||||
width_before_rotation = width;
|
||||
height_before_rotation = height;
|
||||
}
|
||||
|
||||
// left and bottom may be negative, yet absolute mouse coordinates start at 0x0
|
||||
// Ensure offset starts at 0x0
|
||||
offset_x -= GetSystemMetrics(SM_XVIRTUALSCREEN);
|
||||
|
||||
@@ -107,6 +107,7 @@ namespace platf::dxgi {
|
||||
blob_t convert_UV_linear_ps_hlsl;
|
||||
blob_t convert_UV_PQ_ps_hlsl;
|
||||
blob_t scene_vs_hlsl;
|
||||
blob_t cursor_vs_hlsl;
|
||||
blob_t convert_Y_ps_hlsl;
|
||||
blob_t convert_Y_linear_ps_hlsl;
|
||||
blob_t convert_Y_PQ_ps_hlsl;
|
||||
@@ -472,6 +473,17 @@ namespace platf::dxgi {
|
||||
}
|
||||
device_ctx->VSSetConstantBuffers(0, 1, &info_scene);
|
||||
|
||||
{
|
||||
int32_t rotation_modifier = display->display_rotation == DXGI_MODE_ROTATION_UNSPECIFIED ? 0 : display->display_rotation - 1;
|
||||
int32_t rotation_data[16 / sizeof(int32_t)] { -rotation_modifier }; // aligned to 16-byte
|
||||
auto rotation = make_buffer(device.get(), rotation_data);
|
||||
if (!rotation) {
|
||||
BOOST_LOG(error) << "Failed to create display rotation vertex constant buffer";
|
||||
return -1;
|
||||
}
|
||||
device_ctx->VSSetConstantBuffers(1, 1, &rotation);
|
||||
}
|
||||
|
||||
D3D11_RENDER_TARGET_VIEW_DESC nv12_rt_desc {
|
||||
format == DXGI_FORMAT_P010 ? DXGI_FORMAT_R16_UNORM : DXGI_FORMAT_R8_UNORM,
|
||||
D3D11_RTV_DIMENSION_TEXTURE2D
|
||||
@@ -618,7 +630,11 @@ namespace platf::dxgi {
|
||||
convert_UV_vs_hlsl->GetBufferPointer(), convert_UV_vs_hlsl->GetBufferSize(),
|
||||
&input_layout);
|
||||
|
||||
this->display = std::move(display);
|
||||
this->display = std::dynamic_pointer_cast<display_base_t>(display);
|
||||
if (!this->display) {
|
||||
return -1;
|
||||
}
|
||||
display = nullptr;
|
||||
|
||||
blend_disable = make_blend(device.get(), false, false);
|
||||
if (!blend_disable) {
|
||||
@@ -735,7 +751,7 @@ namespace platf::dxgi {
|
||||
// amongst multiple hwdevice_t objects (and therefore multiple ID3D11Devices).
|
||||
std::map<uint32_t, encoder_img_ctx_t> img_ctx_map;
|
||||
|
||||
std::shared_ptr<platf::display_t> display;
|
||||
std::shared_ptr<display_base_t> display;
|
||||
|
||||
vs_t convert_UV_vs;
|
||||
ps_t convert_UV_ps;
|
||||
@@ -988,8 +1004,11 @@ namespace platf::dxgi {
|
||||
}
|
||||
|
||||
if (frame_info.LastMouseUpdateTime.QuadPart) {
|
||||
cursor_alpha.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, frame_info.PointerPosition.Visible);
|
||||
cursor_xor.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, frame_info.PointerPosition.Visible);
|
||||
cursor_alpha.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y,
|
||||
width, height, display_rotation, frame_info.PointerPosition.Visible);
|
||||
|
||||
cursor_xor.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y,
|
||||
width, height, display_rotation, frame_info.PointerPosition.Visible);
|
||||
}
|
||||
|
||||
const bool blend_mouse_cursor_flag = (cursor_alpha.visible || cursor_xor.visible) && cursor_visible;
|
||||
@@ -1008,7 +1027,7 @@ namespace platf::dxgi {
|
||||
|
||||
// It's possible for our display enumeration to race with mode changes and result in
|
||||
// mismatched image pool and desktop texture sizes. If this happens, just reinit again.
|
||||
if (desc.Width != width || desc.Height != height) {
|
||||
if (desc.Width != width_before_rotation || desc.Height != height_before_rotation) {
|
||||
BOOST_LOG(info) << "Capture size changed ["sv << width << 'x' << height << " -> "sv << desc.Width << 'x' << desc.Height << ']';
|
||||
return capture_e::reinit;
|
||||
}
|
||||
@@ -1109,8 +1128,8 @@ namespace platf::dxgi {
|
||||
|
||||
// Otherwise create a new surface.
|
||||
D3D11_TEXTURE2D_DESC t {};
|
||||
t.Width = width;
|
||||
t.Height = height;
|
||||
t.Width = width_before_rotation;
|
||||
t.Height = height_before_rotation;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.SampleDesc.Count = 1;
|
||||
@@ -1217,8 +1236,8 @@ namespace platf::dxgi {
|
||||
}
|
||||
|
||||
auto blend_cursor = [&](img_d3d_t &d3d_img) {
|
||||
device_ctx->VSSetShader(scene_vs.get(), nullptr, 0);
|
||||
device_ctx->PSSetShader(scene_ps.get(), nullptr, 0);
|
||||
device_ctx->VSSetShader(cursor_vs.get(), nullptr, 0);
|
||||
device_ctx->PSSetShader(cursor_ps.get(), nullptr, 0);
|
||||
device_ctx->OMSetRenderTargets(1, &d3d_img.capture_rt, nullptr);
|
||||
|
||||
if (cursor_alpha.texture.get()) {
|
||||
@@ -1338,15 +1357,26 @@ namespace platf::dxgi {
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = device->CreateVertexShader(scene_vs_hlsl->GetBufferPointer(), scene_vs_hlsl->GetBufferSize(), nullptr, &scene_vs);
|
||||
status = device->CreateVertexShader(cursor_vs_hlsl->GetBufferPointer(), cursor_vs_hlsl->GetBufferSize(), nullptr, &cursor_vs);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create scene vertex shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
int32_t rotation_modifier = display_rotation == DXGI_MODE_ROTATION_UNSPECIFIED ? 0 : display_rotation - 1;
|
||||
int32_t rotation_data[16 / sizeof(int32_t)] { rotation_modifier }; // aligned to 16-byte
|
||||
auto rotation = make_buffer(device.get(), rotation_data);
|
||||
if (!rotation) {
|
||||
BOOST_LOG(error) << "Failed to create display rotation vertex constant buffer";
|
||||
return -1;
|
||||
}
|
||||
device_ctx->VSSetConstantBuffers(2, 1, &rotation);
|
||||
}
|
||||
|
||||
if (config.dynamicRange && is_hdr()) {
|
||||
// This shader will normalize scRGB white levels to a user-defined white level
|
||||
status = device->CreatePixelShader(scene_NW_ps_hlsl->GetBufferPointer(), scene_NW_ps_hlsl->GetBufferSize(), nullptr, &scene_ps);
|
||||
status = device->CreatePixelShader(scene_NW_ps_hlsl->GetBufferPointer(), scene_NW_ps_hlsl->GetBufferSize(), nullptr, &cursor_ps);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create scene pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
@@ -1365,7 +1395,7 @@ namespace platf::dxgi {
|
||||
device_ctx->PSSetConstantBuffers(1, 1, &sdr_multiplier);
|
||||
}
|
||||
else {
|
||||
status = device->CreatePixelShader(scene_ps_hlsl->GetBufferPointer(), scene_ps_hlsl->GetBufferSize(), nullptr, &scene_ps);
|
||||
status = device->CreatePixelShader(scene_ps_hlsl->GetBufferPointer(), scene_ps_hlsl->GetBufferSize(), nullptr, &cursor_ps);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create scene pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
@@ -1392,8 +1422,8 @@ namespace platf::dxgi {
|
||||
auto img = std::make_shared<img_d3d_t>();
|
||||
|
||||
// Initialize format-independent fields
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
img->width = width_before_rotation;
|
||||
img->height = height_before_rotation;
|
||||
img->id = next_image_id++;
|
||||
|
||||
return img;
|
||||
@@ -1645,6 +1675,11 @@ namespace platf::dxgi {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cursor_vs_hlsl = compile_vertex_shader(SUNSHINE_SHADERS_DIR "/CursorVS.hlsl");
|
||||
if (!cursor_vs_hlsl) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
convert_Y_ps_hlsl = compile_pixel_shader(SUNSHINE_SHADERS_DIR "/ConvertYPS.hlsl");
|
||||
if (!convert_Y_ps_hlsl) {
|
||||
return -1;
|
||||
|
||||
Reference in New Issue
Block a user