| // Copyright (c) Microsoft Corporation |
| |
| #include "SwapChainProcessor.h" |
| |
| namespace display::test { |
| |
| SwapChainProcessor::SwapChainProcessor(IDDCX_SWAPCHAIN hSwapChain, |
| std::unique_ptr<Direct3DDevice> Device, |
| HANDLE NewFrameEvent) |
| : m_hSwapChain(hSwapChain), |
| m_Device(std::move(Device)), |
| m_hAvailableBufferEvent(NewFrameEvent) { |
| m_hTerminateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); |
| |
| // Immediately create and run the swap-chain processing thread, passing 'this' |
| // as the thread parameter |
| m_hThread.Attach(CreateThread(nullptr, 0, RunThread, this, 0, nullptr)); |
| } |
| |
| SwapChainProcessor::~SwapChainProcessor() { |
| // Alert the swap-chain processing thread to terminate |
| SetEvent(m_hTerminateEvent.Get()); |
| |
| if (m_hThread.Get()) { |
| // Wait for the thread to terminate |
| WaitForSingleObject(m_hThread.Get(), INFINITE); |
| } |
| } |
| |
| DWORD CALLBACK SwapChainProcessor::RunThread(LPVOID Argument) { |
| reinterpret_cast<SwapChainProcessor*>(Argument)->Run(); |
| return 0; |
| } |
| |
| void SwapChainProcessor::Run() { |
| // For improved performance, make use of the Multimedia Class Scheduler |
| // Service, which will intelligently prioritize this thread for improved |
| // throughput in high CPU-load scenarios. |
| DWORD AvTask = 0; |
| HANDLE AvTaskHandle = AvSetMmThreadCharacteristicsW(L"Distribution", &AvTask); |
| |
| RunCore(); |
| |
| // Always delete the swap-chain object when swap-chain processing loop |
| // terminates in order to kick the system to provide a new swap-chain if |
| // necessary. |
| WdfObjectDelete((WDFOBJECT)m_hSwapChain); |
| m_hSwapChain = nullptr; |
| |
| AvRevertMmThreadCharacteristics(AvTaskHandle); |
| } |
| |
| void SwapChainProcessor::RunCore() { |
| // Get the DXGI device interface |
| Microsoft::WRL::ComPtr<IDXGIDevice> DxgiDevice; |
| HRESULT hr = m_Device->Device.As(&DxgiDevice); |
| if (FAILED(hr)) { |
| return; |
| } |
| |
| IDARG_IN_SWAPCHAINSETDEVICE SetDevice = {}; |
| SetDevice.pDevice = DxgiDevice.Get(); |
| |
| hr = IddCxSwapChainSetDevice(m_hSwapChain, &SetDevice); |
| if (FAILED(hr)) { |
| return; |
| } |
| |
| // Acquire and release buffers in a loop |
| for (;;) { |
| Microsoft::WRL::ComPtr<IDXGIResource> AcquiredBuffer; |
| |
| // Ask for the next buffer from the producer |
| IDARG_OUT_RELEASEANDACQUIREBUFFER Buffer = {}; |
| hr = IddCxSwapChainReleaseAndAcquireBuffer(m_hSwapChain, &Buffer); |
| |
| // AcquireBuffer immediately returns STATUS_PENDING if no buffer is yet |
| // available |
| if (hr == E_PENDING) { |
| // We must wait for a new buffer |
| HANDLE WaitHandles[] = {m_hAvailableBufferEvent, m_hTerminateEvent.Get()}; |
| DWORD WaitResult = WaitForMultipleObjects(ARRAYSIZE(WaitHandles), |
| WaitHandles, FALSE, 16); |
| if (WaitResult == WAIT_OBJECT_0 || WaitResult == WAIT_TIMEOUT) { |
| // We have a new buffer, so try the AcquireBuffer again |
| continue; |
| } else if (WaitResult == WAIT_OBJECT_0 + 1) { |
| // We need to terminate |
| break; |
| } else { |
| // The wait was cancelled or something unexpected happened |
| hr = HRESULT_FROM_WIN32(WaitResult); |
| break; |
| } |
| } else if (SUCCEEDED(hr)) { |
| // We have new frame to process, the surface has a reference on it that |
| // the driver has to release |
| AcquiredBuffer.Attach(Buffer.MetaData.pSurface); |
| |
| // ============================== |
| // TODO: Process the frame here |
| // |
| // This is the most performance-critical section of code in an IddCx |
| // driver. It's important that whatever is done with the acquired surface |
| // be finished as quickly as possible. This operation could be: |
| // * a GPU copy to another buffer surface for later processing (such as a |
| // staging surface for mapping to CPU memory) |
| // * a GPU encode operation |
| // * a GPU VPBlt to another surface |
| // * a GPU custom compute shader encode operation |
| // ============================== |
| |
| // We have finished processing this frame hence we release the reference |
| // on it. If the driver forgets to release the reference to the surface, |
| // it will be leaked which results in the surfaces being left around after |
| // swapchain is destroyed. NOTE: Although we release reference to the |
| // surface here; the driver still owns the Buffer.MetaData.pSurface |
| // surface until IddCxSwapChainReleaseAndAcquireBuffer returns S_OK and |
| // gives us a new frame, a driver may want to use the surface in future |
| // to re-encode the desktop for better quality if there is no new frame |
| // for a while |
| AcquiredBuffer.Reset(); |
| |
| // Indicate to OS that we have finished inital processing of the frame, it |
| // is a hint that OS could start preparing another frame |
| hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain); |
| if (FAILED(hr)) { |
| break; |
| } |
| |
| // ============================== |
| // TODO: Report frame statistics once the asynchronous encode/send work is |
| // completed |
| // |
| // Drivers should report information about sub-frame timings, like encode |
| // time, send time, etc. |
| // ============================== |
| // IddCxSwapChainReportFrameStatistics(m_hSwapChain, ...); |
| } else { |
| // The swap-chain was likely abandoned (e.g. DXGI_ERROR_ACCESS_LOST), so |
| // exit the processing loop |
| break; |
| } |
| } |
| } |
| } // namespace display::test |