Index: gpu/ipc/service/direct_composition_surface_win_unittest.cc |
diff --git a/gpu/ipc/service/direct_composition_surface_win_unittest.cc b/gpu/ipc/service/direct_composition_surface_win_unittest.cc |
index 862e1525bd58bc046dc165220d5ff1e9b75e7ead..864353fe72d95aacd1ac1d467db2c532a8e5d69b 100644 |
--- a/gpu/ipc/service/direct_composition_surface_win_unittest.cc |
+++ b/gpu/ipc/service/direct_composition_surface_win_unittest.cc |
@@ -6,15 +6,35 @@ |
#include "base/memory/weak_ptr.h" |
#include "base/run_loop.h" |
#include "base/synchronization/waitable_event.h" |
+#include "base/threading/thread_task_runner_handle.h" |
+#include "base/win/scoped_gdi_object.h" |
+#include "base/win/scoped_hdc.h" |
+#include "base/win/scoped_select_object.h" |
#include "testing/gtest/include/gtest/gtest.h" |
#include "ui/base/win/hidden_window.h" |
+#include "ui/gfx/gdi_util.h" |
+#include "ui/gfx/transform.h" |
+#include "ui/gl/dc_renderer_layer_params.h" |
#include "ui/gl/gl_angle_util_win.h" |
#include "ui/gl/gl_context.h" |
+#include "ui/gl/gl_image_dxgi.h" |
#include "ui/gl/init/gl_factory.h" |
+#include "ui/platform_window/platform_window_delegate.h" |
+#include "ui/platform_window/win/win_window.h" |
namespace gpu { |
namespace { |
+bool CheckIfDCSupported() { |
+ if (!gl::QueryDirectCompositionDevice( |
+ gl::QueryD3D11DeviceObjectFromANGLE())) { |
+ LOG(WARNING) |
+ << "GL implementation not using DirectComposition, skipping test."; |
+ return false; |
+ } |
+ return true; |
+} |
+ |
class TestImageTransportSurfaceDelegate |
: public ImageTransportSurfaceDelegate, |
public base::SupportsWeakPtr<TestImageTransportSurfaceDelegate> { |
@@ -24,7 +44,10 @@ class TestImageTransportSurfaceDelegate |
// ImageTransportSurfaceDelegate implementation. |
void DidCreateAcceleratedSurfaceChildWindow( |
SurfaceHandle parent_window, |
- SurfaceHandle child_window) override {} |
+ SurfaceHandle child_window) override { |
+ if (parent_window) |
+ ::SetParent(child_window, parent_window); |
+ } |
void DidSwapBuffersComplete(SwapBuffersCompleteParams params) override {} |
const gles2::FeatureInfo* GetFeatureInfo() const override { return nullptr; } |
void SetLatencyInfoCallback(const LatencyInfoCallback& callback) override {} |
@@ -34,6 +57,22 @@ class TestImageTransportSurfaceDelegate |
int32_t GetRouteID() const override { return 0; } |
}; |
+class TestPlatformDelegate : public ui::PlatformWindowDelegate { |
+ public: |
+ // ui::PlatformWindowDelegate implementation. |
+ void OnBoundsChanged(const gfx::Rect& new_bounds) 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 new_state) override {} |
+ void OnLostCapture() override {} |
+ void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget, |
+ float device_pixel_ratio) override {} |
+ void OnAcceleratedWidgetDestroyed() override {} |
+ void OnActivationChanged(bool active) override {} |
+}; |
+ |
void RunPendingTasks(scoped_refptr<base::TaskRunner> task_runner) { |
base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
base::WaitableEvent::InitialState::NOT_SIGNALED); |
@@ -58,12 +97,8 @@ void DestroySurface(scoped_refptr<DirectCompositionSurfaceWin> surface) { |
} |
TEST(DirectCompositionSurfaceTest, TestMakeCurrent) { |
- if (!gl::QueryDirectCompositionDevice( |
- gl::QueryD3D11DeviceObjectFromANGLE())) { |
- LOG(WARNING) |
- << "GL implementation not using DirectComposition, skipping test."; |
+ if (!CheckIfDCSupported()) |
return; |
- } |
TestImageTransportSurfaceDelegate delegate; |
@@ -126,12 +161,8 @@ TEST(DirectCompositionSurfaceTest, TestMakeCurrent) { |
// Tests that switching using EnableDCLayers works. |
TEST(DirectCompositionSurfaceTest, DXGIDCLayerSwitch) { |
- if (!gl::QueryDirectCompositionDevice( |
- gl::QueryD3D11DeviceObjectFromANGLE())) { |
- LOG(WARNING) |
- << "GL implementation not using DirectComposition, skipping test."; |
+ if (!CheckIfDCSupported()) |
return; |
- } |
TestImageTransportSurfaceDelegate delegate; |
@@ -181,5 +212,177 @@ TEST(DirectCompositionSurfaceTest, DXGIDCLayerSwitch) { |
context = nullptr; |
DestroySurface(std::move(surface)); |
} |
+ |
+COLORREF ReadBackWindowPixel(HWND window, const gfx::Point& point) { |
+ base::win::ScopedCreateDC mem_hdc(::CreateCompatibleDC(nullptr)); |
+ void* bits = nullptr; |
+ BITMAPV4HEADER hdr; |
+ gfx::CreateBitmapV4Header(point.x() + 1, point.y() + 1, &hdr); |
+ DCHECK(mem_hdc.IsValid()); |
+ base::win::ScopedBitmap bitmap( |
+ ::CreateDIBSection(mem_hdc.Get(), reinterpret_cast<BITMAPINFO*>(&hdr), |
+ DIB_RGB_COLORS, &bits, nullptr, 0)); |
+ DCHECK(bitmap.is_valid()); |
+ |
+ base::win::ScopedSelectObject select_object(mem_hdc.Get(), bitmap.get()); |
+ |
+ // Grab a copy of the window. Use PrintWindow because it works even when the |
+ // window's partially occluded. The PW_RENDERFULLCONTENT flag is undocumented, |
+ // but works starting in Windows 8.1. It allows for capturing the contents of |
+ // the window that are drawn using DirectComposition. |
+ UINT flags = PW_CLIENTONLY | PW_RENDERFULLCONTENT; |
+ |
+ BOOL result = PrintWindow(window, mem_hdc.Get(), flags); |
+ if (!result) |
+ PLOG(ERROR) << "Failed to print window"; |
+ |
+ GdiFlush(); |
+ |
+ uint32_t pixel_value = |
+ static_cast<uint32_t*>(bits)[hdr.bV4Width * point.y() + point.x()]; |
+ |
+ return pixel_value; |
+} |
+ |
+class DirectCompositionPixelTest : public testing::Test { |
+ public: |
+ DirectCompositionPixelTest() |
+ : window_(&platform_delegate_, gfx::Rect(0, 0, 100, 100)) {} |
+ |
+ protected: |
+ void InitializeSurface() { |
+ static_cast<ui::PlatformWindow*>(&window_)->Show(); |
+ |
+ surface_ = |
+ new DirectCompositionSurfaceWin(delegate_.AsWeakPtr(), window_.hwnd()); |
+ EXPECT_TRUE(surface_->Initialize()); |
+ } |
+ |
+ void PixelTestSwapChain(bool layers_enabled) { |
+ if (!CheckIfDCSupported()) |
+ return; |
+ |
+ InitializeSurface(); |
+ |
+ surface_->SetEnableDCLayers(layers_enabled); |
+ gfx::Size window_size(100, 100); |
+ |
+ scoped_refptr<gl::GLContext> context = gl::init::CreateGLContext( |
+ nullptr, surface_.get(), gl::GLContextAttribs()); |
+ EXPECT_TRUE(surface_->Resize(window_size, 1.0, true)); |
+ EXPECT_TRUE(surface_->SetDrawRectangle(gfx::Rect(window_size))); |
+ EXPECT_TRUE(context->MakeCurrent(surface_.get())); |
+ |
+ glClearColor(1.0, 0.0, 0.0, 1.0); |
+ glClear(GL_COLOR_BUFFER_BIT); |
+ |
+ EXPECT_EQ(gfx::SwapResult::SWAP_ACK, surface_->SwapBuffers()); |
+ |
+ // Ensure DWM swap completed. |
+ Sleep(1000); |
+ |
+ SkColor actual_color = |
+ ReadBackWindowPixel(window_.hwnd(), gfx::Point(75, 75)); |
+ EXPECT_EQ(SK_ColorRED, actual_color); |
+ |
+ EXPECT_TRUE(context->IsCurrent(surface_.get())); |
+ |
+ context = nullptr; |
+ DestroySurface(std::move(surface_)); |
+ } |
+ |
+ TestPlatformDelegate platform_delegate_; |
+ TestImageTransportSurfaceDelegate delegate_; |
+ ui::WinWindow window_; |
+ scoped_refptr<DirectCompositionSurfaceWin> surface_; |
+}; |
+ |
+TEST_F(DirectCompositionPixelTest, DCLayersEnabled) { |
+ PixelTestSwapChain(true); |
+} |
+ |
+TEST_F(DirectCompositionPixelTest, DCLayersDisabled) { |
+ PixelTestSwapChain(false); |
+} |
+ |
+base::win::ScopedComPtr<ID3D11Texture2D> CreateNV12Texture( |
+ const base::win::ScopedComPtr<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; |
+ |
+ std::vector<char> image_data(size.width() * size.height() * 3 / 2); |
+ // Y, U, and V should all be Oxff. Output color should be pink. |
+ memset(&image_data[0], 0xff, size.width() * size.height() * 3 / 2); |
+ |
+ D3D11_SUBRESOURCE_DATA data = {}; |
+ data.pSysMem = (const void*)&image_data[0]; |
+ data.SysMemPitch = size.width(); |
+ |
+ base::win::ScopedComPtr<ID3D11Texture2D> texture; |
+ HRESULT hr = d3d11_device->CreateTexture2D(&desc, &data, texture.Receive()); |
+ 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; |
+} |
+ |
+TEST_F(DirectCompositionPixelTest, VideoSwapchain) { |
+ if (!CheckIfDCSupported()) |
+ return; |
+ InitializeSurface(); |
+ surface_->SetEnableDCLayers(true); |
+ gfx::Size window_size(100, 100); |
+ |
+ scoped_refptr<gl::GLContext> context = gl::init::CreateGLContext( |
+ nullptr, surface_.get(), gl::GLContextAttribs()); |
+ EXPECT_TRUE(surface_->Resize(window_size, 1.0, true)); |
+ |
+ base::win::ScopedComPtr<ID3D11Device> d3d11_device = |
+ gl::QueryD3D11DeviceObjectFromANGLE(); |
+ |
+ gfx::Size texture_size(50, 50); |
+ base::win::ScopedComPtr<ID3D11Texture2D> texture = |
+ CreateNV12Texture(d3d11_device, texture_size); |
+ |
+ scoped_refptr<gl::GLImageDXGI> image_dxgi( |
+ new gl::GLImageDXGI(texture_size, nullptr)); |
+ image_dxgi->SetTexture(texture, 0); |
+ |
+ ui::DCRendererLayerParams params(false, gfx::Rect(), 1, gfx::Transform(), |
+ image_dxgi.get(), |
+ gfx::RectF(gfx::Rect(texture_size)), |
+ gfx::Rect(window_size), 0, 0, 1.0, 0); |
+ surface_->ScheduleDCLayer(params); |
+ |
+ EXPECT_EQ(gfx::SwapResult::SWAP_ACK, surface_->SwapBuffers()); |
+ Sleep(1000); |
+ |
+ SkColor expected_color = SkColorSetRGB(0xff, 0xb7, 0xff); |
+ SkColor actual_color = |
+ ReadBackWindowPixel(window_.hwnd(), gfx::Point(75, 75)); |
+ EXPECT_TRUE(AreColorsSimilar(expected_color, actual_color)) |
+ << std::hex << "Expected " << expected_color << " Actual " |
+ << actual_color; |
+ |
+ context = nullptr; |
+ DestroySurface(std::move(surface_)); |
+} |
+ |
} // namespace |
} // namespace gpu |