如何在DirectX11(低于11.1版本)下创建带完整Mipmaps的Texture2D
解决Windows 7 + DirectX 11环境下生成Mipmap的问题
首先得指出你代码里的一个关键错误:D3D11_USAGE_DEFAULT类型的资源不能设置D3D11_CPU_ACCESS_WRITE,这是DirectX 11的核心规则——DEFAULT资源是专门给GPU访问的,CPU无法直接写入,这个错误在任何DX11环境下都应该触发失败,只是DX12的规则更宽松才没报错。这也是你当前收到"Invalid arguments"错误的主要原因之一。
回到你的核心需求:在Windows 7(仅支持DX11)环境下生成Mipmap,因为无法使用D3D11_RESOURCE_MISC_GENERATE_MIPS标志(该特性需要DX11.1及以上),这里提供两种可行的替代方案:
方案一:使用Staging纹理作为中间载体 + D3DX11自动生成Mipmap
这是最简单的方案,如果你项目中可以链接D3DX11库的话:
- 创建Staging纹理:用来让CPU写入原始视频数据(Staging类型允许CPU读写)
- 创建带完整Mip层级的目标纹理:同时绑定Shader Resource和Render Target(DX11支持这种组合,只是不支持自动生成Mip的Misc标志)
- 复制数据到目标纹理的最高层级
- 调用D3DX11FilterTexture生成所有Mip层级
代码示例:
// 计算完整的Mip层级数 UINT calculateMipLevels(UINT width, UINT height) { UINT levels = 1; while ((width > 1) || (height > 1)) { width = max(1U, width / 2); height = max(1U, height / 2); levels++; } return levels; } // 1. 创建Staging纹理 D3D11_TEXTURE2D_DESC stagingDesc = {}; stagingDesc.Width = nWidth; stagingDesc.Height = nHeight; stagingDesc.MipLevels = 1; stagingDesc.ArraySize = 1; stagingDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; stagingDesc.SampleDesc.Count = 1; stagingDesc.Usage = D3D11_USAGE_STAGING; stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; stagingDesc.BindFlags = 0; stagingDesc.MiscFlags = 0; ID3D11Texture2D* pStagingTex = nullptr; HRESULT hr = m_pD3dDevice->CreateTexture2D(&stagingDesc, nullptr, &pStagingTex); if (FAILED(hr)) { // 处理创建失败 return hr; } // 2. 写入视频数据到Staging纹理 D3D11_MAPPED_SUBRESOURCE mapped; hr = m_pD3dContext->Map(pStagingTex, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); if (SUCCEEDED(hr)) { memcpy(mapped.pData, pYourVideoBuffer, nWidth * nHeight * 4); // 假设数据是RGBA8格式 m_pD3dContext->Unmap(pStagingTex, 0); } // 3. 创建带完整Mip层级的目标纹理 UINT mipLevels = calculateMipLevels(nWidth, nHeight); D3D11_TEXTURE2D_DESC targetDesc = {}; targetDesc.Width = nWidth; targetDesc.Height = nHeight; targetDesc.MipLevels = mipLevels; targetDesc.ArraySize = 1; targetDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; targetDesc.SampleDesc.Count = 1; targetDesc.Usage = D3D11_USAGE_DEFAULT; targetDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; targetDesc.CPUAccessFlags = 0; targetDesc.MiscFlags = 0; ID3D11Texture2D* pTargetTex = nullptr; hr = m_pD3dDevice->CreateTexture2D(&targetDesc, nullptr, &pTargetTex); if (FAILED(hr)) { pStagingTex->Release(); return hr; } // 4. 复制Staging纹理数据到目标纹理的Mip Level 0 m_pD3dContext->CopySubresourceRegion(pTargetTex, 0, 0, 0, 0, pStagingTex, 0, nullptr); pStagingTex->Release(); // 不再需要Staging纹理 // 5. 生成Mipmap(需要链接D3DX11.lib) D3DX11FilterTexture(m_pD3dContext, pTargetTex, 0, D3DX11_DEFAULT); // 后续可以创建Shader Resource View供Shader使用 D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; srvDesc.Format = targetDesc.Format; srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MipLevels = mipLevels; srvDesc.Texture2D.MostDetailedMip = 0; ID3D11ShaderResourceView* pTexSRV = nullptr; hr = m_pD3dDevice->CreateShaderResourceView(pTargetTex, &srvDesc, &pTexSRV);
方案二:手动渲染生成Mip层级(不依赖D3DX库)
如果你的项目不能使用D3DX库,可以手动通过渲染管线生成每个Mip层级:
- 按照方案一的步骤创建Staging纹理和目标纹理
- 复制数据到目标纹理的Mip Level 0
- 创建一个简单的全屏Pixel Shader,用来将上一层级的纹理缩小采样
- 为每个Mip层级创建对应的Render Target View(RTV),设置匹配的视口大小,绑定上一层级的Shader Resource View(SRV),渲染全屏三角形/四边形到当前RTV,完成缩小采样
核心代码片段(生成单个Mip层级):
// 假设已经创建了目标纹理pTargetTex和设备上下文m_pD3dContext // 遍历每个Mip层级,从1开始到最高层级 for (UINT level = 1; level < mipLevels; level++) { UINT prevWidth = max(1U, nWidth / (UINT)pow(2, level-1)); UINT prevHeight = max(1U, nHeight / (UINT)pow(2, level-1)); UINT currWidth = max(1U, prevWidth / 2); UINT currHeight = max(1U, prevHeight / 2); // 创建当前层级的RTV D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {}; rtvDesc.Format = targetDesc.Format; rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; rtvDesc.Texture2D.MipSlice = level; ID3D11RenderTargetView* pCurrRTV = nullptr; m_pD3dDevice->CreateRenderTargetView(pTargetTex, &rtvDesc, &pCurrRTV); // 创建上一层级的SRV D3D11_SHADER_RESOURCE_VIEW_DESC prevSrvDesc = {}; prevSrvDesc.Format = targetDesc.Format; prevSrvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; prevSrvDesc.Texture2D.MipLevels = 1; prevSrvDesc.Texture2D.MostDetailedMip = level-1; ID3D11ShaderResourceView* pPrevSRV = nullptr; m_pD3dDevice->CreateShaderResourceView(pTargetTex, &prevSrvDesc, &pPrevSRV); // 设置视口 D3D11_VIEWPORT vp = {}; vp.Width = (FLOAT)currWidth; vp.Height = (FLOAT)currHeight; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; m_pD3dContext->RSSetViewports(1, &vp); // 绑定RTV和SRV m_pD3dContext->OMSetRenderTargets(1, &pCurrRTV, nullptr); m_pD3dContext->PSSetShaderResources(0, 1, &pPrevSRV); // 渲染全屏三角形(这里假设你已经有对应的Vertex Shader和Input Layout) m_pD3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_pD3dContext->Draw(3, 0); // 释放资源 pCurrRTV->Release(); pPrevSRV->Release(); }
这个方案需要你提前准备好全屏渲染的基础管线(Vertex Shader、Input Layout等),但好处是完全不依赖第三方库,兼容性更好。
内容的提问来源于stack exchange,提问作者RustOnce
相关产品推荐
相关产品推荐

