Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

Вопрос DX11 ImGui Hook — Ошибка ResizeBuffers (Non-zero reference count)

Sloppy
Начинающий
Начинающий
Статус
Оффлайн
Регистрация
13 Фев 2026
Сообщения
682
Реакции
18
Здарова, кодеры. Столкнулся с классической проблемой при доработке вьюпорта в DX11 хуке. Суть в том, что при попытке смены разрешения или перехода в фулскрин, ResizeBuffers выплевывает ошибку о ненулевом референс-каунте.

Direct3D жалуется, что какие-то объекты не были освобождены перед ресайзом, из-за чего SwapChain просто отказывается обновляться. Обычно это происходит, когда RenderTargetView или сам бэк-буфер все еще висят в памяти.

Сам код хука:
Код:
Expand Collapse Copy
HRESULT WINAPI hkResize(IDXGISwapChain* This, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags)
 {
    if (mainRenderTargetView) {
        mainRenderTargetView->Release();
        mainRenderTargetView = nullptr;
    }

    HRESULT hr = oResize(This, BufferCount, Width, Height, NewFormat, SwapChainFlags);
    ID3D11Texture2D* pBackBuffer = nullptr;

    hr = This->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
    hr = pDevice->CreateRenderTargetView(pBackBuffer, nullptr, &mainRenderTargetView);
    pBackBuffer->Release();

    PContext->OMSetRenderTargets(1, &mainRenderTargetView, NULL);

    D3D11_VIEWPORT viewport = {};
    viewport.Width = static_cast<FLOAT>(Width);
    viewport.Height = static_cast<FLOAT>(Height);
    viewport.MinDepth = 0.0f;
    viewport.MaxDepth = 1.0f;
    viewport.TopLeftX = 0.0f;
    viewport.TopLeftY = 0.0f;
    PContext->RSSetViewports(1, &viewport);
    return hr;
}

Что было проверено:
  1. RTV вроде как релизнуто и занулено перед вызовом оригинала.
  2. Бэк-буфер после создания RTV тоже освобождается.
  3. В контексте (PContext) таргеты сбрасываются.

Но беда в том, что оно всё равно не работает как надо в рантайме. Есть подозрение, что ImGui или какой-то внутренний стейт девайса держит ссылки на текстуры. Те, кто плотно сидит на DX11 — подскажите, может стоит еще вручную дергать ClearState или чекать другие хуки?

Думаю, стоит подрубить DirectX Debug Layer, чтобы точно увидеть, какой именно объект «застрял» в пайплайне.
 
🎮🖥️ Классика DX11 — `ResizeBuffers` падает с `DXGI_ERROR_INVALID_CALL` (0x887A0001) из-за живых ссылок.

😵 **Почему твой код не помогает:**

`PContext->OMSetRenderTargets(1, &mainRenderTargetView, NULL)` **не освобождает старые RTV** полностью. Глубинный буфер (`DepthStencilView`) всё ещё привязан, и если он создавался отдельно — `ResizeBuffers` фейлится.

✅ **Рабочий паттерн для DX11 (проверено на множестве игр):**

```cpp
HRESULT WINAPI hkResizeBuffers(IDXGISwapChain* pSwapChain, UINT BufferCount,
UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags)
{
// 1. Очищаем все привязки в контексте
ID3D11DeviceContext* pContext = nullptr;
pDevice->GetImmediateContext(&pContext);

pContext->OMSetRenderTargets(0, nullptr, nullptr);
pContext->Flush(); // Форсируем завершение команд

// 2. Освобождаем RenderTargetView
if (g_pRenderTargetView) {
g_pRenderTargetView->Release();
g_pRenderTargetView = nullptr;
}

// 3. Освобождаем DepthStencilView (если есть)
if (g_pDepthStencilView) {
g_pDepthStencilView->Release();
g_pDepthStencilView = nullptr;
}

// 4. Освобождаем бэк-буфер через GetBuffer(0)
ID3D11Texture2D* pBackBuffer = nullptr;
pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer);
if (pBackBuffer) {
pBackBuffer->Release(); // Важно! Уменьшаем refcount до 0
pBackBuffer = nullptr;
}

// 5. Вызываем оригинальный ResizeBuffers
HRESULT hr = g_pOriginalResizeBuffers(pSwapChain, BufferCount, Width, Height, NewFormat, SwapChainFlags);

if (SUCCEEDED(hr)) {
// 6. Заново создаём RTV и DSV
pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer);
pDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_pRenderTargetView);
pBackBuffer->Release();

// 7. Восстанавливаем привязку
pContext->OMSetRenderTargets(1, &g_pRenderTargetView, g_pDepthStencilView);

// 8. Восстанавливаем вьюпорт
D3D11_VIEWPORT vp = {0, 0, (FLOAT)Width, (FLOAT)Height, 0.0f, 1.0f};
pContext->RSSetViewports(1, &vp);
}

pContext->Release();
return hr;
}
```

🔥 **Если всё равно падает — подключи Debug Layer:**

```cpp
// В начале программы
ID3D11Debug* pDebug = nullptr;
pDevice->QueryInterface(__uuidof(ID3D11Debug), (void**)&pDebug);
pDebug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
pDebug->Release();
```

Он покажет точный объект с живым refcount (обычно это `ID3D11Texture2D` или `IDXGISurface`).

💀 **Особенности для оверлеев (ImGui/Dear ImGui):**

ImGui держит свои `RenderTargetView` и `ShaderResourceView`. Перед `ResizeBuffers` нужно вызвать:
```cpp
ImGui_ImplDX11_InvalidateDeviceObjects();
// ... ресайз
ImGui_ImplDX11_CreateDeviceObjects();
```

Без этого — гарантированный краш или ошибка ресайза.
 
Назад
Сверху Снизу