You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/RenderDll/Common/Memory/VRAMDriller.cpp

394 lines
20 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "RenderDll_precompiled.h"
#include <Common/Memory/VRAMDriller.h>
#include <AzCore/Math/Crc.h>
namespace Render
{
namespace Debug
{
//=========================================================================
struct VRAMCategoryInfo
{
VRAMAllocationCategory m_category = VRAM_CATEGORY_INVALID;
const char* m_categoryName = nullptr;
VRAMSubCategoryType m_subcategories;
};
struct VRAMAllocationInfo
{
void* m_address = nullptr;
size_t m_byteSize = 0;
string m_allocationName;
VRAMAllocationCategory m_category = VRAM_CATEGORY_INVALID;
VRAMAllocationSubcategory m_subcategory = VRAM_SUBCATEGORY_INVALID;
};
typedef AZStd::unordered_map<VRAMAllocationCategory, VRAMCategoryInfo, AZStd::hash<VRAMAllocationCategory>, AZStd::equal_to<VRAMAllocationCategory>, AZ::OSStdAllocator> VRAMCategoryType;
typedef AZStd::unordered_map<void*, VRAMAllocationInfo, AZStd::hash<void*>, AZStd::equal_to<void*>, AZ::OSStdAllocator> VRAMAllocationRecordsType;
/**
* VRAMDrillerAllocations: A class that tracks VRAM allocations and categories/subcategories for the allocations
*/
class VRAMDrillerAllocations
{
friend class VRAMDriller;
AZ_CLASS_ALLOCATOR(VRAMDrillerAllocations, AZ::OSAllocator, 0)
public:
VRAMDrillerAllocations() {}
~VRAMDrillerAllocations()
{
m_allocations.clear();
}
//=========================================================================
const VRAMCategoryInfo* RegisterCategory(VRAMAllocationCategory category, const char* categoryName, const VRAMSubCategoryType& subcategories)
{
AZ_Assert(category < VRAM_CATEGORY_INVALID, ("Error, invalid VRAM category"));
// Insert and populate the category
VRAMCategoryType::pair_iter_bool iterBool = m_categories.insert_key(category);
AZ_Assert(iterBool.second, "VRAM category %u is already registered!", category);
VRAMCategoryInfo& categoryInfo = iterBool.first->second;
categoryInfo.m_category = category;
categoryInfo.m_categoryName = categoryName;
categoryInfo.m_subcategories = subcategories;
return &categoryInfo;
}
void UnregisterAllCategories(AZ::Debug::DrillerOutputStream* output)
{
// Skip if we have no active output
if (output != nullptr)
{
for (AZStd::pair<VRAMAllocationCategory, VRAMCategoryInfo> currentCategory : m_categories)
{
output->BeginTag(AZ_CRC("VRAMDriller"));
output->BeginTag(AZ_CRC("UnregisterCategory"));
output->Write(AZ_CRC("Category"), static_cast<unsigned int>(currentCategory.first));
output->EndTag(AZ_CRC("UnregisterCategory"));
output->EndTag(AZ_CRC("VRAMDriller"));
}
}
m_categories.clear();
}
const VRAMCategoryType& GetCategoriesMap()
{
return m_categories;
}
//=========================================================================
const VRAMAllocationInfo* RegisterAllocation(void* address, size_t byteSize, const char* allocationName, VRAMAllocationCategory category, VRAMAllocationSubcategory subcategory)
{
AZ_Assert(address, ("Error, allocation address is null"));
// Insert and populate the allocation record
VRAMAllocationRecordsType::pair_iter_bool iterBool = m_allocations.insert_key(address);
// Turning off the VRAMDriller altogether causes weird allocation errors
AZ_Warning("Driller", iterBool.second, "VRAM memory address 0x%p is already allocated and being tracked! VRAM memory reporting may now be inaccurate.", address);
VRAMAllocationInfo& allocationInfo = iterBool.first->second;
allocationInfo.m_byteSize = byteSize;
allocationInfo.m_allocationName = allocationName;
allocationInfo.m_category = category;
allocationInfo.m_subcategory = subcategory;
// Update simple tracking statistics
m_simpleAllocationStatistics[category][subcategory].m_allocatedBytes += byteSize;
m_simpleAllocationStatistics[category][subcategory].m_numberAllocations++;
return &allocationInfo;
}
void UnregisterAllocation(void* address)
{
VRAMAllocationRecordsType::iterator iter = m_allocations.find(address);
// Turning off the VRAMDriller altogether causes weird allocation errors
AZ_Warning("Driller", iter != m_allocations.end(), "VRAM memory address 0x%p does not exist in the records. VRAM memory reporting may now be inaccurate.", address);
if ( iter != m_allocations.end() )
{
// Update simple tracking statistics
VRAMAllocationInfo& allocationInfo = iter->second;
m_simpleAllocationStatistics[allocationInfo.m_category][allocationInfo.m_subcategory].m_allocatedBytes -= allocationInfo.m_byteSize;
m_simpleAllocationStatistics[allocationInfo.m_category][allocationInfo.m_subcategory].m_numberAllocations--;
m_allocations.erase(iter);
}
}
const VRAMAllocationRecordsType& GetAllocationsMap()
{
return m_allocations;
}
//=========================================================================
struct SimpleAllocationStatistics
{
size_t m_allocatedBytes = 0;
size_t m_numberAllocations = 0;
};
SimpleAllocationStatistics m_simpleAllocationStatistics[VRAM_CATEGORY_NUMBER_CATEGORIES][VRAM_SUBCATEGORY_NUMBER_SUBCATEGORIES];
private:
VRAMCategoryType m_categories;
VRAMAllocationRecordsType m_allocations;
};
//=========================================================================
VRAMDriller::VRAMDriller()
{
#if PLATFORM_MEMORY_INSTRUMENTATION_ENABLED
m_platformMemoryInstrumentationRootGroupId = AZ::PlatformMemoryInstrumentation::GetNextGroupId();
AZ::PlatformMemoryInstrumentation::RegisterGroup(m_platformMemoryInstrumentationRootGroupId, "VRAM", AZ::PlatformMemoryInstrumentation::m_groupRoot);
m_platformMemoryInstrumentationCategoryIds[VRAM_CATEGORY_TEXTURE] = AZ::PlatformMemoryInstrumentation::GetNextGroupId();
AZ::PlatformMemoryInstrumentation::RegisterGroup(m_platformMemoryInstrumentationCategoryIds[VRAM_CATEGORY_TEXTURE], "Texture", m_platformMemoryInstrumentationRootGroupId);
m_platformMemoryInstrumentationCategoryIds[VRAM_CATEGORY_BUFFER] = AZ::PlatformMemoryInstrumentation::GetNextGroupId();
AZ::PlatformMemoryInstrumentation::RegisterGroup(m_platformMemoryInstrumentationCategoryIds[VRAM_CATEGORY_BUFFER], "Buffer", m_platformMemoryInstrumentationRootGroupId);
m_platformMemoryInstrumentationCategoryIds[VRAM_CATEGORY_MISC] = AZ::PlatformMemoryInstrumentation::GetNextGroupId();
AZ::PlatformMemoryInstrumentation::RegisterGroup(m_platformMemoryInstrumentationCategoryIds[VRAM_CATEGORY_MISC], "Misc", m_platformMemoryInstrumentationRootGroupId);
m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_TEXTURE_RENDERTARGET] = AZ::PlatformMemoryInstrumentation::GetNextGroupId();
AZ::PlatformMemoryInstrumentation::RegisterGroup(m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_TEXTURE_RENDERTARGET], "Render Target", m_platformMemoryInstrumentationCategoryIds[VRAM_CATEGORY_TEXTURE]);
m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_TEXTURE_TEXTURE] = AZ::PlatformMemoryInstrumentation::GetNextGroupId();
AZ::PlatformMemoryInstrumentation::RegisterGroup(m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_TEXTURE_TEXTURE], "Texture", m_platformMemoryInstrumentationCategoryIds[VRAM_CATEGORY_TEXTURE]);
m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_TEXTURE_DYNAMIC] = AZ::PlatformMemoryInstrumentation::GetNextGroupId();
AZ::PlatformMemoryInstrumentation::RegisterGroup(m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_TEXTURE_DYNAMIC], "Dynamic", m_platformMemoryInstrumentationCategoryIds[VRAM_CATEGORY_TEXTURE]);
m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_BUFFER_VERTEX_BUFFER] = AZ::PlatformMemoryInstrumentation::GetNextGroupId();
AZ::PlatformMemoryInstrumentation::RegisterGroup(m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_BUFFER_VERTEX_BUFFER], "Vertex Buffer", m_platformMemoryInstrumentationCategoryIds[VRAM_CATEGORY_BUFFER]);
m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_BUFFER_INDEX_BUFFER] = AZ::PlatformMemoryInstrumentation::GetNextGroupId();
AZ::PlatformMemoryInstrumentation::RegisterGroup(m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_BUFFER_INDEX_BUFFER], "Index Buffer", m_platformMemoryInstrumentationCategoryIds[VRAM_CATEGORY_BUFFER]);
m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_BUFFER_CONSTANT_BUFFER] = AZ::PlatformMemoryInstrumentation::GetNextGroupId();
AZ::PlatformMemoryInstrumentation::RegisterGroup(m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_BUFFER_CONSTANT_BUFFER], "Constant Buffer", m_platformMemoryInstrumentationCategoryIds[VRAM_CATEGORY_BUFFER]);
m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_BUFFER_OTHER_BUFFER] = AZ::PlatformMemoryInstrumentation::GetNextGroupId();
AZ::PlatformMemoryInstrumentation::RegisterGroup(m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_BUFFER_OTHER_BUFFER], "Other Buffer", m_platformMemoryInstrumentationCategoryIds[VRAM_CATEGORY_BUFFER]);
m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_MISC_OTHER] = AZ::PlatformMemoryInstrumentation::GetNextGroupId();
AZ::PlatformMemoryInstrumentation::RegisterGroup(m_platformMemoryInstrumentationSubcategoryIds[VRAM_SUBCATEGORY_MISC_OTHER], "Misc", m_platformMemoryInstrumentationCategoryIds[VRAM_CATEGORY_MISC]);
#endif
BusConnect();
}
VRAMDriller::~VRAMDriller()
{
BusDisconnect();
}
const char* VRAMDriller::GroupName() const
{
return "RenderingDrillers";
}
const char* VRAMDriller::GetName() const
{
return "VRAMDriller";
}
const char* VRAMDriller::GetDescription() const
{
return "Reports all VRAM memory allocations.";
}
void VRAMDriller::Start(const Param* params, int numParams)
{
(void)params;
(void)numParams;
if (m_allocations)
{
const VRAMCategoryType& categoriesMap = m_allocations->GetCategoriesMap();
for (VRAMCategoryType::const_iterator iter = categoriesMap.begin(); iter != categoriesMap.end(); ++iter)
{
RegisterCategoryOutput(iter->first, &iter->second);
}
const VRAMAllocationRecordsType& allocationMap = m_allocations->GetAllocationsMap();
for (VRAMAllocationRecordsType::const_iterator iter = allocationMap.begin(); iter != allocationMap.end(); ++iter)
{
RegisterAllocationOutput(iter->first, &iter->second);
}
}
}
void VRAMDriller::Stop()
{
}
void VRAMDriller::CreateAllocationRecords([[maybe_unused]] unsigned char stackRecordLevels, [[maybe_unused]] bool isMemoryGuard, [[maybe_unused]] bool isMarkUnallocatedMemory)
{
AZ_Assert(m_allocations == nullptr, "Allocation records for the VRAMDriller already exist");
m_allocations = aznew VRAMDrillerAllocations();
}
void VRAMDriller::DestroyAllocationRecords()
{
AZ_Assert(m_allocations != nullptr, "Allocation records for the VRAMDriller do not exist");
delete m_allocations;
m_allocations = nullptr;
}
//=========================================================================
void VRAMDriller::RegisterAllocation(void* address, size_t byteSize, const char* allocationName, VRAMAllocationCategory category, VRAMAllocationSubcategory subcategory)
{
AZ_Assert(category != VRAM_CATEGORY_INVALID, "Invalid VRAM allocation category");
AZ_Assert(subcategory != VRAM_SUBCATEGORY_INVALID, "No subcategory provided for VRAM Allocation");
#if PLATFORM_MEMORY_INSTRUMENTATION_ENABLED
AZ::PlatformMemoryInstrumentation::Alloc(address, byteSize, 0, m_platformMemoryInstrumentationSubcategoryIds[subcategory]);
#else
AZ_Assert(m_allocations != nullptr, "Allocation records for the VRAMDriller do not exist!");
const VRAMAllocationInfo* info = m_allocations->RegisterAllocation(address, byteSize, allocationName, category, subcategory);
// Skip if we have no active output
if (m_output == nullptr)
{
return;
}
RegisterAllocationOutput(address, info);
#endif
}
void VRAMDriller::RegisterAllocationOutput(void* address, const VRAMAllocationInfo* info)
{
AZ_Assert(m_output != nullptr, ("The DrillerOutputStream is null"));
m_output->BeginTag(AZ_CRC("VRAMDriller"));
m_output->BeginTag(AZ_CRC("RegisterAllocation", 0x992a9780));
m_output->Write(AZ_CRC("Address", 0x0d4e6f81), address);
m_output->Write(AZ_CRC("Category"), static_cast<unsigned int>(info->m_category));
m_output->Write(AZ_CRC("Subcategory"), static_cast<unsigned int>(info->m_subcategory));
m_output->Write(AZ_CRC("Name", 0x5e237e06), info->m_allocationName.c_str());
m_output->Write(AZ_CRC("Size", 0xf7c0246a), info->m_byteSize);
m_output->EndTag(AZ_CRC("RegisterAllocation", 0x992a9780));
m_output->EndTag(AZ_CRC("VRAMDriller"));
}
void VRAMDriller::UnregisterAllocation(void* address)
{
#if PLATFORM_MEMORY_INSTRUMENTATION_ENABLED
AZ::PlatformMemoryInstrumentation::Free(address);
#else
AZ_Assert(m_allocations != nullptr, "Allocation records for the VRAMDriller do not exist!");
m_allocations->UnregisterAllocation(address);
// Skip if the driller is not actively capturing
if (m_output == nullptr)
{
return;
}
m_output->BeginTag(AZ_CRC("VRAMDriller"));
m_output->BeginTag(AZ_CRC("UnRegisterAllocation", 0xea5dc4cd));
m_output->Write(AZ_CRC("Address", 0x0d4e6f81), address);
m_output->EndTag(AZ_CRC("UnRegisterAllocation", 0xea5dc4cd));
m_output->EndTag(AZ_CRC("VRAMDriller"));
#endif
}
//=========================================================================
void VRAMDriller::RegisterCategory(VRAMAllocationCategory category, const char* categoryName, const VRAMSubCategoryType& subcategories)
{
AZ_Assert(m_allocations != nullptr, "Allocation records for the VRAMDriller do not exist!");
AZ_Assert(category != VRAM_CATEGORY_INVALID, "Invalid VRAM allocation category");
AZ_Assert(subcategories.size(), "No subcategory provided for VRAM category");
const VRAMCategoryInfo* info = m_allocations->RegisterCategory(category, categoryName, subcategories);
// Skip if the driller is not actively capturing
if (m_output == nullptr)
{
return;
}
RegisterCategoryOutput(category, info);
}
void VRAMDriller::RegisterCategoryOutput(VRAMAllocationCategory category, const VRAMCategoryInfo* info)
{
AZ_Assert(m_output != nullptr, ("The DrillerOutputStream is null"));
AZ_Assert(info != nullptr, ("The VRAMCategoryInfo is null"));
m_output->BeginTag(AZ_CRC("VRAMDriller"));
m_output->BeginTag(AZ_CRC("RegisterCategory"));
m_output->Write(AZ_CRC("Category"), static_cast<unsigned int>(category));
m_output->Write(AZ_CRC("CategoryName"), info->m_categoryName);
for (VRAMSubCategoryType::const_iterator iter = info->m_subcategories.begin(); iter != info->m_subcategories.end(); ++iter)
{
const VRAMSubcategory* subcategoryInfo = iter;
m_output->Write(AZ_CRC("SubcategoryId"), static_cast<unsigned int>(subcategoryInfo->m_subcategoryId));
m_output->Write(AZ_CRC("SubcategoryName"), subcategoryInfo->m_subcategoryName);
}
m_output->EndTag(AZ_CRC("RegisterCategory"));
m_output->EndTag(AZ_CRC("VRAMDriller"));
}
void VRAMDriller::UnregisterAllCategories()
{
AZ_Assert(m_allocations != nullptr, "Allocation records for the VRAMDriller do not exist!");
m_allocations->UnregisterAllCategories(m_output);
}
//=========================================================================
void VRAMDriller::GetCurrentVRAMStats(VRAMAllocationCategory category, VRAMAllocationSubcategory subcategory, AZStd::string& categoryName, AZStd::string& subcategoryName, size_t& numberBytesAllocated, size_t& numberAllocations)
{
// Verify the category exists
const VRAMCategoryType& categoriesMap = m_allocations->GetCategoriesMap();
auto categoryIter = categoriesMap.find(category);
if (categoryIter != categoriesMap.end())
{
// Get the category and subcategory names
const VRAMCategoryInfo& categoryInfo = categoryIter->second;
categoryName = categoryInfo.m_categoryName;
subcategoryName = "INVALID_SUBCATEGORY";
for (int subCat=0; subCat<categoryInfo.m_subcategories.size(); ++subCat)
{
if (categoryInfo.m_subcategories[subCat].m_subcategoryId == subcategory)
{
subcategoryName = categoryInfo.m_subcategories[subCat].m_subcategoryName;
break;
}
}
// Get the basic allocation statistics
VRAMDrillerAllocations::SimpleAllocationStatistics& stats = m_allocations->m_simpleAllocationStatistics[category][subcategory];
numberBytesAllocated = stats.m_allocatedBytes;
numberAllocations = stats.m_numberAllocations;
}
}
//=========================================================================
}// namespace Debug
} // namespace Render