如何使用 glImportMemoryWin32HandleEXT 与 OpenGL 共享 ID3D11Texture2D KeyedMutex 共享句柄?

How to use glImportMemoryWin32HandleEXT to share an ID3D11Texture2D KeyedMutex Shared handle with OpenGL?

我正在研究如何使用 EXT_external_objects, EXT_external_objects_win32 and EXT_win32_keyed_mutex OpenGL extensions. My goal is to share a B8G8R8A8_UNORM texture (an external library expects BGRA and I can not change it. What's relevant here is the byte depth of 4) with 1 Mip-level allocated and written to offscreen with D3D11 by one application, and render it with OpenGL in another. Because the texture is being drawn to off-process, I can not use WGL_NV_DX_interop2.

与 OpenGL 和 Direct3D 11 进行跨进程互操作

我的实际代码可以看here,是用C#写的Silk.NET。不过为了说明的目的,我将在 psuedo-C(++) 中描述我的问题。

首先,我使用 D3D11 在进程 A 中创建我的纹理,并获取它的共享句柄,并将其发送到进程 B。

#define WIDTH 100
#define HEIGHT 100
#define BPP 4 // BGRA8 is 4 bytes per pixel


ID3D11Texture2D *texture;

D3D11_TEXTURE2D_DESC texDesc = {
  .Width = WIDTH,
  .Height = HEIGHT,
  .MipLevels = 1,
  .ArraySize = 1,
  .Format = DXGI_FORMAT_B8G8R8A8_UNORM,
  .SampleDesc = { .Count = 1, .Quality = 0 }
  .Usage = USAGE_DEFAULT,
  .BindFlags = BIND_SHADER_RESOURCE
  .CPUAccessFlags = 0,
  .MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX
};

device->CreateTexture2D(&texDesc, NULL, &texture);

HANDLE sharedHandle;
texture->CreateSharedHandle(NULL, DXGI_SHARED_RESOURCE_READ, NULL, &sharedHandle);

SendToProcessB(sharedHandle, pid);

在进程 B 中,我首先复制句柄以获得进程本地句柄。

HANDLE localSharedHandle;
HANDLE hProcA = OpenProcess(PROCESS_DUP_HANDLE, false, processAPID);
DuplicateHandle(hProcA, sharedHandle, GetCurrentProcess(), &localSharedHandle, 0, false, DUPLICATE_SAME_ACCESS);
CloseHandle(hProcA)

此时,我在 localSharedHandle 中有一个有效的 DXGI 资源共享句柄。我有一个 ProcessB 的 D3D11 实现,它能够在使用 OpenSharedResource1 打开后成功渲染共享纹理。然而,我的问题是 OpenGL。

这就是我目前为 OpenGL 做的事情

GLuint sharedTexture, memObj;
glCreateTextures(GL_TEXTURE_2D, 1, &sharedTexture);
glTextureParameteri(sharedTexture, TEXTURE_TILING_EXT, OPTIMAL_TILING_EXT); // D3D11 side is D3D11_TEXTURE_LAYOUT_UNDEFINED
// Create the memory object handle
glCreateMemoryObjectsEXT(1, &memObj);

// I am not actually sure what the size parameter here is referring to.
// Since the source texture is DX11, there's no way to get the allocation size, 
// I make a guess of W * H * BPP

// According to docs for VkExternalMemoryHandleTypeFlagBitsNV, NtHandle Shared Resources use HANDLE_TYPE_D3D11_IMAGE_EXT
glImportMemoryWin32HandleEXT(memObj, WIDTH * HEIGHT * BPP, GL_HANDLE_TYPE_D3D11_IMAGE_EXT, (void*)localSharedHandle);
DBG_GL_CHECK_ERROR(); // GL_NO_ERROR

沿途检查错误似乎表明导入成功。但是我无法绑定纹理。

if (glAcquireKeyedMutexWin32EXT(memObj, 0, (UINT)-1) {
  DBG_GL_CHECK_ERROR(); // GL_NO_ERROR
  glTextureStorageMem2D(sharedTexture, 1, GL_RGBA8, WIDTH, HEIGHT, memObj, 0); 
  DBG_GL_CHECK_ERROR(); // GL_INVALID_VALUE
  glReleaseKeyedMutexWin32EXT(memObj, 0);
}

对 glTextureStorageMem2D 的调用出了问题。正在正确获取和释放共享的 KeyedMutex。扩展文档不清楚我应该如何正确绑定此纹理并绘制它。

经过更多调试后,我设法从调试上下文中获取了 [DebugSeverityHigh] DebugSourceApi: DebugTypeError, id: 1281: GL_INVALID_VALUE error generated. Memory object too small。通过将我的宽度分成两半,我能够在屏幕上得到一些乱码输出。

事实证明导入纹理所需的大小不是 WIDTH * HEIGHT * BPP,(在本例中 BGRA 的 BPP = 4),而是 WIDTH * HEIGHT * BPP * 2。导入大小为 WIDTH * HEIGHT * BPP * 2 的句柄允许纹理正确绑定和正确渲染。