chromium/gpu/command_buffer/service/dxgi_shared_handle_manager_unittest.cc

// Copyright 2021 The Chromium Authors
// 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 <windows.h>

#include <d3d11_1.h>

#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/task/thread_pool.h"
#include "base/test/bind.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 {
    d3d11_device_ = gl::QueryD3D11DeviceObjectFromANGLE();
    dxgi_shared_handle_manager_ =
        base::MakeRefCounted<DXGISharedHandleManager>();
  }

  bool ShouldSkipTest() const { return !d3d11_device_; }

  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), d3d11_device_);
  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), d3d11_device_);
    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), d3d11_device_);
  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), d3d11_device_);
          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