我需要弄清楚如何将来自 D3D 纹理和表面的数据返回系统内存。做这些事情的最快方法是什么?
另外,如果我只需要一个 subrect,那么如何只读回该部分而不必将整个内容读回系统内存?
简而言之,我正在寻找有关如何将以下内容复制到 的简明描述。系统内存 :
这是 Direct3D 9,但有关 D3D 较新版本的答案也将不胜感激。
最佳答案
最复杂的部分是从视频内存(“默认池”)中的某个表面读取。这通常是渲染目标。
让我们先得到简单的部分:
所以现在我们在视频内存中留下了表面(“默认池”)。这将是标记为渲染目标的任何表面/纹理,或您在默认池中创建的任何常规表面/纹理,或后台缓冲区本身。这里的复杂部分是你不能锁定它。
简短的回答是:GetRenderTargetData D3D 设备上的方法。
更长的答案(下面的代码的粗略概述):
更长的答案(从我正在处理的代码库中粘贴)如下。这不会开箱即用,因为它使用了代码库其余部分中的一些类、函数、宏和实用程序;但它应该让你开始。我还省略了大部分错误检查(例如,给定的宽度/高度是否超出范围)。我还省略了读取实际像素并可能将它们转换为合适的目标格式的部分(这很容易,但可能会很长,具体取决于您要支持的格式转换数量)。
bool GfxDeviceD3D9::ReadbackImage( /* params */ )
{
HRESULT hr;
IDirect3DDevice9* dev = GetD3DDevice();
SurfacePointer renderTarget;
hr = dev->GetRenderTarget( 0, &renderTarget );
if( !renderTarget || FAILED(hr) )
return false;
D3DSURFACE_DESC rtDesc;
renderTarget->GetDesc( &rtDesc );
SurfacePointer resolvedSurface;
if( rtDesc.MultiSampleType != D3DMULTISAMPLE_NONE )
{
hr = dev->CreateRenderTarget( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &resolvedSurface, NULL );
if( FAILED(hr) )
return false;
hr = dev->StretchRect( renderTarget, NULL, resolvedSurface, NULL, D3DTEXF_NONE );
if( FAILED(hr) )
return false;
renderTarget = resolvedSurface;
}
SurfacePointer offscreenSurface;
hr = dev->CreateOffscreenPlainSurface( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DPOOL_SYSTEMMEM, &offscreenSurface, NULL );
if( FAILED(hr) )
return false;
hr = dev->GetRenderTargetData( renderTarget, offscreenSurface );
bool ok = SUCCEEDED(hr);
if( ok )
{
// Here we have data in offscreenSurface.
D3DLOCKED_RECT lr;
RECT rect;
rect.left = 0;
rect.right = rtDesc.Width;
rect.top = 0;
rect.bottom = rtDesc.Height;
// Lock the surface to read pixels
hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
if( SUCCEEDED(hr) )
{
// Pointer to data is lt.pBits, each row is
// lr.Pitch bytes apart (often it is the same as width*bpp, but
// can be larger if driver uses padding)
// Read the data here!
offscreenSurface->UnlockRect();
}
else
{
ok = false;
}
}
return ok;
}
SurfacePointer
在上面的代码中是一个指向 COM 对象的智能指针(它在赋值或析构函数时释放对象)。大大简化了错误处理。这与 _comptr_t
非常相似Visual C++ 中的东西。上面的代码读回整个表面。如果您只想有效地阅读其中的一部分,那么我认为最快的方法大致是:
事实上,这与上面处理多采样表面的代码非常相似。我想,如果您只想获得多重采样表面的一部分,您可以进行多重采样解析并将其一部分放入一个 StretchRect 中。
编辑 :删除了一段实际读取像素和格式转换的代码。与问题没有直接关系,代码很长。
编辑 :更新以匹配编辑的问题。
关于directx - 从 Direct3D 纹理和表面进行回读,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/120066/