当前位置:  开发笔记 > 编程语言 > 正文

从Direct3D纹理和曲面进行回读

如何解决《从Direct3D纹理和曲面进行回读》经验,为你挑选了1个好方法。

我需要弄清楚如何将D3D纹理和曲面的数据恢复到系统内存.做这些事情的最快方法是什么?

另外,如果我只需要一个subrect,那么如何只读回那部分而不必将整个内容读回系统内存?

总之,我正在寻找如何将以下内容复制到系统内存的简明描述:

    一个质地

    一个子集 A的质地

    一个表面

    一个子集 a的表面

    一个D3DUSAGE_RENDERTARGET质地

    一个子集 A的D3DUSAGE_RENDERTARGET质感

这是Direct3D 9,但关于D3D的新版本的答案也将受到赞赏.



1> NeARAZ..:

最常见的部分是从视频内存中的某个表面读取("默认池").这通常是渲染目标.

让我们先得到简单的部分:

    从纹理读取与从该纹理的0级表面读取相同.见下文.

    对于纹理的子集也是如此.

    从非默认内存池("系统"或"托管")中的表面读取只是锁定它并读取字节.

    对于表面子集也是如此.只需锁定相关部分并阅读即可.

所以现在我们已经离开了视频内存中的表面("默认池").这将是标记为渲染目标的任何表面/纹理,或您在默认池中创建的任何常规表面/纹理,或后备缓冲区本身.这里复杂的部分是你无法锁定它.

简短的回答是:D3D设备上的GetRenderTargetData方法.

更长的答案(下面的代码的大致轮廓):

    rt =获取渲染目标表面(这可以是纹理的表面,或后退缓冲等)

    如果rt是多重采样(GetDesc,检查D3DSURFACE_DESC.MultiSampleType),则:a)创建另一个相同大小,相同格式但没有多重采样的渲染目标表面; b)StretchRect从rt进入这个新的表面; c)rt =这个新表面(即在这个新表面上进行).

    off =创建屏幕外平面(CreateOffscreenPlainSurface,D3DPOOL_SYSTEMMEM池)

    device-> GetRenderTargetData(rt,off)

    now off包含渲染目标数据.LockRect(),读取数据,UnlockRect()就可以了.

    清理

更长的答案(从我正在处理的代码库中粘贴)如下.这不会开箱即用,因为它使用了代码库其余部分的一些类,函数,宏和实用程序; 但它应该让你开始.我还省略了大多数错误检查(例如,给定的宽度/高度是否超出范围).我还省略了读取实际像素的部分,并可能将它们转换为合适的目标格式(这非常简单,但可能会变长,具体取决于您要支持的格式转换次数).

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_tVisual C++中的内容非常相似.

上面的代码读回整个表面.如果你想有效地阅读它的一部分,那么我相信最快的方式是:

    创建一个具有所需大小的默认池表面.

    StretchRect从原始表面的一部分到较小的表面.

    与较小的一个正常进行.

实际上,这与处理多采样表面的上述代码非常相似.如果你想获得多采样表面的一部分,你可以做一个多采样解析,并在一个StretchRect中得到它的一部分,我想.

编辑:删除了实际读取像素和格式转换的代码段.与问题没有直接关系,代码很长.

编辑:已更新以匹配已编辑的问题.

推荐阅读
小色米虫_524
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有