转换DirectX接口

时间:2014-07-15 09:44:22

标签: c++ com directx

我试图将接口指针放在不同类型的着色器中,如下所示:

std::vector<ID3D11DeviceChild*> shaders;
ID3D11VertexShader* VS;
// ... instantiate and init VS
shaders.push_back(static_cast<ID3D11DeviceChild*>(VS));
// and when VS is needed again:
ID3D11VertexShader* VS = static_cast<ID3D11VertexShader*>(shaders[i]);
// use VS here

我的问题是这种类型转换COM接口的方法是否安全。我知道它适用于普通的多态类层次结构,但我不确定COM。

3 个答案:

答案 0 :(得分:2)

首先,我建议在std::vector之类的STL容器中存储智能指针而不是 raw拥有指针,例如

std::vector<CComPtr<ID3D11DeviceChild>> shaders;

在STL容器中使用原始拥有指针是泄漏源,潜在错误和异常不安全代码(观察原始指针很好)。

要从ID3D11DeviceChild*转发到ID3D11VertexShader*,请考虑使用QueryInterface()(再次使用ATL智能指针,如CComPtr简化代码):

CComPtr<ID3D11VertexShader> spVertexShader;
HRESULT hr = (shaders[i])->QueryInterface(IID_PPV_ARGS(&spVertexShader));
if (FAILED(hr))
    ...

<强> PS

除非我遗漏了某些内容,因为ID3D11VertexShaderID3D11DeviceChild的派生类,因此您不需要static_cast

shaders.push_back(static_cast<ID3D11DeviceChild*>(VS));

这应该没问题:

shaders.push_back(VS);

关于CAdapt

的说明

请注意,您可能需要CAdapt使用std::vector

std::vector<CAdapt<CComPtr<ID3D11DeviceChild>>> shaders;

符合要求的C ++ 11 STL实现需要CAdapt,但我认为至少需要VS2008和VS2010。 我不确定更多现代版本的Visual Studio,但它似乎已经修复了,例如: 您可能希望阅读this Visual C++ blog post中标题为“抵制重载地址的操作符”的段落。

答案 1 :(得分:1)

ID3D11VertexShader继承自ID3D11DeviceChild,因此您甚至无需使用static_cast<ID3D11DeviceChild*>将其推送到矢量。向下转换矢量元素到shader的更安全的方法是使用QueryInterface

CComPtr<ID3D11VertexShader> vs;
if (S_OK == shaders[i]->QueryInterface(IID_ID3D11VertexShader, 
       reinterpret_cast<void**>(&vs)))
{
   // Use vs here
}

答案 2 :(得分:1)

只要您绝对确定,该向量只包含ID3D11VertexShader,这是安全的,在这种情况下,您应该只使用它而不是ID3D11DeviceChild

如果您希望向量包含混合的接口指针,每个接口指针都有共同的祖先ID3D11DeviceChild,那么最好依赖于QueryInterface,以便存储到向量中并从向量中检索。

请记住,正确的代码优于快速代码,至少在开始时是这样。

当您分析时,如果,并且只有当您发现QI是瓶颈时,那么您应该对此做些什么,但我建议您使用结构矢量它有一个指向ID3D11DeviceChild的指针和一个接口标识符(IID),因此您可以在运行时检查您存储的接口指针类型:

struct DeviceChildPtr {
    ID3D11DeviceChild *ptr;
    REFIID riid;
};

std::vector<DeviceChildPtr> shaders;
// ... instantiate, initialize ...
DeviceChildPtr shader;
shader.ptr = static_cast<ID3D11DeviceChild*>(VS);
shader.iid = IID_ID3D11VertexShader;
shaders.push_back(static_cast<ID3D11DeviceChild*>(shader));

// ...

DeviceChildPtr shader = shaders[i];
// If you only ever use global IIDs, you may replace IsEqualIID with ==
// This is like a half-baked QueryInterface that only checks for a specific IID
if (IsEqualIID(shader.riid, IID_ID3D11VertexShader) {
    ID3D11VertexShader* VS = static_cast<ID3D11VertexShader*>(shader.ptr);
    // ... use ...
}

如果您经历过这条路径,请记住通过这种方式评论为什么(性能?)以及如何(通过运行时接口类型检查进行转换,安全性),而不是使用QueryInterface

我的猜测是,因为内部DX本身很可能会使用QueryInterface,所以你不应该在这里找到瓶颈。