2012-11-30 29 views
2

我一直在使用d3d11很長一段時間了,在發現directx調試器之後,我最近發現我的程序從所有沒有正確釋放的com對象的任何地方都漏出內存。經過幾個小時的盯着代碼窺探之後,我已經開發了一些方法來隔離哪些地方我得到了這些意外增加到ref計數。D3D11從哪裏增加裁判計數?

首先,所有的對象都被包裝在std :: shared_ptrs中,用定製的刪除器來調用它們各自的釋放函數。我這樣做是爲了不必調用addref,只有當對象超出範圍時纔會調用釋放的第一個調用,即刪除者中的調用。這將是這個樣子:

// 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個未完成的對我創建的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!很明顯,我誤解了這些com對象應該被處理的方式。有沒有這樣的方式,我可以手動刪除它們,而不是使用那裏的幕後裁判廢話?

+0

如果你手動刪除他們,而別的東西了他們一個參考,你的程序會崩潰。引用計數較高,因爲您已創建了保存對原始結構的引用的新結構。這很好,但是當新的結構被銷燬時,你可以釋放你的引用而不用擔心它,然後舊的結構會放棄它們的引用計數。 –

回答

6

每次創建緩衝區或着色器或任何依賴於設備的內容時,該對象都可能包含對設備的引用,因此會提升引用計數以確保它在仍在使用時不會被刪除。

這聽起來像你的做法很可能會全面工作,因爲你會基本上保持一個單一的參考設備在你的代碼,以阻止它被刪除,而當所有的內部引用都消失了釋放它。但是,d3d仍然會自己進行引用計數,因此只有當您釋放每個其他相關對象的引用時,引用計數纔會降至零。即使只是創建交換鏈和設備也會返回緩衝區等等,這可能需要維護對設備的引用。

我想了一會兒這個同樣的想法......而最後發現它更容易只是

#include <atlbase> 

然後使用

CComPtr<ID311Device> m_Device 

因爲這幾乎是該類別是什麼爲std :: shader_ptr設計,它比std :: shader_ptr更輕便,因爲對象中已經有一個引用計數器,所以不需要保留單獨的引用計數器。

+0

我非常希望使用CComPtr對象,但顯然它在Visual Studio 2010快速版中不可用。這就是爲什麼我不得不繞過使用std :: shared_ptr。所以基本上你說的是,如果我可以確保所有其他d3d11對象的銷燬,那麼定製刪除器內的版本應該可以正常工作?我想我會去尋找那些沒有被破壞的物體。謝謝您的幫助!另外,是「std :: shader_ptr」是一個錯字,還是你的意思是寫這個? – FatalCatharsis

+0

這是一種類型,抱歉。哦,對不起,我沒有;「知道這是不具備的Express版本 – jcoder

+0

錯字......哎呀典型的 - 它可能不會太難寫反正最少替換但是CComPtr,它只是一個簡單的包裝,適當時調用Release和AddRef。 – jcoder

1

即使使用自定義刪除程序,對於Direct3D(COM)對象,使用shared_ptr也不正確。

首先,COM對象使用侵入式引用計數,這意味着引用計數存儲在對象本身中。另一方面,shared_ptr使用非侵入式引用計數,這意味着引用計數存儲在智能定位器對象中。因此,對COM對象使用shared_ptr意味着您有兩個獨立的獨立引用計數:COM對象和shared_ptr。其次,使用自定義刪除程序可以解決正確釋放對象的問題,但它不能解決正確獲取對象引用的問題。將COM對象分配給shared_ptr將增加shared_ptr的引用計數,但不會增加對象的引用計數。

這就解釋了爲什麼你要泄漏對象:D3D方法增加對象的引用計數,但是你使用shared_ptrs只在對象的整個生命週期內減少對象的引用計數一次(當所有指向對象的shared_ptrs都是破壞)。

因此,您需要使用COM智能指針,例如ATL的CComPtr

+0

雖然我同意,在使用COM對象的shared_ptr通常是一個壞主意,嵌套的引用計數不吃虧泄露你描述對象的問題。在這種情況下,shared_ptr保存COM對象的一個​​引用,一旦所有對shared_ptr對象的引用都消失,定製刪除器就會釋放它,這是正確的行爲。所以即使它不是很好的習慣,它仍然是正確的。額外引用的原因是(因爲@ J99已經指出)不同COM對象之間的依賴關係。 – Stacker