Commit 653ce951 authored by Maggie Chen's avatar Maggie Chen Committed by Commit Bot

Prevent frequent overlay swapchain recreation in Windows

When there is a new swapchain size request, don't recreate a new swapchain
immediately. Instead, use the previous swapchain size until the the new size
has been requested for 30 frames. Then create a new swapchain with the new size.
We can achieve the same onscreen video size by adding this size delta to the
transform matrix calculation.

Bug:885182

Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: I846b8bcb0753a9bd9ccce8329354dc55decc1481
Reviewed-on: https://chromium-review.googlesource.com/c/1235316Reviewed-by: default avatarZhenyao Mo <zmo@chromium.org>
Reviewed-by: default avatarSunny Sachanandani <sunnyps@chromium.org>
Commit-Queue: Maggie Chen <magchen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#596463}
parent dbf2640b
...@@ -43,6 +43,8 @@ ...@@ -43,6 +43,8 @@
namespace gpu { namespace gpu {
namespace { namespace {
// Number of stable frames before the swap chain can be resized
static constexpr int kNumFramesBeforeSwapChainResize = 30;
// Some drivers fail to correctly handle BT.709 video in overlays. This flag // Some drivers fail to correctly handle BT.709 video in overlays. This flag
// converts them to BT.601 in the video processor. // converts them to BT.601 in the video processor.
...@@ -342,6 +344,7 @@ class DCLayerTree { ...@@ -342,6 +344,7 @@ class DCLayerTree {
const gfx::Point& offset); const gfx::Point& offset);
void CalculateVideoSwapChainParameters( void CalculateVideoSwapChainParameters(
VisualInfo* visual_info,
const ui::DCRendererLayerParams& params, const ui::DCRendererLayerParams& params,
gfx::Size* video_swap_chain_size, gfx::Size* video_swap_chain_size,
gfx::Transform* video_visual_transform, gfx::Transform* video_visual_transform,
...@@ -386,6 +389,8 @@ class DCLayerTree::SwapChainPresenter { ...@@ -386,6 +389,8 @@ class DCLayerTree::SwapChainPresenter {
return swap_chain_; return swap_chain_;
} }
gfx::Size GetStableSwapChainSize(gfx::Size requested_swap_chain_size);
private: private:
bool UploadVideoImages(gl::GLImageMemory* y_image_memory, bool UploadVideoImages(gl::GLImageMemory* y_image_memory,
gl::GLImageMemory* uv_image_memory); gl::GLImageMemory* uv_image_memory);
...@@ -411,6 +416,8 @@ class DCLayerTree::SwapChainPresenter { ...@@ -411,6 +416,8 @@ class DCLayerTree::SwapChainPresenter {
PresentationHistory presentation_history_; PresentationHistory presentation_history_;
bool failed_to_create_yuv_swapchain_ = false; bool failed_to_create_yuv_swapchain_ = false;
int frames_since_color_space_change_ = 0; int frames_since_color_space_change_ = 0;
int frames_since_stable_swap_chain_resize_ = 0;
gfx::Size last_requested_swap_chain_size_;
// These are the GLImages that were presented in the last frame. // These are the GLImages that were presented in the last frame.
std::vector<scoped_refptr<gl::GLImage>> last_gl_images_; std::vector<scoped_refptr<gl::GLImage>> last_gl_images_;
...@@ -550,6 +557,33 @@ bool DCLayerTree::SwapChainPresenter::ShouldUseYUVSwapChain() { ...@@ -550,6 +557,33 @@ bool DCLayerTree::SwapChainPresenter::ShouldUseYUVSwapChain() {
} }
} }
// To avoid frequent swap chain recreation during video resizing, keep
// the same swap chain size until the onscreen video size is stable for
// at least 30 frames.
gfx::Size DCLayerTree::SwapChainPresenter::GetStableSwapChainSize(
gfx::Size requested_swap_chain_size) {
if (requested_swap_chain_size == swap_chain_size_ ||
swap_chain_size_.IsEmpty()) {
last_requested_swap_chain_size_ = requested_swap_chain_size;
return requested_swap_chain_size;
}
if (requested_swap_chain_size == last_requested_swap_chain_size_) {
frames_since_stable_swap_chain_resize_++;
if (frames_since_stable_swap_chain_resize_ >=
kNumFramesBeforeSwapChainResize) {
frames_since_stable_swap_chain_resize_ = 0;
return requested_swap_chain_size;
}
} else {
frames_since_stable_swap_chain_resize_ = 0;
last_requested_swap_chain_size_ = requested_swap_chain_size;
}
// Keep the previous swap chain size
return swap_chain_size_;
}
bool DCLayerTree::SwapChainPresenter::UploadVideoImages( bool DCLayerTree::SwapChainPresenter::UploadVideoImages(
gl::GLImageMemory* y_image_memory, gl::GLImageMemory* y_image_memory,
gl::GLImageMemory* uv_image_memory) { gl::GLImageMemory* uv_image_memory) {
...@@ -701,7 +735,7 @@ bool DCLayerTree::SwapChainPresenter::PresentToSwapChain( ...@@ -701,7 +735,7 @@ bool DCLayerTree::SwapChainPresenter::PresentToSwapChain(
return false; return false;
} }
// DirectComposition can display black for a swapchain between the first // DirectComposition can display black for a swap chain between the first
// and second time it's presented to - maybe the first Present can get // and second time it's presented to - maybe the first Present can get
// lost somehow and it shows the wrong buffer. In that case copy the // lost somehow and it shows the wrong buffer. In that case copy the
// buffers so both have the correct contents, which seems to help. The // buffers so both have the correct contents, which seems to help. The
...@@ -1027,8 +1061,8 @@ bool DCLayerTree::UpdateVisualForVideo(VisualInfo* visual_info, ...@@ -1027,8 +1061,8 @@ bool DCLayerTree::UpdateVisualForVideo(VisualInfo* visual_info,
gfx::Transform video_transform; gfx::Transform video_transform;
gfx::Point video_offset; gfx::Point video_offset;
CalculateVideoSwapChainParameters(params, &swap_chain_size, &video_transform, CalculateVideoSwapChainParameters(visual_info, params, &swap_chain_size,
&video_offset); &video_transform, &video_offset);
Microsoft::WRL::ComPtr<IDCompositionVisual2> dc_visual = Microsoft::WRL::ComPtr<IDCompositionVisual2> dc_visual =
visual_info->content_visual; visual_info->content_visual;
...@@ -1072,6 +1106,7 @@ bool DCLayerTree::UpdateVisualForVideo(VisualInfo* visual_info, ...@@ -1072,6 +1106,7 @@ bool DCLayerTree::UpdateVisualForVideo(VisualInfo* visual_info,
} }
void DCLayerTree::CalculateVideoSwapChainParameters( void DCLayerTree::CalculateVideoSwapChainParameters(
VisualInfo* visual_info,
const ui::DCRendererLayerParams& params, const ui::DCRendererLayerParams& params,
gfx::Size* video_swap_chain_size, gfx::Size* video_swap_chain_size,
gfx::Transform* video_visual_transform, gfx::Transform* video_visual_transform,
...@@ -1124,6 +1159,14 @@ void DCLayerTree::CalculateVideoSwapChainParameters( ...@@ -1124,6 +1159,14 @@ void DCLayerTree::CalculateVideoSwapChainParameters(
gfx::Size ceiled_input_size = gfx::Size ceiled_input_size =
gfx::ToCeiledSize(params.contents_rect.size()); gfx::ToCeiledSize(params.contents_rect.size());
swap_chain_size.SetToMin(ceiled_input_size); swap_chain_size.SetToMin(ceiled_input_size);
// We don't want to recreate the swap chain too frequently, so override the
// swap chain size with GetStableSwapChainSize().
// TODO(magchen): This might cause overlay downscaling if the
// new size is bigger than display_rect.
if (visual_info->swap_chain_presenter)
swap_chain_size =
visual_info->swap_chain_presenter->GetStableSwapChainSize(
swap_chain_size);
} }
// YUV surfaces must have an even width. // YUV surfaces must have an even width.
...@@ -1131,7 +1174,7 @@ void DCLayerTree::CalculateVideoSwapChainParameters( ...@@ -1131,7 +1174,7 @@ void DCLayerTree::CalculateVideoSwapChainParameters(
swap_chain_size.set_width(swap_chain_size.width() + 1); swap_chain_size.set_width(swap_chain_size.width() + 1);
// Update the transform matrix. It will be used in dc_visual->SetTransform() // Update the transform matrix. It will be used in dc_visual->SetTransform()
// This is the scale from the swapchain size to the size of the contents // This is the scale from the swap chain size to the size of the contents
// onscreen. // onscreen.
float swap_chain_scale_x = float swap_chain_scale_x =
params.rect.width() * 1.0f / swap_chain_size.width(); params.rect.width() * 1.0f / swap_chain_size.width();
...@@ -1368,6 +1411,11 @@ void DirectCompositionSurfaceWin::SetScaledOverlaysSupportedForTesting( ...@@ -1368,6 +1411,11 @@ void DirectCompositionSurfaceWin::SetScaledOverlaysSupportedForTesting(
g_supports_scaled_overlays = value; g_supports_scaled_overlays = value;
} }
// static
int DirectCompositionSurfaceWin::GetNumFramesBeforeSwapChainResizeForTesting() {
return kNumFramesBeforeSwapChainResize;
}
// static // static
bool DirectCompositionSurfaceWin::IsHDRSupported() { bool DirectCompositionSurfaceWin::IsHDRSupported() {
// HDR support was introduced in Windows 10 Creators Update. // HDR support was introduced in Windows 10 Creators Update.
......
...@@ -52,6 +52,8 @@ class GPU_IPC_SERVICE_EXPORT DirectCompositionSurfaceWin ...@@ -52,6 +52,8 @@ class GPU_IPC_SERVICE_EXPORT DirectCompositionSurfaceWin
static void SetScaledOverlaysSupportedForTesting(bool value); static void SetScaledOverlaysSupportedForTesting(bool value);
static int GetNumFramesBeforeSwapChainResizeForTesting();
bool InitializeNativeWindow(); bool InitializeNativeWindow();
// GLSurfaceEGL implementation. // GLSurfaceEGL implementation.
......
...@@ -383,19 +383,114 @@ TEST(DirectCompositionSurfaceTest, NoPresentTwice) { ...@@ -383,19 +383,114 @@ TEST(DirectCompositionSurfaceTest, NoPresentTwice) {
EXPECT_TRUE(SUCCEEDED(swap_chain->GetLastPresentCount(&last_present_count))); EXPECT_TRUE(SUCCEEDED(swap_chain->GetLastPresentCount(&last_present_count)));
EXPECT_EQ(2u, last_present_count); EXPECT_EQ(2u, last_present_count);
// The size of the swapchain changed, so it should be recreated. // The image changed, we should get a new present
scoped_refptr<gl::GLImageDXGI> image_dxgi2(
new gl::GLImageDXGI(texture_size, nullptr));
image_dxgi2->SetTexture(texture, 0);
image_dxgi2->SetColorSpace(gfx::ColorSpace::CreateREC709());
ui::DCRendererLayerParams params2( ui::DCRendererLayerParams params2(
false, gfx::Rect(), 1, gfx::Transform(),
std::vector<scoped_refptr<gl::GLImage>>{image_dxgi2},
gfx::RectF(gfx::Rect(texture_size)), gfx::Rect(window_size), 0, 0, 1.0, 0,
false);
surface->ScheduleDCLayer(params2);
EXPECT_EQ(gfx::SwapResult::SWAP_ACK, surface->SwapBuffers(base::DoNothing()));
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain3 =
surface->GetLayerSwapChainForTesting(1);
EXPECT_TRUE(SUCCEEDED(swap_chain3->GetLastPresentCount(&last_present_count)));
// the present count should increase with the new present
EXPECT_EQ(3u, last_present_count);
context = nullptr;
DestroySurface(std::move(surface));
}
// Ensure no frequent swap chain recreation due to size changes
TEST(DirectCompositionSurfaceTest, NoFrequentSwapchainRecreation) {
if (!CheckIfDCSupported())
return;
TestImageTransportSurfaceDelegate delegate;
scoped_refptr<DirectCompositionSurfaceWin> surface(
new DirectCompositionSurfaceWin(nullptr, delegate.AsWeakPtr(),
ui::GetHiddenWindow()));
EXPECT_TRUE(surface->Initialize());
scoped_refptr<gl::GLContext> context =
gl::init::CreateGLContext(nullptr, surface.get(), gl::GLContextAttribs());
EXPECT_TRUE(context->MakeCurrent(surface.get()));
surface->SetEnableDCLayers(true);
surface->SetScaledOverlaysSupportedForTesting(true);
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
gl::QueryD3D11DeviceObjectFromANGLE();
// Initial frame
gfx::Size texture_size(50, 50);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
CreateNV12Texture(d3d11_device, texture_size, false);
scoped_refptr<gl::GLImageDXGI> image_dxgi(
new gl::GLImageDXGI(texture_size, nullptr));
image_dxgi->SetTexture(texture, 0);
image_dxgi->SetColorSpace(gfx::ColorSpace::CreateREC709());
gfx::Size window_size(100, 100);
ui::DCRendererLayerParams params(
false, gfx::Rect(), 1, gfx::Transform(),
std::vector<scoped_refptr<gl::GLImage>>{image_dxgi},
gfx::RectF(gfx::Rect(texture_size)), gfx::Rect(window_size), 0, 0, 1.0, 0,
false);
surface->ScheduleDCLayer(params);
EXPECT_EQ(gfx::SwapResult::SWAP_ACK, surface->SwapBuffers(base::DoNothing()));
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain =
surface->GetLayerSwapChainForTesting(1);
ASSERT_TRUE(swap_chain);
// A new frame with the same swapchian size
surface->ScheduleDCLayer(params);
EXPECT_EQ(gfx::SwapResult::SWAP_ACK, surface->SwapBuffers(base::DoNothing()));
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain2 =
surface->GetLayerSwapChainForTesting(1);
EXPECT_EQ(swap_chain2.Get(), swap_chain.Get());
// Start to change the swapchain size. It should not be recreated for the
// first few frames after the size change.
ui::DCRendererLayerParams params3(
false, gfx::Rect(), 1, gfx::Transform(), false, gfx::Rect(), 1, gfx::Transform(),
std::vector<scoped_refptr<gl::GLImage>>{image_dxgi}, std::vector<scoped_refptr<gl::GLImage>>{image_dxgi},
gfx::RectF(gfx::Rect(texture_size)), gfx::Rect(0, 0, 25, 25), 0, 0, 1.0, gfx::RectF(gfx::Rect(texture_size)), gfx::Rect(0, 0, 25, 25), 0, 0, 1.0,
0, false); 0, false);
surface->ScheduleDCLayer(params2);
EXPECT_EQ(gfx::SwapResult::SWAP_ACK, surface->SwapBuffers(base::DoNothing())); for (int i = 1; i <= 5; ++i) {
surface->ScheduleDCLayer(params3);
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface->SwapBuffers(base::DoNothing()));
}
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain3 = Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain3 =
surface->GetLayerSwapChainForTesting(1); surface->GetLayerSwapChainForTesting(1);
EXPECT_NE(swap_chain2.Get(), swap_chain3.Get()); EXPECT_EQ(swap_chain3.Get(), swap_chain.Get());
// 31 frames with the same swapchain size
int frames = surface->GetNumFramesBeforeSwapChainResizeForTesting() + 1;
for (int i = 6; i <= frames; ++i) {
surface->ScheduleDCLayer(params3);
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface->SwapBuffers(base::DoNothing()));
}
// The swapchain should be recreated now
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain4 =
surface->GetLayerSwapChainForTesting(1);
EXPECT_NE(swap_chain4.Get(), swap_chain.Get());
context = nullptr; context = nullptr;
DestroySurface(std::move(surface)); DestroySurface(std::move(surface));
...@@ -453,6 +548,11 @@ TEST(DirectCompositionSurfaceTest, SwapchainSizeWithScaledOverlays) { ...@@ -453,6 +548,11 @@ TEST(DirectCompositionSurfaceTest, SwapchainSizeWithScaledOverlays) {
EXPECT_EQ((int)Desc.BufferDesc.Width, texture_size.width()); EXPECT_EQ((int)Desc.BufferDesc.Width, texture_size.width());
EXPECT_EQ((int)Desc.BufferDesc.Height, texture_size.height()); EXPECT_EQ((int)Desc.BufferDesc.Height, texture_size.height());
// Clear SwapChainPresenters
// Must do Clear first because the swap chain won't resize immediately if
// a new size is given unless this is the very first time after Clear.
EXPECT_EQ(gfx::SwapResult::SWAP_ACK, surface->SwapBuffers(base::DoNothing()));
// The input texture size is bigger than the window size. // The input texture size is bigger than the window size.
window_size = gfx::Size(32, 48); window_size = gfx::Size(32, 48);
ui::DCRendererLayerParams params2( ui::DCRendererLayerParams params2(
......
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