DesktopDuplication API создает черные рамки, в то время как некоторые приложения находятся в полноэкранном режиме

Я создаю приложение, которое используется для создания и обмена скриншотами в режиме реального времени между несколькими клиентами по сети.

Я использую API дублирования рабочего стола MS чтобы получить данные изображения, и он работает гладко, за исключением некоторых крайних случаев.

Я использовал четыре игры в качестве тестовых приложений, чтобы проверить, как снимок экрана ведет себя в полноэкранном режиме. Это Герои Шторма, Радуга Шести Осада, Counter Strike и PlayerUnknown’s Battlegrounds.

На моей машине с видеокартой GeForce GTX 1070; все отлично работает как в полноэкранном режиме, так и вне его для всех тестовых приложений. Однако на двух других машинах, на которых установлен GeForce GTX 980; все тестовые приложения кроме PUBG работает. Когда PUBG работает в полноэкранном режиме, дублирование моего рабочего стола вместо этого создает полностью черное изображение, и я не могу понять, почему, как
Образец дублирования рабочего стола отлично работает на всех тестовых машинах и тестовых приложениях.

То, что я делаю, в основном то же самое, что и пример, за исключением того, что я извлекаю пиксельные данные и создаю свою собственную текстуру SDL (OpenGL) из этих данных вместо непосредственного использования полученного ID3D11Texture2D.

Почему PUBG в полноэкранном режиме на GTX 980 — единственный тестовый случай, который не проходит?

Что-то не так с тем, как я получаю кадр, обрабатываю ошибку «DXGI_ERROR_ACCESS_LOST» или как я копирую данные из GPU?

Объявления:

IDXGIOutputDuplication* m_OutputDup = nullptr;
Microsoft::WRL::ComPtr<ID3D11Device> m_Device = nullptr;
ID3D11DeviceContext* m_DeviceContext = nullptr;
D3D11_TEXTURE2D_DESC m_TextureDesc;

Инициализация:

bool InitializeScreenCapture()
{
HRESULT result = E_FAIL;
if (!m_Device)
{
D3D_FEATURE_LEVEL featureLevels = D3D_FEATURE_LEVEL_11_0;
D3D_FEATURE_LEVEL featureLevel;
result = D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
0,
&featureLevels,
1,
D3D11_SDK_VERSION,
&m_Device,
&featureLevel,
&m_DeviceContext);
if (FAILED(result) || !m_Device)
{
Log("Failed to create D3DDevice);
return false;
}
}

// Get DXGI device
ComPtr<IDXGIDevice> DxgiDevice;
result = m_Device.As(&DxgiDevice);
if (FAILED(result))
{
Log("Failed to get DXGI device);
return false;
}

// Get DXGI adapter
ComPtr<IDXGIAdapter> DxgiAdapter;
result = DxgiDevice->GetParent(__uuidof(IDXGIAdapter), &DxgiAdapter);
if (FAILED(result))
{
Log("Failed to get DXGI adapter);
return false;
}

DxgiDevice.Reset();

// Get output
UINT Output = 0;
ComPtr<IDXGIOutput> DxgiOutput;
result = DxgiAdapter->EnumOutputs(Output, &DxgiOutput);
if (FAILED(result))
{
Log("Failed to get DXGI output);
return false;
}

DxgiAdapter.Reset();

ComPtr<IDXGIOutput1> DxgiOutput1;
result = DxgiOutput.As(&DxgiOutput1);
if (FAILED(result))
{
Log("Failed to get DXGI output1);
return false;
}

DxgiOutput.Reset();

// Create desktop duplication
result = DxgiOutput1->DuplicateOutput(m_Device.Get(), &m_OutputDup);
if (FAILED(result))
{
Log("Failed to create output duplication);
return false;
}

DxgiOutput1.Reset();

DXGI_OUTDUPL_DESC outputDupDesc;
m_OutputDup->GetDesc(&outputDupDesc);

// Create CPU access texture description
m_TextureDesc.Width = outputDupDesc.ModeDesc.Width;
m_TextureDesc.Height = outputDupDesc.ModeDesc.Height;
m_TextureDesc.Format = outputDupDesc.ModeDesc.Format;
m_TextureDesc.ArraySize = 1;
m_TextureDesc.BindFlags = 0;
m_TextureDesc.MiscFlags = 0;
m_TextureDesc.SampleDesc.Count = 1;
m_TextureDesc.SampleDesc.Quality = 0;
m_TextureDesc.MipLevels = 1;
m_TextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_FLAG::D3D11_CPU_ACCESS_READ;
m_TextureDesc.Usage = D3D11_USAGE::D3D11_USAGE_STAGING;

return true;
}

Скриншот:

void TeamSystem::CaptureScreen()
{
if (!m_ScreenCaptureInitialized)
{
Log("Attempted to capture screen without ScreenCapture being initialized");
return false;
}

HRESULT result = E_FAIL;
DXGI_OUTDUPL_FRAME_INFO frameInfo;
ComPtr<IDXGIResource> desktopResource = nullptr;
ID3D11Texture2D* copyTexture = nullptr;
ComPtr<ID3D11Resource> image;

int32_t attemptCounter = 0;
DWORD startTicks = GetTickCount();
do // Loop until we get a non empty frame
{
m_OutputDup->ReleaseFrame();
result = m_OutputDup->AcquireNextFrame(1000, &frameInfo, &desktopResource);
if (FAILED(result))
{
if (result == DXGI_ERROR_ACCESS_LOST) // Access may be lost when changing from/to fullscreen mode(any application); when this happens we need to reaquirce the outputdup
{
m_OutputDup->ReleaseFrame();
m_OutputDup->Release();
m_OutputDup = nullptr;
m_ScreenCaptureInitialized = InitializeScreenCapture();
if (m_ScreenCaptureInitialized)
{
result = m_OutputDup->AcquireNextFrame(1000, &frameInfo, &desktopResource);
}
else
{
Log("Failed to reinitialize screen capture after access was lost");
return false;
}
}

if (FAILED(result))
{
Log("Failed to acquire next frame);
return false;
}
}
attemptCounter++;

if (GetTickCount() - startTicks > 3000)
{
Log("Screencapture timed out after " << attemptCounter << " attempts");
return false;
}

} while(frameInfo.TotalMetadataBufferSize <= 0 || frameInfo.LastPresentTime.QuadPart <= 0); // This is how you wait for an image containing image data according to SO (https://stackoverflow.com/questions/49481467/acquirenextframe-not-working-desktop-duplication-api-d3d11)

Log("ScreenCapture succeeded after " << attemptCounter << " attempt(s)");

// Query for IDXGIResource interface
result = desktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&copyTexture));
desktopResource->Release();
desktopResource = nullptr;
if (FAILED(result))
{
Log("Failed to acquire texture from resource);
m_OutputDup->ReleaseFrame();
return false;
}

// Copy image into a CPU access texture
ID3D11Texture2D* stagingTexture = nullptr;
result = m_Device->CreateTexture2D(&m_TextureDesc, nullptr, &stagingTexture);
if (FAILED(result) || stagingTexture == nullptr)
{
Log("Failed to copy image data to access texture);
m_OutputDup->ReleaseFrame();
return false;
}

D3D11_MAPPED_SUBRESOURCE mappedResource;
m_DeviceContext->CopyResource(stagingTexture, copyTexture);
m_DeviceContext->Map(stagingTexture, 0, D3D11_MAP_READ, 0, &mappedResource);
void* copy = malloc(m_TextureDesc.Width * m_TextureDesc.Height * 4);
memcpy(copy, mappedResource.pData, m_TextureDesc.Width * m_TextureDesc.Height * 4);
m_DeviceContext->Unmap(stagingTexture, 0);
stagingTexture->Release();
m_OutputDup->ReleaseFrame();

// Create a new SDL texture from the data in the copy varialbe

free(copy);
return true;
}

Некоторые заметки:

  • Я изменил свой исходный код, чтобы сделать его более читабельным, поэтому отсутствует некоторая очистка и регистрация ошибок.
  • Ни один из случаев ошибки или тайм-аута (кроме DXGI_ERROR_ACCESS_LOST) не запускается ни в одном сценарии тестирования.
  • В любом сценарии тестирования «tryupounter» никогда не превышает 2.
  • Тестовые случаи ограничены, так как у меня нет доступа к компьютеру, который создает черный корпус изображения.

2

Решение

Виновником был CopyResource () и то, как я создал текстуру доступа к процессору.
CopyResource () возвращает void, и поэтому я не смотрел на это раньше; Я не думал, что это может потерпеть неудачу каким-либо существенным образом, так как я ожидал, что он вернет bool или HRESULT, если это так.

Однако в документации CopyResource () раскрыто несколько случаев сбоя.

Этот метод необычен тем, что заставляет графический процессор выполнять операцию копирования (аналогично memcpy для процессора). В результате у него есть несколько ограничений, предназначенных для повышения производительности. Например, исходный и целевой ресурсы:

  • Должны быть разные ресурсы.
  • Должно быть того же типа.
  • Должны иметь одинаковые размеры (включая ширину, высоту, глубину и размер в зависимости от ситуации).
  • Должны иметь совместимые форматы DXGI, что означает, что форматы должны быть идентичными или, по крайней мере, из одной группы типов.
  • В настоящее время не может быть сопоставлено.

Поскольку код инициализации запускается до того, как тестовое приложение переходит в полноэкранный режим, описание текстуры доступа к ЦП настраивается с использованием разрешения рабочего стола, формата и т. Д. Это приводило к автоматическому сбою CopyResouce () и простой записи чего-либо в stagingTexture в тестовых случаях, когда для тестового приложения использовалось нестандартное разрешение.

В заключение; Я только что переместил m_TextureDescription установить CaptureScreen () и использовать описание copyTexture чтобы получить переменные, которые я не хотел менять между текстурами.

// Create CPU access texture
D3D11_TEXTURE2D_DESC copyTextureDesc;
copyTexture->GetDesc(&copyTextureDesc);

D3D11_TEXTURE2D_DESC textureDesc;
textureDesc.Width = copyTextureDesc.Width;
textureDesc.Height = copyTextureDesc.Height;
textureDesc.Format = copyTextureDesc.Format;
textureDesc.ArraySize = copyTextureDesc.ArraySize;
textureDesc.BindFlags = 0;
textureDesc.MiscFlags = 0;
textureDesc.SampleDesc = copyTextureDesc.SampleDesc;
textureDesc.MipLevels = copyTextureDesc.MipLevels;
textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_FLAG::D3D11_CPU_ACCESS_READ;
textureDesc.Usage = D3D11_USAGE::D3D11_USAGE_STAGING;

ID3D11Texture2D* stagingTexture = nullptr;
result = m_Device->CreateTexture2D(&textureDesc, nullptr, &stagingTexture);

Пока это решало проблемы, которые у меня были; Я до сих пор не знаю, почему повторная инициализация в обработке DXGI_ERROR_ACCESS_LOST все равно не решила проблему. Разве DesctopDuplicationDescription не использует те же размеры и формат, что и copyTexture?

Я также не знаю, почему это не сработало таким же образом на компьютерах с более новыми видеокартами. Однако я заметил, что эти машины могли захватывать полноэкранные приложения с помощью простого BitBlt () на поверхности рабочего стола.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]