Как стать автором
Обновить

О том, как память текла, а я не мог понять, почему

Время на прочтение 2 мин
Количество просмотров 18K
Здравствуйте, уважаемые хабрачеловеки.

В этом коротком посте я хочу поделиться с вами некоторыми моментами, с которыми столкнулся при разработке одного из своих приложений (читалка для Windows). Речь пойдет о DirectX и, как мне показалось, странных утечках памяти.

Как я создал себе проблему?


Для отображения содержимого страниц я решил использовать DirectX. Задумка была проста: сначала создаю 2D-текстуру с текстом, а потом отображаю 3D модель с использованием подготовленных ранее текстур. Это дает мне возможность делать анимацию 3D перелистывания страниц.

Как-то так:



В момент выпуска приложения в магазин я ожидал всеобщего восхищения. Но не тут-то было. Пользователи оказались недовольны. Анализ ситуации показал, что течет память. И очень хорошо течет. Но почему? Этого я долго не мог понять.
С учетом того, что приложения в Windows 8.1 и Windows Phone 8.1 полностью не выгружаются при «закрытии», утечки памяти накапливались.

Сам процесс поиска утечки оказался совсем не интересным. А вот результат показался мне странным.

Что мне показалось странным?


Освобождение ресурсов render-target (не знаю как перевести) и буфера глубины


Для отображения одного кадра 3D сцены используются следующие объекты:

Microsoft::WRL::ComPtr<ID3D11RenderTargetView> m_renderTargetView;
Microsoft::WRL::ComPtr<ID3D11DepthStencilView> m_depthStencilView;

Далее, где-то в методе отображения сцены, мы будем использовать примерно такой код:

ID3D11RenderTargetView *const targets[1] = { m_renderTargetView.Get() };
m_d3dContext->OMSetRenderTargets(1, targets, m_depthStencilView.Get());

Ресурсы надо освобождать. И вот здесь таится одна из проблем, которая стала причиной этого поста.
Просто так, написать:

m_renderTargetView = nullptr;
m_depthStencilView = nullptr;

недостаточно. У m_d3dContext где-то внутри остаются ссылки на render-target и буфер глубины.
Т.е. надо нашему контексту указать ничто в качестве цели.

m_d3dContext->OMSetRenderTargets(0, nullptr, nullptr);

Беда заключается в том, что мы получим утечку памяти если сделаем вот так:

m_d3dContext->OMSetRenderTargets(0, nullptr, nullptr);
m_renderTargetView = nullptr;
m_depthStencilView = nullptr;

Чтобы исправить ситуацию, достаточно изменить порядок инструкций:

m_renderTargetView = nullptr;
m_depthStencilView = nullptr;
m_d3dContext->OMSetRenderTargets(0, nullptr, nullptr);

Это первая «странность», которую я не понимаю. Я буду рад, если кто-нибудь расскажет почему так происходит.

Освобождение ресурсов текстур


Вторая «странность» связана с текстурами. А точнее с шейдерными ресурсами.
Дело в том, что если мы делаем вот так:

Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> texture = ...
m_d3dContext->PSSetShaderResources(0, 1, &texture);

То должны сделать и так:

ID3D11ShaderResourceView* empty = NULL;
d3dContext->PSSetShaderResources(0, 1, &empty);

В противном случае текстура освобождена не будет, даже если вы её «удалите».

texture = nullptr;

Иными словами перед удалением (release) шейдерных ресурсов их надо освободить (unbind).

В принципе, после осознания первой «странности», вторая уже и не кажется таковой.
И тем не менее, мне это показалось не очевидным.

P.S.: Сейчас проблема с утечкой памяти исправлена и гнев пользователей постепенно меняется на милость.
Теги:
Хабы:
+11
Комментарии 20
Комментарии Комментарии 20

Публикации

Истории

Работа

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн
PG Bootcamp 2024
Дата 16 апреля
Время 09:30 – 21:00
Место
Минск Онлайн
EvaConf 2024
Дата 16 апреля
Время 11:00 – 16:00
Место
Москва Онлайн