1. 程式人生 > >Vulkan Cookbook 第四章 6 分配記憶體物件和將其繫結到影象

Vulkan Cookbook 第四章 6 分配記憶體物件和將其繫結到影象

分配記憶體物件和將其繫結到影象

譯者注:示例程式碼點選此處

與緩衝區類似,影象不是使用繫結的記憶體儲存建立的。我們需要隱式建立一個記憶體物件並將其繫結到影象。也可以使用現有記憶體來實現此目的。

譯者注:影象不是使用繫結的記憶體儲存建立的原文are not created with a bound memory storage。這句話的意思是說,建立了影象物件但是並沒有繫結記憶體,我們需要自己去為它繫結好記憶體。

怎麼做...

1.獲取建立邏輯裝置的物理裝置控制代碼。將其儲存在名為physical_device的VkPhysicalDevice型別變數中。
2.建立名為physical_device_memory_properties的VkPhysicalDeviceMemoryProperties型別變數。
3.呼叫vkGetPhysicalDeviceMemoryProperties( physical_device, &physical_device_memory_properties ),為其提供物理裝置控制代碼和指向physical_device_memory_properties變數的指標。此呼叫將獲取物理裝置記憶體引數(堆的數量,大小和型別)。
4.獲取從物理裝置建立的邏輯裝置的控制代碼,將控制代碼儲存在名為logical_device的VkDevice型別變數中。
5.獲取由名為image的VkImage型別變量表示的已建立的影象控制代碼。
6.建立名為memory_requirements的VkMemoryRequirements型別變數。
7.獲取需要用於影象的記憶體引數。通過呼叫vkGetImageMemoryRequirements( logical_device, image, &memory_requirements )並在第一個引數中提供邏輯裝置控制代碼,在第二個引數中提供建立的影象控制代碼,以及在第三個引數中提供指向memory_requirements變數的指標來完成。
8.建立一個名為memory_object的VkDeviceMemory型別變數,它將表示為影象建立的記憶體物件,併為其分配VK_NULL_HANDLE值。
9.建立名為memory_properties的VkMemoryPropertyFlagBits型別變數。在變數中儲存其他(選定的)記憶體屬性,如果不需要其他屬性,則為0值。
10.迭代可用的物理裝置的記憶體型別。次數為physical_device_memory_properties變數的memoryTypeCount成員變數,通過使用名為type的uint32_t型別變數型別的變數來執行此操作。對於每次迭代:
    1.確保設定了memory_requirements變數的memoryTypeBits成員的位。
    2.確保memory_properties變數在physical_device_memory_properties變數中的索引型別具有與記憶體型別memoryTypes的propertyFlags成員相同的位。
    3.如果第1點和第2點不成立,則繼續迭代迴圈。
    4.建立名為image_memory_allocate_info的VkMemoryAllocateInfo型別變數,併為其成員分配以下值:
        ·sType為VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
        ·pNext為nullptr
        ·allocationSize為memory_requirements.size
        memoryTypeIndex為type
    5.呼叫vkAllocateMemory( logical_device, &image_memory_allocate_info, nullptr, &memory_object),為其提供邏輯裝置的控制代碼,指向image_memory_allocate_info變數的指標,nullptr值以及指向memory_object變數的指標。
    6.通過檢查呼叫返回的值是否等於VK_SUCCESS來確保呼叫成功,並停止迭代迴圈。
11.通過檢查memory_object變數是否不等於VK_NULL_HANDLE,確保迴圈內的記憶體物件分配成功。
12.通過呼叫vkAllocateMemory( logical_device, &image_memory_allocate_info, nullptr, &memory_object )將記憶體物件繫結到影象,為其提供邏輯裝置控制代碼,指向image_memory_allocate_info變數的指標,nullptr值以及指向memory_object變數的指標。
13.確保呼叫成功,返回值等於VK_SUCCESS。

這個怎麼運作...

與為緩衝區建立記憶體物件類似,我們首先檢查給定物理裝置上可用的記憶體型別以及他們的屬性,當然,我們可以忽略這些步驟,並在應用程式初始化階段一次性收集這些資訊:

VkPhysicalDeviceMemoryProperties physical_device_memory_properties; 
vkGetPhysicalDeviceMemoryProperties( physical_device, &physical_device_memory_properties );

接下來,我們獲取給定影象的特定記憶體要求。這些可以(也可能會)對每個影象都不同,因為它們取決於格式,大小,mipmap和圖層的數量以及影象的其他引數:

VkMemoryRequirements memory_requirements;
vkGetImageMemoryRequirements( logical_device, image, &memory_requirements );

接下來是找具有適當引數並與影象記憶體要求相容的記憶體型別:

memory_object = VK_NULL_HANDLE;
for( uint32_t type = 0; type < physical_device_memory_properties.memoryTypeCount; ++type ) {
  if( (memory_requirements.memoryTypeBits & (1 << type)) && 
      ((physical_device_memory_properties.memoryTypes[type].propertyFlags & memory_properties) == memory_properties) ) {
      
    VkMemoryAllocateInfo image_memory_allocate_info = {
      VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, 
      nullptr,
      memory_requirements.size,
      type
    };
    
    VkResult result = vkAllocateMemory( logical_device, &image_memory_allocate_info, nullptr, &memory_object );
    if( VK_SUCCESS == result ) {
      break; 
    }
  } 
}

在這裡,我們迭代所有可用的記憶體型別。如果設定了影象記憶體屬性的memoryTypeBits成員的給定位,則這意味著具有相同編號的記憶體型別與影象相容,我們可以將其用於記憶體物件。還可以檢查記憶體型別的其他屬性,找到適合我們需求的屬性。例如,如果希望使用可以對映到CPU(主機可見記憶體host-visible)的記憶體。

下一步,我們檢查迴圈內的記憶體物件分配是否成功,如果是,將建立的記憶體物件與我們的影象繫結:

if( VK_NULL_HANDLE == memory_object ) {
  std::cout << "Could not allocate memory for an image." << std::endl; 
  return false;
}

VkResult result = vkBindImageMemory( logical_device, image, memory_object, 0 ); 
if( VK_SUCCESS != result ) {
  std::cout << "Could not bind memory object to an image." << std::endl;
  return false; 
}

return true;

從現在開始,我們可以將影象用於建立過程中定義的所有用途。

還有更多...

與將記憶體物件繫結到緩衝區類似,我們應該分配更大的記憶體物件並將他們的一部分繫結到多個影象。這樣驅動程式會跟蹤更少數量的記憶體物件。這可以改善我們的應用程式效能。它還可以准許我們節省一些記憶體,因為每次分配可能需要比分配期間請求的記憶體分配更多記憶體(換句話說,其大小可能總是向上舍入到記憶體頁大小的倍數)。分配更大的記憶體物件並將他們的一部分重用與多個影象可以省去浪費的區域。