| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "gpu/command_buffer/service/dxgi_shared_handle_manager.h" |
| |
| #include <d3d11_1.h> |
| #include <windows.h> |
| |
| #include "base/synchronization/condition_variable.h" |
| #include "base/synchronization/lock.h" |
| #include "base/task/thread_pool.h" |
| #include "base/test/bind.h" |
| #include "base/win/windows_version.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gl/gl_angle_util_win.h" |
| |
| namespace gpu { |
| namespace { |
| |
| class DXGISharedHandleManagerTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| // Using DXGI NT handles is universally supported only on Win8 and above. |
| // TODO(sunnyps): Unify this with the check in D3DImageBackingFactory. |
| const bool shared_handles_supported = |
| base::win::GetVersion() >= base::win::Version::WIN8; |
| d3d11_device_ = gl::QueryD3D11DeviceObjectFromANGLE(); |
| if (shared_handles_supported && d3d11_device_) { |
| dxgi_shared_handle_manager_ = |
| base::MakeRefCounted<DXGISharedHandleManager>(d3d11_device_); |
| } |
| } |
| |
| bool ShouldSkipTest() const { return !dxgi_shared_handle_manager_; } |
| |
| Microsoft::WRL::ComPtr<ID3D11Texture2D> CreateTexture() { |
| D3D11_TEXTURE2D_DESC desc; |
| desc.Width = 1; |
| desc.Height = 1; |
| desc.MipLevels = 1; |
| desc.ArraySize = 1; |
| desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; |
| desc.SampleDesc.Count = 1; |
| desc.SampleDesc.Quality = 0; |
| desc.Usage = D3D11_USAGE_DEFAULT; |
| desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; |
| desc.CPUAccessFlags = 0; |
| desc.MiscFlags = |
| D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED; |
| |
| Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture; |
| HRESULT hr = d3d11_device_->CreateTexture2D(&desc, nullptr, &d3d11_texture); |
| EXPECT_EQ(hr, S_OK); |
| return d3d11_texture; |
| } |
| |
| base::win::ScopedHandle CreateSharedHandle( |
| const Microsoft::WRL::ComPtr<ID3D11Texture2D>& d3d11_texture) { |
| Microsoft::WRL::ComPtr<IDXGIResource1> dxgi_resource; |
| HRESULT hr = d3d11_texture.As(&dxgi_resource); |
| EXPECT_EQ(hr, S_OK); |
| |
| HANDLE shared_handle; |
| hr = dxgi_resource->CreateSharedHandle( |
| nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, |
| nullptr, &shared_handle); |
| EXPECT_EQ(hr, S_OK); |
| |
| return base::win::ScopedHandle(shared_handle); |
| } |
| |
| Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_; |
| scoped_refptr<DXGISharedHandleManager> dxgi_shared_handle_manager_; |
| }; |
| |
| TEST_F(DXGISharedHandleManagerTest, LookupByToken) { |
| if (ShouldSkipTest()) |
| return; |
| |
| auto d3d11_texture = CreateTexture(); |
| ASSERT_TRUE(d3d11_texture); |
| |
| constexpr int kNumHandles = 5; |
| |
| auto orig_token = gfx::DXGIHandleToken(); |
| base::win::ScopedHandle orig_handle = CreateSharedHandle(d3d11_texture); |
| ASSERT_TRUE(orig_handle.IsValid()); |
| |
| scoped_refptr<DXGISharedHandleState> orig_state = |
| dxgi_shared_handle_manager_->GetOrCreateSharedHandleState( |
| orig_token, std::move(orig_handle)); |
| ASSERT_NE(orig_state, nullptr); |
| |
| EXPECT_EQ(dxgi_shared_handle_manager_->GetSharedHandleMapSizeForTesting(), |
| 1u); |
| |
| for (int i = 0; i < kNumHandles - 1; i++) { |
| HANDLE handle; |
| ::DuplicateHandle(::GetCurrentProcess(), orig_state->GetSharedHandle(), |
| ::GetCurrentProcess(), &handle, |
| /*dwDesiredAccess=*/0, |
| /*bInerhitHandle=*/FALSE, DUPLICATE_SAME_ACCESS); |
| base::win::ScopedHandle new_handle(handle); |
| ASSERT_TRUE(new_handle.IsValid()); |
| |
| scoped_refptr<DXGISharedHandleState> state = |
| dxgi_shared_handle_manager_->GetOrCreateSharedHandleState( |
| orig_token, std::move(new_handle)); |
| EXPECT_EQ(state, orig_state); |
| |
| EXPECT_EQ(dxgi_shared_handle_manager_->GetSharedHandleMapSizeForTesting(), |
| 1u); |
| } |
| |
| orig_state = nullptr; |
| |
| EXPECT_EQ(dxgi_shared_handle_manager_->GetSharedHandleMapSizeForTesting(), |
| 0u); |
| } |
| |
| TEST_F(DXGISharedHandleManagerTest, LookupByTokenMultiThread) { |
| if (ShouldSkipTest()) |
| return; |
| |
| auto d3d11_texture = CreateTexture(); |
| ASSERT_TRUE(d3d11_texture); |
| |
| constexpr int kNumHandles = 101; |
| |
| auto orig_token = gfx::DXGIHandleToken(); |
| base::win::ScopedHandle orig_handle = CreateSharedHandle(d3d11_texture); |
| ASSERT_TRUE(orig_handle.IsValid()); |
| |
| scoped_refptr<DXGISharedHandleState> orig_state = |
| dxgi_shared_handle_manager_->GetOrCreateSharedHandleState( |
| orig_token, std::move(orig_handle)); |
| ASSERT_NE(orig_state, nullptr); |
| |
| EXPECT_EQ(dxgi_shared_handle_manager_->GetSharedHandleMapSizeForTesting(), |
| 1u); |
| |
| base::Lock lock; |
| base::ConditionVariable cv(&lock); |
| |
| base::AutoLock auto_lock(lock); |
| int remaining_handles = kNumHandles - 1; |
| |
| for (int i = 0; i < remaining_handles; i++) { |
| base::ThreadPool::PostTask( |
| FROM_HERE, base::BindLambdaForTesting([&] { |
| HANDLE handle; |
| ::DuplicateHandle(::GetCurrentProcess(), |
| orig_state->GetSharedHandle(), |
| ::GetCurrentProcess(), &handle, |
| /*dwDesiredAccess=*/0, |
| /*bInerhitHandle=*/FALSE, DUPLICATE_SAME_ACCESS); |
| base::win::ScopedHandle new_handle(handle); |
| ASSERT_TRUE(new_handle.IsValid()); |
| |
| scoped_refptr<DXGISharedHandleState> state = |
| dxgi_shared_handle_manager_->GetOrCreateSharedHandleState( |
| orig_token, std::move(new_handle)); |
| EXPECT_EQ(state, orig_state); |
| |
| EXPECT_EQ( |
| dxgi_shared_handle_manager_->GetSharedHandleMapSizeForTesting(), |
| 1u); |
| |
| state = nullptr; |
| |
| EXPECT_EQ( |
| dxgi_shared_handle_manager_->GetSharedHandleMapSizeForTesting(), |
| 1u); |
| |
| base::AutoLock auto_lock(lock); |
| remaining_handles--; |
| cv.Signal(); |
| })); |
| } |
| |
| while (remaining_handles > 0) |
| cv.Wait(); |
| |
| EXPECT_EQ(dxgi_shared_handle_manager_->GetSharedHandleMapSizeForTesting(), |
| 1u); |
| |
| orig_state = nullptr; |
| |
| EXPECT_EQ(dxgi_shared_handle_manager_->GetSharedHandleMapSizeForTesting(), |
| 0u); |
| } |
| |
| } // anonymous namespace |
| } // namespace gpu |