mirror of
https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator
synced 2024-11-05 12:20:07 +00:00
Changed "CONFIGURATION SECTION" to contain #ifndef so you can define these macros before including this header, not necessarily change them in the file. (Cherry pick from v1)
This commit is contained in:
parent
b0425876ec
commit
2a22d61297
@ -122,15 +122,11 @@ When you want to create a buffer or image:
|
|||||||
|
|
||||||
\section configuration Configuration
|
\section configuration Configuration
|
||||||
|
|
||||||
Set VMA_STATS_STRING_ENABLED macro in vk_mem_alloc.h to 0 or 1 to disable/enable
|
Please check "CONFIGURATION SECTION" in the code to find macros that you can define
|
||||||
compilation of code for dumping internal allocator state to string in JSON
|
before each #include of this file or change directly in this file to provide
|
||||||
format.
|
your own implementation of basic facilities like assert, min and max functions,
|
||||||
|
mutex etc. C++ STL is used by default, but changing these allows you to get rid
|
||||||
Please check "CONFIGURATION SECTION" below to find macros and other definitions
|
of any STL usage if you want, as many game developers tend to do.
|
||||||
that you can change to connect the library to your own implementation of basic
|
|
||||||
facilities like assert, min and max functions, mutex etc. C++ STL is used by
|
|
||||||
default, but changing these allows you to get rid of any STL usage if you want,
|
|
||||||
as many game developers tend to do.
|
|
||||||
|
|
||||||
\section custom_memory_allocator Custom memory allocator
|
\section custom_memory_allocator Custom memory allocator
|
||||||
|
|
||||||
@ -685,10 +681,12 @@ void vmaDestroyImage(
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
CONFIGURATION SECTION
|
CONFIGURATION SECTION
|
||||||
|
|
||||||
Change these definitions depending on your environment.
|
Define some of these macros before each #include of this header or change them
|
||||||
|
here if you need other then default behavior depending on your environment.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define VMA_USE_STL_CONTAINERS 0
|
// Define this macro to 1 to make the library use STL containers instead of its own implementation.
|
||||||
|
//#define VMA_USE_STL_CONTAINERS 1
|
||||||
|
|
||||||
/* Set this macro to 1 to make the library including and using STL containers:
|
/* Set this macro to 1 to make the library including and using STL containers:
|
||||||
std::pair, std::vector, std::list, std::unordered_map.
|
std::pair, std::vector, std::list, std::unordered_map.
|
||||||
@ -697,21 +695,21 @@ Set it to 0 or undefined to make the library using its own implementation of
|
|||||||
the containers.
|
the containers.
|
||||||
*/
|
*/
|
||||||
#if VMA_USE_STL_CONTAINERS
|
#if VMA_USE_STL_CONTAINERS
|
||||||
#define VMA_USE_STL_VECTOR 1
|
#define VMA_USE_STL_VECTOR 1
|
||||||
#define VMA_USE_STL_UNORDERED_MAP 1
|
#define VMA_USE_STL_UNORDERED_MAP 1
|
||||||
#define VMA_USE_STL_LIST 1
|
#define VMA_USE_STL_LIST 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if VMA_USE_STL_VECTOR
|
#if VMA_USE_STL_VECTOR
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if VMA_USE_STL_UNORDERED_MAP
|
#if VMA_USE_STL_UNORDERED_MAP
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if VMA_USE_STL_LIST
|
#if VMA_USE_STL_LIST
|
||||||
#include <list>
|
#include <list>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -726,120 +724,172 @@ remove them if not needed.
|
|||||||
#include <malloc.h> // for aligned_alloc()
|
#include <malloc.h> // for aligned_alloc()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Normal assert to check for programmer's errors, especially in Debug configuration.
|
||||||
#ifdef _DEBUG
|
#ifndef VMA_ASSERT
|
||||||
// Normal assert to check for programmer's errors, especially in Debug configuration.
|
#ifdef _DEBUG
|
||||||
#define VMA_ASSERT(expr) assert(expr)
|
#define VMA_ASSERT(expr) assert(expr)
|
||||||
// Assert that will be called very often, like inside data structures e.g. operator[].
|
#else
|
||||||
// Making it non-empty can make program slow.
|
#define VMA_ASSERT(expr)
|
||||||
#define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
|
#endif
|
||||||
#else
|
|
||||||
#define VMA_ASSERT(expr)
|
|
||||||
#define VMA_HEAVY_ASSERT(expr)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
|
// Assert that will be called very often, like inside data structures e.g. operator[].
|
||||||
#define VMA_NULL nullptr
|
// Making it non-empty can make program slow.
|
||||||
|
#ifndef VMA_HEAVY_ASSERT
|
||||||
#define VMA_ALIGN_OF(type) (__alignof(type))
|
#ifdef _DEBUG
|
||||||
|
#define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
|
||||||
#if defined(_WIN32)
|
#else
|
||||||
#define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
|
#define VMA_HEAVY_ASSERT(expr)
|
||||||
#define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
|
#endif
|
||||||
#else
|
|
||||||
#define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
|
|
||||||
#define VMA_SYSTEM_FREE(ptr) free(ptr)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define VMA_MIN(v1, v2) (std::min((v1), (v2)))
|
#ifndef VMA_NULL
|
||||||
#define VMA_MAX(v1, v2) (std::max((v1), (v2)))
|
// Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
|
||||||
#define VMA_SWAP(v1, v2) std::swap((v1), (v2))
|
#define VMA_NULL nullptr
|
||||||
|
#endif
|
||||||
|
|
||||||
// You can just comment this out to use internal sorting implementation.
|
#ifndef VMA_ALIGN_OF
|
||||||
//#define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
|
#define VMA_ALIGN_OF(type) (__alignof(type))
|
||||||
|
#endif
|
||||||
|
|
||||||
#define VMA_DEBUG_LOG(format, ...)
|
#ifndef VMA_SYSTEM_ALIGNED_MALLOC
|
||||||
/*
|
#if defined(_WIN32)
|
||||||
#define VMA_DEBUG_LOG(format, ...) do { \
|
#define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
|
||||||
printf(format, __VA_ARGS__); \
|
#else
|
||||||
printf("\n"); \
|
#define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
|
||||||
} while(false)
|
#endif
|
||||||
*/
|
#endif
|
||||||
|
|
||||||
|
#ifndef VMA_SYSTEM_FREE
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
|
||||||
|
#else
|
||||||
|
#define VMA_SYSTEM_FREE(ptr) free(ptr)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef VMA_MIN
|
||||||
|
#define VMA_MIN(v1, v2) (std::min((v1), (v2)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef VMA_MAX
|
||||||
|
#define VMA_MAX(v1, v2) (std::max((v1), (v2)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef VMA_SWAP
|
||||||
|
#define VMA_SWAP(v1, v2) std::swap((v1), (v2))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef VMA_SORT
|
||||||
|
#define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef VMA_DEBUG_LOG
|
||||||
|
#define VMA_DEBUG_LOG(format, ...)
|
||||||
|
/*
|
||||||
|
#define VMA_DEBUG_LOG(format, ...) do { \
|
||||||
|
printf(format, __VA_ARGS__); \
|
||||||
|
printf("\n"); \
|
||||||
|
} while(false)
|
||||||
|
*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
|
||||||
#if VMA_STATS_STRING_ENABLED
|
#if VMA_STATS_STRING_ENABLED
|
||||||
|
static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
|
||||||
|
{
|
||||||
|
_ultoa_s(num, outStr, strLen, 10);
|
||||||
|
}
|
||||||
|
static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
|
||||||
|
{
|
||||||
|
_ui64toa_s(num, outStr, strLen, 10);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
|
#ifndef VMA_MUTEX
|
||||||
{
|
class VmaMutex
|
||||||
_ultoa_s(num, outStr, strLen, 10);
|
{
|
||||||
}
|
public:
|
||||||
static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
|
VmaMutex() { }
|
||||||
{
|
~VmaMutex() { }
|
||||||
_ui64toa_s(num, outStr, strLen, 10);
|
void Lock() { m_Mutex.lock(); }
|
||||||
}
|
void Unlock() { m_Mutex.unlock(); }
|
||||||
|
private:
|
||||||
|
std::mutex m_Mutex;
|
||||||
|
};
|
||||||
|
#define VMA_MUTEX VmaMutex
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // #if VMA_STATS_STRING_ENABLED
|
#ifndef VMA_BEST_FIT
|
||||||
|
/**
|
||||||
|
Main parameter for function assessing how good is a free suballocation for a new
|
||||||
|
allocation request.
|
||||||
|
|
||||||
class VmaMutex
|
- Set to 1 to use Best-Fit algorithm - prefer smaller blocks, as close to the
|
||||||
{
|
size of requested allocations as possible.
|
||||||
public:
|
- Set to 0 to use Worst-Fit algorithm - prefer larger blocks, as large as
|
||||||
VmaMutex() { }
|
possible.
|
||||||
~VmaMutex() { }
|
|
||||||
void Lock() { m_Mutex.lock(); }
|
|
||||||
void Unlock() { m_Mutex.unlock(); }
|
|
||||||
private:
|
|
||||||
std::mutex m_Mutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
Experiments in special testing environment showed that Best-Fit algorithm is
|
||||||
Main parameter for function assessing how good is a free suballocation for a new
|
better.
|
||||||
allocation request.
|
*/
|
||||||
|
#define VMA_BEST_FIT (1)
|
||||||
|
#endif
|
||||||
|
|
||||||
- Set to true to use Best-Fit algorithm - prefer smaller blocks, as close to the
|
#ifndef VMA_DEBUG_ALWAYS_OWN_MEMORY
|
||||||
size of requested allocations as possible.
|
/**
|
||||||
- Set to false to use Worst-Fit algorithm - prefer larger blocks, as large as
|
Every object will have its own allocation.
|
||||||
possible.
|
Define to 1 for debugging purposes only.
|
||||||
|
*/
|
||||||
|
#define VMA_DEBUG_ALWAYS_OWN_MEMORY (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
Experiments in special testing environment showed that Best-Fit algorithm is
|
#ifndef VMA_DEBUG_ALIGNMENT
|
||||||
better.
|
/**
|
||||||
*/
|
Minimum alignment of all suballocations, in bytes.
|
||||||
static const bool VMA_BEST_FIT = true;
|
Set to more than 1 for debugging purposes only. Must be power of two.
|
||||||
|
*/
|
||||||
|
#define VMA_DEBUG_ALIGNMENT (1)
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
#ifndef VMA_DEBUG_MARGIN
|
||||||
Every object will have its own allocation.
|
/**
|
||||||
Enable for debugging purposes only.
|
Minimum margin between suballocations, in bytes.
|
||||||
*/
|
Set nonzero for debugging purposes only.
|
||||||
static const bool VMA_DEBUG_ALWAYS_OWN_MEMORY = false;
|
*/
|
||||||
|
#define VMA_DEBUG_MARGIN (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
#ifndef VMA_DEBUG_GLOBAL_MUTEX
|
||||||
Minimum alignment of all suballocations, in bytes.
|
/**
|
||||||
Set to more than 1 for debugging purposes only. Must be power of two.
|
Set this to 1 for debugging purposes only, to enable single mutex protecting all
|
||||||
*/
|
entry calls to the library. Can be useful for debugging multithreading issues.
|
||||||
static const VkDeviceSize VMA_DEBUG_ALIGNMENT = 1;
|
*/
|
||||||
|
#define VMA_DEBUG_GLOBAL_MUTEX (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
#ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
|
||||||
Minimum margin between suballocations, in bytes.
|
/**
|
||||||
Set nonzero for debugging purposes only.
|
Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
|
||||||
*/
|
Set to more than 1 for debugging purposes only. Must be power of two.
|
||||||
static const VkDeviceSize VMA_DEBUG_MARGIN = 0;
|
*/
|
||||||
|
#define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
#ifndef VMA_SMALL_HEAP_MAX_SIZE
|
||||||
Set this to 1 for debugging purposes only, to enable single mutex protecting all
|
/// Maximum size of a memory heap in Vulkan to consider it "small".
|
||||||
entry calls to the library. Can be useful for debugging multithreading issues.
|
#define VMA_SMALL_HEAP_MAX_SIZE (512 * 1024 * 1024)
|
||||||
*/
|
#endif
|
||||||
#define VMA_DEBUG_GLOBAL_MUTEX 0
|
|
||||||
|
|
||||||
/*
|
#ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
|
||||||
Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
|
/// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
|
||||||
Set to more than 1 for debugging purposes only. Must be power of two.
|
#define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256 * 1024 * 1024)
|
||||||
*/
|
#endif
|
||||||
static const VkDeviceSize VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY = 1;
|
|
||||||
|
|
||||||
// Maximum size of a memory heap in Vulkan to consider it "small".
|
#ifndef VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE
|
||||||
static const VkDeviceSize VMA_SMALL_HEAP_MAX_SIZE = 512 * 1024 * 1024;
|
/// Default size of a block allocated as single VkDeviceMemory from a "small" heap.
|
||||||
// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
|
#define VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE (64 * 1024 * 1024)
|
||||||
static const VkDeviceSize VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE = 256 * 1024 * 1024;
|
#endif
|
||||||
// Default size of a block allocated as single VkDeviceMemory from a "small" heap.
|
|
||||||
static const VkDeviceSize VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE = 64 * 1024 * 1024;
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
END OF CONFIGURATION
|
END OF CONFIGURATION
|
||||||
@ -989,15 +1039,15 @@ static inline bool VmaIsBufferImageGranularityConflict(
|
|||||||
struct VmaMutexLock
|
struct VmaMutexLock
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VmaMutexLock(VmaMutex& mutex) : m_Mutex(mutex) { mutex.Lock(); }
|
VmaMutexLock(VMA_MUTEX& mutex) : m_Mutex(mutex) { mutex.Lock(); }
|
||||||
~VmaMutexLock() { m_Mutex.Unlock(); }
|
~VmaMutexLock() { m_Mutex.Unlock(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VmaMutex& m_Mutex;
|
VMA_MUTEX& m_Mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if VMA_DEBUG_GLOBAL_MUTEX
|
#if VMA_DEBUG_GLOBAL_MUTEX
|
||||||
static VmaMutex gDebugGlobalMutex;
|
static VMA_MUTEX gDebugGlobalMutex;
|
||||||
#define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex);
|
#define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex);
|
||||||
#else
|
#else
|
||||||
#define VMA_DEBUG_GLOBAL_MUTEX_LOCK
|
#define VMA_DEBUG_GLOBAL_MUTEX_LOCK
|
||||||
@ -2341,12 +2391,12 @@ struct VmaAllocator_T
|
|||||||
hysteresis to avoid pessimistic case of alternating creation and destruction
|
hysteresis to avoid pessimistic case of alternating creation and destruction
|
||||||
of a VkDeviceMemory. */
|
of a VkDeviceMemory. */
|
||||||
bool m_HasEmptyBlock[VK_MAX_MEMORY_TYPES];
|
bool m_HasEmptyBlock[VK_MAX_MEMORY_TYPES];
|
||||||
VmaMutex m_BlocksMutex[VK_MAX_MEMORY_TYPES];
|
VMA_MUTEX m_BlocksMutex[VK_MAX_MEMORY_TYPES];
|
||||||
|
|
||||||
// Each vector is sorted by memory (handle value).
|
// Each vector is sorted by memory (handle value).
|
||||||
typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
|
typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
|
||||||
AllocationVectorType* m_pOwnAllocations[VK_MAX_MEMORY_TYPES][VMA_BLOCK_VECTOR_TYPE_COUNT];
|
AllocationVectorType* m_pOwnAllocations[VK_MAX_MEMORY_TYPES][VMA_BLOCK_VECTOR_TYPE_COUNT];
|
||||||
VmaMutex m_OwnAllocationsMutex[VK_MAX_MEMORY_TYPES];
|
VMA_MUTEX m_OwnAllocationsMutex[VK_MAX_MEMORY_TYPES];
|
||||||
|
|
||||||
VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
|
VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
|
||||||
~VmaAllocator_T();
|
~VmaAllocator_T();
|
||||||
@ -2361,7 +2411,7 @@ struct VmaAllocator_T
|
|||||||
VkDeviceSize GetBufferImageGranularity() const
|
VkDeviceSize GetBufferImageGranularity() const
|
||||||
{
|
{
|
||||||
return VMA_MAX(
|
return VMA_MAX(
|
||||||
VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,
|
static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
|
||||||
m_PhysicalDeviceProperties.limits.bufferImageGranularity);
|
m_PhysicalDeviceProperties.limits.bufferImageGranularity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2914,7 +2964,7 @@ bool VmaBlock::CheckAllocation(
|
|||||||
*pOffset += VMA_DEBUG_MARGIN;
|
*pOffset += VMA_DEBUG_MARGIN;
|
||||||
|
|
||||||
// Apply alignment.
|
// Apply alignment.
|
||||||
const VkDeviceSize alignment = VMA_MAX(allocAlignment, VMA_DEBUG_ALIGNMENT);
|
const VkDeviceSize alignment = VMA_MAX(allocAlignment, static_cast<VkDeviceSize>(VMA_DEBUG_ALIGNMENT));
|
||||||
*pOffset = VmaAlignUp(*pOffset, alignment);
|
*pOffset = VmaAlignUp(*pOffset, alignment);
|
||||||
|
|
||||||
// Check previous suballocations for BufferImageGranularity conflicts.
|
// Check previous suballocations for BufferImageGranularity conflicts.
|
||||||
@ -3828,9 +3878,9 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
|
m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
|
||||||
pCreateInfo->preferredLargeHeapBlockSize : VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE;
|
pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
|
||||||
m_PreferredSmallHeapBlockSize = (pCreateInfo->preferredSmallHeapBlockSize != 0) ?
|
m_PreferredSmallHeapBlockSize = (pCreateInfo->preferredSmallHeapBlockSize != 0) ?
|
||||||
pCreateInfo->preferredSmallHeapBlockSize : VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE;
|
pCreateInfo->preferredSmallHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE);
|
||||||
|
|
||||||
vkGetPhysicalDeviceProperties(m_PhysicalDevice, &m_PhysicalDeviceProperties);
|
vkGetPhysicalDeviceProperties(m_PhysicalDevice, &m_PhysicalDeviceProperties);
|
||||||
vkGetPhysicalDeviceMemoryProperties(m_PhysicalDevice, &m_MemProps);
|
vkGetPhysicalDeviceMemoryProperties(m_PhysicalDevice, &m_MemProps);
|
||||||
|
Loading…
Reference in New Issue
Block a user