Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save pabloko/42e28680e04cb15059c9c1a92b4ae622 to your computer and use it in GitHub Desktop.
Save pabloko/42e28680e04cb15059c9c1a92b4ae622 to your computer and use it in GitHub Desktop.
Query information from NT Handles of shared D3D11 Textures

Query information from NT Handles of shared D3D11 Textures

tl;dr: you can't. Only D3D12 and Vulkan resources expose API to query it's physical size

Looking into using the gl extension EXT_external_objects_win32 stumbled upon this <size> parameter in glImportMemoryWin32HandleEXT when using a D3D11 resources

tl;dr: Use 0 as size when using HANDLE_TYPE_D3D11_IMAGE_EXT / HANDLE_TYPE_D3D11_IMAGE_KMT_EXT

the driver implmentation will figure out the internal size of the resource. This size would be alignedWidth * alignedHeight * Bpp where alignment varies depending of the graphics card vendor, model and driver version.

To give some context, here are some values from my NV card with updated drivers

size wh4 real size alignedWidth alignedHeight
500x350 700000 786432 512 384
1000x700 2800000 3096576 1024 756
1x1 4 512 16 8
2x2 16 512 16 8
1024x1024 4194304 4194304 1024 1024

Other graphics like intel or AMD showed similar values but different alignment, but a quick conclussion can be taken, non power-of-two texture support is just an illusion on top of the driver, so writing highly memory efficient programs requires the usage of power-of-two atlas

It's still possible to access information of the handle using D3DKMT api. Obtain a D3DKMT Adapter and device (shown in example) and use D3DKMTQueryResourceInfoFromNtHandle for the NT handle to obtain the sizes of the private data tables and then obtain the private data using D3DKMTOpenResourceFromNtHandle, in case of KMT handle, i guess D3DKMTQueryResourceInfo and D3DKMTOpenResource are used instead. Theres two structs we're looking for:

  • PrivateRuntimeData: This is a DXGI private structure that is confirmed to change across different versions of windows
  • TotalPrivateDriverDataBuffer: This is the vendor ICD private data associated with the resource, where the byte size value resides, and confirmed to change across hardware vendor and even versions of the driver

At the time of the writing, the PrivateRuntimeData structure looks like:

typedef struct DXGI_PRIVATEDATA {
	UINT Size;
	INT Id;
	UINT Width;
	UINT Height;
	DXGI_FORMAT Format;
	UINT ArraySize;
	INT ReadOnly;
	INT Sync;
	INT KeyedMutex;
	INT unk0;
	INT NTHandle;
	INT uin1;
	INT unk2;
	INT unk3;
	D3D11_RESOURCE_DIMENSION Dimension;
	D3D11_TEXTURE2D_DESC Desc;
} DXGI_PRIVATEDATA;

TotalPrivateDriverDataBuffer contains the resource bytesize on a different offset in each observed hardware and even different driver versions of the same hardware, for example on latest NV drivers, offset is 0x13E so theres no reliable way of obtaining the bytesize automatically.

#include <stdio.h>
#include <windows.h>
#include <D3dkmthk.h>
#include <d3d11.h>
#include <d3d11_1.h>
#pragma comment(lib, "d3d11")
extern "C"
{
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}
/*
* Simple hexdump
* https://gist.github.com/ccbrown/9722406
*/
void DumpHex(const void* data, size_t size) {
char ascii[17];
size_t i, j;
ascii[16] = '\0';
for (i = 0; i < size; ++i) {
printf(" %02X", ((unsigned char*)data)[i]);
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {
ascii[i % 16] = ((unsigned char*)data)[i];
}
else {
ascii[i % 16] = '.';
}
if ((i + 1) % 8 == 0 || i + 1 == size) {
printf(" ");
if ((i + 1) % 16 == 0) {
printf("| %s \n", ascii);
//printf("\n");
}
else if (i + 1 == size) {
ascii[(i + 1) % 16] = '\0';
if ((i + 1) % 16 <= 8) {
printf(" ");
}
for (j = (i + 1) % 16; j < 16; ++j) {
printf(" ");
}
printf("| %s \n", ascii);
//printf("\n");
}
}
}
}
/*
* Seems to be DXGI part of the data, representing a texture desc
*/
typedef struct DXGI_PRIVATEDATA {
UINT Size;
INT Id;
UINT Width;
UINT Height;
DXGI_FORMAT Format;
UINT ArraySize;
INT ReadOnly;
INT Sync;
INT KeyedMutex;
INT unk0;
INT NTHandle;
INT uin1;
INT unk2;
INT unk3;
D3D11_RESOURCE_DIMENSION Dimension;
D3D11_TEXTURE2D_DESC Desc;
} DXGI_PRIVATEDATA;
/*
* Seems to be the ICD implementation part of the data (vendor specific)
*/
#pragma pack(1)
typedef struct UNK_TOTALDRIVERPRIVATEDATA {
const char unk_pad0[0x13E];
UINT pMagicValue;
};
/*
* Helper to debug D3DKMT returns
*/
BOOL CHECK_NT(NTSTATUS status, const char* error)
{
if (FAILED(status))
{
printf("Failed (%08X) %s\n", status, error);
return true;
}
printf("Success: %s\n", error);
return false;
}
#define CHECK_NTSTATUS(X) if(CHECK_NT(NT_RETURN, X)) return 0;
int main()
{
UINT WIDTH = 1000;
UINT HEIGHT = 500;
// Create a D3D11 device
ID3D11Device* device = NULL;
ID3D11DeviceContext* context = NULL;
UINT creation_flags = (D3D11_CREATE_DEVICE_VIDEO_SUPPORT | D3D11_CREATE_DEVICE_BGRA_SUPPORT);
static const D3D_FEATURE_LEVEL feature_levels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1 };
D3D_FEATURE_LEVEL* ftret = NULL;
if (FAILED(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, creation_flags, feature_levels, 7, D3D11_SDK_VERSION, &device, ftret, &context))) return NULL;
printf("Success: D3D11CreateDevice\n");
// Create a texture
ID3D11Texture2D* texture = NULL;
D3D11_TEXTURE2D_DESC texDesc{ 0 };
texDesc.Width = WIDTH;
texDesc.Height = HEIGHT;
texDesc.MipLevels = 1;
texDesc.ArraySize = 1;
texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
texDesc.CPUAccessFlags = 0;
texDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED;
if (FAILED(device->CreateTexture2D(&texDesc, NULL, &texture))) return NULL;
printf("Success: ID3D11Device::CreateTexture2D (%dx%d)\n", texDesc.Width, texDesc.Height);
// Share the texture to a NT HANDLE
HANDLE ntHandle = 0;
IDXGIResource* dxgiRsrc = 0;
if (SUCCEEDED(texture->QueryInterface(&dxgiRsrc)))
{
IDXGIResource1* dxgiResource = 0;
if (SUCCEEDED(dxgiRsrc->QueryInterface(&dxgiResource)))
{
if (SUCCEEDED(dxgiResource->CreateSharedHandle(NULL, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, NULL, &ntHandle)))
{
printf("Success: IDXGIResource::CreateSharedHandle (NTHANDLE: %08X)\n", ntHandle);
}
dxgiResource->Release();
}
dxgiRsrc->Release();
}
NTSTATUS NT_RETURN = 0;
/*
D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME OPENADAPTERFROMGDIDISPLAYNAME{ 0 };
wsprintf(OPENADAPTERFROMGDIDISPLAYNAME.DeviceName, L"\\\\.\\DISPLAY1");
NT_RETURN = D3DKMTOpenAdapterFromGdiDisplayName(&OPENADAPTERFROMGDIDISPLAYNAME);
CHECK_NTSTATUS("D3DKMTOpenAdapterFromGdiDisplayName");
D3DKMT_HANDLE hAdapter = OPENADAPTERFROMGDIDISPLAYNAME.hAdapter;
*/
D3DKMT_ENUMADAPTERS ENUMADAPTERS{ 0 };
NT_RETURN = D3DKMTEnumAdapters(&ENUMADAPTERS);
CHECK_NTSTATUS("D3DKMTEnumAdapters");
D3DKMT_HANDLE hAdapter = ENUMADAPTERS.Adapters[0].hAdapter;
// Create a kmt device, what is this device? i dont know, but we close it later
D3DKMT_CREATEDEVICE CREATEDEVICE{ 0 };
CREATEDEVICE.hAdapter = hAdapter;
NT_RETURN = D3DKMTCreateDevice(&CREATEDEVICE);
CHECK_NTSTATUS("D3DKMTCreateDevice");
// Some debug info about the gl icd and device
D3DKMT_QUERYADAPTERINFO QUERYADAPTERINFO{ 0 };
QUERYADAPTERINFO.hAdapter = hAdapter;
QUERYADAPTERINFO.Type = KMTQAITYPE_ADAPTERREGISTRYINFO;
D3DKMT_ADAPTERREGISTRYINFO ADAPTERREGISTRYINFO{ 0 };
QUERYADAPTERINFO.PrivateDriverDataSize = sizeof(D3DKMT_ADAPTERREGISTRYINFO);
QUERYADAPTERINFO.pPrivateDriverData = &ADAPTERREGISTRYINFO;
NT_RETURN = D3DKMTQueryAdapterInfo(&QUERYADAPTERINFO);
CHECK_NTSTATUS("D3DKMTQueryAdapterInfo");
wprintf(L" Adapter: %s\n BIOS: %s\n DAC: %s\n CHIP: %s\n", ADAPTERREGISTRYINFO.AdapterString, ADAPTERREGISTRYINFO.BiosString, ADAPTERREGISTRYINFO.DacType, ADAPTERREGISTRYINFO.ChipType);
D3DKMT_OPENGLINFO OPENGLINFO{ 0 };
QUERYADAPTERINFO.Type = KMTQAITYPE_UMOPENGLINFO;
QUERYADAPTERINFO.PrivateDriverDataSize = sizeof(D3DKMT_OPENGLINFO);
QUERYADAPTERINFO.pPrivateDriverData = &OPENGLINFO;
NT_RETURN = D3DKMTQueryAdapterInfo(&QUERYADAPTERINFO);
CHECK_NTSTATUS("D3DKMTQueryAdapterInfo");
wprintf(L" OpenGL ICD: %s\n Version: %d\n Flags: %d\n", OPENGLINFO.UmdOpenGlIcdFileName, OPENGLINFO.Version, OPENGLINFO.Flags);
// Obtain the sizes of the private data structures
D3DKMT_QUERYRESOURCEINFOFROMNTHANDLE QUERYRESOURCEINFOFROMNTHANDLE{ 0 };
QUERYRESOURCEINFOFROMNTHANDLE.hDevice = CREATEDEVICE.hDevice;
QUERYRESOURCEINFOFROMNTHANDLE.hNtHandle = ntHandle;
NT_RETURN = D3DKMTQueryResourceInfoFromNtHandle(&QUERYRESOURCEINFOFROMNTHANDLE);
CHECK_NTSTATUS("D3DKMTQueryResourceInfoFromNtHandle");
wprintf(L" PrivateRuntimeDataSize: %d\n TotalPrivateDriverDataSize: %d\n", QUERYRESOURCEINFOFROMNTHANDLE.PrivateRuntimeDataSize, QUERYRESOURCEINFOFROMNTHANDLE.TotalPrivateDriverDataSize);
/* non nt handles? (KMT)
D3DKMT_QUERYRESOURCEINFO QUERYRESOURCEINFO{ 0 };
ntret = D3DKMTQueryResourceInfo(&QUERYRESOURCEINFO);
*/
// read the private data structures
D3DKMT_OPENRESOURCEFROMNTHANDLE OPENRESOURCEFROMNTHANDLE{ 0 };
OPENRESOURCEFROMNTHANDLE.hDevice = CREATEDEVICE.hDevice;
OPENRESOURCEFROMNTHANDLE.hNtHandle = ntHandle;
OPENRESOURCEFROMNTHANDLE.NumAllocations = QUERYRESOURCEINFOFROMNTHANDLE.NumAllocations;
D3DDDI_OPENALLOCATIONINFO2* OPENALLOCATIONINFO2 = (D3DDDI_OPENALLOCATIONINFO2*)calloc(sizeof(D3DDDI_OPENALLOCATIONINFO2), QUERYRESOURCEINFOFROMNTHANDLE.NumAllocations);
OPENRESOURCEFROMNTHANDLE.pOpenAllocationInfo2 = OPENALLOCATIONINFO2;
OPENRESOURCEFROMNTHANDLE.KeyedMutexPrivateRuntimeDataSize = 0;
OPENRESOURCEFROMNTHANDLE.PrivateRuntimeDataSize = QUERYRESOURCEINFOFROMNTHANDLE.PrivateRuntimeDataSize;
void* pPrivateRuntimeData = (void*)calloc(1, QUERYRESOURCEINFOFROMNTHANDLE.PrivateRuntimeDataSize);
OPENRESOURCEFROMNTHANDLE.pPrivateRuntimeData = pPrivateRuntimeData;
OPENRESOURCEFROMNTHANDLE.ResourcePrivateDriverDataSize = QUERYRESOURCEINFOFROMNTHANDLE.ResourcePrivateDriverDataSize;
void* pResourcePrivateDriverData = (void*)calloc(1, QUERYRESOURCEINFOFROMNTHANDLE.ResourcePrivateDriverDataSize);
OPENRESOURCEFROMNTHANDLE.pResourcePrivateDriverData = pResourcePrivateDriverData;
OPENRESOURCEFROMNTHANDLE.TotalPrivateDriverDataBufferSize = QUERYRESOURCEINFOFROMNTHANDLE.TotalPrivateDriverDataSize;
void* pTotalPrivateDriverDataBuffer = (void*)calloc(1, QUERYRESOURCEINFOFROMNTHANDLE.TotalPrivateDriverDataSize);
OPENRESOURCEFROMNTHANDLE.pTotalPrivateDriverDataBuffer = pTotalPrivateDriverDataBuffer;
NT_RETURN = D3DKMTOpenResourceFromNtHandle(&OPENRESOURCEFROMNTHANDLE);
CHECK_NTSTATUS("D3DKMTOpenResourceFromNtHandle");
// hex dump the structures
printf("PrivateRuntimeData:\n");
DumpHex(OPENRESOURCEFROMNTHANDLE.pPrivateRuntimeData, OPENRESOURCEFROMNTHANDLE.PrivateRuntimeDataSize);
printf("TotalPrivateDriverData:\n");
DumpHex(OPENRESOURCEFROMNTHANDLE.pTotalPrivateDriverDataBuffer, OPENRESOURCEFROMNTHANDLE.TotalPrivateDriverDataBufferSize);
// check sizes can be holded in our struct, seems to be constant, PrivateRuntimeDataSize: 104 and TotalPrivateDriverDataSize: 594 (at least on nvidia ICD)
if (sizeof(UNK_TOTALDRIVERPRIVATEDATA) > QUERYRESOURCEINFOFROMNTHANDLE.TotalPrivateDriverDataSize) return 0;
if (sizeof(DXGI_PRIVATEDATA) > QUERYRESOURCEINFOFROMNTHANDLE.PrivateRuntimeDataSize) return 0;
// cast buffers to readable structures
DXGI_PRIVATEDATA* dv = (DXGI_PRIVATEDATA*)OPENRESOURCEFROMNTHANDLE.pPrivateRuntimeData;
UNK_TOTALDRIVERPRIVATEDATA* pv = (UNK_TOTALDRIVERPRIVATEDATA*)OPENRESOURCEFROMNTHANDLE.pTotalPrivateDriverDataBuffer;
// debug
printf(" Texture size: %dx%d\n", dv->Width, dv->Height);
printf(" Texture format: %d MiscFlags: %d\n", dv->Format, dv->Desc.MiscFlags);
printf("\n Texture size: %d Bytes\n\n", pv->pMagicValue);
// the handle has been opened and an allocation is made, somehow this seems to close it
D3DKMT_DESTROYALLOCATION DESTROYALLOCATION{ 0 };
DESTROYALLOCATION.hDevice = CREATEDEVICE.hDevice;
DESTROYALLOCATION.hResource = OPENRESOURCEFROMNTHANDLE.hResource;
NT_RETURN = D3DKMTDestroyAllocation(&DESTROYALLOCATION);
CHECK_NTSTATUS("D3DKMTDestroyAllocation");
// not sure if i need to do this, i guess its ok¿?
CloseHandle(ntHandle);
// close the staging device
D3DKMT_DESTROYDEVICE DESTROYDEVICE{ 0 };
DESTROYDEVICE.hDevice = CREATEDEVICE.hDevice;
NT_RETURN = D3DKMTDestroyDevice(&DESTROYDEVICE);
CHECK_NTSTATUS("D3DKMTDestroyDevice");
// free resources
//ReleaseDC(0, OPENADAPTERFROMHDC.hDc);
free(OPENALLOCATIONINFO2);
free(pPrivateRuntimeData);
free(pResourcePrivateDriverData);
free(pTotalPrivateDriverDataBuffer);
texture->Release();
context->Release();
device->Release();
getchar();
return 0;
}
Success: D3D11CreateDevice
Success: ID3D11Device::CreateTexture2D (1000x500)
Success: IDXGIResource::CreateSharedHandle (NTHANDLE: 00000404)
Success: D3DKMTEnumAdapters
Success: D3DKMTCreateDevice
Success: D3DKMTQueryAdapterInfo
Adapter: NVIDIA GeForce RTX 3060
BIOS: Version94.6.2f.0.d6
DAC: Integrated RAMDAC
CHIP: NVIDIA GeForce RTX 3060
Success: D3DKMTQueryAdapterInfo
OpenGL ICD: C:\Windows\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_f52c4b8723f8dd33\nvoglv64.dll
Version: 4096
Flags: 3
Success: D3DKMTQueryResourceInfoFromNtHandle
PrivateRuntimeDataSize: 104
TotalPrivateDriverDataSize: 594
Success: D3DKMTOpenResourceFromNtHandle
PrivateRuntimeData:
68 00 00 00 04 00 00 00 E8 03 00 00 F4 01 00 00 | h...............
57 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 | W...............
00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 03 00 00 00 E8 03 00 00 | ................
F4 01 00 00 01 00 00 00 01 00 00 00 57 00 00 00 | ............W...
01 00 00 00 00 00 00 00 00 00 00 00 28 00 00 00 | ............(...
00 00 00 00 02 08 00 00 | ........
TotalPrivateDriverData:
41 44 56 4E 04 00 01 00 52 02 00 00 58 44 56 4E | ADVN....R...XDVN
28 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | (...............
11 80 00 00 23 00 00 00 07 00 00 00 00 00 00 00 | ....#...........
E8 03 00 00 F4 01 00 00 01 00 00 00 01 00 00 00 | ................
01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 00 F4 01 00 00 04 00 | ................
00 00 03 00 00 00 03 00 00 00 01 00 00 00 00 00 | ................
00 00 00 00 00 00 00 10 00 A0 00 00 00 00 01 00 | ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 40 F0 03 00 00 F4 01 | .........@......
00 00 C0 0F 00 00 00 00 20 00 00 00 00 00 FF FF | ........ .......
1F 00 00 00 00 00 04 20 82 01 00 00 00 00 00 00 | ....... ........
00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF | ................
FF FF FF FF FF FF 00 00 00 00 00 00 00 00 00 00 | ................
00 00 06 00 10 00 C0 0F 00 00 00 00 20 00 00 00 | ............ ...
00 00 00 00 00 00 04 20 02 00 06 00 00 00 00 00 | ....... ........
00 00 04 00 00 00 00 00 00 00 3F 00 00 00 04 00 | ..........?.....
00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 80 | ................
1F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 08 00 00 00 06 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 20 00 00 00 20 00 08 00 | ........ ... ...
00 00 B9 13 03 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 80 1F 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 | ..
Texture size: 1000x500
Texture format: 87 MiscFlags: 2050
Texture size: 2064384 Bytes
Success: D3DKMTDestroyAllocation
Success: D3DKMTDestroyDevice
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment