chromium/ui/gfx/win/d3d_shared_fence.cc

// Copyright 2022 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/gfx/win/d3d_shared_fence.h"

#include "base/debug/alias.h"
#include "base/logging.h"

namespace gfx {

namespace {
Microsoft::WRL::ComPtr<ID3D11DeviceContext4> GetDeviceContext4(
    ID3D11Device* d3d11_device) {
  Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
  d3d11_device->GetImmediateContext(&context);

  Microsoft::WRL::ComPtr<ID3D11DeviceContext4> context4;
  HRESULT hr = context.As(&context4);
  if (FAILED(hr)) {
    DLOG(ERROR) << "Failed to get ID3D11DeviceContext4: "
                << logging::SystemErrorCodeToString(hr);
    return nullptr;
  }
  return context4;
}

base::win::ScopedHandle DuplicateSharedHandle(HANDLE shared_handle) {
  const base::ProcessHandle process = ::GetCurrentProcess();
  HANDLE duplicated_handle = INVALID_HANDLE_VALUE;
  const BOOL result =
      ::DuplicateHandle(process, shared_handle, process, &duplicated_handle, 0,
                        FALSE, DUPLICATE_SAME_ACCESS);
  if (!result) {
    const DWORD last_error = ::GetLastError();
    base::debug::Alias(&last_error);
    CHECK(false);
  }
  return base::win::ScopedHandle(duplicated_handle);
}
}  // namespace

// static
scoped_refptr<D3DSharedFence> D3DSharedFence::CreateForD3D11(
    Microsoft::WRL::ComPtr<ID3D11Device> d3d11_signal_device) {
  Microsoft::WRL::ComPtr<ID3D11Device5> d3d11_device5;
  HRESULT hr = d3d11_signal_device.As(&d3d11_device5);
  if (FAILED(hr)) {
    DLOG(ERROR) << "Failed to get ID3D11Device5: "
                << logging::SystemErrorCodeToString(hr);
    return nullptr;
  }

  Microsoft::WRL::ComPtr<ID3D11Fence> d3d11_fence;
  hr = d3d11_device5->CreateFence(0, D3D11_FENCE_FLAG_SHARED,
                                  IID_PPV_ARGS(&d3d11_fence));
  if (FAILED(hr)) {
    DLOG(ERROR) << "CreateFence failed with error "
                << logging::SystemErrorCodeToString(hr);
    return nullptr;
  }

  return CreateFromD3D11Fence(std::move(d3d11_signal_device),
                              std::move(d3d11_fence),
                              /*fence_value=*/0);
}

// static
scoped_refptr<D3DSharedFence> D3DSharedFence::CreateFromD3D11Fence(
    Microsoft::WRL::ComPtr<ID3D11Device> d3d11_signal_device,
    Microsoft::WRL::ComPtr<ID3D11Fence> d3d11_fence,
    uint64_t fence_value) {
  HANDLE shared_handle = nullptr;
  HRESULT hr = d3d11_fence->CreateSharedHandle(nullptr, GENERIC_ALL, nullptr,
                                               &shared_handle);
  if (FAILED(hr)) {
    DLOG(ERROR) << "Unable to create shared handle for D3D11Fence: "
                << logging::SystemErrorCodeToString(hr);
    return nullptr;
  }
  auto fence = base::WrapRefCounted(new D3DSharedFence(
      base::win::ScopedHandle(shared_handle), DXGIHandleToken()));
  fence->d3d11_signal_device_ = std::move(d3d11_signal_device);
  fence->d3d11_signal_fence_ = std::move(d3d11_fence);
  fence->fence_value_ = fence_value;
  return fence;
}

// static
bool D3DSharedFence::IsSupported(ID3D11Device* d3d11_device) {
  DCHECK(d3d11_device);
  Microsoft::WRL::ComPtr<ID3D11Device5> d3d11_device5;
  HRESULT hr = d3d11_device->QueryInterface(IID_PPV_ARGS(&d3d11_device5));
  if (FAILED(hr)) {
    DVLOG(1) << "Failed to get ID3D11Device5: "
             << logging::SystemErrorCodeToString(hr);
    return false;
  }
  return true;
}

// static
scoped_refptr<D3DSharedFence> D3DSharedFence::CreateFromScopedHandle(
    base::win::ScopedHandle fence_handle,
    const DXGIHandleToken& fence_token) {
  return base::WrapRefCounted(
      new D3DSharedFence(std::move(fence_handle), fence_token));
}

scoped_refptr<D3DSharedFence> D3DSharedFence::CreateFromUnownedHandle(
    HANDLE shared_handle) {
  return base::WrapRefCounted(new D3DSharedFence(
      DuplicateSharedHandle(shared_handle), DXGIHandleToken()));
}

D3DSharedFence::D3DSharedFence(base::win::ScopedHandle shared_handle,
                               const DXGIHandleToken& dxgi_token)
    : shared_handle_(std::move(shared_handle)),
      dxgi_token_(dxgi_token),
      d3d11_wait_fence_map_(kMaxD3D11FenceMapSize) {}

D3DSharedFence::~D3DSharedFence() = default;

HANDLE D3DSharedFence::GetSharedHandle() const {
  return shared_handle_.get();
}

base::win::ScopedHandle D3DSharedFence::CloneSharedHandle() {
  return DuplicateSharedHandle(shared_handle_.get());
}

uint64_t D3DSharedFence::GetFenceValue() const {
  return fence_value_;
}

const DXGIHandleToken& D3DSharedFence::GetDXGIHandleToken() const {
  return dxgi_token_;
}

Microsoft::WRL::ComPtr<ID3D11Device> D3DSharedFence::GetD3D11Device() const {
  return d3d11_signal_device_;
}

bool D3DSharedFence::IsSameFenceAsHandle(HANDLE shared_handle) const {
  return CompareObjectHandles(shared_handle_.Get(), shared_handle);
}

void D3DSharedFence::Update(uint64_t fence_value) {
  if (fence_value > fence_value_) {
    fence_value_ = fence_value;
  }
}

bool D3DSharedFence::WaitD3D11(
    Microsoft::WRL::ComPtr<ID3D11Device> d3d11_wait_device) {
  // Skip wait if passed in device is the same as signaling device.
  if (d3d11_wait_device == d3d11_signal_device_) {
    return true;
  }

  auto it = d3d11_wait_fence_map_.Get(d3d11_wait_device);
  if (it == d3d11_wait_fence_map_.end()) {
    Microsoft::WRL::ComPtr<ID3D11Device5> d3d11_device5;
    HRESULT hr = d3d11_wait_device.As(&d3d11_device5);
    if (FAILED(hr)) {
      DLOG(ERROR) << "Failed to get ID3D11Device5: "
                  << logging::SystemErrorCodeToString(hr);
      return false;
    }
    Microsoft::WRL::ComPtr<ID3D11Fence> d3d11_fence;
    hr = d3d11_device5->OpenSharedFence(shared_handle_.get(),
                                        IID_PPV_ARGS(&d3d11_fence));
    if (FAILED(hr)) {
      DLOG(ERROR) << "OpenSharedFence failed: "
                  << logging::SystemErrorCodeToString(hr);
      return false;
    }
    it = d3d11_wait_fence_map_.Put(d3d11_wait_device, std::move(d3d11_fence));
  }

  const Microsoft::WRL::ComPtr<ID3D11Fence>& fence = it->second;
  // Skip wait if we're already past the wait value.
  if (fence->GetCompletedValue() >= fence_value_) {
    return true;
  }

  Microsoft::WRL::ComPtr<ID3D11DeviceContext4> context4 =
      GetDeviceContext4(d3d11_wait_device.Get());
  if (!context4) {
    return false;
  }

  HRESULT hr = context4->Wait(fence.Get(), fence_value_);
  if (FAILED(hr)) {
    DLOG(ERROR) << "D3D11 fence wait failed: "
                << logging::SystemErrorCodeToString(hr);
    return false;
  }
  return true;
}

bool D3DSharedFence::IncrementAndSignalD3D11() {
  CHECK(d3d11_signal_device_)
      << "D3D11 fence is expected to be signaled externally";
  DCHECK(d3d11_signal_fence_);

  Microsoft::WRL::ComPtr<ID3D11DeviceContext4> context4 =
      GetDeviceContext4(d3d11_signal_device_.Get());
  if (!context4) {
    return false;
  }

  HRESULT hr = context4->Signal(d3d11_signal_fence_.Get(), fence_value_ + 1);
  if (FAILED(hr)) {
    DLOG(ERROR) << "D3D11 fence signal failed: "
                << logging::SystemErrorCodeToString(hr);
    return false;
  }
  fence_value_++;
  return true;
}

}  // namespace gfx