zoukankan      html  css  js  c++  java
  • DirectX读取纹理数据到CPU

    最近自己在研究一个问题:DX中给定一张Texture,当数据已经存在于GPU端后,应该如何做才能将纹理的数据读取到CPU中?
    要解决这个问题,首先应当知道DirectX中对于一个Texture的描述,这里我们以2DTexture为例,描述它的数据结构如下:

    typedef struct D3D11_TEXTURE2D_DESC
    {
        UINT Width;         			// 纹理宽度
        UINT Height;        			// 纹理高度
        UINT MipLevels;     			// 允许的Mip等级数
        UINT ArraySize;     			// 可以用于创建纹理数组,这里指定纹理的数目,单个纹理使用1
        DXGI_FORMAT Format; 			// DXGI支持的数据格式,默认DXGI_FORMAT_R8G8B8A8_UNORM
        DXGI_SAMPLE_DESC SampleDesc;    // MSAA描述
        D3D11_USAGE Usage;  			// 使用D3D11_USAGE枚举值指定数据的CPU/GPU访问权限
        UINT BindFlags;     			// 使用D3D11_BIND_FLAG枚举来决定该数据的使用类型
        UINT CPUAccessFlags;    		// 使用D3D11_CPU_ACCESS_FLAG枚举来决定CPU访问权限
        UINT MiscFlags;     			// 使用D3D11_RESOURCE_MISC_FLAG枚举
    }   D3D11_TEXTURE2D_DESC;
    
    typedef struct DXGI_SAMPLE_DESC
    {
        UINT Count;                     // MSAA采样数
        UINT Quality;                   // MSAA质量等级
    } DXGI_SAMPLE_DESC;
    

    其中Usage有四种类型:D3D11_USAGE_DEFAULT、D3D11_USAGE_IMMUTABLE、D3D11_USAGE_DYNAMIC、D3D11_USAGE_STAGING,它们的关系如下表所示:

    D3D11_USAGE CPU读 CPU写 GPU读 GPU写
    D3D11_USAGE_DEFAULT
    D3D11_USAGE_IMMUTABLE
    D3D11_USAGE_DYNAMIC
    D3D11_USAGE_STAGING

    DX中能够支持CPU读的只有D3D11_USAGE_STAGING,但是它是不能参与到渲染管线中的,因此BindFlags应当设为0。
    除此之外,如果我们希望能对Texture进行CPU读写操作,那么CPUAccessFlags也要做相应的设置(设置为D3D11_CPU_ACCESS_WRITE或D3D11_CPU_ACCESS_READ)。
    另外值得一提的是,这四种类型中性能最高的其实就是D3D11_USAGE_IMMUTABLE,但是这种类型的数据只能在一开始创建的时候设定,之后就不能更改了。

    了解了以上内容后我们就可以知道,对于对于不是D3D11_USAGE_STAGING类型的Texture,我们其实是没有直接的方法可以将它的数据读取到CPU中的。但是我们可以曲线救国:创建一个D3D11_USAGE_STAGING类型的Texture a,然后将我们要取数据的Texture b复制到这个Texture a中,最后再让CPU读取a的数据,就等于变相读取了b的数据。
    具体可以看看代码,我们首先创建了一个Texture b,把它设为一个Shader Resource和Render Target,并对它进行了一系列渲染为它赋值:

    D3D11_TEXTURE2D_DESC texDesc;
    texDesc.Width = w;
    texDesc.Height = h;
    texDesc.MipLevels = 1;
    texDesc.ArraySize = 1;
    texDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    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 = 0;
    device->CreateTexture2D(&texDesc, 0, &tex_b);		
    device->CreateShaderResourceView(tex_b, 0, &irradianceSRV);
    device->CreateRenderTargetView(tex_b, 0, &irradianceRTV);
    

    接着,我们要想读取b的数据到CPU,那么需要创建一个D3D11_USAGE_STAGING的Texture a,并且它的宽高、Format等信息需要和a一致:

    ID3D11Resource* res = nullptr;
    ID3D11Texture2D* tex_b = nullptr;
    
    //这个SRV注意就是tex_b的SRV
    irradianceSRV->GetResource(&res);
    
    res->QueryInterface(&tex_b);
    D3D11_TEXTURE2D_DESC desc;
    tex_b->GetDesc(&desc);
    
    ID3D11Texture2D* copy_tex;
    desc.Usage = D3D11_USAGE_STAGING;
    desc.BindFlags = 0;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    device->CreateTexture2D(&desc, 0, &copy_tex);
    

    然后要做的,就是进行资源复制,并调用context的Map函数:

    context->CopyResource(copy_tex, res);
    
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    context->Map(copy_tex, 0, D3D11_MAP_READ, 0, &mappedResource);
    

    通过以上代码,得到的mappedResource就是读取到CPU的数据。mappedResource.pData就是从数据的起始地址,mappedResource.RowPitch记录了这些数据中每一行的大小。
    需要注意的一点是,RowPitch是经过内存对齐后的大小,一般是要比图片一行数据的大小更大。
    因此,读取数据可以参考以下代码:

    int index;
    float*img_data = new float[desc.Width * desc.Height * 3];
    char* begin_data = reinterpret_cast<char*>(mappedResource.pData);
    for(int i=0;i< desc.Height;i++)
    	for (int j = 0; j < desc.Width; j++)
    	{
    		int y = desc.Height -1 - i;
    		index = y * desc.Width + j;
    		img_data[index * 3] = *reinterpret_cast<float*>(begin_data + i * mappedResource.RowPitch + j * 16);
    		img_data[index * 3 + 1] = *reinterpret_cast<float*>(begin_data + i * mappedResource.RowPitch + j * 16 + 4);
    		img_data[index * 3 + 2] = *reinterpret_cast<float*>(begin_data + i * mappedResource.RowPitch + j * 16 + 8);
    	}
    

    由于Map函数会封锁GPU对该数据的访问权限,最后记得要调用Unmap函数解除限制,并清除相应的临时对象:

    context->Unmap(copy_tex, 0);
    SAFE_DELETE(img_data);
    SAFE_RELEASE(res);
    SAFE_RELEASE(tex);
    SAFE_RELEASE(copy_tex);
    
  • 相关阅读:
    JS BOM对象 History对象 Location对象
    JS 字符串对象 数组对象 函数对象 函数作用域
    JS 引入方式 基本数据类型 运算符 控制语句 循环 异常
    Pycharm Html CSS JS 快捷方式创建元素
    CSS 内外边距 float positio属性
    CSS 颜色 字体 背景 文本 边框 列表 display属性
    【Android】RxJava的使用(三)转换——map、flatMap
    【Android】RxJava的使用(二)Action
    【Android】RxJava的使用(一)基本用法
    【Android】Retrofit 2.0 的使用
  • 原文地址:https://www.cnblogs.com/wickedpriest/p/13568190.html
Copyright © 2011-2022 走看看