Adds 0Hz support to WGC behind a flag

Also requires changes in Chrome, see https://chromium-review.googlesource.com/c/chromium/src/+/4315678

NOTRY=True
NOPRESUBMIT=True

Bug: chromium:1421242
Change-Id: Id1e6675e4ab4d1d82b011b85b799dc4e5b757c12
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/296501
Commit-Queue: Henrik Andreassson <henrika@webrtc.org>
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
Cr-Commit-Position: refs/heads/main@{#39542}
This commit is contained in:
henrika 2023-03-13 11:44:16 +01:00 committed by WebRTC LUCI CQ
parent e744af5455
commit 210b8b2325
3 changed files with 61 additions and 5 deletions

View File

@ -185,6 +185,11 @@ class RTC_EXPORT DesktopCaptureOptions {
void set_allow_wgc_capturer_fallback(bool allow) { void set_allow_wgc_capturer_fallback(bool allow) {
allow_wgc_capturer_fallback_ = allow; allow_wgc_capturer_fallback_ = allow;
} }
// This flag enables 0Hz mode in combination with the WGC capturer.
// The flag has no effect if the allow_wgc_capturer flag is false.
bool allow_wgc_zero_hertz() const { return allow_wgc_zero_hertz_; }
void set_allow_wgc_zero_hertz(bool allow) { allow_wgc_zero_hertz_ = allow; }
#endif // defined(RTC_ENABLE_WIN_WGC) #endif // defined(RTC_ENABLE_WIN_WGC)
#endif // defined(WEBRTC_WIN) #endif // defined(WEBRTC_WIN)
@ -239,6 +244,7 @@ class RTC_EXPORT DesktopCaptureOptions {
#if defined(RTC_ENABLE_WIN_WGC) #if defined(RTC_ENABLE_WIN_WGC)
bool allow_wgc_capturer_ = false; bool allow_wgc_capturer_ = false;
bool allow_wgc_capturer_fallback_ = false; bool allow_wgc_capturer_fallback_ = false;
bool allow_wgc_zero_hertz_ = false;
#endif #endif
#endif #endif
#if defined(WEBRTC_USE_X11) #if defined(WEBRTC_USE_X11)

View File

@ -192,6 +192,8 @@ HRESULT WgcCaptureSession::StartCapture(const DesktopCaptureOptions& options) {
} }
} }
allow_zero_hertz_ = options.allow_wgc_zero_hertz();
hr = session_->StartCapture(); hr = session_->StartCapture();
if (FAILED(hr)) { if (FAILED(hr)) {
RTC_LOG(LS_ERROR) << "Failed to start CaptureSession: " << hr; RTC_LOG(LS_ERROR) << "Failed to start CaptureSession: " << hr;
@ -234,13 +236,22 @@ bool WgcCaptureSession::GetFrame(std::unique_ptr<DesktopFrame>* output_frame) {
ProcessFrame(); ProcessFrame();
} }
// Return false if we still don't have a valid frame leading to a // Return a NULL frame and false as `result` if we still don't have a valid
// DesktopCapturer::Result::ERROR_PERMANENT posted by the WGC capturer. // frame. This will lead to a DesktopCapturer::Result::ERROR_PERMANENT being
if (!queue_.current_frame()) { // posted by the WGC capturer.
DesktopFrame* current_frame = queue_.current_frame();
if (!current_frame) {
RTC_LOG(LS_ERROR) << "GetFrame failed."; RTC_LOG(LS_ERROR) << "GetFrame failed.";
return false; return false;
} }
// Swap in the DesktopRegion in `damage_region_` which is updated in
// ProcessFrame(). The updated region is either empty or the full rect being
// captured where an empty damage region corresponds to "no change in content
// since last frame".
current_frame->mutable_updated_region()->Swap(&damage_region_);
damage_region_.Clear();
// Emit the current frame. // Emit the current frame.
std::unique_ptr<DesktopFrame> new_frame = queue_.current_frame()->Share(); std::unique_ptr<DesktopFrame> new_frame = queue_.current_frame()->Share();
*output_frame = std::move(new_frame); *output_frame = std::move(new_frame);
@ -294,7 +305,7 @@ HRESULT WgcCaptureSession::ProcessFrame() {
queue_.MoveToNextFrame(); queue_.MoveToNextFrame();
if (queue_.current_frame() && queue_.current_frame()->IsShared()) { if (queue_.current_frame() && queue_.current_frame()->IsShared()) {
RTC_DLOG(LS_WARNING) << "Overwriting frame that is still shared."; RTC_DLOG(LS_VERBOSE) << "Overwriting frame that is still shared.";
} }
ComPtr<WGC::IDirect3D11CaptureFrame> capture_frame; ComPtr<WGC::IDirect3D11CaptureFrame> capture_frame;
@ -410,7 +421,7 @@ HRESULT WgcCaptureSession::ProcessFrame() {
// Allocate the current frame buffer only if it is not already allocated or // Allocate the current frame buffer only if it is not already allocated or
// if the size has changed. Note that we can't reallocate other buffers at // if the size has changed. Note that we can't reallocate other buffers at
// this point, since the caller may still be reading from them. The queue can // this point, since the caller may still be reading from them. The queue can
// hold up tp two frames. // hold up to two frames.
DesktopSize image_size(image_width, image_height); DesktopSize image_size(image_width, image_height);
if (!queue_.current_frame() || if (!queue_.current_frame() ||
!queue_.current_frame()->size().equals(image_size)) { !queue_.current_frame()->size().equals(image_size)) {
@ -433,6 +444,32 @@ HRESULT WgcCaptureSession::ProcessFrame() {
d3d_context->Unmap(mapped_texture_.Get(), 0); d3d_context->Unmap(mapped_texture_.Get(), 0);
if (allow_zero_hertz()) {
DesktopFrame* previous_frame = queue_.previous_frame();
if (previous_frame) {
const int previous_frame_size =
previous_frame->stride() * previous_frame->size().height();
const int current_frame_size =
current_frame->stride() * current_frame->size().height();
// Compare the latest frame with the previous and check if the frames are
// equal (both contain the exact same pixel values).
if (current_frame_size == previous_frame_size) {
const bool frames_are_equal = !memcmp(
current_frame->data(), previous_frame->data(), current_frame_size);
if (!frames_are_equal) {
// TODO(https://crbug.com/1421242): If we had an API to report proper
// damage regions we should be doing AddRect() with a SetRect() call
// on a resize.
damage_region_.SetRect(DesktopRect::MakeSize(current_frame->size()));
}
} else {
// Mark resized frames as damaged.
damage_region_.SetRect(DesktopRect::MakeSize(current_frame->size()));
}
}
}
if (empty_frame_credit_count_ > 0) if (empty_frame_credit_count_ > 0)
--empty_frame_credit_count_; --empty_frame_credit_count_;
size_ = new_size; size_ = new_size;

View File

@ -82,6 +82,8 @@ class WgcCaptureSession final {
void RemoveEventHandlers(); void RemoveEventHandlers();
bool allow_zero_hertz() const { return allow_zero_hertz_; }
std::unique_ptr<EventRegistrationToken> frame_arrived_token_; std::unique_ptr<EventRegistrationToken> frame_arrived_token_;
std::unique_ptr<EventRegistrationToken> item_closed_token_; std::unique_ptr<EventRegistrationToken> item_closed_token_;
@ -140,6 +142,17 @@ class WgcCaptureSession final {
// expected and should not be seen as an error. // expected and should not be seen as an error.
int empty_frame_credit_count_ = 0; int empty_frame_credit_count_ = 0;
// Caches the value of DesktopCaptureOptions.allow_wgc_zero_hertz() in
// StartCapture(). Adds 0Hz detection in ProcessFrame() when enabled which
// adds complexity since memcmp() is performed on two successive frames.
bool allow_zero_hertz_ = false;
// Tracks damage region updates that were reported since the last time a frame
// was captured. Currently only supports either the complete rect being
// captured or an empty region. Will always be empty if `allow_zero_hertz_` is
// false.
DesktopRegion damage_region_;
SequenceChecker sequence_checker_; SequenceChecker sequence_checker_;
}; };