Commit 9c028cae authored by Zhenyao Mo's avatar Zhenyao Mo Committed by Commit Bot

In force full damage mode, always present with full damage.

On Windows, if we call swap chain's Present1 with damage rect info,
even the damage rect info indicates full damage, the chance of Chrome's
back buffer being promoted into an overlays are much lower.

If we want to consistently Chrome's BGRA back buffer to an overlay,
we have to always call Present which doesn't take damage rect info,
indicating full damage.

I measured some common use case, and their percentage of frames
being promoted to overlays are as below:

  - Google Meet (5 person, 2x2): 97.8%
  - wikipedia: 80.5%
  - a story sharing site: 77.2%
  - gmail (reading a few emails and then compose one): 61.3%
  - Google speadsheet: 78.2%
  - Google photos: 95.5%

If we promote a frame to overlay, then we save an extra copy (DWM
doesn't need to copy chrome's back buffer into OS's back buffer).

If we fail to promote a frame to overlay and it's partial damage,
now we use full damage, so at worst we do an extra copy.

So as far as we promote more than 50% frames to overlays, there is
performance gains. For use cases where performance is critical,
like Google Meet, or when we scroll, we get almost 100% promotion,
so we get maximum performance gain.

Besides, always use full damage simplifies logic quite a bit.

The UMA "GPU.DirectComposition.CompositionMode.MainBuffer" will show
us if this is a good decision by collecting overlay promotion
percentage from the wild.

BUG=1117185
TEST=manual
R=sunnyps@chromium.org

Change-Id: I12d927f472652f17e146debc83e40d9f40a1b950
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2363771
Commit-Queue: Zhenyao Mo <zmo@chromium.org>
Reviewed-by: default avatarSunny Sachanandani <sunnyps@chromium.org>
Cr-Commit-Position: refs/heads/master@{#799862}
parent 1ef533e9
......@@ -28,7 +28,6 @@
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/gl_switches.h"
#include "ui/gl/gl_utils.h"
#include "ui/gl/gl_version_info.h"
......@@ -54,8 +53,8 @@ SkiaOutputDeviceGL::SkiaOutputDeviceGL(
capabilities_.output_surface_origin = gl_surface_->GetOrigin();
capabilities_.supports_post_sub_buffer = gl_surface_->SupportsPostSubBuffer();
#if defined(OS_WIN)
if (base::FeatureList::IsEnabled(
features::kDirectCompositionForceFullDamage)) {
if (gl_surface_->SupportsDCLayers() &&
gl::ShouldForceDirectCompositionRootSurfaceFullDamage()) {
// We need to set this bit to allow viz to track the previous damage rect
// of a backbuffer in a multiple backbuffer system, so backbuffers always
// have valid pixels, even outside the current damage rect.
......
......@@ -47,16 +47,6 @@ namespace {
IDCompositionSurface* g_current_surface = nullptr;
bool g_direct_composition_swap_chain_failed = false;
// When more than percentage of area is damaged, present with full damage
// is considered.
// TODO(zmo): 0.6f is just a place holder. Need more profiling to get better
// heuristics.
constexpr float kLargeDamageThreshold = 0.6f;
// Only switch out of full damage mode after encountering the certain number
// of frames with damage < kLargeDamageThreshold.
constexpr size_t kNumFramesBeforeDisablingFullDamage = 5;
} // namespace
DirectCompositionChildSurfaceWin::DirectCompositionChildSurfaceWin(
......@@ -135,55 +125,21 @@ bool DirectCompositionChildSurfaceWin::ReleaseDrawTexture(bool will_discard) {
UINT flags = use_swap_chain_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0;
flags |= DXGI_PRESENT_USE_DURATION;
bool force_full_damage = false;
if (base::FeatureList::IsEnabled(
features::kDirectCompositionForceFullDamage) &&
DirectCompositionSurfaceWin::AreScaledOverlaysSupported()) {
// The actual condition we want to check is BGRA hardware overlay
// capability. However, that bit is incorrect on devices we tested on.
// Here we assume when hardware overlays are supported, BGRA
// hardware overlays are supported.
// Use AreScaledOverlaysSupported() instead of AreOverlaysSupported()
// here because the latter returns true even if only software layers
// are supported.
base::CheckedNumeric<int> swap_chain_area = size_.GetCheckedArea();
base::CheckedNumeric<int> damage_area =
swap_rect_.size().GetCheckedArea();
if (damage_area.IsValid() && swap_chain_area.IsValid() &&
swap_chain_area.ValueOrDie() > 0) {
float damage_ratio = static_cast<float>(damage_area.ValueOrDie()) /
static_cast<float>(swap_chain_area.ValueOrDie());
if (damage_ratio > kLargeDamageThreshold) {
small_damage_frame_count_ = 0;
force_full_damage = true;
} else {
++small_damage_frame_count_;
// There are "noise" frames between large damage frames. For
// example, in Google Meet, every 5-10 large damage frames, there
// is a "noise" frame with only 1% damage.
// If we keep switching between full damage and partial damage
// when presenting the swap chain, DWM doesn't seem to consider
// the swap chain as an overlay candidate.
if (small_damage_frame_count_ < kNumFramesBeforeDisablingFullDamage)
force_full_damage = true;
}
}
}
DXGI_PRESENT_PARAMETERS params = {};
RECT dirty_rect = swap_rect_.ToRECT();
bool force_full_damage =
ShouldForceDirectCompositionRootSurfaceFullDamage();
TRACE_EVENT2("gpu", "DirectCompositionChildSurfaceWin::PresentSwapChain",
"interval", interval, "dirty_rect",
force_full_damage ? "full_damage" : swap_rect_.ToString());
HRESULT hr;
if (force_full_damage) {
params.DirtyRectsCount = 0;
params.pDirtyRects = nullptr;
hr = swap_chain_->Present(interval, flags);
} else {
DXGI_PRESENT_PARAMETERS params = {};
RECT dirty_rect = swap_rect_.ToRECT();
params.DirtyRectsCount = 1;
params.pDirtyRects = &dirty_rect;
hr = swap_chain_->Present1(interval, flags, &params);
}
TRACE_EVENT2("gpu", "DirectCompositionChildSurfaceWin::PresentSwapChain",
"interval", interval, "dirty_rect",
force_full_damage ? "full_damage" : swap_rect_.ToString());
HRESULT hr = swap_chain_->Present1(interval, flags, &params);
// Ignore DXGI_STATUS_OCCLUDED since that's not an error but only
// indicates that the window is occluded and we can stop rendering.
if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
......@@ -192,7 +148,7 @@ bool DirectCompositionChildSurfaceWin::ReleaseDrawTexture(bool will_discard) {
}
Microsoft::WRL::ComPtr<IDXGISwapChainMedia> swap_chain_media;
if (force_full_damage && SUCCEEDED(swap_chain_.As(&swap_chain_media))) {
if (SUCCEEDED(swap_chain_.As(&swap_chain_media))) {
DXGI_FRAME_STATISTICS_MEDIA stats = {};
// GetFrameStatisticsMedia fails with
// DXGI_ERROR_FRAME_STATISTICS_DISJOINT sometimes, which means an
......
......@@ -101,11 +101,6 @@ class GL_EXPORT DirectCompositionChildSurfaceWin : public GLSurfaceEGL {
// Number of frames per second.
float frame_rate_ = 0.f;
// Number of frames whose damage < kLargeDamageThreshold.
// This is reset to 0 when a frame is encountered whose
// damage >= kLargeDamageThreshold
size_t small_damage_frame_count_ = 0;
DISALLOW_COPY_AND_ASSIGN(DirectCompositionChildSurfaceWin);
};
......
......@@ -52,6 +52,7 @@ bool SupportsOverlays() {
base::AutoLock auto_lock(GetOverlayLock());
return g_supports_overlays;
}
void SetSupportsOverlays(bool support) {
base::AutoLock auto_lock(GetOverlayLock());
g_supports_overlays = support;
......@@ -95,12 +96,23 @@ DXGI_FORMAT g_overlay_format_used = DXGI_FORMAT_NV12;
DXGI_FORMAT g_overlay_format_used_hdr = DXGI_FORMAT_UNKNOWN;
// These are the raw support info, which shouldn't depend on field trial state,
// or command line flags.
// or command line flags. GUARDED_BY GetOverlayLock().
UINT g_nv12_overlay_support_flags = 0;
UINT g_yuy2_overlay_support_flags = 0;
UINT g_bgra8_overlay_support_flags = 0;
UINT g_rgb10a2_overlay_support_flags = 0;
void SetOverlaySupportFlagsForFormats(UINT nv12_flags,
UINT yuy2_flags,
UINT bgra8_flags,
UINT rgb10a2_flags) {
base::AutoLock auto_lock(GetOverlayLock());
g_nv12_overlay_support_flags = nv12_flags;
g_yuy2_overlay_support_flags = yuy2_flags;
g_bgra8_overlay_support_flags = bgra8_flags;
g_rgb10a2_overlay_support_flags = rgb10a2_flags;
}
bool FlagsSupportsOverlays(UINT flags) {
return (flags & (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT |
DXGI_OVERLAY_SUPPORT_FLAG_SCALING));
......@@ -288,12 +300,11 @@ void UpdateOverlaySupport() {
// Update global caps
SetSupportsOverlays(supports_overlays);
SetOverlaySupportFlagsForFormats(
nv12_overlay_support_flags, yuy2_overlay_support_flags,
bgra8_overlay_support_flags, rgb10a2_overlay_support_flags);
g_overlay_format_used = overlay_format_used;
g_overlay_format_used_hdr = overlay_format_used_hdr;
g_nv12_overlay_support_flags = nv12_overlay_support_flags;
g_yuy2_overlay_support_flags = yuy2_overlay_support_flags;
g_bgra8_overlay_support_flags = bgra8_overlay_support_flags;
g_rgb10a2_overlay_support_flags = rgb10a2_overlay_support_flags;
g_overlay_monitor_size = overlay_monitor_size;
}
......@@ -471,6 +482,7 @@ bool DirectCompositionSurfaceWin::AreScaledOverlaysSupported() {
// static
UINT DirectCompositionSurfaceWin::GetOverlaySupportFlags(DXGI_FORMAT format) {
UpdateOverlaySupport();
base::AutoLock auto_lock(GetOverlayLock());
UINT support_flag = 0;
switch (format) {
case DXGI_FORMAT_NV12:
......
......@@ -81,6 +81,7 @@ class GL_EXPORT DirectCompositionSurfaceWin : public GLSurfaceEGL,
// Returns overlay support flags for the given format.
// Caller should check for DXGI_OVERLAY_SUPPORT_FLAG_DIRECT and
// DXGI_OVERLAY_SUPPORT_FLAG_SCALING bits.
// This function is thread safe.
static UINT GetOverlaySupportFlags(DXGI_FORMAT format);
// Returns true if there is an HDR capable display connected.
......
......@@ -116,5 +116,22 @@ unsigned int FrameRateToPresentDuration(float frame_rate) {
UINT GetOverlaySupportFlags(DXGI_FORMAT format) {
return gl::DirectCompositionSurfaceWin::GetOverlaySupportFlags(format);
}
bool ShouldForceDirectCompositionRootSurfaceFullDamage() {
static bool should_force = []() {
if (!base::FeatureList::IsEnabled(
features::kDirectCompositionForceFullDamage)) {
return false;
}
UINT brga_flags = DirectCompositionSurfaceWin::GetOverlaySupportFlags(
DXGI_FORMAT_B8G8R8A8_UNORM);
constexpr UINT kSupportBits =
DXGI_OVERLAY_SUPPORT_FLAG_DIRECT | DXGI_OVERLAY_SUPPORT_FLAG_SCALING;
if ((brga_flags & kSupportBits) == 0)
return false;
return true;
};
return should_force;
}
#endif // OS_WIN
} // namespace gl
......@@ -39,6 +39,10 @@ GL_EXPORT bool AreOverlaysSupportedWin();
GL_EXPORT unsigned int FrameRateToPresentDuration(float frame_rate);
GL_EXPORT UINT GetOverlaySupportFlags(DXGI_FORMAT format);
// Whether to use full damage when direct compostion root surface presents.
// This function is thread safe.
GL_EXPORT bool ShouldForceDirectCompositionRootSurfaceFullDamage();
#endif
} // namespace gl
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment