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

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

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

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

Освобождение ресурсов render-target (не знаю как перевести) и буфера глубины Для отображения одного кадра 3D сцены используются следующие объекты: Microsoft: WRL: ComPtr m_renderTargetView; Microsoft: WRL: ComPtr 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 texture = … m_d3dContext→PSSetShaderResources (0, 1, &texture); То должны сделать и так: ID3D11ShaderResourceView* empty = NULL; d3dContext→PSSetShaderResources (0, 1, &empty); В противном случае текстура освобождена не будет даже если вы её «удалите». texture = nullptr; Иными словами перед удалением (release) шейдерных ресурсов их надо освободить (unbind).В принципе, после осознания первой «странности», вторая уже и не кажется таковой.И тем не менее мне это показалось не очевидным.

PS: Сейчас проблема с утечкой памяти исправлена и гнев пользователей постепенно меняется на милость.

© Habrahabr.ru