Skip to content

Commit

Permalink
Force full damage on main back buffer for overlays.
Browse files Browse the repository at this point in the history
This is Windows only.

DWM won't consider a swap chain as an overlay candidate
unless it's updating at high enough fps with full damages.

So we need to force full damage when the actual damage is
closer to full damage in order to promote the main back
buffer as an overlay.

This CL also added the bit to allow viz to make sure
swap chain's buffers always have up to date pixels
inside or outside damage rects. This enables us to
force full damage.

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

Change-Id: Idff7a90952c810a07992df0ace040be92b63e4d3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2359730
Commit-Queue: Zhenyao Mo <zmo@chromium.org>
Reviewed-by: Steven Holte <holte@chromium.org>
Reviewed-by: Rafael Cintron <rafael.cintron@microsoft.com>
Reviewed-by: Jonathan Backer <backer@chromium.org>
Reviewed-by: Sunny Sachanandani <sunnyps@chromium.org>
Reviewed-by: Maggie Chen <magchen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#799445}
  • Loading branch information
zhenyao authored and Commit Bot committed Aug 19, 2020
1 parent cc6f597 commit a39ef6a
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 3 deletions.
10 changes: 10 additions & 0 deletions components/viz/service/display_embedder/skia_output_device_gl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#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"

Expand All @@ -52,6 +53,15 @@ SkiaOutputDeviceGL::SkiaOutputDeviceGL(
capabilities_.uses_default_gl_framebuffer = true;
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)) {
// 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.
capabilities_.preserve_buffer_content = true;
}
#endif // OS_WIN
if (feature_info->workarounds()
.disable_post_sub_buffers_for_onscreen_surfaces) {
capabilities_.supports_post_sub_buffer = false;
Expand Down
10 changes: 10 additions & 0 deletions tools/metrics/histograms/histograms.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65589,6 +65589,16 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
</summary>
</histogram>

<histogram name="GPU.DirectComposition.CompositionMode.MainBuffer"
enum="DxgiFramePresentationMode" expires_after="2021-08-17">
<owner>zmo@chromium.org</owner>
<owner>graphics-dev@chromium.org</owner>
<summary>
How the Desktop Window Manager presented Chrome's main DirectComposition
layer to the screen.
</summary>
</histogram>

<histogram name="GPU.DirectComposition.CreateSwapChainForComposition"
enum="Hresult" expires_after="M85">
<owner>zmo@chromium.org</owner>
Expand Down
75 changes: 72 additions & 3 deletions ui/gl/direct_composition_child_surface_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ 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(
Expand Down Expand Up @@ -125,20 +134,80 @@ bool DirectCompositionChildSurfaceWin::ReleaseDrawTexture(bool will_discard) {
first_swap_ || !vsync_enabled_ || use_swap_chain_tearing ? 0 : 1;
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();
params.DirtyRectsCount = 1;
params.pDirtyRects = &dirty_rect;
if (force_full_damage) {
params.DirtyRectsCount = 0;
params.pDirtyRects = nullptr;
} else {
params.DirtyRectsCount = 1;
params.pDirtyRects = &dirty_rect;
}

TRACE_EVENT2("gpu", "DirectCompositionChildSurfaceWin::PresentSwapChain",
"interval", interval, "dirty_rect", swap_rect_.ToString());
"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) {
DLOG(ERROR) << "Present1 failed with error " << std::hex << hr;
return false;
}

Microsoft::WRL::ComPtr<IDXGISwapChainMedia> swap_chain_media;
if (force_full_damage && SUCCEEDED(swap_chain_.As(&swap_chain_media))) {
DXGI_FRAME_STATISTICS_MEDIA stats = {};
// GetFrameStatisticsMedia fails with
// DXGI_ERROR_FRAME_STATISTICS_DISJOINT sometimes, which means an
// event (such as power cycle) interrupted the gathering of
// presentation statistics. In this situation, calling the function
// again succeeds but returns with CompositionMode = NONE.
// Waiting for the DXGI adapter to finish presenting before calling
// the function doesn't get rid of the failure.
if (SUCCEEDED(swap_chain_media->GetFrameStatisticsMedia(&stats))) {
base::UmaHistogramSparse(
"GPU.DirectComposition.CompositionMode.MainBuffer",
stats.CompositionMode);
}
}

if (first_swap_) {
// Wait for the GPU to finish executing its commands before
// committing the DirectComposition tree, or else the swapchain
Expand Down
5 changes: 5 additions & 0 deletions ui/gl/direct_composition_child_surface_win.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ 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);
};

Expand Down
6 changes: 6 additions & 0 deletions ui/gl/gl_switches.cc
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ const int kGLSwitchesCopiedFromGpuProcessHostNumSwitches =

namespace features {

// Forces Chrome's main backbuffer to full damage if the actual damage
// is large enough and allows DWM to consider the main backbuffer as an
// an overlay candidate.
const base::Feature kDirectCompositionForceFullDamage{
"DirectCompositionForceFullDamage", base::FEATURE_DISABLED_BY_DEFAULT};

// Use IDXGIOutput::WaitForVBlank() to drive begin frames.
const base::Feature kDirectCompositionGpuVSync{
"DirectCompositionGpuVSync", base::FEATURE_ENABLED_BY_DEFAULT};
Expand Down
1 change: 1 addition & 0 deletions ui/gl/gl_switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ GL_EXPORT extern const int kGLSwitchesCopiedFromGpuProcessHostNumSwitches;
} // namespace switches

namespace features {
GL_EXPORT extern const base::Feature kDirectCompositionForceFullDamage;
GL_EXPORT extern const base::Feature kDirectCompositionGpuVSync;
GL_EXPORT extern const base::Feature kDirectCompositionLowLatencyPresentation;
GL_EXPORT extern const base::Feature kDirectCompositionPreferNV12Overlays;
Expand Down

0 comments on commit a39ef6a

Please sign in to comment.