D3D11 texture 创建 NV12 cpu 访问 内存分布 拷贝 map updateresource copyresource

nv12 详细介绍:https://docs.microsoft.com/zh-cn/windows/desktop/api/dxgiformat/ne-dxgiformat-dxgi_format(关于查看msdn文档,对于所有的windows的类型,在vs下输入标移动到对应的类型或其属性或枚举下,按f1浏览器就能调到对应的官方文档对应的网页,还是相当方便的)
nv12 显示或者作为shader resource传入shader参考:https://mp.csdn.net/postedit/88841445
texture 创建:

//HRESULT hr ;
//UINT pSup;
//hr = g_pd3dDevice->CheckFormatSupport(DXGI_FORMAT_NV12, &pSup); //D3D11 on GeGorce GTX TITAN X device ,don't surpport DXGI_FORMAT_NV12 !!!!
//if (hr != S_OK)
//{
//	MessageBox(NULL, L"Dont support that format", L"Error", MB_OK);
//}
//显存对内存对齐要求比较严苛,这对使用效率及内部显存管理比较重要。 使用图像分辨率2226*1252。
D3D11_TEXTURE2D_DESC desc;
desc.Width = static_cast<UINT>(width);//我设置的是2226(0x8b2)  ,在我机器上内存对齐需要将值改成2240(0x8c0)
desc.Height = static_cast<UINT>(height);//我设置的是1252(0x4e4),在我机器上内存对齐需要将值改成1264(0x4f0)
desc.MipLevels = static_cast<UINT>(1);
desc.ArraySize = static_cast<UINT>(1);
desc.Format = DXGI_FORMAT_NV12;// DXGI_FORMAT_R8G8B8A8_UINT;//DXGI_FORMAT_NV11;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;//表示绑定到pipeline的阶段中,如果只是输出可以使用D3D11_BIND_RENDER_TARGET
desc.CPUAccessFlags = 0;
//usage 
//当Usage 为D3D11_USAGE_IMMUTABLE和D3D11_USAGE_DEFAULT时,不可以设置D3D11_CPU_ACCESS_WRITE或D3D11_CPU_ACCESS_READ,并且必须设置bindflags。这两者是在gpu上申请的内存。(使用gpu device创建的情况时)
//当usage为D3D11_USAGE_IMMUTABLE(不可修改)时,bindflags不能设置为D3D11_BIND_RENDER_TARGET。cpuaccessflags 必须设置为0
//当usage为D3D11_USAGE_DEFUALT时,bindflag可以D3D11_BIND_RENDER_TARGET与D3D11_BIND_SHADER_RESOURCE的组合。
//当Usage为D3D11_USAGE_DYNAMIC时,bindflags必须设置一项(bingflags似乎永远只能设置一项),并
//且必须设置cpuaccessflags为D3D11_CPU_ACCESS_WRITE,经过测试,发现dynamic其实是在gpu上申请了
//内存。(使用gpu device创建的情况时)
//当Usage为D3D11_USAGE_STAGING 时,bindflags必须为0,并且必须设置cpuaccessflags为read或者
//write或者两者的结合,不能为0,经过测试,staging是在普通内存上申请了一块内存。
//对于D3D11_USAGE_STAGIN的资源在pipeline的整个流程所有阶段gpu是无法访问的,显然pipeline阶段显卡访问普通内存是低效的,需要将东西拷贝到显卡内存才行。
//D3D11_CPU_ACCESS_READ 必须用在staging资源上。
//调用接口错误信息可以查看:https://blog.csdn.net/qiushangren/article/details/89280682
//参考:https://blog.csdn.net/sinat_24229853/article/details/48814023
desc.MiscFlags = 0;

D3D11_SUBRESOURCE_DATA sdesc;//texture 初始化参数
sdesc.pSysMem = buffer;//设置用于初始化的内容入口地址,内部会自动拷贝
sdesc.SysMemPitch = width;//一行占用的像素,如果硬盘中存的内容的slice是2304,这里填2304,在创建是会自动截掉多余width的部分。对于这个slice,该值在存入文件中时如果是通过D3Dtexture map函数来存放,可以通过其中的参数来获知。
sdesc.SysMemSlicePitch = width*height+width*height/2;//总共占用的像素。
ID3D11Texture2D* texTemp = nullptr;

//HRESULT hr = device->CreateTexture2D(&desc, NULL, &texTemp);
HRESULT hr = device->CreateTexture2D(&desc,&sdesc,&texTemp);//创建texture

//texTemp->getDesc(&desc);//获取创建时的参数,与初始化时D3D11_SUBRESOURCE_DATA 中的pitch和内部的内存对齐无关。

texture初始化参数为何要这么设计: 主要是考虑内存对齐,考虑如下的情况,红色区域是有效像素,而白色加红色的大区域是内存所占空间。而图片在内存中一般就是一行一行从左到右存储的。有的为了高效而设置了内存对齐往外补了几个像素。windows中大部分操作像素的接口都是这样式的。

关于显存与内存之间的数据传输及内存管理可参考:https://www.zhihu.com/question/35068373
另外有一种高效的叫DMA的技术,不知道内部是否有应用。
 


如果希望cpu中转,可通过ID3D11DeviceContext->map来访问,需要对texture创建参数做如下两步:
1、设置desc的Usage 为D3D11_USAGE_DYNAMIC 或者D3D11_USAGE_STAGING,即大于等于D3D11_USAGE_DYNAMIC表示为cpu可访问的texture。
2、设置CPUAccessFlags 的属性D3D11_CPU_ACCESS_READ,D3D11_CPU_ACCESS_WRITE。设置cpu访问的权限。


texture 的拷贝,在D3d11中可以通过ID3D11DeviceContext->updateresource,或ID3D11DeviceContext->copyresource等等来操作。这几个函数对source texture和dest texture的属性没有限制!!! 当然dest 肯定不能为immutable。具体使用参考官方文档。及调试时出现的错误信息(信息很明确)。

//texture 拷贝。tex 是按tex1的属性来创建的并设置了其cpu可访问性和可修改性属性,只能在创建的时候设置texture的属性。
//copy 方式经过测试两幅4096*4096*4的图像src与dest均为dynamic或default类型时拷贝时间为10的-5级别s(应该是有并行操作在这里,
//这时间比内存到内存的memcpy都快)。说明优化很棒。其中有一方为staging类型时,为0.0001s(在我电脑上0到100000的累加平均耗时在0.000269
//,最小为0.000249). dest 不能为immutable
dctx->CopyResource(tex1, tex);

//dctx->CopySubresourceRegion(tex1, 0, 0, 0, 0, tex, 0, NULL);

//dctx->UpdateSubresource(tex1,0,NULL,tex,0,0);

//通过tex->GetDesc(&desc); 检测到tex(源)的分辨率为2240*1264
//texture map
D3D11_MAPPED_SUBRESOURCE mappedResource;//用于存放map返回的参数。
mappedResource.DepthPitch = 0;
mappedResource.RowPitch = 0;
mappedResource.pData = 0;
//注意参数访问性的设置及对应的要求https://docs.microsoft.com/zh-cn/windows/desktop/api/d3d11/ne-d3d11-d3d11_map
if ((ret = dctx->Map(tex1, 0, D3D11_MAP_READ, 0, &mappedResource)) == 0)//_DISCARD
{//mapppedResource的rowpitch为2304(0x900,我显卡位宽为256,实际位对齐),depthpitch为4368384(2304*1264+2304/2*1264)------(y+uv)
//可以断定,获取出来的pitch与创建时用的参数是不一致的,这里考虑到了内存对齐。也就是类似将实际申请的内存的地址拿出来了。
    bool needSave = false;
    if(needSave){
	    FILE *file= fopen("./ttt");
        fwrite((char*)mappedResource.pData,1,mappedResource.DepthPitch,file);
        fclose(file);
    }
    dctx->Unmap(tex1, 0);
}

猜你喜欢

转载自blog.csdn.net/qiushangren/article/details/89244745
今日推荐