如何使用 CreateExternalTexture 和从 C++ 库传递的指针创建 Texture2D?
How to create Texture2D with CreateExternalTexture and a pointer passed from C++ library?
我正在尝试了解如何在 C++ DirectX11 代码和 Unity 引擎之间建立通信。
我想做的是在 C++ 中创建一个非常简单的 ID3D11Texture2D
,然后将其传递给 Unity,通过 CreateExternalTexture 方法创建它的 Texture2D
。
所以,我有一个 C++ DLL VS 项目,其中有一个函数:
ID3D11ShaderResourceView* GetTexture()
{
ofstream myfile;
myfile.open("GetTexture.log");
const int width = 640;
const int height = 480;
unsigned char* pixels = new unsigned char[width * height * 4];
unsigned char* pixelsP = pixels;
int numPixels = (int)(width * height);
ID3D11Texture2D* tex = nullptr;
ID3D11ShaderResourceView* shaderView = nullptr;
ID3D11Device* device = CreateDX11device();
if (!device)
{
myfile << "Cannot create DX11 device" << endl;
myfile.close();
return 0;
}
else
{
myfile << "Successfully created DX11 device" << endl;
}
for (int i = 0; i < numPixels; i++)
{
*(pixelsP) = 255;
*(pixelsP + 1) = 0;
*(pixelsP + 2) = 0;
*(pixelsP + 3) = 122;
pixelsP += 4;
}
D3D11_SUBRESOURCE_DATA initData = { 0 };
initData.pSysMem = (const void*)pixels;
initData.SysMemPitch = width * 4;
initData.SysMemSlicePitch = width * height * 4;
D3D11_TEXTURE2D_DESC desc = {};
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DYNAMIC;//D3D11_USAGE_IMMUTABLE;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;// | D3D11_BIND_RENDER_TARGET;//D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
HRESULT hr = device->CreateTexture2D(&desc, &initData, &tex);
if (FAILED(hr))
{
myfile << "FAILED TO CREATE DX TEXTURE" << endl;
myfile << hr << endl;
myfile.close();
return nullptr;
}
else
{
myfile << "Successfully Created DX Texture" << endl;
}
myfile.close();
D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
ZeroMemory(&shaderResourceViewDesc, sizeof(shaderResourceViewDesc));
shaderResourceViewDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
shaderResourceViewDesc.Texture2D.MipLevels = 1;
device->CreateShaderResourceView(tex, &shaderResourceViewDesc, &shaderView);
return shaderView;
}
下面是我创建 DX11 设备的方法:
ID3D11Device* CreateDX11device()
{
ID3D11Device* device = nullptr;
HRESULT hr = S_OK;
UINT createDeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
D3D_DRIVER_TYPE driverTypes[] = {
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE,
};
UINT numDriverTypes = ARRAYSIZE(driverTypes);
D3D_FEATURE_LEVEL featureLevels[] = {
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_1
};
ID3D11DeviceContext* context = nullptr;
UINT numFeatureLevels = ARRAYSIZE(featureLevels);
hr = D3D11CreateDevice(nullptr,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
createDeviceFlags,
featureLevels,
numFeatureLevels,
D3D11_SDK_VERSION,
&device,
nullptr,
&context);
if (FAILED(hr)) {
cout << "Could not create DX11 device" << endl;
return nullptr;
}
cout << "DX Device Created" << endl;
return device;
}
现在,函数 GetTexture
仅从 Unity 中的 C# 代码调用,结果被视为 IntPtr
。然后我开始创建 Texture2D 并设置纹理以在 Unity 中查看结果:
[DllImport("TestDX11.dll", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr GetTexture();
void Start()
{
IntPtr ptr = GetTexture();
Texture2D texture = Texture2D.CreateExternalTexture(640, 480, TextureFormat.RGBA32, false, false, ptr);
GetComponent<RawImage>().texture = texture;
}
问题是我希望在 Unity 应用程序中看到一个红色图像,但我什么也没看到,纹理是空白的,就好像我没有在里面放任何东西一样。为了确保我尝试从 DLL 输出接收到的指针并且它是非空的,所以我确实在纹理上得到了一个指针。在纹理创建过程中尝试使用不同的标志时,我感到很迷茫,但没有任何帮助。
我也尝试发送 ID3D11Texture2D*
而不是 ID3D11ResourceShaderView*
正如 this link 指出的那样,但是当 CreateExternalTexture
方法被调用时由于某种原因它崩溃了 Unity。
如有任何帮助,我将不胜感激!
在您的情况下,您正在使用由 D3D11CreateDevice 创建的设备创建纹理。
这是一个不同于Unity使用的设备,所以当你调用Texture2D.CreateExternalTexture时,无论是Unity还是Unity使用的设备都会检测到它不拥有这个资源,所以它不能使用它。
为了让 Unity 可以访问此纹理,您需要使用 Unity 本身使用的设备创建纹理。
为此,您需要使用 Unity 的 Low-level native plug-in interface
您需要在您的 dll 中声明一些特定的函数,以便 Unity 可以检测到它们然后调用。
在您的情况下,您可以使用 UnityPluginLoad“回调,并从那里检索设备(为简单起见,下面的代码不按照使用 Direct3D12 或其他图形的 Unity 处理情况 API)。
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
UnityPluginLoad(IUnityInterfaces* interfaces)
{
ID3D11Device* dxDevice;
ID3D11DeviceContext* dxDeviceContext;
dxDevice = interfaces->Get<IUnityGraphicsD3D11>()->GetDevice();
dxDevice ->GetImmediateContext(&dxDeviceContext);
}
可以从 Texture2D.CreateExternalTexture
访问使用此设备创建的任何纹理
注意:由于 Unity Direct3D11 设备(当时我在 Unity 中做了一些项目,那是几年前的事了,我不确定最新版本),使用的是 D3D11_CREATE_DEVICE_FLAG::D3D11_CREATE_DEVICE_SINGLETHREADED),你需要在标准 Unity 线程中创建你的纹理,否则你可能(将)有潜在的崩溃(在我们的例子中,我们必须启动 unity 和我们的应用程序,使用自定义命令行来禁用这个标志)。
我正在尝试了解如何在 C++ DirectX11 代码和 Unity 引擎之间建立通信。
我想做的是在 C++ 中创建一个非常简单的 ID3D11Texture2D
,然后将其传递给 Unity,通过 CreateExternalTexture 方法创建它的 Texture2D
。
所以,我有一个 C++ DLL VS 项目,其中有一个函数:
ID3D11ShaderResourceView* GetTexture()
{
ofstream myfile;
myfile.open("GetTexture.log");
const int width = 640;
const int height = 480;
unsigned char* pixels = new unsigned char[width * height * 4];
unsigned char* pixelsP = pixels;
int numPixels = (int)(width * height);
ID3D11Texture2D* tex = nullptr;
ID3D11ShaderResourceView* shaderView = nullptr;
ID3D11Device* device = CreateDX11device();
if (!device)
{
myfile << "Cannot create DX11 device" << endl;
myfile.close();
return 0;
}
else
{
myfile << "Successfully created DX11 device" << endl;
}
for (int i = 0; i < numPixels; i++)
{
*(pixelsP) = 255;
*(pixelsP + 1) = 0;
*(pixelsP + 2) = 0;
*(pixelsP + 3) = 122;
pixelsP += 4;
}
D3D11_SUBRESOURCE_DATA initData = { 0 };
initData.pSysMem = (const void*)pixels;
initData.SysMemPitch = width * 4;
initData.SysMemSlicePitch = width * height * 4;
D3D11_TEXTURE2D_DESC desc = {};
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DYNAMIC;//D3D11_USAGE_IMMUTABLE;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;// | D3D11_BIND_RENDER_TARGET;//D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
HRESULT hr = device->CreateTexture2D(&desc, &initData, &tex);
if (FAILED(hr))
{
myfile << "FAILED TO CREATE DX TEXTURE" << endl;
myfile << hr << endl;
myfile.close();
return nullptr;
}
else
{
myfile << "Successfully Created DX Texture" << endl;
}
myfile.close();
D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
ZeroMemory(&shaderResourceViewDesc, sizeof(shaderResourceViewDesc));
shaderResourceViewDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
shaderResourceViewDesc.Texture2D.MipLevels = 1;
device->CreateShaderResourceView(tex, &shaderResourceViewDesc, &shaderView);
return shaderView;
}
下面是我创建 DX11 设备的方法:
ID3D11Device* CreateDX11device()
{
ID3D11Device* device = nullptr;
HRESULT hr = S_OK;
UINT createDeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
D3D_DRIVER_TYPE driverTypes[] = {
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE,
};
UINT numDriverTypes = ARRAYSIZE(driverTypes);
D3D_FEATURE_LEVEL featureLevels[] = {
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_1
};
ID3D11DeviceContext* context = nullptr;
UINT numFeatureLevels = ARRAYSIZE(featureLevels);
hr = D3D11CreateDevice(nullptr,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
createDeviceFlags,
featureLevels,
numFeatureLevels,
D3D11_SDK_VERSION,
&device,
nullptr,
&context);
if (FAILED(hr)) {
cout << "Could not create DX11 device" << endl;
return nullptr;
}
cout << "DX Device Created" << endl;
return device;
}
现在,函数 GetTexture
仅从 Unity 中的 C# 代码调用,结果被视为 IntPtr
。然后我开始创建 Texture2D 并设置纹理以在 Unity 中查看结果:
[DllImport("TestDX11.dll", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr GetTexture();
void Start()
{
IntPtr ptr = GetTexture();
Texture2D texture = Texture2D.CreateExternalTexture(640, 480, TextureFormat.RGBA32, false, false, ptr);
GetComponent<RawImage>().texture = texture;
}
问题是我希望在 Unity 应用程序中看到一个红色图像,但我什么也没看到,纹理是空白的,就好像我没有在里面放任何东西一样。为了确保我尝试从 DLL 输出接收到的指针并且它是非空的,所以我确实在纹理上得到了一个指针。在纹理创建过程中尝试使用不同的标志时,我感到很迷茫,但没有任何帮助。
我也尝试发送 ID3D11Texture2D*
而不是 ID3D11ResourceShaderView*
正如 this link 指出的那样,但是当 CreateExternalTexture
方法被调用时由于某种原因它崩溃了 Unity。
如有任何帮助,我将不胜感激!
在您的情况下,您正在使用由 D3D11CreateDevice 创建的设备创建纹理。
这是一个不同于Unity使用的设备,所以当你调用Texture2D.CreateExternalTexture时,无论是Unity还是Unity使用的设备都会检测到它不拥有这个资源,所以它不能使用它。
为了让 Unity 可以访问此纹理,您需要使用 Unity 本身使用的设备创建纹理。
为此,您需要使用 Unity 的 Low-level native plug-in interface
您需要在您的 dll 中声明一些特定的函数,以便 Unity 可以检测到它们然后调用。
在您的情况下,您可以使用 UnityPluginLoad“回调,并从那里检索设备(为简单起见,下面的代码不按照使用 Direct3D12 或其他图形的 Unity 处理情况 API)。
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
UnityPluginLoad(IUnityInterfaces* interfaces)
{
ID3D11Device* dxDevice;
ID3D11DeviceContext* dxDeviceContext;
dxDevice = interfaces->Get<IUnityGraphicsD3D11>()->GetDevice();
dxDevice ->GetImmediateContext(&dxDeviceContext);
}
可以从 Texture2D.CreateExternalTexture
访问使用此设备创建的任何纹理注意:由于 Unity Direct3D11 设备(当时我在 Unity 中做了一些项目,那是几年前的事了,我不确定最新版本),使用的是 D3D11_CREATE_DEVICE_FLAG::D3D11_CREATE_DEVICE_SINGLETHREADED),你需要在标准 Unity 线程中创建你的纹理,否则你可能(将)有潜在的崩溃(在我们的例子中,我们必须启动 unity 和我们的应用程序,使用自定义命令行来禁用这个标志)。