Vulkan編程指南翻譯 第二章 第一節 CPU內存管理


 

你將在本章中學到:

l  Vulkan如何管理主機和設備內存

l  在應用程序中如何有效地管理內存

l  Vulkan如何使用imagesbuffers消費和生產數據

memory是是幾乎所有計算機系統做任何操作的基礎,也包括Vulkan。在Vulkan里,memory基本上有兩種類型:主機memory和設備memoryVulkan操作的所有資源必須被設備內存支持,應用程序需要負責管理內存。此外,內存也被用作主機上存儲數據。Vulkan提供了讓應用程序管理內存的機會。在本章中,您將學到Vulkan用來管理內存的各種機制。

         主機內存管理

每當Vulkan創建新對象時,它可能需要內存來存儲與它們相關的數據。為此,它使用主機內存,可以被CPU訪問,是通過malloc或者new調用返回的通常意義的內存。然而,除了正常的分配器,Vulkan有一些特殊的內存分配需求。其中最常見的,是它希望分配的內存被對齊。這是因為一些高性能CPU指令在對齊的內存上工作的最好。若存儲CPU端的數據被對齊,Vulkan可以無條件的使用這些高性能指令,提供實質性能優勢。

         由於上述要求,Vulkan實現將使用高級分配器。但是,為了某些,甚至全部操作,它還為您的應用程序提供了替換默認分配器的機會。這是通過指定多數的設備創建函數的pAllocator參數來實現的。例如,讓我們重新看一遍vkCreateInstance() 函數,它可能是你的應用程序第一個調用的函數。原型如下:

VkResult vkCreateInstance (

const VkInstanceCreateInfo*         pCreateInfo,

const VkAllocationCallbacks*         pAllocator,

VkInstance*                                         pInstance);

pAllocator參數是一個指向VkAllocationCallbacks類型數據的指針。直到目前,我們一直設置pAllocatornullptr,這告訴Vulkan去使用它內部提哦那個的默認內存分配器,而不是應用程序提供的內存分配器。VkAllocationCallbacks書籍結構封裝了我們提供的自定義內存分配器。這個數據結構定義如下:

typedef struct VkAllocationCallbacks {

void* pUserData;

PFN_vkAllocationFunction pfnAllocation;

PFN_vkReallocationFunction pfnReallocation;

PFN_vkFreeFunction pfnFree;

PFN_vkInternalAllocationNotification pfnInternalAllocation;

PFN_vkInternalFreeNotification pfnInternalFree;

} VkAllocationCallbacks;

你可以通過VkAllocationCallbacks的定義知道,它基本上是一些函數指針的集合和一個void* pUserData。這個指針可以供應用程序使用。它可以指向任何位置。Vulkan不會dereference它。事實上,它甚至不需要是一個指針。你可以放任何東西在哪兒,只要它放入一個指針占用的內存區。VulkanpUserData做的唯一的事情,就是將它傳遞到包含VkAllocationCallback指針的回調函數。

         pfnAllocationpfnReallocationpfnFree用於通常的、對象級的內存管理。它們被定義為指向與以下聲明匹配的函數的指針:

         void* VKAPI_CALL Allocation(

void* pUserData,

size_t size,

size_t alignment,

VkSystemAllocationScope allocationScope);

void* VKAPI_CALL Reallocation(

void* pUserData,

void* pOriginal

size_t size,

size_t alignment,

VkSystemAllocationScope allocationScope);

void VKAPI_CALL Free(

void* pUserData,

void* pMemory);

         注意,這三個函數用一個pUserData作為第一個參數,這是和VkAllocationCallbacks數據結構的pUserData是同一東西。如果你的應用程序使用數據結構來管理內存,這是放置他們的地址的好地方。用一個C ++類實現你的內存分配器(假設你在使用C++),並且把這個類的this指針放到pUserData,是一個合理的方式。

         Allocation函數負責新的內存分配。size參數指定了分配多少byteAlignment參數指定了安幾個byte進行要求的內存對齊,這是一個經常被忽視的參數。非常容易和原生的內存分配器malloc掛鈎聯系起來。如果這么做,你將會發現程序會正常運行一段時間,但是在某個函數中神奇的崩潰。如果你提供自己的allocator,,你需要重視alignment參數。

         最后一個參數allocationScope,,告訴應用程序,內存分配的范圍,生命周期是什么樣的。它是VkSystemAllocationScope值中的某一個,

l  VK_SYSTEM_ALLOCATION_SCOPE_COMMAND  意味着分配的內存將只生存於調用Allocationdemand中。因為只在一個command內生存,Vulkan有可能使用它做作為臨時的內存分配。

l  VK_SYSTEM_ALLOCATION_SCOPE_OBJECT  內存分配和一個特定的Vulkan對象關聯。在對象被銷毀之前,分配的內存一直存在。這種類型的內存分配只發生在command創建(所有以vkCreate開頭的函數)期間。

l  VK_SYSTEM_ALLOCATION_SCOPE_CACHE  意味着分配的內存和內部緩存或者VkPipelineCache對象關聯。

l  VK_SYSTEM_ALLOCATION_SCOPE_DEVICE  意味着分配的內存在整個devcie中都有效。當Vulkan需要和GPU關聯的內存,而不是和一個對象關聯。比如,該內存分配器在某些blocks中分配內存,有很多對象或許都在這個block內,那么,分配的內存就不能和任何特定對象直接關聯。

l  VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE  意味着分配的內存在一個instance內有效。這個與VK_SYSTEM_ALLOCATION_SCOPE_DEVICE.相似。這種類型的內存分配通常是layer或者Vulkan啟動時做的,比如vkCreateInstance()vkEnumeratePhysicalDevices()

pfnInternalAllocationpfnInternalFree函數指針指向Vulkan使用自帶分配器時的 備用的函數。他們和pfnAllocationpfnInternalFree的函數簽名相同,除了pfnInternalAllocation不返回值,且pfnInternalFree不應該真的釋放內存。這些函數僅僅用來通知應用程序管理好Vulkan在使用的內存。這些函數原型如下:

         void VKAPI_CALL InternalAllocationNotification(

void* pUserData,

size_t                                           size,

VkInternalAllocationType        allocationType,

VkSystemAllocationScope       allocationScope);

 

         void VKAPI_CALL InternalFreeNotification(

void*                                             pUserData,

size_t                                           size,

VkInternalAllocationType        allocationType,

VkSystemAllocationScope       allocationScope);

 

         對於pfnInternalAllocationpfnInternalFree提供的信息,你並不能做什么,除了做日志和跟蹤應用程序的內存使用量。這些函數指針是可選的,但如果你指定了一個,另外一個也需要指定。如果你不想用,把他們都設置為nullptr即可。

         Listing2.1 展示了一個如何寫C++ class作為和Vulkan內存分配回調函數匹配的allocator的例子。因為這些回調函數被Vulkan通過C函數指針調用,所以這些回調函數被聲明為該class靜態成員函數,然而,真的實現函數被聲明為非靜態成員函數。

         class allocator

{

public:

// Operator that allows an instance of this class to be used as a

// VkAllocationCallbacks structure

inline operator VkAllocationCallbacks() const

{

VkAllocationCallbacks result;

result.pUserData = (void*)this;

result.pfnAllocation = &Allocation;

result.pfnReallocation = &Reallocation;

result.pfnFree = &Free;

result.pfnInternalAllocation = nullptr;

result.pfnInternalFree = nullptr;

return result;

};

private:

// Declare the allocator callbacks as static member functions.

static void* VKAPI_CALL Allocation(

void* pUserData,

size_t size,

size_t alignment,

VkSystemAllocationScope allocationScope);

static void* VKAPI_CALL Reallocation(

void* pUserData,

void* pOriginal,

size_t size,

size_t alignment,

VkSystemAllocationScope allocationScope);

static void VKAPI_CALL Free(

void* pUserData,

void* pMemory);

// Now declare the nonstatic member functions that will actually

perform

// the allocations.

void* Allocation(

size_t size,

size_t alignment,

VkSystemAllocationScope allocationScope);

void* Reallocation(

void* pOriginal,

size_t size,

size_t alignment,

VkSystemAllocationScope allocationScope);

void Free(

void* pMemory);

};

Listing2.2 中展示了該類的實現。它把Vulkan的內存分配函數映射到符合POSIX標准的aligned_malloc函數。注意,這個allocator幾乎不會比Vulkan內部大多的默認分配器好,這只是作為一個用自己的代碼寫回調函數的例子。

void* allocator::Allocation(

size_t size,

size_t alignment,

VkSystemAllocationScope allocationScope)

{

return aligned_malloc(size, alignment);

}

 

void* VKAPI_CALL allocator::Allocation(

void* pUserData,

size_t size,

size_t alignment,

VkSystemAllocationScope allocationScope)

{

return static_cast<allocator*>(pUserData)->Allocation(size,

alignment,

allocationScope);

}

 

void* allocator::Reallocation(

void* pOriginal,

size_t size,

size_t alignment,

VkSystemAllocationScope allocationScope)

{

return aligned_realloc(pOriginal, size, alignment);

}

void* VKAPI_CALL allocator::Reallocation(

void* pUserData,

void* pOriginal,

size_t size,

size_t alignment,

VkSystemAllocationScope allocationScope)

{

return static_cast<allocator*>(pUserData)->Reallocation(pOriginal,

size,

alignment,

allocationScope);

}

 

void allocator::Free(

void* pMemory)

{

aligned_free(pMemory);

}

 

void VKAPI_CALL allocator::Free(

void* pUserData,

void* pMemory)

{

return static_cast<allocator*>(pUserData)->Free(pMemory);

}

Listing2.2中我們可以看到,靜態成員函數內部,可以簡單的把pUserData參數靜態類型轉換回該類的一個實例對象,並調用非靜態成員函數。因為非靜態和靜態函數在同一個編譯單元內,非靜態函數很有可能被內聯了,以致這種實現是很高效的。

如果有任何意見,歡迎留言討論。 


[ 主頁 ]


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com