Я работал с d3d11 уже довольно давно, и после обнаружения отладчика directx я недавно обнаружил, что моя программа пропускает память повсюду из всех com-объектов, которые не высвобождаются должным образом. После некоторого слежки и многочасового изучения кода, я разработал несколько методов, чтобы изолировать, откуда я получаю эти неожиданные увеличения числа ссылок.
во-первых, все объекты обернуты в std :: shared_ptrs с пользовательскими удалителями, которые вызывают их соответствующую функцию выпуска. Я сделал это так, что мне никогда не пришлось бы вызывать addref, и первый вызов release, который был в удалителе, был бы вызван только тогда, когда объект вышел из области видимости. Это будет выглядеть примерно так:
// in D3D11Renderer.h
...
// declaration
std::shared_ptr<ID3D11Device *> m_Device;
...
// after call to ID3D11CreateDeviceAndSwapChain
m_Device.reset(device, [](ID3D11Device * ptr){ptr->Release();})
Проблема в том, что некоторые случайные функции в вызовах API просто случайным образом увеличивают количество ссылок, ожидая, что мне придется иметь дело с этим позже.
кое-что, что я нашел полезным в диагностике, было функцией, которая была похожа на это:
template <typename T>
int getRefCount(T object)
{
object->AddRef();
return object->Release();
}
который просто увеличивает и уменьшает значение, чтобы получить текущее количество ссылок на этот объект. Используя это, я обнаружил, что непосредственно перед вызовом релиза в пользовательском удалителе есть 10 выдающихся ссылок на 1 ID3D11Device, который я создал. Любопытно, я медленно возвращался назад, вызывая эту функцию обратно через программу, вплоть до того места, где я ее изначально создал. Забавно, но сразу после того, как я впервые создал объект (даже до того, как shared_ptr вступил во владение), количество выдающихся ссылок уже составляет 3! Это происходит сразу после этого здесь.
result = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, &featureLevel, 1,
D3D11_SDK_VERSION, &swapChainDesc, &swapChain, &device, NULL, &deviceContext);
if(FAILED(result))
{
return false;
}
это первый раз, когда я вызываю любую такую функцию, которая создает устройство, и когда я проверяю, сколько ссылок сразу после этого, и он говорит 3! Ясно, что я неправильно понимаю, как эти ком-объекты должны обрабатываться. Есть ли такой способ, которым я могу просто вручную удалить их, а не использовать там закулисные ссылки подсчета бессмыслицы?
Каждый раз, когда вы создаете буфер, шейдер или что-либо, что зависит от устройства, этот объект, скорее всего, будет содержать ссылку на устройство, поэтому увеличит его счетчик ссылок, чтобы гарантировать, что он не будет удален, пока он все еще использует его.
Похоже, ваш подход в целом может сработать, так как вы по существу сохраните одну единственную ссылку на устройство в своем коде, чтобы остановить его удаление, и когда все ваши внутренние ссылки исчезнут, отпустите его. Однако d3d по-прежнему будет вести собственный подсчет ссылок, поэтому счетчик ссылок будет сбрасываться до нуля только тогда, когда вы освобождаете каждую ссылку на любой другой связанный объект. Даже простое создание цепочки подкачки и устройства создаст резервные буферы и т. Д., Которые, вероятно, должны поддерживать ссылку на устройство.
Я попробовал эту же идею некоторое время … И в конце концов оказалось, что просто
#include <atlbase>
Тогда используйте
CComPtr<ID311Device> m_Device
поскольку именно для этого и предназначен этот класс, и он более легкий, чем std :: shader_ptr, поскольку у объектов уже есть счетчик ссылок, поэтому нет необходимости хранить отдельный.
Использование shared_ptr неправильно для объектов Direct3D (COM), даже если вы используете пользовательские средства удаления.
Во-первых, COM-объекты используют навязчивый подсчет ссылок, что означает, что счетчик ссылок хранится в самом объекте. shared_ptr, с другой стороны, использует ненавязчивый подсчет ссылок, что означает, что счетчик ссылок хранится в объекте умного пуара. Следовательно, использование shared_ptr для COM-объектов означает, что у вас есть два отдельных независимых счетчика ссылок: COM-объект и shared_ptr.
Во-вторых, использование пользовательского удалителя решает проблему правильного освобождения объекта, но не решает проблему правильного получения ссылки на объект. Назначение COM-объекта для shared_ptr приведет к увеличению счетчика ссылок для shared_ptr, но не для объекта.
Это объясняет причину утечки объектов: методы D3D увеличивают счетчик ссылок на объекты, но вы используете shared_ptrs, который уменьшает счетчик ссылок на объект только один раз за все время существования объекта (когда все shared_ptrs, указывающие на объект, уничтожены).
Итак, вам нужно использовать смарт-указатель COM, такой как ATL CComPtr.