blob: b4d0b50d8d033c2687e93126c2746d250cc994f6 [file] [log] [blame]
// 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.
#ifndef GPU_COMMAND_BUFFER_SERVICE_DXGI_SHARED_HANDLE_MANAGER_H_
#define GPU_COMMAND_BUFFER_SERVICE_DXGI_SHARED_HANDLE_MANAGER_H_
#include <d3d11.h>
#include <wrl/client.h>
#include "base/containers/flat_map.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "base/types/pass_key.h"
#include "base/win/scoped_handle.h"
#include "gpu/gpu_gles2_export.h"
#include "ui/gfx/gpu_memory_buffer.h"
#include "ui/gl/buildflags.h"
// Usage of BUILDFLAG(USE_DAWN) needs to be after the include for
// ui/gl/buildflags.h
#if BUILDFLAG(USE_DAWN)
#include <dawn/native/D3DBackend.h>
using dawn::native::d3d::ExternalImageDXGI;
#endif // BUILDFLAG(USE_DAWN)
namespace gpu {
// DXGISharedHandleManager caches the state associated with DXGI shared handles
// using gfx::DXGIHandleToken as the key. These tokens are used to uniquely
// identify the texture associated with the shared handle even after the handle
// is duplicated. See |dxgi_token| in GpuMemoryBufferHandle.
//
// DXGISharedHandleManager is safe to call from any thread and is guaranteed to
// outlive any scoped_refptrs it hands out. Currently, the manager is only used
// on the GPU main thread, but it is expected that in the near future, the
// scoped_refptrs could be released on other threads e.g. on DrDC thread.
//
// DXGISharedHandleState holds the shared handle and its associated state like
// D3D texture, keyed mutex state, etc. Its lifetime is managed exclusively by
// scoped_refptrs handed out by the DXGISharedHandleManager.
//
// DXGISharedHandleState is implemented as a ref-counted type with custom AddRef
// and Release methods. The manager only holds raw pointers to state instances
// for lookup by token, so DXGISharedHandleState ensures that the raw pointers
// are cleaned up when the refcount goes to zero.
class DXGISharedHandleManager;
class GPU_GLES2_EXPORT DXGISharedHandleState
: public base::subtle::RefCountedThreadSafeBase {
public:
REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
DXGISharedHandleState(base::PassKey<DXGISharedHandleManager>,
scoped_refptr<DXGISharedHandleManager> manager,
gfx::DXGIHandleToken token,
base::win::ScopedHandle shared_handle,
Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture);
DXGISharedHandleState(const DXGISharedHandleState&) = delete;
DXGISharedHandleState& operator=(const DXGISharedHandleState&) = delete;
void AddRef() const;
void Release() const;
HANDLE GetSharedHandle() const { return shared_handle_.Get(); }
bool has_keyed_mutex() const { return has_keyed_mutex_; }
// Returns D3D11 texture for given device. Imports shared handle and caches
// texture and keyed mutex if not present in |d3d11_texture_state_map_|.
Microsoft::WRL::ComPtr<ID3D11Texture2D> GetOrCreateD3D11Texture(
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device);
// Acquires keyed mutex if necessary for given device. Disallows concurrent
// access on different devices due to the possibility of deadlock.
bool BeginAccessD3D11(Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device);
// Releases keyed mutex if all pending access for given device are ended.
void EndAccessD3D11(Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device);
#if BUILDFLAG(USE_DAWN)
// Returns the Dawn ExternalImageDXGI associated with given device. It's the
// caller's responsibility to initialize the external image if needed.
std::unique_ptr<ExternalImageDXGI>& GetDawnExternalImage(WGPUDevice device);
// Returns true if there's no concurrent keyed mutex access on another device
// allowing Dawn to acquire the keyed mutex if needed.
bool BeginAccessDawn(WGPUDevice device);
// Updates keyed mutex acquired state after Dawn has released it. Erases the
// Dawn state entry from the map if the external image is already destroyed
// after all pending Dawn access is done.
void EndAccessDawn(WGPUDevice device);
#endif // BUILDFLAG(USE_DAWN)
private:
struct D3D11TextureState {
D3D11TextureState(Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture);
~D3D11TextureState();
D3D11TextureState(D3D11TextureState&&);
D3D11TextureState& operator=(D3D11TextureState&&);
Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
Microsoft::WRL::ComPtr<IDXGIKeyedMutex> dxgi_keyed_mutex;
int keyed_mutex_acquired_count = 0;
};
~DXGISharedHandleState();
scoped_refptr<DXGISharedHandleManager> manager_;
const gfx::DXGIHandleToken token_;
base::win::ScopedHandle shared_handle_;
using D3D11TextureStateMap =
base::flat_map<Microsoft::WRL::ComPtr<ID3D11Device>, D3D11TextureState>;
D3D11TextureStateMap d3d11_texture_state_map_;
#if BUILDFLAG(USE_DAWN)
// When Dawn uses keyed mutex for synchronization with the D3D11 backend, we
// want a single instance of ExternalImageDXGI (per device) for each unique
// texture even if we have multiple duplicated handles (and shared images)
// pointing to the texture. Caching the ExternalImageDXGI here enables this.
// Note that it's ok to use raw WGPUDevice pointers here since the external
// image acts like a weak pointer to the device, and we can detect if the
// entry is valid by checking ExternalImageDXGI::IsValid().
struct DawnExternalImageState {
DawnExternalImageState();
~DawnExternalImageState();
DawnExternalImageState(DawnExternalImageState&&);
DawnExternalImageState& operator=(DawnExternalImageState&&);
std::unique_ptr<ExternalImageDXGI> external_image;
int access_count = 0;
};
using DawnExternalImageCache =
base::flat_map<WGPUDevice, DawnExternalImageState>;
DawnExternalImageCache dawn_external_image_cache_;
#endif // BUILDFLAG(USE_DAWN)
// True if the texture has an underlying keyed mutex.
bool has_keyed_mutex_ = false;
// True if the keyed mutex is acquired on any device.
bool keyed_mutex_acquired_ = false;
};
class GPU_GLES2_EXPORT DXGISharedHandleManager
: public base::RefCountedThreadSafe<DXGISharedHandleManager> {
public:
DXGISharedHandleManager();
// Retrieves an existing state associated with |token| or creates a new one if
// none exists. Note that the returned state won't not have |shared_handle| as
// its handle if |token| was registered previously, but the state's handle
// will refer to the same D3D11 texture. |d3d11_device| is used for opening
// the shared handle if a state is not found. Returns a nullptr on error.
scoped_refptr<DXGISharedHandleState> GetOrCreateSharedHandleState(
gfx::DXGIHandleToken token,
base::win::ScopedHandle shared_handle,
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device);
// Creates a new unique state for given |shared_handle| and |d3d11_texture|.
// No other state will have references to the same shared handle and texture.
// Useful when creating handles which are guaranteed to never be duplicated
// e.g. WebGPU usage shared image that only needs a handle for Dawn interop.
scoped_refptr<DXGISharedHandleState> CreateAnonymousSharedHandleState(
base::win::ScopedHandle shared_handle,
Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture);
size_t GetSharedHandleMapSizeForTesting() const;
private:
friend class base::RefCountedThreadSafe<DXGISharedHandleManager>;
friend class DXGISharedHandleState;
~DXGISharedHandleManager();
mutable base::Lock lock_;
using SharedHandleMap =
base::flat_map<gfx::DXGIHandleToken, DXGISharedHandleState*>;
SharedHandleMap shared_handle_state_map_ GUARDED_BY(lock_);
};
} // namespace gpu
#endif // GPU_COMMAND_BUFFER_SERVICE_DXGI_SHARED_HANDLE_MANAGER_H_