D3D11绘制NV12格式内存数据图像到窗口

最近我们播放器(Kommander)有需要绘制NV12格式的内存数据图像到窗口上,为了搞清楚NV12的内存数据如何送入纹理对象,我专门写了个demo来实现这一块的功能,一是为了彻底搞清楚这一块原理,二也是为了方便其他人的学习。

demo代码如下,分别用3种方式实现了NV12格式数据送入纹理:

/**
* @details 此demo主要是验证NV12格式的内存数据图像如何通过d3d11渲染到窗口
* @author knight
* @email xiaoge777888@126.com
* @version 1.0.0
* @date 2023-07-08
*/#include 
#include 
#include 
#include 
#include #pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dcompiler.lib")using namespace DirectX;struct VertexPosTex
{XMFLOAT3 pos;XMFLOAT2 tex;static const D3D11_INPUT_ELEMENT_DESC inputLayout[2];
};const D3D11_INPUT_ELEMENT_DESC VertexPosTex::inputLayout[2] = {{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};// 以下代码省略了错误检查以简洁清晰,实际项目中你需要添加适当的错误处理LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{if (message == WM_CLOSE || message == WM_DESTROY){PostQuitMessage(0);return 0;}return DefWindowProc(hwnd, message, wParam, lParam);
}int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{WNDCLASSEX windowClass = { 0 };windowClass.cbSize = sizeof(WNDCLASSEX);windowClass.style = CS_HREDRAW | CS_VREDRAW;windowClass.lpfnWndProc = WndProc;windowClass.hInstance = hInstance;windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);windowClass.lpszClassName = "D3D11Class";RegisterClassEx(&windowClass);RECT windowRect = { 0, 0, 800, 600 };AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);HWND hwnd = CreateWindow("D3D11Class","D3D11 Window",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,windowRect.right - windowRect.left,windowRect.bottom - windowRect.top,NULL,NULL,hInstance,NULL);ShowWindow(hwnd, nCmdShow);UpdateWindow(hwnd);// 创建D3D11设备和上下文ID3D11Device* device;ID3D11DeviceContext* context;D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, NULL, 0, D3D11_SDK_VERSION, &device, NULL, &context);// 获取IDXGIDevice接口IDXGIDevice* dxgiDevice;device->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice);// 获取IDXGIAdapter接口IDXGIAdapter* dxgiAdapter;dxgiDevice->GetAdapter(&dxgiAdapter);// 获取IDXGIFactory接口IDXGIFactory* dxgiFactory;dxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void**)&dxgiFactory);// 创建交换链DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 };swapChainDesc.BufferCount = 2;swapChainDesc.BufferDesc.Width = 800;swapChainDesc.BufferDesc.Height = 600;swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;swapChainDesc.OutputWindow = hwnd;swapChainDesc.SampleDesc.Count = 1;swapChainDesc.Windowed = TRUE;IDXGISwapChain* swapChain;dxgiFactory->CreateSwapChain(device ,&swapChainDesc, &swapChain);// 创建顶点着色器const char* vertexShaderCode ="struct VertexPosIn""{""    float3 PosL : POSITION;""    float2 Tex : TEXCOORD;""};""struct VertexPosOut""{""	float4 PosH : SV_POSITION;""	float2 Tex : TEXCOORD;""};""VertexPosOut main(VertexPosIn vIn)""{""	VertexPosOut vOut;""	vOut.PosH=float4(vIn.PosL,1.0f);""   vOut.Tex=vIn.Tex;""	return vOut;""}";ID3DBlob* vsBlob = NULL;HRESULT hr=D3DCompile(vertexShaderCode, strlen(vertexShaderCode), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vsBlob, NULL);ID3D11VertexShader* vertexShader;device->CreateVertexShader(vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), NULL, &vertexShader);ID3D11InputLayout* inputLayout;device->CreateInputLayout(VertexPosTex::inputLayout, ARRAYSIZE(VertexPosTex::inputLayout),vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), &inputLayout);// 创建像素着色器const char* pixelShaderCode ="Texture2D yTexture : register(t0);""Texture2D uvTexture : register(t1);""SamplerState samplerState : register(s0);""float4 main(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target""{""    float y = yTexture.Sample(samplerState, uv).r;"//"	   return float4(y,y,y,1.0f);""    float u = uvTexture.Sample(samplerState, uv).r-0.5f;""    float v = uvTexture.Sample(samplerState, uv).g-0.5f;"//"	 float u=0.0f;"//"	 float v=0.0f;""    float3 yuv = float3(y, u, v);"//"    float3 rgb = mul(yuv,float3x3(1.0, 1.0, 1.0,0.0,-0.39465,2.03211,1.13983,-0.58060,0.0));"//如果矩阵放在mul第二个参数位置,则是取每一列相乘"    float3 rgb = mul(float3x3(1.0, 0.0, 1.13983, 1.0, -0.39465, -0.58060, 1.0, 2.03211, 0.0),yuv);"//如果矩阵放在mul第一个参数位置,则是取每一行相乘"    return float4(rgb, 1.0);""}";ID3DBlob* psBlob = NULL;D3DCompile(pixelShaderCode, strlen(pixelShaderCode), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &psBlob, NULL);ID3D11PixelShader* pixelShader = NULL;device->CreatePixelShader(psBlob->GetBufferPointer(), psBlob->GetBufferSize(), NULL, &pixelShader);//创建采样状态D3D11_SAMPLER_DESC sampDesc;ZeroMemory(&sampDesc, sizeof(sampDesc));sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;sampDesc.MinLOD = 0;sampDesc.MaxLOD = D3D11_FLOAT32_MAX;ID3D11SamplerState* samplerState=NULL;device->CreateSamplerState(&sampDesc, &samplerState);// 创建四边形VertexPosTex quadVertices[] ={{ DirectX::XMFLOAT3(-1.0f, 1.0f, 0.0f), DirectX::XMFLOAT2(0.0f, 0.0f) },{ DirectX::XMFLOAT3(1.0f, 1.0f, 0.0f), DirectX::XMFLOAT2(1.0f, 0.0f) },{ DirectX::XMFLOAT3(-1.0f, -1.0f, 0.0f), DirectX::XMFLOAT2(0.0f, 1.0f) },{ DirectX::XMFLOAT3(1.0f, -1.0f, 0.0f), DirectX::XMFLOAT2(1.0f, 1.0f) }};D3D11_BUFFER_DESC vertexBufferDesc = {};vertexBufferDesc.ByteWidth = sizeof(quadVertices);vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;D3D11_SUBRESOURCE_DATA vertexData = {};vertexData.pSysMem = quadVertices;ID3D11Buffer* vertexBuffer= NULL;device->CreateBuffer(&vertexBufferDesc, &vertexData, &vertexBuffer);//初始化NV12纹理内存数据int texWidth = 800;int texHeight = 600;const int dataSize = texWidth * (texHeight + texHeight / 2);const uint8_t* pSysData = new uint8_t[dataSize];memset((void*)pSysData, 150, texWidth* texHeight);memset((void*)(pSysData+texWidth*texHeight), 128, texWidth* texHeight/2);//创建NV12纹理ID3D11Texture2D* textureNV12 = NULL;
#if 0	//创建GPU读纹理,在创建的时候上传数据D3D11_TEXTURE2D_DESC textureDesc = {};textureDesc.Width = texWidth;textureDesc.Height = texHeight;textureDesc.MipLevels = 1;textureDesc.ArraySize = 1;textureDesc.Format = DXGI_FORMAT_NV12; // YUV数据通常是8位单通道textureDesc.SampleDesc.Count = 1;textureDesc.Usage = D3D11_USAGE_IMMUTABLE;textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;//把NV12纹理内存数据传入纹理D3D11_SUBRESOURCE_DATA textureSubresourceData = {};textureSubresourceData.pSysMem = pSysData;textureSubresourceData.SysMemPitch = texWidth; // 1字节每像素device->CreateTexture2D(&textureDesc, &textureSubresourceData, &textureNV12);
#elif 0 //创建GPU读写纹理,用UpdateResource上传数据D3D11_TEXTURE2D_DESC textureDesc = {};textureDesc.Width = texWidth;textureDesc.Height = texHeight;textureDesc.MipLevels = 1;textureDesc.ArraySize = 1;textureDesc.Format = DXGI_FORMAT_NV12; // YUV数据通常是8位单通道textureDesc.SampleDesc.Count = 1;textureDesc.Usage = D3D11_USAGE_DEFAULT;textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;device->CreateTexture2D(&textureDesc, nullptr, &textureNV12);//把NV12纹理内存数据传入纹理context->UpdateSubresource(textureNV12, 0, NULL, pSysData, texWidth, 0);
#else //创建CPU写,GPU读纹理,用Map/Unmap上传数据D3D11_TEXTURE2D_DESC textureDesc = {};textureDesc.Width = texWidth;textureDesc.Height = texHeight;textureDesc.MipLevels = 1;textureDesc.ArraySize = 1;textureDesc.Format = DXGI_FORMAT_NV12; // YUV数据通常是8位单通道textureDesc.SampleDesc.Count = 1;textureDesc.Usage = D3D11_USAGE_DYNAMIC;textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;device->CreateTexture2D(&textureDesc, nullptr, &textureNV12);D3D11_MAPPED_SUBRESOURCE mappedData;if (SUCCEEDED(context->Map(textureNV12, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData))){byte* pDest = (BYTE*)mappedData.pData;int stride = mappedData.RowPitch;int dataHeight = texHeight + texHeight / 2;for (int i = 0; i < dataHeight; i++){memcpy(pDest + i * stride, pSysData + i * texWidth, texWidth);}context->Unmap(textureNV12,0);}
#endifD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};srvDesc.Format = DXGI_FORMAT_R8_UNORM;srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;srvDesc.Texture2D.MipLevels = textureDesc.MipLevels;ID3D11ShaderResourceView* textureViewY = NULL;device->CreateShaderResourceView(textureNV12, &srvDesc, &textureViewY);ID3D11ShaderResourceView* textureViewUV = NULL;srvDesc.Format = DXGI_FORMAT_R8G8_UNORM;device->CreateShaderResourceView(textureNV12, &srvDesc, &textureViewUV);// 创建渲染目标视图ID3D11Texture2D* backBuffer;swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);ID3D11RenderTargetView* renderTargetView;device->CreateRenderTargetView(backBuffer, NULL, &renderTargetView);//设置渲染状态context->OMSetRenderTargets(1, &renderTargetView, NULL);// 设置视口D3D11_VIEWPORT viewport = { 0, 0, 800, 600, 0, 1 };context->RSSetViewports(1, &viewport);context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);//设置顶点输入格式context->IASetInputLayout(inputLayout);UINT stride = sizeof(VertexPosTex);UINT offset = 0;context->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);// 设置顶点着色器context->VSSetShader(vertexShader, NULL, 0);// 设置着色器和纹理context->PSSetShader(pixelShader, NULL, 0);//设置采样状态context->PSSetSamplers(0, 1, &samplerState);context->PSSetShaderResources(0, 1, &textureViewY);context->PSSetShaderResources(1, 1, &textureViewUV);MSG msg = { 0 };while (msg.message != WM_QUIT){if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);}else{float clearColor[4] = { 100.0f / 255.0f, 149.0f / 255.0f, 237.0f / 255.0f, 1.0f }; // 替换CornflowerBlue颜色// 渲染context->ClearRenderTargetView(renderTargetView, clearColor);//... 在这里添加你的绘制调用context->Draw(4, 0);swapChain->Present(0, 0);}}// 释放所有Direct3D资源textureViewUV->Release();textureViewY->Release();textureNV12->Release();samplerState->Release();pixelShader->Release();vertexShader->Release();vertexBuffer->Release();inputLayout->Release();renderTargetView->Release();backBuffer->Release();swapChain->Release();context->Release();device->Release();return 0;
}

上面demo中,我自己创建了一块NV12内存数据,为了简单起见,我把NV12内存数据图像设置成一个灰色了(你们也可以根据自己需要改为其他颜色),所以你们执行程序最终会在窗口上看到一个灰色图像。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部