chromium/ui/gfx/win/d3d_shared_fence.h

// 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.

#ifndef UI_GFX_WIN_D3D_SHARED_FENCE_H_
#define UI_GFX_WIN_D3D_SHARED_FENCE_H_

#include <d3d11.h>
#include <d3d11_4.h>
#include <wrl/client.h>

#include "base/containers/lru_cache.h"
#include "base/memory/ref_counted.h"
#include "base/win/scoped_handle.h"
#include "ui/gfx/gfx_export.h"
#include "ui/gfx/gpu_memory_buffer.h"

namespace gfx {

// A thread-safe ref-counted wrapper around D3D11Fence and its shared handle.
// Fences are opened for each D3D11 device the fence is waited on. Thread-safe
// ref-counting is used so that the same fence object can be referred to in
// multiple places e.g. as the signaling fence or in list of fences to wait for
// next access, and also multiple threads e.g. the gpu main thread and the media
// service thread. This class must be externally synchronized.
class GFX_EXPORT D3DSharedFence
    : public base::RefCountedThreadSafe<D3DSharedFence> {
 public:
  // Create a new ID3D11Fence with initial value 0 on given
  // |d3d11_signal_device|. The provided device is considered the owning device
  // for the fence, and is the device used for signaling the fence.
  static scoped_refptr<D3DSharedFence> CreateForD3D11(
      Microsoft::WRL::ComPtr<ID3D11Device> d3d11_signal_device);

  // Create from an existing ID3D11Fence with a specified value to wait on. The
  // |d3d11_signal_device| is passed explicitly in the case that the device
  // signaling the fence is different than the device that created it.
  // |fence_value| is the initial value this fence will wait on.
  static scoped_refptr<D3DSharedFence> CreateFromD3D11Fence(
      Microsoft::WRL::ComPtr<ID3D11Device> d3d11_signal_device,
      Microsoft::WRL::ComPtr<ID3D11Fence> d3d11_fence,
      uint64_t fence_value);

  // Create from existing scoped shared handle e.g. from IPC. The ID3D11Fence
  // is lazily created on Wait or Signal for the device provided to those calls.
  static scoped_refptr<D3DSharedFence> CreateFromScopedHandle(
      base::win::ScopedHandle fence_handle,
      const DXGIHandleToken& fence_token);

  // Create from existing shared handle e.g. from Dawn. Doesn't take ownership
  // of |shared_handle| and duplicates it instead. The ID3D11Fence is lazily
  // created on Wait or Signal for the device provided to those calls.
  static scoped_refptr<D3DSharedFence> CreateFromUnownedHandle(
      HANDLE shared_handle);

  // Returns true if fences are supported i.e. if ID3D11Device5 is supported.
  static bool IsSupported(ID3D11Device* d3d11_device);

  // Return the shared handle for the fence.
  HANDLE GetSharedHandle() const;

  // Clone the shared handle for IPC.
  base::win::ScopedHandle CloneSharedHandle();

  // Returns the fence value that was last known to be signaled on this fence,
  // and should be used for waiting.
  uint64_t GetFenceValue() const;

  // Returns unique identifier for this fence when used across processes.
  const DXGIHandleToken& GetDXGIHandleToken() const;

  // Returns the D3D11 device if this fence was created using CreateForD3D11.
  Microsoft::WRL::ComPtr<ID3D11Device> GetD3D11Device() const;

  // Returns true if the underlying fence object is the same as |shared_handle|.
  bool IsSameFenceAsHandle(HANDLE shared_handle) const;

  // Updates fence value to |fence_value| provided that it's larger.
  void Update(uint64_t fence_value);

  // Issue a wait for the fence on the immediate context of |d3d11_device| using
  // |fence_value_|. The wait is skipped if the passed in device is the same as
  // |d3d11_signal_device_|. Returns true on success.
  bool WaitD3D11(Microsoft::WRL::ComPtr<ID3D11Device> d3d11_wait_device);

  // Increment |fence_value_| and issue a signal for the fence on the immediate
  // context of |d3d11_signal_device_| using |fence_value_|. Returns true on
  // success.
  bool IncrementAndSignalD3D11();

 private:
  friend class base::RefCountedThreadSafe<D3DSharedFence>;

  // 5 D3D11 devices ought to be enough for anybody.
  static constexpr size_t kMaxD3D11FenceMapSize = 5;

  explicit D3DSharedFence(base::win::ScopedHandle shared_handle,
                          const DXGIHandleToken& dxgi_token);

  ~D3DSharedFence();

  // Owned shared handle corresponding to the fence.
  base::win::ScopedHandle shared_handle_;

  // Value last known to be signaled for this fence to be used for future waits.
  uint64_t fence_value_ = 0;

  // Unique identifier for this fence when used across processes.
  DXGIHandleToken dxgi_token_;

  // If present, this is the D3D11 device that the fence was created on, and
  // used to signal |d3d11_signal_fence_|. Can be null if the fence will be
  // signaled externally.
  Microsoft::WRL::ComPtr<ID3D11Device> d3d11_signal_device_;

  // If present, this is the D3D11 fence object this fence was created with and
  // used for signaling.
  Microsoft::WRL::ComPtr<ID3D11Fence> d3d11_signal_fence_;

  // Map of D3D11 device to D3D11 fence objects used by WaitD3D11().
  base::LRUCache<Microsoft::WRL::ComPtr<ID3D11Device>,
                 Microsoft::WRL::ComPtr<ID3D11Fence>>
      d3d11_wait_fence_map_;
};

}  // namespace gfx

#endif  // UI_GFX_WIN_D3D_SHARED_FENCE_H_