Search Unity

Capture unity screen in native code using DirectX capture

Discussion in 'Editor & General Support' started by ranjan_rishi, Aug 13, 2015.

  1. ranjan_rishi

    ranjan_rishi

    Joined:
    Dec 1, 2014
    Posts:
    9
    I want to capture game in Native code using DirectX capture. I use staging buffer to capture the screen. But I only get black screen after capture. Any input on what I am doing wrong in code attached?

    Code (C++):
    1. // Check out http://docs.unity3d.com/Documentation/Manual/NativePluginInterface.html
    2. // for additional documentation about this process.
    3.  
    4. #include "UnityPlugin.h"
    5. #include "GraphicsInclude.h"
    6.  
    7. #if _WIN32
    8. #include "Capture/DXCapture.h"
    9. #include <d3d11.h>
    10. #include <DXGI.h>
    11. #endif
    12.  
    13.  
    14. #include <string>
    15. #include <iostream>
    16.  
    17.  
    18. #if _WIN32
    19. // DirectX states
    20. ID3D11Device* g_d3d_Device;
    21. ID3D11DeviceContext* g_d3d_DeviceContext;
    22. ID3D11RenderTargetView* g_renderTargetView[1];
    23. ID3D11Texture2D* g_backBuffer;
    24. #endif
    25.  
    26. // For now width and height are hardcoded
    27. int width = 1280;
    28. int height = 720;
    29. int bpp = 4;
    30.  
    31. #pragma once
    32.  
    33. #pragma warning (push)
    34.  
    35. #if _MSC_VER && !__INTEL_COMPILER
    36. // Disable warnings about macro re-definitions if we're using the DX SDK
    37. // http://stackoverflow.com/questions/19336841/i-get-a-lot-of-macro-redefinition-when-using-directx-10
    38. #pragma warning (disable: 4005)
    39. #endif
    40.  
    41. #if _WIN32
    42. #include <d3d11.h>
    43. #endif
    44.  
    45. #pragma warning (pop)
    46.  
    47. typedef void(*FuncPtr)(const char *);
    48.  
    49. FuncPtr Debug;
    50.  
    51. static void DebugLog (const char* str)
    52. {
    53. #if UNITY_WIN
    54.     OutputDebugStringA (str);  
    55.     //Debug(str);
    56. #else
    57.     printf ("%s", str);
    58. #endif
    59. }
    60.  
    61. extern "C"
    62. {
    63.     EXPORT_API void SetDebugFunction(FuncPtr fp)
    64.     {
    65.         Debug = fp;
    66.     }
    67. }
    68.  
    69. // If exported by a plugin, this function will be called when graphics device is created, destroyed,
    70. // and before and after it is reset (ie, resolution changed).
    71. extern "C" void EXPORT_API UnitySetGraphicsDevice( void* device, int deviceType, int eventType )
    72. {
    73.     char log[256];
    74.  
    75. #if _WIN32
    76.     ID3D11DepthStencilView* depthViews[1];
    77.     ID3D11Resource* targetResource;
    78.     D3D11_RENDER_TARGET_VIEW_DESC targetView;
    79.     // Assign default values.
    80.     g_d3d_Device = (ID3D11Device*)device;
    81.     g_d3d_Device->GetImmediateContext(&g_d3d_DeviceContext);
    82.  
    83.     g_d3d_DeviceContext->OMGetRenderTargets(1, g_renderTargetView, depthViews);
    84.  
    85.     if (g_renderTargetView[0] == nullptr){
    86.         DebugLog("No debug target view found\n");
    87.     }
    88.  
    89.     g_renderTargetView[0]->GetResource(&targetResource);
    90.  
    91.     g_renderTargetView[0]->GetDesc(&targetView);
    92.  
    93.     sprintf(log, "Format is %d\n", targetView.Format);
    94.     DebugLog(log);
    95.  
    96.     // Try to initialize screen capture methods.
    97.     // We try them explicitly one by one until we find one that works
    98.  
    99.     DebugLog("Trying DirectX capture method\n");
    100.     g_screenCapture = new DXCapture(g_d3d_Device, g_d3d_DeviceContext, targetResource, width, height, targetView.Format);
    101.     if (!g_screenCapture->supported())
    102.     {
    103.         DebugLog("Cannot initialize DirectX capture method\n");
    104.         delete g_screenCapture;
    105.         g_screenCapture = NULL;
    106.     }
    107.     else
    108.     {
    109.         DebugLog("Using DirectX Capture method\n");
    110.         goto foundCaptureMethod;
    111.     }
    112.  
    113.     // at this point, couldn't find any capture methods
    114.     DebugLog("Could not initialize any capture methods");
    115.  
    116. foundCaptureMethod:
    117.     DebugLog("Initialize capture methods");
    118.  
    119. #endif
    120.  
    121.  
    122. }
    123.  
    124.  
    125. // If exported by a plugin, this function will be called for GL.IssuePluginEvent script calls.
    126. // The function will be called on a rendering thread; note that when multithreaded rendering is used,
    127. // the rendering thread WILL BE DIFFERENT from the thread that all scripts & other game logic happens!
    128. // You have to ensure any synchronization with other plugin script calls is properly done by you.
    129. extern "C" void EXPORT_API UnityRenderEvent ( int eventID )
    130. {
    131.  
    132.         // Capture the frame using our current capture method
    133.         const unsigned char* theFrame = g_screenCapture->capture();
    134.         g_screenCapture->endCapture(); // Done with capture data
    135.  
    136. }
    137.  
    138.  
    DXCapture.cpp
    Code (C++):
    1.  
    2.  
    3. #include "DXCapture.h"
    4.  
    5. DXCapture::DXCapture(ID3D11Device* d3DDevice, ID3D11DeviceContext* d3DDeviceContext,
    6.                                          ID3D11Resource* backbuffer,
    7.                      int backbufferWidth, int backbufferHeight,
    8.                                          DXGI_FORMAT format)
    9.     : _d3D11Device(d3DDevice),
    10.       _d3D11DeviceContext(d3DDeviceContext),
    11.       _backbuffer(backbuffer),
    12.       _backbufferWidth(backbufferWidth),
    13.       _backbufferHeight(backbufferHeight),
    14.           _format(format)
    15. {
    16. }
    17.  
    18. bool DXCapture::supported()
    19. {
    20.     try
    21.     {
    22.         // Create a staging resource
    23.         D3D11_TEXTURE2D_DESC stagingTextureDesc;
    24.         stagingTextureDesc.Width = _backbufferWidth;
    25.         stagingTextureDesc.Height = _backbufferHeight;
    26.         stagingTextureDesc.MipLevels = stagingTextureDesc.ArraySize = 1;
    27.         //stagingTextureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    28.                 stagingTextureDesc.Format = _format;
    29.         stagingTextureDesc.SampleDesc.Count = 1;
    30.         stagingTextureDesc.SampleDesc.Quality = 0;
    31.         stagingTextureDesc.Usage = D3D11_USAGE_STAGING;
    32.         stagingTextureDesc.BindFlags = 0;
    33.         stagingTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    34.         stagingTextureDesc.MiscFlags = 0;
    35.  
    36.         if (!SUCCEEDED(_d3D11Device->CreateTexture2D(&stagingTextureDesc, NULL, &_stagingBuffer)))
    37.             return false;
    38.  
    39.         return true;
    40.     }
    41.     catch (...)
    42.     {
    43.         return false; // Something went wrong, we can't set up DX capture
    44.     }
    45. }
    46.  
    47. CapturePixelFormat DXCapture::getCapturePixelFormat()
    48. {
    49.     return CAPTURE_PIXELFORMAT_R8G8B8A8; // Always
    50. }
    51.  
    52. const unsigned char* DXCapture::capture()
    53. {
    54.     // Copy the frame to the staging buffer
    55.     _d3D11DeviceContext->CopyResource(_stagingBuffer, _backbuffer);
    56.  
    57.     // Lock the staging buffer and post it to the host app for encoding
    58.     D3D11_MAPPED_SUBRESOURCE mappedBuffer;
    59.     HRESULT bufferMapResult = _d3D11DeviceContext->Map(_stagingBuffer, 0, D3D11_MAP_READ, 0, &mappedBuffer);
    60.     if (SUCCEEDED(bufferMapResult))
    61.     {
    62.         unsigned char* pixel_bytes = (unsigned char*)mappedBuffer.pData;
    63.         return pixel_bytes;
    64.     }
    65.  
    66.     return NULL;
    67. }
    68.  
    69. void DXCapture::endCapture()
    70. {
    71.     // Unmap this buffer, the application is done with the capture
    72.     _d3D11DeviceContext->Unmap(_stagingBuffer, 0);
    73. }
    74.  
    75. DXCapture::~DXCapture()
    76. {
    77.     if (_stagingBuffer != NULL)
    78.     {
    79.         _stagingBuffer->Release();
    80.         _stagingBuffer = NULL;
    81.     }
    82. }
    83.  
    84.  
     
  2. ranjan_rishi

    ranjan_rishi

    Joined:
    Dec 1, 2014
    Posts:
    9
    I know the issues. I should us D3D11_RTV_DIMENSION_TEXTURE2D for copyresource.
     
  3. ranjan_rishi

    ranjan_rishi

    Joined:
    Dec 1, 2014
    Posts:
    9
  4. weir75034

    weir75034

    Joined:
    Aug 24, 2018
    Posts:
    1
    I try your method and use ScreenGrab, here is the code:
    Code (CSharp):
    1. void App3Main::SaveScreenshot()
    2. {
    3.     auto file = ref new Platform::String(Windows::Storage::ApplicationData::Current->TemporaryFolder->Path->Data()) + "\\Screenshot.png";
    4.  
    5.  
    6.  
    7.     /*
    8.     ComPtr<ID3D11Texture2D> backBuffer;
    9.     DX::ThrowIfFailed(m_deviceResources->GetSwapChain()->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBuffer));
    10.     */
    11.  
    12.     ID3D11RenderTargetView* targets[] = { nullptr, nullptr };
    13.     m_sceneRenderer->GetContext()->OMGetRenderTargets(ARRAYSIZE(targets), targets, nullptr);
    14.     D3D11_RENDER_TARGET_VIEW_DESC targetView;
    15.     targets[0]->GetDesc(&targetView);
    16.     ID3D11Resource* targetResource;
    17.     targets[0]->GetResource(&targetResource);
    18.  
    19.     auto hResult = SaveWICTextureToFile(m_sceneRenderer->GetContext(), targetResource, GUID_ContainerFormatPng, file->Data());
    20. }
    Eventually, I succeeded. Good luck!!!