I have a Windows.Graphics.DirectX.Direct3D11.IDirect3DSurface - how do I get a pointer to the graphics data to operate on? Write a C++/winrt sample if you can.
OK, next up, split the locked bytes into a std::vector<> of NV12 plane definitions.
| #include <windows.h> | |
| #include <winrt/Windows.Graphics.DirectX.Direct3D11.h> | |
| #include <Windows.Graphics.DirectX.Direct3D11.interop.h> | |
| #include <d3d11.h> | |
| #include <wil/com.h> | |
| #include <wil/result.h> | |
| using namespace winrt; | |
| using namespace Windows::Graphics::DirectX::Direct3D11; | |
| // Function to get CPU-accessible pointer from IDirect3DSurface | |
| HRESULT GetSurfaceDataPointer( | |
| const IDirect3DSurface& surface, | |
| _Outptr_ void** ppData, | |
| _Out_ UINT* pRowPitch, | |
| _Out_ UINT* pDepthPitch) | |
| { | |
| *ppData = nullptr; | |
| *pRowPitch = 0; | |
| *pDepthPitch = 0; | |
| // Get the native ID3D11Texture2D from the WinRT surface | |
| wil::com_ptr<::Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess> dxgiInterfaceAccess; | |
| RETURN_IF_FAILED(surface.as(dxgiInterfaceAccess)); | |
| wil::com_ptr<ID3D11Texture2D> d3dTexture; | |
| RETURN_IF_FAILED(dxgiInterfaceAccess->GetInterface(IID_PPV_ARGS(&d3dTexture))); | |
| // Get the D3D11 device context | |
| wil::com_ptr<ID3D11Device> d3dDevice; | |
| d3dTexture->GetDevice(&d3dDevice); | |
| wil::com_ptr<ID3D11DeviceContext> d3dContext; | |
| d3dDevice->GetImmediateContext(&d3dContext); | |
| // Get texture description to check if it's CPU accessible | |
| D3D11_TEXTURE2D_DESC desc; | |
| d3dTexture->GetDesc(&desc); | |
| wil::com_ptr<ID3D11Texture2D> stagingTexture; | |
| // If the texture is not CPU accessible, create a staging texture | |
| if ((desc.Usage != D3D11_USAGE_STAGING) || | |
| !(desc.CPUAccessFlags & (D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE))) | |
| { | |
| // Create staging texture with CPU read access | |
| D3D11_TEXTURE2D_DESC stagingDesc = desc; | |
| stagingDesc.Usage = D3D11_USAGE_STAGING; | |
| stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; | |
| stagingDesc.BindFlags = 0; | |
| stagingDesc.MiscFlags = 0; | |
| RETURN_IF_FAILED(d3dDevice->CreateTexture2D(&stagingDesc, nullptr, &stagingTexture)); | |
| // Copy the original texture to staging texture | |
| d3dContext->CopyResource(stagingTexture.get(), d3dTexture.get()); | |
| } | |
| else | |
| { | |
| stagingTexture = d3dTexture; | |
| } | |
| // Map the texture to get CPU access | |
| D3D11_MAPPED_SUBRESOURCE mappedResource; | |
| RETURN_IF_FAILED(d3dContext->Map( | |
| stagingTexture.get(), | |
| 0, // subresource | |
| D3D11_MAP_READ_WRITE, | |
| 0, // map flags | |
| &mappedResource)); | |
| *ppData = mappedResource.pData; | |
| *pRowPitch = mappedResource.RowPitch; | |
| *pDepthPitch = mappedResource.DepthPitch; | |
| // Note: You must call Unmap when done with the data | |
| // d3dContext->Unmap(stagingTexture.get(), 0); | |
| return S_OK; | |
| } | |
| // RAII wrapper for automatic unmapping | |
| class SurfaceMapper | |
| { | |
| public: | |
| SurfaceMapper() = default; | |
| ~SurfaceMapper() | |
| { | |
| if (m_context && m_texture) | |
| { | |
| m_context->Unmap(m_texture.get(), 0); | |
| } | |
| } | |
| HRESULT MapSurface( | |
| const IDirect3DSurface& surface, | |
| _Outptr_ void** ppData, | |
| _Out_ UINT* pRowPitch, | |
| _Out_ UINT* pDepthPitch) | |
| { | |
| *ppData = nullptr; | |
| *pRowPitch = 0; | |
| *pDepthPitch = 0; | |
| // Get the native ID3D11Texture2D from the WinRT surface | |
| wil::com_ptr<::Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess> dxgiInterfaceAccess; | |
| RETURN_IF_FAILED(surface.as(dxgiInterfaceAccess)); | |
| wil::com_ptr<ID3D11Texture2D> d3dTexture; | |
| RETURN_IF_FAILED(dxgiInterfaceAccess->GetInterface(IID_PPV_ARGS(&d3dTexture))); | |
| // Get the D3D11 device context | |
| wil::com_ptr<ID3D11Device> d3dDevice; | |
| d3dTexture->GetDevice(&d3dDevice); | |
| d3dDevice->GetImmediateContext(&m_context); | |
| // Get texture description | |
| D3D11_TEXTURE2D_DESC desc; | |
| d3dTexture->GetDesc(&desc); | |
| // Check if we need a staging texture | |
| if ((desc.Usage != D3D11_USAGE_STAGING) || | |
| !(desc.CPUAccessFlags & (D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE))) | |
| { | |
| // Create staging texture | |
| D3D11_TEXTURE2D_DESC stagingDesc = desc; | |
| stagingDesc.Usage = D3D11_USAGE_STAGING; | |
| stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; | |
| stagingDesc.BindFlags = 0; | |
| stagingDesc.MiscFlags = 0; | |
| RETURN_IF_FAILED(d3dDevice->CreateTexture2D(&stagingDesc, nullptr, &m_texture)); | |
| m_context->CopyResource(m_texture.get(), d3dTexture.get()); | |
| } | |
| else | |
| { | |
| m_texture = d3dTexture; | |
| } | |
| // Map the texture | |
| D3D11_MAPPED_SUBRESOURCE mappedResource; | |
| RETURN_IF_FAILED(m_context->Map( | |
| m_texture.get(), | |
| 0, | |
| D3D11_MAP_READ_WRITE, | |
| 0, | |
| &mappedResource)); | |
| *ppData = mappedResource.pData; | |
| *pRowPitch = mappedResource.RowPitch; | |
| *pDepthPitch = mappedResource.DepthPitch; | |
| return S_OK; | |
| } | |
| private: | |
| wil::com_ptr<ID3D11DeviceContext> m_context; | |
| wil::com_ptr<ID3D11Texture2D> m_texture; | |
| }; | |
| // Example usage function | |
| void ProcessSurfaceData(const IDirect3DSurface& surface) | |
| { | |
| try | |
| { | |
| SurfaceMapper mapper; | |
| void* pData = nullptr; | |
| UINT rowPitch = 0; | |
| UINT depthPitch = 0; | |
| THROW_IF_FAILED(mapper.MapSurface(surface, &pData, &rowPitch, &depthPitch)); | |
| // Now you can access the pixel data | |
| // The layout depends on the surface format (BGRA8, RGB32F, etc.) | |
| auto pixelData = static_cast<uint8_t*>(pData); | |
| // Example: Process pixels row by row | |
| // Note: You need to know the surface dimensions and format | |
| for (UINT row = 0; row < /* height */; ++row) | |
| { | |
| auto rowData = pixelData + (row * rowPitch); | |
| // Process pixels in this row | |
| // Format-specific processing goes here | |
| } | |
| // The mapper automatically unmaps when it goes out of scope | |
| } | |
| catch (...) | |
| { | |
| // Handle errors | |
| LOG_CAUGHT_EXCEPTION(); | |
| } | |
| } | |
| // Alternative approach using gsl::span for safer buffer access | |
| #include <gsl/span> | |
| #include <vector> | |
| template<typename PixelType> | |
| gsl::span<PixelType> GetSurfacePixels( | |
| const IDirect3DSurface& surface, | |
| SurfaceMapper& mapper, | |
| UINT width, | |
| UINT height) | |
| { | |
| void* pData = nullptr; | |
| UINT rowPitch = 0; | |
| UINT depthPitch = 0; | |
| THROW_IF_FAILED(mapper.MapSurface(surface, &pData, &rowPitch, &depthPitch)); | |
| // Calculate total pixel count | |
| // Note: This assumes tightly packed pixels, which may not always be true due to rowPitch | |
| size_t pixelCount = static_cast<size_t>(width) * height; | |
| return gsl::make_span(static_cast<PixelType*>(pData), pixelCount); | |
| } | |
| // NV12 plane definition | |
| struct NV12Plane | |
| { | |
| gsl::span<uint8_t> data; | |
| UINT width; | |
| UINT height; | |
| UINT rowPitch; | |
| UINT pixelStride; // 1 for Y plane, 2 for UV plane | |
| }; | |
| // NV12 format structure | |
| struct NV12Surface | |
| { | |
| NV12Plane yPlane; // Luminance plane (Y) | |
| NV12Plane uvPlane; // Chrominance plane (interleaved U and V) | |
| UINT surfaceWidth; | |
| UINT surfaceHeight; | |
| }; | |
| // Split NV12 surface data into plane definitions | |
| NV12Surface SplitNV12Surface( | |
| gsl::span<uint8_t> surfaceData, | |
| UINT width, | |
| UINT height, | |
| UINT rowPitch) | |
| { | |
| // NV12 format layout: | |
| // - Y plane: full resolution (width x height), 1 byte per pixel | |
| // - UV plane: half resolution (width/2 x height/2), 2 bytes per pixel (interleaved U,V) | |
| THROW_HR_IF(E_INVALIDARG, width == 0 || height == 0); | |
| THROW_HR_IF(E_INVALIDARG, width % 2 != 0 || height % 2 != 0); // NV12 requires even dimensions | |
| NV12Surface result{}; | |
| result.surfaceWidth = width; | |
| result.surfaceHeight = height; | |
| // Y plane (luminance) - starts at beginning of buffer | |
| UINT yPlaneSize = rowPitch * height; | |
| result.yPlane.data = surfaceData.subspan(0, yPlaneSize); | |
| result.yPlane.width = width; | |
| result.yPlane.height = height; | |
| result.yPlane.rowPitch = rowPitch; | |
| result.yPlane.pixelStride = 1; | |
| // UV plane (chrominance) - starts after Y plane | |
| // UV plane has half the height and width, but 2 bytes per "pixel" (U and V interleaved) | |
| UINT uvPlaneHeight = height / 2; | |
| UINT uvPlaneWidth = width / 2; | |
| UINT uvRowPitch = rowPitch; // Usually same as Y plane, but contains interleaved U,V | |
| UINT uvPlaneSize = uvRowPitch * uvPlaneHeight; | |
| THROW_HR_IF(E_INVALIDARG, yPlaneSize + uvPlaneSize > surfaceData.size()); | |
| result.uvPlane.data = surfaceData.subspan(yPlaneSize, uvPlaneSize); | |
| result.uvPlane.width = uvPlaneWidth; | |
| result.uvPlane.height = uvPlaneHeight; | |
| result.uvPlane.rowPitch = uvRowPitch; | |
| result.uvPlane.pixelStride = 2; // U and V are interleaved | |
| return result; | |
| } | |
| // Convenience function to get NV12 planes from a Direct3D surface | |
| NV12Surface GetNV12SurfaceFromDirect3D(const IDirect3DSurface& surface) | |
| { | |
| // First, get the surface description to validate it's NV12 | |
| wil::com_ptr<::Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess> dxgiInterfaceAccess; | |
| THROW_IF_FAILED(surface.as(dxgiInterfaceAccess)); | |
| wil::com_ptr<ID3D11Texture2D> d3dTexture; | |
| THROW_IF_FAILED(dxgiInterfaceAccess->GetInterface(IID_PPV_ARGS(&d3dTexture))); | |
| D3D11_TEXTURE2D_DESC desc; | |
| d3dTexture->GetDesc(&desc); | |
| // Validate this is an NV12 surface | |
| THROW_HR_IF(E_INVALIDARG, desc.Format != DXGI_FORMAT_NV12); | |
| // Map the surface to get CPU access | |
| SurfaceMapper mapper; | |
| void* pData = nullptr; | |
| UINT rowPitch = 0; | |
| UINT depthPitch = 0; | |
| THROW_IF_FAILED(mapper.MapSurface(surface, &pData, &rowPitch, &depthPitch)); | |
| // Calculate total surface size for NV12 | |
| // Y plane: rowPitch * height | |
| // UV plane: rowPitch * (height/2) | |
| UINT totalSize = rowPitch * desc.Height + rowPitch * (desc.Height / 2); | |
| auto surfaceSpan = gsl::make_span(static_cast<uint8_t*>(pData), totalSize); | |
| return SplitNV12Surface(surfaceSpan, desc.Width, desc.Height, rowPitch); | |
| } | |
| // Helper functions to access individual color components | |
| inline uint8_t GetYValue(const NV12Plane& yPlane, UINT x, UINT y) | |
| { | |
| THROW_HR_IF(E_INVALIDARG, x >= yPlane.width || y >= yPlane.height); | |
| return yPlane.data[y * yPlane.rowPitch + x]; | |
| } | |
| inline std::pair<uint8_t, uint8_t> GetUVValues(const NV12Plane& uvPlane, UINT x, UINT y) | |
| { | |
| THROW_HR_IF(E_INVALIDARG, x >= uvPlane.width || y >= uvPlane.height); | |
| UINT offset = y * uvPlane.rowPitch + x * 2; | |
| uint8_t u = uvPlane.data[offset]; | |
| uint8_t v = uvPlane.data[offset + 1]; | |
| return {u, v}; | |
| } | |
| // Example processing function | |
| void ProcessNV12Surface(const IDirect3DSurface& surface) | |
| { | |
| try | |
| { | |
| auto nv12 = GetNV12SurfaceFromDirect3D(surface); | |
| // Process Y plane (luminance) | |
| for (UINT y = 0; y < nv12.yPlane.height; ++y) | |
| { | |
| auto rowStart = nv12.yPlane.data.data() + (y * nv12.yPlane.rowPitch); | |
| auto rowSpan = gsl::make_span(rowStart, nv12.yPlane.width); | |
| // Process luminance values in this row | |
| for (uint8_t& yValue : rowSpan) | |
| { | |
| // Example: Apply brightness adjustment | |
| yValue = static_cast<uint8_t>(std::min(255, static_cast<int>(yValue) + 10)); | |
| } | |
| } | |
| // Process UV plane (chrominance) | |
| for (UINT y = 0; y < nv12.uvPlane.height; ++y) | |
| { | |
| auto rowStart = nv12.uvPlane.data.data() + (y * nv12.uvPlane.rowPitch); | |
| for (UINT x = 0; x < nv12.uvPlane.width; ++x) | |
| { | |
| UINT offset = x * 2; | |
| uint8_t& u = rowStart[offset]; | |
| uint8_t& v = rowStart[offset + 1]; | |
| // Example: Apply color adjustment | |
| // Reduce saturation slightly | |
| u = static_cast<uint8_t>(128 + (static_cast<int>(u) - 128) * 0.9); | |
| v = static_cast<uint8_t>(128 + (static_cast<int>(v) - 128) * 0.9); | |
| } | |
| } | |
| } | |
| catch (...) | |
| { | |
| LOG_CAUGHT_EXCEPTION(); | |
| } | |
| } |