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);
    
  • 相关阅读:
    不相交集实现实例
    TQ2440开发板挂载U盘出现乱码
    快速选择实例
    linux2.6.30.4内核移植(7)——插入hello world驱动模块
    linux2.6.30.4内核移植(6)——移植应用程序hello world常见的错误:-bin/sh ./hello not found
    linux2.6.30.4内核移植(5)——构建根文件系统(yaffs文件系统格式的镜像)
    linux2.6.30.4内核移植(4)——完善串口驱动
    linux2.6.30.4内核移植(3)——yaffs文件系统移植
    linux2.6.30.4内核移植(2)——Nand Flash驱动移植
    快速排序实例
  • 原文地址:https://www.cnblogs.com/wickedpriest/p/13568190.html
Copyright © 2011-2022 走看看