libobs-winrt: win-capture: Support client area toggle for WGC

master
jpark37 2020-02-24 21:21:52 -08:00
parent cce9fb7f95
commit 2111a3a02f
5 changed files with 116 additions and 23 deletions

View File

@ -24,11 +24,13 @@ target_precompile_headers(libobs-winrt
[["../libobs/util/windows/ComPtr.hpp"]]
<obs-module.h>
<d3d11.h>
<dwmapi.h>
<Windows.Graphics.Capture.Interop.h>
<winrt/Windows.Foundation.Metadata.h>
<winrt/Windows.Graphics.Capture.h>)
target_link_libraries(libobs-winrt
libobs
Dwmapi
windowsapp)
install_obs_core(libobs-winrt)

View File

@ -31,8 +31,56 @@ static winrt::com_ptr<T> GetDXGIInterfaceFromObject(
return result;
}
static bool get_client_box(HWND window, uint32_t width, uint32_t height,
D3D11_BOX *client_box)
{
RECT client_rect, window_rect{};
POINT upper_left{};
const bool client_box_available =
GetClientRect(window, &client_rect) &&
(DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS,
&window_rect,
sizeof(window_rect)) == S_OK) &&
ClientToScreen(window, &upper_left);
if (client_box_available) {
const uint32_t left =
(upper_left.x > window_rect.left)
? (upper_left.x - window_rect.left)
: 0;
client_box->left = left;
const uint32_t top = (upper_left.y > window_rect.top)
? (upper_left.y - window_rect.top)
: 0;
client_box->top = top;
uint32_t texture_width = 1;
if (width > left) {
texture_width =
min(width - left, (uint32_t)client_rect.right);
}
uint32_t texture_height = 1;
if (height > top) {
texture_height =
min(height - top, (uint32_t)client_rect.bottom);
}
client_box->right = left + texture_width;
client_box->bottom = top + texture_height;
client_box->front = 0;
client_box->back = 1;
}
return client_box_available;
}
struct winrt_capture {
bool capture_cursor;
HWND window;
bool client_area;
gs_texture_t *texture;
bool texture_written;
@ -48,6 +96,11 @@ struct winrt_capture {
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
FrameArrived_revoker frame_arrived;
uint32_t texture_width;
uint32_t texture_height;
D3D11_BOX client_box;
bool client_box_available;
bool thread_changed;
struct winrt_capture *next;
@ -70,23 +123,45 @@ struct winrt_capture {
D3D11_TEXTURE2D_DESC desc;
frame_surface->GetDesc(&desc);
client_box_available = false;
if (client_area) {
client_box_available = get_client_box(
window, desc.Width, desc.Height, &client_box);
}
if (client_box_available) {
texture_width = client_box.right - client_box.left;
texture_height = client_box.bottom - client_box.top;
} else {
texture_width = desc.Width;
texture_height = desc.Height;
}
if (texture) {
if (desc.Width != gs_texture_get_width(texture) ||
desc.Height != gs_texture_get_height(texture)) {
if (texture_width != gs_texture_get_width(texture) ||
texture_height != gs_texture_get_height(texture)) {
gs_texture_destroy(texture);
texture = nullptr;
}
}
if (!texture) {
texture = gs_texture_create(desc.Width, desc.Height,
GS_BGRA, 1, nullptr, 0);
texture = gs_texture_create(texture_width,
texture_height, GS_BGRA, 1,
nullptr, 0);
}
/* if they gave an SRV, we could avoid this copy */
context->CopyResource(
(ID3D11Texture2D *)gs_texture_get_obj(texture),
frame_surface.get());
if (client_box_available) {
context->CopySubresourceRegion(
(ID3D11Texture2D *)gs_texture_get_obj(texture),
0, 0, 0, 0, frame_surface.get(), 0,
&client_box);
} else {
/* if they gave an SRV, we could avoid this copy */
context->CopyResource(
(ID3D11Texture2D *)gs_texture_get_obj(texture),
frame_surface.get());
}
texture_written = true;
@ -160,8 +235,8 @@ static void winrt_capture_device_loss_rebuild(void *device_void, void *data)
thread_local bool initialized_tls;
extern "C" EXPORT struct winrt_capture *winrt_capture_init(bool cursor,
HWND window)
extern "C" EXPORT struct winrt_capture *
winrt_capture_init(bool cursor, HWND window, bool client_area)
{
ID3D11Device *const d3d_device = (ID3D11Device *)gs_get_device_obj();
ComPtr<IDXGIDevice> dxgi_device;
@ -192,8 +267,9 @@ extern "C" EXPORT struct winrt_capture *winrt_capture_init(bool cursor,
IGraphicsCaptureItem>(),
reinterpret_cast<void **>(winrt::put_abi(item)));
} catch (winrt::hresult_invalid_argument &) {
blog(LOG_WARNING, "[winrt_capture_init] Failed to "
"create GraphicsCaptureItem");
/* too spammy */
//blog(LOG_WARNING, "[winrt_capture_init] Failed to "
// "create GraphicsCaptureItem");
return nullptr;
}
@ -216,6 +292,8 @@ extern "C" EXPORT struct winrt_capture *winrt_capture_init(bool cursor,
struct winrt_capture *capture = new winrt_capture{};
capture->capture_cursor = cursor;
capture->window = window;
capture->client_area = client_area;
capture->item = item;
capture->device = device;
d3d_device->GetImmediateContext(&capture->context);
@ -315,14 +393,14 @@ extern "C" EXPORT void winrt_capture_render(struct winrt_capture *capture,
}
}
extern "C" EXPORT int32_t
extern "C" EXPORT uint32_t
winrt_capture_width(const struct winrt_capture *capture)
{
return capture ? capture->last_size.Width : 0;
return capture ? capture->texture_width : 0;
}
extern "C" EXPORT int32_t
extern "C" EXPORT uint32_t
winrt_capture_height(const struct winrt_capture *capture)
{
return capture ? capture->last_size.Height : 0;
return capture ? capture->texture_height : 0;
}

View File

@ -10,13 +10,14 @@ extern "C" {
#endif
EXPORT bool winrt_capture_supported();
EXPORT struct winrt_capture *winrt_capture_init(bool cursor, HWND window);
EXPORT struct winrt_capture *winrt_capture_init(bool cursor, HWND window,
bool client_area);
EXPORT void winrt_capture_free(struct winrt_capture *capture);
EXPORT void winrt_capture_render(struct winrt_capture *capture,
gs_effect_t *effect);
EXPORT int32_t winrt_capture_width(const struct winrt_capture *capture);
EXPORT int32_t winrt_capture_height(const struct winrt_capture *capture);
EXPORT uint32_t winrt_capture_width(const struct winrt_capture *capture);
EXPORT uint32_t winrt_capture_height(const struct winrt_capture *capture);
#ifdef __cplusplus
}

View File

@ -11,6 +11,7 @@ WindowCapture.Priority.Class="Match title, otherwise find window of same type"
WindowCapture.Priority.Exe="Match title, otherwise find window of same executable"
CaptureCursor="Capture Cursor"
Compatibility="Multi-adapter Compatibility"
ClientArea="Client Area"
SLIFix="SLI/Crossfire Capture Mode (Slow)"
AllowTransparency="Allow Transparency"
Monitor="Display"

View File

@ -19,6 +19,7 @@
#define TEXT_MATCH_EXE obs_module_text("WindowCapture.Priority.Exe")
#define TEXT_CAPTURE_CURSOR obs_module_text("CaptureCursor")
#define TEXT_COMPATIBILITY obs_module_text("Compatibility")
#define TEXT_CLIENT_AREA obs_module_text("ClientArea")
/* clang-format on */
@ -26,12 +27,13 @@
struct winrt_exports {
bool *(*winrt_capture_supported)();
struct winrt_capture *(*winrt_capture_init)(bool cursor, HWND window);
struct winrt_capture *(*winrt_capture_init)(bool cursor, HWND window,
bool client_area);
void (*winrt_capture_free)(struct winrt_capture *capture);
void (*winrt_capture_render)(struct winrt_capture *capture,
gs_effect_t *effect);
int32_t (*winrt_capture_width)(const struct winrt_capture *capture);
int32_t (*winrt_capture_height)(const struct winrt_capture *capture);
uint32_t (*winrt_capture_width)(const struct winrt_capture *capture);
uint32_t (*winrt_capture_height)(const struct winrt_capture *capture);
};
enum window_capture_method {
@ -50,6 +52,7 @@ struct window_capture {
enum window_priority priority;
bool cursor;
bool compatibility;
bool client_area;
bool use_wildcards; /* TODO */
struct dc_capture capture;
@ -139,6 +142,7 @@ static void update_settings(struct window_capture *wc, obs_data_t *s)
wc->cursor = obs_data_get_bool(s, "cursor");
wc->use_wildcards = obs_data_get_bool(s, "use_wildcards");
wc->compatibility = obs_data_get_bool(s, "compatibility");
wc->client_area = obs_data_get_bool(s, "client_area");
}
/* ------------------------------------------------------------------------- */
@ -252,18 +256,23 @@ static void wc_defaults(obs_data_t *defaults)
obs_data_set_default_int(defaults, "method", METHOD_AUTO);
obs_data_set_default_bool(defaults, "cursor", true);
obs_data_set_default_bool(defaults, "compatibility", false);
obs_data_set_default_bool(defaults, "client_area", true);
}
static void update_settings_visibility(obs_properties_t *props,
enum window_capture_method method)
{
const bool bitblt_options = method == METHOD_BITBLT;
const bool wgc_options = method == METHOD_WGC;
obs_property_t *p = obs_properties_get(props, "cursor");
obs_property_set_visible(p, bitblt_options);
p = obs_properties_get(props, "compatibility");
obs_property_set_visible(p, bitblt_options);
p = obs_properties_get(props, "client_area");
obs_property_set_visible(p, wgc_options);
}
static bool wc_capture_method_changed(obs_properties_t *props,
@ -321,6 +330,8 @@ static obs_properties_t *wc_properties(void *data)
obs_properties_add_bool(ppts, "compatibility", TEXT_COMPATIBILITY);
obs_properties_add_bool(ppts, "client_area", TEXT_CLIENT_AREA);
return ppts;
}
@ -441,7 +452,7 @@ static void wc_tick(void *data, float seconds)
} else if (wc->method == METHOD_WGC) {
if (wc->window && (wc->capture_winrt == NULL)) {
wc->capture_winrt = wc->exports.winrt_capture_init(
wc->cursor, wc->window);
wc->cursor, wc->window, wc->client_area);
}
}