blob: 93b1081d70925ef8550f840d0a77cd3555d308e3 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/gl/direct_composition_surface_win.h"
#include <wrl/client.h>
#include <wrl/implements.h>
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/power_monitor_test.h"
#include "base/test/scoped_feature_list.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/win/hidden_window.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/frame_data.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/gl/dc_layer_overlay_params.h"
#include "ui/gl/dc_layer_tree.h"
#include "ui/gl/direct_composition_child_surface_win.h"
#include "ui/gl/direct_composition_support.h"
#include "ui/gl/gl_angle_util_win.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_switches.h"
#include "ui/gl/gl_version_info.h"
#include "ui/gl/init/gl_factory.h"
#include "ui/gl/test/gl_test_helper.h"
#include "ui/platform_window/platform_window_delegate.h"
#include "ui/platform_window/win/win_window.h"
namespace gl {
namespace {
class TestPlatformDelegate : public ui::PlatformWindowDelegate {
public:
// ui::PlatformWindowDelegate implementation.
void OnBoundsChanged(const BoundsChange& change) override {}
void OnDamageRect(const gfx::Rect& damaged_region) override {}
void DispatchEvent(ui::Event* event) override {}
void OnCloseRequest() override {}
void OnClosed() override {}
void OnWindowStateChanged(ui::PlatformWindowState old_state,
ui::PlatformWindowState new_state) override {}
void OnLostCapture() override {}
void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) override {}
void OnWillDestroyAcceleratedWidget() override {}
void OnAcceleratedWidgetDestroyed() override {}
void OnActivationChanged(bool active) override {}
void OnMouseEnter() override {}
};
void RunPendingTasks(scoped_refptr<base::TaskRunner> task_runner) {
base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED);
task_runner->PostTask(
FROM_HERE, BindOnce(&base::WaitableEvent::Signal, Unretained(&done)));
done.Wait();
}
void DestroySurface(scoped_refptr<DirectCompositionSurfaceWin> surface) {
scoped_refptr<base::TaskRunner> task_runner =
surface->GetWindowTaskRunnerForTesting();
DCHECK(surface->HasOneRef());
surface = nullptr;
// Ensure that the ChildWindowWin posts the task to delete the thread to the
// main loop before doing RunUntilIdle. Otherwise the child threads could
// outlive the main thread.
RunPendingTasks(task_runner);
base::RunLoop().RunUntilIdle();
}
Microsoft::WRL::ComPtr<ID3D11Texture2D> CreateNV12Texture(
const Microsoft::WRL::ComPtr<ID3D11Device>& d3d11_device,
const gfx::Size& size) {
D3D11_TEXTURE2D_DESC desc = {};
desc.Width = size.width();
desc.Height = size.height();
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_NV12;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.SampleDesc.Count = 1;
desc.BindFlags = 0;
desc.MiscFlags = 0;
std::vector<char> image_data(size.width() * size.height() * 3 / 2);
// Y, U, and V should all be 160. Output color should be pink.
memset(&image_data[0], 160, size.width() * size.height() * 3 / 2);
D3D11_SUBRESOURCE_DATA data = {};
data.pSysMem = (const void*)&image_data[0];
data.SysMemPitch = size.width();
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture;
HRESULT hr = d3d11_device->CreateTexture2D(&desc, &data, &texture);
CHECK(SUCCEEDED(hr));
return texture;
}
bool AreColorsSimilar(int a, int b) {
// The precise colors may differ depending on the video processor, so allow
// a margin for error.
const int kMargin = 10;
return abs(SkColorGetA(a) - SkColorGetA(b)) < kMargin &&
abs(SkColorGetR(a) - SkColorGetR(b)) < kMargin &&
abs(SkColorGetG(a) - SkColorGetG(b)) < kMargin &&
abs(SkColorGetB(a) - SkColorGetB(b)) < kMargin;
}
} // namespace
class DirectCompositionSurfaceTest : public testing::Test,
public testing::WithParamInterface<bool> {
public:
DirectCompositionSurfaceTest() : parent_window_(ui::GetHiddenWindow()) {}
protected:
void SetUp() override {
SetupScopedFeatureList();
// These tests are assumed to run on battery.
fake_power_monitor_source_.SetOnBatteryPower(true);
// Without this, the following check always fails.
display_ = gl::init::InitializeGLNoExtensionsOneOff(
/*init_bindings=*/true, /*gpu_preference=*/gl::GpuPreference::kDefault);
if (!DirectCompositionSupported()) {
LOG(WARNING) << "DirectComposition not supported, skipping test.";
return;
}
surface_ = CreateDirectCompositionSurfaceWin();
context_ = CreateGLContext(surface_);
if (surface_)
surface_->SetEnableDCLayers(true);
SetDirectCompositionScaledOverlaysSupportedForTesting(false);
SetDirectCompositionOverlayFormatUsedForTesting(DXGI_FORMAT_NV12);
}
void TearDown() override {
context_ = nullptr;
if (surface_)
DestroySurface(std::move(surface_));
gl::init::ShutdownGL(display_, false);
}
virtual void SetupScopedFeatureList() {
if (GetParam()) {
scoped_feature_list_.InitAndEnableFeature(
features::kDCompVisualTreeOptimization);
} else {
scoped_feature_list_.InitAndDisableFeature(
features::kDCompVisualTreeOptimization);
}
}
scoped_refptr<DirectCompositionSurfaceWin>
CreateDirectCompositionSurfaceWin() {
DirectCompositionSurfaceWin::Settings settings;
scoped_refptr<DirectCompositionSurfaceWin> surface =
base::MakeRefCounted<DirectCompositionSurfaceWin>(
gl::GLSurfaceEGL::GetGLDisplayEGL(),
DirectCompositionSurfaceWin::VSyncCallback(), settings);
EXPECT_TRUE(surface->Initialize(GLSurfaceFormat()));
// ImageTransportSurfaceDelegate::AddChildWindowToBrowser() is called in
// production code here. However, to remove dependency from
// gpu/ipc/service/image_transport_surface_delegate.h, here we directly
// executes the required minimum code.
if (parent_window_)
::SetParent(surface->window(), parent_window_);
return surface;
}
scoped_refptr<GLContext> CreateGLContext(
scoped_refptr<DirectCompositionSurfaceWin> surface) {
scoped_refptr<GLContext> context =
gl::init::CreateGLContext(nullptr, surface.get(), GLContextAttribs());
EXPECT_TRUE(context->MakeCurrent(surface.get()));
return context;
}
HWND parent_window_;
scoped_refptr<DirectCompositionSurfaceWin> surface_;
scoped_refptr<GLContext> context_;
base::test::ScopedPowerMonitorTestSource fake_power_monitor_source_;
raw_ptr<GLDisplay> display_ = nullptr;
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_TEST_SUITE_P(All, DirectCompositionSurfaceTest, testing::Bool());
TEST_P(DirectCompositionSurfaceTest, TestMakeCurrent) {
if (!surface_)
return;
EXPECT_TRUE(
surface_->Resize(gfx::Size(100, 100), 1.0, gfx::ColorSpace(), true));
// First SetDrawRectangle must be full size of surface.
EXPECT_FALSE(surface_->SetDrawRectangle(gfx::Rect(0, 0, 50, 50)));
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(0, 0, 100, 100)));
// SetDrawRectangle can't be called again until swap.
EXPECT_FALSE(surface_->SetDrawRectangle(gfx::Rect(0, 0, 100, 100)));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
EXPECT_TRUE(context_->IsCurrent(surface_.get()));
// SetDrawRectangle must be contained within surface.
EXPECT_FALSE(surface_->SetDrawRectangle(gfx::Rect(0, 0, 101, 101)));
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(0, 0, 100, 100)));
EXPECT_TRUE(context_->IsCurrent(surface_.get()));
EXPECT_TRUE(
surface_->Resize(gfx::Size(50, 50), 1.0, gfx::ColorSpace(), true));
EXPECT_TRUE(context_->IsCurrent(surface_.get()));
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(0, 0, 50, 50)));
EXPECT_TRUE(context_->IsCurrent(surface_.get()));
scoped_refptr<DirectCompositionSurfaceWin> surface2 =
CreateDirectCompositionSurfaceWin();
scoped_refptr<GLContext> context2 = CreateGLContext(surface2.get());
surface2->SetEnableDCLayers(true);
EXPECT_TRUE(
surface2->Resize(gfx::Size(100, 100), 1.0, gfx::ColorSpace(), true));
// The previous IDCompositionSurface should be suspended when another
// surface is being drawn to.
EXPECT_TRUE(surface2->SetDrawRectangle(gfx::Rect(0, 0, 100, 100)));
EXPECT_TRUE(context2->IsCurrent(surface2.get()));
// It should be possible to switch back to the previous surface and
// unsuspend it.
EXPECT_TRUE(context_->MakeCurrent(surface_.get()));
context2 = nullptr;
DestroySurface(std::move(surface2));
}
// Tests that switching using EnableDCLayers works.
TEST_P(DirectCompositionSurfaceTest, DXGIDCLayerSwitch) {
if (!surface_)
return;
surface_->SetEnableDCLayers(false);
EXPECT_TRUE(
surface_->Resize(gfx::Size(100, 100), 1.0, gfx::ColorSpace(), true));
EXPECT_FALSE(surface_->GetBackbufferSwapChainForTesting());
// First SetDrawRectangle must be full size of surface for DXGI swapchain.
EXPECT_FALSE(surface_->SetDrawRectangle(gfx::Rect(0, 0, 50, 50)));
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(0, 0, 100, 100)));
EXPECT_TRUE(surface_->GetBackbufferSwapChainForTesting());
// SetDrawRectangle and SetEnableDCLayers can't be called again until swap.
EXPECT_FALSE(surface_->SetDrawRectangle(gfx::Rect(0, 0, 100, 100)));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
EXPECT_TRUE(context_->IsCurrent(surface_.get()));
surface_->SetEnableDCLayers(true);
// Surface switched to use IDCompositionSurface, so must draw to entire
// surface.
EXPECT_FALSE(surface_->SetDrawRectangle(gfx::Rect(0, 0, 50, 50)));
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(0, 0, 100, 100)));
EXPECT_FALSE(surface_->GetBackbufferSwapChainForTesting());
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
EXPECT_TRUE(context_->IsCurrent(surface_.get()));
surface_->SetEnableDCLayers(false);
// Surface switched to use IDXGISwapChain, so must draw to entire surface.
EXPECT_FALSE(surface_->SetDrawRectangle(gfx::Rect(0, 0, 50, 50)));
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(0, 0, 100, 100)));
EXPECT_TRUE(surface_->GetBackbufferSwapChainForTesting());
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
EXPECT_TRUE(context_->IsCurrent(surface_.get()));
}
// Ensure that the swapchain's alpha is correct.
TEST_P(DirectCompositionSurfaceTest, SwitchAlpha) {
if (!surface_)
return;
surface_->SetEnableDCLayers(false);
EXPECT_TRUE(
surface_->Resize(gfx::Size(100, 100), 1.0, gfx::ColorSpace(), true));
EXPECT_FALSE(surface_->GetBackbufferSwapChainForTesting());
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(0, 0, 100, 100)));
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain =
surface_->GetBackbufferSwapChainForTesting();
ASSERT_TRUE(swap_chain);
DXGI_SWAP_CHAIN_DESC1 desc;
swap_chain->GetDesc1(&desc);
EXPECT_EQ(DXGI_ALPHA_MODE_PREMULTIPLIED, desc.AlphaMode);
// Resize to the same parameters should have no effect.
EXPECT_TRUE(
surface_->Resize(gfx::Size(100, 100), 1.0, gfx::ColorSpace(), true));
EXPECT_TRUE(surface_->GetBackbufferSwapChainForTesting());
EXPECT_TRUE(
surface_->Resize(gfx::Size(100, 100), 1.0, gfx::ColorSpace(), false));
EXPECT_FALSE(surface_->GetBackbufferSwapChainForTesting());
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(0, 0, 100, 100)));
swap_chain = surface_->GetBackbufferSwapChainForTesting();
ASSERT_TRUE(swap_chain);
swap_chain->GetDesc1(&desc);
EXPECT_EQ(DXGI_ALPHA_MODE_IGNORE, desc.AlphaMode);
}
// Ensure that the overlay image isn't presented again unless it changes.
TEST_P(DirectCompositionSurfaceTest, NoPresentTwice) {
if (!surface_)
return;
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
GetDirectCompositionD3D11Device();
gfx::Size texture_size(50, 50);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
CreateNV12Texture(d3d11_device, texture_size);
EXPECT_NE(texture, nullptr);
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(texture_size);
params->quad_rect = gfx::Rect(100, 100);
params->color_space = gfx::ColorSpace::CreateREC709();
surface_->ScheduleDCLayer(std::move(params));
}
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain =
surface_->GetLayerSwapChainForTesting(0);
ASSERT_FALSE(swap_chain);
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
swap_chain = surface_->GetLayerSwapChainForTesting(0);
ASSERT_TRUE(swap_chain);
UINT last_present_count = 0;
EXPECT_TRUE(SUCCEEDED(swap_chain->GetLastPresentCount(&last_present_count)));
// One present is normal, and a second present because it's the first frame
// and the other buffer needs to be drawn to.
EXPECT_EQ(2u, last_present_count);
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(texture_size);
params->quad_rect = gfx::Rect(100, 100);
params->color_space = gfx::ColorSpace::CreateREC709();
surface_->ScheduleDCLayer(std::move(params));
}
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain2 =
surface_->GetLayerSwapChainForTesting(0);
EXPECT_EQ(swap_chain2.Get(), swap_chain.Get());
// It's the same image, so it should have the same swapchain.
EXPECT_TRUE(SUCCEEDED(swap_chain->GetLastPresentCount(&last_present_count)));
EXPECT_EQ(2u, last_present_count);
// The image changed, we should get a new present.
texture = CreateNV12Texture(d3d11_device, texture_size);
EXPECT_NE(texture, nullptr);
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(texture_size);
params->quad_rect = gfx::Rect(100, 100);
params->color_space = gfx::ColorSpace::CreateREC709();
surface_->ScheduleDCLayer(std::move(params));
}
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain3 =
surface_->GetLayerSwapChainForTesting(0);
EXPECT_TRUE(SUCCEEDED(swap_chain3->GetLastPresentCount(&last_present_count)));
// the present count should increase with the new present
EXPECT_EQ(3u, last_present_count);
}
// Ensure the swapchain size is set to the correct size if HW overlay scaling
// is support - swapchain should be set to the onscreen video size.
TEST_P(DirectCompositionSurfaceTest, SwapchainSizeWithScaledOverlays) {
if (!surface_)
return;
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
GetDirectCompositionD3D11Device();
gfx::Size texture_size(64, 64);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
CreateNV12Texture(d3d11_device, texture_size);
// HW supports scaled overlays.
// The input texture size is maller than the window size.
SetDirectCompositionScaledOverlaysSupportedForTesting(true);
// Onscreen quad.
gfx::Rect quad_rect = gfx::Rect(100, 100);
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(texture_size);
params->quad_rect = quad_rect;
params->color_space = gfx::ColorSpace::CreateREC709();
surface_->ScheduleDCLayer(std::move(params));
}
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain =
surface_->GetLayerSwapChainForTesting(0);
ASSERT_TRUE(swap_chain);
DXGI_SWAP_CHAIN_DESC desc;
EXPECT_TRUE(SUCCEEDED(swap_chain->GetDesc(&desc)));
// Onscreen quad_rect.size is (100, 100).
EXPECT_EQ(100u, desc.BufferDesc.Width);
EXPECT_EQ(100u, desc.BufferDesc.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(), gfx::FrameData()));
// The input texture size is bigger than the window size.
quad_rect = gfx::Rect(32, 48);
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(texture_size);
params->quad_rect = quad_rect;
params->color_space = gfx::ColorSpace::CreateREC709();
surface_->ScheduleDCLayer(std::move(params));
}
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain2 =
surface_->GetLayerSwapChainForTesting(0);
ASSERT_TRUE(swap_chain2);
EXPECT_TRUE(SUCCEEDED(swap_chain2->GetDesc(&desc)));
// Onscreen quad_rect.size is (32, 48).
EXPECT_EQ(32u, desc.BufferDesc.Width);
EXPECT_EQ(48u, desc.BufferDesc.Height);
}
// Ensure the swapchain size is set to the correct size if HW overlay scaling
// is not support - swapchain should be the onscreen video size.
TEST_P(DirectCompositionSurfaceTest, SwapchainSizeWithoutScaledOverlays) {
if (!surface_)
return;
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
GetDirectCompositionD3D11Device();
gfx::Size texture_size(80, 80);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
CreateNV12Texture(d3d11_device, texture_size);
gfx::Rect quad_rect = gfx::Rect(42, 42);
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(texture_size);
params->quad_rect = quad_rect;
params->color_space = gfx::ColorSpace::CreateREC709();
surface_->ScheduleDCLayer(std::move(params));
}
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain =
surface_->GetLayerSwapChainForTesting(0);
ASSERT_TRUE(swap_chain);
DXGI_SWAP_CHAIN_DESC desc;
EXPECT_TRUE(SUCCEEDED(swap_chain->GetDesc(&desc)));
// Onscreen quad_rect.size is (42, 42).
EXPECT_EQ(42u, desc.BufferDesc.Width);
EXPECT_EQ(42u, desc.BufferDesc.Height);
// The input texture size is smaller than the window size.
quad_rect = gfx::Rect(124, 136);
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(texture_size);
params->quad_rect = quad_rect;
params->color_space = gfx::ColorSpace::CreateREC709();
surface_->ScheduleDCLayer(std::move(params));
}
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain2 =
surface_->GetLayerSwapChainForTesting(0);
ASSERT_TRUE(swap_chain2);
EXPECT_TRUE(SUCCEEDED(swap_chain2->GetDesc(&desc)));
// Onscreen quad_rect.size is (124, 136).
EXPECT_EQ(124u, desc.BufferDesc.Width);
EXPECT_EQ(136u, desc.BufferDesc.Height);
}
// Test protected video flags
TEST_P(DirectCompositionSurfaceTest, ProtectedVideos) {
if (!surface_)
return;
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
GetDirectCompositionD3D11Device();
gfx::Size texture_size(1280, 720);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
CreateNV12Texture(d3d11_device, texture_size);
gfx::Size window_size(640, 360);
// Clear video
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->quad_rect = gfx::Rect(window_size);
params->content_rect = gfx::Rect(texture_size);
params->color_space = gfx::ColorSpace::CreateREC709();
params->protected_video_type = gfx::ProtectedVideoType::kClear;
surface_->ScheduleDCLayer(std::move(params));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain =
surface_->GetLayerSwapChainForTesting(0);
ASSERT_TRUE(swap_chain);
DXGI_SWAP_CHAIN_DESC desc;
EXPECT_TRUE(SUCCEEDED(swap_chain->GetDesc(&desc)));
auto display_only_flag = desc.Flags & DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY;
auto hw_protected_flag = desc.Flags & DXGI_SWAP_CHAIN_FLAG_HW_PROTECTED;
EXPECT_EQ(0u, display_only_flag);
EXPECT_EQ(0u, hw_protected_flag);
}
// Software protected video
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->quad_rect = gfx::Rect(window_size);
params->content_rect = gfx::Rect(texture_size);
params->color_space = gfx::ColorSpace::CreateREC709();
params->protected_video_type = gfx::ProtectedVideoType::kSoftwareProtected;
surface_->ScheduleDCLayer(std::move(params));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain =
surface_->GetLayerSwapChainForTesting(0);
ASSERT_TRUE(swap_chain);
DXGI_SWAP_CHAIN_DESC Desc;
EXPECT_TRUE(SUCCEEDED(swap_chain->GetDesc(&Desc)));
auto display_only_flag = Desc.Flags & DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY;
auto hw_protected_flag = Desc.Flags & DXGI_SWAP_CHAIN_FLAG_HW_PROTECTED;
EXPECT_EQ(DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY, display_only_flag);
EXPECT_EQ(0u, hw_protected_flag);
}
// TODO(magchen): Add a hardware protected video test when hardware procted
// video support is enabled by defaut in the Intel driver and Chrome
}
class DirectCompositionPixelTest : public DirectCompositionSurfaceTest {
public:
DirectCompositionPixelTest()
: window_(&platform_delegate_, gfx::Rect(100, 100)) {
parent_window_ = window_.hwnd();
}
protected:
void SetUp() override {
static_cast<ui::PlatformWindow*>(&window_)->Show();
DirectCompositionSurfaceTest::SetUp();
}
void TearDown() override {
// Test harness times out without DestroyWindow() here.
if (IsWindow(parent_window_))
DestroyWindow(parent_window_);
DirectCompositionSurfaceTest::TearDown();
}
void InitializeForPixelTest(const gfx::Size& window_size,
const gfx::Size& texture_size,
const gfx::Rect& content_rect,
const gfx::Rect& quad_rect) {
EXPECT_TRUE(surface_->Resize(window_size, 1.0, gfx::ColorSpace(), true));
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(window_size)));
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
GetDirectCompositionD3D11Device();
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
CreateNV12Texture(d3d11_device, texture_size);
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = content_rect;
params->quad_rect = quad_rect;
params->color_space = gfx::ColorSpace::CreateREC709();
surface_->ScheduleDCLayer(std::move(params));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
Sleep(1000);
}
void PixelTestSwapChain(bool layers_enabled) {
if (!surface_)
return;
if (!layers_enabled)
surface_->SetEnableDCLayers(false);
gfx::Size window_size(100, 100);
EXPECT_TRUE(surface_->Resize(window_size, 1.0, gfx::ColorSpace(), true));
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(window_size)));
glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
// Ensure DWM swap completed.
Sleep(1000);
SkColor expected_color = SK_ColorRED;
SkColor actual_color =
GLTestHelper::ReadBackWindowPixel(window_.hwnd(), gfx::Point(75, 75));
EXPECT_EQ(expected_color, actual_color)
<< std::hex << "Expected " << expected_color << " Actual "
<< actual_color;
EXPECT_TRUE(context_->IsCurrent(surface_.get()));
}
TestPlatformDelegate platform_delegate_;
ui::WinWindow window_;
};
INSTANTIATE_TEST_SUITE_P(All, DirectCompositionPixelTest, testing::Bool());
TEST_P(DirectCompositionPixelTest, DCLayersEnabled) {
PixelTestSwapChain(true);
}
TEST_P(DirectCompositionPixelTest, DCLayersDisabled) {
PixelTestSwapChain(false);
}
class DirectCompositionVideoPixelTest : public DirectCompositionPixelTest {
protected:
void TestVideo(const gfx::ColorSpace& color_space,
SkColor expected_color,
bool check_color) {
if (!surface_)
return;
gfx::Size window_size(100, 100);
EXPECT_TRUE(surface_->Resize(window_size, 1.0, gfx::ColorSpace(), true));
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
GetDirectCompositionD3D11Device();
gfx::Size texture_size(50, 50);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
CreateNV12Texture(d3d11_device, texture_size);
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(texture_size);
params->quad_rect = gfx::Rect(texture_size);
params->color_space = color_space;
surface_->ScheduleDCLayer(std::move(params));
}
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
// Scaling up the swapchain with the same image should cause it to be
// transformed again, but not presented again.
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(texture_size);
params->quad_rect = gfx::Rect(window_size);
params->color_space = color_space;
surface_->ScheduleDCLayer(std::move(params));
}
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
Sleep(1000);
if (check_color) {
SkColor actual_color =
GLTestHelper::ReadBackWindowPixel(window_.hwnd(), gfx::Point(75, 75));
EXPECT_TRUE(AreColorsSimilar(expected_color, actual_color))
<< std::hex << "Expected " << expected_color << " Actual "
<< actual_color;
}
}
};
INSTANTIATE_TEST_SUITE_P(All, DirectCompositionVideoPixelTest, testing::Bool());
TEST_P(DirectCompositionVideoPixelTest, BT601) {
TestVideo(gfx::ColorSpace::CreateREC601(), SkColorSetRGB(0xdb, 0x81, 0xe8),
true);
}
TEST_P(DirectCompositionVideoPixelTest, BT709) {
TestVideo(gfx::ColorSpace::CreateREC709(), SkColorSetRGB(0xe1, 0x90, 0xeb),
true);
}
TEST_P(DirectCompositionVideoPixelTest, SRGB) {
// SRGB doesn't make sense on an NV12 input, but don't crash.
TestVideo(gfx::ColorSpace::CreateSRGB(), SK_ColorTRANSPARENT, false);
}
TEST_P(DirectCompositionVideoPixelTest, SCRGBLinear) {
// SCRGB doesn't make sense on an NV12 input, but don't crash.
TestVideo(gfx::ColorSpace::CreateSRGBLinear(), SK_ColorTRANSPARENT, false);
}
TEST_P(DirectCompositionVideoPixelTest, InvalidColorSpace) {
// Invalid color space should be treated as BT.709
TestVideo(gfx::ColorSpace(), SkColorSetRGB(0xe1, 0x90, 0xeb), true);
}
TEST_P(DirectCompositionPixelTest, SoftwareVideoSwapchain) {
if (!surface_)
return;
gfx::Size window_size(100, 100);
EXPECT_TRUE(surface_->Resize(window_size, 1.0, gfx::ColorSpace(), true));
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
GetDirectCompositionD3D11Device();
gfx::Size y_size(50, 50);
size_t stride = y_size.width();
std::vector<uint8_t> nv12_pixmap(stride * 3 * y_size.height() / 2, 0xff);
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image =
DCLayerOverlayImage(y_size, nv12_pixmap.data(), stride);
params->content_rect = gfx::Rect(y_size);
params->quad_rect = gfx::Rect(window_size);
params->color_space = gfx::ColorSpace::CreateREC709();
surface_->ScheduleDCLayer(std::move(params));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
Sleep(1000);
SkColor expected_color = SkColorSetRGB(0xff, 0xb7, 0xff);
SkColor actual_color =
GLTestHelper::ReadBackWindowPixel(window_.hwnd(), gfx::Point(75, 75));
EXPECT_TRUE(AreColorsSimilar(expected_color, actual_color))
<< std::hex << "Expected " << expected_color << " Actual "
<< actual_color;
}
TEST_P(DirectCompositionPixelTest, VideoHandleSwapchain) {
if (!surface_)
return;
gfx::Size window_size(100, 100);
gfx::Size texture_size(50, 50);
gfx::Rect content_rect(texture_size);
gfx::Rect quad_rect(window_size);
InitializeForPixelTest(window_size, texture_size, content_rect, quad_rect);
SkColor expected_color = SkColorSetRGB(0xe1, 0x90, 0xeb);
SkColor actual_color =
GLTestHelper::ReadBackWindowPixel(window_.hwnd(), gfx::Point(75, 75));
EXPECT_TRUE(AreColorsSimilar(expected_color, actual_color))
<< std::hex << "Expected " << expected_color << " Actual "
<< actual_color;
}
TEST_P(DirectCompositionPixelTest, SkipVideoLayerEmptyBoundsRect) {
if (!surface_)
return;
gfx::Size window_size(100, 100);
gfx::Size texture_size(50, 50);
gfx::Rect content_rect(texture_size);
gfx::Rect quad_rect; // Layer with empty bounds rect.
InitializeForPixelTest(window_size, texture_size, content_rect, quad_rect);
// No color is written since the visual committed to DirectComposition has no
// content.
SkColor expected_color = SK_ColorBLACK;
SkColor actual_color =
GLTestHelper::ReadBackWindowPixel(window_.hwnd(), gfx::Point(75, 75));
EXPECT_TRUE(AreColorsSimilar(expected_color, actual_color))
<< std::hex << "Expected " << expected_color << " Actual "
<< actual_color;
}
TEST_P(DirectCompositionPixelTest, SkipVideoLayerEmptyContentsRect) {
if (!surface_)
return;
// Swap chain size is overridden to onscreen size only if scaled overlays
// are supported.
SetDirectCompositionScaledOverlaysSupportedForTesting(true);
gfx::Size window_size(100, 100);
EXPECT_TRUE(surface_->Resize(window_size, 1.0, gfx::ColorSpace(), true));
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(window_size)));
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
GetDirectCompositionD3D11Device();
gfx::Size texture_size(50, 50);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
CreateNV12Texture(d3d11_device, texture_size);
// Layer with empty content rect.
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->quad_rect = gfx::Rect(window_size);
params->color_space = gfx::ColorSpace::CreateREC709();
surface_->ScheduleDCLayer(std::move(params));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
Sleep(1000);
// No color is written since the visual committed to DirectComposition has no
// content.
SkColor expected_color = SK_ColorBLACK;
SkColor actual_color =
GLTestHelper::ReadBackWindowPixel(window_.hwnd(), gfx::Point(75, 75));
EXPECT_TRUE(AreColorsSimilar(expected_color, actual_color))
<< std::hex << "Expected " << expected_color << " Actual "
<< actual_color;
}
TEST_P(DirectCompositionPixelTest, NV12SwapChain) {
if (!surface_)
return;
// Swap chain size is overridden to onscreen rect size only if scaled overlays
// are supported.
SetDirectCompositionScaledOverlaysSupportedForTesting(true);
gfx::Size window_size(100, 100);
gfx::Size texture_size(50, 50);
// Pass content rect with odd with and height. Surface should round up
// width and height when creating swap chain.
gfx::Rect content_rect(0, 0, 49, 49);
gfx::Rect quad_rect(window_size);
InitializeForPixelTest(window_size, texture_size, content_rect, quad_rect);
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain =
surface_->GetLayerSwapChainForTesting(0);
ASSERT_TRUE(swap_chain);
DXGI_SWAP_CHAIN_DESC1 desc;
EXPECT_TRUE(SUCCEEDED(swap_chain->GetDesc1(&desc)));
// Onscreen window_size is (100, 100).
EXPECT_EQ(DXGI_FORMAT_NV12, desc.Format);
EXPECT_EQ(100u, desc.Width);
EXPECT_EQ(100u, desc.Height);
SkColor expected_color = SkColorSetRGB(0xe1, 0x90, 0xeb);
SkColor actual_color =
GLTestHelper::ReadBackWindowPixel(window_.hwnd(), gfx::Point(75, 75));
EXPECT_TRUE(AreColorsSimilar(expected_color, actual_color))
<< std::hex << "Expected " << expected_color << " Actual "
<< actual_color;
}
TEST_P(DirectCompositionPixelTest, YUY2SwapChain) {
if (!surface_)
return;
// CreateSwapChainForCompositionSurfaceHandle fails with YUY2 format on
// Win10/AMD bot (Radeon RX550). See https://crbug.com/967860.
if (context_ && context_->GetVersionInfo() &&
context_->GetVersionInfo()->driver_vendor.find("AMD") !=
std::string::npos)
return;
// Swap chain size is overridden to onscreen rect size only if scaled overlays
// are supported.
SetDirectCompositionScaledOverlaysSupportedForTesting(true);
// By default NV12 is used, so set it to YUY2 explicitly.
SetDirectCompositionOverlayFormatUsedForTesting(DXGI_FORMAT_YUY2);
gfx::Size window_size(100, 100);
gfx::Size texture_size(50, 50);
// Pass content rect with odd with and height. Surface should round up
// width and height when creating swap chain.
gfx::Rect content_rect(0, 0, 49, 49);
gfx::Rect quad_rect(window_size);
InitializeForPixelTest(window_size, texture_size, content_rect, quad_rect);
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain =
surface_->GetLayerSwapChainForTesting(0);
ASSERT_TRUE(swap_chain);
DXGI_SWAP_CHAIN_DESC1 desc;
EXPECT_TRUE(SUCCEEDED(swap_chain->GetDesc1(&desc)));
// Onscreen window_size is (100, 100).
EXPECT_EQ(DXGI_FORMAT_YUY2, desc.Format);
EXPECT_EQ(100u, desc.Width);
EXPECT_EQ(100u, desc.Height);
SkColor expected_color = SkColorSetRGB(0xe1, 0x90, 0xeb);
SkColor actual_color =
GLTestHelper::ReadBackWindowPixel(window_.hwnd(), gfx::Point(75, 75));
EXPECT_TRUE(AreColorsSimilar(expected_color, actual_color))
<< std::hex << "Expected " << expected_color << " Actual "
<< actual_color;
}
TEST_P(DirectCompositionPixelTest, NonZeroBoundsOffset) {
if (!surface_)
return;
// Swap chain size is overridden to onscreen rect size only if scaled overlays
// are supported.
SetDirectCompositionScaledOverlaysSupportedForTesting(true);
gfx::Size window_size(100, 100);
gfx::Size texture_size(50, 50);
gfx::Rect content_rect(texture_size);
gfx::Rect quad_rect(gfx::Point(25, 25), texture_size);
InitializeForPixelTest(window_size, texture_size, content_rect, quad_rect);
SkColor video_color = SkColorSetRGB(0xe1, 0x90, 0xeb);
struct {
gfx::Point point;
SkColor expected_color;
} test_cases[] = {
// Outside bounds
{{24, 24}, SK_ColorBLACK},
{{75, 75}, SK_ColorBLACK},
// Inside bounds
{{25, 25}, video_color},
{{74, 74}, video_color},
};
auto pixels = GLTestHelper::ReadBackWindow(window_.hwnd(), window_size);
for (const auto& test_case : test_cases) {
const auto& point = test_case.point;
const auto& expected_color = test_case.expected_color;
SkColor actual_color = GLTestHelper::GetColorAtPoint(pixels, point);
EXPECT_TRUE(AreColorsSimilar(expected_color, actual_color))
<< std::hex << "Expected " << expected_color << " Actual "
<< actual_color << " at " << point.ToString();
}
}
TEST_P(DirectCompositionPixelTest, ResizeVideoLayer) {
if (!surface_)
return;
// Swap chain size is overridden to onscreen rect size only if scaled overlays
// are supported.
SetDirectCompositionScaledOverlaysSupportedForTesting(true);
gfx::Size window_size(100, 100);
EXPECT_TRUE(surface_->Resize(window_size, 1.0, gfx::ColorSpace(), true));
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(window_size)));
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
GetDirectCompositionD3D11Device();
gfx::Size texture_size(50, 50);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
CreateNV12Texture(d3d11_device, texture_size);
// (1) Test if swap chain is overridden to window size (100, 100).
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(texture_size);
params->quad_rect = gfx::Rect(window_size);
params->color_space = gfx::ColorSpace::CreateREC709();
surface_->ScheduleDCLayer(std::move(params));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
}
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain =
surface_->GetLayerSwapChainForTesting(0);
ASSERT_TRUE(swap_chain);
DXGI_SWAP_CHAIN_DESC1 desc;
EXPECT_TRUE(SUCCEEDED(swap_chain->GetDesc1(&desc)));
// Onscreen window_size is (100, 100).
EXPECT_EQ(100u, desc.Width);
EXPECT_EQ(100u, desc.Height);
// (2) Test if swap chain is overridden to window size (100, 100).
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(30, 30);
params->quad_rect = gfx::Rect(window_size);
params->color_space = gfx::ColorSpace::CreateREC709();
surface_->ScheduleDCLayer(std::move(params));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
}
swap_chain = surface_->GetLayerSwapChainForTesting(0);
EXPECT_TRUE(SUCCEEDED(swap_chain->GetDesc1(&desc)));
// Onscreen window_size is (100, 100).
EXPECT_EQ(100u, desc.Width);
EXPECT_EQ(100u, desc.Height);
// (3) Test if swap chain is adjusted to fit the monitor when overlay scaling
// is not supported and video on-screen size is slightly smaller than the
// monitor. Clipping is on.
SetDirectCompositionScaledOverlaysSupportedForTesting(false);
gfx::Size monitor_size = window_size;
SetDirectCompositionMonitorInfoForTesting(1, window_size);
gfx::Rect on_screen_rect =
gfx::Rect(0, 0, monitor_size.width() - 2, monitor_size.height() - 2);
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(50, 50);
params->quad_rect = on_screen_rect;
params->clip_rect = on_screen_rect;
params->color_space = gfx::ColorSpace::CreateREC709();
surface_->ScheduleDCLayer(std::move(params));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
}
// Swap chain is set to monitor/onscreen size.
swap_chain = surface_->GetLayerSwapChainForTesting(0);
EXPECT_TRUE(SUCCEEDED(swap_chain->GetDesc1(&desc)));
EXPECT_EQ(static_cast<UINT>(monitor_size.width()), desc.Width);
EXPECT_EQ(static_cast<UINT>(monitor_size.height()), desc.Height);
gfx::Transform transform;
gfx::Point offset;
gfx::Rect clip_rect;
surface_->GetSwapChainVisualInfoForTesting(0, &transform, &offset,
&clip_rect);
EXPECT_TRUE(transform.IsIdentity());
EXPECT_EQ(gfx::Rect(monitor_size), clip_rect);
// (4) Test if the final on-screen size is adjusted to fit the monitor when
// overlay scaling is supported and video on-screen size is slightly bigger
// than the monitor. Clipping is off.
SetDirectCompositionScaledOverlaysSupportedForTesting(true);
on_screen_rect =
gfx::Rect(0, 0, monitor_size.width() + 2, monitor_size.height() + 2);
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(50, 50);
params->quad_rect = on_screen_rect;
params->color_space = gfx::ColorSpace::CreateREC709();
surface_->ScheduleDCLayer(std::move(params));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
}
// Swap chain is set to monitor size (100, 100).
swap_chain = surface_->GetLayerSwapChainForTesting(0);
EXPECT_TRUE(SUCCEEDED(swap_chain->GetDesc1(&desc)));
EXPECT_EQ(100u, desc.Width);
EXPECT_EQ(100u, desc.Height);
// Make sure the new transform matrix is adjusted, so it transforms the swap
// chain to |new_on_screen_rect| which fits the monitor.
surface_->GetSwapChainVisualInfoForTesting(0, &transform, &offset,
&clip_rect);
EXPECT_EQ(gfx::Rect(monitor_size), transform.MapRect(gfx::Rect(100, 100)));
}
TEST_P(DirectCompositionPixelTest, SwapChainImage) {
if (!surface_)
return;
// Fails on AMD RX 5500 XT. https://crbug.com/1152565.
if (context_ && context_->GetVersionInfo() &&
context_->GetVersionInfo()->driver_vendor.find("AMD") !=
std::string::npos)
return;
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
GetDirectCompositionD3D11Device();
ASSERT_TRUE(d3d11_device);
Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
d3d11_device.As(&dxgi_device);
ASSERT_TRUE(dxgi_device);
Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
dxgi_device->GetAdapter(&dxgi_adapter);
ASSERT_TRUE(dxgi_adapter);
Microsoft::WRL::ComPtr<IDXGIFactory2> dxgi_factory;
dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory));
ASSERT_TRUE(dxgi_factory);
gfx::Size swap_chain_size(50, 50);
DXGI_SWAP_CHAIN_DESC1 desc = {};
desc.Width = swap_chain_size.width();
desc.Height = swap_chain_size.height();
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.Stereo = FALSE;
desc.SampleDesc.Count = 1;
desc.BufferCount = 2;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
desc.Scaling = DXGI_SCALING_STRETCH;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
desc.Flags = 0;
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain;
ASSERT_TRUE(SUCCEEDED(dxgi_factory->CreateSwapChainForComposition(
d3d11_device.Get(), &desc, nullptr, &swap_chain)));
ASSERT_TRUE(swap_chain);
Microsoft::WRL::ComPtr<ID3D11Texture2D> front_buffer_texture;
ASSERT_TRUE(SUCCEEDED(
swap_chain->GetBuffer(1u, IID_PPV_ARGS(&front_buffer_texture))));
ASSERT_TRUE(front_buffer_texture);
Microsoft::WRL::ComPtr<ID3D11Texture2D> back_buffer_texture;
ASSERT_TRUE(
SUCCEEDED(swap_chain->GetBuffer(0u, IID_PPV_ARGS(&back_buffer_texture))));
ASSERT_TRUE(back_buffer_texture);
Microsoft::WRL::ComPtr<ID3D11RenderTargetView> rtv;
ASSERT_TRUE(SUCCEEDED(d3d11_device->CreateRenderTargetView(
back_buffer_texture.Get(), nullptr, &rtv)));
ASSERT_TRUE(rtv);
Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
d3d11_device->GetImmediateContext(&context);
ASSERT_TRUE(context);
gfx::Size window_size(100, 100);
EXPECT_TRUE(surface_->Resize(window_size, 1.0, gfx::ColorSpace(), true));
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(window_size)));
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
DXGI_PRESENT_PARAMETERS present_params = {};
present_params.DirtyRectsCount = 0;
present_params.pDirtyRects = nullptr;
// Clear to red and present.
{
float clear_color[] = {1.0, 0.0, 0.0, 1.0};
context->ClearRenderTargetView(rtv.Get(), clear_color);
ASSERT_TRUE(SUCCEEDED(swap_chain->Present1(0, 0, &present_params)));
auto dc_layer_params = std::make_unique<DCLayerOverlayParams>();
dc_layer_params->overlay_image =
DCLayerOverlayImage(swap_chain_size, swap_chain);
dc_layer_params->content_rect = gfx::Rect(swap_chain_size);
dc_layer_params->quad_rect = gfx::Rect(window_size);
dc_layer_params->color_space = gfx::ColorSpace::CreateSRGB();
surface_->ScheduleDCLayer(std::move(dc_layer_params));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
SkColor expected_color = SK_ColorRED;
SkColor actual_color =
GLTestHelper::ReadBackWindowPixel(window_.hwnd(), gfx::Point(75, 75));
EXPECT_TRUE(AreColorsSimilar(expected_color, actual_color))
<< std::hex << "Expected " << expected_color << " Actual "
<< actual_color;
}
// Clear to green and present.
{
float clear_color[] = {0.0, 1.0, 0.0, 1.0};
context->ClearRenderTargetView(rtv.Get(), clear_color);
ASSERT_TRUE(SUCCEEDED(swap_chain->Present1(0, 0, &present_params)));
auto dc_layer_params = std::make_unique<DCLayerOverlayParams>();
dc_layer_params->overlay_image =
DCLayerOverlayImage(swap_chain_size, swap_chain);
dc_layer_params->content_rect = gfx::Rect(swap_chain_size);
dc_layer_params->quad_rect = gfx::Rect(window_size);
dc_layer_params->color_space = gfx::ColorSpace::CreateSRGB();
surface_->ScheduleDCLayer(std::move(dc_layer_params));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
SkColor expected_color = SK_ColorGREEN;
SkColor actual_color =
GLTestHelper::ReadBackWindowPixel(window_.hwnd(), gfx::Point(75, 75));
EXPECT_TRUE(AreColorsSimilar(expected_color, actual_color))
<< std::hex << "Expected " << expected_color << " Actual "
<< actual_color;
}
// Present without clearing. This will flip front and back buffers so the
// previous rendered contents (red) will become visible again.
{
ASSERT_TRUE(SUCCEEDED(swap_chain->Present1(0, 0, &present_params)));
auto dc_layer_params = std::make_unique<DCLayerOverlayParams>();
dc_layer_params->overlay_image =
DCLayerOverlayImage(swap_chain_size, swap_chain);
dc_layer_params->content_rect = gfx::Rect(swap_chain_size);
dc_layer_params->quad_rect = gfx::Rect(window_size);
dc_layer_params->color_space = gfx::ColorSpace::CreateSRGB();
surface_->ScheduleDCLayer(std::move(dc_layer_params));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
SkColor expected_color = SK_ColorRED;
SkColor actual_color =
GLTestHelper::ReadBackWindowPixel(window_.hwnd(), gfx::Point(75, 75));
EXPECT_TRUE(AreColorsSimilar(expected_color, actual_color))
<< std::hex << "Expected " << expected_color << " Actual "
<< actual_color;
}
// Clear to blue without present.
{
float clear_color[] = {0.0, 0.0, 1.0, 1.0};
context->ClearRenderTargetView(rtv.Get(), clear_color);
auto dc_layer_params = std::make_unique<DCLayerOverlayParams>();
dc_layer_params->overlay_image =
DCLayerOverlayImage(swap_chain_size, swap_chain);
dc_layer_params->content_rect = gfx::Rect(swap_chain_size);
dc_layer_params->quad_rect = gfx::Rect(window_size);
dc_layer_params->color_space = gfx::ColorSpace::CreateSRGB();
surface_->ScheduleDCLayer(std::move(dc_layer_params));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
SkColor expected_color = SK_ColorRED;
SkColor actual_color =
GLTestHelper::ReadBackWindowPixel(window_.hwnd(), gfx::Point(75, 75));
EXPECT_TRUE(AreColorsSimilar(expected_color, actual_color))
<< std::hex << "Expected " << expected_color << " Actual "
<< actual_color;
}
}
// Offsets BeginDraw update rect so that the returned update offset is also
// offset by at least the same amount from the original update rect.
class DrawOffsetOverridingDCompositionSurface
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
IDCompositionSurface> {
public:
DrawOffsetOverridingDCompositionSurface(
Microsoft::WRL::ComPtr<IDCompositionSurface> surface,
const gfx::Point& draw_offset)
: surface_(std::move(surface)), draw_offset_(draw_offset) {}
IFACEMETHODIMP BeginDraw(const RECT* updateRect,
REFIID iid,
void** updateObject,
POINT* updateOffset) override {
RECT offsetRect = *updateRect;
offsetRect.left += draw_offset_.x();
offsetRect.right += draw_offset_.x();
offsetRect.top += draw_offset_.y();
offsetRect.bottom += draw_offset_.y();
return surface_->BeginDraw(&offsetRect, iid, updateObject, updateOffset);
}
IFACEMETHODIMP EndDraw() override { return surface_->EndDraw(); }
IFACEMETHODIMP ResumeDraw() override { return surface_->ResumeDraw(); }
IFACEMETHODIMP SuspendDraw() override { return surface_->SuspendDraw(); }
IFACEMETHODIMP Scroll(const RECT* scrollRect,
const RECT* clipRect,
int offsetX,
int offsetY) override {
return surface_->Scroll(scrollRect, clipRect, offsetX, offsetY);
}
private:
Microsoft::WRL::ComPtr<IDCompositionSurface> surface_;
const gfx::Point draw_offset_;
};
TEST_P(DirectCompositionPixelTest, RootSurfaceDrawOffset) {
if (!surface_)
return;
constexpr gfx::Size window_size(100, 100);
EXPECT_TRUE(surface_->Resize(window_size, 1.0, gfx::ColorSpace(), true));
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(window_size)));
glClearColor(0.0, 0.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
constexpr gfx::Point draw_offset(50, 50);
auto root_surface = surface_->GetRootSurfaceForTesting();
Microsoft::WRL::ComPtr<IDCompositionSurface> orig_dcomp_surface =
root_surface->dcomp_surface();
auto dcomp_surface =
Microsoft::WRL::Make<DrawOffsetOverridingDCompositionSurface>(
root_surface->dcomp_surface(), draw_offset);
// Although |dcomp_surface| is not used beyond this point, it should not be
// freed. This is because surface_->SetDrawRectangle sets |dcomp_surface|
// as a row pointer to DirectCompositionChildSurfaceWin::g_current_surface,
// and later, root_surface->SetDCompSurfaceForTesting releases dcomp_surface
// without nullifying DirectCompositionChildSurfaceWin::g_current_surface.
// As a result, accessing the invalid
// DirectCompositionChildSurfaceWin::g_current_surface in
// DirectCompositionChildSurfaceWin::OnMakeCurrent leads to
// EXCEPTION_ACCESS_VIOLATION.
root_surface->SetDCompSurfaceForTesting(dcomp_surface);
// Even though draw_rect is the first quadrant, the rendering will be limited
// to the third quadrant because the dcomp surface will return that offset.
constexpr gfx::Rect draw_rect(0, 0, 50, 50);
EXPECT_TRUE(surface_->SetDrawRectangle(draw_rect));
glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// Reset the original dcomp surface so it can be successfully set to a visual
// in dcomp tree. Passing DrawOffsetOverridingDCompositionSurface to
// Visual::SetContent returns an error.
root_surface->SetDCompSurfaceForTesting(std::move(orig_dcomp_surface));
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
Sleep(1000);
// Note: The colors read back are BGRA so the expected colors are inverted
// with respect to the clear color.
struct {
gfx::Point position;
SkColor expected_color;
} test_cases[] = {{gfx::Point(75, 75), SkColorSetRGB(255, 0, 0)},
{gfx::Point(25, 25), SkColorSetRGB(0, 0, 255)}};
for (const auto& test_case : test_cases) {
SkColor actual_color =
GLTestHelper::ReadBackWindowPixel(window_.hwnd(), test_case.position);
EXPECT_TRUE(AreColorsSimilar(test_case.expected_color, actual_color))
<< std::hex << "Expected " << test_case.expected_color << " Actual "
<< actual_color;
}
}
void RunBufferCountTest(scoped_refptr<DirectCompositionSurfaceWin> surface,
UINT buffer_count,
bool for_video) {
if (!surface)
return;
if (for_video) {
SetDirectCompositionScaledOverlaysSupportedForTesting(true);
EXPECT_TRUE(surface->SetEnableDCLayers(true));
} else {
EXPECT_TRUE(surface->SetEnableDCLayers(false));
}
constexpr gfx::Size window_size(100, 100);
EXPECT_TRUE(surface->Resize(window_size, 1.0, gfx::ColorSpace(), true));
EXPECT_TRUE(surface->SetDrawRectangle(gfx::Rect(window_size)));
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
constexpr gfx::Size texture_size(50, 50);
if (for_video) {
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
GetDirectCompositionD3D11Device();
ASSERT_TRUE(d3d11_device);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
CreateNV12Texture(d3d11_device, texture_size);
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(texture_size);
params->quad_rect = gfx::Rect(window_size);
params->color_space = gfx::ColorSpace::CreateREC709();
EXPECT_TRUE(surface->ScheduleDCLayer(std::move(params)));
}
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface->SwapBuffers(base::DoNothing(), gfx::FrameData()));
auto swap_chain = for_video ? surface->GetLayerSwapChainForTesting(0)
: surface->GetBackbufferSwapChainForTesting();
ASSERT_TRUE(swap_chain);
DXGI_SWAP_CHAIN_DESC1 desc;
EXPECT_TRUE(SUCCEEDED(swap_chain->GetDesc1(&desc)));
// The expected size is window_size(100, 100).
EXPECT_EQ(100u, desc.Width);
EXPECT_EQ(100u, desc.Height);
EXPECT_EQ(buffer_count, desc.BufferCount);
}
TEST_P(DirectCompositionSurfaceTest, RootSwapChainBufferCount) {
RunBufferCountTest(surface_, /*buffer_count=*/2u, /*for_video=*/false);
}
TEST_P(DirectCompositionSurfaceTest, VideoSwapChainBufferCount) {
RunBufferCountTest(surface_, /*buffer_count=*/2u, /*for_video=*/true);
}
// Ensure that swap chains stay attached to the same visual between subsequent
// frames.
// Please ensure this test is not broken. Re-attaching swapchains between
// subsequent frames may cause flickering under certain conditions that include
// specific Intel drivers, custom present duration etc.
// See https://bugs.chromium.org/p/chromium/issues/detail?id=1421175.
TEST_P(DirectCompositionSurfaceTest, VisualsReused) {
if (!surface_) {
return;
}
// Frame 1:
// overlay 0: root dcomp surface
// overlay 1: swapchain z-order = 1 (overlay)
constexpr gfx::Size window_size(100, 100);
EXPECT_TRUE(surface_->Resize(window_size, 1.0, gfx::ColorSpace(), true));
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(window_size)));
glClearColor(0.0, 0.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
GetDirectCompositionD3D11Device();
gfx::Size texture_size(50, 50);
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture =
CreateNV12Texture(d3d11_device, texture_size);
EXPECT_NE(texture, nullptr);
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(texture_size);
params->quad_rect = gfx::Rect(100, 100);
params->color_space = gfx::ColorSpace::CreateREC709();
// Overlay
params->z_order = 1;
surface_->ScheduleDCLayer(std::move(params));
}
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
DCLayerTree* dcLayerTree = surface_->GetLayerTreeForTesting();
EXPECT_EQ(2u, dcLayerTree->GetDcompLayerCountForTesting());
Microsoft::WRL::ComPtr<IDCompositionVisual2> visual0 =
dcLayerTree->GetContentVisualForTesting(0);
Microsoft::WRL::ComPtr<IDCompositionVisual2> visual1 =
dcLayerTree->GetContentVisualForTesting(1);
// Frame 2:
// overlay 0: root dcomp surface
// overlay 1: swapchain z-order = -1 (underlay)
{
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image.emplace(texture_size, texture);
params->content_rect = gfx::Rect(texture_size);
params->quad_rect = gfx::Rect(100, 100);
params->color_space = gfx::ColorSpace::CreateREC709();
// Underlay
params->z_order = -1;
surface_->ScheduleDCLayer(std::move(params));
}
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
EXPECT_EQ(2u, dcLayerTree->GetDcompLayerCountForTesting());
// Verify that the visuals are reused from the previous frame but attached
// to the root visual in a reversed order.
EXPECT_EQ(visual0.Get(), dcLayerTree->GetContentVisualForTesting(1));
EXPECT_EQ(visual1.Get(), dcLayerTree->GetContentVisualForTesting(0));
#if DCHECK_IS_ON()
if (base::FeatureList::IsEnabled(features::kDCompVisualTreeOptimization)) {
EXPECT_TRUE(dcLayerTree->GetAttachedToRootFromPreviousFrameForTesting(0));
EXPECT_FALSE(dcLayerTree->GetAttachedToRootFromPreviousFrameForTesting(1));
}
#endif // DCHECK_IS_ON()
}
void ScheduleDCLayer(scoped_refptr<DirectCompositionSurfaceWin> surface,
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain,
const gfx::Size& swap_chain_size,
int z_order) {
auto params = std::make_unique<DCLayerOverlayParams>();
params->overlay_image = DCLayerOverlayImage(swap_chain_size, swap_chain);
params->content_rect = gfx::Rect(swap_chain_size);
params->quad_rect = gfx::Rect(100, 100);
params->color_space = gfx::ColorSpace::CreateSRGB();
params->z_order = z_order;
surface->ScheduleDCLayer(std::move(params));
}
void CreateSwapChain(IDXGIFactory2* dxgi_factory,
ID3D11Device* d3d11_device,
const DXGI_SWAP_CHAIN_DESC1& desc,
Microsoft::WRL::ComPtr<IDXGISwapChain1>& swap_chain) {
ASSERT_HRESULT_SUCCEEDED(dxgi_factory->CreateSwapChainForComposition(
d3d11_device, &desc, nullptr, &swap_chain));
ASSERT_TRUE(swap_chain);
}
TEST_P(DirectCompositionSurfaceTest, MatchedAndUnmatchedVisualsReused) {
if (!base::FeatureList::IsEnabled(features::kDCompVisualTreeOptimization)) {
GTEST_SKIP() << "kDCompVisualTreeOptimization test only.\n";
}
if (!surface_) {
return;
}
// Fails on AMD RX 5500 XT. https://crbug.com/1152565.
if (context_ && context_->GetVersionInfo() &&
context_->GetVersionInfo()->driver_vendor.find("AMD") !=
std::string::npos) {
return;
}
constexpr gfx::Size window_size(100, 100);
EXPECT_TRUE(surface_->Resize(window_size, 1.0, gfx::ColorSpace(), true));
EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(window_size)));
glClearColor(0.0, 0.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
QueryD3D11DeviceObjectFromANGLE();
ASSERT_TRUE(d3d11_device);
Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
ASSERT_HRESULT_SUCCEEDED(d3d11_device.As(&dxgi_device));
ASSERT_TRUE(dxgi_device);
Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
ASSERT_HRESULT_SUCCEEDED(dxgi_device->GetAdapter(&dxgi_adapter));
ASSERT_TRUE(dxgi_adapter);
Microsoft::WRL::ComPtr<IDXGIFactory2> dxgi_factory;
ASSERT_HRESULT_SUCCEEDED(
dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory)));
ASSERT_TRUE(dxgi_factory);
gfx::Size swap_chain_size(50, 50);
DXGI_SWAP_CHAIN_DESC1 desc = {};
desc.Width = swap_chain_size.width();
desc.Height = swap_chain_size.height();
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.Stereo = FALSE;
desc.SampleDesc.Count = 1;
desc.BufferCount = 2;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
desc.Scaling = DXGI_SCALING_STRETCH;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
desc.Flags = 0;
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chainA;
CreateSwapChain(dxgi_factory.Get(), d3d11_device.Get(), desc, swap_chainA);
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chainB;
CreateSwapChain(dxgi_factory.Get(), d3d11_device.Get(), desc, swap_chainB);
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chainC;
CreateSwapChain(dxgi_factory.Get(), d3d11_device.Get(), desc, swap_chainC);
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chainD;
CreateSwapChain(dxgi_factory.Get(), d3d11_device.Get(), desc, swap_chainD);
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chainE;
CreateSwapChain(dxgi_factory.Get(), d3d11_device.Get(), desc, swap_chainE);
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chainF;
CreateSwapChain(dxgi_factory.Get(), d3d11_device.Get(), desc, swap_chainF);
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chainL;
CreateSwapChain(dxgi_factory.Get(), d3d11_device.Get(), desc, swap_chainL);
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chainM;
CreateSwapChain(dxgi_factory.Get(), d3d11_device.Get(), desc, swap_chainM);
// Frame 1: RootSurface, A B C D E F
ScheduleDCLayer(surface_, swap_chainA, swap_chain_size, 1);
ScheduleDCLayer(surface_, swap_chainB, swap_chain_size, 2);
ScheduleDCLayer(surface_, swap_chainC, swap_chain_size, 3);
ScheduleDCLayer(surface_, swap_chainD, swap_chain_size, 4);
ScheduleDCLayer(surface_, swap_chainE, swap_chain_size, 5);
ScheduleDCLayer(surface_, swap_chainF, swap_chain_size, 6);
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
DCLayerTree* dc_layer_tree = surface_->GetLayerTreeForTesting();
EXPECT_EQ(7u, dc_layer_tree->GetDcompLayerCountForTesting());
Microsoft::WRL::ComPtr<IDCompositionVisual2> visualRS =
dc_layer_tree->GetContentVisualForTesting(0);
EXPECT_NE(visualRS, nullptr);
Microsoft::WRL::ComPtr<IDCompositionVisual2> visualA =
dc_layer_tree->GetContentVisualForTesting(1);
EXPECT_NE(visualA, nullptr);
Microsoft::WRL::ComPtr<IDCompositionVisual2> visualB =
dc_layer_tree->GetContentVisualForTesting(2);
EXPECT_NE(visualB, nullptr);
Microsoft::WRL::ComPtr<IDCompositionVisual2> visualC =
dc_layer_tree->GetContentVisualForTesting(3);
EXPECT_NE(visualC, nullptr);
Microsoft::WRL::ComPtr<IDCompositionVisual2> visualD =
dc_layer_tree->GetContentVisualForTesting(4);
EXPECT_NE(visualD, nullptr);
Microsoft::WRL::ComPtr<IDCompositionVisual2> visualE =
dc_layer_tree->GetContentVisualForTesting(5);
EXPECT_NE(visualE, nullptr);
Microsoft::WRL::ComPtr<IDCompositionVisual2> visualF =
dc_layer_tree->GetContentVisualForTesting(6);
EXPECT_NE(visualF, nullptr);
// Frame 2: RootSurface, A L D C M
ScheduleDCLayer(surface_, swap_chainA, swap_chain_size, 1);
ScheduleDCLayer(surface_, swap_chainL, swap_chain_size, 2);
ScheduleDCLayer(surface_, swap_chainD, swap_chain_size, 3);
ScheduleDCLayer(surface_, swap_chainC, swap_chain_size, 4);
ScheduleDCLayer(surface_, swap_chainM, swap_chain_size, 5);
EXPECT_EQ(gfx::SwapResult::SWAP_ACK,
surface_->SwapBuffers(base::DoNothing(), gfx::FrameData()));
EXPECT_EQ(6u, dc_layer_tree->GetDcompLayerCountForTesting());
// Verify:
// RootSurface is matched to RootSurface and kept attached to the root.
// A is matched to A and kept attached to the root.
// L is reused from B and kept attached to the root.
// D is matched to D and kept attached to the root.
// C is matched to C and reattached to the root.
// M is reused from E and kept attached to the root.
EXPECT_EQ(visualRS.Get(),
dc_layer_tree->GetContentVisualForTesting(0) /*RS*/);
EXPECT_EQ(visualA.Get(), dc_layer_tree->GetContentVisualForTesting(1) /*A*/);
EXPECT_EQ(visualB.Get(), dc_layer_tree->GetContentVisualForTesting(2) /*L*/);
EXPECT_EQ(visualD.Get(), dc_layer_tree->GetContentVisualForTesting(3) /*D*/);
EXPECT_EQ(visualC.Get(), dc_layer_tree->GetContentVisualForTesting(4) /*C*/);
EXPECT_EQ(visualE.Get(), dc_layer_tree->GetContentVisualForTesting(5) /*M*/);
#if DCHECK_IS_ON()
EXPECT_TRUE(dc_layer_tree->GetAttachedToRootFromPreviousFrameForTesting(0));
EXPECT_TRUE(dc_layer_tree->GetAttachedToRootFromPreviousFrameForTesting(1));
EXPECT_TRUE(dc_layer_tree->GetAttachedToRootFromPreviousFrameForTesting(2));
EXPECT_TRUE(dc_layer_tree->GetAttachedToRootFromPreviousFrameForTesting(3));
EXPECT_FALSE(dc_layer_tree->GetAttachedToRootFromPreviousFrameForTesting(4));
EXPECT_TRUE(dc_layer_tree->GetAttachedToRootFromPreviousFrameForTesting(5));
#endif // DCHECK_IS_ON()
}
class DirectCompositionTripleBufferingTest
: public DirectCompositionSurfaceTest {
public:
DirectCompositionTripleBufferingTest() = default;
~DirectCompositionTripleBufferingTest() override = default;
protected:
void SetUp() override { DirectCompositionSurfaceTest::SetUp(); }
void SetupScopedFeatureList() override {
if (GetParam()) {
scoped_feature_list_.InitWithFeatures(
{features::kDCompTripleBufferRootSwapChain,
features::kDCompTripleBufferVideoSwapChain,
features::kDCompVisualTreeOptimization},
{});
} else {
scoped_feature_list_.InitWithFeatures(
{features::kDCompTripleBufferRootSwapChain,
features::kDCompTripleBufferVideoSwapChain},
{features::kDCompVisualTreeOptimization});
}
}
};
INSTANTIATE_TEST_SUITE_P(All,
DirectCompositionTripleBufferingTest,
testing::Bool());
TEST_P(DirectCompositionTripleBufferingTest, MainSwapChainBufferCount) {
RunBufferCountTest(surface_, /*buffer_count=*/3u, /*for_video=*/false);
}
TEST_P(DirectCompositionTripleBufferingTest, VideoSwapChainBufferCount) {
RunBufferCountTest(surface_, /*buffer_count=*/3u, /*for_video=*/true);
}
} // namespace gl