Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
+188
View File
@@ -0,0 +1,188 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/Console/IConsole.h>
#include <CryAssert.h>
namespace AZ
{
AZ_CVAR_EXTERNED(int, bg_traceLogLevel);
}
/**
* Hook Trace bus so we can funnel AZ asserts, warnings, etc to CryEngine.
*
* Note: This is currently owned by CrySystem, because CrySystem owns
* the logging mechanism for which it is relevant.
*/
class AZCoreLogSink
: public AZ::Debug::TraceMessageBus::Handler
{
public:
inline static void Connect()
{
GetInstance().m_ignoredAsserts = new IgnoredAssertMap();
GetInstance().BusConnect();
}
inline static void Disconnect()
{
GetInstance().BusDisconnect();
delete GetInstance().m_ignoredAsserts;
}
static AZCoreLogSink& GetInstance()
{
static AZCoreLogSink s_sink;
return s_sink;
}
static bool IsCryLogReady()
{
static bool hasSetCVar = false;
bool ready = gEnv && gEnv->pSystem && gEnv->pLog;
#ifdef _RELEASE
if(!hasSetCVar && ready)
{
// AZ logging only has a concept of 3 levels (error, warning, info) but cry logging has 4 levels (..., messaging). If info level is set, we'll turn on messaging as well
int logLevel = AZ::bg_traceLogLevel == AZ::Debug::LogLevel::Info ? 4 : AZ::bg_traceLogLevel;
gEnv->pConsole->GetCVar("log_WriteToFileVerbosity")->Set(logLevel);
hasSetCVar = true;
}
#endif
return ready;
}
bool OnPreAssert(const char* fileName, int line, const char* func, const char* message) override
{
#if defined(USE_CRY_ASSERT) && AZ_LEGACY_CRYSYSTEM_TRAIT_DO_PREASSERT
AZ::Crc32 crc;
crc.Add(&line, sizeof(line));
if (fileName)
{
crc.Add(fileName, strlen(fileName));
}
bool* ignore = nullptr;
auto foundIter = m_ignoredAsserts->find(crc);
if (foundIter == m_ignoredAsserts->end())
{
ignore = &((*m_ignoredAsserts)[crc]);
*ignore = false;
}
else
{
ignore = &((*m_ignoredAsserts)[crc]);
}
if (!(*ignore))
{
using namespace AZ::Debug;
Trace::Output(nullptr, "\n==================================================================\n");
AZ::OSString outputMsg = AZ::OSString::format("Trace::Assert\n %s(%d): '%s'\n%s\n", fileName, line, func, message);
Trace::Output(nullptr, outputMsg.c_str());
// Suppress 3 in stack depth - this function, the bus broadcast that got us here, and Trace::Assert
Trace::Output(nullptr, "------------------------------------------------\n");
Trace::PrintCallstack(nullptr, 3);
Trace::Output(nullptr, "\n==================================================================\n");
AZ::EnvironmentVariable<bool> inEditorBatchMode = AZ::Environment::FindVariable<bool>("InEditorBatchMode");
if (!inEditorBatchMode.IsConstructed() || !inEditorBatchMode.Get())
{
// Note - CryAssertTrace doesn't actually print any info to logging
// it just stores the message internally for the message box in CryAssert to use
CryAssertTrace("%s", message);
if (CryAssert("Assertion failed", fileName, line, ignore) || Trace::IsDebuggerPresent())
{
Trace::Break();
}
}
}
else
{
CryLogAlways("%s", message);
}
return true; // suppress default AzCore behavior.
#else
AZ_UNUSED(fileName);
AZ_UNUSED(line);
AZ_UNUSED(func);
AZ_UNUSED(message);
return false; // allow AZCore to do its default behavior. This usually results in an application shutdown.
#endif
}
bool OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message) override
{
AZ_UNUSED(fileName);
AZ_UNUSED(line);
AZ_UNUSED(func);
if (!IsCryLogReady())
{
return false; // allow AZCore to do its default behavior.
}
gEnv->pLog->LogError("(%s) - %s", window, message);
return true; // suppress default AzCore behavior.
}
bool OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message) override
{
AZ_UNUSED(fileName);
AZ_UNUSED(line);
AZ_UNUSED(func);
if (!IsCryLogReady())
{
return false; // allow AZCore to do its default behavior.
}
CryWarning(VALIDATOR_MODULE_UNKNOWN, VALIDATOR_WARNING, "(%s) - %s", window, message);
return true; // suppress default AzCore behavior.
}
bool OnOutput(const char* window, const char* message) override
{
if (!IsCryLogReady())
{
return false; // allow AZCore to do its default behavior.
}
if (window == AZ::Debug::Trace::GetDefaultSystemWindow())
{
CryLogAlways("%s", message);
}
else
{
CryLog("(%s) - %s", window, message);
}
return true; // suppress default AzCore behavior.
}
private:
using IgnoredAssertMap = AZStd::unordered_map<AZ::Crc32, bool, AZStd::hash<AZ::Crc32>, AZStd::equal_to<AZ::Crc32>, AZ::OSStdAllocator>;
IgnoredAssertMap* m_ignoredAsserts;
};
@@ -0,0 +1,53 @@
/*
* 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 "CrySystem_precompiled.h"
#include <AZCrySystemInitLogSink.h>
#include <AzCore/NativeUI/NativeUIRequests.h>
#include <AzCore/std/string/osstring.h>
#include <ISystem.h>
namespace AZ
{
namespace Debug
{
void CrySystemInitLogSink::SetFatalMessageBox(bool enable)
{
m_isMessageBoxFatal = enable;
}
void CrySystemInitLogSink::DisplayCollectedErrorStrings() const
{
if (m_errorStringsCollected.empty())
{
return;
}
AZ::OSString msgBoxMessage;
msgBoxMessage.append("CrySystem could not initialize correctly for the following reason(s):");
for (const AZ::OSString& errMsg : m_errorStringsCollected)
{
msgBoxMessage.append("\n");
msgBoxMessage.append(errMsg.c_str());
}
Trace::Output(nullptr, "\n==================================================================\n");
Trace::Output(nullptr, msgBoxMessage.c_str());
Trace::Output(nullptr, "\n==================================================================\n");
EBUS_EVENT(AZ::NativeUI::NativeUIRequestBus, DisplayOkDialog, "CrySystem Initialization Failed", msgBoxMessage.c_str(), false);
}
} // namespace Debug
} // namespace AZ
@@ -0,0 +1,49 @@
/*
* 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.
*
*/
#pragma once
#include <AzFramework/Logging/StartupLogSinkReporter.h>
namespace AZ
{
namespace Debug
{
/**
* A handler for the TraceMessageBus which is meant to collect errors and asserts during CrySystem::Init to display them to the user.
* It will also elevate all output to CryLogAlways while it is in scope.
* As such, it assumes that it is being used within the CrySystem library and gEnv and gEnv->pSystem are valid.
*/
class CrySystemInitLogSink
: public StartupLogSink
{
public:
CrySystemInitLogSink() = default;
/**
* Enables or disables the fatal flags to send to the platform specific message box
*/
void SetFatalMessageBox(bool enable = true);
/**
* Formats the collected error messages into a platform specific message box to display to the user.
* This expects that a valid gEnv->pSystem exists, and the IOSPlatform has been initialzied.
* Will also log output messages to whatever medium is possible for OnOutput messages on TraceMessageBus (ex. debug output, logs).
*/
void DisplayCollectedErrorStrings() const override;
protected:
bool m_isMessageBoxFatal = false;
};
} // namespace Debug
} // namespace AZ
@@ -0,0 +1,94 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Console implementation for Android, reports back to the main interface.
#include "CrySystem_precompiled.h"
#if defined(ANDROID)
#include "AndroidConsole.h"
#include "android/log.h"
CAndroidConsole::CAndroidConsole()
: m_isInitialized(false)
{
}
CAndroidConsole::~CAndroidConsole()
{
}
// Interface IOutputPrintSink /////////////////////////////////////////////
void CAndroidConsole::Print(const char* line)
{
__android_log_print(ANDROID_LOG_VERBOSE, "CryEngine", "MSG: %s\n", line);
}
// Interface ISystemUserCallback //////////////////////////////////////////
bool CAndroidConsole::OnError(const char* errorString)
{
__android_log_print(ANDROID_LOG_ERROR, "CryEngine", "ERR: %s\n", errorString);
return true;
}
void CAndroidConsole::OnInitProgress(const char* sProgressMsg)
{
(void) sProgressMsg;
// Do Nothing
}
void CAndroidConsole::OnInit(ISystem* pSystem)
{
if (!m_isInitialized)
{
IConsole* pConsole = pSystem->GetIConsole();
if (pConsole != 0)
{
pConsole->AddOutputPrintSink(this);
}
m_isInitialized = true;
}
}
void CAndroidConsole::OnShutdown()
{
if (m_isInitialized)
{
// remove outputprintsink
m_isInitialized = false;
}
}
void CAndroidConsole::OnUpdate()
{
// Do Nothing
}
void CAndroidConsole::GetMemoryUsage(ICrySizer* pSizer)
{
size_t size = sizeof(*this);
pSizer->AddObject(this, size);
}
// Interface ITextModeConsole /////////////////////////////////////////////
Vec2_tpl<int> CAndroidConsole::BeginDraw()
{
return Vec2_tpl<int>(0, 0);
}
void CAndroidConsole::PutText(int x, int y, const char* msg)
{
__android_log_print(ANDROID_LOG_VERBOSE, "CryEngine", "PUT: %s\n", msg);
}
void CAndroidConsole::EndDraw()
{
// Do Nothing
}
#endif // ANDROID
+63
View File
@@ -0,0 +1,63 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Console implementation for Android, reports back to the main interface.
#ifndef CRYINCLUDE_CRYSYSTEM_ANDROIDCONSOLE_H
#define CRYINCLUDE_CRYSYSTEM_ANDROIDCONSOLE_H
#pragma once
#include <IConsole.h>
#include <ITextModeConsole.h>
#include <INetwork.h>
class CAndroidConsole
: public ISystemUserCallback
, public IOutputPrintSink
, public ITextModeConsole
{
CAndroidConsole(const CAndroidConsole&);
CAndroidConsole& operator = (const CAndroidConsole&);
bool m_isInitialized;
public:
static CryCriticalSectionNonRecursive s_lock;
public:
CAndroidConsole();
~CAndroidConsole();
// Interface IOutputPrintSink /////////////////////////////////////////////
DLL_EXPORT virtual void Print(const char* line);
// Interface ISystemUserCallback //////////////////////////////////////////
virtual bool OnError(const char* errorString);
virtual bool OnSaveDocument() { return false; }
virtual void OnProcessSwitch() { }
virtual void OnInitProgress(const char* sProgressMsg);
virtual void OnInit(ISystem*);
virtual void OnShutdown();
virtual void OnUpdate();
virtual void GetMemoryUsage(ICrySizer* pSizer);
void SetRequireDedicatedServer(bool) {}
void SetHeader(const char*) {}
// Interface ITextModeConsole /////////////////////////////////////////////
virtual Vec2_tpl<int> BeginDraw();
virtual void PutText(int x, int y, const char* msg);
virtual void EndDraw();
};
#endif // CRYINCLUDE_CRYSYSTEM_ANDROIDCONSOLE_H
@@ -0,0 +1,498 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Manage async pak files
#include "CrySystem_precompiled.h"
#include "AsyncPakManager.h"
#include "System.h"
#include "IStreamEngine.h"
#include <AzFramework/Archive/Archive.h>
#include "ResourceManager.h"
#define MEGA_BYTE 1024* 1024
//////////////////////////////////////////////////////////////////////////
string& CAsyncPakManager::SAsyncPak::GetStatus(string& status) const
{
switch (eState)
{
case STATE_UNLOADED:
status = "Unloaded";
break;
case STATE_REQUESTED:
status = "Requested";
break;
case STATE_REQUESTUNLOAD:
status = "RequestUnload";
break;
case STATE_LOADED:
status = "Loaded";
break;
default:
status = "Unknown";
break;
}
return status;
}
//////////////////////////////////////////////////////////////////////////
CAsyncPakManager::CAsyncPakManager()
{
m_nTotalOpenLayerPakSize = 0;
m_bRequestLayerUpdate = false;
}
CAsyncPakManager::~CAsyncPakManager()
{
Clear();
}
//////////////////////////////////////////////////////////////////////////
void CAsyncPakManager::Clear()
{
//float startTime = gEnv->pTimer->GetAsyncCurTime();
for (TPakMap::iterator it = m_paks.begin();
it != m_paks.end(); ++it)
{
SAsyncPak& layerPak = it->second;
if (layerPak.bStreaming)
{
// wait until finished
layerPak.pReadStream->Abort();
}
ReleaseData(&layerPak);
}
m_paks.clear();
m_bRequestLayerUpdate = false;
assert(m_nTotalOpenLayerPakSize == 0);
m_nTotalOpenLayerPakSize = 0;
//printf("CAsyncPakManager::Clear() %0.4f secs\n", gEnv->pTimer->GetAsyncCurTime() - startTime);
}
void CAsyncPakManager::UnloadLevelLoadPaks()
{
for (TPakMap::iterator it = m_paks.begin();
it != m_paks.end(); ++it)
{
SAsyncPak& layerPak = it->second;
if (layerPak.eLifeTime == SAsyncPak::LIFETIME_LOAD_ONLY)
{
if (layerPak.bStreaming)
{
// wait until finished
layerPak.pReadStream->Abort();
}
ReleaseData(&layerPak);
}
}
}
//////////////////////////////////////////////////////////////////////////
void CAsyncPakManager::ParseLayerPaks(const string& levelCachePath)
{
string layerPath = levelCachePath + "/"; // "/layers/";
string search = layerPath + "*";
auto pPak = gEnv->pCryPak;
// allow this find first to actually touch the file system
AZ::IO::ArchiveFileIterator fileIterator= pPak->FindFirst(search.c_str(), 0, true);
if (fileIterator)
{
do
{
if ((fileIterator.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory || fileIterator.m_filename == "." || fileIterator.m_filename == "..")
{
continue;
}
string pakName(fileIterator.m_filename.data(), fileIterator.m_filename.size());
size_t findPos = pakName.find_last_of('.');
if (findPos == string::npos)
{
continue;
}
string extension = pakName.substr(findPos + 1, pakName.size());
if (extension != "pak")
{
continue;
}
SAsyncPak layerPak;
layerPak.layername = pakName.substr(0, findPos);
layerPak.filename = layerPath + pakName;
layerPak.nSize = pPak->FGetSize(layerPak.filename.c_str(), true); // allow to go to disc for this access
layerPak.bClosePakOnRelease = true;
m_paks[layerPak.layername] = layerPak;
} while (fileIterator = pPak->FindNext(fileIterator));
pPak->FindClose(fileIterator);
}
}
//////////////////////////////////////////////////////////////////////////
void CAsyncPakManager::StartStreaming(SAsyncPak* pLayerPak)
{
StreamReadParams params;
params.dwUserData = (DWORD_PTR) pLayerPak;
params.nSize = 0;
params.pBuffer = NULL;
params.nFlags = IStreamEngine::FLAGS_FILE_ON_DISK;
params.ePriority = estpIdle;
pLayerPak->pReadStream = gEnv->pSystem->GetStreamEngine()->StartRead(eStreamTaskTypePak, pLayerPak->filename.c_str(), this, &params);
if (pLayerPak->pReadStream)
{
pLayerPak->bStreaming = true;
}
else
{
pLayerPak->eState = SAsyncPak::STATE_UNLOADED;
pLayerPak->pData.reset();
}
}
void CAsyncPakManager::ReleaseData(SAsyncPak* pLayerPak)
{
if (pLayerPak->eState == SAsyncPak::STATE_LOADED)
{
if (pLayerPak->bClosePakOnRelease)
{
gEnv->pCryPak->ClosePack(pLayerPak->filename.c_str(), 0);
//printf("Unload pak from mem: %s\n", pLayerPak->filename.c_str());
}
else
{
gEnv->pCryPak->LoadPakToMemory(pLayerPak->filename.c_str(), AZ::IO::IArchive::eInMemoryPakLocale_Unload);
//printf("Close pak: %s\n", pLayerPak->filename.c_str());
}
m_nTotalOpenLayerPakSize -= pLayerPak->nSize;
}
if (pLayerPak->pData)
{
assert(pLayerPak->pData->use_count() == 1);
}
assert((!pLayerPak->pData) || (pLayerPak->pData && pLayerPak->pData->use_count() == 1));
pLayerPak->pData.reset();
pLayerPak->eState = SAsyncPak::STATE_UNLOADED;
m_bRequestLayerUpdate = true;
}
//////////////////////////////////////////////////////////////////////////
bool CAsyncPakManager::LoadLayerPak(const char* sLayerName)
{
// only load layer paks from valid files
TPakMap::iterator findResult = m_paks.find(sLayerName);
if (findResult != m_paks.end())
{
return LoadPak(findResult->second);
}
return false;
}
bool CAsyncPakManager::LoadPakToMemAsync(const char* pPath, bool bLevelLoadOnly)
{
//check if pak reference exists
TPakMap::iterator findResult = m_paks.find(pPath);
if (findResult != m_paks.end())
{
return LoadPak(findResult->second);
}
else
{
char szFullPathBuf[AZ::IO::IArchive::MaxPath];
const char* szFullPath = gEnv->pCryPak->AdjustFileName(pPath, szFullPathBuf, AZ_ARRAY_SIZE(szFullPathBuf), AZ::IO::IArchive::FOPEN_HINT_QUIET | AZ::IO::IArchive::FLAGS_PATH_REAL);
// Check if the pak file actually exists before trying to load
if (!gEnv->pCryPak->IsFileExist(szFullPath, AZ::IO::IArchive::eFileLocation_Any))
{
// Cached file does not exist
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Level cache pak file %s does not exist", szFullPath);
return false;
}
SAsyncPak layerPak;
layerPak.layername = pPath;
layerPak.filename = szFullPathBuf;
layerPak.nSize = 0;
layerPak.eLifeTime = bLevelLoadOnly ? SAsyncPak::LIFETIME_LOAD_ONLY : SAsyncPak::LIFETIME_LEVEL_COMPLETE;
m_paks[layerPak.layername] = layerPak;
return LoadPak(m_paks[layerPak.layername]);
}
return false;
}
bool CAsyncPakManager::LoadPak(SAsyncPak& layerPak)
{
layerPak.nRequestCount++;
if (layerPak.eState == SAsyncPak::STATE_LOADED || layerPak.bStreaming ||
layerPak.eState == SAsyncPak::STATE_REQUESTED)
{
return true;
}
layerPak.eState = SAsyncPak::STATE_REQUESTED;
//printf("Streaming level pak: %s\n", layerPak.layername.c_str());
StartStreaming(&layerPak);
return false;
}
//////////////////////////////////////////////////////////////////////////
void CAsyncPakManager::UnloadLayerPak(const char* sLayerName)
{
TPakMap::iterator findResult = m_paks.find(sLayerName);
if (findResult == m_paks.end())
{
return;
}
SAsyncPak& layerPak = findResult->second;
layerPak.nRequestCount--;
assert(layerPak.nRequestCount >= 0);
if (layerPak.nRequestCount > 0)
{
return;
}
if (layerPak.bStreaming)
{
if (layerPak.pReadStream)
{
layerPak.pReadStream->Abort();
}
layerPak.eState = SAsyncPak::STATE_REQUESTUNLOAD;
return;
}
if (layerPak.eState == SAsyncPak::STATE_LOADED)
{
ReleaseData(&layerPak);
m_bRequestLayerUpdate = true;
}
if (layerPak.eState == SAsyncPak::STATE_REQUESTED)
{
layerPak.eState = SAsyncPak::STATE_UNLOADED;
}
}
//////////////////////////////////////////////////////////////////////////
void CAsyncPakManager::GetLayerPakStats(
SLayerPakStats& stats, bool bCollectAllStats) const
{
stats.m_MaxSize = (g_cvars.archiveVars.nTotalInMemoryPakSizeLimit * MEGA_BYTE);
stats.m_UsedSize = m_nTotalOpenLayerPakSize;
for (TPakMap::const_iterator it = m_paks.begin(); it != m_paks.end(); ++it)
{
const SAsyncPak& layerPak = it->second;
if (bCollectAllStats || layerPak.eState != SAsyncPak::STATE_UNLOADED)
{
SLayerPakStats::SEntry entry;
entry.name = it->first;
entry.nSize = layerPak.nSize;
entry.bStreaming = layerPak.bStreaming;
layerPak.GetStatus(entry.status);
stats.m_entries.push_back(entry);
}
}
}
//////////////////////////////////////////////////////////////////////////
void CAsyncPakManager::StreamAsyncOnComplete(
IReadStream* pStream, unsigned nError)
{
if (nError != 0)
{
return;
}
SAsyncPak* pLayerPak = (SAsyncPak*) pStream->GetUserData();
//Check is pak is already open, if so, just assign mem
if (gEnv->pCryPak->LoadPakToMemory(pLayerPak->filename.c_str(), AZ::IO::IArchive::eInMemoryPakLocale_GPU, pLayerPak->pData))
{
pLayerPak->bPakAlreadyOpen = true;
}
else
{
//
// ugly hack - depending on the pak file pak may need special root info / open flags
//
if (pLayerPak->layername.find("level.pak") != string::npos)
{
gEnv->pCryPak->OpenPack({ pLayerPak->filename.c_str(), pLayerPak->filename.size() }, AZ::IO::IArchive::FLAGS_FILENAMES_AS_CRC32, NULL);
}
else if (pLayerPak->layername.find("levelshadercache.pak") != string::npos)
{
gEnv->pCryPak->OpenPack("@assets@", { pLayerPak->filename.c_str(), pLayerPak->filename.size() }, AZ::IO::IArchive::FLAGS_PATH_REAL, NULL);
}
else
{
gEnv->pCryPak->OpenPack("@assets@", { pLayerPak->filename.c_str(), pLayerPak->filename.size() }, AZ::IO::IArchive::FLAGS_FILENAMES_AS_CRC32, NULL);
}
gEnv->pCryPak->LoadPakToMemory(pLayerPak->filename.c_str(), AZ::IO::IArchive::eInMemoryPakLocale_GPU, pLayerPak->pData);
}
pLayerPak->eState = SAsyncPak::STATE_LOADED;
//printf("Finished streaming level pak: %s\n", pLayerPak->layername.c_str());
}
//////////////////////////////////////////////////////////////////////////
void CAsyncPakManager::StreamOnComplete(
IReadStream* pStream, unsigned nError)
{
SAsyncPak* pLayerPak = (SAsyncPak*) pStream->GetUserData();
if (nError != 0)
{
ReleaseData(pLayerPak);
}
pLayerPak->bStreaming = false;
pLayerPak->pReadStream = NULL;
m_bRequestLayerUpdate = true;
}
void* CAsyncPakManager::StreamOnNeedStorage(IReadStream* pStream, unsigned nSize, bool& bAbortOnFailToAlloc)
{
SAsyncPak* pAsyncPak = (SAsyncPak*)pStream->GetUserData();
pAsyncPak->nSize = nSize;
if ((m_nTotalOpenLayerPakSize + nSize) > (size_t)(g_cvars.archiveVars.nTotalInMemoryPakSizeLimit * MEGA_BYTE))
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Not enough space to load in memory layer pak %s (Current: %" PRISIZE_T " Required: %d)",
pAsyncPak->filename.c_str(), m_nTotalOpenLayerPakSize, nSize);
//printf("Not enough space to load in memory layer pak %s (Current: %d Required: %d)\n", pAsyncPak->filename.c_str(), m_nTotalOpenLayerPakSize, nSize);
pAsyncPak->eState = SAsyncPak::STATE_UNLOADED;
pAsyncPak->bStreaming = false;
pAsyncPak->pReadStream = NULL;
bAbortOnFailToAlloc = true;
return NULL;
}
if (nSize)
{
auto pCryPak = static_cast<AZ::IO::Archive*>(gEnv->pCryPak);
// allocate the data
const char* szUsage = "In Memory Zip File";
pAsyncPak->pData = pCryPak->PoolAllocMemoryBlock(nSize, szUsage, alignof(uint8_t));
m_nTotalOpenLayerPakSize += nSize;
return pAsyncPak->pData->m_address.get();
}
return NULL;
}
//////////////////////////////////////////////////////////////////////////
void CAsyncPakManager::Update()
{
if (!m_bRequestLayerUpdate)
{
return;
}
m_bRequestLayerUpdate = false;
for (TPakMap::iterator it = m_paks.begin();
it != m_paks.end(); ++it)
{
SAsyncPak& layerPak = it->second;
if (!layerPak.bStreaming)
{
if (layerPak.eState == SAsyncPak::STATE_REQUESTUNLOAD)
{
// done streaming and not interested in it anymore, then release it again
ReleaseData(&layerPak);
}
else if (layerPak.eState == SAsyncPak::STATE_REQUESTED &&
(m_nTotalOpenLayerPakSize + layerPak.nSize <= ((size_t)g_cvars.archiveVars.nTotalInMemoryPakSizeLimit * MEGA_BYTE)))
{
// do we have enough memory now to start streaming the pak
StartStreaming(&layerPak);
}
}
}
}
// Abort streaming jobs and prevent any more requests
// Paks which are loaded remain, they will be cleaned up as usual
void CAsyncPakManager::CancelPendingJobs()
{
for (TPakMap::iterator it = m_paks.begin(); it != m_paks.end(); ++it)
{
SAsyncPak& layerPak = it->second;
if (layerPak.bStreaming)
{
layerPak.pReadStream->Abort();
ReleaseData(&layerPak);
//printf("Pak %s Aborted\n", layerPak.filename.c_str());
}
else if (layerPak.eState == SAsyncPak::STATE_REQUESTED)
{
layerPak.eState = SAsyncPak::STATE_UNLOADED;
ReleaseData(&layerPak);
//printf("Pak %s Cancelled\n", layerPak.filename.c_str());
}
}
}
//////////////////////////////////////////////////////////////////////////
+116
View File
@@ -0,0 +1,116 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Manage async pak files
#ifndef CRYINCLUDE_CRYSYSTEM_ASYNCPAKMANAGER_H
#define CRYINCLUDE_CRYSYSTEM_ASYNCPAKMANAGER_H
#pragma once
#include <AzCore/std/smart_ptr/intrusive_ptr.h>
#include <IResourceManager.h>
#include <IStreamEngine.h>
namespace AZ::IO
{
struct MemoryBlock;
}
class CAsyncPakManager
: public IStreamCallback
{
protected:
struct SAsyncPak
{
enum EState
{
STATE_UNLOADED,
STATE_REQUESTED,
STATE_REQUESTUNLOAD,
STATE_LOADED,
};
enum ELifeTime
{
LIFETIME_LOAD_ONLY,
LIFETIME_LEVEL_COMPLETE,
LIFETIME_PERMANENT
};
SAsyncPak()
: nRequestCount(0)
, eState(STATE_UNLOADED)
, eLifeTime(LIFETIME_LOAD_ONLY)
, nSize(0)
, pData(0)
, bStreaming(false)
, bPakAlreadyOpen(false)
, bClosePakOnRelease(false)
, pReadStream(0) {}
string& GetStatus(string&) const;
string layername;
string filename;
size_t nSize;
AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pData;
EState eState;
ELifeTime eLifeTime;
bool bStreaming;
bool bPakAlreadyOpen;
bool bClosePakOnRelease;
int nRequestCount;
IReadStreamPtr pReadStream;
};
typedef std::map<string, SAsyncPak> TPakMap;
public:
CAsyncPakManager();
~CAsyncPakManager();
void ParseLayerPaks(const string& levelCachePath);
bool LoadPakToMemAsync(const char* pPath, bool bLevelLoadOnly);
void UnloadLevelLoadPaks();
bool LoadLayerPak(const char* sLayerName);
void UnloadLayerPak(const char* sLayerName);
void CancelPendingJobs();
void GetLayerPakStats(SLayerPakStats& stats, bool bCollectAllStats) const;
void Clear();
void Update();
protected:
bool LoadPak(SAsyncPak& layerPak);
void StartStreaming(SAsyncPak* pLayerPak);
void ReleaseData(SAsyncPak* pLayerPak);
//////////////////////////////////////////////////////////////////////////
// IStreamCallback interface implementation.
//////////////////////////////////////////////////////////////////////////
virtual void StreamAsyncOnComplete (IReadStream* pStream, unsigned nError);
virtual void StreamOnComplete (IReadStream* pStream, unsigned nError);
virtual void* StreamOnNeedStorage(IReadStream* pStream, unsigned nSize, bool& bAbortOnFailToAlloc);
//////////////////////////////////////////////////////////////////////////
TPakMap m_paks;
size_t m_nTotalOpenLayerPakSize;
bool m_bRequestLayerUpdate;
};
#endif // CRYINCLUDE_CRYSYSTEM_ASYNCPAKMANAGER_H
File diff suppressed because it is too large Load Diff
+54
View File
@@ -0,0 +1,54 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_AUTODETECTSPEC_H
#define CRYINCLUDE_CRYSYSTEM_AUTODETECTSPEC_H
#pragma once
#if defined(WIN32) || defined(WIN64)
// exposed AutoDetectSpec() helper functions for reuse in CrySystem
namespace Win32SysInspect
{
enum DXFeatureLevel
{
DXFL_Undefined,
DXFL_9_1,
DXFL_9_2,
DXFL_9_3,
DXFL_10_0,
DXFL_10_1,
DXFL_11_0
};
const char* GetFeatureLevelAsString(DXFeatureLevel featureLevel);
void GetNumCPUCores(unsigned int& totAvailToSystem, unsigned int& totAvailToProcess);
bool IsDX11Supported();
bool GetGPUInfo(char* pName, size_t bufferSize, unsigned int& vendorID, unsigned int& deviceID, unsigned int& totLocalVidMem, DXFeatureLevel& featureLevel);
int GetGPURating(unsigned int vendorId, unsigned int deviceId);
void GetOS(SPlatformInfo::EWinVersion& ver, bool& is64Bit, char* pName, size_t bufferSize);
bool IsVistaKB940105Required();
inline size_t SafeMemoryThreshold(size_t memMB)
{
return (memMB * 8) / 10;
}
}
#endif // #if defined(WIN32) || defined(WIN64)
#endif // CRYINCLUDE_CRYSYSTEM_AUTODETECTSPEC_H
+630
View File
@@ -0,0 +1,630 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#if defined(ENABLE_LOADING_PROFILER)
#include "BootProfiler.h"
#include "ThreadInfo.h"
#include <stack>
#include <AzFramework/IO/FileOperations.h>
namespace
{
StaticInstance<CBootProfiler, AZStd::no_destruct<CBootProfiler>> gProfilerInstance;
enum
{
eMAX_THREADS_TO_PROFILE = 128,
eNUM_RECORDS_PER_POOL = 2048, // so, eNUM_RECORDS_PER_POOL * sizeof(CBootProfilerRecord) == mem consumed by pool item
// sizeof(CProfileBlockTimes)==152,
// poolmem = 304Kb for 1 pool per thread
};
}
int CBootProfiler::CV_sys_bp_frames = 0;
float CBootProfiler::CV_sys_bp_time_threshold = 0;
class CProfileBlockTimes
{
protected:
LARGE_INTEGER m_startTimeStamp;
LARGE_INTEGER m_stopTimeStamp;
LARGE_INTEGER m_freq;
CProfileBlockTimes()
{
memset(&m_startTimeStamp, 0, sizeof(m_startTimeStamp));
memset(&m_stopTimeStamp, 0, sizeof(m_stopTimeStamp));
memset(&m_freq, 0, sizeof(m_freq));
}
};
class CBootProfilerRecord
{
public:
const char* m_label;
LARGE_INTEGER m_startTimeStamp;
LARGE_INTEGER m_stopTimeStamp;
LARGE_INTEGER m_freq;
CBootProfilerRecord* m_pParent;
typedef AZStd::vector<CBootProfilerRecord*> ChildVector;
ChildVector m_Childs;
CryFixedStringT<256> m_args;
ILINE CBootProfilerRecord(const char* label, LARGE_INTEGER timestamp, LARGE_INTEGER freq, const char* args)
: m_label(label)
, m_startTimeStamp(timestamp)
, m_freq(freq)
, m_pParent(NULL)
{
memset(&m_stopTimeStamp, 0, sizeof(m_stopTimeStamp));
if (args)
{
m_args = args;
}
}
ILINE ~CBootProfilerRecord()
{
// childs are allocated via pool as well, the destructors of each child
// is called explicitly, for the purpose of freeing memory occupied by
// m_Child vector. Otherwise there will be a memory leak.
ChildVector::iterator it = m_Childs.begin();
while (it != m_Childs.end())
{
(*it)->~CBootProfilerRecord();
++it;
}
}
void Print(AZ::IO::HandleType fileHandle, char* buf, size_t buf_size, size_t depth, LARGE_INTEGER stopTime, const char* threadName, const float timeThreshold)
{
if (m_stopTimeStamp.QuadPart == 0)
{
m_stopTimeStamp = stopTime;
}
const float time = (float)(m_stopTimeStamp.QuadPart - m_startTimeStamp.QuadPart) * 1000.f / (float)m_freq.QuadPart;
if (timeThreshold > 0.0f && time < timeThreshold)
{
return;
}
string tabs; //tabs(depth++, '\t')
tabs.insert(0, depth++, '\t');
{
string label = m_label;
label.replace("&", "&amp;");
label.replace("<", "&lt;");
label.replace(">", "&gt;");
label.replace("\"", "&quot;");
label.replace("'", "&apos;");
if (m_args.size() > 0)
{
m_args.replace("&", "&amp;");
m_args.replace("<", "&lt;");
m_args.replace(">", "&gt;");
m_args.replace("\"", "&quot;");
m_args.replace("'", "&apos;");
m_args.replace("%", "&#37;");
}
sprintf_s(buf, buf_size, "%s<block name=\"%s\" totalTimeMS=\"%f\" startTime=\"%" PRIu64 "\" stopTime=\"%" PRIu64 "\" args=\"%s\"> \n",
tabs.c_str(), label.c_str(), time, m_startTimeStamp.QuadPart, m_stopTimeStamp.QuadPart, m_args.c_str());
AZ::IO::Print(fileHandle, buf);
}
const size_t childsSize = m_Childs.size();
for (size_t i = 0; i < childsSize; ++i)
{
CBootProfilerRecord* record = m_Childs[i];
assert(record);
record->Print(fileHandle, buf, buf_size, depth, stopTime, threadName, timeThreshold);
}
sprintf_s(buf, buf_size, "%s</block>\n", tabs.c_str());
AZ::IO::Print(fileHandle, buf);
}
};
//////////////////////////////////////////////////////////////////////////
class CProfileInfo
{
friend class CBootProfilerSession;
private:
CBootProfilerRecord* m_pRoot;
CBootProfilerRecord* m_pCurrent;
public:
CProfileInfo()
: m_pRoot(NULL)
, m_pCurrent(NULL) {}
};
class CBootProfilerThreadsInterface
{
protected:
CBootProfilerThreadsInterface()
{
memset(m_threadInfo, 0, sizeof(m_threadInfo));
m_threadCounter = 0;
}
unsigned int GetThreadIndexByID(unsigned int threadID);
const char* GetThreadNameByIndex(unsigned int threadIndex);
int m_threadCounter;
private:
unsigned int m_threadInfo[eMAX_THREADS_TO_PROFILE]; //threadIDs
};
//////////////////////////////////////////////////////////////////////////
ILINE unsigned int CBootProfilerThreadsInterface::GetThreadIndexByID(unsigned int threadID)
{
for (int i = 0; i < eMAX_THREADS_TO_PROFILE; ++i)
{
if (m_threadInfo[i] == 0)
{
break;
}
if (m_threadInfo[i] == threadID)
{
return i;
}
}
unsigned int counter = CryInterlockedIncrement(&m_threadCounter) - 1; //count to index
m_threadInfo[counter] = threadID;
return counter;
}
ILINE const char* CBootProfilerThreadsInterface::GetThreadNameByIndex(unsigned int threadIndex)
{
assert(threadIndex < m_threadCounter);
const char* threadName = CryThreadGetName(m_threadInfo[threadIndex]);
return threadName;
}
class CRecordPool
{
public:
CRecordPool()
: m_baseAddr(NULL)
, m_allocCounter(0)
, m_next(NULL)
{
m_baseAddr = (CBootProfilerRecord*)CryModuleMemalign(eNUM_RECORDS_PER_POOL * sizeof(CBootProfilerRecord), 16);
}
~CRecordPool()
{
CryModuleMemalignFree(m_baseAddr);
delete m_next;
}
ILINE CBootProfilerRecord* allocateRecord()
{
if (m_allocCounter < eNUM_RECORDS_PER_POOL)
{
CBootProfilerRecord* newRecord = m_baseAddr + m_allocCounter;
++m_allocCounter;
return newRecord;
}
else
{
return NULL;
}
}
ILINE void setNextPool(CRecordPool* pool) { m_next = pool; }
private:
CBootProfilerRecord* m_baseAddr;
uint32 m_allocCounter;
CRecordPool* m_next;
};
class CBootProfilerSession
: public CBootProfilerThreadsInterface
, protected CProfileBlockTimes
{
public:
CBootProfilerSession();
~CBootProfilerSession();
void Start();
void Stop();
CBootProfilerRecord* StartBlock(const char* name, const char* args);
void StopBlock(CBootProfilerRecord* record);
void CollectResults(const char* filename, const float timeThreshold);
private:
string m_name;
CProfileInfo m_threadsProfileInfo[eMAX_THREADS_TO_PROFILE];
CRecordPool* m_threadsRecordsPool[eMAX_THREADS_TO_PROFILE]; //head
CRecordPool* m_threadsCurrentPools[eMAX_THREADS_TO_PROFILE]; //current
};
//////////////////////////////////////////////////////////////////////////
CBootProfilerSession::CBootProfilerSession()
{
memset(m_threadsProfileInfo, 0, sizeof(m_threadsProfileInfo));
memset(m_threadsRecordsPool, 0, sizeof(m_threadsRecordsPool));
memset(m_threadsCurrentPools, 0, sizeof(m_threadsCurrentPools));
}
CBootProfilerSession::~CBootProfilerSession()
{
for (unsigned int i = 0; i < m_threadCounter; ++i)
{
CProfileInfo& profile = m_threadsProfileInfo[i];
// Since m_pRoot is allocated using memory pool (line 296),
// its destructor is called explicitly to free the memory of
// m_Childs and each of its child.
if (profile.m_pRoot)
{
profile.m_pRoot->~CBootProfilerRecord();
}
delete m_threadsRecordsPool[i];
}
}
void CBootProfilerSession::Start()
{
LARGE_INTEGER time, freq;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&time);
m_startTimeStamp = time;
m_freq = freq;
}
void CBootProfilerSession::Stop()
{
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
m_stopTimeStamp = time;
}
CBootProfilerRecord* CBootProfilerSession::StartBlock(const char* name, const char* args)
{
const unsigned int curThread = CryGetCurrentThreadId();
const unsigned int threadIndex = GetThreadIndexByID(curThread);
assert(threadIndex < eMAX_THREADS_TO_PROFILE);
CProfileInfo& profile = m_threadsProfileInfo[threadIndex];
CRecordPool* pool = m_threadsCurrentPools[threadIndex];
if (!profile.m_pRoot)
{
if (!pool)
{
pool = new CRecordPool;
m_threadsRecordsPool[threadIndex] = pool;
m_threadsCurrentPools[threadIndex] = pool;
}
CBootProfilerRecord* rec = pool->allocateRecord();
profile.m_pRoot = profile.m_pCurrent = new(rec)CBootProfilerRecord("root", m_startTimeStamp, m_freq, args);
}
assert(pool);
LARGE_INTEGER time, freq;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&time);
CBootProfilerRecord* pParent = profile.m_pCurrent;
assert(pParent);
assert(profile.m_pRoot);
CBootProfilerRecord* rec = pool->allocateRecord();
if (!rec)
{
//pool is full, create a new one
pool = new CRecordPool;
m_threadsCurrentPools[threadIndex]->setNextPool(pool);
m_threadsCurrentPools[threadIndex] = pool;
rec = pool->allocateRecord();
}
profile.m_pCurrent = new(rec)CBootProfilerRecord(name, time, freq, args);
profile.m_pCurrent->m_pParent = pParent;
pParent->m_Childs.push_back(profile.m_pCurrent);
return profile.m_pCurrent;
}
void CBootProfilerSession::StopBlock(CBootProfilerRecord* record)
{
if (record)
{
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
record->m_stopTimeStamp = time;
unsigned int curThread = CryGetCurrentThreadId();
unsigned int threadIndex = GetThreadIndexByID(curThread);
assert(threadIndex < eMAX_THREADS_TO_PROFILE);
CProfileInfo& profile = m_threadsProfileInfo[threadIndex];
profile.m_pCurrent = record->m_pParent;
}
}
void CBootProfilerSession::CollectResults(const char* filename, const float timeThreshold)
{
if (!gEnv || !gEnv->pCryPak)
{
AZ_Warning("BootProfiler", false, "CryPak not set - skipping CollectResults");
return;
}
static const char* szTestResults = "@cache@\\TestResults";
string filePath = string(szTestResults) + "\\" + "bp_" + filename + ".xml";
char path[AZ::IO::IArchive::MaxPath] = "";
gEnv->pCryPak->AdjustFileName(filePath.c_str(), path, AZ_ARRAY_SIZE(path), AZ::IO::IArchive::FLAGS_PATH_REAL | AZ::IO::IArchive::FLAGS_FOR_WRITING);
gEnv->pCryPak->MakeDir(szTestResults);
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
gEnv->pFileIO->Open(path, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary, fileHandle);
if (fileHandle == AZ::IO::InvalidHandle)
{
return;
}
char buf[512];
const unsigned int buf_size = sizeof(buf);
sprintf_s(buf, buf_size, "<root>\n");
AZ::IO::Print(fileHandle, buf);
const size_t numThreads = m_threadCounter;
for (size_t i = 0; i < numThreads; ++i)
{
CBootProfilerRecord* pRoot = m_threadsProfileInfo[i].m_pRoot;
if (pRoot)
{
pRoot->m_stopTimeStamp = m_stopTimeStamp;
const char* threadName = GetThreadNameByIndex(i);
if (!threadName)
{
threadName = "UNKNOWN";
}
const float time = (float)(pRoot->m_stopTimeStamp.QuadPart - pRoot->m_startTimeStamp.QuadPart) * 1000.f / (float)pRoot->m_freq.QuadPart;
sprintf_s(buf, buf_size, "\t<thread name=\"%s\" totalTimeMS=\"%f\" startTime=\"%" PRIu64 "\" stopTime=\"%" PRIu64 "\" > \n", threadName, time,
pRoot->m_startTimeStamp.QuadPart, pRoot->m_stopTimeStamp.QuadPart);
AZ::IO::Print(fileHandle, buf);
for (size_t recordIdx = 0; recordIdx < pRoot->m_Childs.size(); ++recordIdx)
{
CBootProfilerRecord* record = pRoot->m_Childs[recordIdx];
assert(record);
record->Print(fileHandle, buf, buf_size, 2, m_stopTimeStamp, threadName, timeThreshold);
}
sprintf_s(buf, buf_size, "\t</thread>\n");
AZ::IO::Print(fileHandle, buf);
}
}
sprintf_s(buf, buf_size, "</root>\n");
AZ::IO::Print(fileHandle, buf);
gEnv->pFileIO->Close(fileHandle);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
CBootProfiler& CBootProfiler::GetInstance()
{
return gProfilerInstance;
}
CBootProfiler::CBootProfiler()
: m_pCurrentSession(NULL)
, m_pFrameRecord(NULL)
, m_levelLoadAdditionalFrames(0)
{
}
CBootProfiler::~CBootProfiler()
{
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
for (TSessionMap::iterator it = m_sessions.begin(); it != m_sessions.end(); ++it)
{
CBootProfilerSession* session = it->second;
delete session;
}
}
// start session
void CBootProfiler::StartSession(const char* sessionName)
{
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
TSessionMap::const_iterator it = m_sessions.find(sessionName);
if (it == m_sessions.end())
{
m_pCurrentSession = new CBootProfilerSession();
m_sessions[sessionName] = m_pCurrentSession;
m_pCurrentSession->Start();
}
}
// stop session
void CBootProfiler::StopSession(const char* sessionName)
{
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
if (m_pCurrentSession)
{
TSessionMap::iterator it = m_sessions.find(sessionName);
if (it != m_sessions.end())
{
if (m_pCurrentSession == it->second)
{
CBootProfilerSession* session = m_pCurrentSession;
m_pCurrentSession = NULL;
session->Stop();
session->CollectResults(sessionName, CV_sys_bp_time_threshold);
delete session;
}
m_sessions.erase(it);
}
}
}
CBootProfilerRecord* CBootProfiler::StartBlock(const char* name, const char* args)
{
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
if (m_pCurrentSession)
{
return m_pCurrentSession->StartBlock(name, args);
}
return NULL;
}
void CBootProfiler::StopBlock(CBootProfilerRecord* record)
{
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
if (m_pCurrentSession)
{
m_pCurrentSession->StopBlock(record);
}
}
void CBootProfiler::StartFrame(const char* name)
{
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
if (CV_sys_bp_frames)
{
StartSession("frames");
m_pFrameRecord = StartBlock(name, NULL);
}
}
void CBootProfiler::StopFrame()
{
AZStd::lock_guard<AZStd::recursive_mutex> recordGuard{ m_recordMutex };
if (m_pCurrentSession && CV_sys_bp_frames)
{
StopBlock(m_pFrameRecord);
m_pFrameRecord = NULL;
--CV_sys_bp_frames;
if (0 == CV_sys_bp_frames)
{
StopSession("frames");
}
}
if (m_pCurrentSession && m_levelLoadAdditionalFrames)
{
--m_levelLoadAdditionalFrames;
if (0 == m_levelLoadAdditionalFrames)
{
StopSession("level");
}
}
}
void CBootProfiler::Init(ISystem* pSystem)
{
//REGISTER_CVAR(sys_BootProfiler, 1, VF_DEV_ONLY,
// "Collect and output session statistics into TestResults/bp_(session_name).xml \n"
// "0 = Disabled\n"
// "1 = Enabled\n");
pSystem->GetISystemEventDispatcher()->RegisterListener(this);
StartSession("boot");
}
void CBootProfiler::RegisterCVars()
{
REGISTER_CVAR2("sys_bp_frames", &CV_sys_bp_frames, 0, VF_DEV_ONLY, "Starts frame profiling for specified number of frames using BootProfiler");
REGISTER_CVAR2("sys_bp_time_threshold", &CV_sys_bp_time_threshold, 0.1f, VF_DEV_ONLY, "If greater than 0 don't write blocks that took less time (default 0.1 ms)");
}
void CBootProfiler::OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam)
{
switch (event)
{
case ESYSTEM_EVENT_GAME_POST_INIT_DONE:
{
StopSession("boot");
break;
}
case ESYSTEM_EVENT_GAME_MODE_SWITCH_START:
{
break;
}
case ESYSTEM_EVENT_GAME_MODE_SWITCH_END:
{
break;
}
case ESYSTEM_EVENT_LEVEL_LOAD_START:
{
break;
}
case ESYSTEM_EVENT_LEVEL_LOAD_PREPARE:
{
StartSession("level");
break;
}
case ESYSTEM_EVENT_LEVEL_LOAD_END:
{
StopSession("level");
break;
}
case ESYSTEM_EVENT_LEVEL_PRECACHE_END:
{
//level loading can be stopped here, or m_levelLoadAdditionalFrames can be used to prolong dump for this amount of frames
//StopSession("level");
m_levelLoadAdditionalFrames = 20;
break;
}
}
}
void CBootProfiler::SetFrameCount(int frameCount)
{
CV_sys_bp_frames = frameCount;
}
#endif
+67
View File
@@ -0,0 +1,67 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_BOOTPROFILER_H
#define CRYINCLUDE_CRYSYSTEM_BOOTPROFILER_H
#pragma once
#if defined(ENABLE_LOADING_PROFILER)
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/parallel/mutex.h>
class CBootProfilerRecord;
class CBootProfilerSession;
class CBootProfiler
: public ISystemEventListener
{
friend class CBootProfileBLock;
public:
CBootProfiler();
~CBootProfiler();
static CBootProfiler& GetInstance();
void Init(ISystem* pSystem);
void RegisterCVars();
void StartSession(const char* sessionName);
void StopSession(const char* sessionName);
CBootProfilerRecord* StartBlock(const char* name, const char* args);
void StopBlock(CBootProfilerRecord* record);
void StartFrame(const char* name);
void StopFrame();
protected:
// === ISystemEventListener
virtual void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam);
void SetFrameCount(int frameCount);
private:
CBootProfilerSession* m_pCurrentSession;
typedef AZStd::unordered_map<AZStd::string, CBootProfilerSession*> TSessionMap;
TSessionMap m_sessions;
static int CV_sys_bp_frames;
static float CV_sys_bp_time_threshold;
CBootProfilerRecord* m_pFrameRecord;
AZStd::recursive_mutex m_recordMutex;
int m_levelLoadAdditionalFrames;
};
#endif
#endif // CRYINCLUDE_CRYSYSTEM_BOOTPROFILER_H
+127
View File
@@ -0,0 +1,127 @@
#
# 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.
#
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
ly_get_list_relative_pal_filename(common_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/Common)
add_subdirectory(XML)
# The following target is a 'C' file only library to work around an issue in cmake and VS generators that
# will append 'std=c++17' to both C and C++ compiler flags for clang. Do not add any .cpp files to this
# library.
ly_add_target(
NAME CrySystem.DLMalloc.C STATIC
NAMESPACE Legacy
FILES_CMAKE
crysystem_dlmalloc_files.cmake
INCLUDE_DIRECTORIES
PUBLIC
.
PRIVATE
${pal_dir}
)
ly_get_pal_tool_dirs(pal_tool_dirs ${CMAKE_CURRENT_LIST_DIR}/Platform)
ly_add_target(
NAME CrySystem.Static STATIC
NAMESPACE Legacy
FILES_CMAKE
crysystem_files.cmake
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
PLATFORM_INCLUDE_FILES
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
INCLUDE_DIRECTORIES
PUBLIC
.
${pal_dir}
PRIVATE
${common_dir}
${pal_tool_dirs}
BUILD_DEPENDENCIES
PUBLIC
Legacy::CrySystem.DLMalloc.C
PRIVATE
3rdParty::expat
3rdParty::LibTomCrypt
3rdParty::LibTomMath
3rdParty::lz4
3rdParty::md5
3rdParty::tiff
3rdParty::zlib
3rdParty::zstd
Legacy::CryCommon
Legacy::CrySystem.XMLBinary
Legacy::RemoteConsoleCore
AZ::AzFramework
RUNTIME_DEPENDENCIES
Legacy::Cry3DEngine
Legacy::CryNetwork
)
ly_add_source_properties(
SOURCES SystemInit.cpp
PROPERTY COMPILE_DEFINITIONS
VALUES ${LY_PAL_TOOLS_DEFINES}
)
ly_add_source_properties(
SOURCES SystemCFG.cpp
PROPERTY COMPILE_DEFINITIONS
VALUES LY_BUILD=${LY_VERSION_BUILD_NUMBER}
)
ly_add_target(
NAME CrySystem ${PAL_TRAIT_MONOLITHIC_DRIVEN_LIBRARY_TYPE}
NAMESPACE Legacy
FILES_CMAKE
crysystem_shared_files.cmake
PLATFORM_INCLUDE_FILES
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
INCLUDE_DIRECTORIES
PUBLIC
.
BUILD_DEPENDENCIES
PRIVATE
Legacy::CrySystem.Static
AZ::AzCore
Legacy::CryCommon
Legacy::CryCommon.EngineSettings.Static
RUNTIME_DEPENDENCIES
Legacy::CryFont
)
################################################################################
# Tests
################################################################################
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_target(
NAME CrySystem.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE Legacy
FILES_CMAKE
crysystem_test_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
Legacy::CryCommon
Legacy::CryCommon.EngineSettings.Static
Legacy::CrySystem.Static
AZ::AzFramework
)
ly_add_googletest(
NAME Legacy::CrySystem.Tests
)
endif()
File diff suppressed because it is too large Load Diff
+180
View File
@@ -0,0 +1,180 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_CPUDETECT_H
#define CRYINCLUDE_CRYSYSTEM_CPUDETECT_H
#pragma once
//-------------------------------------------------------
/// Cpu class
//-------------------------------------------------------
#if defined(WIN64) || defined(LINUX)
#define MAX_CPU 96
#else
#define MAX_CPU 32
#endif
/// Cpu Features
#define CFI_FPUEMULATION 0x01
#define CFI_MMX 0x02
#define CFI_3DNOW 0x04
#define CFI_SSE 0x08
#define CFI_SSE2 0x10
#define CFI_SSE3 0x20
#define CFI_F16C 0x40
#define CFI_SSE41 0x80
/// Type of Cpu Vendor.
enum ECpuVendor
{
eCVendor_Unknown,
eCVendor_Intel,
eCVendor_Cyrix,
eCVendor_AMD,
eCVendor_Centaur,
eCVendor_NexGen,
eCVendor_UMC,
eCVendor_M68K
};
/// Type of Cpu Model.
enum ECpuModel
{
eCpu_Unknown,
eCpu_8086,
eCpu_80286,
eCpu_80386,
eCpu_80486,
eCpu_Pentium,
eCpu_PentiumPro,
eCpu_Pentium2,
eCpu_Pentium3,
eCpu_Pentium4,
eCpu_Pentium2Xeon,
eCpu_Pentium3Xeon,
eCpu_Celeron,
eCpu_CeleronA,
eCpu_Am5x86,
eCpu_AmK5,
eCpu_AmK6,
eCpu_AmK6_2,
eCpu_AmK6_3,
eCpu_AmK6_3D,
eCpu_AmAthlon,
eCpu_AmDuron,
eCpu_CyrixMediaGX,
eCpu_Cyrix6x86,
eCpu_CyrixGXm,
eCpu_Cyrix6x86MX,
eCpu_CenWinChip,
eCpu_CenWinChip2,
};
struct SCpu
{
ECpuVendor meVendor;
ECpuModel meModel;
unsigned long mFeatures;
bool mbSerialPresent;
char mSerialNumber[30];
int mFamily;
int mModel;
int mStepping;
char mVendor[64];
char mCpuType[64];
char mFpuType[64];
bool mbPhysical; // false for hyperthreaded
DWORD_PTR mAffinityMask;
// constructor
SCpu()
: meVendor(eCVendor_Unknown)
, meModel(eCpu_Unknown)
, mFeatures(0)
, mbSerialPresent(false)
, mFamily(0)
, mModel(0)
, mStepping(0)
, mbPhysical(true)
, mAffinityMask(0)
{
memset(mSerialNumber, 0, sizeof(mSerialNumber));
memset(mVendor, 0, sizeof(mVendor));
memset(mCpuType, 0, sizeof(mCpuType));
memset(mFpuType, 0, sizeof(mFpuType));
}
};
class CCpuFeatures
{
private:
int m_NumLogicalProcessors;
int m_NumSystemProcessors;
int m_NumAvailProcessors;
int m_NumPhysicsProcessors;
bool m_bOS_ISSE;
bool m_bOS_ISSE_EXCEPTIONS;
public:
SCpu m_Cpu[MAX_CPU];
public:
CCpuFeatures()
{
m_NumLogicalProcessors = 0;
m_NumSystemProcessors = 0;
m_NumAvailProcessors = 0;
m_NumPhysicsProcessors = 0;
m_bOS_ISSE = 0;
m_bOS_ISSE_EXCEPTIONS = 0;
ZeroMemory(m_Cpu, sizeof(m_Cpu));
}
void Detect(void);
bool hasSSE() { return (m_Cpu[0].mFeatures & CFI_SSE) != 0; }
bool hasSSE2() { return (m_Cpu[0].mFeatures & CFI_SSE2) != 0; }
bool hasSSE3() { return (m_Cpu[0].mFeatures & CFI_SSE3) != 0; }
bool hasSSE41() { return (m_Cpu[0].mFeatures & CFI_SSE41) != 0; }
bool has3DNow() { return (m_Cpu[0].mFeatures & CFI_3DNOW) != 0; }
bool hasMMX() { return (m_Cpu[0].mFeatures & CFI_MMX) != 0; }
bool hasF16C() { return (m_Cpu[0].mFeatures & CFI_F16C) != 0; }
unsigned int GetLogicalCPUCount() { return m_NumLogicalProcessors; }
unsigned int GetPhysCPUCount() { return m_NumPhysicsProcessors; }
unsigned int GetCPUCount() { return m_NumAvailProcessors; }
DWORD_PTR GetCPUAffinityMask(unsigned int iCPU) { assert(iCPU < MAX_CPU); return iCPU < GetCPUCount() ? m_Cpu[iCPU].mAffinityMask : 0; }
DWORD_PTR GetPhysCPUAffinityMask(unsigned int iCPU)
{
if (iCPU > GetPhysCPUCount())
{
return 0;
}
int i;
for (i = 0; (int)iCPU >= 0; i++)
{
if (m_Cpu[i].mbPhysical)
{
--iCPU;
}
}
PREFAST_ASSUME(i > 0 && i < MAX_CPU);
return m_Cpu[i - 1].mAffinityMask;
}
};
#endif // CRYINCLUDE_CRYSYSTEM_CPUDETECT_H
@@ -0,0 +1,90 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "ProjectDefines.h"
#if defined(MAP_LOADING_SLICING)
#include "ClientHandler.h"
ClientHandler::ClientHandler(const char* bucket, int affinity, int clientTimeout)
: HandlerBase(bucket, affinity)
{
m_clientTimeout = clientTimeout;
Reset();
}
void ClientHandler::Reset()
{
m_srvLock.reset(0);
for (int i = 0; i < MAX_CLIENTS_NUM; i++)
{
std::unique_ptr<SSyncLock> srv(new SSyncLock(m_serverLockName, i, false));
// first get the client lock up!
if (!srv->IsValid())
{
//try to create client lock
m_clientLock.reset(new SSyncLock(m_clientLockName, i, true));
if (m_clientLock->IsValid())
{
break;
}
else
{
m_clientLock.reset(0);
}
}
}
}
bool ClientHandler::ServerIsValid()
{
if (!m_srvLock.get())
{
if (m_clientLock.get() && m_clientLock->IsValid())
{
m_srvLock.reset(new SSyncLock(m_serverLockName, m_clientLock->number, false));
if (m_srvLock->IsValid())
{
SetAffinity();
//got synched
return true;
}
m_srvLock.reset(0);
}
return false;
}
return m_srvLock->IsValid();
}
bool ClientHandler::Sync()
{
if (ServerIsValid())
{
m_clientLock->Signal();//signal that we're done and
if (m_srvLock->Wait(m_clientTimeout))//wait for server
{
//bla bla, track waiting
return true;
}
else
{
Reset();
}
}
return false;
}
#endif // defined(MAP_LOADING_SLICING)
+36
View File
@@ -0,0 +1,36 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_CLIENTHANDLER_H
#define CRYINCLUDE_CRYSYSTEM_CLIENTHANDLER_H
#pragma once
#include "HandlerBase.h"
#include "SyncLock.h"
struct ClientHandler
: public HandlerBase
{
ClientHandler(const char* bucket, int affinity, int clientTimeout);
void Reset();
bool ServerIsValid();
bool Sync();
private:
int m_clientTimeout;
std::unique_ptr<SSyncLock> m_clientLock;
std::unique_ptr<SSyncLock> m_srvLock;
};
#endif
+196
View File
@@ -0,0 +1,196 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "CmdLine.h"
void CCmdLine::PushCommand(const string& sCommand, const string& sParameter)
{
if (sCommand.empty())
{
return;
}
ECmdLineArgType type = eCLAT_Normal;
const char* szCommand = sCommand.c_str();
if (sCommand[0] == '-')
{
type = eCLAT_Pre;
++szCommand;
}
else if (sCommand[0] == '+')
{
type = eCLAT_Post;
++szCommand;
}
m_args.push_back(CCmdLineArg(szCommand, sParameter.c_str(), type));
}
CCmdLine::CCmdLine(const char* commandLine)
{
m_sCmdLine = commandLine;
char* src = (char*)commandLine;
string command, parameter;
for (;; )
{
if (*src == '\0')
{
break;
}
string arg = Next(src);
if (m_args.empty())
{
// this is the filename, convert backslash to forward slash
arg.replace('\\', '/');
m_args.push_back(CCmdLineArg("filename", arg.c_str(), eCLAT_Executable));
}
else
{
bool bSecondCharIsNumber = false;
if (arg[0] && arg[1] >= '0' && arg[1] <= '9')
{
bSecondCharIsNumber = true;
}
if ((arg[0] == '-' && !bSecondCharIsNumber)
|| (arg[0] == '+' && !bSecondCharIsNumber)
|| command.empty()) // separator or first parameter
{
PushCommand(command, parameter);
command = arg;
parameter = "";
}
else
{
if (parameter.empty())
{
parameter = arg;
}
else
{
parameter += string(" ") + arg;
}
}
}
}
PushCommand(command, parameter);
}
CCmdLine::~CCmdLine()
{
}
const ICmdLineArg* CCmdLine::GetArg(int n) const
{
if ((n >= 0) && (n < (int)m_args.size()))
{
return &m_args[n];
}
return 0;
}
int CCmdLine::GetArgCount() const
{
return (int)m_args.size();
}
const ICmdLineArg* CCmdLine::FindArg(const ECmdLineArgType ArgType, const char* name, bool caseSensitive) const
{
if (caseSensitive)
{
for (std::vector<CCmdLineArg>::const_iterator it = m_args.begin(); it != m_args.end(); ++it)
{
if (it->GetType() == ArgType)
{
if (!strcmp(it->GetName(), name))
{
return &(*it);
}
}
}
}
else
{
for (std::vector<CCmdLineArg>::const_iterator it = m_args.begin(); it != m_args.end(); ++it)
{
if (it->GetType() == ArgType)
{
if (!_stricmp(it->GetName(), name))
{
return &(*it);
}
}
}
}
return 0;
}
string CCmdLine::Next(char*& src)
{
char ch = 0;
char* org = src;
while (ch = *src++)
{
switch (ch)
{
case '\'':
case '\"':
org = src;
while ((*src++ != ch) && *src)
{
;
}
return string(org, src - 1);
case '[':
org = src;
while ((*src++ != ']') && *src)
{
;
}
return string(org, src - 1);
case ' ':
continue;
default:
org = src - 1;
for (; *src != ' ' && *src != '\t' && *src; ++src)
{
;
}
return string(org, src);
}
}
return string();
}
+47
View File
@@ -0,0 +1,47 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Implements the command line interface ICmdLine.
#ifndef CRYINCLUDE_CRYSYSTEM_CMDLINE_H
#define CRYINCLUDE_CRYSYSTEM_CMDLINE_H
#pragma once
#include <ICmdLine.h>
#include "CmdLineArg.h"
class CCmdLine
: public ICmdLine
{
public:
CCmdLine(const char* commandLine);
virtual ~CCmdLine();
virtual const ICmdLineArg* GetArg(int n) const;
virtual int GetArgCount() const;
virtual const ICmdLineArg* FindArg(const ECmdLineArgType ArgType, const char* name, bool caseSensitive = false) const;
virtual const char* GetCommandLine() const { return m_sCmdLine; };
private:
void PushCommand(const string& sCommand, const string& sParameter);
string Next(char*& str);
string m_sCmdLine;
std::vector<CCmdLineArg> m_args;
};
#endif // CRYINCLUDE_CRYSYSTEM_CMDLINE_H
+50
View File
@@ -0,0 +1,50 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "CmdLineArg.h"
CCmdLineArg::CCmdLineArg(const char* name, const char* value, ECmdLineArgType type)
{
m_name = name;
m_value = value;
m_type = type;
}
CCmdLineArg::~CCmdLineArg()
{
}
const char* CCmdLineArg::GetName() const
{
return m_name.c_str();
}
const char* CCmdLineArg::GetValue() const
{
return m_value.c_str();
}
const ECmdLineArgType CCmdLineArg::GetType() const
{
return m_type;
}
const float CCmdLineArg::GetFValue() const
{
return (float)atof(m_value.c_str());
}
const int CCmdLineArg::GetIValue() const
{
return atoi(m_value.c_str());
}
+45
View File
@@ -0,0 +1,45 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Implements the command line argument interface ICmdLineArg.
#ifndef CRYINCLUDE_CRYSYSTEM_CMDLINEARG_H
#define CRYINCLUDE_CRYSYSTEM_CMDLINEARG_H
#pragma once
#include <ICmdLine.h>
class CCmdLineArg
: public ICmdLineArg
{
public:
CCmdLineArg(const char* name, const char* value, ECmdLineArgType type);
virtual ~CCmdLineArg();
const char* GetName() const;
const char* GetValue() const;
const ECmdLineArgType GetType() const;
const float GetFValue() const;
const int GetIValue() const;
private:
ECmdLineArgType m_type;
string m_name;
string m_value;
};
#endif // CRYINCLUDE_CRYSYSTEM_CMDLINEARG_H
@@ -0,0 +1,167 @@
/*
* 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 "CrySystem_precompiled.h"
#include <AzTest/AzTest.h>
#include <MathConversion.h>
#include <Cry_Quat.h>
#include <Cry_Matrix34.h>
//namespace MathConversionUnitTests
//{
const float kEpsilon = 0.01f;
bool IsNearlyEqual(const AZ::Vector3& az, const Vec3& ly)
{
return fcmp(az.GetX(), ly.x, kEpsilon)
&& fcmp(az.GetY(), ly.y, kEpsilon)
&& fcmp(az.GetZ(), ly.z, kEpsilon);
}
bool IsNearlyEqual(const AZ::Quaternion& az, const Quat& ly)
{
return fcmp(az.GetX(), ly.v.x, kEpsilon)
&& fcmp(az.GetY(), ly.v.y, kEpsilon)
&& fcmp(az.GetZ(), ly.v.z, kEpsilon)
&& fcmp(az.GetW(), ly.w, kEpsilon);
}
bool IsNearlyEqual(const AZ::Transform& az, const Matrix34& ly)
{
float azFloats[12];
const AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromTransform(az);
matrix3x4.StoreToRowMajorFloat12(azFloats);
const float* lyFloats = ly.GetData();
for (int i = 0; i < 12; ++i)
{
if (!fcmp(azFloats[i], lyFloats[i], kEpsilon))
{
return false;
}
}
return true;
}
bool IsNearlyEqual(const AZ::Transform& az, const QuatT& ly)
{
return IsNearlyEqual(az.GetTranslation(), ly.t)
&& IsNearlyEqual(az.GetRotation(), ly.q);
}
TEST(MathConversionTests, BasicConversions)
{
{ // check vector3 comparisons
AZ::Vector3 az(1.f, 2.f, 3.f);
Vec3 ly(1.f, 2.f, 3.f);
EXPECT_TRUE(IsNearlyEqual(az, ly));
// reverse XYZ
ly = Vec3(3.f, 2.f, 1.f);
EXPECT_TRUE(!IsNearlyEqual(az, ly));
// off by 0.1
ly = Vec3(1.1f, 2.1f, 3.1f);
EXPECT_TRUE(!IsNearlyEqual(az, ly));
}
{ // check vector3 conversions
Vec3 ly1(1.f, 2.f, 3.f);
AZ::Vector3 az = LYVec3ToAZVec3(ly1);
EXPECT_TRUE(IsNearlyEqual(az, ly1));
Vec3 ly2 = AZVec3ToLYVec3(az);
EXPECT_TRUE(IsNearlyEqual(az, ly1));
EXPECT_TRUE(ly1.IsEquivalent(ly2));
}
{ // check quaternion comparisons
AZ::Quaternion az(AZ::Quaternion::CreateIdentity());
Quat ly(IDENTITY);
EXPECT_TRUE(IsNearlyEqual(az, ly));
az = AZ::Quaternion(1.f, 2.f, 3.f, 4.f);
ly = Quat(4.f, 1.f, 2.f, 3.f);
EXPECT_TRUE(IsNearlyEqual(az, ly));
// w in wrong place
ly = Quat(1.f, 2.f, 3.f, 4.f);
EXPECT_TRUE(!IsNearlyEqual(az, ly));
}
{ // check quaternion conversions
Quat ly1(4.f, 1.f, 2.f, 3.f);
AZ::Quaternion az = LYQuaternionToAZQuaternion(ly1);
EXPECT_TRUE(IsNearlyEqual(az, ly1));
Quat ly2 = AZQuaternionToLYQuaternion(az);
EXPECT_TRUE(IsNearlyEqual(az, ly2));
EXPECT_TRUE(Quat::IsEquivalent(ly1, ly2));
}
{ // check transform comparisons
AZ::Transform az = AZ::Transform::Identity();
Matrix34 ly = Matrix34::CreateIdentity();
EXPECT_TRUE(IsNearlyEqual(az, ly));
// rotating pi/2 will get us a non-symmetric matrix.
// good for testing that we're not confusing rows & columns
float rotation = gf_PI / 2.f;
ly = Matrix34::CreateRotationX(rotation, Vec3(1.f, 2.f, 3.f));
az = AZ::Transform::CreateRotationX(rotation);
az.SetTranslation(1.f, 2.f, 3.f);
EXPECT_TRUE(IsNearlyEqual(az, ly));
// rotate around different axis
ly = Matrix34::CreateRotationY(rotation, Vec3(1.f, 2.f, 3.f));
EXPECT_TRUE(!IsNearlyEqual(az, ly));
}
{ // check transform conversions
Matrix34 ly1 = Matrix34::CreateRotationXYZ(Ang3(0.1f, 0.5f, 0.9f), Vec3(1.f, 2.f, 3.f));
AZ::Transform az = LYTransformToAZTransform(ly1);
EXPECT_TRUE(IsNearlyEqual(az, ly1));
Matrix34 ly2 = AZTransformToLYTransform(az);
EXPECT_TRUE(IsNearlyEqual(az, ly2));
EXPECT_TRUE(Matrix34::IsEquivalent(ly1, ly2));
}
{ // check QuatT comparisons
AZ::Transform az = AZ::Transform::Identity();
QuatT ly(IDENTITY);
EXPECT_TRUE(IsNearlyEqual(az, ly));
az = AZ::Transform::CreateRotationX(AZ::Constants::HalfPi);
az.SetTranslation(1.f, 2.f, 3.f);
ly.q.SetRotationX(AZ::Constants::HalfPi);
ly.t.Set(1.f, 2.f, 3.f);
EXPECT_TRUE(IsNearlyEqual(az, ly));
// off by 0.1
ly.t.z += 0.1f;
EXPECT_TRUE(!IsNearlyEqual(az, ly));
}
{ // check QuatT conversions
QuatT ly1(Quat::CreateRotationX(AZ::Constants::HalfPi), Vec3(5.f, 6.f, 7.f));
AZ::Transform az = LYQuatTToAZTransform(ly1);
EXPECT_TRUE(IsNearlyEqual(az, ly1));
QuatT ly2 = AZTransformToLYQuatT(az);
EXPECT_TRUE(IsNearlyEqual(az, ly2));
EXPECT_TRUE(QuatT::IsEquivalent(ly1, ly2));
}
}
//} // namespace MathConversionUnitTests
@@ -0,0 +1,38 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "System.h"
#include "CryZlib.h"
bool CSystem::CompressDataBlock(const void* input, size_t inputSize, void* output, size_t& outputSize, int level)
{
uLongf destLen = outputSize;
Bytef* dest = static_cast<Bytef*>(output);
uLong sourceLen = inputSize;
const Bytef* source = static_cast<const Bytef*>(input);
bool ok = Z_OK == compress2(dest, &destLen, source, sourceLen, level);
outputSize = destLen;
return ok;
}
bool CSystem::DecompressDataBlock(const void* input, size_t inputSize, void* output, size_t& outputSize)
{
uLongf destLen = outputSize;
Bytef* dest = static_cast<Bytef*>(output);
uLong sourceLen = inputSize;
const Bytef* source = static_cast<const Bytef*>(input);
bool ok = Z_OK == uncompress(dest, &destLen, source, sourceLen);
outputSize = destLen;
return ok;
}
@@ -0,0 +1,200 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Executes an ASCII batch file of console commands...
#include "CrySystem_precompiled.h"
#include "ConsoleBatchFile.h"
#include "IConsole.h"
#include "ISystem.h"
#include "XConsole.h"
#include <CryPath.h>
#include <stdio.h>
#include "System.h"
#include <AzCore/IO/FileIO.h>
IConsole* CConsoleBatchFile::m_pConsole = NULL;
void CConsoleBatchFile::Init()
{
m_pConsole = gEnv->pConsole;
REGISTER_COMMAND("exec", (ConsoleCommandFunc)ExecuteFileCmdFunc, 0, "executes a batch file of console commands");
}
//////////////////////////////////////////////////////////////////////////
void CConsoleBatchFile::ExecuteFileCmdFunc(IConsoleCmdArgs* args)
{
if (!m_pConsole)
{
Init();
}
if (!args->GetArg(1))
{
return;
}
ExecuteConfigFile(args->GetArg(1));
}
//////////////////////////////////////////////////////////////////////////
bool CConsoleBatchFile::ExecuteConfigFile(const char* sFilename)
{
if (!sFilename)
{
return false;
}
if (!m_pConsole)
{
Init();
}
string filename;
if (sFilename[0] != '@') // console config files are actually by default in @root@ instead of @assets@
{
// However, if we've passed in a relative or absolute path that matches an existing file name,
// don't change it. Only change it to "@root@/filename" and strip off any relative paths
// if the given pattern *didn't* match a file.
if (AZ::IO::FileIOBase::GetDirectInstance()->Exists(sFilename))
{
filename = sFilename;
}
else
{
filename = PathUtil::Make("@root@", PathUtil::GetFile(sFilename));
}
}
else
{
filename = sFilename;
}
if (strlen(PathUtil::GetExt(filename)) == 0)
{
filename = PathUtil::ReplaceExtension(filename, "cfg");
}
#if defined(CVARS_WHITELIST)
bool ignoreWhitelist = true;
if (_stricmp(sFilename, "autoexec.cfg") == 0)
{
ignoreWhitelist = false;
}
#endif // defined(CVARS_WHITELIST)
//////////////////////////////////////////////////////////////////////////
CCryFile file;
{
const char* szLog = "Executing console batch file (try game,config,root):";
string filenameLog;
string sfn = PathUtil::GetFile(filename);
if (file.Open(filename, "rb", AZ::IO::IArchive::FOPEN_HINT_QUIET | AZ::IO::IArchive::FOPEN_ONDISK))
{
filenameLog = string("game/") + sfn;
}
else if (file.Open(string("config/") + sfn, "rb", AZ::IO::IArchive::FOPEN_HINT_QUIET | AZ::IO::IArchive::FOPEN_ONDISK))
{
filenameLog = string("game/config/") + sfn;
}
else if (file.Open(string("./") + sfn, "rb", AZ::IO::IArchive::FOPEN_HINT_QUIET | AZ::IO::IArchive::FOPEN_ONDISK))
{
filenameLog = string("./") + sfn;
}
else
{
CryLog("%s \"%s\" not found!", szLog, filename.c_str());
return false;
}
CryLog("%s \"%s\" found in %s ...", szLog, PathUtil::GetFile(filenameLog.c_str()), PathUtil::GetPath(filenameLog).c_str());
}
int nLen = file.GetLength();
char* sAllText = new char [nLen + 16];
file.ReadRaw(sAllText, nLen);
sAllText[nLen] = '\0';
sAllText[nLen + 1] = '\0';
/*
This can't work properly as ShowConsole() can be called during the execution of the scripts,
which means bConsoleStatus is outdated and must not be set at the end of the function
bool bConsoleStatus = ((CXConsole*)m_pConsole)->GetStatus();
((CXConsole*)m_pConsole)->SetStatus(false);
*/
char* strLast = sAllText + nLen;
char* str = sAllText;
while (str < strLast)
{
char* s = str;
while (str < strLast && *str != '\n' && *str != '\r')
{
str++;
}
*str = '\0';
str++;
while (str < strLast && (*str == '\n' || *str == '\r'))
{
str++;
}
string strLine = s;
//trim all whitespace characters at the beginning and the end of the current line and store its size
strLine.Trim();
size_t strLineSize = strLine.size();
//skip comments, comments start with ";" or "--" but may have preceding whitespace characters
if (strLineSize > 0)
{
if (strLine[0] == ';')
{
continue;
}
else if (strLine.find("--") == 0)
{
continue;
}
}
//skip empty lines
else
{
continue;
}
#if defined(CVARS_WHITELIST)
if (ignoreWhitelist || (gEnv->pSystem->GetCVarsWhiteList() && gEnv->pSystem->GetCVarsWhiteList()->IsWhiteListed(strLine, false)))
#endif // defined(CVARS_WHITELIST)
{
m_pConsole->ExecuteString(strLine);
}
#if defined(CVARS_WHITELIST)
else if (gEnv->IsDedicated())
{
gEnv->pSystem->GetILog()->LogError("Failed to execute command: '%s' as it is not whitelisted\n", strLine.c_str());
}
#endif // defined(CVARS_WHITELIST)
}
// See above
// ((CXConsole*)m_pConsole)->SetStatus(bConsoleStatus);
delete []sAllText;
return true;
}
@@ -0,0 +1,36 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Executes an ASCII batch file of console commands...
#ifndef CRYINCLUDE_CRYSYSTEM_CONSOLEBATCHFILE_H
#define CRYINCLUDE_CRYSYSTEM_CONSOLEBATCHFILE_H
#pragma once
struct IConsoleCmdArgs;
struct IConsole;
class CConsoleBatchFile
{
public:
static void Init();
static bool ExecuteConfigFile(const char* filename);
private:
static void ExecuteFileCmdFunc(IConsoleCmdArgs* args);
static IConsole* m_pConsole;
};
#endif // CRYINCLUDE_CRYSYSTEM_CONSOLEBATCHFILE_H
+947
View File
@@ -0,0 +1,947 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#if defined(WIN32) || defined(WIN64)
#include "ConsoleHelpGen.h"
#include "System.h"
// remove bad characters, toupper, not very fast
string CConsoleHelpGen::FixAnchorName(const char* szName)
{
string ret;
const char* p = szName;
while (*p)
{
if ((*p >= 'a' && *p <= 'z')
|| (*p >= 'A' && *p <= 'Z')
|| (*p >= '0' && *p <= '9'))
{
if (*p >= 'a' && *p <= 'z')
{
ret += *p - 'a' + 'A';
}
else
{
ret += *p;
}
}
++p;
}
return ret;
}
string CConsoleHelpGen::GetCleanPrefix(const char* p)
{
string sRet;
while (*p != '_' && *p != 0)
{
sRet += *p++;
}
return sRet;
}
string CConsoleHelpGen::SplitPrefixString_Part1(const char* p)
{
string sRet;
while (*p != 10 && *p != 13 && *p != 0)
{
sRet += *p++;
}
return sRet;
}
const char* CConsoleHelpGen::SplitPrefixString_Part2(const char* p)
{
while (*p != 10 && *p != 13 && *p != 0)
{
p++;
}
while (*p == 10 || *p == 13)
{
p++;
}
return p;
}
void CConsoleHelpGen::StartPage(FILE* f, const char* szPageName, const char* szPageDescription) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<HTML><HEAD><TITLE>%s</TITLE><META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-1\">", szPageName);
fprintf(f, "<META NAME=\"DESCRIPTION\" CONTENT=\"%s\">", szPageDescription);
fprintf(f, "<META NAME=\"author\" content=\"Crytek\">");
fprintf(f, "<META NAME=\"copyright\" CONTENT=\"Crytek\">");
fprintf(f, "<META NAME=\"KEYWORDS\" CONTENT=\"CryEngine,Crytek\">");
fprintf(f, "<META NAME=\"distribution\" CONTENT=\"Crytek\">");
fprintf(f, "<META NAME=\"revisit-after\" CONTENT=\"10 days\">");
fprintf(f, "<META NAME=\"robots\" CONTENT=\"INDEX, NOFOLLOW\">");
fprintf(f, "</HEAD><BODY bgcolor=#ffffff leftmargin=0 topmargin=0 alink=#0000ff link=#0000ff vlink=#0000ff text=#000000>");
}
}
void CConsoleHelpGen::EndPage(FILE* f) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<P></P></BODY></HTML>");
}
}
void CConsoleHelpGen::KeyValue(FILE* f, const char* szKey, const char* szValue) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<b>%s:</b> %s<br>\n", szKey, szValue);
}
else if (m_eWorkMode == eWM_Confluence)
{
// fprintf(f,"*%s:* %s\n",szKey,szValue);
fprintf(f, "| *%s:* | %s |\n", szKey, szValue);
}
else
{
assert(0);
}
}
void CConsoleHelpGen::LogVersion(FILE* f) const
{
char s[1024];
{
GetModuleFileName(NULL, s, sizeof(s));
char fdir[_MAX_PATH];
char fdrive[_MAX_PATH];
char file[_MAX_PATH];
char fext[_MAX_PATH];
_splitpath_s(s, fdrive, fdir, file, fext);
KeyValue(f, "Executable", (string(file) + fext).c_str());
}
{
time_t ltime;
time(&ltime);
tm today;
localtime_s(&today, &ltime);
strftime(s, 128, "%c", &today);
KeyValue(f, "Date(MM/DD/YY) Time", s);
}
{
const SFileVersion& ver = gEnv->pSystem->GetFileVersion();
ver.ToString(s, sizeof(s));
KeyValue(f, "FileVersion", s);
}
{
const SFileVersion& ver = gEnv->pSystem->GetProductVersion();
ver.ToString(s, sizeof(s));
KeyValue(f, "ProductVersion", s);
}
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<br>\n");
}
}
void CConsoleHelpGen::StartH1(FILE* f, const char* szName) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<h1>%s</h1>\n", szName);
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "h1. %s\n", szName);
}
else
{
assert(0);
}
}
void CConsoleHelpGen::EndH1(FILE* f) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<br>\n");
}
}
void CConsoleHelpGen::StartH3(FILE* f, const char* szName) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<h3>%s</h3><ul>\n", szName);
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "\nh3. %s\n", szName);
}
else
{
assert(0);
}
}
void CConsoleHelpGen::EndH3(FILE* f) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "</ul>\n");
}
else if (m_eWorkMode == eWM_Confluence)
{
}
else
{
assert(0);
}
}
void CConsoleHelpGen::StartCVar(FILE* f, const char* szName) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<h3>%s</h3><ul>\n", szName);
}
else if (m_eWorkMode == eWM_Confluence)
{
// fprintf(f,"\nh3. %s\n",szName);
fprintf(f, "\n<div class=\"panel\" style=\"border-style: none;border-width: 1px;\"><div class=\"panelContent\"><p><b>%s</b><br/>\n", szName);
}
else
{
assert(0);
}
}
void CConsoleHelpGen::EndCVar(FILE* f) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "</ul>\n");
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "</div></div>\n\n");
}
else
{
assert(0);
}
}
void CConsoleHelpGen::SingleLinePrefix(FILE* f, const char* szPrefix, const char* szPrefixDesc, const char* szLink) const
{
// group within the list of all groups (no elements)
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<li><a href=\"%s\">%s_ %s</a></li>\n", szLink, szPrefix, szPrefixDesc);
}
else if (m_eWorkMode == eWM_Confluence)
{
string sPrefix;
if (*szPrefix)
{
sPrefix = string(szPrefix) + "_";
}
// fprintf(f,"| %s | [%s|%s] |\n",sPrefix.c_str(),szPrefixDesc,szLink); // e.g. "" "CL_" "CC_" "I_" "T_"
fprintf(f, "{section:border=false}\n"
"{column:width=50px}{align:right}%s{align}{column}\n"
"{column:width=10px}{column}\n"
"{column}{align:left}[%s|%s]{align}{column}\n"
"{section}\n",
sPrefix.c_str(), szPrefixDesc, szLink); // e.g. "" "CL_" "CC_" "I_" "T_"
}
else
{
assert(0);
}
}
void CConsoleHelpGen::StartPrefix(FILE* f, const char* szPrefix, const char* szPrefixDesc, const char* szLink) const
{
// group before all the group elements
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<li><a href=\"%s\">%s_ %s</li></a><ul>\n", szLink, szPrefix, szPrefixDesc);
}
else if (m_eWorkMode == eWM_Confluence)
{
if (*szPrefix)
{
fprintf(f, "* [%s %s|%s]\n", szPrefix, szPrefixDesc, szLink);
}
else
{
fprintf(f, "* [%s|%s]\n", szPrefixDesc, szLink);
}
}
else
{
assert(0);
}
}
void CConsoleHelpGen::EndPrefix(FILE* f) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "</ul>\n");
}
}
void CConsoleHelpGen::SingleLineEntry_InGlobal(FILE* f, const char* szName, const char* szLink) const
{
// element within a group
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<li><a href=\"%s\">%s</a></li>\n", szLink, szName);
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "** [%s|%s]\n", szName, szLink);
}
else
{
assert(0);
}
}
void CConsoleHelpGen::SingleLineEntry_InGroup(FILE* f, const char* szName, const char* szLink) const
{
// element within a group
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<li><a href=\"%s\">%s</a></li>\n", szLink, szName);
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "* [%s|%s]\n", szName, szLink);
}
else
{
assert(0);
}
}
// szName without #
void CConsoleHelpGen::Anchor(FILE* f, const char* szName) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<a name=\"%s\"></a>\n", szName);
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "{anchor:%s}", szName);
}
else
{
assert(0);
}
}
void CConsoleHelpGen::InsertConsoleVars(std::set<const char*, string_nocase_lt>& setCmdAndVars, const char* szLocalPrefix) const
{
CXConsole::ConsoleVariablesMap::const_iterator itrVar, itrVarEnd = m_rParent.m_mapVariables.end();
for (itrVar = m_rParent.m_mapVariables.begin(); itrVar != itrVarEnd; ++itrVar)
{
ICVar* var = itrVar->second;
if (_strnicmp(var->GetName(), szLocalPrefix, strlen(szLocalPrefix)) == 0)
{
setCmdAndVars.insert(var->GetName());
}
}
}
void CConsoleHelpGen::InsertConsoleCommands(std::set<const char*, string_nocase_lt>& setCmdAndVars, const char* szLocalPrefix) const
{
CXConsole::ConsoleCommandsMap::const_iterator itrCmd, itrCmdEnd = m_rParent.m_mapCommands.end();
for (itrCmd = m_rParent.m_mapCommands.begin(); itrCmd != itrCmdEnd; ++itrCmd)
{
const CConsoleCommand& cmd = itrCmd->second;
if (_strnicmp(cmd.m_sName, szLocalPrefix, strlen(szLocalPrefix)) == 0)
{
setCmdAndVars.insert(cmd.m_sName.c_str());
}
}
}
void CConsoleHelpGen::InsertConsoleVars(std::set<const char*, string_nocase_lt>& setCmdAndVars, std::map<string, const char*> mapPrefix) const
{
CXConsole::ConsoleVariablesMap::const_iterator itrVar, itrVarEnd = m_rParent.m_mapVariables.end();
for (itrVar = m_rParent.m_mapVariables.begin(); itrVar != itrVarEnd; ++itrVar)
{
ICVar* var = itrVar->second;
bool bInsert = true;
{
std::map<string, const char*>::const_iterator it2, end = mapPrefix.end();
for (it2 = mapPrefix.begin(); it2 != end; ++it2)
{
if (it2->first != "___" && _strnicmp(var->GetName(), it2->first, it2->first.size()) == 0)
{
bInsert = false;
break;
}
}
}
if (bInsert)
{
setCmdAndVars.insert(var->GetName());
}
}
}
void CConsoleHelpGen::InsertConsoleCommands(std::set<const char*, string_nocase_lt>& setCmdAndVars, std::map<string, const char*> mapPrefix) const
{
CXConsole::ConsoleCommandsMap::const_iterator itrCmd, itrCmdEnd = m_rParent.m_mapCommands.end();
for (itrCmd = m_rParent.m_mapCommands.begin(); itrCmd != itrCmdEnd; ++itrCmd)
{
const CConsoleCommand& cmd = itrCmd->second;
bool bInsert = true;
{
std::map<string, const char*>::const_iterator it2, end = mapPrefix.end();
for (it2 = mapPrefix.begin(); it2 != end; ++it2)
{
if (it2->first != "___" && _strnicmp(cmd.m_sName, it2->first, it2->first.size()) == 0)
{
bInsert = false;
break;
}
}
}
if (bInsert)
{
setCmdAndVars.insert(cmd.m_sName.c_str());
}
}
}
void CConsoleHelpGen::CreateSingleEntryFile(const char* szName) const
{
assert(m_eWorkMode == eWM_Confluence); // only needed for confluence
FILE* f3 = nullptr;
azfopen(&f3, (string(GetFolderName()) + GetFileExtension() + "/" + FixAnchorName(szName)).c_str(), "w");
if (!f3)
{
assert(0);
return; // error
}
// StartPage(f3,szName,""); // HTML style
IncludeSingleEntry(f3, szName);
// EndPage(f3); // HTML style
fclose(f3);
}
const CConsoleCommand* CConsoleHelpGen::FindConsoleCommand(const char* szName) const
{
CXConsole::ConsoleCommandsMap::const_iterator itrCmd, itrCmdEnd = m_rParent.m_mapCommands.end();
for (itrCmd = m_rParent.m_mapCommands.begin(); itrCmd != itrCmdEnd; ++itrCmd)
{
const CConsoleCommand& cmd = itrCmd->second;
if (strcmp(cmd.m_sName.c_str(), szName) == 0)
{
return &cmd;
}
}
return 0;
}
void CConsoleHelpGen::IncludeSingleEntry(FILE* f, const char* szName) const
{
StartCVar(f, szName);
uint32 dwFlags = 0;
const char* szHelp = "";
// slow but good for simpler code
if (ICVar* pVar = gEnv->pConsole->GetCVar(szName))
{
dwFlags = pVar->GetFlags();
szHelp = pVar->GetHelp();
}
else if (const CConsoleCommand* pCommand = FindConsoleCommand(szName))
{
dwFlags = pCommand->m_nFlags;
szHelp = pCommand->m_sHelp.c_str();
}
else
{
assert(0); // internal error
}
const char* szFlags = CXConsole::GetFlagsString(dwFlags);
if (*szFlags)
{
// fprintf(f3,"%%GRAY%% %s %%ENDCOLOR%%<br>\n",szFlags); // twiki style
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "%s<br>\n", szFlags); // simple HTML style
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "<font color=\"#808080\">%s</font></p>\n", szFlags);
}
else
{
assert(0);
}
}
if (*szHelp == 0)
{
// fprintf(f,"%%TODO%%\n"); // wiki style, in our wiki %TODO% is defined as <img src=\"%%ICONURL{todo}%%\" width=\"37\" height=\"16\" alt=\"TODO\" border=\"0\" />
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<blockquote></b>*TODO*</b></blockquote>\n"); // HTML style
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "{warning}TODO{warning}\n"); // Confluence style
}
else
{
assert(0);
}
}
else
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<blockquote><pre><verbatim><tt>\n%s\n</tt></verbatim></pre></blockquote>\n", szHelp); // HTML style, <tt> to get fixed with font (layout in code is often making the assumption the forn is fixed width)
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f, "<pre>\n");
string sHelp = szHelp;
// currently not required as the {noformat} is used
// sHelp.replace("[","\\["); sHelp.replace("]","\\]");
// sHelp.replace("{","\\{"); sHelp.replace("}","\\}");
// sHelp.replace("(","\\("); sHelp.replace(")","\\)");
fprintf(f, "%s\n", sHelp.c_str()); // Confluence style
fprintf(f, "</pre>");
}
else
{
assert(0);
}
}
EndCVar(f);
}
void CConsoleHelpGen::Work()
{
// string sEngineFolder = string("@user@/")+GetFolderName();
// gEnv->pCryPak->RemoveDir(sEngineFolder.c_str()); // todo: check if that works
// gEnv->pFileIO->CreatePath(sEngineFolder.c_str());
m_eWorkMode = eWM_HTML;
CreateMainPages();
m_eWorkMode = eWM_Confluence;
CreateMainPages();
CreateFileForEachEntry();
m_eWorkMode = eWM_None;
}
void CConsoleHelpGen::CreateFileForEachEntry()
{
assert(m_eWorkMode == eWM_Confluence); // only needed for confluence
// generate a single file for each console command
{
CXConsole::ConsoleCommandsMap::const_iterator itrCmd, itrCmdEnd = m_rParent.m_mapCommands.end();
for (itrCmd = m_rParent.m_mapCommands.begin(); itrCmd != itrCmdEnd; ++itrCmd)
{
const CConsoleCommand& cmd = itrCmd->second;
CreateSingleEntryFile(cmd.m_sName.c_str());
}
}
// generate a single file for each console variable
{
CXConsole::ConsoleVariablesMap::iterator itrVar, itrVarEnd = m_rParent.m_mapVariables.end();
for (itrVar = m_rParent.m_mapVariables.begin(); itrVar != itrVarEnd; ++itrVar)
{
ICVar* var = itrVar->second;
CreateSingleEntryFile(var->GetName());
}
}
}
void CConsoleHelpGen::CreateMainPages()
{
gEnv->pFileIO->CreatePath(GetFolderName());
std::map<string, const char*> mapPrefix;
// order here doesn't matter, after the name some help can be added (after the first return)
mapPrefix[ "AI_"] = "Artificial Intelligence";
mapPrefix[ "NET_"] = "Network";
mapPrefix[ "ED_"] = "Editor";
mapPrefix[ "ES_"] = "Entity System";
mapPrefix[ "CON_"] = "Console";
mapPrefix[ "AG_"] = "Animation Graph" "\n" "High level animation logic, describes animation selection and flow, matches animation state to current game logical state.";
mapPrefix[ "AC_"] = "Animated Character" "\n" "Better name would be 'Character Movement'.\nBridges game controlled movement and animation controlled movement.";
mapPrefix[ "CA_"] = "Character Animation" "\n" "Motion synthesize and playback, parameterization through blending and inversed kinematics.";
mapPrefix[ "E_"] = "3DEngine";
mapPrefix[ "I_"] = "Input";
mapPrefix[ "FG_"] = "Flow Graph" "\n" "hyper graph: game logic";
mapPrefix[ "P_"] = "Physics";
mapPrefix[ "R_"] = "Renderer";
mapPrefix[ "S_"] = "Sound";
mapPrefix[ "G_"] = "Game" "\n" "game specific, not part of CryEngine";
mapPrefix[ "SYS_"] = "System";
mapPrefix[ "V_"] = "Vehicle";
mapPrefix[ "FT_"] = "Feature Test";
mapPrefix[ "DEMO_"] = "Time Demo";
mapPrefix[ "FT_"] = "Feature Test";
mapPrefix[ "GL_"] = "Game Lobby";
mapPrefix[ "HUD_"] = "Heads Up Display";
mapPrefix[ "KC_"] = "Kill Cam";
mapPrefix[ "PL_"] = "Player";
mapPrefix[ "PP_"] = "Player Progression";
mapPrefix[ "AIM_"] = "Aiming";
mapPrefix["CAPTURE_"] = "Capture";
mapPrefix[ "DS_"] = "Dialog Scripts";
mapPrefix[ "GT_"] = "Game Token";
mapPrefix[ "LOG_"] = "Logging";
mapPrefix[ "MOV_"] = "Movie Sequences";
mapPrefix[ "OSM_"] = "Overload Scene Manager";
mapPrefix["PROFILE_"] = "Profiling";
mapPrefix[ "STAP_"] = "Screen-space Torso Aim Pose";
mapPrefix[ "LUA_"] = "Lua" "\n" "scripting system";
mapPrefix[ "SV_"] = "Server";
mapPrefix[ "MFX_"] = "Material Effects";
mapPrefix[ "M_"] = "Multi threading";
mapPrefix[ "CC_"] = "Character Customization";
mapPrefix[ "CL_"] = "Client";
mapPrefix[ "Q_"] = "Quality" "\n" "usually shader quality";
mapPrefix[ "T_"] = "Timer";
mapPrefix[ "___"] = "Remaining"; // key defined to get it sorted in the end
FILE* f1 = nullptr;
azfopen(&f1, (string(GetFolderName()) + "/index" + GetFileExtension()).c_str(), "w");
if (!f1)
{
return;
}
StartPage(f1, "CryEngine ConsoleHTMLHelp", "main page");
StartH1(f1, "Console Commands and Variables");
LogVersion(f1);
if (m_eWorkMode == eWM_HTML)
{
fprintf(f1, "This list was exported from the engine by using the <b>DumpCommandsVars</b> console command.<br>\n\n");
}
else if (m_eWorkMode == eWM_Confluence)
{
fprintf(f1, "This list was exported from the engine by using the *DumpCommandsVars* console command.\n\n");
}
else
{
assert(0);
}
// show all registered Prefix with one line
{
std::map<string, const char*>::const_iterator it, end = mapPrefix.end();
StartH3(f1, "Registered Prefixes");
for (it = mapPrefix.begin(); it != end; ++it)
{
const char* szLocalPrefix = it->first.c_str(); // can be 0 for remaining ones
string sCleanPrefix = GetCleanPrefix(szLocalPrefix);
string sPrefixName = SplitPrefixString_Part1(it->second);
SingleLinePrefix(f1, sCleanPrefix.c_str(), sPrefixName.c_str(), (string("CONSOLEPREFIX") + FixAnchorName(sCleanPrefix.c_str()) + GetFileExtension()).c_str());
// fprintf(f1," * [[CONSOLEPREFIX%s][%s_ %s]]\n",FixAnchorName(sCleanPrefix.c_str()).c_str(),sCleanPrefix.c_str(),sPrefixName.c_str());
}
EndH3(f1); // Registered prefixes
}
{
std::map<string, const char*>::const_iterator it, it2, end = mapPrefix.end();
StartH3(f1, "Console Commands and Variables Sorted by Prefix");
for (it = mapPrefix.begin(); it != end; ++it)
{
const char* szLocalPrefix = it->first.c_str(); // can be 0 for remaining ones
string sCleanPrefix = GetCleanPrefix(szLocalPrefix);
string sPrefixName = SplitPrefixString_Part1(it->second);
std::set<const char*, string_nocase_lt> setCmdAndVars; // to get console variables and commands sorted together
if (strcmp("___", szLocalPrefix) != 0)
{
// insert all starting with the prefix
InsertConsoleVars(setCmdAndVars, szLocalPrefix);
InsertConsoleCommands(setCmdAndVars, szLocalPrefix);
}
else
{
// insert all not starting with any of the prefix
InsertConsoleVars(setCmdAndVars, mapPrefix);
InsertConsoleCommands(setCmdAndVars, mapPrefix);
}
// -------------------------------
string sSubName = string("CONSOLEPREFIX") + sCleanPrefix;
StartPrefix(f1, sCleanPrefix.c_str(), sPrefixName.c_str(), (sSubName + GetFileExtension()).c_str());
string sFileOut = string(GetFolderName()) + "/" + sSubName + GetFileExtension();
FILE* f2 = nullptr;
azfopen(&f2, sFileOut.c_str(), "w");
if (!f2)
{
fclose(f1);
return;
}
// headline
{
string sHeadline;
if (sCleanPrefix.empty())
{
sHeadline = "Console Commands and Variables Without Special Prefix";
}
else
{
sHeadline = string("Console Commands and Variables with Prefix ") + sCleanPrefix + "_";
}
StartH1(f2, sHeadline.c_str());
}
Explanation(f2, SplitPrefixString_Part2(it->second));
KeyValue(f2, "Possible Flags", CXConsole::GetFlagsString(0xffffffff));
// fprintf(f2,"<b>Possible Flags:</b><br>\n");
// fprintf(f2," <blockquote>%s</blockquote>",CXConsole::GetFlagsString(0xffffffff));
// log console variables and commands
{
StartH3(f2, "Alphabetically Sorted");
std::set<const char*, string_nocase_lt>::const_iterator itI, endI = setCmdAndVars.end();
for (itI = setCmdAndVars.begin(); itI != endI; ++itI)
{
SingleLineEntry_InGlobal(f1, *itI, (sSubName + GetFileExtension() + "#Anchor" + FixAnchorName(*itI)).c_str());
SingleLineEntry_InGroup(f2, *itI, (string("#Anchor") + FixAnchorName(*itI)).c_str());
}
EndH3(f2);
}
{
StartH3(f2, "Console Variables and Commands");
bool bFirst = true;
{
std::set<const char*, string_nocase_lt>::const_iterator itI, endI = setCmdAndVars.end();
for (itI = setCmdAndVars.begin(); itI != endI; ++itI)
{
if (!bFirst)
{
Separator(f2);
}
bFirst = false;
Anchor(f2, (string("Anchor") + FixAnchorName(*itI)).c_str()); // anchor
IncludeSingleEntry(f2, *itI);
}
}
EndH3(f2);
}
EndH1(f2); // Console Commands and Variables ...
fclose(f2);
f2 = 0;
EndPrefix(f1);
}
EndH3(f1); // Console commands and variables sorted by prefix
}
EndH1(f1);
EndPage(f1);
fclose(f1);
m_rParent.ConsoleLogInputResponse("successfully wrote directory %s", GetFolderName());
}
void CConsoleHelpGen::Separator(FILE* f) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<hr>\n");
}
else if (m_eWorkMode == eWM_Confluence)
{
// fprintf(f,"----\n");
}
else
{
assert(0);
}
}
void CConsoleHelpGen::Explanation(FILE* f, const char* szText) const
{
if (m_eWorkMode == eWM_HTML)
{
fprintf(f, "<blockquote>%s</blockquote><br>\n<br>\n", szText);
}
else if (m_eWorkMode == eWM_Confluence)
{
// fprintf(f,"{quote}%s{quote}\n\n",szText);
fprintf(f, "%s\n\n", szText);
}
else
{
assert(0);
}
}
const char* CConsoleHelpGen::GetFileExtension() const
{
if (m_eWorkMode == eWM_HTML)
{
return ".html";
}
else if (m_eWorkMode == eWM_Confluence)
{
}
else
{
assert(0);
}
return "";
}
const char* CConsoleHelpGen::GetFolderName() const
{
if (m_eWorkMode == eWM_HTML)
{
return "ConsoleHTMLHelp";
}
else if (m_eWorkMode == eWM_Confluence)
{
return "ConsoleHTMLHelp/CRYAUTOGEN";
}
else
{
assert(0);
}
return 0;
}
#endif // defined(WIN32) || defined(WIN64)
+127
View File
@@ -0,0 +1,127 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#ifndef CRYINCLUDE_CRYSYSTEM_CONSOLEHELPGEN_H
#define CRYINCLUDE_CRYSYSTEM_CONSOLEHELPGEN_H
#if defined(WIN32) || defined(WIN64)
#include "XConsole.h" // CXConsole, struct string_nocase_lt
// extract consoel variable and command help
// in some HTML pages and many small files that can be included in Confluence wiki
// pages (so maintain the documentation only in one place)
//
// Possible improvements/Known issues:
// - Nicer HTML layout (CSS?)
// - Searching in the content of the cvars is tricky (main page doesn't have help content)
// - %TODO% (was wiki image, should look good in confluence and HTML)
// - many small files should be stored in some extra folder for clearity
// - before generating the data the older directoy should be cleaned
// - file should be generated in the user folder
// - "wb" should be used instead of "w", to get the same result in unix
class CConsoleHelpGen
{
public:
CConsoleHelpGen(CXConsole& rParent)
: m_rParent(rParent)
, m_eWorkMode(eWM_None)
{
}
void Work();
private: // --------------------------------------------------------
enum EWorkMode
{
eWM_None,
eWM_HTML,
eWM_Confluence
};
//
void CreateMainPages();
// to create one file for for each cvar/command in confluence style
void CreateFileForEachEntry();
// insert if the name starts with the with prefix
void InsertConsoleVars(std::set<const char*, string_nocase_lt>& setCmdAndVars, const char* szPrefix) const;
// insert if the name starts with the with prefix
void InsertConsoleCommands(std::set<const char*, string_nocase_lt>& setCmdAndVars, const char* szPrefix) const;
// insert if the name does not start with any of the prefix in the map
void InsertConsoleVars(std::set<const char*, string_nocase_lt>& setCmdAndVars, std::map<string, const char*> mapPrefix) const;
// insert if the name does not start with any of the prefix in the map
void InsertConsoleCommands(std::set<const char*, string_nocase_lt>& setCmdAndVars, std::map<string, const char*> mapPrefix) const;
// a single file for the entry is generate
void CreateSingleEntryFile(const char* szName) const;
void IncludeSingleEntry(FILE* f, const char* szName) const;
static string FixAnchorName(const char* szName);
static string GetCleanPrefix(const char* p);
// split before "|" (to get the prefix itself)
static string SplitPrefixString_Part1(const char* p);
// split string after "|" (to get the optional help)
static const char* SplitPrefixString_Part2(const char* p);
void StartPage(FILE* f, const char* szPageName, const char* szPageDescription) const;
void EndPage(FILE* f) const;
void StartH1(FILE* f, const char* szName) const;
void EndH1(FILE* f) const;
void StartH3(FILE* f, const char* szName) const;
void EndH3(FILE* f) const;
void StartCVar(FILE* f, const char* szName) const;
void EndCVar(FILE* f) const;
void SingleLinePrefix(FILE* f, const char* szPrefix, const char* szPrefixDesc, const char* szLink) const;
void StartPrefix(FILE* f, const char* szPrefix, const char* szPrefixDesc, const char* szLink) const;
void EndPrefix(FILE* f) const;
void SingleLineEntry_InGlobal(FILE* f, const char* szName, const char* szLink) const;
void SingleLineEntry_InGroup(FILE* f, const char* szName, const char* szLink) const;
void Anchor(FILE* f, const char* szName) const;
// to log some inital stats like date or application name
void KeyValue(FILE* f, const char* szKey, const char* szValue) const;
// prefix explanation and indention for following text
void Explanation(FILE* f, const char* szText) const;
void Separator(FILE* f) const;
void LogVersion(FILE* f) const;
// case senstive
// Returns
// 0 if not found
const CConsoleCommand* FindConsoleCommand(const char* szName) const;
// ----------------------------------------------
//
const char* GetFolderName() const;
//
const char* GetFileExtension() const;
// ----------------------------------------------
CXConsole& m_rParent;
EWorkMode m_eWorkMode; // during Work() thise true:HTML and false:Confluence at some point
};
#endif // defined(WIN32) || defined(WIN64)
#endif // CRYINCLUDE_CRYSYSTEM_CONSOLEHELPGEN_H
+114
View File
@@ -0,0 +1,114 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_CRITICAL_ERROR, DIALOG
BEGIN
LEFTMARGIN, 5
RIGHTMARGIN, 260
BOTTOMMARGIN, 223
END
IDD_EXCEPTION, DIALOG
BEGIN
END
IDD_CONFIRM_SAVE_LEVEL, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 239
TOPMARGIN, 7
BOTTOMMARGIN, 96
END
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_CRITICAL_ERROR DIALOGEX 0, 0, 267, 230
STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION
CAPTION "Critical Exception"
FONT 8, "MS Sans Serif", 0, 0, 0x0
BEGIN
DEFPUSHBUTTON "&Abort",IDB_EXIT,100,207,58,14
EDITTEXT IDC_CALLSTACK,10,95,245,102,ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | WS_HSCROLL
EDITTEXT IDC_EXCEPTION_CODE,10,25,50,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Call Stack Trace",IDC_STATIC,13,85,54,8
LTEXT "Code",IDC_STATIC,10,15,18,8
LTEXT "Address:",IDC_STATIC,66,15,28,8
EDITTEXT IDC_EXCEPTION_ADDRESS,65,25,75,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Description",IDC_STATIC,10,40,36,8
GROUPBOX "Exception Info",IDC_STATIC,5,5,255,200
EDITTEXT IDC_EXCEPTION_MODULE,145,25,110,12,ES_AUTOHSCROLL | ES_READONLY
LTEXT "Module",IDC_STATIC,145,15,24,8
EDITTEXT IDC_EXCEPTION_DESC,10,50,245,30,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY
PUSHBUTTON "&Ignore",IDB_IGNORE,160,207,59,14,WS_DISABLED
END
IDD_EXCEPTION DIALOG 0, 0, 138, 52
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Exception"
FONT 8, "MS Sans Serif"
BEGIN
LTEXT "Exception Intercepted\r\nRetrieving Info...",IDC_STATIC,33,18,71,19
END
IDD_CONFIRM_SAVE_LEVEL DIALOGEX 0, 0, 280, 123
STYLE DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION
EXSTYLE WS_EX_TOPMOST
CAPTION "Engine, Game or Editor Crash"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
PUSHBUTTON "Save",IDB_CONFIRM_SAVE,4,96,68,20
PUSHBUTTON "Cancel",IDB_DONT_SAVE,206,96,68,20
LTEXT "Lumberyard has encountered an error and needs to close.\n\nA backup has been saved to the '_savebackup' subfolder.\n\nIf you are unable to save your file, you can recover by copying the contents of the _savebackup folder over the broken files.",IDC_STATIC,60,8,210,61
LTEXT "Attempt save?",IDC_STATIC,60,72,180,21
CONTROL 128,IDC_STATIC,"Static",SS_BITMAP | SS_CENTERIMAGE | SS_REALSIZEIMAGE,8,8,48,40
END
/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//
IDB_CRASH_FACE BITMAP "crash_face.bmp"
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
@@ -0,0 +1,58 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include <AzCore/Debug/Profiler.h>
#include <AzCore/Jobs/JobFunction.h>
namespace
{
static void cryAsyncMemcpy_Int(
void* dst
, const void* src
, size_t size
, int nFlags
, volatile int* sync)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::System);
cryMemcpy(dst, src, size, nFlags);
if (sync)
{
CryInterlockedDecrement(sync);
}
}
}
#if !defined(CRY_ASYNC_MEMCPY_DELEGATE_TO_CRYSYSTEM)
CRY_ASYNC_MEMCPY_API void cryAsyncMemcpy(
#else
CRY_ASYNC_MEMCPY_API void cryAsyncMemcpyDelegate(
#endif
void* dst
, const void* src
, size_t size
, int nFlags
, volatile int* sync)
{
AZ::Job* job = AZ::CreateJobFunction(
[dst, src, size, nFlags, sync]()
{
cryAsyncMemcpy_Int(dst, src, size, nFlags, sync);
},
true); // Auto-delete
job->Start();
}
File diff suppressed because it is too large Load Diff
+251
View File
@@ -0,0 +1,251 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include <ITimer.h>
#include <CrySizer.h>
#include "CrySizerImpl.h"
CrySizerImpl::CrySizerImpl()
: m_pResourceCollector(0)
{
m_nFlags = 0;
m_nTotalSize = 0;
// to avoid reallocations during walk through the memory tree, reserve the space for the names
clear();
}
CrySizerImpl::~CrySizerImpl()
{
}
void CrySizerImpl::Push (const char* szComponentName)
{
m_stackNames.push_back (getNameIndex(getCurrentName(), szComponentName));
// if the depth is too deep, something is wrong, perhaps an infinite loop
assert (m_stackNames.size() < 128);
}
void CrySizerImpl::PushSubcomponent (const char* szSubcomponentName)
{
Push (szSubcomponentName);
}
void CrySizerImpl::Pop ()
{
if (!m_stackNames.empty())
{
m_stackNames.pop_back();
}
else
{
assert (0);
}
}
// returns the index of the current name on the top of the name stack
size_t CrySizerImpl::getCurrentName() const
{
assert(!m_stackNames.empty());
return m_stackNames.empty() ? 0 : m_stackNames.back();
}
// searches for the name in the name array; adds the name if it's not there and returns the index
size_t CrySizerImpl::getNameIndex(size_t nParent, const char* szComponentName)
{
NameArray::const_iterator it = m_arrNames.begin(), itEnd = it + m_arrNames.size();
for (; it != itEnd; ++it)
{
#if defined(LINUX)
if (!strcasecmp(it->strName.c_str(), szComponentName) && it->nParent == nParent)
#else
if (!strcmp(it->strName.c_str(), szComponentName) && it->nParent == nParent)
#endif
{
return (size_t)(it - m_arrNames.begin());//it-m_arrNames.begin();
}
}
size_t nNewName = m_arrNames.size();
m_arrNames.resize(nNewName + 1);
m_arrNames[nNewName].assign(szComponentName, nParent);
m_arrNames[nParent].arrChildren.push_back(nParent);
return nNewName;
}
static NullResCollector s_nullCollector;
IResourceCollector* CrySizerImpl::GetResourceCollector()
{
return m_pResourceCollector != 0 ? m_pResourceCollector : &s_nullCollector;
}
void CrySizerImpl::Reset()
{
clear();
m_nTotalSize = 0;
//m_arrNames.resize(0);
//m_arrNames.push_back("TOTAL"); // the default name, with index 0
//m_LastObject.clear();
////m_nFlags;
//m_nTotalSize=0;
//if (m_pResourceCollector)
//{
// m_pResourceCollector->Reset();
//}
//m_setObjects->clear();
//m_stackNames.resize(0);
//m_stackNames.push_back(0);
}
// adds an object identified by the unique pointer (it needs not be
// the actual object position in the memory, though it would be nice,
// but it must be unique throughout the system and unchanging for this object)
// RETURNS: true if the object has actually been added (for the first time)
// and calculated
bool CrySizerImpl::AddObject (const void* pIdentifier, size_t sizeBytes, int nCount)
{
if (!pIdentifier || !sizeBytes)
{
return false; // we don't add the NULL objects
}
Object NewObject(pIdentifier, sizeBytes, getCurrentName());
// check if the last object was the same
if (NewObject == m_LastObject)
{
assert (m_LastObject.nSize == sizeBytes);
return false;
}
ObjectSet& rSet = m_setObjects[getHash(pIdentifier)];
ObjectSet::iterator it = rSet.find (NewObject);
if (it == rSet.end())
{
// there's no such object in the map, add it
rSet.insert (NewObject);
ComponentName& CompName = m_arrNames[getCurrentName()];
CompName.numObjects += nCount;
CompName.sizeObjects += sizeBytes;
m_nTotalSize += sizeBytes;
return true;
}
else
{
Object* pObj = const_cast<Object*>(&(*it));
// if we do an heap check, don't accept the same object twice
if (sizeBytes != pObj->nSize)
{
// if the following assert fails:
// assert (0);
// .. it means we have one object that's added two times with different sizes; that's screws up the whole idea
// we assume there are two different objects that are for some reason assigned the same id
pObj->nSize += sizeBytes; // anyway it's an invalid situation
ComponentName& CompName = m_arrNames[getCurrentName()];
CompName.sizeObjects += sizeBytes;
return true; // yes we added the object, though there were an error condition
}
return false;
}
}
size_t CrySizerImpl::GetObjectCount()
{
size_t count = m_stackNames.size();
for (int i = 0; i < g_nHashSize; i++)
{
count += m_setObjects[i].size();
}
return count;
}
// finalizes data collection, should be called after all objects have been added
void CrySizerImpl::End()
{
// clean up the totals of each name
int i;
for (i = 0; i < m_arrNames.size(); ++i)
{
assert (i == 0 || ((int)m_arrNames[i].nParent < i && m_arrNames[i].nParent >= 0));
m_arrNames[i].sizeObjectsTotal = m_arrNames[i].sizeObjects;
}
// add the component's size to the total size of the parent.
// for every component, all their children are put after them in the name array
// we don't include the root because it doesn't belong to any other parent (nowhere further to add)
for (i = m_arrNames.size() - 1; i > 0; --i)
{
// the parent's total size is increased by the _total_ size (already calculated) of this object
m_arrNames[m_arrNames[i].nParent].sizeObjectsTotal += m_arrNames[i].sizeObjectsTotal;
}
}
void CrySizerImpl::clear()
{
for (unsigned i = 0; i < g_nHashSize; ++i)
{
m_setObjects[i].clear();
}
m_arrNames.clear();
m_arrNames.push_back("TOTAL"); // the default name, with index 0
m_stackNames.clear();
m_stackNames.push_back(0);
m_LastObject.pId = NULL;
if (m_pResourceCollector)
{
m_pResourceCollector->Reset();
}
}
// hash function for an address; returns value 0..1<<g_nHashSize
unsigned CrySizerImpl::getHash (const void* pId)
{
//return (((unsigned)pId) >> 4) & (g_nHashSize-1);
// pseudorandomizing transform
ldiv_t Qrem = (ldiv_t)ldiv(((uint32)(UINT_PTR)pId >> 2), 127773);
Qrem.rem = 16807 * Qrem.rem - 2836 * Qrem.quot;
if (Qrem.rem < 0)
{
Qrem.rem += 2147483647; // 0x7FFFFFFF
}
return ((unsigned)Qrem.rem) & (g_nHashSize - 1);
}
unsigned CrySizerImpl::GetDepthLevel(unsigned nCurrent)
{
uint32 nDepth = 0;
nCurrent = m_arrNames[nCurrent].nParent;
while (nCurrent != 0)
{
nDepth++;
nCurrent = m_arrNames[nCurrent].nParent;
}
return nDepth;
}
+184
View File
@@ -0,0 +1,184 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Implementation of the ICrySizer interface, which is used to
// calculate the memory usage by the subsystems and components, to help
// the artists keep the memory budged low.
#ifndef CRYINCLUDE_CRYSYSTEM_CRYSIZERIMPL_H
#define CRYINCLUDE_CRYSYSTEM_CRYSIZERIMPL_H
#pragma once
// prerequisities
//////////////////////////////////////////////////////////////////////////
// implementation of interface ICrySizer
// ICrySizer is passed to all subsystems and has a lot of helper functions that
// are compiled in the appropriate subsystems. CrySizerImpl is created in CrySystem
// and is passed to all the other subsystems
class CrySizerImpl
: public ICrySizer
{
public:
CrySizerImpl();
~CrySizerImpl();
virtual void Release() { delete this; }
virtual size_t GetTotalSize() { return m_nTotalSize; };
virtual size_t GetObjectCount();
void Reset();
// adds an object identified by the unique pointer (it needs not be
// the actual object position in the memory, though it would be nice,
// but it must be unique throughout the system and unchanging for this object)
// RETURNS: true if the object has actually been added (for the first time)
// and calculated
virtual bool AddObject (const void* pIdentifier, size_t nSizeBytes, int nCount = 1);
virtual IResourceCollector* GetResourceCollector();
// finalizes data collection, should be called after all objects have been added
void End();
void clear();
// Arguments:
// pColl - can be 0
void SetResourceCollector(IResourceCollector* pColl) { m_pResourceCollector = pColl; }
protected:
IResourceCollector* m_pResourceCollector; //
// these functions must operate on the component name stack
// they are to be only accessible from within class CrySizerComponentNameHelper
// which should be used through macro SIZER_COMPONENT_NAME
virtual void Push (const char* szComponentName);
virtual void PushSubcomponent (const char* szSubcomponentName);
virtual void Pop ();
// searches for the name in the name array; adds the name if it's not there and returns the index
size_t getNameIndex (size_t nParent, const char* szComponentName);
// returns the index of the current name on the top of the name stack
size_t getCurrentName() const;
protected:
friend class CrySizerStatsBuilder;
// the stack of subsystem names; the indices in the name array are kept, not the names themselves
typedef DynArray<size_t> NameStack;
NameStack m_stackNames;
// the array of names; each name ever pushed on the stack is present here
struct ComponentName
{
ComponentName (){}
ComponentName (const char* szName, size_t parent = 0)
: strName (szName)
, nParent (parent)
, numObjects(0)
, sizeObjects (0)
, sizeObjectsTotal (0)
{
}
void assign (const char* szName, size_t parent = 0)
{
strName = szName;
nParent = parent;
numObjects = 0;
sizeObjects = 0;
sizeObjectsTotal = 0;
arrChildren.clear();
}
// the component name, not including the parents' names
string strName;
// the index of the parent, 0 being the root
size_t nParent;
// the number of objects within this component
size_t numObjects;
// the size of the objects belonging to this component, in bytes
size_t sizeObjects;
// the total size of all objects; gets filled by the end() method of the CrySizerImpl
size_t sizeObjectsTotal;
// the children components
DynArray<size_t> arrChildren;
};
typedef DynArray<ComponentName> NameArray;
NameArray m_arrNames;
// the set of objects and their sizes: the key is the object address/id,
// the value is the size of the object and its name (the index of the name actually)
struct Object
{
const void* pId; // unique pointer identifying the object in memory
size_t nSize; // the size of the object in bytes
size_t nName; // the index of the name in the name array
Object ()
{clear(); }
Object (const void* id, size_t size = 0, size_t name = 0)
: pId(id)
, nSize(size)
, nName(name) {}
// the objects are sorted by their Id
bool operator < (const Object& right) const {return (UINT_PTR)pId < (UINT_PTR)right.pId; }
bool operator < (const void* right) const {return (UINT_PTR)pId < (UINT_PTR)right; }
//friend bool operator < (const void* left, const Object& right);
bool operator == (const Object& right) const {return pId == right.pId; }
void clear()
{
pId = NULL;
nSize = 0;
nName = 0;
}
};
typedef std::set <Object> ObjectSet;
// 2^g_nHashPower == the number of subsets comprising the hash
enum
{
g_nHashPower = 12
};
// hash size (number of subsets)
enum
{
g_nHashSize = 1 << g_nHashPower
};
// hash function for an address; returns value 0..1<<g_nHashSize
unsigned getHash (const void* pId);
unsigned GetDepthLevel(unsigned nCurrent);
ObjectSet m_setObjects[g_nHashSize];
// the last object inserted; this is a small optimization for our template implementaiton
// that often can add two times the same object
Object m_LastObject;
size_t m_nTotalSize;
};
#endif // CRYINCLUDE_CRYSYSTEM_CRYSIZERIMPL_H
+484
View File
@@ -0,0 +1,484 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "ILog.h"
#include "ITimer.h"
#include "ISystem.h"
#include "IConsole.h"
#include "IRenderer.h"
#include "CrySizerImpl.h"
#include "CrySizerStats.h"
#include "ITextModeConsole.h"
CrySizerStatsBuilder::CrySizerStatsBuilder (CrySizerImpl* pSizer, int nMinSubcomponentBytes)
: m_pSizer (pSizer)
, m_nMinSubcomponentBytes (nMinSubcomponentBytes < 0 || nMinSubcomponentBytes > 0x10000000 ? 0 : nMinSubcomponentBytes)
{
}
// creates the map of names from old (in the sizer Impl) to new (in the Stats)
void CrySizerStatsBuilder::processNames()
{
size_t numCompNames = m_pSizer->m_arrNames.size();
m_pStats->m_arrComponents.reserve (numCompNames);
m_pStats->m_arrComponents.clear();
m_mapNames.resize (numCompNames, (size_t)-1);
// add all root objects
addNameSubtree(0, 0);
}
//////////////////////////////////////////////////////////////////////////
// given the name in the old system, adds the subtree of names to the
// name map and components. In case all the subtree is empty, returns false and
// adds nothing
size_t CrySizerStatsBuilder::addNameSubtree (unsigned nDepth, size_t nName)
{
assert ((int)nName < m_pSizer->m_arrNames.size());
CrySizerImpl::ComponentName& rCompName = m_pSizer->m_arrNames[nName];
size_t sizeObjectsTotal = rCompName.sizeObjectsTotal;
if (sizeObjectsTotal <= m_nMinSubcomponentBytes)
{
return sizeObjectsTotal; // the subtree didn't pass
}
// the index of the component in the stats object (sorted by the depth-first traverse order)
size_t nNewName = m_pStats->m_arrComponents.size();
m_pStats->m_arrComponents.resize (nNewName + 1);
Component& rNewComp = m_pStats->m_arrComponents[nNewName];
rNewComp.strName = rCompName.strName;
rNewComp.nDepth = nDepth;
rNewComp.numObjects = rCompName.numObjects;
rNewComp.sizeBytes = rCompName.sizeObjects;
rNewComp.sizeBytesTotal = sizeObjectsTotal;
m_mapNames[nName] = nNewName;
// find the immediate children and sort them by their total size
typedef std::map<size_t, size_t> UintUintMap;
UintUintMap mapSizeName; // total size -> child index (name in old indexation)
for (int i = nName + 1; i < m_pSizer->m_arrNames.size(); ++i)
{
CrySizerImpl::ComponentName& rChild = m_pSizer->m_arrNames[i];
if (rChild.nParent == nName && rChild.sizeObjectsTotal > m_nMinSubcomponentBytes)
{
mapSizeName.insert (UintUintMap::value_type(rChild.sizeObjectsTotal, i));
}
}
// add the sorted components
/*
for (unsigned i = nName + 1; i < m_pSizer->m_arrNames.size(); ++i)
if (m_pSizer->m_arrNames[i].nParent == nName)
addNameSubtree(nDepth+1,i);
*/
for (UintUintMap::reverse_iterator it = mapSizeName.rbegin(); it != mapSizeName.rend(); ++it)
{
addNameSubtree(nDepth + 1, it->second);
}
return sizeObjectsTotal;
}
//////////////////////////////////////////////////////////////////////////
// creates the statistics out of the given CrySizerImpl into the given CrySizerStats
// Maps the old to new names according to the depth-walk tree rule
void CrySizerStatsBuilder::build (CrySizerStats* pStats)
{
m_pStats = pStats;
m_mapNames.clear();
processNames();
m_pSizer->clear();
pStats->refresh();
pStats->m_nAgeFrames = 0;
}
//////////////////////////////////////////////////////////////////////////
// constructs the statistics based on the given cry sizer
CrySizerStats::CrySizerStats (CrySizerImpl* pCrySizer)
{
CrySizerStatsBuilder builder (pCrySizer);
builder.build(this);
}
CrySizerStats::CrySizerStats ()
: m_nStartRow(0)
{
}
void CrySizerStats::updateKeys()
{
const unsigned int statSize = size();
//assume 10 pixels for font
unsigned int height = gEnv->pRenderer->GetHeight() / 12;
if (CryGetAsyncKeyState(VK_UP))
{
if (m_nStartRow > 0)
{
--m_nStartRow;
}
}
if (CryGetAsyncKeyState(VK_DOWN))
{
if (statSize > height + m_nStartRow)
{
++m_nStartRow;
}
}
if (CryGetAsyncKeyState(VK_RIGHT) & 1)
{
//assume 10 pixels for font
if (statSize > height)
{
m_nStartRow = statSize - height;
}
}
if (CryGetAsyncKeyState(VK_LEFT) & 1)
{
m_nStartRow = 0;
}
}
// if there is already such name in the map, then just returns the index
// of the compoentn in the component array; otherwise adds an entry to themap
// and to the component array nad returns its index
CrySizerStatsBuilder::Component& CrySizerStatsBuilder::mapName (unsigned nName)
{
assert (m_mapNames[nName] != -1);
return m_pStats->m_arrComponents[m_mapNames[nName]];
/*
IdToIdMap::iterator it = m_mapNames.find (nName);
if (it == m_mapNames.end())
{
unsigned nNewName = m_arrComponents.size();
m_mapNames.insert (IdToIdMap::value_type(nName, nNewName));
m_arrComponents.resize(nNewName + 1);
m_arrComponents[nNewName].strName.swap(m_pSizer->m_arrNames[nName]);
return m_arrComponents.back();
}
else
{
assert (it->second < m_arrComponents.size());
return m_arrComponents[it->second];
}
*/
}
// refreshes the statistics built after the component array is built
void CrySizerStats::refresh()
{
m_nMaxNameLength = 0;
for (size_t i = 0; i < m_arrComponents.size(); ++i)
{
size_t nLength = m_arrComponents[i].strName.length() + m_arrComponents[i].nDepth;
if (nLength > m_nMaxNameLength)
{
m_nMaxNameLength = nLength;
}
}
}
bool CrySizerStats::Component::GenericOrder::operator () (const Component& left, const Component& right) const
{
return left.strName < right.strName;
}
CrySizerStatsRenderer::CrySizerStatsRenderer (ISystem* pSystem, CrySizerStats* pStats, unsigned nMaxSubcomponentDepth, int nMinSubcomponentBytes)
: m_pStats(pStats)
, m_pRenderer(pSystem->GetIRenderer())
, m_pLog (pSystem->GetILog())
, m_pTextModeConsole(pSystem->GetITextModeConsole())
, m_nMinSubcomponentBytes (nMinSubcomponentBytes < 0 || nMinSubcomponentBytes > 0x10000000 ? 0x8000 : nMinSubcomponentBytes)
, m_nMaxSubcomponentDepth (nMaxSubcomponentDepth)
{
}
static void DrawStatsText(float x, float y, float fScale, float color[4], const char* format, ...)
{
va_list args;
va_start(args, format);
SDrawTextInfo ti;
ti.xscale = fScale;
ti.yscale = fScale;
ti.color[0] = color[0];
ti.color[1] = color[1];
ti.color[2] = color[2];
ti.color[3] = color[3];
ti.flags = eDrawText_2D | eDrawText_FixedSize | eDrawText_Monospace;
gEnv->pRenderer->DrawTextQueued(Vec3(x, y, 0.5f), ti, format, args);
va_end(args);
}
void CrySizerStatsRenderer::render(bool bRefreshMark)
{
if (!m_pStats->size())
{
return;
}
int x, y, dx, dy;
m_pRenderer->GetViewport(&x, &y, &dx, &dy);
// left coordinate of the text
unsigned nNameWidth = (unsigned)(m_pStats->getMaxNameLength() + 1);
if (nNameWidth < 25)
{
nNameWidth = 25;
}
float fCharScaleX = 1.2f;
float fLeft = 0;
float fTop = 8;
float fVStep = 9;
#ifdef _DEBUG
const char* szCountStr1 = "count";
const char* szCountStr2 = "_____";
#else // _DEBUG
const char* szCountStr1 = "", * szCountStr2 = "";
#endif // _DEBUG
float fTextColor[4] = {0.9f, 0.85f, 1, 0.85f};
DrawStatsText(fLeft, fTop, fCharScaleX, fTextColor,
"%-*s total partial %s", nNameWidth, bRefreshMark ? "Memory usage (refresh*)" : "Memory usage (refresh )", szCountStr1);
DrawStatsText(fLeft, fTop + fVStep * 0.25f, fCharScaleX, fTextColor,
"%*s _____ _______ %s", nNameWidth, "", szCountStr2);
unsigned nSubgroupDepth = 1;
// different colors used to paint the statistics subgroups
// a new statistic subgroup starts with a new subtree of depth <= specified
float fGray = 0;//0.45f;
float fLightGray = 0.5f;//0.8f;
float fColors[] =
{
fLightGray, fLightGray, fGray, 1,
1, 1, 1, 1,
fGray, 1, 1, 1,
1, fGray, 1, 1,
1, 1, fGray, 1,
fGray, fLightGray, 1, 1,
fGray, 1, fGray, 1,
1, fGray, fGray, 1
};
float* pColor = fColors;
unsigned statSize = m_pStats->size();
unsigned startRow = m_pStats->row();
unsigned i = 0;
for (; i < startRow; ++i)
{
const Component& rComp = (*m_pStats)[i];
if (rComp.nDepth <= nSubgroupDepth)
{
//switch the color
pColor += 4;
if (pColor >= fColors + sizeof(fColors) / sizeof(fColors[0]))
{
pColor = fColors;
}
fTop += fVStep * (0.333333f + (nSubgroupDepth - rComp.nDepth) * 0.15f);
}
}
for (unsigned r = startRow; i < statSize; ++i)
{
const Component& rComp = (*m_pStats)[i];
if (rComp.nDepth <= nSubgroupDepth)
{
//switch the color
pColor += 4;
if (pColor >= fColors + sizeof(fColors) / sizeof(fColors[0]))
{
pColor = fColors;
}
fTop += fVStep * (0.333333f + (nSubgroupDepth - rComp.nDepth) * 0.15f);
}
if (rComp.sizeBytesTotal <= m_nMinSubcomponentBytes || rComp.nDepth > m_nMaxSubcomponentDepth)
{
continue;
}
fTop += fVStep;
char szDepth[32] = " ..............................";
if (rComp.nDepth < sizeof(szDepth))
{
szDepth[rComp.nDepth] = '\0';
}
char szSize[32];
if (rComp.sizeBytes > 0)
{
if (rComp.sizeBytesTotal > rComp.sizeBytes)
{
azsprintf(szSize, "%7.3f %7.3f", rComp.getTotalSizeMBytes(), rComp.getSizeMBytes());
}
else
{
azsprintf(szSize, " %7.3f", rComp.getSizeMBytes());
}
}
else
{
assert (rComp.sizeBytesTotal > 0);
azsprintf(szSize, "%7.3f ", rComp.getTotalSizeMBytes());
}
char szCount[16];
#ifdef _DEBUG
if (rComp.numObjects)
{
azsprintf(szCount, "%zd" PRIu64 "", rComp.numObjects);
}
else
#endif
szCount[0] = '\0';
DrawStatsText(fLeft, fTop, fCharScaleX, pColor,
"%s%-*s:%s%s", szDepth, nNameWidth - rComp.nDepth, rComp.strName.c_str(), szSize, szCount);
if (m_pTextModeConsole)
{
string text;
text.Format("%s%-*s:%s%s", szDepth, nNameWidth - rComp.nDepth, rComp.strName.c_str(), szSize, szCount);
m_pTextModeConsole->PutText(0, r++, text.c_str());
}
}
float fLTGrayColor[4] = {fLightGray, fLightGray, fLightGray, 1.0f};
fTop += 0.25f * fVStep;
DrawStatsText(fLeft, fTop, fCharScaleX, fLTGrayColor,
"%-*s %s", nNameWidth, "___________________________", "________________");
fTop += fVStep;
const char* szOverheadNames[CrySizerStats::g_numTimers] =
{
".Collection",
".Transformation",
".Cleanup"
};
bool bOverheadsHeaderPrinted = false;
for (i = 0; i < CrySizerStats::g_numTimers; ++i)
{
float fTime = m_pStats->getTime(i);
if (fTime < 20)
{
continue;
}
// print the header
if (!bOverheadsHeaderPrinted)
{
DrawStatsText(fLeft, fTop, fCharScaleX, fTextColor,
"%-*s", nNameWidth, "Overheads");
fTop += fVStep;
bOverheadsHeaderPrinted = true;
}
DrawStatsText(fLeft, fTop, fCharScaleX, fTextColor,
"%-*s:%7.1f ms", nNameWidth, szOverheadNames[i], fTime);
fTop += fVStep;
}
}
void CrySizerStatsRenderer::dump(bool bUseKB)
{
if (!m_pStats->size())
{
return;
}
unsigned nNameWidth = (unsigned)(m_pStats->getMaxNameLength() + 1);
// left coordinate of the text
m_pLog->LogToFile ("Memory Statistics: %s", bUseKB ? "KB" : "MB");
m_pLog->LogToFile("%-*s TOTAL partial count", nNameWidth, "");
// different colors used to paint the statistics subgroups
// a new statistic subgroup starts with a new subtree of depth <= specified
for (unsigned i = 0; i < m_pStats->size(); ++i)
{
const Component& rComp = (*m_pStats)[i];
if (rComp.sizeBytesTotal <= m_nMinSubcomponentBytes || rComp.nDepth > m_nMaxSubcomponentDepth)
{
continue;
}
char szDepth[32] = " ..............................";
if (rComp.nDepth < sizeof(szDepth))
{
szDepth[rComp.nDepth] = '\0';
}
char szSize[32];
if (rComp.sizeBytes > 0)
{
if (rComp.sizeBytesTotal > rComp.sizeBytes)
{
azsprintf(szSize, bUseKB ? "%7.2f %7.2f" : "%7.3f %7.3f", bUseKB ? rComp.getTotalSizeKBytes() : rComp.getTotalSizeMBytes(), bUseKB ? rComp.getSizeKBytes() : rComp.getSizeMBytes());
}
else
{
azsprintf(szSize, bUseKB ? " %7.2f" : " %7.3f", bUseKB ? rComp.getSizeKBytes() : rComp.getSizeMBytes());
}
}
else
{
assert (rComp.sizeBytesTotal > 0);
azsprintf(szSize, bUseKB ? "%7.2f " : "%7.3f ", bUseKB ? rComp.getTotalSizeKBytes() : rComp.getTotalSizeMBytes());
}
char szCount[16];
if (rComp.numObjects)
{
azsprintf(szCount, "%8u", (unsigned int)rComp.numObjects);
}
else
{
szCount[0] = '\0';
}
m_pLog->LogToFile ("%s%-*s:%s%s", szDepth, nNameWidth - rComp.nDepth, rComp.strName.c_str(), szSize, szCount);
}
}
void CrySizerStats::startTimer(unsigned nTimer, ITimer* pTimer)
{
assert (nTimer < g_numTimers);
m_fTime[nTimer] = pTimer->GetAsyncCurTime();
}
void CrySizerStats::stopTimer(unsigned nTimer, ITimer* pTimer)
{
assert (nTimer < g_numTimers);
m_fTime[nTimer] = 1000 * (pTimer->GetAsyncCurTime() - m_fTime[nTimer]);
}
+207
View File
@@ -0,0 +1,207 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Declaration of the CrySizerStats class, which is used to
// calculate the memory usage by the subsystems and components, to help
// the artists keep the memory budged low.
#ifndef CRYINCLUDE_CRYSYSTEM_CRYSIZERSTATS_H
#define CRYINCLUDE_CRYSYSTEM_CRYSIZERSTATS_H
#pragma once
class CrySizerImpl;
//////////////////////////////////////////////////////////////////////////
// This class holds only the necessary statistics data, which can be carried
// over a few frames without significant impact on memory usage
// CrySizerImpl is an implementation of ICrySizer, which is used to collect
// those data; it must be destructed immediately after constructing the Stats
// object to avoid excessive memory usage.
class CrySizerStats
{
public:
// constructs the statistics based on the given cry sizer
CrySizerStats (CrySizerImpl* pCrySizer);
CrySizerStats ();
// this structure describes one component of the memory size statistics
struct Component
{
Component() {clear(); }
Component (const string& name, unsigned size = 0, unsigned num = 0)
: strName(name)
, sizeBytes(size)
, numObjects(num)
, nDepth(0) {}
void clear()
{
strName = "";
sizeBytes = 0;
numObjects = 0;
nDepth = 0;
}
// the name of the component, as it appeared in the push() call
string strName;
// the total size, in bytes, of objects in the component
size_t sizeBytes;
// the total size including the subcomponents
size_t sizeBytesTotal;
// the number of objects allocated
size_t numObjects;
unsigned nDepth;
float getSizeKBytes() const {return sizeBytes / float(1 << 10); }
float getTotalSizeKBytes () const {return sizeBytesTotal / float(1 << 10); }
float getSizeMBytes() const {return sizeBytes / float(1 << 20); }
float getTotalSizeMBytes () const {return sizeBytesTotal / float(1 << 20); }
struct NameOrder
{
bool operator () (const Component& left, const Component& right) const {return left.strName < right.strName; }
};
struct SizeOrder
{
bool operator () (const Component& left, const Component& right) const {return left.sizeBytes < right.sizeBytes; }
};
struct GenericOrder
{
bool operator () (const Component& left, const Component& right) const;
};
};
// returns the number of different subsystems/components used
unsigned numComponents() const {return (unsigned)m_arrComponents.size(); }
// returns the name of the i-th component
const Component& getComponent(unsigned nComponent) const {return m_arrComponents[nComponent]; }
unsigned size() const {return numComponents(); }
const Component& operator [] (unsigned i) const {return getComponent(i); }
const Component& operator [] (signed i) const {return getComponent(i); }
unsigned row() const {return m_nStartRow; }
void updateKeys();
size_t getMaxNameLength() const {return m_nMaxNameLength; }
enum
{
g_numTimers = 3
};
void startTimer(unsigned nTimer, ITimer* pTimer);
void stopTimer(unsigned nTimer, ITimer* pTimer);
float getTime(unsigned nTimer) const {assert (nTimer < g_numTimers); return m_fTime[nTimer]; }
int getAgeFrames() const {return m_nAgeFrames; }
void incAgeFrames() {++m_nAgeFrames; }
protected:
// refreshes the statistics built after the component array is built
void refresh();
protected:
// the names of the components
typedef std::vector<Component> ComponentArray;
ComponentArray m_arrComponents;
// the maximum length of the component name, in characters
size_t m_nMaxNameLength;
// the timer that counts the time spent on statistics gathering
float m_fTime[g_numTimers];
// the age of the statistics, in frames
int m_nAgeFrames;
//current row offset inc/dec by cursor keys
unsigned m_nStartRow;
friend class CrySizerStatsBuilder;
};
//////////////////////////////////////////////////////////////////////////
// this is the constructor for the CrySizerStats
class CrySizerStatsBuilder
{
public:
CrySizerStatsBuilder (CrySizerImpl* pSizer, int nMinSubcomponentBytes = 0);
void build (CrySizerStats* pStats);
protected:
typedef CrySizerStats::Component Component;
// if there is already such name in the map, then just returns the index
// of the compoentn in the component array; otherwise adds an entry to themap
// and to the component array nad returns its index
Component& mapName (unsigned nName);
// creates the map of names from old to new, and initializes the components themselves
void processNames();
// given the name in the old system, adds the subtree of names to the
// name map and components. In case all the subtree is empty, returns 0 and
// adds nothing. Otherwise, returns the total size of objects belonging to the
// subtree
size_t addNameSubtree (unsigned nDepth, size_t nName);
protected:
CrySizerStats* m_pStats;
CrySizerImpl* m_pSizer;
// this is the mapping from the old names into the new componentn indices
typedef std::vector<size_t> IdToIdMap;
// from old to new
IdToIdMap m_mapNames;
// this is the threshold: if the total number of bytes in the subcomponent
// is less than this, the subcomponent isn't shown
unsigned m_nMinSubcomponentBytes;
};
//////////////////////////////////////////////////////////////////////////
// Renders the given usage stats; gets created upon every rendering
class CrySizerStatsRenderer
{
public:
// constructor
CrySizerStatsRenderer (ISystem* pSystem, CrySizerStats* pStats, unsigned nMaxDepth = 2, int nMinSubcomponentBytes = -1);
void render(bool bRefreshMark = false);
// dumps it to log. uses MB as default
void dump (bool bUseKB = false);
protected: // -------------------------------------------------
typedef CrySizerStats::Component Component;
IRenderer* m_pRenderer; //
ILog* m_pLog; //
CrySizerStats* m_pStats; //
ITextModeConsole* m_pTextModeConsole;
// this is the threshold: if the total number of bytes in the subcomponent
// is less than this, the subcomponent isn't shown
unsigned m_nMinSubcomponentBytes;
// the max depth of the branch to output
unsigned m_nMaxSubcomponentDepth;
};
#endif // CRYINCLUDE_CRYSYSTEM_CRYSIZERSTATS_H
+91
View File
@@ -0,0 +1,91 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// German (Germany) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU)
LANGUAGE LANG_GERMAN, SUBLANG_GERMAN
#pragma code_page(1252)
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "000904b0"
BEGIN
VALUE "CompanyName", "Amazon.com, Inc."
VALUE "FileVersion", "1, 0, 0, 1"
VALUE "LegalCopyright", "Portions of this file Copyright (c) Amazon.com, Inc. or its affiliates. All Rights Reserved. Original file Copyright (c) Crytek GMBH. Used under license by Amazon.com, Inc. and its affiliates."
VALUE "ProductName", "Lumberyard"
VALUE "ProductVersion", "1, 0, 0, 1"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x9, 1200
END
END
#endif // German (Germany) resources
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // English (United States) resources
@@ -0,0 +1,14 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// If you make changes in ICryPak.h, make changes here, to dirty the PCH.
#include "CrySystem_precompiled.h"
@@ -0,0 +1,138 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Precompiled Header.
#pragma once
#include <AzCore/PlatformIncl.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <fcntl.h>
#if defined(AZ_RESTRICTED_PLATFORM)
#undef AZ_RESTRICTED_SECTION
#define STDAFX_H_SECTION_1 1
#define STDAFX_H_SECTION_2 2
#endif
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION STDAFX_H_SECTION_1
#include AZ_RESTRICTED_FILE(CrySystem_precompiled_h)
#elif defined(LINUX) // Scrubber friendly negated define pattern
#elif !defined(APPLE)
#include <memory.h>
#include <malloc.h>
#endif
//on mac the precompiled header is auto included in every .c and .cpp file, no include line necessary.
//.c files don't like the cpp things in here
#ifdef __cplusplus
#include <vector>
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION STDAFX_H_SECTION_2
#include AZ_RESTRICTED_FILE(CrySystem_precompiled_h)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#elif defined(APPLE) // Scrubber friendly negated define pattern
#elif defined(ANDROID) // Scrubber friendly negated define pattern
#elif defined(LINUX)
# include <sys/io.h>
#else
# include <io.h>
#endif
//#define DEFINE_MODULE_NAME "CrySystem"
#define CRYSYSTEM_EXPORTS
#include <platform.h>
#if defined(WIN32) || defined(WIN64) || defined(APPLE) || defined(LINUX)
#if defined(DEDICATED_SERVER)
// enable/disable map load slicing functionality from the build
#define MAP_LOADING_SLICING
#endif
#endif
#ifdef WIN32
#include <CryWindows.h>
#include <tlhelp32.h>
#undef GetCharWidth
#undef GetUserName
#endif
/////////////////////////////////////////////////////////////////////////////
// CRY Stuff ////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
#include "Cry_Math.h"
#include <Cry_Camera.h>
#include <smartptr.h>
#include <Range.h>
#include <CrySizer.h>
#include <StlUtils.h>
inline int RoundToClosestMB(size_t memSize)
{
// add half a MB and shift down to get closest MB
return((int) ((memSize + (1 << 19)) >> 20));
}
//////////////////////////////////////////////////////////////////////////
// For faster compilation
//////////////////////////////////////////////////////////////////////////
#include <IRenderer.h>
#include <CryFile.h>
#include <ISystem.h>
#include <I3DEngine.h>
#include <ITimer.h>
#include <IPhysics.h>
#include <IXml.h>
#include <ICmdLine.h>
#include <IConsole.h>
#include <ILog.h>
/////////////////////////////////////////////////////////////////////////////
//forward declarations for common Interfaces.
/////////////////////////////////////////////////////////////////////////////
class ITexture;
struct IRenderer;
struct ISystem;
struct ITimer;
struct IFFont;
struct IKeyboard;
struct ICVar;
struct IConsole;
struct IProcess;
namespace AZ::IO
{
struct IArchive;
}
struct ICryFont;
struct I3DEngine;
struct IMovieSystem;
struct IAudioSystem;
struct IPhysicalWorld;
#endif //__cplusplus
@@ -0,0 +1,259 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
//////////////////////////////////////////////////////////////////////////
// NOTE: INTERNAL HEADER NOT FOR PUBLIC USE
// This header should only be include by SystemThreading.cpp only
// It provides an interface for PThread intrinsics
// It's only client should be CThreadManager which should manage all thread interaction
#if !defined(INCLUDED_FROM_SYSTEM_THREADING_CPP)
# error "CRYTEK INTERNAL HEADER. ONLY INCLUDE FROM SYSTEMTHRADING.CPP."
#endif
//////////////////////////////////////////////////////////////////////////
#define DEFAULT_THREAD_STACK_SIZE_KB 0
#define CRY_PTHREAD_THREAD_NAME_MAX 16
//////////////////////////////////////////////////////////////////////////
// THREAD CREATION AND MANAGMENT
//////////////////////////////////////////////////////////////////////////
namespace CryThreadUtil
{
// Define type for platform specific thread handle
typedef pthread_t TThreadHandle;
struct SThreadCreationDesc
{
// Define platform specific thread entry function functor type
typedef void* (* EntryFunc)(void*);
const char* szThreadName;
EntryFunc fpEntryFunc;
void* pArgList;
uint32 nStackSizeInBytes;
};
//////////////////////////////////////////////////////////////////////////
TThreadHandle CryGetCurrentThreadHandle()
{
return (TThreadHandle)pthread_self();
}
//////////////////////////////////////////////////////////////////////////
// Note: Handle must be closed lated via CryCloseThreadHandle()
TThreadHandle CryDuplicateThreadHandle(const TThreadHandle& hThreadHandle)
{
// Do not do anything
// If you add a new platform which duplicates handles make sure to mirror the change in CryCloseThreadHandle(..)
return hThreadHandle;
}
//////////////////////////////////////////////////////////////////////////
void CryCloseThreadHandle(TThreadHandle& hThreadHandle)
{
pthread_detach(hThreadHandle);
}
//////////////////////////////////////////////////////////////////////////
threadID CryGetCurrentThreadId()
{
return threadID(pthread_self());
}
//////////////////////////////////////////////////////////////////////////
threadID CryGetThreadId(TThreadHandle hThreadHandle)
{
return threadID(hThreadHandle);
}
//////////////////////////////////////////////////////////////////////////
// Note: On OSX the thread name can only be set by the thread itself.
void CrySetThreadName(TThreadHandle pThreadHandle, const char* sThreadName)
{
char threadName[CRY_PTHREAD_THREAD_NAME_MAX];
if (!cry_strcpy(threadName, sThreadName))
{
CryLog("<ThreadInfo> CrySetThreadName: input thread name '%s' truncated to '%s'", sThreadName, threadName);
}
#if AZ_TRAIT_OS_PLATFORM_APPLE
// On OSX the thread name can only be set by the thread itself.
assert(pthread_equal(pthread_self(), (pthread_t )pThreadHandle));
if (pthread_setname_np(threadName) != 0)
#else
if (pthread_setname_np(pThreadHandle, threadName) != 0)
#endif
{
switch (errno)
{
case ERANGE:
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> CrySetThreadName: Unable to rename thread \"%s\". Error Msg: \"Name to long. Exceeds %d bytes.\"", sThreadName, CRY_PTHREAD_THREAD_NAME_MAX);
break;
default:
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> CrySetThreadName: Unsupported error code: %i", errno);
break;
}
}
}
//////////////////////////////////////////////////////////////////////////
void CrySetThreadAffinityMask(TThreadHandle pThreadHandle, DWORD dwAffinityMask)
{
#if defined(AZ_PLATFORM_ANDROID)
// Not supported on ANDROID
// Alternative solution
// Watch out that android will clear the mask after a core has been switched off hence loosing the affinity mask setting!
// http://stackoverflow.com/questions/16319725/android-set-thread-affinity
#elif AZ_TRAIT_OS_PLATFORM_APPLE
# pragma message "Warning: <ThreadInfo> CrySetThreadAffinityMask not implemented for platform"
// Implementation details can be found here
// https://developer.apple.com/library/mac/releasenotes/Performance/RN-AffinityAPI/
#else
cpu_set_t cpu_mask;
CPU_ZERO(&cpu_mask);
for (int cpu = 0; cpu < sizeof(cpu_mask) * 8; ++cpu)
{
if (dwAffinityMask & (1 << cpu))
{
CPU_SET(cpu, &cpu_mask);
}
}
if (sched_setaffinity(0, sizeof(cpu_mask), &cpu_mask) != 0)
{
switch (errno)
{
case EFAULT:
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> CrySetThreadAffinityMask: Supplied memory address was invalid.");
break;
case EINVAL:
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> CrySetThreadAffinityMask: The affinity bit mask [%u] contains no processors that are currently physically on the system and permitted to the process .", dwAffinityMask);
break;
case EPERM:
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> CrySetThreadAffinityMask: The calling process does not have appropriate privileges. Mask [%u].", dwAffinityMask);
break;
case ESRCH:
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> CrySetThreadAffinityMask: The process whose ID is pid could not be found.");
break;
default:
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> CrySetThreadAffinityMask: Unsupported error code: %i", errno);
break;
}
}
#endif
}
//////////////////////////////////////////////////////////////////////////
void CrySetThreadPriority(TThreadHandle pThreadHandle, DWORD dwPriority)
{
int policy;
struct sched_param param;
pthread_getschedparam(pThreadHandle, &policy, &param);
param.sched_priority = sched_get_priority_max(dwPriority);
pthread_setschedparam(pThreadHandle, policy, &param);
}
//////////////////////////////////////////////////////////////////////////
void CrySetThreadPriorityBoost(TThreadHandle pThreadHandle, bool bEnabled)
{
// Not supported
}
//////////////////////////////////////////////////////////////////////////
bool CryCreateThread(TThreadHandle* pThreadHandle, const SThreadCreationDesc& threadDesc)
{
uint32 nStackSize = threadDesc.nStackSizeInBytes != 0 ? threadDesc.nStackSizeInBytes : DEFAULT_THREAD_STACK_SIZE_KB * 1024;
assert(pThreadHandle != reinterpret_cast<TThreadHandle*>(THREADID_NULL));
pthread_attr_t threadAttr;
sched_param schedParam;
pthread_attr_init(&threadAttr);
pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_JOINABLE);
pthread_attr_setstacksize(&threadAttr, nStackSize);
const int err = pthread_create(
pThreadHandle,
&threadAttr,
threadDesc.fpEntryFunc,
threadDesc.pArgList);
// Handle error on thread creation
switch (err)
{
case 0:
// No error
break;
case EAGAIN:
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Unable to create thread \"%s\". Error Msg: \"Insufficient resources to create another thread, or a system-imposed limit on the number of threads was encountered.\"", threadDesc.szThreadName);
return false;
case EINVAL:
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Unable to create thread \"%s\". Error Msg: \"Invalid attribute setting for thread creation.\"", threadDesc.szThreadName);
return false;
case EPERM:
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Unable to create thread \"%s\". Error Msg: \"No permission to set the scheduling policy and parameters specified in attribute setting\"", threadDesc.szThreadName);
return false;
default:
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Unable to create thread \"%s\". Unknown error message. Error code %i", threadDesc.szThreadName, err);
break;
}
// Print info to log
CryComment("<ThreadInfo>: New thread \"%s\" | StackSize: %u(KB)", threadDesc.szThreadName, threadDesc.nStackSizeInBytes / 1024);
return true;
}
//////////////////////////////////////////////////////////////////////////
void CryThreadExitCall()
{
// Notes on: pthread_exit
// A thread that was create with pthread_create implicitly calls pthread_exit when the thread returns from its start routine (the function that was first called after a thread was created).
// pthread_exit(NULL);
}
}
//////////////////////////////////////////////////////////////////////////
// FLOATING POINT EXCEPTIONS
//////////////////////////////////////////////////////////////////////////
namespace CryThreadUtil
{
///////////////////////////////////////////////////////////////////////////
void EnableFloatExceptions(EFPE_Severity eFPESeverity)
{
// TODO:
// Not implemented
// for potential implementation see http://linux.die.net/man/3/feenableexcept
}
//////////////////////////////////////////////////////////////////////////
void EnableFloatExceptions(threadID nThreadId, EFPE_Severity eFPESeverity)
{
// TODO:
// Not implemented
// for potential implementation see http://linux.die.net/man/3/feenableexcept
}
//////////////////////////////////////////////////////////////////////////
uint GetFloatingPointExceptionMask()
{
// Not implemented
return ~0;
}
//////////////////////////////////////////////////////////////////////////
void SetFloatingPointExceptionMask(uint nMask)
{
// Not implemented
}
}
@@ -0,0 +1,430 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
//////////////////////////////////////////////////////////////////////////
// NOTE: INTERNAL HEADER NOT FOR PUBLIC USE
// This header should only be include by SystemThreading.cpp only
// It provides an interface for WinApi intrinsics
// It's only client should be CThreadManager which should manage all thread interaction
#pragma once
#if defined(AZ_RESTRICTED_PLATFORM)
#undef AZ_RESTRICTED_SECTION
#define CRYTHREADUTIL_WIN32_THREAD_H_SECTION_1 1
#define CRYTHREADUTIL_WIN32_THREAD_H_SECTION_2 2
#endif
#if !defined(INCLUDED_FROM_SYSTEM_THREADING_CPP)
# error "CRYTEK INTERNAL HEADER. ONLY INCLUDE FROM SYSTEMTHRADING.CPP."
#endif
//////////////////////////////////////////////////////////////////////////
#define DEFAULT_THREAD_STACK_SIZE_KB 0
// Returns the last Win32 error, in string format. Returns an empty string if there is no error.
static string GetLastErrorAsString()
{
// Get the error message, if any.
DWORD errorMessageID = GetLastError();
if (errorMessageID == 0)
{
return "";
}
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION CRYTHREADUTIL_WIN32_THREAD_H_SECTION_1
#include AZ_RESTRICTED_FILE(CryThreadUtil_win32_thread_h)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#else
LPSTR messageBuffer = nullptr;
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), messageBuffer, 0, NULL);
string message(messageBuffer, size);
// Free the buffer.
LocalFree(messageBuffer);
return message;
#endif
}
//////////////////////////////////////////////////////////////////////////
// THREAD CREATION AND MANAGMENT
//////////////////////////////////////////////////////////////////////////
namespace CryThreadUtil
{
// Define type for platform specific thread handle
typedef THREAD_HANDLE TThreadHandle;
struct SThreadCreationDesc
{
// Define platform specific thread entry function functor type
typedef unsigned int(_stdcall * EntryFunc)(void*);
const char* szThreadName;
EntryFunc fpEntryFunc;
void* pArgList;
uint32 nStackSizeInBytes;
};
//////////////////////////////////////////////////////////////////////////
TThreadHandle CryGetCurrentThreadHandle()
{
return GetCurrentThread(); // most likely returns pseudo handle (0xfffffffe)
}
//////////////////////////////////////////////////////////////////////////
// Note: Handle must be closed lated via CryCloseThreadHandle()
TThreadHandle CryDuplicateThreadHandle(const TThreadHandle& hThreadHandle)
{
// NOTES:
// GetCurrentThread() may return a psydo handle to the current thread
// to avoid going into the slower kernel mode.
// Hence the handle is useless when being used from an other thread.
// - GetCurrentThread() -> 0xfffffffe
// - GetCurrentProcess() -> 0xffffffff
HANDLE hRealHandle = 0;
DuplicateHandle(GetCurrentProcess(), // Source Process Handle.
hThreadHandle, // Source Handle to dup.
GetCurrentProcess(), // Target Process Handle.
&hRealHandle, // Target Handle pointer.
0, // Options flag.
TRUE, // Inheritable flag
DUPLICATE_SAME_ACCESS); // Options
return (TThreadHandle)hRealHandle;
}
//////////////////////////////////////////////////////////////////////////
void CryCloseThreadHandle(TThreadHandle& hThreadHandle)
{
if (hThreadHandle)
{
CloseHandle(hThreadHandle);
}
}
//////////////////////////////////////////////////////////////////////////
threadID CryGetCurrentThreadId()
{
return GetCurrentThreadId();
}
//////////////////////////////////////////////////////////////////////////
threadID CryGetThreadId(TThreadHandle hThreadHandle)
{
return GetThreadId(hThreadHandle);
}
//////////////////////////////////////////////////////////////////////////
void CrySetThreadName(TThreadHandle pThreadHandle, const char* sThreadName)
{
const DWORD MS_VC_EXCEPTION = 0x406D1388;
struct SThreadNameDesc
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
};
SThreadNameDesc info;
info.dwType = 0x1000;
info.szName = sThreadName;
info.dwThreadID = GetThreadId(pThreadHandle);
info.dwFlags = 0;
AZ_PUSH_DISABLE_WARNING(6312 6322, "-Wunknown-warning-option")
// warning C6312: Possible infinite loop: use of the constant EXCEPTION_CONTINUE_EXECUTION in the exception-filter expression of a try-except
// warning C6322: empty _except block
__try
{
// Raise exception to set thread name for attached debugger
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR*)&info);
}
__except (GetExceptionCode() == MS_VC_EXCEPTION ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER)
{
}
AZ_POP_DISABLE_WARNING
}
//////////////////////////////////////////////////////////////////////////
void CrySetThreadAffinityMask(TThreadHandle pThreadHandle, DWORD dwAffinityMask)
{
SetThreadAffinityMask(pThreadHandle, dwAffinityMask);
}
//////////////////////////////////////////////////////////////////////////
void CrySetThreadPriority(TThreadHandle pThreadHandle, DWORD dwPriority)
{
if (!SetThreadPriority(pThreadHandle, dwPriority))
{
string errMsg = GetLastErrorAsString();
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Unable to set thread priority. System Error Msg: \"%s\"", errMsg.c_str());
return;
}
}
//////////////////////////////////////////////////////////////////////////
void CrySetThreadPriorityBoost(TThreadHandle pThreadHandle, bool bEnabled)
{
SetThreadPriorityBoost(pThreadHandle, !bEnabled);
}
//////////////////////////////////////////////////////////////////////////
bool CryCreateThread(TThreadHandle* pThreadHandle, const SThreadCreationDesc& threadDesc)
{
const uint32 nStackSize = threadDesc.nStackSizeInBytes != 0 ? threadDesc.nStackSizeInBytes : DEFAULT_THREAD_STACK_SIZE_KB * 1024;
// Create thread
unsigned int threadId = 0;
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION CRYTHREADUTIL_WIN32_THREAD_H_SECTION_2
#include AZ_RESTRICTED_FILE(CryThreadUtil_win32_thread_h)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#else
*pThreadHandle = (void*)_beginthreadex(NULL, nStackSize, threadDesc.fpEntryFunc, threadDesc.pArgList, CREATE_SUSPENDED, &threadId);
#endif
if (!(*pThreadHandle))
{
string errMsg = GetLastErrorAsString();
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Unable to create thread \"%s\". System Error Msg: \"%s\"", threadDesc.szThreadName, errMsg.c_str());
return false;
}
// Start thread
ResumeThread(*pThreadHandle);
// Print info to log
CryComment("<ThreadInfo>: New thread \"%s\" | StackSize: %u(KB)", threadDesc.szThreadName, threadDesc.nStackSizeInBytes / 1024);
return true;
}
//////////////////////////////////////////////////////////////////////////
void CryThreadExitCall()
{
// Note on: ExitThread() (from MSDN)
// ExitThread is the preferred method of exiting a thread in C code.
// However, in C++ code, the thread is exited before any destructor can be called or any other automatic cleanup can be performed.
// Therefore, in C++ code, you should return from your thread function.
}
}
//////////////////////////////////////////////////////////////////////////
// FLOATING POINT EXCEPTIONS
//////////////////////////////////////////////////////////////////////////
namespace CryThreadUtil
{
///////////////////////////////////////////////////////////////////////////
void EnableFloatExceptions([[maybe_unused]] EFPE_Severity eFPESeverity)
{
AZ_PUSH_DISABLE_WARNING(4996, "-Wunknown-warning-option")
// Optimization
// Enable DAZ/FZ
// Denormals Are Zeros
// Flush-to-Zero
_controlfp(_DN_FLUSH, _MCW_DN);
_mm_setcsr(_mm_getcsr() | _MM_FLUSH_ZERO_ON);
#ifndef _RELEASE
if (eFPESeverity == eFPE_None)
{
// mask all floating exceptions off.
_controlfp(_MCW_EM, _MCW_EM);
_mm_setcsr(_mm_getcsr() | _MM_MASK_MASK);
}
else
{
// Clear pending exceptions
_fpreset();
if (eFPESeverity == eFPE_Basic)
{
// Enable:
// - _EM_ZERODIVIDE
// - _EM_INVALID
//
// Disable:
// - _EM_DENORMAL
// - _EM_OVERFLOW
// - _EM_UNDERFLOW
// - _EM_INEXACT
_controlfp(_EM_INEXACT | _EM_DENORMAL | _EM_UNDERFLOW | _EM_OVERFLOW, _MCW_EM);
_mm_setcsr((_mm_getcsr() & ~_MM_MASK_MASK) | (_MM_MASK_DENORM | _MM_MASK_INEXACT | _MM_MASK_UNDERFLOW | _MM_MASK_OVERFLOW));
//_mm_setcsr(_mm_getcsr() & ~0x280);
}
if (eFPESeverity == eFPE_All)
{
// Enable:
// - _EM_ZERODIVIDE
// - _EM_INVALID
// - _EM_UNDERFLOW
// - _EM_OVERFLOW
//
// Disable:
// - _EM_INEXACT
// - _EM_DENORMAL
_controlfp(_EM_INEXACT | _EM_DENORMAL, _MCW_EM);
_mm_setcsr((_mm_getcsr() & ~_MM_MASK_MASK) | (_MM_MASK_INEXACT | _MM_MASK_DENORM));
}
}
#endif // _RELEASE
AZ_POP_DISABLE_WARNING
}
//////////////////////////////////////////////////////////////////////////
void EnableFloatExceptions(threadID nThreadId, EFPE_Severity eFPESeverity)
{
if (eFPESeverity >= eFPE_LastEntry)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Floating Point Exception (FPE) severity is out of range. (%i)", eFPESeverity);
}
// Check if the thread ID matches the current thread
if (nThreadId == 0 || nThreadId == CryGetCurrentThreadId())
{
EnableFloatExceptions(eFPESeverity);
return;
}
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, true, nThreadId);
if (hThread == 0)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Unable to open thread. %p", hThread);
return;
}
SuspendThread(hThread);
CONTEXT ctx;
memset(&ctx, 0, sizeof(ctx));
ctx.ContextFlags = CONTEXT_ALL;
if (GetThreadContext(hThread, &ctx) == 0)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Unable to get thread context");
ResumeThread(hThread);
CloseHandle(hThread);
return;
}
#ifdef PLATFORM_64BIT
//////////////////////////////////////////////////////////////////////////
// Note:
// DO NOT USE ctx.FltSave.MxCsr ... SetThreadContext() will copy the value of ctx.MxCsr into it
//////////////////////////////////////////////////////////////////////////
DWORD& floatMxCsr = ctx.MxCsr; // Hold FPE Mask and Status for MMX (SSE) floating point registers
WORD& floatControlWord = ctx.FltSave.ControlWord; // Hold FPE Mask for floating point registers
WORD& floatStatuslWord = ctx.FltSave.StatusWord; // Holds FPE Status for floating point registers
#else
DWORD& floatMxCsr = *(DWORD*)(&ctx.ExtendedRegisters[24]); // Hold FPE Mask and Status for MMX (SSE) floating point registers
DWORD& floatControlWord = ctx.FloatSave.ControlWord; // Hold FPE Mask for floating point registers
DWORD& floatStatuslWord = ctx.FloatSave.StatusWord; // Holds FPE Status for floating point registers
#endif
// Flush-To-Zero Mode
// Two conditions must be met for FTZ processing to occur:
// - The FTZ bit (bit 15) in the MXCSR register must be masked (value = 1).
// - The underflow exception (bit 11) needs to be masked (value = 1).
// Set flush mode to zero mode
floatControlWord = (floatControlWord & ~_MCW_DN) | _DN_FLUSH;
floatMxCsr = (floatMxCsr & ~_MM_FLUSH_ZERO_MASK) | (_MM_FLUSH_ZERO_ON);
#ifndef _RELEASE
// Reset FPE bits
floatControlWord = floatControlWord | _MCW_EM;
floatMxCsr = floatMxCsr | _MM_MASK_MASK;
// Clear pending exceptions
floatStatuslWord = floatStatuslWord & ~(_SW_INEXACT | _SW_UNDERFLOW | _SW_OVERFLOW | _SW_ZERODIVIDE | _SW_INVALID | _SW_DENORMAL);
floatMxCsr = floatMxCsr & ~(_MM_EXCEPT_INEXACT | _MM_EXCEPT_UNDERFLOW | _MM_EXCEPT_OVERFLOW | _MM_EXCEPT_DIV_ZERO | _MM_EXCEPT_INVALID | _MM_EXCEPT_DENORM);
if (eFPESeverity == eFPE_Basic)
{
// Enable:
// - _EM_ZERODIVIDE
// - _EM_INVALID
//
// Disable:
// - _EM_DENORMAL
// - _EM_OVERFLOW
// - _EM_UNDERFLOW
// - _EM_INEXACT
floatControlWord = (floatControlWord & ~_MCW_EM) | (_EM_DENORMAL | _EM_INEXACT | EM_UNDERFLOW | _EM_OVERFLOW);
floatMxCsr = (floatMxCsr & ~_MM_MASK_MASK) | (_MM_MASK_DENORM | _MM_MASK_INEXACT | _MM_MASK_UNDERFLOW | _MM_MASK_OVERFLOW);
}
if (eFPESeverity == eFPE_All)
{
// Enable:
// - _EM_ZERODIVIDE
// - _EM_INVALID
// - _EM_UNDERFLOW
// - _EM_OVERFLOW
//
// Disable:
// - _EM_INEXACT
// - _EM_DENORMAL
floatControlWord = (floatControlWord & ~_MCW_EM) | (_EM_INEXACT | _EM_DENORMAL);
floatMxCsr = (floatMxCsr & ~_MM_MASK_MASK) | (_MM_MASK_INEXACT | _MM_MASK_DENORM);
}
#endif
ctx.ContextFlags = CONTEXT_ALL;
if (SetThreadContext(hThread, &ctx) == 0)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Error setting ThreadContext for ThreadID: %u", nThreadId);
ResumeThread(hThread);
CloseHandle(hThread);
return;
}
ResumeThread(hThread);
CloseHandle(hThread);
}
//////////////////////////////////////////////////////////////////////////
uint GetFloatingPointExceptionMask()
{
uint nMask = 0;
_clearfp();
_controlfp_s(&nMask, 0, 0);
return nMask;
}
//////////////////////////////////////////////////////////////////////////
void SetFloatingPointExceptionMask(uint nMask)
{
uint temp = 0;
_clearfp();
const unsigned int kAllowedBits = _MCW_DN | _MCW_EM | _MCW_RC;
_controlfp_s(&temp, nMask, kAllowedBits);
}
}
+44
View File
@@ -0,0 +1,44 @@
/*
* 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 "CrySystem_precompiled.h"
#include "CryTomcrypt.h"
//////////////////////////////////////////////////////////////////////////
#ifdef INCLUDE_LIBTOMCRYPT
prng_state g_yarrow_prng_state;
// Main public RSA key used for verifying Cry Pak comments
rsa_key g_rsa_key_public_for_sign;
void* LTC_CALL tomcrypt_Malloc(size_t size)
{
return CryModuleMalloc(size);
}
void* LTC_CALL tomcrypt_Realloc(void* ptr, size_t size)
{
return CryModuleRealloc(ptr, size);
}
void* LTC_CALL tomcrypt_Calloc(size_t num, size_t size)
{
return CryModuleCalloc(num, size);
}
void LTC_CALL tomcrypt_Free(void* ptr)
{
CryModuleFree(ptr);
}
#endif // INCLUDE_LIBTOMCRYPT
+47
View File
@@ -0,0 +1,47 @@
/*
* 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.
*
*/
#pragma once
#include "ProjectDefines.h"
#ifdef INCLUDE_LIBTOMCRYPT
#include "CryMemoryManager.h"
#define USE_LTM
#define LTM_DESC
#define LTC_EXPORT
#define LTC_NO_PROTOTYPES
#if defined(AZ_COMPILER_MSVC)
#define LTC_CALL __cdecl
#else
#define LTC_CALL
#endif
LTC_EXPORT void* LTC_CALL tomcrypt_Malloc(size_t size);
LTC_EXPORT void* LTC_CALL tomcrypt_Realloc(void* ptr, size_t size);
LTC_EXPORT void* LTC_CALL tomcrypt_Calloc(size_t num, size_t size);
LTC_EXPORT void LTC_CALL tomcrypt_Free(void* ptr);
#define XMALLOC tomcrypt_Malloc
#define XREALLOC tomcrypt_Realloc
#define XCALLOC tomcrypt_Calloc
#define XFREE tomcrypt_Free
#include <tomcrypt.h>
#undef byte // tomcrypt defines a byte macro which conflicts with out byte data type
#define STREAM_CIPHER_NAME "twofish"
extern prng_state g_yarrow_prng_state;
extern rsa_key g_rsa_key_public_for_sign;
#endif //INCLUDE_LIBTOMCRYPT
+34
View File
@@ -0,0 +1,34 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Header for adding a watermark to an exe, which can then be set
// by the external CryWaterMark program. To use, simply write:
//
// WATERMARKDATA(__blah);
//
// anywhere in the global scope in the program
#ifndef CRYINCLUDE_CRYSYSTEM_CRYWATERMARK_H
#define CRYINCLUDE_CRYSYSTEM_CRYWATERMARK_H
#pragma once
#define NUMMARKWORDS 10
#define WATERMARKDATA(name) unsigned int name[] = { 0xDEBEFECA, 0xFABECEDA, 0xADABAFBE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// (the name is such that you can have multiple watermarks in one exe, don't use
// names like "watermark" just incase you accidentally give out an exe with
// debug information).
#endif // CRYINCLUDE_CRYSYSTEM_CRYWATERMARK_H
@@ -0,0 +1,57 @@
/*
* 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 "CrySystem_precompiled.h"
#include <Cryptography/Crypto.h>
#include <Cryptography/StreamCipher.h>
//-----------------------------------------------------------------------------
void Crypto::EncryptBuffer(uint8* pOutput, const uint8* pInput, uint32 bufferLength, const uint8* pKey, uint32 keyLength)
{
if (pKey && (keyLength > 0))
{
StreamCipherState cipher;
m_streamCipher.Init(cipher, pKey, keyLength);
if (pInput && pOutput && (bufferLength > 0))
{
m_streamCipher.Encrypt(cipher, pInput, bufferLength, pOutput);
}
}
}
//-----------------------------------------------------------------------------
void Crypto::DecryptBuffer(uint8* pOutput, const uint8* pInput, uint32 bufferLength, const uint8* pKey, uint32 keyLength)
{
if (pKey && (keyLength > 0))
{
StreamCipherState cipher;
m_streamCipher.Init(cipher, pKey, keyLength);
if (pInput && pOutput && (bufferLength > 0))
{
m_streamCipher.Decrypt(cipher, pInput, bufferLength, pOutput);
}
}
}
//-----------------------------------------------------------------------------
IRijndael* Crypto::GetRijndael()
{
return &m_rijndael;
}
//-----------------------------------------------------------------------------
IStreamCipher* Crypto::GetStreamCipher()
{
return &m_streamCipher;
}
@@ -0,0 +1,43 @@
/*
* 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.
*
*/
#ifndef CRYINCLUDE_CRYSYSTEM_CRYPTO_H
#define CRYINCLUDE_CRYSYSTEM_CRYPTO_H
#pragma once
#include <ICrypto.h>
#include <Cryptography/rijndael.h>
#include <Cryptography/StreamCipher.h>
#include <Cryptography/Whirlpool.h>
class Crypto
: public ICrypto
{
public:
// Exposed block encryption
void EncryptBuffer(uint8* pOutput, const uint8* pInput, uint32 bufferLength, const uint8* pKey, uint32 keyLength) override;
void DecryptBuffer(uint8* pOutput, const uint8* pInput, uint32 bufferLength, const uint8* pKey, uint32 keyLength) override;
// Crypto implementations
IRijndael* GetRijndael() override;
IStreamCipher* GetStreamCipher() override;
void InitWhirlpoolHash(uint8* hash);
void InitWhirlpoolHash(uint8* hash, const string& str);
void InitWhirlpoolHash(uint8* hash, const uint8* input, size_t length);
protected:
Rijndael m_rijndael;
CStreamCipher m_streamCipher;
};
#endif // CRYINCLUDE_CRYSYSTEM_CRYPTO_H
@@ -0,0 +1,84 @@
/*
* 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 "CrySystem_precompiled.h"
#include "StreamCipher.h"
StreamCipherState CStreamCipher::BeginCipher(const uint8* pKey, uint32 keyLength)
{
StreamCipherState cipher;
Init(cipher, pKey, keyLength);
return cipher;
}
void CStreamCipher::Init(StreamCipherState& state, const uint8* key, int keyLen)
{
int i, j;
for (i = 0; i < 256; i++)
{
state.m_S[i] = i;
}
if (key)
{
for (i = j = 0; i < 256; i++)
{
uint8 temp;
j = (j + key[i % keyLen] + state.m_S[i]) & 255;
temp = state.m_S[i];
state.m_S[i] = state.m_S[j];
state.m_S[j] = temp;
}
}
state.m_I = state.m_J = 0;
for (i = 0; i < 1024; i++)
{
GetNext(state);
}
memcpy(state.m_StartS, state.m_S, sizeof(state.m_StartS));
state.m_StartI = state.m_I;
state.m_StartJ = state.m_J;
}
uint8 CStreamCipher::GetNext(StreamCipherState& state)
{
uint8 tmp;
state.m_I = (state.m_I + 1) & 0xff;
state.m_J = (state.m_J + state.m_S[state.m_I]) & 0xff;
tmp = state.m_S[state.m_J];
state.m_S[state.m_J] = state.m_S[state.m_I];
state.m_S[state.m_I] = tmp;
return state.m_S[(state.m_S[state.m_I] + state.m_S[state.m_J]) & 0xff];
}
void CStreamCipher::ProcessBuffer(StreamCipherState& state, const uint8* input, int inputLen, uint8* output, bool resetKey)
{
if (resetKey)
{
memcpy(state.m_S, state.m_StartS, sizeof(state.m_S));
state.m_I = state.m_StartI;
state.m_J = state.m_StartJ;
}
for (int i = 0; i < inputLen; i++)
{
output[i] = input[i] ^ GetNext(state);
}
}
@@ -0,0 +1,38 @@
/*
* 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.
*
*/
/****************************************************
A simple stream cipher based on RC4
****************************************************/
#ifndef CRYINCLUDE_CRYPTOGRAPHY_STREAMCIPHER_H
#define CRYINCLUDE_CRYPTOGRAPHY_STREAMCIPHER_H
#pragma once
#include <ICrypto.h>
class CStreamCipher
: public IStreamCipher
{
public:
StreamCipherState BeginCipher(const uint8* pKey, uint32 keyLength);
void Init(StreamCipherState& state, const uint8* key, int keyLen);
void Encrypt(StreamCipherState& state, const uint8* input, int inputLen, uint8* output) { ProcessBuffer(state, input, inputLen, output, true); }
void Decrypt(StreamCipherState& state, const uint8* input, int inputLen, uint8* output) { ProcessBuffer(state, input, inputLen, output, true); }
void EncryptStream(StreamCipherState& state, const uint8* input, int inputLen, uint8* output) { ProcessBuffer(state, input, inputLen, output, false); }
void DecryptStream(StreamCipherState& state, const uint8* input, int inputLen, uint8* output) { ProcessBuffer(state, input, inputLen, output, false); }
private:
uint8 GetNext(StreamCipherState& state);
void ProcessBuffer(StreamCipherState& state, const uint8* input, int inputLen, uint8* output, bool resetKey);
};
#endif // CRYINCLUDE_CRYPTOGRAPHY_STREAMCIPHER_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
/*
* 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.
*
*/
#ifndef CRYINCLUDE_CRYPTOGRAPHY_WHIRLPOOL_H
#define CRYINCLUDE_CRYPTOGRAPHY_WHIRLPOOL_H
#pragma once
bool WhirlpoolHash_Test();
#endif // CRYINCLUDE_CRYPTOGRAPHY_WHIRLPOOL_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,133 @@
/*
* 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.
*
*/
#ifndef CRYINCLUDE_CRYPTOGRAPHY_RIJNDAEL_H
#define CRYINCLUDE_CRYPTOGRAPHY_RIJNDAEL_H
#pragma once
#include <ICrypto.h>
//
// File : rijndael.h
// Creation date : Sun Nov 5 2000 03:21:05 CEST
// Author : Szymon Stefanek (stefanek@tin.it)
//
// Another implementation of the Rijndael cipher.
// This is intended to be an easily usable library file.
// This code is public domain.
// Based on the Vincent Rijmen and K.U.Leuven implementation 2.4.
//
//
// Original Copyright notice:
//
// rijndael-alg-fst.c v2.4 April '2000
// rijndael-alg-fst.h
// rijndael-api-fst.c
// rijndael-api-fst.h
//
// Optimised ANSI C code
//
// authors: v1.0: Antoon Bosselaers
// v2.0: Vincent Rijmen, K.U.Leuven
// v2.3: Paulo Barreto
// v2.4: Vincent Rijmen, K.U.Leuven
//
// This code is placed in the public domain.
//
//
// This implementation works on 128 , 192 , 256 bit keys
// and on 128 bit blocks
//
//
// Example of usage:
//
// // Input data
// unsigned char key[32]; // The key
// initializeYour256BitKey(); // Obviously initialized with sth
// const unsigned char * plainText = getYourPlainText(); // Your plain text
// int plainTextLen = strlen(plainText); // Plain text length
//
// // Encrypting
// Rijndael rin;
// unsigned char output[plainTextLen + 16];
//
// rin.init(Rijndael::CBC,Rijndael::Encrypt,key,Rijndael::Key32Bytes);
// // It is a good idea to check the error code
// int len = rin.padEncrypt(plainText,len,output);
// if(len >= 0)useYourEncryptedText();
// else encryptError(len);
//
// // Decrypting: we can reuse the same object
// unsigned char output2[len];
// rin.init(Rijndael::CBC,Rijndael::Decrypt,key,Rijndael::Key32Bytes));
// len = rin.padDecrypt(output,len,output2);
// if(len >= 0)useYourDecryptedText();
// else decryptError(len);
//
class Rijndael
: public IRijndael
{
public:
//////////////////////////////////////////////////////////////////////////////////////////
// API
//////////////////////////////////////////////////////////////////////////////////////////
// init(): Initializes the crypt session
// Returns RIJNDAEL_SUCCESS or an error code
// mode : Rijndael::ECB, Rijndael::CBC or Rijndael::CFB1
// You have to use the same mode for encrypting and decrypting
// dir : Rijndael::Encrypt or Rijndael::Decrypt
// A cipher instance works only in one direction
// (Well , it could be easily modified to work in both
// directions with a single init() call, but it looks
// useless to me...anyway , it is a matter of generating
// two expanded keys)
// key : array of unsigned octets , it can be 16 , 24 or 32 bytes long
// this CAN be binary data (it is not expected to be null terminated)
// keyLen : Rijndael::Key16Bytes , Rijndael::Key24Bytes or Rijndael::Key32Bytes
// initVector: initialization vector, you will usually use 0 here
int init(RijndaelState& state, RijndaelMode mode, RijndaelDirection dir, const uint8* key, RijndaelKeyLength keyLen, uint8* initVector = 0);
// Encrypts the input array (can be binary data)
// The input array length must be a multiple of 16 bytes, the remaining part
// is DISCARDED.
// so it actually encrypts inputLen / 128 blocks of input and puts it in outBuffer
// Input len is in BITS!
// outBuffer must be at least inputLen / 8 bytes long.
// Returns the encrypted buffer length in BITS or an error code < 0 in case of error
int blockEncrypt(RijndaelState& state, const uint8* input, int inputLen, uint8* outBuffer);
// Encrypts the input array (can be binary data)
// The input array can be any length , it is automatically padded on a 16 byte boundary.
// Input len is in BYTES!
// outBuffer must be at least (inputLen + 16) bytes long
// Returns the encrypted buffer length in BYTES or an error code < 0 in case of error
int padEncrypt(RijndaelState& state, const uint8* input, int inputOctets, uint8* outBuffer);
// Decrypts the input vector
// Input len is in BITS!
// outBuffer must be at least inputLen / 8 bytes long
// Returns the decrypted buffer length in BITS and an error code < 0 in case of error
int blockDecrypt(RijndaelState& state, const uint8* input, int inputLen, uint8* outBuffer);
// Decrypts the input vector
// Input len is in BYTES!
// outBuffer must be at least inputLen bytes long
// Returns the decrypted buffer length in BYTES and an error code < 0 in case of error
int padDecrypt(RijndaelState& state, const uint8* input, int inputOctets, uint8* outBuffer);
protected:
void keySched(RijndaelState & state, uint8 key[_MAX_KEY_COLUMNS][4]);
void keyEncToDec(RijndaelState& state);
void encrypt(RijndaelState & state, const uint8 a[16], uint8 b[16]);
void decrypt(RijndaelState & state, const uint8 a[16], uint8 b[16]);
};
#endif // CRYINCLUDE_CRYPTOGRAPHY_RIJNDAEL_H
@@ -0,0 +1,155 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "CustomMemoryHeap.h"
#if defined(AZ_RESTRICTED_PLATFORM)
#undef AZ_RESTRICTED_SECTION
#define CUSTOMMEMORYHEAP_CPP_SECTION_1 1
#define CUSTOMMEMORYHEAP_CPP_SECTION_2 2
#define CUSTOMMEMORYHEAP_CPP_SECTION_3 3
#endif
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION CUSTOMMEMORYHEAP_CPP_SECTION_1
#include AZ_RESTRICTED_FILE(CustomMemoryHeap_cpp)
#endif
//////////////////////////////////////////////////////////////////////////
CCustomMemoryHeapBlock::CCustomMemoryHeapBlock(CCustomMemoryHeap* pHeap)
: m_pHeap(pHeap)
, m_pData(0)
, m_nSize(0)
, m_nGPUHandle(0)
{
}
//////////////////////////////////////////////////////////////////////////
CCustomMemoryHeapBlock::~CCustomMemoryHeapBlock()
{
m_pHeap->DeallocateBlock(this);
}
//////////////////////////////////////////////////////////////////////////
void* CCustomMemoryHeapBlock::GetData()
{
return m_pData;
}
//////////////////////////////////////////////////////////////////////////
void CCustomMemoryHeapBlock::CopyMemoryRegion(void* pOutputBuffer, size_t nOffset, size_t nSize)
{
assert(nOffset + nSize <= m_nSize);
if (nOffset + nSize <= m_nSize)
{
memcpy(pOutputBuffer, (uint8*)m_pData + nOffset, nSize);
}
else
{
CryFatalError("Bad CopyMemoryRegion range");
}
}
//////////////////////////////////////////////////////////////////////////
ICustomMemoryBlock* CCustomMemoryHeap::AllocateBlock(size_t const nAllocateSize, char const* const sUsage, size_t const nAlignment /* = 16 */)
{
CCustomMemoryHeapBlock* pBlock = new CCustomMemoryHeapBlock(this);
pBlock->m_sUsage = sUsage;
pBlock->m_nSize = nAllocateSize;
switch (m_eAllocPolicy)
{
case IMemoryManager::eapDefaultAllocator:
{
size_t allocated = 0;
pBlock->m_pData = CryMalloc(nAllocateSize, allocated, nAlignment);
break;
}
case IMemoryManager::eapPageMapped:
pBlock->m_pData = CryMemory::AllocPages(nAllocateSize);
break;
case IMemoryManager::eapCustomAlignment:
#if defined(DEBUG)
if (nAlignment == 0)
{
CryFatalError("CCustomMemoryHeap: trying to allocate memory via eapCustomAlignment with an alignment of zero!");
}
#endif
pBlock->m_pData = CryModuleMemalign(nAllocateSize, nAlignment);
break;
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION CUSTOMMEMORYHEAP_CPP_SECTION_2
#include AZ_RESTRICTED_FILE(CustomMemoryHeap_cpp)
#endif
default:
CryFatalError("CCustomMemoryHeap: unknown allocation policy during AllocateBlock!");
break;
}
CryInterlockedAdd(&m_nAllocatedSize, nAllocateSize);
return pBlock;
}
void CCustomMemoryHeap::DeallocateBlock(CCustomMemoryHeapBlock* pBlock)
{
switch (m_eAllocPolicy)
{
case IMemoryManager::eapDefaultAllocator:
CryFree(pBlock->m_pData, 0);
break;
case IMemoryManager::eapPageMapped:
CryMemory::FreePages(pBlock->m_pData, pBlock->GetSize());
break;
case IMemoryManager::eapCustomAlignment:
CryModuleMemalignFree(pBlock->m_pData);
break;
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION CUSTOMMEMORYHEAP_CPP_SECTION_3
#include AZ_RESTRICTED_FILE(CustomMemoryHeap_cpp)
#endif
default:
CryFatalError("CCustomMemoryHeap: unknown allocation policy during DeallocateBlock!");
break;
}
int nAllocateSize = (int)pBlock->m_nSize;
CryInterlockedAdd(&m_nAllocatedSize, -nAllocateSize);
}
//////////////////////////////////////////////////////////////////////////
void CCustomMemoryHeap::GetMemoryUsage(ICrySizer* pSizer)
{
pSizer->AddObject(this, m_nAllocatedSize);
}
//////////////////////////////////////////////////////////////////////////
size_t CCustomMemoryHeap::GetAllocated()
{
return m_nAllocatedSize;
}
//////////////////////////////////////////////////////////////////////////
CCustomMemoryHeap::CCustomMemoryHeap(IMemoryManager::EAllocPolicy const eAllocPolicy)
{
m_nAllocatedSize = 0;
m_eAllocPolicy = eAllocPolicy;
m_nTraceHeapHandle = 0;
}
//////////////////////////////////////////////////////////////////////////
CCustomMemoryHeap::~CCustomMemoryHeap()
{
}
+959
View File
@@ -0,0 +1,959 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "DebugCallStack.h"
#if defined(WIN32) || defined(WIN64)
#include <IConsole.h>
#include <CryPath.h>
#include <Pak/CryPakUtils.h>
#include "System.h"
#include <AzCore/Debug/StackTracer.h>
#include <AzCore/Debug/EventTraceDrillerBus.h>
#include "resource.h"
__pragma(comment(lib, "version.lib"))
//! Needs one external of DLL handle.
extern HMODULE gDLLHandle;
#include <DbgHelp.h>
#define MAX_PATH_LENGTH 1024
#define MAX_SYMBOL_LENGTH 512
static HWND hwndException = 0;
static bool g_bUserDialog = true; // true=on crash show dialog box, false=supress user interaction
static int PrintException(EXCEPTION_POINTERS* pex);
static bool IsFloatingPointException(EXCEPTION_POINTERS* pex);
extern LONG WINAPI CryEngineExceptionFilterWER(struct _EXCEPTION_POINTERS* pExceptionPointers);
extern LONG WINAPI CryEngineExceptionFilterMiniDump(struct _EXCEPTION_POINTERS* pExceptionPointers, const char* szDumpPath, MINIDUMP_TYPE mdumpValue);
//=============================================================================
CONTEXT CaptureCurrentContext()
{
CONTEXT context;
memset(&context, 0, sizeof(context));
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);
return context;
}
LONG __stdcall CryUnhandledExceptionHandler(EXCEPTION_POINTERS* pex)
{
return DebugCallStack::instance()->handleException(pex);
}
BOOL CALLBACK EnumModules(
PCSTR ModuleName,
DWORD64 BaseOfDll,
PVOID UserContext)
{
DebugCallStack::TModules& modules = *static_cast<DebugCallStack::TModules*>(UserContext);
modules[(void*)BaseOfDll] = ModuleName;
return TRUE;
}
//=============================================================================
// Class Statics
//=============================================================================
// Return single instance of class.
IDebugCallStack* IDebugCallStack::instance()
{
static DebugCallStack sInstance;
return &sInstance;
}
//------------------------------------------------------------------------------------------------------------------------
// Sets up the symbols for functions in the debug file.
//------------------------------------------------------------------------------------------------------------------------
DebugCallStack::DebugCallStack()
: prevExceptionHandler(0)
, m_pSystem(0)
, m_nSkipNumFunctions(0)
, m_bCrash(false)
, m_szBugMessage(NULL)
{
}
DebugCallStack::~DebugCallStack()
{
}
void DebugCallStack::RemoveOldFiles()
{
RemoveFile("error.log");
RemoveFile("error.bmp");
RemoveFile("error.dmp");
}
void DebugCallStack::RemoveFile(const char* szFileName)
{
FILE* pFile = nullptr;
azfopen(&pFile, szFileName, "r");
const bool bFileExists = (pFile != NULL);
if (bFileExists)
{
fclose(pFile);
WriteLineToLog("Removing file \"%s\"...", szFileName);
if (remove(szFileName) == 0)
{
WriteLineToLog("File successfully removed.");
}
else
{
WriteLineToLog("Couldn't remove file!");
}
}
}
void DebugCallStack::installErrorHandler(ISystem* pSystem)
{
m_pSystem = pSystem;
prevExceptionHandler = (void*)SetUnhandledExceptionFilter(CryUnhandledExceptionHandler);
}
//////////////////////////////////////////////////////////////////////////
void DebugCallStack::SetUserDialogEnable(const bool bUserDialogEnable)
{
g_bUserDialog = bUserDialogEnable;
}
DWORD g_idDebugThreads[10];
const char* g_nameDebugThreads[10];
int g_nDebugThreads = 0;
volatile int g_lockThreadDumpList = 0;
void MarkThisThreadForDebugging(const char* name)
{
EBUS_EVENT(AZ::Debug::EventTraceDrillerSetupBus, SetThreadName, AZStd::this_thread::get_id(), name);
WriteLock lock(g_lockThreadDumpList);
DWORD id = GetCurrentThreadId();
if (g_nDebugThreads == sizeof(g_idDebugThreads) / sizeof(g_idDebugThreads[0]))
{
return;
}
for (int i = 0; i < g_nDebugThreads; i++)
{
if (g_idDebugThreads[i] == id)
{
return;
}
}
g_nameDebugThreads[g_nDebugThreads] = name;
g_idDebugThreads[g_nDebugThreads++] = id;
((CSystem*)gEnv->pSystem)->EnableFloatExceptions(g_cvars.sys_float_exceptions);
}
void UnmarkThisThreadFromDebugging()
{
WriteLock lock(g_lockThreadDumpList);
DWORD id = GetCurrentThreadId();
for (int i = g_nDebugThreads - 1; i >= 0; i--)
{
if (g_idDebugThreads[i] == id)
{
memmove(g_idDebugThreads + i, g_idDebugThreads + i + 1, (g_nDebugThreads - 1 - i) * sizeof(g_idDebugThreads[0]));
memmove(g_nameDebugThreads + i, g_nameDebugThreads + i + 1, (g_nDebugThreads - 1 - i) * sizeof(g_nameDebugThreads[0]));
--g_nDebugThreads;
}
}
}
extern int prev_sys_float_exceptions;
void UpdateFPExceptionsMaskForThreads()
{
int mask = -iszero(g_cvars.sys_float_exceptions);
CONTEXT ctx;
for (int i = 0; i < g_nDebugThreads; i++)
{
if (g_idDebugThreads[i] != GetCurrentThreadId())
{
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, TRUE, g_idDebugThreads[i]);
ctx.ContextFlags = CONTEXT_ALL;
SuspendThread(hThread);
GetThreadContext(hThread, &ctx);
#ifndef WIN64
(ctx.FloatSave.ControlWord |= 7) &= ~5 | mask;
(*(WORD*)(ctx.ExtendedRegisters + 24) |= 0x280) &= ~0x280 | mask;
#else
(ctx.FltSave.ControlWord |= 7) &= ~5 | mask;
(ctx.FltSave.MxCsr |= 0x280) &= ~0x280 | mask;
#endif
SetThreadContext(hThread, &ctx);
ResumeThread(hThread);
}
}
}
//////////////////////////////////////////////////////////////////////////
int DebugCallStack::handleException(EXCEPTION_POINTERS* exception_pointer)
{
if (gEnv == NULL)
{
return EXCEPTION_EXECUTE_HANDLER;
}
ResetFPU(exception_pointer);
prev_sys_float_exceptions = 0;
const int cached_sys_float_exceptions = g_cvars.sys_float_exceptions;
((CSystem*)gEnv->pSystem)->EnableFloatExceptions(0);
if (g_cvars.sys_WER)
{
gEnv->pLog->FlushAndClose();
return CryEngineExceptionFilterWER(exception_pointer);
}
if (g_cvars.sys_no_crash_dialog)
{
DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
SetErrorMode(dwMode | SEM_NOGPFAULTERRORBOX);
}
m_bCrash = true;
if (g_cvars.sys_no_crash_dialog)
{
DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
SetErrorMode(dwMode | SEM_NOGPFAULTERRORBOX);
}
static bool firstTime = true;
if (g_cvars.sys_dump_aux_threads)
{
for (int i = 0; i < g_nDebugThreads; i++)
{
if (g_idDebugThreads[i] != GetCurrentThreadId())
{
SuspendThread(OpenThread(THREAD_ALL_ACCESS, TRUE, g_idDebugThreads[i]));
}
}
}
// uninstall our exception handler.
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)prevExceptionHandler);
if (!firstTime)
{
WriteLineToLog("Critical Exception! Called Multiple Times!");
gEnv->pLog->FlushAndClose();
// Exception called more then once.
return EXCEPTION_EXECUTE_HANDLER;
}
// Print exception info:
{
char excCode[80];
char excAddr[80];
WriteLineToLog("<CRITICAL EXCEPTION>");
sprintf_s(excAddr, "0x%04X:0x%p", exception_pointer->ContextRecord->SegCs, exception_pointer->ExceptionRecord->ExceptionAddress);
sprintf_s(excCode, "0x%08X", exception_pointer->ExceptionRecord->ExceptionCode);
WriteLineToLog("Exception: %s, at Address: %s", excCode, excAddr);
if (CSystem* pSystem = (CSystem*)GetSystem())
{
if (const char* pLoadingProfilerCallstack = pSystem->GetLoadingProfilerCallstack())
{
if (pLoadingProfilerCallstack[0])
{
WriteLineToLog("<CrySystem> LoadingProfilerCallstack: %s", pLoadingProfilerCallstack);
}
}
}
{
IMemoryManager::SProcessMemInfo memInfo;
if (gEnv->pSystem->GetIMemoryManager()->GetProcessMemInfo(memInfo))
{
uint32 nMemUsage = (uint32)(memInfo.PagefileUsage / (1024 * 1024));
WriteLineToLog("Virtual memory usage: %dMb", nMemUsage);
}
gEnv->szDebugStatus[SSystemGlobalEnvironment::MAX_DEBUG_STRING_LENGTH - 1] = '\0';
WriteLineToLog("Debug Status: %s", gEnv->szDebugStatus);
}
if (gEnv->pRenderer)
{
ID3DDebugMessage* pMsg = 0;
gEnv->pRenderer->EF_Query(EFQ_GetLastD3DDebugMessage, pMsg);
if (pMsg)
{
const char* pStr = pMsg->GetMessage();
WriteLineToLog("Last D3D debug message: %s", pStr ? pStr : "#unknown#");
SAFE_RELEASE(pMsg);
}
}
}
firstTime = false;
const int ret = SubmitBug(exception_pointer);
if (ret != IDB_IGNORE)
{
CryEngineExceptionFilterWER(exception_pointer);
}
gEnv->pLog->FlushAndClose();
if (exception_pointer->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{
// This is non continuable exception. abort application now.
exit(exception_pointer->ExceptionRecord->ExceptionCode);
}
//typedef long (__stdcall *ExceptionFunc)(EXCEPTION_POINTERS*);
//ExceptionFunc prevFunc = (ExceptionFunc)prevExceptionHandler;
//return prevFunc( (EXCEPTION_POINTERS*)exception_pointer );
if (ret == IDB_EXIT)
{
// Immediate exit.
// on windows, exit() and _exit() do all sorts of things, unfortuantely
// TerminateProcess is the only way to die.
TerminateProcess(GetCurrentProcess(), exception_pointer->ExceptionRecord->ExceptionCode); // we crashed, so don't return a zero exit code!
// on linux based systems, _exit will not call ATEXIT and other things, which makes it more suitable for termination in an emergency such
// as an unhandled exception.
// however, this function is a windows exception handler.
}
else if (ret == IDB_IGNORE)
{
#ifndef WIN64
exception_pointer->ContextRecord->FloatSave.StatusWord &= ~31;
exception_pointer->ContextRecord->FloatSave.ControlWord |= 7;
(*(WORD*)(exception_pointer->ContextRecord->ExtendedRegisters + 24) &= 31) |= 0x1F80;
#else
exception_pointer->ContextRecord->FltSave.StatusWord &= ~31;
exception_pointer->ContextRecord->FltSave.ControlWord |= 7;
(exception_pointer->ContextRecord->FltSave.MxCsr &= 31) |= 0x1F80;
#endif
firstTime = true;
prevExceptionHandler = (void*)SetUnhandledExceptionFilter(CryUnhandledExceptionHandler);
g_cvars.sys_float_exceptions = cached_sys_float_exceptions;
((CSystem*)gEnv->pSystem)->EnableFloatExceptions(g_cvars.sys_float_exceptions);
return EXCEPTION_CONTINUE_EXECUTION;
}
// Continue;
return EXCEPTION_EXECUTE_HANDLER;
}
void DebugCallStack::ReportBug(const char* szErrorMessage)
{
WriteLineToLog("Reporting bug: %s", szErrorMessage);
m_szBugMessage = szErrorMessage;
m_context = CaptureCurrentContext();
SubmitBug(NULL);
m_szBugMessage = NULL;
}
void DebugCallStack::dumpCallStack(std::vector<string>& funcs)
{
WriteLineToLog("=============================================================================");
int len = (int)funcs.size();
for (int i = 0; i < len; i++)
{
const char* str = funcs[i].c_str();
WriteLineToLog("%2d) %s", len - i, str);
}
WriteLineToLog("=============================================================================");
}
//////////////////////////////////////////////////////////////////////////
void DebugCallStack::LogExceptionInfo(EXCEPTION_POINTERS* pex)
{
string path("");
if ((gEnv) && (gEnv->pFileIO))
{
const char* logAlias = nullptr;
if (
(logAlias = gEnv->pFileIO->GetAlias("@log@")) ||
(logAlias = gEnv->pFileIO->GetAlias("@root@"))
)
{
path = logAlias;
path += "/";
}
}
string fileName = path;
fileName += "error.log";
struct stat fileInfo;
string timeStamp;
string backupPath;
if (gEnv->IsDedicated())
{
backupPath = PathUtil::ToUnixPath(PathUtil::AddSlash(path + "DumpBackups"));
gEnv->pFileIO->CreatePath(backupPath.c_str());
if (stat(fileName.c_str(), &fileInfo) == 0)
{
// Backup log
tm creationTime;
localtime_s(&creationTime, &fileInfo.st_mtime);
char tempBuffer[32];
strftime(tempBuffer, sizeof(tempBuffer), "%d %b %Y (%H %M %S)", &creationTime);
timeStamp = tempBuffer;
string backupFileName = backupPath + timeStamp + " error.log";
CopyFile(fileName.c_str(), backupFileName.c_str(), true);
}
}
FILE* f = nullptr;
azfopen(&f, fileName.c_str(), "wt");
CDebugAllowFileAccess ignoreInvalidFileAccess;
static char errorString[s_iCallStackSize];
errorString[0] = 0;
// Time and Version.
char versionbuf[1024];
azstrcpy(versionbuf, AZ_ARRAY_SIZE(versionbuf), "");
PutVersion(versionbuf, AZ_ARRAY_SIZE(versionbuf));
cry_strcat(errorString, versionbuf);
cry_strcat(errorString, "\n");
char excCode[MAX_WARNING_LENGTH];
char excAddr[80];
char desc[1024];
char excDesc[MAX_WARNING_LENGTH];
// make sure the mouse cursor is visible
ShowCursor(TRUE);
const char* excName;
if (m_bIsFatalError || !pex)
{
const char* const szMessage = m_bIsFatalError ? s_szFatalErrorCode : m_szBugMessage;
excName = szMessage;
cry_strcpy(excCode, szMessage);
cry_strcpy(excAddr, "");
cry_strcpy(desc, "");
cry_strcpy(m_excModule, "");
cry_strcpy(excDesc, szMessage);
}
else
{
sprintf_s(excAddr, "0x%04X:0x%p", pex->ContextRecord->SegCs, pex->ExceptionRecord->ExceptionAddress);
sprintf_s(excCode, "0x%08X", pex->ExceptionRecord->ExceptionCode);
excName = TranslateExceptionCode(pex->ExceptionRecord->ExceptionCode);
cry_strcpy(desc, "");
sprintf_s(excDesc, "%s\r\n%s", excName, desc);
if (pex->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
if (pex->ExceptionRecord->NumberParameters > 1)
{
ULONG_PTR iswrite = pex->ExceptionRecord->ExceptionInformation[0];
DWORD64 accessAddr = pex->ExceptionRecord->ExceptionInformation[1];
if (iswrite)
{
sprintf_s(desc, "Attempt to write data to address 0x%08llu\r\nThe memory could not be \"written\"", accessAddr);
}
else
{
sprintf_s(desc, "Attempt to read from address 0x%08llu\r\nThe memory could not be \"read\"", accessAddr);
}
}
}
}
WriteLineToLog("Exception Code: %s", excCode);
WriteLineToLog("Exception Addr: %s", excAddr);
WriteLineToLog("Exception Module: %s", m_excModule);
WriteLineToLog("Exception Name : %s", excName);
WriteLineToLog("Exception Description: %s", desc);
cry_strcpy(m_excDesc, excDesc);
cry_strcpy(m_excAddr, excAddr);
cry_strcpy(m_excCode, excCode);
char errs[32768];
sprintf_s(errs, "Exception Code: %s\nException Addr: %s\nException Module: %s\nException Description: %s, %s\n",
excCode, excAddr, m_excModule, excName, desc);
IMemoryManager::SProcessMemInfo memInfo;
if (gEnv->pSystem->GetIMemoryManager()->GetProcessMemInfo(memInfo))
{
char memoryString[256];
double MB = 1024 * 1024;
sprintf_s(memoryString, "Memory in use: %3.1fMB\n", (double)(memInfo.PagefileUsage) / MB);
cry_strcat(errs, memoryString);
}
{
const int tempStringSize = 256;
char tempString[tempStringSize];
gEnv->szDebugStatus[SSystemGlobalEnvironment::MAX_DEBUG_STRING_LENGTH - 1] = '\0';
sprintf_s(tempString, tempStringSize, "Debug Status: %s\n", gEnv->szDebugStatus);
cry_strcat(errs, tempString);
sprintf_s(tempString, tempStringSize, "Out of Memory: %d\n", gEnv->bIsOutOfMemory);
cry_strcat(errs, tempString);
}
cry_strcat(errs, "\nCall Stack Trace:\n");
std::vector<string> funcs;
if (gEnv->bIsOutOfMemory)
{
cry_strcat(errs, "1) OUT_OF_MEMORY()\n");
}
else
{
AZ::Debug::StackFrame frames[25];
AZ::Debug::SymbolStorage::StackLine lines[AZ_ARRAY_SIZE(frames)];
unsigned int numFrames = AZ::Debug::StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), 3);
if (numFrames)
{
AZ::Debug::SymbolStorage::DecodeFrames(frames, numFrames, lines);
for (unsigned int i = 0; i < numFrames; i++)
{
funcs.push_back(lines[i]);
}
}
dumpCallStack(funcs);
// Fill call stack.
char str[s_iCallStackSize];
cry_strcpy(str, "");
for (unsigned int i = 0; i < funcs.size(); i++)
{
char temp[s_iCallStackSize];
sprintf_s(temp, "%2zd) %s", funcs.size() - i, (const char*)funcs[i].c_str());
cry_strcat(str, temp);
cry_strcat(str, "\r\n");
cry_strcat(errs, temp);
cry_strcat(errs, "\n");
}
cry_strcpy(m_excCallstack, str);
}
cry_strcat(errorString, errs);
if (f)
{
fwrite(errorString, strlen(errorString), 1, f);
if (!gEnv->bIsOutOfMemory)
{
if (g_cvars.sys_dump_aux_threads)
{
for (int i = 0; i < g_nDebugThreads; i++)
{
if (g_idDebugThreads[i] != GetCurrentThreadId())
{
fprintf(f, "\n\nSuspended thread (%s):\n", g_nameDebugThreads[i]);
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, TRUE, g_idDebugThreads[i]);
// mirrors the AZ::Debug::Trace::PrintCallstack() functionality, but prints to a file
{
AZ::Debug::StackFrame frames[10];
// Without StackFrame explicit alignment frames array is aligned to 4 bytes
// which causes the stack tracing to fail.
AZ::Debug::SymbolStorage::StackLine lines[AZ_ARRAY_SIZE(frames)];
unsigned int numFrames = AZ::Debug::StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), 0, hThread);
if (numFrames)
{
AZ::Debug::SymbolStorage::DecodeFrames(frames, numFrames, lines);
for (unsigned int i2 = 0; i2 < numFrames; ++i2)
{
fprintf(f, "%2d) %s\n", numFrames - i2, lines[i2]);
}
}
}
ResumeThread(hThread);
}
}
}
}
fflush(f);
fclose(f);
}
if (pex)
{
MINIDUMP_TYPE mdumpValue;
bool bDump = true;
switch (g_cvars.sys_dump_type)
{
case 0:
bDump = false;
break;
case 1:
mdumpValue = MiniDumpNormal;
break;
case 2:
mdumpValue = (MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithDataSegs);
break;
case 3:
mdumpValue = MiniDumpWithFullMemory;
break;
default:
mdumpValue = (MINIDUMP_TYPE)g_cvars.sys_dump_type;
break;
}
if (bDump)
{
fileName = path + "error.dmp";
if (gEnv->IsDedicated() && stat(fileName.c_str(), &fileInfo) == 0)
{
// Backup dump (use timestamp from error.log if available)
if (timeStamp.empty())
{
tm creationTime;
localtime_s(&creationTime, &fileInfo.st_mtime);
char tempBuffer[32];
strftime(tempBuffer, sizeof(tempBuffer), "%d %b %Y (%H %M %S)", &creationTime);
timeStamp = tempBuffer;
}
string backupFileName = backupPath + timeStamp + " error.dmp";
CopyFile(fileName.c_str(), backupFileName.c_str(), true);
}
CryEngineExceptionFilterMiniDump(pex, fileName.c_str(), mdumpValue);
}
}
//if no crash dialog don't even submit the bug
if (m_postBackupProcess && g_cvars.sys_no_crash_dialog == 0 && g_bUserDialog)
{
m_postBackupProcess();
}
else
{
// lawsonn: Disabling the JIRA-based crash reporter for now
// we'll need to deal with it our own way, pending QA.
// if you're customizing the engine this is also your opportunity to deal with it.
if (g_cvars.sys_no_crash_dialog != 0 || !g_bUserDialog)
{
// ------------ place custom crash handler here ---------------------
// it should launch an executable!
/// by this time, error.bmp will be in the engine root folder
// error.log and error.dmp will also be present in the engine root folder
// if your error dumper wants those, it should zip them up and send them or offer to do so.
// ------------------------------------------------------------------
}
}
const bool bQuitting = !gEnv || !gEnv->pSystem || gEnv->pSystem->IsQuitting();
//[AlexMcC|16.04.10] When the engine is shutting down, MessageBox doesn't display a box
// and immediately returns IDYES. Avoid this by just not trying to save if we're quitting.
// Don't ask to save if this isn't a real crash (a real crash has exception pointers)
if (g_cvars.sys_no_crash_dialog == 0 && g_bUserDialog && gEnv->IsEditor() && !bQuitting && pex)
{
BackupCurrentLevel();
const INT_PTR res = DialogBoxParam(gDLLHandle, MAKEINTRESOURCE(IDD_CONFIRM_SAVE_LEVEL), NULL, DebugCallStack::ConfirmSaveDialogProc, NULL);
if (res == IDB_CONFIRM_SAVE)
{
if (SaveCurrentLevel())
{
MessageBox(NULL, "Level has been successfully saved!\r\nPress Ok to terminate Editor.", "Save", MB_OK);
}
else
{
MessageBox(NULL, "Error saving level.\r\nPress Ok to terminate Editor.", "Save", MB_OK | MB_ICONWARNING);
}
}
}
if (g_cvars.sys_no_crash_dialog != 0 || !g_bUserDialog)
{
// terminate immediately - since we're in a crash, there is no point unwinding stack, we've already done access violation or worse.
// calling exit will only cause further death down the line...
TerminateProcess(GetCurrentProcess(), pex->ExceptionRecord->ExceptionCode);
}
}
INT_PTR CALLBACK DebugCallStack::ExceptionDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static EXCEPTION_POINTERS* pex;
static char errorString[32768] = "";
switch (message)
{
case WM_INITDIALOG:
{
pex = (EXCEPTION_POINTERS*)lParam;
HWND h;
if (pex->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{
// Disable continue button for non continuable exceptions.
//h = GetDlgItem( hwndDlg,IDB_CONTINUE );
//if (h) EnableWindow( h,FALSE );
}
DebugCallStack* pDCS = static_cast<DebugCallStack*>(DebugCallStack::instance());
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_DESC);
if (h)
{
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excDesc);
}
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_CODE);
if (h)
{
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excCode);
}
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_MODULE);
if (h)
{
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excModule);
}
h = GetDlgItem(hwndDlg, IDC_EXCEPTION_ADDRESS);
if (h)
{
SendMessage(h, EM_REPLACESEL, FALSE, (LONG_PTR)pDCS->m_excAddr);
}
// Fill call stack.
HWND callStack = GetDlgItem(hwndDlg, IDC_CALLSTACK);
if (callStack)
{
SendMessage(callStack, WM_SETTEXT, FALSE, (LPARAM)pDCS->m_excCallstack);
}
if (hwndException)
{
DestroyWindow(hwndException);
hwndException = 0;
}
if (IsFloatingPointException(pex))
{
EnableWindow(GetDlgItem(hwndDlg, IDB_IGNORE), TRUE);
}
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDB_EXIT:
case IDB_IGNORE:
// Fall through.
EndDialog(hwndDlg, wParam);
return TRUE;
}
}
return FALSE;
}
INT_PTR CALLBACK DebugCallStack::ConfirmSaveDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, [[maybe_unused]] LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
{
// The user might be holding down the spacebar while the engine crashes.
// If we don't remove keyboard focus from this dialog, the keypress will
// press the default button before the dialog actually appears, even if
// the user has already released the key, which is bad.
SetFocus(NULL);
} break;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDB_CONFIRM_SAVE: // Fall through
case IDB_DONT_SAVE:
{
EndDialog(hwndDlg, wParam);
return TRUE;
}
}
} break;
}
return FALSE;
}
bool DebugCallStack::BackupCurrentLevel()
{
CSystem* pSystem = static_cast<CSystem*>(m_pSystem);
if (pSystem && pSystem->GetUserCallback())
{
return pSystem->GetUserCallback()->OnBackupDocument();
}
return false;
}
bool DebugCallStack::SaveCurrentLevel()
{
CSystem* pSystem = static_cast<CSystem*>(m_pSystem);
if (pSystem && pSystem->GetUserCallback())
{
return pSystem->GetUserCallback()->OnSaveDocument();
}
return false;
}
int DebugCallStack::SubmitBug(EXCEPTION_POINTERS* exception_pointer)
{
int ret = IDB_EXIT;
assert(!hwndException);
// If in full screen minimize render window
{
ICVar* pFullscreen = (gEnv && gEnv->pConsole) ? gEnv->pConsole->GetCVar("r_Fullscreen") : 0;
if (pFullscreen && pFullscreen->GetIVal() != 0 && gEnv->pRenderer && gEnv->pRenderer->GetHWND())
{
::ShowWindow((HWND)gEnv->pRenderer->GetHWND(), SW_MINIMIZE);
}
}
//hwndException = CreateDialog( gDLLHandle,MAKEINTRESOURCE(IDD_EXCEPTION),NULL,NULL );
RemoveOldFiles();
AZ::Debug::Trace::PrintCallstack("", 2);
LogExceptionInfo(exception_pointer);
if (IsFloatingPointException(exception_pointer))
{
//! Print exception dialog.
ret = PrintException(exception_pointer);
}
return ret;
}
void DebugCallStack::ResetFPU(EXCEPTION_POINTERS* pex)
{
if (IsFloatingPointException(pex))
{
// How to reset FPU: http://www.experts-exchange.com/Programming/System/Windows__Programming/Q_10310953.html
_clearfp();
#ifndef WIN64
pex->ContextRecord->FloatSave.ControlWord |= 0x2F;
pex->ContextRecord->FloatSave.StatusWord &= ~0x8080;
#endif
}
}
string DebugCallStack::GetModuleNameForAddr(void* addr)
{
if (m_modules.empty())
{
return "[unknown]";
}
if (addr < m_modules.begin()->first)
{
return "[unknown]";
}
TModules::const_iterator it = m_modules.begin();
TModules::const_iterator end = m_modules.end();
for (; ++it != end; )
{
if (addr < it->first)
{
return (--it)->second;
}
}
//if address is higher than the last module, we simply assume it is in the last module.
return m_modules.rbegin()->second;
}
void DebugCallStack::GetProcNameForAddr(void* addr, string& procName, void*& baseAddr, string& filename, int& line)
{
AZ::Debug::SymbolStorage::StackLine func, file, module;
AZ::Debug::SymbolStorage::FindFunctionFromIP(addr, &func, &file, &module, line, baseAddr);
procName = func;
filename = file;
}
string DebugCallStack::GetCurrentFilename()
{
char fullpath[MAX_PATH_LENGTH + 1];
GetModuleFileName(NULL, fullpath, MAX_PATH_LENGTH);
return fullpath;
}
static bool IsFloatingPointException(EXCEPTION_POINTERS* pex)
{
if (!pex)
{
return false;
}
DWORD exceptionCode = pex->ExceptionRecord->ExceptionCode;
switch (exceptionCode)
{
case EXCEPTION_FLT_DENORMAL_OPERAND:
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_FLT_INEXACT_RESULT:
case EXCEPTION_FLT_INVALID_OPERATION:
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_FLT_UNDERFLOW:
case STATUS_FLOAT_MULTIPLE_FAULTS:
case STATUS_FLOAT_MULTIPLE_TRAPS:
return true;
default:
return false;
}
}
int DebugCallStack::PrintException(EXCEPTION_POINTERS* exception_pointer)
{
return (int)DialogBoxParam(gDLLHandle, MAKEINTRESOURCE(IDD_CRITICAL_ERROR), NULL, DebugCallStack::ExceptionDialogProc, (LPARAM)exception_pointer);
}
#else
void MarkThisThreadForDebugging(const char*) {}
void UnmarkThisThreadFromDebugging() {}
void UpdateFPExceptionsMaskForThreads() {}
#endif //WIN32
+95
View File
@@ -0,0 +1,95 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_DEBUGCALLSTACK_H
#define CRYINCLUDE_CRYSYSTEM_DEBUGCALLSTACK_H
#pragma once
#include "IDebugCallStack.h"
#if defined (WIN32) || defined (WIN64)
//! Limits the maximal number of functions in call stack.
const int MAX_DEBUG_STACK_ENTRIES_FILE_DUMP = 12;
struct ISystem;
//!============================================================================
//!
//! DebugCallStack class, capture call stack information from symbol files.
//!
//!============================================================================
class DebugCallStack
: public IDebugCallStack
{
public:
DebugCallStack();
virtual ~DebugCallStack();
ISystem* GetSystem() { return m_pSystem; };
virtual string GetModuleNameForAddr(void* addr);
virtual void GetProcNameForAddr(void* addr, string& procName, void*& baseAddr, string& filename, int& line);
virtual string GetCurrentFilename();
void installErrorHandler(ISystem* pSystem);
virtual int handleException(EXCEPTION_POINTERS* exception_pointer);
virtual void ReportBug(const char*);
void dumpCallStack(std::vector<string>& functions);
void SetUserDialogEnable(const bool bUserDialogEnable);
typedef std::map<void*, string> TModules;
protected:
static void RemoveOldFiles();
static void RemoveFile(const char* szFileName);
static int PrintException(EXCEPTION_POINTERS* exception_pointer);
static INT_PTR CALLBACK ExceptionDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK ConfirmSaveDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam);
void LogExceptionInfo(EXCEPTION_POINTERS* exception_pointer);
bool BackupCurrentLevel();
bool SaveCurrentLevel();
int SubmitBug(EXCEPTION_POINTERS* exception_pointer);
void ResetFPU(EXCEPTION_POINTERS* pex);
static const int s_iCallStackSize = 32768;
char m_excLine[256];
char m_excModule[128];
char m_excDesc[MAX_WARNING_LENGTH];
char m_excCode[MAX_WARNING_LENGTH];
char m_excAddr[80];
char m_excCallstack[s_iCallStackSize];
void* prevExceptionHandler;
bool m_bCrash;
const char* m_szBugMessage;
ISystem* m_pSystem;
int m_nSkipNumFunctions;
CONTEXT m_context;
TModules m_modules;
};
#endif //WIN32
#endif // CRYINCLUDE_CRYSYSTEM_DEBUGCALLSTACK_H
File diff suppressed because it is too large Load Diff
+712
View File
@@ -0,0 +1,712 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_DEFRAGALLOCATOR_H
#define CRYINCLUDE_CRYSYSTEM_DEFRAGALLOCATOR_H
#pragma once
#include "IDefragAllocator.h"
#include "System.h"
#ifndef _RELEASE
#define CDBA_DEBUG
#endif
//#define CDBA_MORE_DEBUG
#ifdef CDBA_DEBUG
#define CDBA_ASSERT(x) do { assert(x); if (!(x)) {__debugbreak(); } \
} while (0)
#else
#define CDBA_ASSERT(x) assert(x)
#endif
union SDefragAllocChunkAttr
{
enum
{
SizeWidth = 27,
SizeMask = (1 << SizeWidth) - 1,
BusyMask = 1 << 27,
MovingMask = 1 << 28,
MaxPinCount = 7,
PinnedCountShift = 29,
PinnedIncMask = 1 << PinnedCountShift,
PinnedCountMask = MaxPinCount << PinnedCountShift,
};
uint32 ui;
ILINE unsigned int GetSize() const { return ui & SizeMask; }
ILINE void SetSize(unsigned int size) { ui = (ui & ~SizeMask) | size; }
ILINE void AddSize(int size) { ui += size; }
ILINE bool IsBusy() const { return (ui & BusyMask) != 0; }
ILINE void SetBusy(bool b) { ui = b ? (ui | BusyMask) : (ui & ~BusyMask); }
ILINE bool IsMoving() const { return (ui & MovingMask) != 0; }
ILINE void SetMoving(bool m) { ui = m ? (ui | MovingMask) : (ui & ~MovingMask); }
ILINE void IncPinCount() { ui += PinnedIncMask; }
ILINE void DecPinCount() { ui -= PinnedIncMask; }
ILINE bool IsPinned() const { return (ui & PinnedCountMask) != 0; }
ILINE unsigned int GetPinCount() const { return (ui & PinnedCountMask) >> PinnedCountShift; }
ILINE void SetPinCount(unsigned int p) { ui = (ui & ~PinnedCountMask) | (p << PinnedCountShift); }
};
struct SDefragAllocChunk
{
enum
{
AlignBitCount = 4,
};
typedef IDefragAllocator::Hdl Index;
Index addrPrevIdx;
Index addrNextIdx;
union
{
struct
{
UINT_PTR ptr : sizeof(UINT_PTR) * 8 - AlignBitCount;
UINT_PTR logAlign: AlignBitCount;
};
UINT_PTR packedPtr;
};
SDefragAllocChunkAttr attr;
union
{
void* pContext;
struct
{
Index freePrevIdx;
Index freeNextIdx;
};
};
#ifndef _RELEASE
const char* source;
#endif
void SwapEndian()
{
::SwapEndian(addrPrevIdx, true);
::SwapEndian(addrNextIdx, true);
::SwapEndian(packedPtr, true);
::SwapEndian(attr.ui, true);
if (attr.IsBusy())
{
::SwapEndian(pContext, true);
}
else
{
::SwapEndian(freePrevIdx, true);
::SwapEndian(freeNextIdx, true);
}
#ifndef _RELEASE
::SwapEndian(source, true);
#endif
}
};
struct SDefragAllocSegment
{
uint32 address;
uint32 capacity;
SDefragAllocChunk::Index headSentinalChunkIdx;
void SwapEndian()
{
::SwapEndian(address, true);
::SwapEndian(capacity, true);
::SwapEndian(headSentinalChunkIdx, true);
}
};
class CDefragAllocator;
class CDefragAllocatorWalker
{
public:
explicit CDefragAllocatorWalker(CDefragAllocator& alloc);
~CDefragAllocatorWalker();
const SDefragAllocChunk* Next();
private:
CDefragAllocatorWalker(const CDefragAllocatorWalker&);
CDefragAllocatorWalker& operator = (const CDefragAllocatorWalker&);
private:
CDefragAllocator* m_pAlloc;
SDefragAllocChunk::Index m_nChunkIdx;
};
class CDefragAllocator
: public IDefragAllocator
{
friend class CDefragAllocatorWalker;
typedef SDefragAllocChunk::Index Index;
public:
CDefragAllocator();
void Release(bool bDiscard);
void Init(UINT_PTR capacity, UINT_PTR minAlignment, const Policy& policy);
#ifndef _RELEASE
void DumpState(const char* filename);
void RestoreState(const char* filename);
#endif
// allocatorDisplayOffset will offset the statistics for the allocator depending on the index.
// 0 means no offset and default location, 1 means bar will be rendered above the 0th bar and stats to the right of the 0th stats
void DisplayMemoryUsage(const char* title, unsigned int allocatorDisplayOffset = 0);
bool AppendSegment(UINT_PTR capacity);
void UnAppendSegment();
Hdl Allocate(size_t sz, const char* source, void* pContext = NULL);
Hdl AllocateAligned(size_t sz, size_t alignment, const char* source, void* pContext = NULL);
AllocatePinnedResult AllocatePinned(size_t sz, const char* source, void* pContext = NULL);
bool Free(Hdl hdl);
void ChangeContext(Hdl hdl, void* pNewContext);
size_t GetAllocated() const { return (size_t)(m_capacity - m_available) << m_logMinAlignment; }
IDefragAllocatorStats GetStats();
size_t DefragmentTick(size_t maxMoves, size_t maxAmount, bool bForce);
ILINE UINT_PTR UsableSize(Hdl hdl)
{
Index chunkIdx = ChunkIdxFromHdl(hdl);
CDBA_ASSERT(chunkIdx < m_chunks.size());
SDefragAllocChunk& chunk = m_chunks[chunkIdx];
SDefragAllocChunkAttr attr = chunk.attr;
CDBA_ASSERT(attr.IsBusy());
return (UINT_PTR)attr.GetSize() << m_logMinAlignment;
}
// Pin the chunk until the next defrag tick, when it will be automatically unpinned
ILINE UINT_PTR WeakPin(Hdl hdl)
{
Index chunkIdx = ChunkIdxFromHdl(hdl);
CDBA_ASSERT(chunkIdx < m_chunks.size());
SDefragAllocChunk& chunk = m_chunks[chunkIdx];
SDefragAllocChunkAttr attr = chunk.attr;
CDBA_ASSERT(attr.IsBusy());
if (attr.IsMoving())
{
CancelMove(chunkIdx, true);
}
return chunk.ptr << m_logMinAlignment;
}
// Pin the chunk until Unpin is called
ILINE UINT_PTR Pin(Hdl hdl)
{
Index chunkIdx = ChunkIdxFromHdl(hdl);
SDefragAllocChunk& chunk = m_chunks[chunkIdx];
SDefragAllocChunkAttr attr;
SDefragAllocChunkAttr newAttr;
do
{
attr.ui = const_cast<volatile uint32&>(chunk.attr.ui);
newAttr.ui = attr.ui;
CDBA_ASSERT(attr.GetPinCount() < SDefragAllocChunkAttr::MaxPinCount);
CDBA_ASSERT(attr.IsBusy());
newAttr.IncPinCount();
}
while (CryInterlockedCompareExchange(alias_cast<volatile LONG*>(&chunk.attr.ui), newAttr.ui, attr.ui) != attr.ui);
// Potentially a Relocate could be in progress here. Either the Relocate is mid-way, in which case 'IsMoving()' will
// still be set, and CancelMove will sync and all is well.
// If 'IsMoving()' is not set, the Relocate should have just completed, in which case ptr should validly point
// to the new location.
if (attr.IsMoving())
{
CancelMove(chunkIdx, true);
}
return chunk.ptr << m_logMinAlignment;
}
ILINE void Unpin(Hdl hdl)
{
SDefragAllocChunk& chunk = m_chunks[ChunkIdxFromHdl(hdl)];
SDefragAllocChunkAttr attr;
SDefragAllocChunkAttr newAttr;
do
{
attr.ui = const_cast<volatile uint32&>(chunk.attr.ui);
newAttr.ui = attr.ui;
CDBA_ASSERT(attr.IsPinned());
CDBA_ASSERT(attr.IsBusy());
newAttr.DecPinCount();
}
while (CryInterlockedCompareExchange(alias_cast<volatile LONG*>(&chunk.attr.ui), newAttr.ui, attr.ui) != attr.ui);
}
ILINE const char* GetSourceOf([[maybe_unused]] Hdl hdl)
{
#ifndef _RELEASE
return m_chunks[ChunkIdxFromHdl(hdl)].source;
#else
return "";
#endif
}
private:
enum
{
NumBuckets = 31, // 2GB
MaxPendingMoves = 64,
AddrStartSentinal = 0,
AddrEndSentinal = 1,
};
struct SplitResult
{
bool bSuccessful;
Index nLeftSplitChunkIdx;
Index nRightSplitChunkIdx;
};
struct PendingMove
{
PendingMove()
: srcChunkIdx(InvalidChunkIdx)
, dstChunkIdx(InvalidChunkIdx)
, userMoveId(0)
, relocated(false)
, cancelled(false)
{
}
Index srcChunkIdx;
Index dstChunkIdx;
uint32 userMoveId;
IDefragAllocatorCopyNotification notify;
bool relocated;
bool cancelled;
void SwapEndian()
{
::SwapEndian(srcChunkIdx, true);
::SwapEndian(dstChunkIdx, true);
::SwapEndian(userMoveId, true);
}
};
struct PendingMoveSrcChunkPredicate
{
PendingMoveSrcChunkPredicate(Index ci)
: m_ci(ci) {}
bool operator () (const PendingMove& pm) const { return pm.srcChunkIdx == m_ci; }
Index m_ci;
};
typedef DynArray<PendingMove> PendingMoveVec;
typedef std::vector<SDefragAllocSegment> SegmentVec;
static const Index InvalidChunkIdx = (Index) - 1;
private:
static ILINE Index ChunkIdxFromHdl(Hdl id) { return id - 1; }
static ILINE Hdl ChunkHdlFromIdx(Index idx) { return idx + 1; }
private:
~CDefragAllocator();
Index AllocateChunk();
void ReleaseChunk(Index idx);
ILINE void MarkAsInUse(SDefragAllocChunk& chunk)
{
CDBA_ASSERT(!chunk.attr.IsBusy());
chunk.attr.SetBusy(true);
m_available -= chunk.attr.GetSize();
++m_numAllocs;
// m_available is unsigned, so check for underflow
CDBA_ASSERT(m_available <= m_capacity);
}
ILINE void MarkAsFree(SDefragAllocChunk& chunk)
{
CDBA_ASSERT(chunk.attr.IsBusy());
chunk.attr.SetPinCount(0);
chunk.attr.SetMoving(false);
chunk.attr.SetBusy(0);
m_available += chunk.attr.GetSize();
--m_numAllocs;
// m_available is unsigned, so check for underflow
CDBA_ASSERT(m_available <= m_capacity);
}
void LinkFreeChunk(Index idx);
void UnlinkFreeChunk(Index idx)
{
SDefragAllocChunk& chunk = m_chunks[idx];
m_chunks[chunk.freePrevIdx].freeNextIdx = chunk.freeNextIdx;
m_chunks[chunk.freeNextIdx].freePrevIdx = chunk.freePrevIdx;
}
void LinkAddrChunk(Index idx, Index afterIdx)
{
SDefragAllocChunk& chunk = m_chunks[idx];
SDefragAllocChunk& afterChunk = m_chunks[afterIdx];
chunk.addrNextIdx = afterChunk.addrNextIdx;
chunk.addrPrevIdx = afterIdx;
m_chunks[chunk.addrNextIdx].addrPrevIdx = idx;
afterChunk.addrNextIdx = idx;
}
void UnlinkAddrChunk(Index id)
{
SDefragAllocChunk& chunk = m_chunks[id];
m_chunks[chunk.addrPrevIdx].addrNextIdx = chunk.addrNextIdx;
m_chunks[chunk.addrNextIdx].addrPrevIdx = chunk.addrPrevIdx;
}
void PrepareMergePopNext(Index* pLists)
{
for (int bucketIdx = 0; bucketIdx < NumBuckets; ++bucketIdx)
{
Index hdrChunkId = m_freeBuckets[bucketIdx];
Index nextId = m_chunks[hdrChunkId].freeNextIdx;
if (nextId != hdrChunkId)
{
pLists[bucketIdx] = nextId;
}
else
{
pLists[bucketIdx] = InvalidChunkIdx;
}
}
}
size_t MergePeekNextChunk(Index* pLists)
{
size_t farList = (size_t)-1;
UINT_PTR farPtr = (UINT_PTR)-1;
for (size_t listIdx = 0; listIdx < NumBuckets; ++listIdx)
{
Index chunkIdx = pLists[listIdx];
if (chunkIdx != InvalidChunkIdx)
{
SDefragAllocChunk& chunk = m_chunks[chunkIdx];
if (chunk.ptr < farPtr)
{
farPtr = chunk.ptr;
farList = listIdx;
}
}
}
return farList;
}
void MergePopNextChunk(Index* pLists, size_t list)
{
using std::swap;
Index fni = m_chunks[pLists[list]].freeNextIdx;
pLists[list] = fni;
if (m_chunks[fni].attr.IsBusy())
{
// End of the list
pLists[list] = InvalidChunkIdx;
}
}
void MergePatchNextRemove(Index* pLists, Index removeIdx)
{
for (int bucketIdx = 0; bucketIdx < NumBuckets; ++bucketIdx)
{
if (pLists[bucketIdx] == removeIdx)
{
Index nextIdx = m_chunks[removeIdx].freeNextIdx;
if (!m_chunks[nextIdx].attr.IsBusy())
{
pLists[bucketIdx] = nextIdx;
}
else
{
pLists[bucketIdx] = InvalidChunkIdx;
}
}
}
}
void MergePatchNextInsert(Index* pLists, Index insertIdx)
{
SDefragAllocChunk& insertChunk = m_chunks[insertIdx];
int bucket = BucketForSize(insertChunk.attr.GetSize());
if (pLists[bucket] != InvalidChunkIdx)
{
SDefragAllocChunk& listChunk = m_chunks[pLists[bucket]];
if (listChunk.ptr > insertChunk.ptr)
{
pLists[bucket] = insertIdx;
}
}
}
void PrepareMergePopPrev(Index* pLists)
{
for (int bucketIdx = 0; bucketIdx < NumBuckets; ++bucketIdx)
{
Index hdrChunkId = m_freeBuckets[bucketIdx];
Index prevIdx = m_chunks[hdrChunkId].freePrevIdx;
if (prevIdx != hdrChunkId)
{
pLists[bucketIdx] = prevIdx;
}
else
{
pLists[bucketIdx] = InvalidChunkIdx;
}
}
}
size_t MergePeekPrevChunk(Index* pLists)
{
size_t farList = (size_t)-1;
UINT_PTR farPtr = 0;
for (size_t listIdx = 0; listIdx < NumBuckets; ++listIdx)
{
Index chunkIdx = pLists[listIdx];
if (chunkIdx != InvalidChunkIdx)
{
SDefragAllocChunk& chunk = m_chunks[chunkIdx];
if (chunk.ptr >= farPtr)
{
farPtr = chunk.ptr;
farList = listIdx;
}
}
}
return farList;
}
void MergePopPrevChunk(Index* pLists, size_t list)
{
using std::swap;
Index fpi = m_chunks[pLists[list]].freePrevIdx;
pLists[list] = fpi;
if (m_chunks[fpi].attr.IsBusy())
{
// End of the list
pLists[list] = InvalidChunkIdx;
}
}
void MergePatchPrevInsert(Index* pLists, Index insertIdx)
{
SDefragAllocChunk& insertChunk = m_chunks[insertIdx];
int bucket = BucketForSize(insertChunk.attr.GetSize());
if (pLists[bucket] != InvalidChunkIdx)
{
SDefragAllocChunk& listChunk = m_chunks[pLists[bucket]];
if (listChunk.ptr < insertChunk.ptr)
{
pLists[bucket] = insertIdx;
}
}
}
void MergePatchPrevRemove(Index* pLists, Index removeIdx)
{
for (int bucketIdx = 0; bucketIdx < NumBuckets; ++bucketIdx)
{
if (pLists[bucketIdx] == removeIdx)
{
Index nextIdx = m_chunks[removeIdx].freePrevIdx;
if (!m_chunks[nextIdx].attr.IsBusy())
{
pLists[bucketIdx] = nextIdx;
}
else
{
pLists[bucketIdx] = InvalidChunkIdx;
}
}
}
}
void MarkAsMoving(SDefragAllocChunk& src)
{
SDefragAllocChunkAttr srcAttr, srcNewAttr;
do
{
srcAttr.ui = const_cast<volatile uint32&>(src.attr.ui);
srcNewAttr.ui = srcAttr.ui;
srcNewAttr.SetMoving(true);
}
while (CryInterlockedCompareExchange(alias_cast<volatile LONG*>(&src.attr.ui), srcNewAttr.ui, srcAttr.ui) != srcAttr.ui);
}
void MarkAsNotMoving(SDefragAllocChunk& src)
{
SDefragAllocChunkAttr srcAttr, srcNewAttr;
do
{
srcAttr.ui = const_cast<volatile uint32&>(src.attr.ui);
srcNewAttr.ui = srcAttr.ui;
srcNewAttr.SetMoving(false);
}
while (CryInterlockedCompareExchange(alias_cast<volatile LONG*>(&src.attr.ui), srcNewAttr.ui, srcAttr.ui) != srcAttr.ui);
}
ILINE bool IsMoveableCandidate(const SDefragAllocChunkAttr& a, uint32 sizeUpperBound)
{
return a.IsBusy() && !a.IsPinned() && !a.IsMoving() && (0 < a.GetSize()) && (a.GetSize() <= sizeUpperBound);
}
bool TryMarkAsMoving(SDefragAllocChunk& src, uint32 sizeUpperBound)
{
SDefragAllocChunkAttr srcAttr, srcNewAttr;
do
{
srcAttr.ui = const_cast<volatile uint32&>(src.attr.ui);
srcNewAttr.ui = srcAttr.ui;
if (!IsMoveableCandidate(srcAttr, sizeUpperBound))
{
return false;
}
srcNewAttr.SetMoving(true);
}
while (CryInterlockedCompareExchange(alias_cast<volatile LONG*>(&src.attr.ui), srcNewAttr.ui, srcAttr.ui) != srcAttr.ui);
return true;
}
bool TryScheduleCopy(SDefragAllocChunk& srcChunk, SDefragAllocChunk& dstChunk, PendingMove* pPM, bool bLowHalf)
{
UINT_PTR dstChunkBase = dstChunk.ptr;
UINT_PTR dstChunkEnd = dstChunkBase + dstChunk.attr.GetSize();
#if AZ_LEGACY_CRYSYSTEM_TRAIT_USE_BIT64
UINT_PTR allocAlign = BIT64(srcChunk.logAlign);
#else
UINT_PTR allocAlign = BIT(srcChunk.logAlign);
#endif
UINT_PTR allocSize = srcChunk.attr.GetSize();
UINT_PTR dstAllocBase = bLowHalf
? Align(dstChunkBase, allocAlign)
: ((dstChunkEnd - allocSize) & ~(allocAlign - 1));
uint32 userId = m_policy.pDefragPolicy->BeginCopy(
srcChunk.pContext,
dstAllocBase << m_logMinAlignment,
srcChunk.ptr << m_logMinAlignment,
allocSize << m_logMinAlignment,
&pPM->notify);
pPM->userMoveId = userId;
return userId != 0;
}
Hdl Allocate_Locked(size_t sz, size_t alignment, const char* source, void* pContext);
SplitResult SplitFreeBlock(Index fbId, size_t sz, size_t alignment, bool allocateInLowHalf);
Index MergeFreeBlock(Index fbId);
#ifdef CDBA_MORE_DEBUG
void Defrag_ValidateFreeBlockIteration();
#endif
Index BestFit_FindFreeBlockForSegment(size_t sz, size_t alignment, uint32 nSegment);
Index BestFit_FindFreeBlockFor(size_t sz, size_t alignment, UINT_PTR addressMin, UINT_PTR addressMax, bool allocateInLowHalf);
Index FirstFit_FindFreeBlockFor(size_t sz, size_t alignment, UINT_PTR addressMin, UINT_PTR addressMax, bool allocateInLowHalf);
size_t Defrag_FindMovesBwd(PendingMove** pMoves, size_t maxMoves, size_t& curAmount, size_t maxAmount);
size_t Defrag_FindMovesFwd(PendingMove** pMoves, size_t maxMoves, size_t& curAmount, size_t maxAmount);
bool Defrag_CompletePendingMoves();
Index Defrag_Bwd_FindFreeBlockFor(size_t sz, size_t alignment, UINT_PTR addressLimit);
Index Defrag_Fwd_FindFreeBlockFor(size_t sz, size_t alignment, UINT_PTR addressLimit);
PendingMove* AllocPendingMove();
void FreePendingMove(PendingMove* pMove);
void CancelMove(Index srcChunkIdx, bool bIsContentNeeded);
void CancelMove_Locked(Index srcChunkIdx, bool bIsContentNeeded);
void Relocate(uint32 userMoveId, Index srcChunkIdx, Index dstChunkIdx);
void SyncMoveSegment(uint32 seg);
void RebuildFreeLists();
void ValidateAddressChain();
void ValidateFreeLists();
int BucketForSize(size_t sz) const
{
return sz > 0
? static_cast<int>(IntegerLog2(sz))
: 0;
}
private:
bool m_isThreadSafe;
bool m_chunksAreFixed;
CryCriticalSection m_lock;
uint32 m_capacity;
uint32 m_available;
Index m_numAllocs;
uint16 m_minAlignment;
uint16 m_logMinAlignment;
Index m_freeBuckets[NumBuckets];
std::vector<SDefragAllocChunk> m_chunks;
std::vector<Index> m_unusedChunks;
PendingMoveVec m_pendingMoves;
SegmentVec m_segments;
uint32 m_nCancelledMoves;
Policy m_policy;
};
#endif // CRYINCLUDE_CRYSYSTEM_DEFRAGALLOCATOR_H
+197
View File
@@ -0,0 +1,197 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "System.h"
#include <AZCrySystemInitLogSink.h>
#include "DebugCallStack.h"
#if defined(AZ_MONOLITHIC_BUILD)
#include <CryCommon/CryExtension/Impl/ICryFactoryRegistryImpl.h>
#include <CryCommon/CryExtension/Impl/RegFactoryNode.h>
#endif
#if defined(AZ_RESTRICTED_PLATFORM)
#undef AZ_RESTRICTED_SECTION
#define DLLMAIN_CPP_SECTION_1 1
#define DLLMAIN_CPP_SECTION_2 2
#define DLLMAIN_CPP_SECTION_3 3
#define DLLMAIN_CPP_SECTION_4 4
#endif
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION DLLMAIN_CPP_SECTION_1
#include AZ_RESTRICTED_FILE(DllMain_cpp)
#endif
// For lua debugger
//#include <malloc.h>
HMODULE gDLLHandle = NULL;
#if !defined(AZ_MONOLITHIC_BUILD) && defined(AZ_HAS_DLL_SUPPORT) && AZ_TRAIT_LEGACY_CRYSYSTEM_DEFINE_DLLMAIN
AZ_PUSH_DISABLE_WARNING(4447, "-Wunknown-warning-option")
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
[[maybe_unused]] LPVOID lpReserved
)
AZ_POP_DISABLE_WARNING
{
PREVENT_MODULE_AND_ENVIRONMENT_SYMBOL_STRIPPING
gDLLHandle = (HMODULE)hModule;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
// int sbh = _set_sbh_threshold(1016);
return TRUE;
}
#endif
//////////////////////////////////////////////////////////////////////////
struct CSystemEventListner_System
: public ISystemEventListener
{
public:
virtual void OnSystemEvent(ESystemEvent event, [[maybe_unused]] UINT_PTR wparam, [[maybe_unused]] UINT_PTR lparam)
{
switch (event)
{
case ESYSTEM_EVENT_LEVEL_UNLOAD:
gEnv->pSystem->SetThreadState(ESubsys_Physics, false);
break;
case ESYSTEM_EVENT_LEVEL_LOAD_START:
case ESYSTEM_EVENT_LEVEL_LOAD_END:
{
CryCleanup();
break;
}
case ESYSTEM_EVENT_LEVEL_POST_UNLOAD:
{
CryCleanup();
STLALLOCATOR_CLEANUP;
gEnv->pSystem->SetThreadState(ESubsys_Physics, true);
break;
}
}
}
};
static CSystemEventListner_System g_system_event_listener_system;
static AZ::EnvironmentVariable<IMemoryManager*> s_cryMemoryManager;
// Force the CryMemoryManager into the AZ::Environment for exposure to other DLLs
void ExportCryMemoryManager()
{
IMemoryManager* cryMemoryManager = nullptr;
CryGetIMemoryManagerInterface((void**)&cryMemoryManager);
AZ_Assert(cryMemoryManager, "Unable to resolve CryMemoryManager");
s_cryMemoryManager = AZ::Environment::CreateVariable<IMemoryManager*>("CryIMemoryManagerInterface", cryMemoryManager);
}
extern "C"
{
CRYSYSTEM_API ISystem* CreateSystemInterface(const SSystemInitParams& startupParams)
{
CSystem* pSystem = NULL;
// We must attach to the environment prior to allocating CSystem, as opposed to waiting
// for ModuleInitISystem(), because the log message sink uses buses.
// Environment should have been attached via InjectEnvironment
AZ_Assert(AZ::Environment::IsReady(), "Environment is not attached, must be attached before CreateSystemInterface can be called");
ExportCryMemoryManager();
pSystem = new CSystem(startupParams.pSharedEnvironment);
ModuleInitISystem(pSystem, "CrySystem");
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION DLLMAIN_CPP_SECTION_2
#include AZ_RESTRICTED_FILE(DllMain_cpp)
#endif
#if defined(AZ_MONOLITHIC_BUILD)
ICryFactoryRegistryImpl* pCryFactoryImpl = static_cast<ICryFactoryRegistryImpl*>(pSystem->GetCryFactoryRegistry());
pCryFactoryImpl->RegisterFactories(g_pHeadToRegFactories);
#endif // AZ_MONOLITHIC_BUILD
// the earliest point the system exists - w2e tell the callback
if (startupParams.pUserCallback)
{
startupParams.pUserCallback->OnSystemConnect(pSystem);
}
// Environment Variable to signal we don't want to override our exception handler - our crash report system will set this
auto envVar = AZ::Environment::FindVariable<bool>("ExceptionHandlerIsSet");
bool handlerIsSet = (envVar && *envVar);
if (!startupParams.bMinimal && !handlerIsSet) // in minimal mode, we want to crash when we crash!
{
#if defined(WIN32)
// Install exception handler in Release modes.
((DebugCallStack*)IDebugCallStack::instance())->installErrorHandler(pSystem);
#elif defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION DLLMAIN_CPP_SECTION_3
#include AZ_RESTRICTED_FILE(DllMain_cpp)
#endif
}
bool retVal = false;
{
AZ::Debug::StartupLogSinkReporter<AZ::Debug::CrySystemInitLogSink> initLogSink;
retVal = pSystem->Init(startupParams);
if (!retVal)
{
initLogSink.GetContainedLogSink().SetFatalMessageBox();
}
}
if (!retVal)
{
delete pSystem;
gEnv = nullptr;
return 0;
}
pSystem->GetISystemEventDispatcher()->RegisterListener(&g_system_event_listener_system);
return pSystem;
}
CRYSYSTEM_API void WINAPI CryInstallUnhandledExceptionHandler()
{
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION DLLMAIN_CPP_SECTION_4
#include AZ_RESTRICTED_FILE(DllMain_cpp)
#endif
}
#if defined(ENABLE_PROFILING_CODE) && !defined(LINUX) && !defined(APPLE)
CRYSYSTEM_API void CryInstallPostExceptionHandler(void (* PostExceptionHandlerCallback)())
{
return IDebugCallStack::instance()->FileCreationCallback(PostExceptionHandlerCallback);
}
#endif
};
@@ -0,0 +1,359 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Part of CryEngine's extension framework.
#include "CrySystem_precompiled.h"
#include "CryFactoryRegistryImpl.h"
#include "../System.h"
#include <CryExtension/ICryUnknown.h>
#include <CryExtension/Impl/RegFactoryNode.h>
#include <CryExtension/Impl/CryGUIDHelper.h>
#include <algorithm>
CCryFactoryRegistryImpl::CCryFactoryRegistryImpl()
: m_guard()
, m_byCName()
, m_byCID()
, m_byIID()
, m_callbacks()
{
}
CCryFactoryRegistryImpl::~CCryFactoryRegistryImpl()
{
}
CCryFactoryRegistryImpl& CCryFactoryRegistryImpl::Access()
{
static StaticInstance<CCryFactoryRegistryImpl, AZStd::no_destruct<CCryFactoryRegistryImpl>> s_registry;
return s_registry;
}
ICryFactory* CCryFactoryRegistryImpl::GetFactory(const char* cname) const
{
AUTO_READLOCK(m_guard);
if (!cname)
{
return 0;
}
const FactoryByCName search(cname);
FactoriesByCNameConstIt it = std::lower_bound(m_byCName.begin(), m_byCName.end(), search);
return it != m_byCName.end() && !(search < *it) ? (*it).m_ptr : 0;
}
ICryFactory* CCryFactoryRegistryImpl::GetFactory(const CryClassID& cid) const
{
AUTO_READLOCK(m_guard);
const FactoryByCID search(cid);
FactoriesByCIDConstIt it = std::lower_bound(m_byCID.begin(), m_byCID.end(), search);
return it != m_byCID.end() && !(search < *it) ? (*it).m_ptr : 0;
}
void CCryFactoryRegistryImpl::IterateFactories(const CryInterfaceID& iid, ICryFactory** pFactories, size_t& numFactories) const
{
AUTO_READLOCK(m_guard);
typedef std::pair<FactoriesByIIDConstIt, FactoriesByIIDConstIt> SearchResult;
SearchResult res = std::equal_range(m_byIID.begin(), m_byIID.end(), FactoryByIID(iid, 0), LessPredFactoryByIIDOnly());
const size_t numFactoriesFound = std::distance(res.first, res.second);
if (pFactories)
{
numFactories = min(numFactories, numFactoriesFound);
FactoriesByIIDConstIt it = res.first;
for (size_t i = 0; i < numFactories; ++i, ++it)
{
pFactories[i] = (*it).m_ptr;
}
}
else
{
numFactories = numFactoriesFound;
}
}
void CCryFactoryRegistryImpl::RegisterCallback(ICryFactoryRegistryCallback* pCallback)
{
if (!pCallback)
{
return;
}
{
AUTO_MODIFYLOCK(m_guard);
Callbacks::iterator it = std::lower_bound(m_callbacks.begin(), m_callbacks.end(), pCallback);
if (it == m_callbacks.end() || pCallback < *it)
{
m_callbacks.insert(it, pCallback);
}
else
{
assert(0 && "CCryFactoryRegistryImpl::RegisterCallback() -- pCallback already registered!");
}
}
{
AUTO_READLOCK(m_guard);
typedef std::pair<FactoriesByIIDConstIt, FactoriesByIIDConstIt> SearchResult;
SearchResult res = std::equal_range(m_byIID.begin(), m_byIID.end(), FactoryByIID(cryiidof<ICryUnknown>(), 0), LessPredFactoryByIIDOnly());
for (; res.first != res.second; ++res.first)
{
pCallback->OnNotifyFactoryRegistered((*res.first).m_ptr);
}
}
}
void CCryFactoryRegistryImpl::UnregisterCallback(ICryFactoryRegistryCallback* pCallback)
{
if (!pCallback)
{
return;
}
AUTO_MODIFYLOCK(m_guard);
Callbacks::iterator it = std::lower_bound(m_callbacks.begin(), m_callbacks.end(), pCallback);
if (it != m_callbacks.end() && !(pCallback < *it))
{
m_callbacks.erase(it);
}
}
bool CCryFactoryRegistryImpl::GetInsertionPos(ICryFactory* pFactory, FactoriesByCNameIt& itPosForCName, FactoriesByCIDIt& itPosForCID)
{
assert(pFactory);
struct FatalError
{
static void Report(ICryFactory* pKnownFactory, ICryFactory* pNewFactory)
{
char err[1024];
sprintf_s(err, sizeof(err), "Conflicting factories...\n"
"Factory (0x%p): ClassID = %s, ClassName = \"%s\"\n"
"Factory (0x%p): ClassID = %s, ClassName = \"%s\"",
pKnownFactory, pKnownFactory ? CryGUIDHelper::Print(pKnownFactory->GetClassID()).c_str() : "$unknown$", pKnownFactory ? pKnownFactory->GetName() : "$unknown$",
pNewFactory, pNewFactory ? CryGUIDHelper::Print(pNewFactory->GetClassID()).c_str() : "$unknown$", pNewFactory ? pNewFactory->GetName() : "$unknown$");
#if AZ_LEGACY_CRYSYSTEM_TRAIT_FACTORY_REGISTRY_USE_PRINTF_FOR_FATAL
printf("\n!!! Fatal error !!!\n");
printf(err);
printf("\n");
#elif defined(WIN32) || defined(WIN64)
OutputDebugStringA("\n!!! Fatal error !!!\n");
OutputDebugStringA(err);
OutputDebugStringA("\n");
MessageBoxA(0, err, "!!! Fatal error !!!", MB_OK | MB_ICONERROR);
#endif
assert(0);
exit(0);
}
};
FactoryByCName searchByCName(pFactory);
FactoriesByCNameIt itForCName = std::lower_bound(m_byCName.begin(), m_byCName.end(), searchByCName);
if (itForCName != m_byCName.end())
{
// If the addresses match, then this factory is already registered. It's not really worth error-ing about,
// as double registration will not cause any harm.
if (itForCName->m_ptr == pFactory)
{
return false;
}
if (!(searchByCName < *itForCName))
{
FatalError::Report((*itForCName).m_ptr, pFactory);
}
}
FactoryByCID searchByCID(pFactory);
FactoriesByCIDIt itForCID = std::lower_bound(m_byCID.begin(), m_byCID.end(), searchByCID);
if (itForCID != m_byCID.end() && !(searchByCID < *itForCID))
{
FatalError::Report((*itForCID).m_ptr, pFactory);
}
itPosForCName = itForCName;
itPosForCID = itForCID;
return true;
}
void CCryFactoryRegistryImpl::RegisterFactories(const SRegFactoryNode* pFactories)
{
size_t numFactoriesToAdd = 0;
size_t numInterfacesSupported = 0;
{
const SRegFactoryNode* p = pFactories;
while (p)
{
ICryFactory* pFactory = p->m_pFactory;
assert(pFactory);
if (pFactory)
{
const CryInterfaceID* pIIDs = 0;
size_t numIIDs = 0;
pFactory->ClassSupports(pIIDs, numIIDs);
numInterfacesSupported += numIIDs;
++numFactoriesToAdd;
}
p = p->m_pNext;
}
}
{
AUTO_MODIFYLOCK(m_guard);
m_byCName.reserve(m_byCName.size() + numFactoriesToAdd);
m_byCID.reserve(m_byCID.size() + numFactoriesToAdd);
m_byIID.reserve(m_byIID.size() + numInterfacesSupported);
size_t numFactoriesAdded = 0;
const SRegFactoryNode* p = pFactories;
while (p)
{
ICryFactory* pFactory = p->m_pFactory;
if (pFactory)
{
FactoriesByCNameIt itPosForCName;
FactoriesByCIDIt itPosForCID;
if (GetInsertionPos(pFactory, itPosForCName, itPosForCID))
{
m_byCName.insert(itPosForCName, FactoryByCName(pFactory));
m_byCID.insert(itPosForCID, FactoryByCID(pFactory));
const CryInterfaceID* pIIDs = 0;
size_t numIIDs = 0;
pFactory->ClassSupports(pIIDs, numIIDs);
for (size_t i = 0; i < numIIDs; ++i)
{
const FactoryByIID newFactory(pIIDs[i], pFactory);
m_byIID.push_back(newFactory);
}
for (size_t i = 0, s = m_callbacks.size(); i < s; ++i)
{
m_callbacks[i]->OnNotifyFactoryRegistered(pFactory);
}
++numFactoriesAdded;
}
}
p = p->m_pNext;
}
if (numFactoriesAdded)
{
std::sort(m_byIID.begin(), m_byIID.end());
}
}
}
void CCryFactoryRegistryImpl::UnregisterFactories(const SRegFactoryNode* pFactories)
{
AUTO_MODIFYLOCK(m_guard);
const SRegFactoryNode* p = pFactories;
while (p)
{
ICryFactory* pFactory = p->m_pFactory;
UnregisterFactoryInternal(pFactory);
p = p->m_pNext;
}
}
void CCryFactoryRegistryImpl::UnregisterFactory(ICryFactory* const pFactory)
{
AUTO_MODIFYLOCK(m_guard);
UnregisterFactoryInternal(pFactory);
}
void CCryFactoryRegistryImpl::UnregisterFactoryInternal(ICryFactory* const pFactory)
{
if (pFactory)
{
FactoryByCName searchByCName(pFactory);
FactoriesByCNameIt itForCName = std::lower_bound(m_byCName.begin(), m_byCName.end(), searchByCName);
if (itForCName != m_byCName.end() && !(searchByCName < *itForCName))
{
assert((*itForCName).m_ptr == pFactory);
if ((*itForCName).m_ptr == pFactory)
{
m_byCName.erase(itForCName);
}
}
FactoryByCID searchByCID(pFactory);
FactoriesByCIDIt itForCID = std::lower_bound(m_byCID.begin(), m_byCID.end(), searchByCID);
if (itForCID != m_byCID.end() && !(searchByCID < *itForCID))
{
assert((*itForCID).m_ptr == pFactory);
if ((*itForCID).m_ptr == pFactory)
{
m_byCID.erase(itForCID);
}
}
const CryInterfaceID* pIIDs = 0;
size_t numIIDs = 0;
pFactory->ClassSupports(pIIDs, numIIDs);
for (size_t i = 0; i < numIIDs; ++i)
{
FactoryByIID searchByIID(pIIDs[i], pFactory);
FactoriesByIIDIt itForIID = std::lower_bound(m_byIID.begin(), m_byIID.end(), searchByIID);
if (itForIID != m_byIID.end() && !(searchByIID < *itForIID))
{
m_byIID.erase(itForIID);
}
}
for (size_t i = 0, s = m_callbacks.size(); i < s; ++i)
{
m_callbacks[i]->OnNotifyFactoryUnregistered(pFactory);
}
}
}
ICryFactoryRegistry* CSystem::GetCryFactoryRegistry() const
{
return &CCryFactoryRegistryImpl::Access();
}
@@ -0,0 +1,128 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Part of CryEngine's extension framework.
#ifndef CRYINCLUDE_CRYSYSTEM_EXTENSIONSYSTEM_CRYFACTORYREGISTRYIMPL_H
#define CRYINCLUDE_CRYSYSTEM_EXTENSIONSYSTEM_CRYFACTORYREGISTRYIMPL_H
#pragma once
#include <CryExtension/Impl/ICryFactoryRegistryImpl.h>
#include <CryExtension/ICryFactory.h>
#include <AzCore/std/containers/vector.h>
class CCryFactoryRegistryImpl
: public ICryFactoryRegistryImpl
{
public:
virtual ICryFactory* GetFactory(const char* cname) const;
virtual ICryFactory* GetFactory(const CryClassID& cid) const;
virtual void IterateFactories(const CryInterfaceID& iid, ICryFactory** pFactories, size_t& numFactories) const;
virtual void RegisterCallback(ICryFactoryRegistryCallback* pCallback);
virtual void UnregisterCallback(ICryFactoryRegistryCallback* pCallback);
virtual void RegisterFactories(const SRegFactoryNode* pFactories);
virtual void UnregisterFactories(const SRegFactoryNode* pFactories);
virtual void UnregisterFactory(ICryFactory* const pFactory);
public:
static CCryFactoryRegistryImpl& Access();
CCryFactoryRegistryImpl();
~CCryFactoryRegistryImpl();
private:
struct FactoryByCName
{
const char* m_cname;
ICryFactory* m_ptr;
FactoryByCName(const char* cname)
: m_cname(cname)
, m_ptr(0) {assert(m_cname); }
FactoryByCName(ICryFactory* ptr)
: m_cname(ptr ? ptr->GetName() : 0)
, m_ptr(ptr) {assert(m_cname && m_ptr); }
bool operator <(const FactoryByCName& rhs) const {return strcmp(m_cname, rhs.m_cname) < 0; }
};
typedef std::vector<FactoryByCName> FactoriesByCName;
typedef FactoriesByCName::iterator FactoriesByCNameIt;
typedef FactoriesByCName::const_iterator FactoriesByCNameConstIt;
struct FactoryByCID
{
CryClassID m_cid;
ICryFactory* m_ptr;
FactoryByCID(const CryClassID& cid)
: m_cid(cid)
, m_ptr(0) {}
FactoryByCID(ICryFactory* ptr)
: m_cid(ptr ? ptr->GetClassID() : MAKE_CRYGUID(0, 0))
, m_ptr(ptr) {assert(m_ptr); }
bool operator <(const FactoryByCID& rhs) const {return m_cid < rhs.m_cid; }
};
typedef std::vector<FactoryByCID> FactoriesByCID;
typedef FactoriesByCID::iterator FactoriesByCIDIt;
typedef FactoriesByCID::const_iterator FactoriesByCIDConstIt;
struct FactoryByIID
{
CryInterfaceID m_iid;
ICryFactory* m_ptr;
FactoryByIID(CryInterfaceID iid, ICryFactory* pFactory)
: m_iid(iid)
, m_ptr(pFactory) {}
bool operator <(const FactoryByIID& rhs) const
{
if (m_iid != rhs.m_iid)
{
return m_iid < rhs.m_iid;
}
return m_ptr < rhs.m_ptr;
}
};
typedef std::vector<FactoryByIID> FactoriesByIID;
typedef FactoriesByIID::iterator FactoriesByIIDIt;
typedef FactoriesByIID::const_iterator FactoriesByIIDConstIt;
struct LessPredFactoryByIIDOnly
{
bool operator ()(const FactoryByIID& lhs, const FactoryByIID& rhs) const {return lhs.m_iid < rhs.m_iid; }
};
typedef std::vector<ICryFactoryRegistryCallback*> Callbacks;
typedef Callbacks::iterator CallbacksIt;
typedef Callbacks::const_iterator CallbacksConstIt;
private:
bool GetInsertionPos(ICryFactory* pFactory, FactoriesByCNameIt& itPosForCName, FactoriesByCIDIt& itPosForCID);
void UnregisterFactoryInternal(ICryFactory* const pFactory);
private:
mutable CryReadModifyLock m_guard;
FactoriesByCName m_byCName;
FactoriesByCID m_byCID;
FactoriesByIID m_byIID;
Callbacks m_callbacks;
};
#endif // CRYINCLUDE_CRYSYSTEM_EXTENSIONSYSTEM_CRYFACTORYREGISTRYIMPL_H
@@ -0,0 +1,955 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Part of CryEngine's extension framework.
#include "CrySystem_precompiled.h"
#include "TestExtensions.h"
#ifdef EXTENSION_SYSTEM_INCLUDE_TESTCASES
#include <CryExtension/Impl/ClassWeaver.h>
#include <CryExtension/Impl/ICryFactoryRegistryImpl.h>
#include <CryExtension/CryCreateClassInstance.h>
//////////////////////////////////////////////////////////////////////////
namespace TestComposition
{
struct ITestExt1
: public ICryUnknown
{
CRYINTERFACE_DECLARE(ITestExt1, 0x9d9e0dcfa5764cb0, 0xa73701595f75bd32)
virtual void Call1() const = 0;
};
DECLARE_SMART_POINTERS(ITestExt1);
class CTestExt1
: public ITestExt1
{
CRYINTERFACE_BEGIN()
CRYINTERFACE_ADD(ITestExt1)
CRYINTERFACE_END()
CRYGENERATE_CLASS(CTestExt1, "TestExt1", 0x43b04e7cc1be45ca, 0x9df6ccb1c0dc1ad8)
public:
virtual void Call1() const;
private:
int i;
};
CRYREGISTER_CLASS(CTestExt1)
CTestExt1::CTestExt1()
{
i = 1;
}
CTestExt1::~CTestExt1()
{
printf("Inside CTestExt1 dtor\n");
}
void CTestExt1::Call1() const
{
printf("Inside CTestExt1::Call1()\n");
}
//////////////////////////////////////////////////////////////////////////
struct ITestExt2
: public ICryUnknown
{
CRYINTERFACE_DECLARE(ITestExt2, 0x8eb7a4b399874b9c, 0xb96bd6da7a8c72f9)
virtual void Call2() = 0;
};
DECLARE_SMART_POINTERS(ITestExt2);
class CTestExt2
: public ITestExt2
{
CRYINTERFACE_BEGIN()
CRYINTERFACE_ADD(ITestExt2)
CRYINTERFACE_END()
CRYGENERATE_CLASS(CTestExt2, "TestExt2", 0x25b3ebf8f1754b9a, 0xb5494e3da7cdd80f)
public:
virtual void Call2();
private:
int i;
};
CRYREGISTER_CLASS(CTestExt2)
CTestExt2::CTestExt2()
{
i = 2;
}
CTestExt2::~CTestExt2()
{
printf("Inside CTestExt2 dtor\n");
}
void CTestExt2::Call2()
{
printf("Inside CTestExt2::Call2()\n");
}
//////////////////////////////////////////////////////////////////////////
class CComposed
: public ICryUnknown
{
CRYGENERATE_CLASS(CComposed, "Composed", 0x0439d74b8dcd4b7f, 0x9287dcdf7e26a3a5)
CRYCOMPOSITE_BEGIN()
CRYCOMPOSITE_ADD(m_pTestExt1, "Ext1")
CRYCOMPOSITE_ADD(m_pTestExt2, "Ext2")
CRYCOMPOSITE_END(CComposed)
CRYINTERFACE_BEGIN()
CRYINTERFACE_END()
private:
ITestExt1Ptr m_pTestExt1;
ITestExt2Ptr m_pTestExt2;
};
CRYREGISTER_CLASS(CComposed)
CComposed::CComposed()
: m_pTestExt1()
, m_pTestExt2()
{
CryCreateClassInstance("TestExt1", m_pTestExt1);
CryCreateClassInstance("TestExt2", m_pTestExt2);
}
CComposed::~CComposed()
{
}
//////////////////////////////////////////////////////////////////////////
struct ITestExt3
: public ICryUnknown
{
CRYINTERFACE_DECLARE(ITestExt3, 0xdd017935a2134898, 0xbd2fffa145551876)
virtual void Call3() = 0;
};
DECLARE_SMART_POINTERS(ITestExt3);
class CTestExt3
: public ITestExt3
{
CRYGENERATE_CLASS(CTestExt3, "TestExt3", 0xeceab40bc4bb4988, 0xa9f63c1db85a69b1)
CRYINTERFACE_BEGIN()
CRYINTERFACE_ADD(ITestExt3)
CRYINTERFACE_END()
public:
virtual void Call3();
private:
int i;
};
CRYREGISTER_CLASS(CTestExt3)
CTestExt3::CTestExt3()
{
i = 3;
}
CTestExt3::~CTestExt3()
{
printf("Inside CTestExt3 dtor\n");
}
void CTestExt3::Call3()
{
printf("Inside CTestExt3::Call3()\n");
}
//////////////////////////////////////////////////////////////////////////
class CComposed2
: public ICryUnknown
{
CRYGENERATE_CLASS(CComposed2, "Composed2", 0x0439d74b8dcd4b7e, 0x9287dcdf7e26a3a6)
CRYCOMPOSITE_BEGIN()
CRYCOMPOSITE_ADD(m_pTestExt3, "Ext3")
CRYCOMPOSITE_END(CComposed2)
CRYINTERFACE_BEGIN()
CRYINTERFACE_END()
private:
ITestExt3Ptr m_pTestExt3;
};
CRYREGISTER_CLASS(CComposed2)
CComposed2::CComposed2()
: m_pTestExt3()
{
CryCreateClassInstance("TestExt3", m_pTestExt3);
}
CComposed2::~CComposed2()
{
}
//////////////////////////////////////////////////////////////////////////
class CTestExt4
: public ITestExt1
, public ITestExt2
, public ITestExt3
{
CRYINTERFACE_BEGIN()
CRYINTERFACE_ADD(ITestExt1)
CRYINTERFACE_ADD(ITestExt2)
CRYINTERFACE_ADD(ITestExt3)
CRYINTERFACE_END()
CRYGENERATE_CLASS(CTestExt4, "TestExt4", 0x43204e7cc1be45ca, 0x9df4ccb1c0dc1ad8)
public:
virtual void Call1() const;
virtual void Call2();
virtual void Call3();
private:
int i;
};
CRYREGISTER_CLASS(CTestExt4)
CTestExt4::CTestExt4()
{
i = 4;
}
CTestExt4::~CTestExt4()
{
printf("Inside CTestExt4 dtor\n");
}
void CTestExt4::Call1() const
{
printf("Inside CTestExt4::Call1()\n");
}
void CTestExt4::Call2()
{
printf("Inside CTestExt4::Call2()\n");
}
void CTestExt4::Call3()
{
printf("Inside CTestExt4::Call3()\n");
}
//////////////////////////////////////////////////////////////////////////
class CMegaComposed
: public CComposed
, public CComposed2
{
CRYGENERATE_CLASS(CMegaComposed, "MegaComposed", 0x512787559f84503, 0x421ac1af66f2fb6f)
CRYCOMPOSITE_BEGIN()
CRYCOMPOSITE_ADD(m_pTestExt4, "Ext4")
CRYCOMPOSITE_ENDWITHBASE2(CMegaComposed, CComposed, CComposed2)
CRYINTERFACE_BEGIN()
CRYINTERFACE_END()
private:
AZStd::shared_ptr<CTestExt4> m_pTestExt4;
};
CRYREGISTER_CLASS(CMegaComposed)
CMegaComposed::CMegaComposed()
: m_pTestExt4()
{
printf("Inside CMegaComposed ctor\n");
m_pTestExt4 = CTestExt4::CreateClassInstance();
}
CMegaComposed::~CMegaComposed()
{
printf("Inside CMegaComposed dtor\n");
}
//////////////////////////////////////////////////////////////////////////
static void TestComposition()
{
printf("\nTest composition:\n");
ICryUnknownPtr p;
if (CryCreateClassInstance("MegaComposed", p))
{
ITestExt1Ptr p1 = cryinterface_cast<ITestExt1>(crycomposite_query(p, "Ext1"));
if (p1)
{
p1->Call1(); // calls CTestExt1::Call1()
}
ITestExt2Ptr p2 = cryinterface_cast<ITestExt2>(crycomposite_query(p, "Ext2"));
if (p2)
{
p2->Call2(); // calls CTestExt2::Call2()
}
ITestExt3Ptr p3 = cryinterface_cast<ITestExt3>(crycomposite_query(p, "Ext3"));
if (p3)
{
p3->Call3(); // calls CTestExt3::Call3()
}
p3 = cryinterface_cast<ITestExt3>(crycomposite_query(p, "Ext4"));
if (p3)
{
p3->Call3(); // calls CTestExt4::Call3()
}
p1 = cryinterface_cast<ITestExt1>(crycomposite_query(p.get(), "Ext4"));
p2 = cryinterface_cast<ITestExt2>(crycomposite_query(p.get(), "Ext4"));
bool b = CryIsSameClassInstance(p1, p2); // true
}
{
ICryUnknownConstPtr pCUnk = p;
ICryUnknownConstPtr pComp1 = crycomposite_query(pCUnk.get(), "Ext1");
//ICryUnknownPtr pComp1 = crycomposite_query(pCUnk, "Ext1"); // must fail to compile due to const rules
ITestExt1ConstPtr p1 = cryinterface_cast<const ITestExt1>(pComp1);
if (p1)
{
p1->Call1();
}
}
}
} // namespace TestComposition
//////////////////////////////////////////////////////////////////////////
namespace TestExtension
{
class CFoobar
: public IFoobar
{
CRYINTERFACE_BEGIN()
CRYINTERFACE_ADD(IFoobar)
CRYINTERFACE_END()
CRYGENERATE_CLASS(CFoobar, "Foobar", 0x76c8dd6d16634531, 0x95d3b1cfabcf7ef4)
public:
virtual void Foo();
};
CRYREGISTER_CLASS(CFoobar)
CFoobar::CFoobar()
{
}
CFoobar::~CFoobar()
{
}
void CFoobar::Foo()
{
printf("Inside CFoobar::Foo()\n");
}
static void TestFoobar()
{
AZStd::shared_ptr<CFoobar> p = CFoobar::CreateClassInstance();
{
CryInterfaceID iid = cryiidof<IFoobar>();
CryClassID clsid = p->GetFactory()->GetClassID();
int t = 0;
}
{
IAPtr sp_ = cryinterface_cast<IA>(p); // sp_ == NULL
ICryUnknownPtr sp1 = cryinterface_cast<ICryUnknown>(p);
IFoobarPtr sp = cryinterface_cast<IFoobar>(sp1);
sp->Foo();
}
{
CFoobar* pF = p.get();
pF->Foo();
ICryUnknown* p1 = cryinterface_cast<ICryUnknown>(pF);
}
IFoobar* pFoo = cryinterface_cast<IFoobar>(p.get());
ICryFactory* pF1 = pFoo->GetFactory();
pFoo->Foo();
int t = 0;
}
//////////////////////////////////////////////////////////////////////////
class CRaboof
: public IRaboof
{
CRYINTERFACE_BEGIN()
CRYINTERFACE_ADD(IRaboof)
CRYINTERFACE_END()
CRYGENERATE_SINGLETONCLASS(CRaboof, "Raabof", 0xba482ce12b2e4309, 0x8238ed8b52cb1f1e)
public:
virtual void Rab();
};
CRYREGISTER_SINGLETON_CLASS(CRaboof)
CRaboof::CRaboof()
{
}
CRaboof::~CRaboof()
{
}
void CRaboof::Rab()
{
printf("Inside CRaboof::Rab()\n");
}
static void TestRaboof()
{
AZStd::shared_ptr<CRaboof> pFoo0_ = CRaboof::CreateClassInstance();
IRaboofPtr pFoo0 = cryinterface_cast<IRaboof>(pFoo0_);
ICryUnknownPtr p0 = cryinterface_cast<ICryUnknown>(pFoo0);
CryInterfaceID iid = cryiidof<IRaboof>();
CryClassID clsid = p0->GetFactory()->GetClassID();
AZStd::shared_ptr<CRaboof> pFoo1 = CRaboof::CreateClassInstance();
pFoo0->Rab();
pFoo1->Rab();
}
//////////////////////////////////////////////////////////////////////////
class CAB
: public IA
, public IB
{
CRYINTERFACE_BEGIN()
CRYINTERFACE_ADD(IA)
CRYINTERFACE_ADD(IB)
CRYINTERFACE_END()
CRYGENERATE_CLASS(CAB, "AB", 0xb9e54711a64448c0, 0xa4819b4ed3024d04)
public:
virtual void A();
virtual void B();
private:
int i;
};
CRYREGISTER_CLASS(CAB)
CAB::CAB()
{
i = 0x12345678;
}
CAB::~CAB()
{
}
void CAB::A()
{
printf("Inside CAB::A()\n");
}
void CAB::B()
{
printf("Inside CAB::B()\n");
}
//////////////////////////////////////////////////////////////////////////
class CABC
: public CAB
, public IC
{
CRYINTERFACE_BEGIN()
CRYINTERFACE_ADD(IC)
CRYINTERFACE_ENDWITHBASE(CAB)
CRYGENERATE_CLASS(CABC, "ABC", 0x4e61feae11854be7, 0xa16157c5f8baadd9)
public:
virtual void C();
private:
int a;
};
CRYREGISTER_CLASS(CABC)
CABC::CABC()
//: CAB()
{
a = 0x87654321;
}
CABC::~CABC()
{
}
void CABC::C()
{
printf("Inside CABC::C()\n");
}
//////////////////////////////////////////////////////////////////////////
class CCustomC
: public ICustomC
{
CRYINTERFACE_BEGIN()
CRYINTERFACE_ADD(IC)
CRYINTERFACE_ADD(ICustomC)
CRYINTERFACE_END()
CRYGENERATE_CLASS(CCustomC, "CustomC", 0xee61760b98a44b71, 0xa05e7372b44bd0fd)
public:
virtual void C();
virtual void C1();
private:
int a;
};
CRYREGISTER_CLASS(CCustomC)
CCustomC::CCustomC()
{
a = 0x87654321;
}
CCustomC::~CCustomC()
{
}
void CCustomC::C()
{
printf("Inside CCustomC::C()\n");
}
void CCustomC::C1()
{
printf("Inside CCustomC::C1()\n");
}
//////////////////////////////////////////////////////////////////////////
class CMultiBase
: public CAB
, public CCustomC
{
CRYINTERFACE_BEGIN()
CRYINTERFACE_ENDWITHBASE2(CAB, CCustomC)
CRYGENERATE_CLASS(CMultiBase, "MultiBase", 0x75966b8f98644d42, 0x8fbdd489e94cc29e)
public:
virtual void A();
virtual void C1();
int i;
};
CRYREGISTER_CLASS(CMultiBase)
CMultiBase::CMultiBase()
{
i = 0x87654321;
}
CMultiBase::~CMultiBase()
{
}
void CMultiBase::C1()
{
printf("Inside CMultiBase::C1()\n");
}
void CMultiBase::A()
{
printf("Inside CMultiBase::A()\n");
}
//////////////////////////////////////////////////////////////////////////
static void TestComplex()
{
{
ICPtr p;
if (CryCreateClassInstance(MAKE_CRYGUID(0x75966b8f98644d42, 0x8fbdd489e94cc29e), p))
{
p->C();
}
}
{
ICustomCPtr p;
if (CryCreateClassInstance("MultiBase", p))
{
p->C();
}
}
{
IFoobarPtr p;
if (CryCreateClassInstance(MAKE_CRYGUID(0x75966b8f98644d42, 0x8fbdd489e94cc29e), p))
{
p->Foo();
}
}
{
AZStd::shared_ptr<CMultiBase> p = CMultiBase::CreateClassInstance();
AZStd::shared_ptr<const CMultiBase> pc = p;
{
ICryUnknownPtr pUnk = cryinterface_cast<ICryUnknown>(p);
ICryUnknownConstPtr pCUnk0 = cryinterface_cast<const ICryUnknown>(p);
ICryUnknownConstPtr pCUnk1 = cryinterface_cast<const ICryUnknown>(pc);
//ICryUnknownPtr pUnkF = cryinterface_cast<ICryUnknown>(pc); // must fail to compile due to const rules
ICryFactory* pF = pUnk->GetFactory();
int t = 0;
}
ICPtr pC = cryinterface_cast<IC>(p);
ICustomCPtr pCC = cryinterface_cast<ICustomC>(pC);
p->C();
p->C1();
pC->C();
pCC->C1();
IAPtr pA = cryinterface_cast<IA>(p);
pA->A();
p->A();
}
{
AZStd::shared_ptr<CCustomC> p = CCustomC::CreateClassInstance();
ICPtr pC = cryinterface_cast<IC>(p);
ICustomCPtr pCC = cryinterface_cast<ICustomC>(pC);
p->C();
p->C1();
pC->C();
pCC->C1();
}
{
CryInterfaceID ia = cryiidof<IA>();
CryInterfaceID ib = cryiidof<IB>();
CryInterfaceID ic = cryiidof<IC>();
CryInterfaceID ico = cryiidof<ICryUnknown>();
}
{
AZStd::shared_ptr<CAB> p = CAB::CreateClassInstance();
CryClassID clsid = p->GetFactory()->GetClassID();
IAPtr pA = cryinterface_cast<IA>(p);
IBPtr pB = cryinterface_cast<IB>(p);
IBPtr pB1 = cryinterface_cast<IB>(pA);
IAPtr pA1 = cryinterface_cast<IA>(pB);
pA->A();
pB->B();
ICryUnknownPtr p1 = cryinterface_cast<ICryUnknown>(pA);
ICryUnknownPtr p2 = cryinterface_cast<ICryUnknown>(pB);
const ICryUnknown* p3 = cryinterface_cast<const ICryUnknown>(pB.get());
int t = 0;
}
{
AZStd::shared_ptr<CABC> pABC = CABC::CreateClassInstance();
CryClassID clsid = pABC->GetFactory()->GetClassID();
ICryFactory* pFac = pABC->GetFactory();
pFac->ClassSupports(cryiidof<IA>());
pFac->ClassSupports(cryiidof<IRaboof>());
IAPtr pABC0 = cryinterface_cast<IA>(pABC);
IBPtr pABC1 = cryinterface_cast<IB>(pABC0);
ICPtr pABC2 = cryinterface_cast<IC>(pABC1);
pABC2->C();
pABC1->B();
pABC2->GetFactory();
const IC* pCconst = pABC2.get();
const ICryUnknown* pOconst = cryinterface_cast<const ICryUnknown>(pCconst);
const IA* pAconst = cryinterface_cast<const IA>(pOconst);
const IB* pBconst = cryinterface_cast<const IB>(pAconst);
//const IA* pA11 = cryinterface_cast<IA>(pOconst);
pCconst = cryinterface_cast<const IC>(pBconst);
IC* pC = static_cast<IC*>(static_cast<void*>(pABC1.get()));
pC->C(); // calls IB::B()
int t = 0;
}
}
//////////////////////////////////////////////////////////////////////////
// use of extension system without any of the helper macros/templates
class CDontLikeMacrosFactory
: public ICryFactory
{
// ICryFactory
public:
virtual const char* GetClassName() const
{
return "DontLikeMacros";
}
virtual const CryClassID& GetClassID() const
{
static const CryClassID cid = {0x73c3ab0042e6488aull, 0x89ca1a3763365565ull};
return cid;
}
virtual bool ClassSupports(const CryInterfaceID& iid) const
{
return iid == cryiidof<ICryUnknown>() || iid == cryiidof<IDontLikeMacros>();
}
virtual void ClassSupports(const CryInterfaceID*& pIIDs, size_t& numIIDs) const
{
static const CryInterfaceID iids[2] = {cryiidof<ICryUnknown>(), cryiidof<IDontLikeMacros>()};
pIIDs = iids;
numIIDs = 2;
}
virtual ICryUnknownPtr CreateClassInstance() const;
public:
static CDontLikeMacrosFactory& Access()
{
return s_factory;
}
private:
CDontLikeMacrosFactory() {}
~CDontLikeMacrosFactory() {}
private:
static CDontLikeMacrosFactory s_factory;
};
CDontLikeMacrosFactory CDontLikeMacrosFactory::s_factory;
class CDontLikeMacros
: public IDontLikeMacros
{
// ICryUnknown
public:
virtual ICryFactory* GetFactory() const
{
return &CDontLikeMacrosFactory::Access();
};
// only needed to be able to create initial shared_ptr<CDontLikeMacros> so we don't lose type info for debugging (i.e. inspecting shared_ptr<>)
template <class T>
friend void AZStd::Internal::sp_ms_deleter<T>::destroy();
template <class T>
friend AZStd::shared_ptr<T> AZStd::make_shared<T>();
protected:
virtual void* QueryInterface(const CryInterfaceID& iid) const
{
if (iid == cryiidof<ICryUnknown>())
{
return (void*) (ICryUnknown*) this;
}
else if (iid == cryiidof<IDontLikeMacros>())
{
return (void*) (IDontLikeMacros*) this;
}
else
{
return 0;
}
}
virtual void* QueryComposite(const char*) const
{
return 0;
}
// IDontLikeMacros
public:
virtual void CallMe()
{
printf("Yey, no macros...\n");
}
CDontLikeMacros() {}
protected:
virtual ~CDontLikeMacros() {}
};
ICryUnknownPtr CDontLikeMacrosFactory::CreateClassInstance() const
{
AZStd::shared_ptr<CDontLikeMacros> p = AZStd::make_shared<CDontLikeMacros>();
return ICryUnknownPtr(*static_cast<AZStd::shared_ptr<ICryUnknown>*>(static_cast<void*>(&p)));
}
static SRegFactoryNode g_dontLikeMacrosFactory(&CDontLikeMacrosFactory::Access());
//////////////////////////////////////////////////////////////////////////
static void TestDontLikeMacros()
{
ICryFactory* f = &CDontLikeMacrosFactory::Access();
f->ClassSupports(cryiidof<ICryUnknown>());
f->ClassSupports(cryiidof<IDontLikeMacros>());
const CryInterfaceID* pIIDs = 0;
size_t numIIDs = 0;
f->ClassSupports(pIIDs, numIIDs);
ICryUnknownPtr p = f->CreateClassInstance();
IDontLikeMacrosPtr pp = cryinterface_cast<IDontLikeMacros>(p);
ICryUnknownPtr pq = crycomposite_query(p, "blah");
pp->CallMe();
}
} // namespace TestExtension
//////////////////////////////////////////////////////////////////////////
void TestExtensions(ICryFactoryRegistryImpl* pReg)
{
printf("Test extensions:\n");
struct MyCallback
: public ICryFactoryRegistryCallback
{
virtual void OnNotifyFactoryRegistered(ICryFactory* pFactory)
{
int test = 0;
}
virtual void OnNotifyFactoryUnregistered(ICryFactory* pFactory)
{
int test = 0;
}
};
//pReg->RegisterCallback((ICryFactoryRegistryCallback*) 0x4);
//pReg->RegisterCallback((ICryFactoryRegistryCallback*) 0x1);
//pReg->RegisterCallback((ICryFactoryRegistryCallback*) 0x3);
//pReg->RegisterCallback((ICryFactoryRegistryCallback*) 0x3);
//pReg->RegisterCallback((ICryFactoryRegistryCallback*) 0x2);
//pReg->UnregisterCallback((ICryFactoryRegistryCallback*) 0x2);
//pReg->UnregisterCallback((ICryFactoryRegistryCallback*) 0x2);
//pReg->UnregisterCallback((ICryFactoryRegistryCallback*) 0x4);
//pReg->UnregisterCallback((ICryFactoryRegistryCallback*) 0x3);
//pReg->UnregisterCallback((ICryFactoryRegistryCallback*) 0x1);
//MyCallback callback0;
//pReg->RegisterCallback(&callback0);
//pReg->RegisterFactories(g_pHeadToRegFactories);
//pReg->RegisterFactories(g_pHeadToRegFactories);
//pReg->UnregisterFactories(g_pHeadToRegFactories);
ICryFactory* pF[4];
size_t numFactories = 4;
pReg->IterateFactories(cryiidof<IA>(), pF, numFactories);
pReg->IterateFactories(MAKE_CRYGUID(-1, -1), pF, numFactories);
numFactories = (size_t) -1;
pReg->IterateFactories(cryiidof<ICryUnknown>(), 0, numFactories);
MyCallback callback1;
pReg->RegisterCallback(&callback1);
pReg->UnregisterCallback(&callback1);
ICryFactory* p;
p = pReg->GetFactory(MAKE_CRYGUID(0xee61760b98a44b71, 0xa05e7372b44bd0fd));
p = pReg->GetFactory("CustomC");
p = pReg->GetFactory("ABC");
p = pReg->GetFactory((const char*)0);
p = pReg->GetFactory("DontLikeMacros");
p = pReg->GetFactory(MAKE_CRYGUID(0x73c3ab0042e6488a, 0x89ca1a3763365565));
TestExtension::TestFoobar();
TestExtension::TestRaboof();
TestExtension::TestComplex();
TestExtension::TestDontLikeMacros();
TestComposition::TestComposition();
}
#endif // #ifdef EXTENSION_SYSTEM_INCLUDE_TESTCASES
@@ -0,0 +1,126 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Part of CryEngine's extension framework.
#ifndef CRYINCLUDE_CRYSYSTEM_EXTENSIONSYSTEM_TESTCASES_TESTEXTENSIONS_H
#define CRYINCLUDE_CRYSYSTEM_EXTENSIONSYSTEM_TESTCASES_TESTEXTENSIONS_H
#pragma once
//#define EXTENSION_SYSTEM_INCLUDE_TESTCASES
#ifdef EXTENSION_SYSTEM_INCLUDE_TESTCASES
#include <CryExtension/ICryUnknown.h>
struct ICryFactoryRegistryImpl;
void TestExtensions(ICryFactoryRegistryImpl* pReg);
struct IFoobar
: public ICryUnknown
{
CRYINTERFACE_DECLARE(IFoobar, 0x539e9c672cad4a03, 0x9ecd8069c99a846b)
virtual void Foo() = 0;
};
DECLARE_SMART_POINTERS(IFoobar);
struct IRaboof
: public ICryUnknown
{
CRYINTERFACE_DECLARE(IRaboof, 0x135ca25e634b4d13, 0x9e4467968a708822)
virtual void Rab() = 0;
};
DECLARE_SMART_POINTERS(IRaboof);
struct IA
: public ICryUnknown
{
CRYINTERFACE_DECLARE(IA, 0xd93aaceb35ec427e, 0xb64bf8dec4997e67)
virtual void A() = 0;
};
DECLARE_SMART_POINTERS(IA);
struct IB
: public ICryUnknown
{
CRYINTERFACE_DECLARE(IB, 0xe0d830c826424e11, 0x9eacfa19eaf31ffb)
virtual void B() = 0;
};
DECLARE_SMART_POINTERS(IB);
struct IC
: public ICryUnknown
{
CRYINTERFACE_DECLARE(IC, 0x577509a20fc5477c, 0x893757c9ca88b27b)
virtual void C() = 0;
};
DECLARE_SMART_POINTERS(IC);
struct ICustomC
: public IC
{
CRYINTERFACE_DECLARE(ICustomC, 0x2ac769da4c7443bf, 0x80911033e21dfbcf)
virtual void C1() = 0;
};
DECLARE_SMART_POINTERS(ICustomC);
//////////////////////////////////////////////////////////////////////////
// use of extension system without any of the helper macros/templates
struct IDontLikeMacros
: public ICryUnknown
{
template <class T>
friend const CryInterfaceID& InterfaceCastSemantics::cryiidof();
template <class T>
friend void AZStd::Internal::sp_ms_deleter<T>::destroy();
template <class T>
friend AZStd::shared_ptr<T> AZStd::make_shared<T>();
protected:
virtual ~IDontLikeMacros() {}
private:
// It's very important that this static function is implemented for each interface!
// Otherwise the consistency of cryinterface_cast<T>() is compromised because
// cryiidof<T>() = cryiidof<baseof<T>>() {baseof<T> = ICryUnknown in most cases}
static const CryInterfaceID& IID()
{
static const CryInterfaceID iid = {0x0f43b7e3f1364af0ull, 0xb4a16a975bea3ec4ull};
return iid;
}
public:
virtual void CallMe() = 0;
};
DECLARE_SMART_POINTERS(IDontLikeMacros);
#endif // #ifdef EXTENSION_SYSTEM_INCLUDE_TESTCASES
#endif // CRYINCLUDE_CRYSYSTEM_EXTENSIONSYSTEM_TESTCASES_TESTEXTENSIONS_H
@@ -0,0 +1,188 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "GeneralMemoryHeap.h"
#include <AzCore/Memory/AllocatorBase.h>
#include <AzCore/Memory/HphaSchema.h>
class GeneralMemoryHeapAllocator
: public AZ::SimpleSchemaAllocator<AZ::HphaSchema>
{
using Base = AZ::SimpleSchemaAllocator<AZ::HphaSchema>;
public:
static const size_t DEFAULT_ALIGNMENT = sizeof(void*);
GeneralMemoryHeapAllocator(const char* desc)
: Base("GeneralMemoryHeapAllocator", desc)
{
}
void Reserve(size_t size)
{
// Allocate a block, then free it, forcing it into the page tree/cache
void* block = m_schema->Allocate(size, GeneralMemoryHeapAllocator::DEFAULT_ALIGNMENT, 0, "GeneralMemoryHeapAllocator Reserve", __FILE__, __LINE__);
m_schema->DeAllocate(block);
}
};
CGeneralMemoryHeap::CGeneralMemoryHeap([[maybe_unused]] UINT_PTR base, [[maybe_unused]] size_t upperLimit, size_t reserveSize, const char* sUsage)
: m_refCount(0)
, m_block(nullptr)
, m_blockSize(0)
{
AZ::HphaSchema::Descriptor desc;
desc.m_subAllocator = &AZ::AllocatorInstance<AZ::LegacyAllocator>::Get();
m_allocator.reset(new AZ::AllocatorWrapper<GeneralMemoryHeapAllocator>);
m_allocator->Create(desc, sUsage);
if (reserveSize)
{
(*m_allocator)->Reserve(reserveSize);
}
}
CGeneralMemoryHeap::CGeneralMemoryHeap(void* base, size_t size, const char* sUsage)
: m_refCount(0)
, m_block(base)
, m_blockSize(size)
{
AZ::HphaSchema::Descriptor desc;
desc.m_fixedMemoryBlock = base;
desc.m_fixedMemoryBlockByteSize = size;
desc.m_fixedMemoryBlockAlignment = GeneralMemoryHeapAllocator::DEFAULT_ALIGNMENT;
m_allocator.reset(new AZ::AllocatorWrapper<GeneralMemoryHeapAllocator>);
m_allocator->Create(desc, sUsage);
}
CGeneralMemoryHeap::~CGeneralMemoryHeap()
{
}
bool CGeneralMemoryHeap::Cleanup()
{
(*m_allocator)->GarbageCollect();
return true;
}
int CGeneralMemoryHeap::AddRef()
{
return m_refCount.fetch_add(1);
}
int CGeneralMemoryHeap::Release()
{
int nRef = m_refCount.fetch_sub(1);
if (nRef <= 1)
{
delete this;
}
return nRef;
}
void CGeneralMemoryHeap::RecordAlloc(void* ptr, size_t size)
{
if (m_block == nullptr)
{
m_allocs.emplace(ptr, size);
}
}
void CGeneralMemoryHeap::RecordFree(void* ptr, size_t size)
{
if (m_block == nullptr)
{
m_allocs.erase(Alloc(ptr, size));
}
}
bool CGeneralMemoryHeap::IsInAddressRange(void* ptr) const
{
if (m_block)
{
return (static_cast<char*>(ptr) - static_cast<char*>(m_block)) <= m_blockSize;
}
auto it = m_allocs.find(Alloc(ptr));
return it != m_allocs.end();
}
void* CGeneralMemoryHeap::Calloc(size_t numElements, size_t size, const char* sUsage)
{
void* ptr = (*m_allocator)->Allocate(numElements * size, GeneralMemoryHeapAllocator::DEFAULT_ALIGNMENT, 0, sUsage, __FILE__, __LINE__);
memset(ptr, 0, numElements * size);
RecordAlloc(ptr, numElements * size);
return ptr;
}
void* CGeneralMemoryHeap::Malloc(size_t size, const char* sUsage)
{
void* ptr = (*m_allocator)->Allocate(size, GeneralMemoryHeapAllocator::DEFAULT_ALIGNMENT, 0, sUsage, __FILE__, __LINE__);
RecordAlloc(ptr, size);
return ptr;
}
size_t CGeneralMemoryHeap::Free(void* ptr)
{
// The client code using these heaps tend to use a guesswork algorithm to freeing
// which involves handing the pointer to every known heap until it frees, so
// it's necessary to validate that the ptr belongs to this heap before attempting to free
if (IsInAddressRange(ptr))
{
size_t size = (*m_allocator)->AllocationSize(ptr);
RecordFree(ptr, size);
(*m_allocator)->DeAllocate(ptr);
return size;
}
return 0;
}
void* CGeneralMemoryHeap::Realloc(void* ptr, size_t size, const char* /*sUsage*/)
{
RecordFree(ptr, (*m_allocator)->AllocationSize(ptr));
void* newPtr = (*m_allocator)->ReAllocate(ptr, size, GeneralMemoryHeapAllocator::DEFAULT_ALIGNMENT);
RecordAlloc(newPtr, size);
return newPtr;
}
void* CGeneralMemoryHeap::ReallocAlign(void* ptr, size_t size, size_t alignment, const char* /*sUsage*/)
{
RecordFree(ptr, (*m_allocator)->AllocationSize(ptr));
void* newPtr = (*m_allocator)->ReAllocate(ptr, size, alignment);
RecordAlloc(newPtr, size);
return newPtr;
}
void* CGeneralMemoryHeap::Memalign(size_t boundary, size_t size, const char* sUsage)
{
void* ptr = (*m_allocator)->Allocate(size, boundary, 0, sUsage, __FILE__, __LINE__);
RecordAlloc(ptr, size);
return ptr;
}
size_t CGeneralMemoryHeap::UsableSize(void* ptr) const
{
// The client code using these heaps tend to use a guesswork algorithm to determine
// which heap owns the pointer. Calls to UsableSize() are a part of this guesswork.
// The overrun detector doesn't play nicely on AllocationSize() lookups for pointers that
// don't belong to the heap, so validate that we're in the correct address range before trying
// to look up the size.
return IsInAddressRange(ptr) ? (*m_allocator)->AllocationSize(ptr) : 0;
}
AZ::IAllocator* CGeneralMemoryHeap::GetAllocator() const
{
return m_allocator->Get();
}
@@ -0,0 +1,94 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_GENERALMEMORYHEAP_H
#define CRYINCLUDE_CRYSYSTEM_GENERALMEMORYHEAP_H
#pragma once
#include "IMemory.h"
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/parallel/atomic.h>
#include <AzCore/std/containers/set.h>
class GeneralMemoryHeapAllocator;
class CGeneralMemoryHeap
: public IGeneralMemoryHeap
{
struct Alloc
{
void* m_base;
size_t m_size;
Alloc(void* base = nullptr, size_t size = 0)
: m_base(base)
, m_size(size)
{}
bool operator==(const Alloc& rhs) const
{
// size doesn't matter
return m_base == rhs.m_base;
}
bool operator<(const Alloc& rhs) const
{
// this will cause allocs to be sorted by address
return m_base < rhs.m_base;
}
};
public:
// Create a heap that will map/unmap pages in the range [baseAddress, baseAddress + upperLimit).
CGeneralMemoryHeap(UINT_PTR baseAddress, size_t upperLimit, size_t reserveSize, const char* sUsage);
// Create a heap that will assumes all memory in the range [base, base + size) is already mapped.
CGeneralMemoryHeap(void* base, size_t size, const char* sUsage);
~CGeneralMemoryHeap();
public: // IGeneralMemoryHeap Members
bool Cleanup();
int AddRef();
int Release();
bool IsInAddressRange(void* ptr) const;
void* Calloc(size_t nmemb, size_t size, const char* sUsage = NULL);
void* Malloc(size_t sz, const char* sUsage = NULL);
size_t Free(void* ptr);
void* Realloc(void* ptr, size_t sz, const char* sUsage = NULL);
void* ReallocAlign(void* ptr, size_t size, size_t alignment, const char* sUsage = NULL);
void* Memalign(size_t boundary, size_t size, const char* sUsage = NULL);
size_t UsableSize(void* ptr) const;
AZ::IAllocator* GetAllocator() const override;
private:
CGeneralMemoryHeap(const CGeneralMemoryHeap&) = delete;
CGeneralMemoryHeap& operator = (const CGeneralMemoryHeap&) = delete;
void RecordAlloc(void* ptr, size_t size);
void RecordFree(void* ptr, size_t size);
private:
AZStd::unique_ptr<AZ::AllocatorWrapper<GeneralMemoryHeapAllocator>> m_allocator;
AZStd::atomic_int m_refCount;
void* m_block;
size_t m_blockSize;
AZStd::set<Alloc, AZStd::less<Alloc>, AZ::AZStdAlloc<AZ::LegacyAllocator>> m_allocs;
};
#endif // CRYINCLUDE_CRYSYSTEM_GENERALMEMORYHEAP_H
+102
View File
@@ -0,0 +1,102 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "HMDCVars.h"
#include <HMDBus.h>
#include <sstream>
namespace AZ
{
namespace VR
{
void HMDCVars::OnHMDRecenter([[maybe_unused]] IConsoleCmdArgs* args)
{
EBUS_EVENT(AZ::VR::HMDDeviceRequestBus, RecenterPose);
}
void HMDCVars::OnHMDTrackingLevelChange(IConsoleCmdArgs* args)
{
if (args->GetArgCount() != 2)
{
// First arg should be the command itself, second arg should be the requested tracking level.
return;
}
// Read the new tracking level.
int argVal = 0;
std::stringstream stream(args->GetArg(1));
stream >> argVal;
AZ::VR::HMDTrackingLevel level = static_cast<AZ::VR::HMDTrackingLevel>(argVal);
EBUS_EVENT(AZ::VR::HMDDeviceRequestBus, SetTrackingLevel, level);
}
void HMDCVars::OnOutputToHMDChanged(ICVar* var)
{
// Set the necessary cvars for turning on/off output to an HMD.
ICVar* mode = gEnv->pConsole->GetCVar("r_StereoMode");
ICVar* output = gEnv->pConsole->GetCVar("r_StereoOutput");
ICVar* height = gEnv->pConsole->GetCVar("r_height");
ICVar* width = gEnv->pConsole->GetCVar("r_width");
if (!mode || !output || !height || !width)
{
return;
}
bool enable = (var->GetIVal() == 1);
if (enable)
{
// Auto-set the resolution.
{
const AZ::VR::HMDDeviceInfo* deviceInfo = nullptr;
EBUS_EVENT_RESULT(deviceInfo, AZ::VR::HMDDeviceRequestBus, GetDeviceInfo);
// If the device info exists then there is a VR device connected and working.
if (deviceInfo)
{
mode->Set(EStereoMode::STEREO_MODE_DUAL_RENDERING);
output->Set(EStereoOutput::STEREO_OUTPUT_HMD);
width->Set(static_cast<int>(deviceInfo->renderWidth));
height->Set(static_cast<int>(deviceInfo->renderHeight));
}
}
}
else
{
mode->Set(EStereoMode::STEREO_MODE_NO_STEREO);
output->Set(EStereoOutput::STEREO_OUTPUT_STANDARD);
}
}
void HMDCVars::OnHMDDebugInfo(ICVar* var)
{
bool enable = (var->GetIVal() == 1);
EBUS_EVENT(AZ::VR::HMDDebuggerRequestBus, EnableInfo, enable);
}
void HMDCVars::OnHMDDebugCamera(ICVar* var)
{
bool enable = (var->GetIVal() == 1);
EBUS_EVENT(AZ::VR::HMDDebuggerRequestBus, EnableCamera, enable);
}
int HMDCVars::hmd_social_screen = static_cast<int>(HMDSocialScreen::UndistortedLeftEye);
int HMDCVars::hmd_debug_info = 0;
int HMDCVars::hmd_debug_camera = 0;
} // namespace VR
} // namespace AZ
+71
View File
@@ -0,0 +1,71 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include <IConsole.h>
#include <ISystem.h>
namespace AZ
{
namespace VR
{
class HMDCVars
{
public:
static int hmd_social_screen;
static int hmd_debug_info;
static int hmd_debug_camera;
static void Register()
{
REGISTER_CVAR2("hmd_social_screen", &hmd_social_screen, hmd_social_screen,
VF_NULL, "Selects the social screen mode: \n"
"-1- Off\n"
"0 - Undistorted left eye\n"
"1 - Undistorted right eye\n"
);
REGISTER_INT_CB("hmd_debug_info", 0, VF_ALWAYSONCHANGE,
"Enable/disable HMD and VR controller debug info/rendering",
OnHMDDebugInfo);
REGISTER_INT_CB("hmd_debug_camera", 0, VF_ALWAYSONCHANGE,
"Enable/disable HMD debug camera",
OnHMDDebugCamera);
REGISTER_COMMAND("hmd_tracking_level", &OnHMDTrackingLevelChange,
VF_NULL, "Set the HMD center reference point.\n"
"0 - Camera (Actor's head)\n"
"1 - Actor's feet (floor)\n");
REGISTER_COMMAND("hmd_recenter_pose", &OnHMDRecenter,
VF_NULL, "Re-centers sensor orientation of the HMD.");
REGISTER_INT_CB("output_to_hmd", 0, VF_ALWAYSONCHANGE,
"Enable/disable output to any connected HMD (for VR)",
OnOutputToHMDChanged);
}
private:
static void OnHMDRecenter(IConsoleCmdArgs* args);
static void OnHMDTrackingLevelChange(IConsoleCmdArgs* args);
static void OnOutputToHMDChanged(ICVar* var);
static void OnHMDDebugInfo(ICVar* var);
static void OnHMDDebugCamera(ICVar* var);
};
} // namespace VR
} // namespace AZ
+131
View File
@@ -0,0 +1,131 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "ProjectDefines.h"
#if defined(MAP_LOADING_SLICING)
#include "HandlerBase.h"
const char* SERVER_LOCK_NAME = "SynchronizeGameServer";
const char* CLIENT_LOCK_NAME = "SynchronizeGameClient";
HandlerBase::HandlerBase(const char* bucket, int affinity)
{
m_serverLockName.Format("%s_%s", SERVER_LOCK_NAME, bucket);
m_clientLockName.Format("%s_%s", CLIENT_LOCK_NAME, bucket);
if (affinity != 0)
{
m_affinity = uint32(1) << (affinity - 1);
}
else
{
m_affinity = -1;
}
m_prevAffinity = 0;
}
HandlerBase::~HandlerBase()
{
if (m_prevAffinity)
{
if (SyncSetAffinity(m_prevAffinity))
{
CryLogAlways("Restored affinity to %d", m_prevAffinity);
}
else
{
CryLogAlways("Failed to restore affinity to %d", m_prevAffinity);
}
}
}
void HandlerBase::SetAffinity()
{
if (m_prevAffinity) //already set
{
return;
}
if (uint32 p = SyncSetAffinity(m_affinity))
{
CryLogAlways("Changed affinity to %d", m_affinity);
m_prevAffinity = p;
}
else
{
CryLogAlways("Failed to change affinity to %d", m_affinity);
}
}
#if defined(LINUX)
uint32 HandlerBase::SyncSetAffinity(uint32 cpuMask)//put -1
{
if (cpuMask != 0)
{
cpu_set_t cpuSet;
uint32 affinity = 0;
if (!sched_getaffinity(getpid(), sizeof cpuSet, &cpuSet))
{
for (int cpu = 0; cpu < sizeof(cpuMask) * 8; ++cpu)
{
if (CPU_ISSET(cpu, &cpuSet))
{
affinity |= 1 << cpu;
}
}
}
if (affinity)
{
CPU_ZERO(&cpuSet);
for (int cpu = 0; cpu < sizeof(cpuMask) * 8; ++cpu)
{
if (cpuMask & (1 << cpu))
{
CPU_SET(cpu, &cpuSet);
}
}
if (!sched_setaffinity(getpid(), sizeof(cpuSet), &cpuSet))
{
return affinity;
}
}
}
return 0;
}
#elif AZ_LEGACY_CRYSYSTEM_TRAIT_USE_HANDLER_SYNC_AFFINITY
uint32 HandlerBase::SyncSetAffinity(uint32 cpuMask)//put -1
{
uint32 p = (uint32)SetThreadAffinityMask(GetCurrentThread(), cpuMask);
if (p == 0)
{
CryLogAlways("Error updating affinity mask to %d", cpuMask);
}
return p;
}
#else
uint32 HandlerBase::SyncSetAffinity(uint32 cpuMask)//put -1
{
CryLogAlways("Updating thread affinity not supported on this platform");
return 0;
}
#endif
#endif // defined(MAP_LOADING_SLICING)
+35
View File
@@ -0,0 +1,35 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_HANDLERBASE_H
#define CRYINCLUDE_CRYSYSTEM_HANDLERBASE_H
#pragma once
const int MAX_CLIENTS_NUM = 100;
struct HandlerBase
{
HandlerBase(const char* bucket, int affinity);
~HandlerBase();
void SetAffinity();
uint32 SyncSetAffinity(uint32 cpuMask);
string m_serverLockName;
string m_clientLockName;
uint32 m_affinity;
uint32 m_prevAffinity;
};
#endif // CRYINCLUDE_CRYSYSTEM_HANDLERBASE_H
+467
View File
@@ -0,0 +1,467 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "Huffman.h"
void HuffmanCoder::BitStreamBuilder::AddBits(uint32 value, uint32 numBits)
{
if (numBits > 24)
{
AddBits(static_cast<uint8>((value >> 24) & 0x000000ff), numBits - 24);
numBits = 24;
}
if (numBits > 16)
{
AddBits(static_cast<uint8>((value >> 16) & 0x000000ff), numBits - 16);
numBits = 16;
}
if (numBits > 8)
{
AddBits(static_cast<uint8>((value >> 8) & 0x000000ff), numBits - 8);
numBits = 8;
}
AddBits(static_cast<uint8>(value & 0x000000ff), numBits);
}
void HuffmanCoder::BitStreamBuilder::AddBits(uint8 value, uint32 numBits)
{
if (m_mode != eM_WRITE)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Trying to write to a read only BitStreamBuilder");
return;
}
uint8 mask;
mask = (uint8)(1 << (numBits - 1));
while (mask != 0)
{
//CryLogAlways("mask is %u", mask);
if (mask & value)
{
//CryLogAlways("Buffer value was %u", *m_pBufferCursor.ptr);
*(m_pBufferCursor.ptr) |= m_mask;
//CryLogAlways("Buffer value now %u", *m_pBufferCursor.ptr);
}
//CryLogAlways("m_mask was %u", m_mask);
m_mask = m_mask >> 1;
//CryLogAlways("m_mask now %u", m_mask);
if (m_mask == 0)
{
if (m_pBufferCursor.ptr == m_pBufferEnd.ptr)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Bit Stream has consumed the last byte of the buffer and is requesting another. This stream will be truncated here.");
return;
}
//CryLogAlways("Buffer cursor was %u (%p)", *m_pBufferCursor.ptr, m_pBufferCursor.ptr);
m_pBufferCursor.ptr++;
//CryLogAlways("Buffer cursor now %u (%p)", *m_pBufferCursor.ptr, m_pBufferCursor.ptr);
m_mask = 0x80;
}
mask = mask >> 1L;
}
}
//Returns 1 or 0 for valid values. Returns 2 if the buffer has run out or is the wrong type of builder.
uint8 HuffmanCoder::BitStreamBuilder::GetBit()
{
if (m_mode != eM_READ)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Trying to read from a write only BitStreamBuilder");
return 2;
}
uint8 value = 0;
if (m_mask == 0)
{
if (m_pBufferCursor.const_ptr == m_pBufferEnd.const_ptr)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Bit Stream has consumed the last byte of the buffer and is requesting another. This stream will be truncated here.");
return 2;
}
//CryLogAlways("Buffer cursor was %u (%p)", *m_pBufferCursor.const_ptr, m_pBufferCursor.const_ptr);
m_pBufferCursor.const_ptr++;
//CryLogAlways("Buffer cursor now %u (%p)", *m_pBufferCursor.const_ptr, m_pBufferCursor.const_ptr);
m_mask = 0x80;
}
if (m_mask & *(m_pBufferCursor.const_ptr))
{
value = 1;
}
//CryLogAlways("m_mask was %u", m_mask);
m_mask = m_mask >> 1;
//CryLogAlways("m_mask now %u", m_mask);
return value;
}
void HuffmanCoder::Init()
{
SAFE_DELETE_ARRAY(m_TreeNodes);
SAFE_DELETE_ARRAY(m_Codes);
m_Counts = new uint32[MAX_NUM_SYMBOLS];
memset(m_Counts, 0, sizeof(uint32) * MAX_NUM_SYMBOLS);
m_State = eHCS_OPEN;
}
//Adds the values of an array of chars to the counts
void HuffmanCoder::Update(const uint8* const pSource, const size_t numBytes)
{
if (m_State != eHCS_OPEN)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Trying to update a Huffman Coder that has not been initialized, or has been finalized");
return;
}
size_t i;
for (i = 0; i < numBytes; i++)
{
const int symbol = pSource[i];
m_Counts[symbol]++;
}
}
void HuffmanCoder::Finalize()
{
if (m_State != eHCS_OPEN)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Trying to finalize a Huffman Coder that has not been initialized, or has been finalized");
return;
}
//Construct the tree
m_TreeNodes = new HuffmanTreeNode[MAX_NUM_NODES];
memset(m_TreeNodes, 0, sizeof(HuffmanTreeNode) * MAX_NUM_NODES);
m_Codes = new HuffmanSymbolCode[MAX_NUM_CODES];
memset(m_Codes, 0, sizeof(HuffmanSymbolCode) * MAX_NUM_CODES);
ScaleCountsAndUpdateNodes();
m_RootNode = BuildTree();
ConvertTreeToCode(m_TreeNodes, m_Codes, 0, 0, m_RootNode);
//Finalize the coder so that it won't accept any more strings
m_State = eHCS_FINAL;
//Counts are no longer needed
SAFE_DELETE_ARRAY(m_Counts);
}
void HuffmanCoder::CompressInput(const uint8* const pInput, const size_t numBytes, uint8* const pOutput, size_t* const outputSize)
{
BitStreamBuilder streamBuilder(pOutput, pOutput + (*outputSize));
for (size_t i = 0; i < numBytes; i++)
{
const int symbol = pInput[i];
const uint32 value = m_Codes[symbol].value;
const uint32 numBits = m_Codes[symbol].numBits;
/*char szBits[33];
memset(szBits, '0', 33);
for( uint32 j = 0; j < numBits; j++ )
{
if( (value & (uint32)(1<<j)) != 0 )
{
szBits[31-j] = '1';
}
else
{
szBits[31-j] = '0';
}
}
szBits[32] = 0;
CryLogAlways("%c - %s (%u)", value, szBits, numBits);*/
streamBuilder.AddBits(value, numBits);
}
streamBuilder.AddBits(m_Codes[END_OF_STREAM].value, m_Codes[END_OF_STREAM].numBits);
*outputSize = (streamBuilder.m_pBufferCursor.ptr - streamBuilder.m_pBufferStart.ptr) + 1;
}
size_t HuffmanCoder::UncompressInput(const uint8* const pInput, const size_t numBytes, uint8* const pOutput, const size_t maxOutputSize)
{
size_t numOutputBytes = 0;
BitStreamBuilder streamBuilder(pInput, pInput + numBytes);
while (1)
{
int code;
int node = m_RootNode;
do
{
uint8 bitValue = streamBuilder.GetBit();
#if 0
CryLogAlways("bit=%ld\n", bitValue);
#endif
if (bitValue == 0)
{
node = m_TreeNodes[node].child0;
}
else
{
node = m_TreeNodes[node].child1;
}
} while (node > END_OF_STREAM);
if (node == END_OF_STREAM)
{
pOutput[numOutputBytes] = '\0';
break;
}
code = node;
#if 0
{
CryLogAlways("%c", code);
if (code == '\0')
{
CryLogAlways("EOM");
}
}
#endif
pOutput[numOutputBytes] = (char)code;
numOutputBytes++;
if (numOutputBytes >= maxOutputSize)
{
pOutput[maxOutputSize - 1] = '\0';
break;
}
}
return numOutputBytes;
}
//Private functions
void HuffmanCoder::ScaleCountsAndUpdateNodes()
{
unsigned long maxCount = 0;
int i;
for (i = 0; i < MAX_NUM_SYMBOLS; i++)
{
const unsigned long count = m_Counts[i];
if (count > maxCount)
{
maxCount = count;
}
}
if (maxCount == 0)
{
m_Counts[0] = 1;
maxCount = 1;
}
maxCount = maxCount / MAX_NUM_SYMBOLS;
maxCount = maxCount + 1;
for (i = 0; i < MAX_NUM_SYMBOLS; i++)
{
const unsigned long count = m_Counts[i];
unsigned int scaledCount = (unsigned int)(count / maxCount);
if ((scaledCount == 0) && (count != 0))
{
scaledCount = 1;
}
m_TreeNodes[i].count = scaledCount;
m_TreeNodes[i].child0 = END_OF_STREAM;
m_TreeNodes[i].child1 = END_OF_STREAM;
}
m_TreeNodes[END_OF_STREAM].count = 1;
m_TreeNodes[END_OF_STREAM].child0 = END_OF_STREAM;
m_TreeNodes[END_OF_STREAM].child1 = END_OF_STREAM;
}
//Jake's file IO code. Kept in case we make the compression and table generation an offline task
/* Format is: startSymbol, stopSymbol, count0, count1, count2, ... countN, ..., 0 */
/* When finding the start, stop symbols only break out if find more than 3 0's in the counts */
/*static void outputCounts(FILE* const pFile, const HuffmanTreeNode* const pNodes)
{
int first = 0;
int last;
int next;
while ((first < MAX_NUM_SYMBOLS) && (pNodes[first].count == 0))
{
first++;
}
last = first;
next = first;
for (; first < MAX_NUM_SYMBOLS; first = next)
{
int i;
last = first+1;
while (1)
{
for (; last < MAX_NUM_SYMBOLS; last++)
{
if (pNodes[last].count == 0)
{
break;
}
}
last--;
for (next = last+1; next < MAX_NUM_SYMBOLS; next++)
{
if (pNodes[next].count != 0)
{
break;
}
}
if (next == MAX_NUM_SYMBOLS)
{
break;
}
if ((next-last) > 3)
{
break;
}
last = next;
}
putc(first, pFile);
putc(last, pFile);
for (i = first; i <= last; i++)
{
const unsigned int count = pNodes[i].count;
putc((int)count, pFile);
}
}
putc(0xFF, pFile);
putc(0xFF, pFile);
putc((int)(pNodes[0xFF].count), pFile);
}*/
/*static void inputCounts(FILE* const pFile, HuffmanTreeNode* const pNodes)
{
while (1)
{
int i;
const int first = getc(pFile);
const int last = getc(pFile);
for (i = first; i <= last; i++)
{
const int count = getc(pFile);
pNodes[i].count = (size_t)count;
pNodes[i].child0 = END_OF_STREAM;
pNodes[i].child1 = END_OF_STREAM;
}
if ((first == last) && (first == 0xFF))
{
break;
}
}
pNodes[END_OF_STREAM].count = 1;
pNodes[END_OF_STREAM].child0 = END_OF_STREAM;
pNodes[END_OF_STREAM].child1 = END_OF_STREAM;
}*/
int HuffmanCoder::BuildTree()
{
int min1;
int min2;
int nextFree;
m_TreeNodes[MAX_NODE].count = 0xFFFFFFF;
for (nextFree = END_OF_STREAM + 1;; nextFree++)
{
int i;
min1 = MAX_NODE;
min2 = MAX_NODE;
for (i = 0; i < nextFree; i++)
{
const unsigned int count = m_TreeNodes[i].count;
if (count != 0)
{
if (count < m_TreeNodes[min1].count)
{
min2 = min1;
min1 = i;
}
else if (count < m_TreeNodes[min2].count)
{
min2 = i;
}
}
}
if (min2 == MAX_NODE)
{
break;
}
m_TreeNodes[nextFree].count = m_TreeNodes[min1].count + m_TreeNodes[min2].count;
m_TreeNodes[min1].savedCount = m_TreeNodes[min1].count;
m_TreeNodes[min1].count = 0;
m_TreeNodes[min2].savedCount = m_TreeNodes[min2].count;
m_TreeNodes[min2].count = 0;
m_TreeNodes[nextFree].child0 = min1;
m_TreeNodes[nextFree].child1 = min2;
m_TreeNodes[nextFree].savedCount = 0;
}
nextFree--;
m_TreeNodes[nextFree].savedCount = m_TreeNodes[nextFree].count;
return nextFree;
}
void HuffmanCoder::ConvertTreeToCode(const HuffmanTreeNode* const pNodes, HuffmanSymbolCode* const pCodes,
const unsigned int value, const unsigned int numBits, const int node)
{
unsigned int nextValue;
unsigned int nextNumBits;
if (node <= END_OF_STREAM)
{
pCodes[node].value = value;
pCodes[node].numBits = numBits;
return;
}
nextValue = value << 1;
nextNumBits = numBits + 1;
ConvertTreeToCode(pNodes, pCodes, nextValue, nextNumBits, pNodes[node].child0);
nextValue = nextValue | 0x1;
ConvertTreeToCode(pNodes, pCodes, nextValue, nextNumBits, pNodes[node].child1);
}
/*static void printChar(const int c)
{
if (c >= ' ' && c < 127)
{
printf("'%c'", c);
}
else
{
printf("0x%03X", c);
}
}
static void printModel(const HuffmanTreeNode* const pNodes, const HuffmanSymbolCode* const pCodes)
{
int i;
for (i = 0; i < MAX_NODE; i++)
{
const unsigned int count = pNodes[i].savedCount;
if (count != 0)
{
printf("node=");
printChar(i);
printf(" count=%3d", count);
printf(" child0=");
printChar(pNodes[i].child0);
printf(" child1=");
printChar(pNodes[i].child1);
if (pCodes && (i <= END_OF_STREAM))
{
printf(" Huffman code=");
binaryFilePrint(stdout, pCodes[i].value, pCodes[i].numBits);
}
printf("\n");
}
}
}*/
+153
View File
@@ -0,0 +1,153 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_HUFFMAN_H
#define CRYINCLUDE_CRYSYSTEM_HUFFMAN_H
#pragma once
class HuffmanCoder
{
private:
struct HuffmanTreeNode
{
uint32 count;
uint32 savedCount;
int child0;
int child1;
};
struct HuffmanSymbolCode
{
uint32 value;
uint32 numBits;
};
struct BitStreamBuilder
{
enum EModes
{
eM_WRITE,
eM_READ
};
union buf_ptr
{
uint8* ptr;
const uint8* const_ptr;
};
EModes m_mode;
uint8 m_mask;
buf_ptr m_pBufferStart;
buf_ptr m_pBufferCursor;
buf_ptr m_pBufferEnd; //Pointer to the last byte in the buffer
BitStreamBuilder(uint8* pBufferStart, uint8* pBufferEnd)
: m_mode(eM_WRITE)
, m_mask(0x80)
{
m_pBufferStart.ptr = pBufferStart;
m_pBufferCursor.ptr = pBufferStart;
m_pBufferEnd.ptr = pBufferEnd;
}
BitStreamBuilder(const uint8* pBufferStart, const uint8* pBufferEnd)
: m_mode(eM_READ)
, m_mask(0x80)
{
m_pBufferStart.const_ptr = pBufferStart;
m_pBufferCursor.const_ptr = pBufferStart;
m_pBufferEnd.const_ptr = pBufferEnd;
}
void AddBits(uint32 value, uint32 numBits);
void AddBits(uint8 value, uint32 numBits);
//Returns 1 or 0 for valid values. Returns 2 if the buffer has run out or is the wrong type of builder.
uint8 GetBit();
};
const static int MAX_SYMBOL_VALUE = (255);
const static int MAX_NUM_SYMBOLS = (MAX_SYMBOL_VALUE + 1);
const static int END_OF_STREAM = (MAX_NUM_SYMBOLS);
const static int MAX_NUM_CODES = (MAX_NUM_SYMBOLS + 1);
const static int MAX_NUM_NODES = (MAX_NUM_CODES * 2);
const static int MAX_NODE = (MAX_NUM_NODES - 1);
enum EHuffmanCoderState
{
eHCS_NEW, //Has been created, Init not called
eHCS_OPEN, //Init has been called, tree not yet constructed. Can accept new data.
eHCS_FINAL //Finalize has been called. Can no longer accept data, but can encode/decode.
};
HuffmanTreeNode* m_TreeNodes;
HuffmanSymbolCode* m_Codes;
uint32* m_Counts;
int m_RootNode;
uint32 m_RefCount;
EHuffmanCoderState m_State;
public:
HuffmanCoder()
: m_TreeNodes(NULL)
, m_Codes(NULL)
, m_Counts(NULL)
, m_State(eHCS_NEW)
, m_RootNode(0)
, m_RefCount(0) {}
~HuffmanCoder()
{
SAFE_DELETE_ARRAY(m_TreeNodes);
SAFE_DELETE_ARRAY(m_Codes);
SAFE_DELETE_ARRAY(m_Counts);
}
//A bit like an MD5 generator, has three phases.
//Clears the existing data
void Init();
//Adds the values of an array of chars to the counts
void Update(const uint8* const pSource, const size_t numBytes);
//Construct the coding tree using the counts
void Finalize();
//We typically create a Huffman Coder per localized string table loaded. Since we can and do unload strings at runtime, it's useful to keep a ref count of each coder.
inline void AddRef() { m_RefCount++; }
inline void DecRef() { m_RefCount = m_RefCount > 0 ? m_RefCount - 1 : 0; }
inline uint32 RefCount() { return m_RefCount; }
void CompressInput(const uint8* const pInput, const size_t numBytes, uint8* const pOutput, size_t* const outputSize);
size_t UncompressInput(const uint8* const pInput, const size_t numBytes, uint8* const pOutput, const size_t maxOutputSize);
void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
if (m_Counts != NULL)
{
pSizer->AddObject(m_Counts, sizeof(uint32), MAX_NUM_SYMBOLS);
}
if (m_TreeNodes != NULL)
{
pSizer->AddObject(m_TreeNodes, sizeof(HuffmanTreeNode), MAX_NUM_NODES);
}
if (m_Codes != NULL)
{
pSizer->AddObject(m_Codes, sizeof(HuffmanSymbolCode), MAX_NUM_CODES);
}
}
private:
void ScaleCountsAndUpdateNodes();
int BuildTree();
void ConvertTreeToCode(const HuffmanTreeNode* const pNodes, HuffmanSymbolCode* const pCodes, const unsigned int value, const unsigned int numBits, const int node);
};
#endif // CRYINCLUDE_CRYSYSTEM_HUFFMAN_H
@@ -0,0 +1,304 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : A multiplatform base class for handling errors and collecting call stacks
#include "CrySystem_precompiled.h"
#include "IDebugCallStack.h"
#include <Pak/CryPakUtils.h>
#include "System.h"
#include <AzFramework/IO/FileOperations.h>
#include <AzCore/NativeUI/NativeUIRequests.h>
#include <AzFramework/StringFunc/StringFunc.h>
//#if !defined(LINUX)
#include <ISystem.h>
const char* const IDebugCallStack::s_szFatalErrorCode = "FATAL_ERROR";
IDebugCallStack::IDebugCallStack()
: m_bIsFatalError(false)
, m_postBackupProcess(0)
, m_memAllocFileHandle(AZ::IO::InvalidHandle)
{
}
IDebugCallStack::~IDebugCallStack()
{
StopMemLog();
}
#if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_SINGLETON
IDebugCallStack* IDebugCallStack::instance()
{
static IDebugCallStack sInstance;
return &sInstance;
}
#endif
void IDebugCallStack::FileCreationCallback(void (* postBackupProcess)())
{
m_postBackupProcess = postBackupProcess;
}
//////////////////////////////////////////////////////////////////////////
void IDebugCallStack::LogCallstack()
{
AZ::Debug::Trace::PrintCallstack("", 2);
}
const char* IDebugCallStack::TranslateExceptionCode(DWORD dwExcept)
{
switch (dwExcept)
{
#if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_TRANSLATE
case EXCEPTION_ACCESS_VIOLATION:
return "EXCEPTION_ACCESS_VIOLATION";
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
return "EXCEPTION_DATATYPE_MISALIGNMENT";
break;
case EXCEPTION_BREAKPOINT:
return "EXCEPTION_BREAKPOINT";
break;
case EXCEPTION_SINGLE_STEP:
return "EXCEPTION_SINGLE_STEP";
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
return "EXCEPTION_FLT_DENORMAL_OPERAND";
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
break;
case EXCEPTION_FLT_INEXACT_RESULT:
return "EXCEPTION_FLT_INEXACT_RESULT";
break;
case EXCEPTION_FLT_INVALID_OPERATION:
return "EXCEPTION_FLT_INVALID_OPERATION";
break;
case EXCEPTION_FLT_OVERFLOW:
return "EXCEPTION_FLT_OVERFLOW";
break;
case EXCEPTION_FLT_STACK_CHECK:
return "EXCEPTION_FLT_STACK_CHECK";
break;
case EXCEPTION_FLT_UNDERFLOW:
return "EXCEPTION_FLT_UNDERFLOW";
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
return "EXCEPTION_INT_DIVIDE_BY_ZERO";
break;
case EXCEPTION_INT_OVERFLOW:
return "EXCEPTION_INT_OVERFLOW";
break;
case EXCEPTION_PRIV_INSTRUCTION:
return "EXCEPTION_PRIV_INSTRUCTION";
break;
case EXCEPTION_IN_PAGE_ERROR:
return "EXCEPTION_IN_PAGE_ERROR";
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
return "EXCEPTION_ILLEGAL_INSTRUCTION";
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
break;
case EXCEPTION_STACK_OVERFLOW:
return "EXCEPTION_STACK_OVERFLOW";
break;
case EXCEPTION_INVALID_DISPOSITION:
return "EXCEPTION_INVALID_DISPOSITION";
break;
case EXCEPTION_GUARD_PAGE:
return "EXCEPTION_GUARD_PAGE";
break;
case EXCEPTION_INVALID_HANDLE:
return "EXCEPTION_INVALID_HANDLE";
break;
//case EXCEPTION_POSSIBLE_DEADLOCK: return "EXCEPTION_POSSIBLE_DEADLOCK"; break ;
case STATUS_FLOAT_MULTIPLE_FAULTS:
return "STATUS_FLOAT_MULTIPLE_FAULTS";
break;
case STATUS_FLOAT_MULTIPLE_TRAPS:
return "STATUS_FLOAT_MULTIPLE_TRAPS";
break;
#endif
default:
return "Unknown";
break;
}
}
void IDebugCallStack::PutVersion(char* str, size_t length)
{
AZ_PUSH_DISABLE_WARNING(4996, "-Wunknown-warning-option")
if (!gEnv || !gEnv->pSystem)
{
return;
}
char sFileVersion[128];
gEnv->pSystem->GetFileVersion().ToString(sFileVersion, sizeof(sFileVersion));
char sProductVersion[128];
gEnv->pSystem->GetProductVersion().ToString(sProductVersion, sizeof(sFileVersion));
//! Get time.
time_t ltime;
time(&ltime);
tm* today = localtime(&ltime);
char s[1024];
//! Use strftime to build a customized time string.
strftime(s, 128, "Logged at %#c\n", today);
azstrcat(str, length, s);
sprintf_s(s, "FileVersion: %s\n", sFileVersion);
azstrcat(str, length, s);
sprintf_s(s, "ProductVersion: %s\n", sProductVersion);
azstrcat(str, length, s);
if (gEnv->pLog)
{
const char* logfile = gEnv->pLog->GetFileName();
if (logfile)
{
sprintf (s, "LogFile: %s\n", logfile);
azstrcat(str, length, s);
}
}
if (gEnv->pConsole)
{
if (ICVar* pCVarGameDir = gEnv->pConsole->GetCVar("sys_game_folder"))
{
sprintf(s, "GameDir: %s\n", pCVarGameDir->GetString());
azstrcat(str, length, s);
}
}
#if AZ_LEGACY_CRYSYSTEM_TRAIT_DEBUGCALLSTACK_APPEND_MODULENAME
GetModuleFileNameA(NULL, s, sizeof(s));
// Log EXE filename only if possible (not full EXE path which could contain sensitive info)
AZStd::string exeName;
if (AzFramework::StringFunc::Path::GetFullFileName(s, exeName))
{
azstrcat(str, length, "Executable: ");
azstrcat(str, length, exeName.c_str());
# ifdef AZ_DEBUG_BUILD
azstrcat(str, length, " (debug: yes");
# else
azstrcat(str, length, " (debug: no");
# endif
}
#endif
AZ_POP_DISABLE_WARNING
}
//Crash the application, in this way the debug callstack routine will be called and it will create all the necessary files (error.log, dump, and eventually screenshot)
void IDebugCallStack::FatalError(const char* description)
{
m_bIsFatalError = true;
WriteLineToLog(description);
#ifndef _RELEASE
bool bShowDebugScreen = g_cvars.sys_no_crash_dialog == 0;
// showing the debug screen is not safe when not called from mainthread
// it normally leads to a infinity recursion followed by a stack overflow, preventing
// useful call stacks, thus they are disabled
bShowDebugScreen = bShowDebugScreen && gEnv->mMainThreadId == CryGetCurrentThreadId();
if (bShowDebugScreen)
{
EBUS_EVENT(AZ::NativeUI::NativeUIRequestBus, DisplayOkDialog, "Lumberyard Fatal Error", description, false);
}
#endif
#if defined(WIN32) || !defined(_RELEASE)
int* p = 0x0;
PREFAST_SUPPRESS_WARNING(6011) * p = 1; // we're intentionally crashing here
#endif
}
void IDebugCallStack::WriteLineToLog(const char* format, ...)
{
CDebugAllowFileAccess allowFileAccess;
va_list ArgList;
char szBuffer[MAX_WARNING_LENGTH];
va_start(ArgList, format);
int count = vsnprintf_s(szBuffer, sizeof(szBuffer), sizeof(szBuffer) - 1, format, ArgList);
cry_strcat(szBuffer, "\n");
szBuffer[sizeof(szBuffer) - 1] = '\0';
va_end(ArgList);
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\error.log", AZ::IO::GetOpenModeFromStringMode("a+t"), fileHandle);
if (fileHandle != AZ::IO::InvalidHandle)
{
AZ::IO::FileIOBase::GetDirectInstance()->Write(fileHandle, szBuffer, strlen(szBuffer));
AZ::IO::FileIOBase::GetDirectInstance()->Flush(fileHandle);
AZ::IO::FileIOBase::GetDirectInstance()->Close(fileHandle);
}
}
void IDebugCallStack::Screenshot(const char* szFileName)
{
WriteLineToLog("Attempting to create error screenshot \"%s\"", szFileName);
static int g_numScreenshots = 0;
if (gEnv->pRenderer && !g_numScreenshots++)
{
if (gEnv->pRenderer->ScreenShot(szFileName))
{
WriteLineToLog("Successfully created screenshot.");
}
else
{
WriteLineToLog("Error creating screenshot.");
}
}
else
{
WriteLineToLog("Ignoring multiple calls to Screenshot");
}
}
//////////////////////////////////////////////////////////////////////////
void IDebugCallStack::StartMemLog()
{
AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\memallocfile.log", AZ::IO::OpenMode::ModeWrite, m_memAllocFileHandle);
assert(m_memAllocFileHandle != AZ::IO::InvalidHandle);
}
//////////////////////////////////////////////////////////////////////////
void IDebugCallStack::StopMemLog()
{
if (m_memAllocFileHandle != AZ::IO::InvalidHandle)
{
AZ::IO::FileIOBase::GetDirectInstance()->Close(m_memAllocFileHandle);
m_memAllocFileHandle = AZ::IO::InvalidHandle;
}
}
//#endif //!defined(LINUX)
@@ -0,0 +1,92 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : A multiplatform base class for handling errors and collecting call stacks
#ifndef CRYINCLUDE_CRYSYSTEM_IDEBUGCALLSTACK_H
#define CRYINCLUDE_CRYSYSTEM_IDEBUGCALLSTACK_H
#pragma once
#include "System.h"
#if AZ_LEGACY_CRYSYSTEM_TRAIT_FORWARD_EXCEPTION_POINTERS
struct EXCEPTION_POINTERS;
#endif
//! Limits the maximal number of functions in call stack.
enum
{
MAX_DEBUG_STACK_ENTRIES = 80
};
class IDebugCallStack
{
public:
// Returns single instance of DebugStack
static IDebugCallStack* instance();
virtual int handleException([[maybe_unused]] EXCEPTION_POINTERS* exception_pointer){return 0; }
// returns the module name of a given address
virtual string GetModuleNameForAddr([[maybe_unused]] void* addr) { return "[unknown]"; }
// returns the function name of a given address together with source file and line number (if available) of a given address
virtual void GetProcNameForAddr(void* addr, string& procName, void*& baseAddr, string& filename, int& line)
{
filename = "[unknown]";
line = 0;
baseAddr = addr;
#if defined(PLATFORM_64BIT)
procName.Format("[%016llX]", addr);
#else
procName.Format("[%08X]", addr);
#endif
}
// returns current filename
virtual string GetCurrentFilename() { return "[unknown]"; }
//! Dumps Current Call Stack to log.
virtual void LogCallstack();
//triggers a fatal error, so the DebugCallstack can create the error.log and terminate the application
void FatalError(const char*);
//Reports a bug and continues execution
virtual void ReportBug(const char*) {}
virtual void FileCreationCallback(void (* postBackupProcess)());
static void WriteLineToLog(const char* format, ...);
virtual void StartMemLog();
virtual void StopMemLog();
protected:
IDebugCallStack();
virtual ~IDebugCallStack();
static const char* TranslateExceptionCode(DWORD dwExcept);
static void PutVersion(char* str, size_t length);
static void Screenshot(const char* szFileName);
bool m_bIsFatalError;
static const char* const s_szFatalErrorCode;
void (* m_postBackupProcess)();
AZ::IO::HandleType m_memAllocFileHandle;
};
#endif // CRYINCLUDE_CRYSYSTEM_IDEBUGCALLSTACK_H
+63
View File
@@ -0,0 +1,63 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Console implementation for iOS, reports back to the main interface
#ifndef CRYINCLUDE_CRYSYSTEM_IOSCONSOLE_H
#define CRYINCLUDE_CRYSYSTEM_IOSCONSOLE_H
#pragma once
#include <IConsole.h>
#include <ITextModeConsole.h>
#include <INetwork.h>
class CIOSConsole
: public ISystemUserCallback
, public IOutputPrintSink
, public ITextModeConsole
{
CIOSConsole(const CIOSConsole&);
CIOSConsole& operator = (const CIOSConsole&);
bool m_isInitialized;
public:
static CryCriticalSectionNonRecursive s_lock;
public:
CIOSConsole();
~CIOSConsole();
// Interface IOutputPrintSink /////////////////////////////////////////////
DLL_EXPORT virtual void Print(const char* line);
// Interface ISystemUserCallback //////////////////////////////////////////
virtual bool OnError(const char* errorString);
virtual bool OnSaveDocument() { return false; }
virtual void OnProcessSwitch() { }
virtual void OnInitProgress(const char* sProgressMsg);
virtual void OnInit(ISystem*);
virtual void OnShutdown();
virtual void OnUpdate();
virtual void GetMemoryUsage(ICrySizer* pSizer);
void SetRequireDedicatedServer(bool) {}
void SetHeader(const char*) {}
// Interface ITextModeConsole /////////////////////////////////////////////
virtual Vec2_tpl<int> BeginDraw();
virtual void PutText(int x, int y, const char* msg);
virtual void EndDraw();
};
#endif // CRYINCLUDE_CRYSYSTEM_IOSCONSOLE_H
+94
View File
@@ -0,0 +1,94 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#if defined(IOS)
#include "IOSConsole.h"
CIOSConsole::CIOSConsole():
m_isInitialized(false)
{
}
CIOSConsole::~CIOSConsole()
{
}
// Interface IOutputPrintSink /////////////////////////////////////////////
void CIOSConsole::Print(const char *line)
{
printf("MSG: %s\n", line);
}
// Interface ISystemUserCallback //////////////////////////////////////////
bool CIOSConsole::OnError(const char *errorString)
{
printf("ERR: %s\n", errorString);
return true;
}
void CIOSConsole::OnInitProgress(const char *sProgressMsg)
{
(void) sProgressMsg;
// Do Nothing
}
void CIOSConsole::OnInit(ISystem *pSystem)
{
if (!m_isInitialized)
{
IConsole* pConsole = pSystem->GetIConsole();
if (pConsole != 0)
{
pConsole->AddOutputPrintSink(this);
}
m_isInitialized = true;
}
}
void CIOSConsole::OnShutdown()
{
if (m_isInitialized)
{
// remove outputprintsink
m_isInitialized = false;
}
}
void CIOSConsole::OnUpdate()
{
// Do Nothing
}
void CIOSConsole::GetMemoryUsage(ICrySizer *pSizer)
{
size_t size = sizeof(*this);
pSizer->AddObject(this, size);
}
// Interface ITextModeConsole /////////////////////////////////////////////
Vec2_tpl<int> CIOSConsole::BeginDraw()
{
return Vec2_tpl<int>(0,0);
}
void CIOSConsole::PutText( int x, int y, const char * msg )
{
printf("PUT: %s\n", msg);
}
void CIOSConsole::EndDraw() {
// Do Nothing
}
#endif // IOS
@@ -0,0 +1,59 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
struct SThreadConfig
{
enum eThreadParamFlag
{
eThreadParamFlag_ThreadName = BIT(0),
eThreadParamFlag_StackSize = BIT(1),
eThreadParamFlag_Affinity = BIT(2),
eThreadParamFlag_Priority = BIT(3),
eThreadParamFlag_PriorityBoost = BIT(4),
};
typedef uint32 TThreadParamFlag;
const char* szThreadName;
uint32 stackSizeBytes;
uint32 affinityFlag;
int32 priority;
bool bDisablePriorityBoost;
TThreadParamFlag paramActivityFlag;
};
class IThreadConfigManager
{
public:
virtual ~IThreadConfigManager()
{
}
//! Called once during System startup.
//! Loads the thread configuration for the executing platform from file.
virtual bool LoadConfig(const char* pcPath) = 0;
//! Returns true if a config has been loaded.
virtual bool ConfigLoaded() const = 0;
//! Gets the thread configuration for the specified thread on the active platform.
//! If no matching config is found a default configuration is returned (which does not have the same name as the search string).
virtual const SThreadConfig* GetThreadConfig(const char* sThreadName, ...) = 0;
virtual const SThreadConfig* GetDefaultThreadConfig() const = 0;
//! Dump a detailed description of the thread startup configurations for this platform to the log file.
virtual void DumpThreadConfigurationsToLog() = 0;
};
+280
View File
@@ -0,0 +1,280 @@
/*
* 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 "CrySystem_precompiled.h"
#include <AzCore/IO/SystemFile.h>
#include "ImageHandler.h"
#include <numeric>
#include "ScopeGuard.h"
#include "Algorithm.h"
#include "System.h"
#if defined(AZ_RESTRICTED_PLATFORM)
#include AZ_RESTRICTED_FILE(ImageHandler_cpp)
#endif
#if !(defined(ANDROID) || defined(IOS) || defined(LINUX)) && AZ_LEGACY_CRYSYSTEM_TRAIT_IMAGEHANDLER_TIFFIO // Rally US1050 - Compile libtiff for Android and IOS
#include <libtiff/tiffio.h>
static_assert(sizeof(thandle_t) >= sizeof(AZ::IO::HandleType), "Platform defines thandle_t to be smaller than required");
#endif
namespace
{
class Image
: public IImageHandler::IImage
{
public:
Image(std::vector<unsigned char>&& data, int width, int height)
{
CRY_ASSERT(data.size() == width * height * ImageHandler::c_BytesPerPixel);
m_data = std::move(data);
m_width = width;
m_height = height;
}
private:
virtual const std::vector<unsigned char>& GetData() const override { return m_data; }
virtual int GetWidth() const override { return m_width; }
virtual int GetHeight() const override { return m_height; }
unsigned int m_width;
unsigned int m_height;
std::vector<unsigned char> m_data;
};
#if AZ_LEGACY_CRYSYSTEM_TRAIT_IMAGEHANDLER_TIFFIO
struct TiffIO
{
static tsize_t Read(thandle_t handle, tdata_t buffer, tsize_t size)
{
AZ::u64 bytesRead = 0;
AZ::IO::FileIOBase::GetDirectInstance()->Read(static_cast<AZ::IO::HandleType>(reinterpret_cast<AZ::u64>(handle)), buffer, size, false, &bytesRead);
return static_cast<tsize_t>(bytesRead);
};
static tsize_t Write(thandle_t handle, tdata_t buffer, tsize_t size)
{
AZ::u64 sizeWritten;
if (AZ::IO::FileIOBase::GetDirectInstance()->Write(static_cast<AZ::IO::HandleType>(reinterpret_cast<AZ::u64>(handle)), buffer, size, &sizeWritten))
{
return static_cast<tsize_t>(sizeWritten);
}
else
{
return 0;
}
};
static int Close(thandle_t handle)
{
AZ::IO::FileIOBase::GetDirectInstance()->Close(static_cast<AZ::IO::HandleType>(reinterpret_cast<AZ::u64>(handle)));
return 0;
};
static toff_t Seek(thandle_t handle, toff_t pos, int mode)
{
if (AZ::IO::FileIOBase::GetDirectInstance()->Seek(static_cast<AZ::IO::HandleType>(reinterpret_cast<AZ::u64>(handle)), static_cast<uint64_t>(pos), AZ::IO::GetSeekTypeFromFSeekMode(mode)))
{
if (mode == SEEK_SET)
{
return pos;
}
else
{
AZ::u64 offsetFromBegin;
if (AZ::IO::FileIOBase::GetDirectInstance()->Tell(static_cast<AZ::IO::HandleType>(reinterpret_cast<AZ::u64>(handle)), offsetFromBegin))
{
return static_cast<tsize_t>(offsetFromBegin);
}
else
{
return -1;
}
}
}
return -1;
};
static toff_t Size(thandle_t handle)
{
AZ::u64 fileSize = 0;
AZ::IO::FileIOBase::GetDirectInstance()->Size(static_cast<AZ::IO::HandleType>(reinterpret_cast<AZ::u64>(handle)), fileSize);
return static_cast<tsize_t>(fileSize);
};
static int Map(thandle_t, tdata_t*, toff_t*)
{
return 0;
};
static void Unmap(thandle_t, tdata_t, toff_t)
{
return;
};
};
#endif
}
std::unique_ptr<IImageHandler::IImage> ImageHandler::CreateImage(std::vector<unsigned char>&& data, int width, int height) const
{
return std::make_unique<Image>(std::move(data), width, height);
}
std::unique_ptr<IImageHandler::IImage> ImageHandler::LoadImage([[maybe_unused]] const char* filename) const
{
#if AZ_LEGACY_CRYSYSTEM_TRAIT_IMAGEHANDLER_TIFFIO
AZ::IO::HandleType fileHandle;
AZ::IO::FileIOBase::GetDirectInstance()->Open(filename, AZ::IO::GetOpenModeFromStringMode("rb"), fileHandle);
if (fileHandle == AZ::IO::InvalidHandle)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Failed to open image file %s", filename);
return nullptr;
}
auto tifHandle = std17::unique_resource_checked(TIFFClientOpen(filename, "rb", reinterpret_cast<thandle_t>(static_cast<AZ::u64>(fileHandle)), TiffIO::Read, TiffIO::Write, TiffIO::Seek, TiffIO::Close, &TiffIO::Size, TiffIO::Map, TiffIO::Unmap), (TIFF*)nullptr, TIFFClose);
if (!tifHandle)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Failed to load image %s", filename);
return nullptr;
}
int width = 0;
int height = 0;
TIFFGetField(tifHandle, TIFFTAG_IMAGEWIDTH, &width);
TIFFGetField(tifHandle, TIFFTAG_IMAGELENGTH, &height);
std::vector<unsigned char> data(4 * width * height);
if (!TIFFReadRGBAImageOriented(tifHandle, width, height, reinterpret_cast<uint32*>(data.data()), ORIENTATION_TOPLEFT))
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Failed to load image %s", filename);
return nullptr;
}
//strip alpha
int every4th = 0;
data.erase(std::remove_if(begin(data), end(data), [&](unsigned char)
{
return (every4th++ & 3) == 3;
}), end(data));
return std::make_unique<Image>(std::move(data), width, height);
#else
CRY_ASSERT(0); // UNIMPLEMENTED
return nullptr;
#endif
}
bool ImageHandler::SaveImage([[maybe_unused]] IImageHandler::IImage* image, [[maybe_unused]] const char* filename) const
{
#if AZ_LEGACY_CRYSYSTEM_TRAIT_IMAGEHANDLER_TIFFIO
AZ::IO::HandleType fileHandle;
AZ::IO::FileIOBase::GetDirectInstance()->Open(filename, AZ::IO::GetOpenModeFromStringMode("wb"), fileHandle);
if (fileHandle == AZ::IO::InvalidHandle)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Failed to open image file for write %s", filename);
return false;
}
auto tifHandle = std17::unique_resource_checked(TIFFClientOpen(filename, "wb", reinterpret_cast<thandle_t>(static_cast<AZ::u64>(fileHandle)), TiffIO::Read, TiffIO::Write, TiffIO::Seek, TiffIO::Close, &TiffIO::Size, TiffIO::Map, TiffIO::Unmap), (TIFF*)nullptr, TIFFClose);
if (!tifHandle)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Failed to save image %s", filename);
return false;
}
TIFFSetField(tifHandle, TIFFTAG_IMAGEWIDTH, image->GetWidth());
TIFFSetField(tifHandle, TIFFTAG_IMAGELENGTH, image->GetHeight());
TIFFSetField(tifHandle, TIFFTAG_SAMPLESPERPIXEL, c_BytesPerPixel);
TIFFSetField(tifHandle, TIFFTAG_BITSPERSAMPLE, 8);
TIFFSetField(tifHandle, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(tifHandle, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(tifHandle, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
TIFFSetField(tifHandle, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
tsize_t bytesPerLine = c_BytesPerPixel * image->GetWidth();
std::vector<unsigned char> lineBuffer;
if (TIFFScanlineSize(tifHandle) != bytesPerLine)
{
lineBuffer.resize(bytesPerLine);
}
else
{
lineBuffer.resize(TIFFScanlineSize(tifHandle));
}
TIFFSetField(tifHandle, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tifHandle, image->GetWidth() * c_BytesPerPixel));
auto srcData = image->GetData().data();
for (uint32 row = 0; row < image->GetHeight(); row++)
{
memcpy(lineBuffer.data(), &srcData[row * bytesPerLine], bytesPerLine);
if (TIFFWriteScanline(tifHandle, lineBuffer.data(), row, 0) < 0)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Failed to write part of image %s", filename);
return false;
}
}
return true;
#else
CRY_ASSERT(0); // UNIMPLEMENTED
return false;
#endif
}
std::unique_ptr<IImageHandler::IImage> ImageHandler::CreateDiffImage(IImageHandler::IImage* image1, IImageHandler::IImage* image2) const
{
if (!image1 || !image2)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Could not create diff image, null arguments");
return nullptr;
}
if (image1->GetWidth() != image2->GetWidth() || image1->GetHeight() != image2->GetHeight())
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Could not create diff image, 2 images were not the same size");
return nullptr;
}
CRY_ASSERT(image1->GetData().size() == image1->GetWidth() * image1->GetHeight() * ImageHandler::c_BytesPerPixel);
CRY_ASSERT(image2->GetData().size() == image2->GetWidth() * image2->GetHeight() * ImageHandler::c_BytesPerPixel);
std::vector<unsigned char> resultRGBData;
auto iter1 = image1->GetData().data();
auto iter2 = image2->GetData().data();
for (int i = 0; i < image1->GetWidth() * image1->GetHeight() * c_BytesPerPixel; ++i)
{
resultRGBData.push_back(static_cast<unsigned char>(abs(static_cast<int>(iter1[i]) - static_cast<int>(iter2[i]))));
}
return std::make_unique<Image>(std::move(resultRGBData), image1->GetWidth(), image1->GetHeight());
}
float ImageHandler::CalculatePSNR(IImageHandler::IImage* diffIimage) const
{
if (!diffIimage)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Could not create diff image, null arguments");
return 0;
}
CRY_ASSERT(diffIimage->GetData().size() == diffIimage->GetWidth() * diffIimage->GetHeight() * ImageHandler::c_BytesPerPixel);
auto mse = std17::accumulate(diffIimage->GetData(), 0.0, [](double result, unsigned char value) -> double { return result += (double)value * (double)value; });
mse /= (c_BytesPerPixel * diffIimage->GetWidth() * diffIimage->GetHeight());
if (mse <= 0)
{
return std::numeric_limits<float>::max();
}
// see http://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio for a derivation of this formula and source for magic numbers
return static_cast<float>(20 * log10(255) - 10 * log10(mse));
}
+30
View File
@@ -0,0 +1,30 @@
/*
* 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.
*
*/
#pragma once
#ifndef CRYINCLUDE_CRYSYSTEM_IMAGEHANDLER_H
#define CRYINCLUDE_CRYSYSTEM_IMAGEHANDLER_H
#include "IImageHandler.h"
class ImageHandler
: public IImageHandler
{
public:
static const int c_BytesPerPixel = 3; //This only deals with RGB data for now, no alpha
private:
virtual std::unique_ptr<IImageHandler::IImage> CreateImage(std::vector<unsigned char>&& rgbData, int width, int height) const override;
virtual std::unique_ptr<IImageHandler::IImage> LoadImage(const char* filename) const override;
virtual bool SaveImage(IImageHandler::IImage* image, const char* filename) const override;
virtual std::unique_ptr<IImageHandler::IImage> CreateDiffImage(IImageHandler::IImage* image1, IImageHandler::IImage* image2) const override;
virtual float CalculatePSNR(IImageHandler::IImage* diffIimage) const override;
};
#endif // CRYINCLUDE_CRYSYSTEM_IMAGEHANDLER_H
@@ -0,0 +1,29 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : lz4 hc decompress wrapper
#include "CrySystem_precompiled.h"
#include <lz4.h>
#include "LZ4Decompressor.h"
bool CLZ4Decompressor::DecompressData(const char* pIn, char* pOut, const uint outputSize) const
{
return LZ4_decompress_fast(pIn, pOut, outputSize) >= 0;
}
void CLZ4Decompressor::Release()
{
delete this;
}
@@ -0,0 +1,35 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : lz4 hc decompress wrapper
#ifndef CRYINCLUDE_CRYSYSTEM_LZ4DECOMPRESSOR_H
#define CRYINCLUDE_CRYSYSTEM_LZ4DECOMPRESSOR_H
#pragma once
#include "ILZ4Decompressor.h"
class CLZ4Decompressor
: public ILZ4Decompressor
{
public:
virtual bool DecompressData(const char* pIn, char* pOut, const uint outputSize) const;
virtual void Release();
private:
virtual ~CLZ4Decompressor() {}
};
#endif // CRYINCLUDE_CRYSYSTEM_LZ4DECOMPRESSOR_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,205 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include "ILevelSystem.h"
#include <AzFramework/Archive/IArchive.h>
namespace LegacyLevelSystem
{
class CLevelInfo
: public ILevelInfo
{
friend class CLevelSystem;
public:
CLevelInfo()
: m_heightmapSize(0)
, m_bMetaDataRead(false)
, m_isModLevel(false)
, m_scanTag(ILevelSystem::TAG_UNKNOWN)
, m_levelTag(ILevelSystem::TAG_UNKNOWN)
{
SwapEndian(m_scanTag, eBigEndian);
SwapEndian(m_levelTag, eBigEndian);
};
// ILevelInfo
virtual const char* GetName() const { return m_levelName.c_str(); };
virtual const bool IsOfType(const char* sType) const;
virtual const char* GetPath() const { return m_levelPath.c_str(); };
virtual const char* GetPaks() const { return m_levelPaks.c_str(); };
virtual bool GetIsModLevel() const { return m_isModLevel; }
virtual const uint32 GetScanTag() const { return m_scanTag; }
virtual const uint32 GetLevelTag() const { return m_levelTag; }
virtual const char* GetDisplayName() const;
virtual const char* GetPreviewImagePath() const { return m_previewImagePath.c_str(); }
virtual const char* GetBackgroundImagePath() const { return m_backgroundImagePath.c_str(); }
virtual const char* GetMinimapImagePath() const {return m_minimapImagePath.c_str(); }
//virtual const ILevelInfo::TStringVec& GetMusicLibs() const { return m_musicLibs; }; // Gets reintroduced when level specific music data loading is implemented.
virtual const bool MetadataLoaded() const { return m_bMetaDataRead; }
virtual int GetGameTypeCount() const { return m_gameTypes.size(); };
virtual const ILevelInfo::TGameTypeInfo* GetGameType(int gameType) const { return &m_gameTypes[gameType]; };
virtual bool SupportsGameType(const char* gameTypeName) const;
virtual const ILevelInfo::TGameTypeInfo* GetDefaultGameType() const;
virtual bool HasGameRules() const{ return !m_gamerules.empty(); }
virtual const ILevelInfo::SMinimapInfo& GetMinimapInfo() const { return m_minimapInfo; }
virtual const char* GetDefaultGameRules() const{ return m_gamerules.empty() ? NULL : m_gamerules[0].c_str(); }
virtual ILevelInfo::TStringVec GetGameRules() const{ return m_gamerules; }
// ~ILevelInfo
void GetMemoryUsage(ICrySizer*) const;
private:
void ReadMetaData();
bool ReadInfo();
bool OpenLevelPak();
void CloseLevelPak();
string m_levelName;
string m_levelPath;
string m_levelPaks;
string m_levelDisplayName;
string m_previewImagePath;
string m_backgroundImagePath;
string m_minimapImagePath;
string m_levelPakFullPath;
TStringVec m_gamerules;
int m_heightmapSize;
uint32 m_scanTag;
uint32 m_levelTag;
bool m_bMetaDataRead;
std::vector<ILevelInfo::TGameTypeInfo> m_gameTypes;
bool m_isModLevel;
SMinimapInfo m_minimapInfo;
DynArray<string> m_levelTypeList;
bool m_isPak = false;
};
class CLevel
: public ILevel
{
friend class CLevelSystem;
public:
CLevel() {}
virtual ~CLevel() = default;
virtual void Release() { delete this; }
virtual ILevelInfo* GetLevelInfo() { return &m_levelInfo; }
private:
CLevelInfo m_levelInfo;
};
class CLevelSystem
: public ILevelSystem
{
public:
CLevelSystem(ISystem* pSystem, const char* levelsFolder);
virtual ~CLevelSystem();
void Release() { delete this; };
// ILevelSystem
virtual DynArray<string>* GetLevelTypeList();
virtual void Rescan(const char* levelsFolder, const uint32 tag);
void ScanFolder(const char* subfolder, bool modFolder, const uint32 tag) override;
void PopulateLevels(string searchPattern, string& folder, AZ::IO::IArchive* pPak, bool& modFolder, const uint32& tag, bool fromFileSystemOnly) override;
virtual int GetLevelCount();
virtual ILevelInfo* GetLevelInfo(int level);
virtual ILevelInfo* GetLevelInfo(const char* levelName);
virtual void AddListener(ILevelSystemListener* pListener);
virtual void RemoveListener(ILevelSystemListener* pListener);
virtual ILevel* GetCurrentLevel() const { return m_pCurrentLevel; }
virtual ILevel* LoadLevel(const char* levelName);
virtual void UnLoadLevel();
virtual ILevel* SetEditorLoadedLevel(const char* levelName, bool bReadLevelInfoMetaData = false);
virtual void PrepareNextLevel(const char* levelName);
virtual float GetLastLevelLoadTime() { return m_fLastLevelLoadTime; };
virtual bool IsLevelLoaded() { return m_bLevelLoaded; }
// If the level load failed then we need to have a different shutdown procedure vs when a level is naturally unloaded
virtual void SetLevelLoadFailed(bool loadFailed) { m_levelLoadFailed = loadFailed; }
virtual bool GetLevelLoadFailed() { return m_levelLoadFailed; }
// ~ILevelSystem
void GetMemoryUsage(ICrySizer* s) const;
void SaveOpenedFilesList();
private:
ILevel* LoadLevelInternal(const char* _levelName);
// ILevelSystemListener events notification
void OnLevelNotFound(const char* levelName);
void OnLoadingStart(ILevelInfo* pLevel);
void OnLoadingComplete(ILevel* pLevel);
void OnLoadingError(ILevelInfo* pLevel, const char* error);
void OnLoadingProgress(ILevelInfo* pLevel, int progressAmount);
void OnUnloadComplete(ILevel* pLevel);
// lowercase string and replace backslashes with forward slashes
// TODO: move this to a more general place in CryEngine
string& UnifyName(string& name);
void LogLoadingTime();
bool LoadLevelInfo(CLevelInfo& levelInfo);
// internal get functions for the level infos ... they preserve the type and don't
// directly cast to the interface
CLevelInfo* GetLevelInfoInternal(int level);
CLevelInfo* GetLevelInfoInternal(const char* levelName);
ISystem* m_pSystem;
std::vector<CLevelInfo> m_levelInfos;
string m_levelsFolder;
ILevel* m_pCurrentLevel;
ILevelInfo* m_pLoadingLevelInfo;
string m_lastLevelName;
float m_fLastLevelLoadTime;
float m_fFilteredProgress;
float m_fLastTime;
bool m_bLevelLoaded;
bool m_bRecordingFileOpens;
bool m_levelLoadFailed = false;
int m_nLoadedLevelsCount;
CTimeValue m_levelLoadStartTime;
static int s_loadCount;
std::vector<ILevelSystemListener*> m_listeners;
DynArray<string> m_levelTypeList;
AZ::IO::IArchive::LevelPackOpenEvent::Handler m_levelPackOpenHandler;
AZ::IO::IArchive::LevelPackCloseEvent::Handler m_levelPackCloseHandler;
};
} // namespace LegacyLevelSystem
@@ -0,0 +1,647 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#if defined(ENABLE_LOADING_PROFILER)
#include "System.h"
#include "LoadingProfiler.h"
#define LOADING_TIME_CONTAINER_MAX_TEXT_SIZE 1024
#define MAX_LOADING_TIME_PROFILER_STACK_DEPTH 16
//#define SAVE_SAVELEVELSTATS_IN_ROOT
struct SLoadingTimeContainer
: public _i_reference_target_t
{
SLoadingTimeContainer() {}
SLoadingTimeContainer(SLoadingTimeContainer* pParent, const char* pPureFuncName, const int nRootIndex)
{
m_dSelfMemUsage = m_dTotalMemUsage = m_dSelfTime = m_dTotalTime = 0;
m_nCounter = 1;
m_pFuncName = pPureFuncName;
m_pParent = pParent;
m_nRootIndex = nRootIndex;
}
static int Cmp_SLoadingTimeContainer_Time(const void* v1, const void* v2)
{
SLoadingTimeContainer* pChunk1 = (SLoadingTimeContainer*)v1;
SLoadingTimeContainer* pChunk2 = (SLoadingTimeContainer*)v2;
if (pChunk1->m_dSelfTime > pChunk2->m_dSelfTime)
{
return -1;
}
else if (pChunk1->m_dSelfTime < pChunk2->m_dSelfTime)
{
return 1;
}
return 0;
}
static int Cmp_SLoadingTimeContainer_MemUsage(const void* v1, const void* v2)
{
SLoadingTimeContainer* pChunk1 = (SLoadingTimeContainer*)v1;
SLoadingTimeContainer* pChunk2 = (SLoadingTimeContainer*)v2;
if (pChunk1->m_dSelfMemUsage > pChunk2->m_dSelfMemUsage)
{
return -1;
}
else if (pChunk1->m_dSelfMemUsage < pChunk2->m_dSelfMemUsage)
{
return 1;
}
return 0;
}
static double GetUsedMemory(ISystem* pSysytem)
{
static IMemoryManager::SProcessMemInfo processMemInfo;
pSysytem->GetIMemoryManager()->GetProcessMemInfo(processMemInfo);
return double(processMemInfo.PagefileUsage) / double(1024 * 1024);
}
void Clear()
{
for (size_t i = 0, end = m_pChilds.size(); i < end; ++i)
{
delete m_pChilds[i];
}
}
~SLoadingTimeContainer()
{
Clear();
}
double m_dSelfTime, m_dTotalTime;
double m_dSelfMemUsage, m_dTotalMemUsage;
uint32 m_nCounter;
const char* m_pFuncName;
SLoadingTimeContainer* m_pParent;
int m_nRootIndex;
std::vector<SLoadingTimeContainer*> m_pChilds;
DiskOperationInfo m_selfInfo;
DiskOperationInfo m_totalInfo;
bool m_bUsed;
};
bool operator== (const SLoadingTimeContainer& a, const SLoadingTimeContainer& b)
{
return b.m_pFuncName == a.m_pFuncName;
}
bool operator== (const SLoadingTimeContainer& a, const char* b)
{
return b == a.m_pFuncName;
}
SLoadingTimeContainer* CLoadingProfilerSystem::m_pCurrentLoadingTimeContainer = 0;
SLoadingTimeContainer* CLoadingProfilerSystem::m_pRoot[2] = {0, 0};
int CLoadingProfilerSystem::m_iActiveRoot = 0;
ICVar* CLoadingProfilerSystem::m_pEnableProfile = 0;
int CLoadingProfilerSystem::nLoadingProfileMode = 1;
int CLoadingProfilerSystem::nLoadingProfilerNotTrackedAllocations = -1;
CryCriticalSection CLoadingProfilerSystem::csLock;
//////////////////////////////////////////////////////////////////////////
void CLoadingProfilerSystem::OutputLoadingTimeStats(ILog* pLog, int nMode)
{
nLoadingProfileMode = nMode;
PodArray<SLoadingTimeContainer> arrNoStack;
CreateNoStackList(arrNoStack);
if (nLoadingProfileMode > 0)
{ // loading mem stats per func
pLog->Log("------ Level loading memory allocations (MB) by function ------------");
pLog->Log(" ||Self | Total | Calls | Function (%d MB lost)||", nLoadingProfilerNotTrackedAllocations);
pLog->Log("---------------------------------------------------------------------");
qsort(arrNoStack.GetElements(), arrNoStack.Count(), sizeof(arrNoStack[0]), SLoadingTimeContainer::Cmp_SLoadingTimeContainer_MemUsage);
for (int i = 0; i < arrNoStack.Count(); i++)
{
const SLoadingTimeContainer* pTimeContainer = &arrNoStack[i];
pLog->Log("|%6.1f | %6.1f | %6d | %s|",
pTimeContainer->m_dSelfMemUsage, pTimeContainer->m_dTotalMemUsage, (int)pTimeContainer->m_nCounter, pTimeContainer->m_pFuncName);
}
pLog->Log("---------------------------------------------------------------------");
}
if (nLoadingProfileMode > 0)
{ // loading time stats per func
pLog->Log("----------- Level loading time (sec) by function --------------------");
pLog->Log(" ||Self | Total | Calls | Function||");
pLog->Log("---------------------------------------------------------------------");
qsort(arrNoStack.GetElements(), arrNoStack.Count(), sizeof(arrNoStack[0]), SLoadingTimeContainer::Cmp_SLoadingTimeContainer_Time);
for (int i = 0; i < arrNoStack.Count(); i++)
{
const SLoadingTimeContainer* pTimeContainer = &arrNoStack[i];
pLog->Log("|%6.1f | %6.1f | %6d | %s|",
pTimeContainer->m_dSelfTime, pTimeContainer->m_dTotalTime, (int)pTimeContainer->m_nCounter, pTimeContainer->m_pFuncName);
}
if (nLoadingProfileMode == 1)
{
pLog->Log("----- ( Use sys_ProfileLevelLoading 2 for more detailed stats ) -----");
}
else
{
pLog->Log("---------------------------------------------------------------------");
}
}
if (nLoadingProfileMode > 0)
{ // file info
pLog->Log("----------------------------- Level file information by function --------------------------------");
pLog->Log("|| Self | Total |Bandwith| Calls | Function||");
pLog->Log("|| Seeks |FileOpen|FileRead| Seeks |FileOpen|FileRead| Kb/s | | ||");
qsort(arrNoStack.GetElements(), arrNoStack.Count(), sizeof(arrNoStack[0]), SLoadingTimeContainer::Cmp_SLoadingTimeContainer_Time);
for (int i = 0; i < arrNoStack.Count(); i++)
{
const SLoadingTimeContainer* pTimeContainer = &arrNoStack[i];
double bandwidth = pTimeContainer->m_dSelfTime > 0 ? (pTimeContainer->m_selfInfo.m_dOperationSize / pTimeContainer->m_dSelfTime / 1024.0) : 0.;
pLog->Log("|%6d | %6d | %6d |%6d | %6d | %6d | %6.1f | %6d | %s|",
pTimeContainer->m_selfInfo.m_nSeeksCount, pTimeContainer->m_selfInfo.m_nFileOpenCount, pTimeContainer->m_selfInfo.m_nFileReadCount,
pTimeContainer->m_totalInfo.m_nSeeksCount, pTimeContainer->m_totalInfo.m_nFileOpenCount, pTimeContainer->m_totalInfo.m_nFileReadCount,
bandwidth, (int)pTimeContainer->m_nCounter, pTimeContainer->m_pFuncName);
}
if (nLoadingProfileMode == 1)
{
pLog->Log("----- ( Use sys_ProfileLevelLoading 2 for more detailed stats ) -----");
}
else
{
pLog->Log("---------------------------------------------------------------------");
}
}
}
struct CSystemEventListner_LoadingProfiler
: public ISystemEventListener
{
private:
CLoadingTimeProfiler* m_pPrecacheProfiler;
ESystemEvent lastEvent;
public:
CSystemEventListner_LoadingProfiler()
: m_pPrecacheProfiler(NULL) {}
virtual void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam)
{
switch (event)
{
case ESYSTEM_EVENT_GAME_MODE_SWITCH_START:
{
CLoadingProfilerSystem::Clean();
if (m_pPrecacheProfiler == NULL)
{
m_pPrecacheProfiler = new CLoadingTimeProfiler(gEnv->pSystem, "ModeSwitch");
}
break;
}
case ESYSTEM_EVENT_GAME_MODE_SWITCH_END:
{
SAFE_DELETE(m_pPrecacheProfiler);
CLoadingProfilerSystem::SaveTimeContainersToFile(gEnv->bMultiplayer == true ? "mode_switch_mp.lmbrlp" : "mode_switch_sp.lmbrlp", 0.0, true);
}
case ESYSTEM_EVENT_LEVEL_LOAD_PREPARE:
{
CLoadingProfilerSystem::Clean();
if (m_pPrecacheProfiler == NULL)
{
m_pPrecacheProfiler = new CLoadingTimeProfiler(gEnv->pSystem, "LevelLoading");
}
break;
}
case ESYSTEM_EVENT_LEVEL_LOAD_END:
{
delete m_pPrecacheProfiler;
m_pPrecacheProfiler = new CLoadingTimeProfiler(gEnv->pSystem, "Precache");
break;
}
case ESYSTEM_EVENT_LEVEL_PRECACHE_END:
{
if (lastEvent == ESYSTEM_EVENT_LEVEL_PRECACHE_FIRST_FRAME)
{
SAFE_DELETE(m_pPrecacheProfiler);
string levelName = "no_level";
ICVar* sv_map = gEnv->pConsole->GetCVar("sv_map");
if (sv_map)
{
levelName = sv_map->GetString();
}
string levelNameFullProfile = levelName + "_LP.lmbrlp";
string levelNameThreshold = levelName + "_LP_OneSec.lmbrlp";
CLoadingProfilerSystem::SaveTimeContainersToFile(levelNameFullProfile.c_str(), 0.0, false);
CLoadingProfilerSystem::SaveTimeContainersToFile(levelNameThreshold.c_str(), 1.0, true);
}
break;
}
case ESYSTEM_EVENT_LEVEL_POST_UNLOAD:
{
// Ensure that the precache profiler is dead
SAFE_DELETE(m_pPrecacheProfiler);
break;
}
}
if (event != ESYSTEM_EVENT_RANDOM_SEED)
{
lastEvent = event;
}
}
};
static CSystemEventListner_LoadingProfiler g_system_event_listener_loadingProfiler;
void CLoadingProfilerSystem::Init()
{
gEnv->pSystem->GetISystemEventDispatcher()->RegisterListener(&g_system_event_listener_loadingProfiler);
}
//////////////////////////////////////////////////////////////////////////
void CLoadingProfilerSystem::ShutDown()
{
if (gEnv && gEnv->pSystem && gEnv->pSystem->GetISystemEventDispatcher())
{
gEnv->pSystem->GetISystemEventDispatcher()->RemoveListener(&g_system_event_listener_loadingProfiler);
}
}
//////////////////////////////////////////////////////////////////////////
SLoadingTimeContainer* CLoadingProfilerSystem::StartLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler, const char* szFuncName)
{
if (!nLoadingProfileMode || !gEnv->pConsole)
{
return NULL;
}
DWORD threadID = GetCurrentThreadId();
static DWORD dwMainThreadId = GetCurrentThreadId();
if (threadID != dwMainThreadId)
{
return NULL;
}
if (!m_pEnableProfile)
{
if (gEnv->pConsole)
{
m_pEnableProfile = gEnv->pConsole->GetCVar("sys_ProfileLevelLoading");
if (!m_pEnableProfile)
{
return 0;
}
}
else
{
return 0;
}
}
if (m_pEnableProfile->GetIVal() <= 0)
{
return 0;
}
//if (m_pCurrentLoadingTimeContainer == m_pRoot && strstr(szFuncName,"Open"))
//{
// pProfiler->m_constructorInfo.m_nFileOpenCount +=1;
//}
CryAutoCriticalSection lock(csLock);
if (true /*pProfiler && pProfiler->m_pSystem*/)
{
ITimer* pTimer = pProfiler->m_pSystem->GetITimer();
pProfiler->m_fConstructorTime = pTimer->GetAsyncTime().GetSeconds();
pProfiler->m_fConstructorMemUsage = SLoadingTimeContainer::GetUsedMemory(pProfiler->m_pSystem);
DiskOperationInfo info;
pProfiler->m_constructorInfo = info;
if (nLoadingProfilerNotTrackedAllocations < 0)
{
nLoadingProfilerNotTrackedAllocations = (int)pProfiler->m_fConstructorMemUsage;
}
}
SLoadingTimeContainer* pParent = m_pCurrentLoadingTimeContainer;
if (!pParent)
{
pParent = m_pCurrentLoadingTimeContainer = m_pRoot[m_iActiveRoot] = new SLoadingTimeContainer(0, "Root", m_iActiveRoot);
}
for (size_t i = 0, end = m_pCurrentLoadingTimeContainer->m_pChilds.size(); i < end; ++i)
{
if (m_pCurrentLoadingTimeContainer->m_pChilds[i]->m_pFuncName == szFuncName)
{
assert(m_pCurrentLoadingTimeContainer->m_pChilds[i]->m_pParent == m_pCurrentLoadingTimeContainer);
assert(!m_pCurrentLoadingTimeContainer->m_pChilds[i]->m_bUsed);
m_pCurrentLoadingTimeContainer->m_pChilds[i]->m_bUsed = true;
m_pCurrentLoadingTimeContainer->m_pChilds[i]->m_nCounter++;
m_pCurrentLoadingTimeContainer = m_pCurrentLoadingTimeContainer->m_pChilds[i];
return m_pCurrentLoadingTimeContainer;
}
}
m_pCurrentLoadingTimeContainer = new SLoadingTimeContainer(pParent, szFuncName, pParent->m_nRootIndex);
m_pCurrentLoadingTimeContainer->m_bUsed = true;
{
// Need to iterate from the end than
pParent->m_pChilds.push_back(m_pCurrentLoadingTimeContainer);
}
return m_pCurrentLoadingTimeContainer;
}
void CLoadingProfilerSystem::EndLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler)
{
if (!nLoadingProfileMode)
{
return;
}
static DWORD dwMainThreadId = GetCurrentThreadId();
if (GetCurrentThreadId() != dwMainThreadId)
{
return;
}
if (!pProfiler->m_pTimeContainer)
{
return;
}
CryAutoCriticalSection lock(csLock);
if (true /*pProfiler && pProfiler->m_pSystem*/)
{
ITimer* pTimer = pProfiler->m_pSystem->GetITimer();
double fSelfTime = pTimer->GetAsyncTime().GetSeconds() - pProfiler->m_fConstructorTime;
double fMemUsage = SLoadingTimeContainer::GetUsedMemory(pProfiler->m_pSystem);
double fSelfMemUsage = fMemUsage - pProfiler->m_fConstructorMemUsage;
if (fSelfTime < 0.0)
{
assert(0);
}
pProfiler->m_pTimeContainer->m_dSelfTime += fSelfTime;
pProfiler->m_pTimeContainer->m_dTotalTime += fSelfTime;
pProfiler->m_pTimeContainer->m_dSelfMemUsage += fSelfMemUsage;
pProfiler->m_pTimeContainer->m_dTotalMemUsage += fSelfMemUsage;
DiskOperationInfo info;
info -= pProfiler->m_constructorInfo;
pProfiler->m_pTimeContainer->m_totalInfo += info;
pProfiler->m_pTimeContainer->m_selfInfo += info;
pProfiler->m_pTimeContainer->m_bUsed = false;
SLoadingTimeContainer* pParent = pProfiler->m_pTimeContainer->m_pParent;
pParent->m_selfInfo -= info;
pParent->m_dSelfTime -= fSelfTime;
pParent->m_dSelfMemUsage -= fSelfMemUsage;
if (pProfiler->m_pTimeContainer->m_pParent && pProfiler->m_pTimeContainer->m_pParent->m_nRootIndex == m_iActiveRoot)
{
m_pCurrentLoadingTimeContainer = pProfiler->m_pTimeContainer->m_pParent;
}
}
}
const char* CLoadingProfilerSystem::GetLoadingProfilerCallstack()
{
CryAutoCriticalSection lock(csLock);
static char szStack[1024];
szStack[0] = 0;
SLoadingTimeContainer* pC = m_pCurrentLoadingTimeContainer;
PodArray<SLoadingTimeContainer*> arrItems;
while (pC)
{
arrItems.Add(pC);
pC = pC->m_pParent;
}
for (int i = arrItems.Count() - 1; i >= 0; i--)
{
cry_strcat(szStack, " > ");
cry_strcat(szStack, arrItems[i]->m_pFuncName);
}
return &szStack[0];
}
void CLoadingProfilerSystem::FillProfilersList(AZStd::vector<SLoadingProfilerInfo>& profilers)
{
UpdateSelfStatistics(m_pRoot[m_iActiveRoot]);
PodArray<SLoadingTimeContainer> arrNoStack;
CreateNoStackList(arrNoStack);
//qsort(arrNoStack.GetElements(), arrNoStack.Count(), sizeof(arrNoStack[0]), SLoadingTimeContainer::Cmp_SLoadingTimeContainer_Time);
uint32 count = arrNoStack.Size();
profilers.resize(count);
for (uint32 i = 0; i < count; ++i)
{
profilers[i].name = arrNoStack[i].m_pFuncName;
profilers[i].selfTime = arrNoStack[i].m_dSelfTime;
profilers[i].callsTotal = arrNoStack[i].m_nCounter;
profilers[i].totalTime = arrNoStack[i].m_dTotalTime;
profilers[i].memorySize = arrNoStack[i].m_dTotalMemUsage;
profilers[i].selfInfo = arrNoStack[i].m_selfInfo;
profilers[i].totalInfo = arrNoStack[i].m_totalInfo;
}
}
void CLoadingProfilerSystem::AddTimeContainerFunction(PodArray<SLoadingTimeContainer>& arrNoStack, SLoadingTimeContainer* node)
{
if (!node)
{
return;
}
SLoadingTimeContainer* it = std::find(arrNoStack.begin(), arrNoStack.end(), node->m_pFuncName);
if (it == arrNoStack.end())
{
arrNoStack.push_back(*node);
}
else
{
it->m_dSelfMemUsage += node->m_dSelfMemUsage;
it->m_dSelfTime += node->m_dSelfTime;
it->m_dTotalMemUsage += node->m_dTotalMemUsage;
it->m_dTotalTime += node->m_dTotalTime;
it->m_nCounter += node->m_nCounter;
it->m_selfInfo += node->m_selfInfo;
it->m_totalInfo += node->m_totalInfo;
}
for (size_t i = 0, end = node->m_pChilds.size(); i < end; ++i)
{
AddTimeContainerFunction(arrNoStack, node->m_pChilds[i]);
}
}
void CLoadingProfilerSystem::CreateNoStackList(PodArray<SLoadingTimeContainer>& arrNoStack)
{
AddTimeContainerFunction(arrNoStack, m_pRoot[m_iActiveRoot]);
}
#define g_szTestResults "@cache@\\TestResults"
void CLoadingProfilerSystem::SaveTimeContainersToFile(const char* name, double fMinTotalTime, bool bClean)
{
if (m_pRoot[m_iActiveRoot])
{
const char* levelName = name;
//Ignore any folders in the input name
const char* folder = strrchr(name, '/');
if (folder != NULL)
{
levelName = folder + 1;
}
char path[AZ::IO::IArchive::MaxPath];
path[sizeof(path) - 1] = 0;
gEnv->pCryPak->AdjustFileName(string(string(g_szTestResults) + "\\" + levelName).c_str(), path, AZ_ARRAY_SIZE(path), AZ::IO::IArchive::FLAGS_PATH_REAL | AZ::IO::IArchive::FLAGS_FOR_WRITING);
gEnv->pCryPak->MakeDir(g_szTestResults);
AZ::IO::HandleType handle = AZ::IO::InvalidHandle;
AZ::IO::Result f = AZ::IO::FileIOBase::GetInstance()->Open(path,AZ::IO::OpenMode::ModeWrite, handle);
if (handle != AZ::IO::InvalidHandle)
{
UpdateSelfStatistics(m_pRoot[m_iActiveRoot]);
WriteTimeContainerToFile(m_pRoot[m_iActiveRoot], handle, 0, fMinTotalTime);
AZ::IO::FileIOBase::GetInstance()->Close(handle);
}
if (bClean)
{
Clean();
}
}
}
void CLoadingProfilerSystem::WriteTimeContainerToFile(SLoadingTimeContainer* p, AZ::IO::HandleType &handle, unsigned int depth, double fMinTotalTime)
{
if (p == NULL)
{
return;
}
if (p->m_dTotalTime < fMinTotalTime)
{
return;
}
CryFixedStringT<MAX_LOADING_TIME_PROFILER_STACK_DEPTH> sDepth;
for (unsigned int i = 0; i < depth; i++)
{
sDepth += "\t";
}
CryFixedStringT<128> str(p->m_pFuncName);
str.replace(':', '_');
char data[4096];
AZ::u64 bytesWritten;
azsnprintf(data, sizeof(data), "%s<%s selfTime='%f' selfMemory='%f' totalTime='%f' totalMemory='%f' count='%i' totalSeeks='%i' totalReads='%i' totalOpens='%i' totalDiskSize='%f' selfSeeks='%i' selfReads='%i' selfOpens='%i' selfDiskSize='%f'>\n",
sDepth.c_str(), str.c_str(), p->m_dSelfTime, p->m_dSelfMemUsage, p->m_dTotalTime, p->m_dTotalMemUsage, p->m_nCounter,
p->m_totalInfo.m_nSeeksCount, p->m_totalInfo.m_nFileReadCount, p->m_totalInfo.m_nFileOpenCount, p->m_totalInfo.m_dOperationSize,
p->m_selfInfo.m_nSeeksCount, p->m_selfInfo.m_nFileReadCount, p->m_selfInfo.m_nFileOpenCount, p->m_selfInfo.m_dOperationSize);
AZ::IO::FileIOBase::GetInstance()->Write(handle, data, strlen(data), &bytesWritten);
for (size_t i = 0, end = p->m_pChilds.size(); i < end; ++i)
{
WriteTimeContainerToFile(p->m_pChilds[i], handle, depth + 1, fMinTotalTime);
}
azsnprintf(data, sizeof(data), "%s</%s>\n", sDepth.c_str(), str.c_str());
AZ::IO::FileIOBase::GetInstance()->Write(handle, data, strlen(data), &bytesWritten);
}
void CLoadingProfilerSystem::UpdateSelfStatistics(SLoadingTimeContainer* p)
{
if (p == NULL)
{
return;
}
p->m_dSelfMemUsage = 0;
p->m_dSelfTime = 0;
p->m_nCounter = 1;
p->m_selfInfo.m_dOperationSize = 0;
p->m_selfInfo.m_nFileOpenCount = 0;
p->m_selfInfo.m_nFileReadCount = 0;
p->m_selfInfo.m_nSeeksCount = 0;
for (size_t i = 0, end = p->m_pChilds.size(); i < end; ++i)
{
p->m_dTotalMemUsage += p->m_pChilds[i]->m_dTotalMemUsage;
p->m_dTotalTime += p->m_pChilds[i]->m_dTotalTime;
p->m_totalInfo += p->m_pChilds[i]->m_totalInfo;
}
}
void CLoadingProfilerSystem::Clean()
{
m_iActiveRoot = (m_iActiveRoot + 1) % 2;
if (m_pRoot[m_iActiveRoot])
{
delete m_pRoot[m_iActiveRoot];
}
m_pCurrentLoadingTimeContainer = m_pRoot[m_iActiveRoot] = 0;
}
#endif
@@ -0,0 +1,69 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_LOADINGPROFILER_H
#define CRYINCLUDE_CRYSYSTEM_LOADINGPROFILER_H
#pragma once
#if defined(ENABLE_LOADING_PROFILER)
struct SLoadingTimeContainer;
struct SLoadingProfilerInfo
{
string name;
double selfTime;
double totalTime;
uint32 callsTotal;
double memorySize;
DiskOperationInfo selfInfo;
DiskOperationInfo totalInfo;
};
class CLoadingProfilerSystem
{
public:
static void Init();
static void ShutDown();
static void CreateNoStackList(PodArray<SLoadingTimeContainer>&);
static void OutputLoadingTimeStats(ILog* pLog, int nMode);
static SLoadingTimeContainer* StartLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler, const char* szFuncName);
static void EndLoadingSectionProfiling(CLoadingTimeProfiler* pProfiler);
static const char* GetLoadingProfilerCallstack();
static void FillProfilersList(AZStd::vector<SLoadingProfilerInfo>& profilers);
static void FlushTimeContainers();
static void SaveTimeContainersToFile(const char*, double fMinTotalTime, bool bClean);
static void WriteTimeContainerToFile(SLoadingTimeContainer* p, AZ::IO::HandleType &handle, unsigned int depth, double fMinTotalTime);
static void UpdateSelfStatistics(SLoadingTimeContainer* p);
static void Clean();
protected:
static void AddTimeContainerFunction(PodArray<SLoadingTimeContainer>&, SLoadingTimeContainer*);
protected:
static int nLoadingProfileMode;
static int nLoadingProfilerNotTrackedAllocations;
static CryCriticalSection csLock;
static int m_iMaxArraySize;
static SLoadingTimeContainer* m_pCurrentLoadingTimeContainer;
static SLoadingTimeContainer* m_pRoot[2];
static int m_iActiveRoot;
static ICVar* m_pEnableProfile;
};
#endif
#endif // CRYINCLUDE_CRYSYSTEM_LOADINGPROFILER_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,324 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include <ILocalizationManager.h>
#include <StlUtils.h>
#include <VectorMap.h>
#include <AzCore/std/containers/map.h>
#include "Huffman.h"
//////////////////////////////////////////////////////////////////////////
/*
Manage Localization Data
*/
class CLocalizedStringsManager
: public ILocalizationManager
, public ISystemEventListener
{
public:
typedef std::vector<string> TLocalizationTagVec;
constexpr const static size_t LOADING_FIXED_STRING_LENGTH = 2048;
constexpr const static size_t COMPRESSION_FIXED_BUFFER_LENGTH = 6144;
CLocalizedStringsManager(ISystem* pSystem);
virtual ~CLocalizedStringsManager();
// ILocalizationManager
const char* LangNameFromPILID(const ILocalizationManager::EPlatformIndependentLanguageID id);
ILocalizationManager::EPlatformIndependentLanguageID PILIDFromLangName(AZStd::string langName) override;
ILocalizationManager::EPlatformIndependentLanguageID GetSystemLanguage() override;
ILocalizationManager::TLocalizationBitfield MaskSystemLanguagesFromSupportedLocalizations(const ILocalizationManager::TLocalizationBitfield systemLanguages);
ILocalizationManager::TLocalizationBitfield IsLanguageSupported(const ILocalizationManager::EPlatformIndependentLanguageID id);
const char* GetLanguage() override;
bool SetLanguage(const char* sLanguage) override;
int GetLocalizationFormat() const override;
virtual AZStd::string GetLocalizedSubtitleFilePath(const AZStd::string& localVideoPath, const AZStd::string& subtitleFileExtension) const override;
virtual AZStd::string GetLocalizedLocXMLFilePath(const AZStd::string& localXmlPath) const override;
bool InitLocalizationData(const char* sFileName, bool bReload = false);
bool RequestLoadLocalizationDataByTag(const char* sTag);
bool LoadLocalizationDataByTag(const char* sTag, bool bReload = false);
bool ReleaseLocalizationDataByTag(const char* sTag);
bool LoadAllLocalizationData(bool bReload = false) override;
bool LoadExcelXmlSpreadsheet(const char* sFileName, bool bReload = false) override;
void ReloadData() override;
void FreeData();
bool LocalizeString_s(const string& sString, string& outLocalizedString, bool bEnglish = false) override;
bool LocalizeString_ch(const char* sString, string& outLocalizedString, bool bEnglish = false) override;
void LocalizeAndSubstituteInternal(AZStd::string& locString, const AZStd::vector<AZStd::string>& keys, const AZStd::vector<AZStd::string>& values) override;
bool LocalizeLabel(const char* sLabel, string& outLocalizedString, bool bEnglish = false) override;
bool IsLocalizedInfoFound(const char* sKey);
bool GetLocalizedInfoByKey(const char* sKey, SLocalizedInfoGame& outGameInfo);
bool GetLocalizedInfoByKey(const char* sKey, SLocalizedSoundInfoGame* pOutSoundInfoGame);
int GetLocalizedStringCount();
bool GetLocalizedInfoByIndex(int nIndex, SLocalizedInfoGame& outGameInfo);
bool GetLocalizedInfoByIndex(int nIndex, SLocalizedInfoEditor& outEditorInfo);
bool GetEnglishString(const char* sKey, string& sLocalizedString) override;
bool GetSubtitle(const char* sKeyOrLabel, string& outSubtitle, bool bForceSubtitle = false) override;
void FormatStringMessage_List(string& outString, const string& sString, const char** sParams, int nParams) override;
void FormatStringMessage(string& outString, const string& sString, const char* param1, const char* param2 = 0, const char* param3 = 0, const char* param4 = 0) override;
void LocalizeTime(time_t t, bool bMakeLocalTime, bool bShowSeconds, string& outTimeString) override;
void LocalizeDate(time_t t, bool bMakeLocalTime, bool bShort, bool bIncludeWeekday, string& outDateString) override;
void LocalizeDuration(int seconds, string& outDurationString) override;
void LocalizeNumber(int number, string& outNumberString) override;
void LocalizeNumber_Decimal(float number, int decimals, string& outNumberString) override;
bool ProjectUsesLocalization() const override;
// ~ILocalizationManager
// ISystemEventManager
void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam);
// ~ISystemEventManager
int GetMemoryUsage(ICrySizer* pSizer);
void GetLoadedTags(TLocalizationTagVec& tagVec);
void FreeLocalizationData();
#if !defined(_RELEASE)
static void LocalizationDumpLoadedInfo(IConsoleCmdArgs* pArgs);
#endif //#if !defined(_RELEASE)
private:
void SetAvailableLocalizationsBitfield(const ILocalizationManager::TLocalizationBitfield availableLocalizations);
bool LocalizeStringInternal(const char* pStr, size_t len, string& outLocalizedString, bool bEnglish);
bool DoLoadExcelXmlSpreadsheet(const char* sFileName, uint8 tagID, bool bReload);
typedef bool(CLocalizedStringsManager::*LoadFunc)(const char*, uint8, bool);
bool DoLoadAGSXmlDocument(const char* sFileName, uint8 tagID, bool bReload);
LoadFunc GetLoadFunction() const;
struct SLocalizedStringEntryEditorExtension
{
string sKey; // Map key text equivalent (without @)
string sOriginalActorLine; // english text
string sUtf8TranslatedActorLine; // localized text
string sOriginalText; // subtitle. if empty, uses English text
string sOriginalCharacterName; // english character name speaking via XML asset
unsigned int nRow; // Number of row in XML file
void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
pSizer->AddObject(sKey);
pSizer->AddObject(sOriginalActorLine);
pSizer->AddObject(sUtf8TranslatedActorLine);
pSizer->AddObject(sOriginalText);
pSizer->AddObject(sOriginalCharacterName);
}
};
struct SLanguage;
//#define LOG_DECOMP_TIMES //If defined, will log decompression times to a file
struct SLocalizedStringEntry
{
//Flags
enum
{
USE_SUBTITLE = BIT(0), //should a subtitle displayed for this key?
IS_DIRECTED_RADIO = BIT(1), //should the radio receiving hud be displayed?
IS_INTERCEPTED = BIT(2), //should the radio receiving hud show the interception display?
IS_COMPRESSED = BIT(3), //Translated text is compressed
};
union trans_text
{
string* psUtf8Uncompressed;
uint8* szCompressed; // Note that no size information is stored. This is for struct size optimization and unfortunately renders the size info inaccurate.
};
string sCharacterName; // character name speaking via XML asset
trans_text TranslatedText; // Subtitle of this line
// audio specific part
string sPrototypeSoundEvent; // associated sound event prototype (radio, ...)
CryHalf fVolume;
CryHalf fRadioRatio;
// SoundMoods
DynArray<SLocalizedAdvancesSoundEntry> SoundMoods;
// EventParameters
DynArray<SLocalizedAdvancesSoundEntry> EventParameters;
// ~audio specific part
// subtitle & radio flags
uint8 flags;
// Index of Huffman tree for translated text. -1 = no tree assigned (error)
int8 huffmanTreeIndex;
uint8 nTagID;
// bool bDependentTranslation; // if the english/localized text contains other localization labels
//Additional information for Sandbox. Null in game
SLocalizedStringEntryEditorExtension* pEditorExtension;
SLocalizedStringEntry()
: flags(0)
, huffmanTreeIndex(-1)
, pEditorExtension(NULL)
{
TranslatedText.psUtf8Uncompressed = NULL;
};
~SLocalizedStringEntry()
{
SAFE_DELETE(pEditorExtension);
if ((flags & IS_COMPRESSED) == 0)
{
SAFE_DELETE(TranslatedText.psUtf8Uncompressed);
}
else
{
SAFE_DELETE_ARRAY(TranslatedText.szCompressed);
}
};
string GetTranslatedText(const SLanguage* pLanguage) const;
void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
pSizer->AddObject(sCharacterName);
if ((flags & IS_COMPRESSED) == 0 && TranslatedText.psUtf8Uncompressed != NULL) //Number of bytes stored for compressed text is unknown, which throws this GetMemoryUsage off
{
pSizer->AddObject(*TranslatedText.psUtf8Uncompressed);
}
pSizer->AddObject(sPrototypeSoundEvent);
pSizer->AddObject(SoundMoods);
pSizer->AddObject(EventParameters);
if (pEditorExtension != NULL)
{
pEditorExtension->GetMemoryUsage(pSizer);
}
}
};
//Keys as CRC32. Strings previously, but these proved too large
typedef VectorMap<uint32, SLocalizedStringEntry*> StringsKeyMap;
struct SLanguage
{
typedef std::vector<SLocalizedStringEntry*> TLocalizedStringEntries;
typedef std::vector<HuffmanCoder*> THuffmanCoders;
string sLanguage;
StringsKeyMap m_keysMap;
TLocalizedStringEntries m_vLocalizedStrings;
THuffmanCoders m_vEncoders;
void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
pSizer->AddObject(sLanguage);
pSizer->AddObject(m_vLocalizedStrings);
pSizer->AddObject(m_keysMap);
pSizer->AddObject(m_vEncoders);
}
};
struct SFileInfo
{
bool bDataStripping;
uint8 nTagID;
};
#ifndef _RELEASE
std::map<string, bool> m_warnedAboutLabels;
bool m_haveWarnedAboutAtLeastOneLabel;
void LocalizedStringsManagerWarning(const char* label, const char* message);
void ListAndClearProblemLabels();
#else
inline void LocalizedStringsManagerWarning(...) {};
inline void ListAndClearProblemLabels() {};
#endif
void AddLocalizedString(SLanguage* pLanguage, SLocalizedStringEntry* pEntry, const uint32 keyCRC32);
void AddControl(int nKey);
//////////////////////////////////////////////////////////////////////////
void ParseFirstLine(IXmlTableReader* pXmlTableReader, char* nCellIndexToType, std::map<int, string>& SoundMoodIndex, std::map<int, string>& EventParameterIndex);
void InternalSetCurrentLanguage(SLanguage* pLanguage);
ISystem* m_pSystem;
// Pointer to the current language.
SLanguage* m_pLanguage;
// all loaded Localization Files
typedef std::pair<string, SFileInfo> pairFileName;
typedef std::map<string, SFileInfo> tmapFilenames;
tmapFilenames m_loadedTables;
// filenames per tag
typedef std::vector<string> TStringVec;
struct STag
{
TStringVec filenames;
uint8 id;
bool loaded;
};
typedef std::map<string, STag> TTagFileNames;
TTagFileNames m_tagFileNames;
TStringVec m_tagLoadRequests;
// Array of loaded languages.
std::vector<SLanguage*> m_languages;
typedef std::set<string> PrototypeSoundEvents;
PrototypeSoundEvents m_prototypeEvents; // this set is purely used for clever string/string assigning to save memory
struct less_strcmp
{
bool operator()(const string& left, const string& right) const
{
return strcmp(left.c_str(), right.c_str()) < 0;
}
};
typedef std::set<string, less_strcmp> CharacterNameSet;
CharacterNameSet m_characterNameSet; // this set is purely used for clever string/string assigning to save memory
// CVARs
int m_cvarLocalizationDebug;
int m_cvarLocalizationEncode; //Encode/Compress translated text to save memory
int m_cvarLocalizationFormat;
//The localizations that are available for this SKU. Used for determining what to show on a language select screen or whether to show one at all
TLocalizationBitfield m_availableLocalizations;
//Lock for
mutable CryCriticalSection m_cs;
typedef CryAutoCriticalSection AutoLock;
};
File diff suppressed because it is too large Load Diff
+236
View File
@@ -0,0 +1,236 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#include <ILog.h>
#include <CryThread.h>
#include <MultiThread.h>
#include <MultiThread_Containers.h>
//////////////////////////////////////////////////////////////////////
#if defined(ANDROID) || defined(AZ_PLATFORM_MAC)
#define MAX_TEMP_LENGTH_SIZE 4098
#define AZ_RESTRICTED_SECTION_IMPLEMENTED
#elif defined(AZ_RESTRICTED_PLATFORM)
#include AZ_RESTRICTED_FILE(Log_h)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#else
#define MAX_TEMP_LENGTH_SIZE 8196
#endif
#define MAX_FILENAME_SIZE 256
#define KEEP_LOG_FILE_OPEN
//////////////////////////////////////////////////////////////////////
class CLog
: public ILog
{
public:
typedef std::list<ILogCallback*> Callbacks;
typedef CryStackStringT<char, MAX_TEMP_LENGTH_SIZE> LogStringType;
// constructor
CLog(ISystem* pSystem);
// destructor
~CLog();
// interface ILog, IMiniLog -------------------------------------------------
virtual void Release() { delete this; };
virtual bool SetFileName(const char* fileNameOrAbsolutePath, bool backupLogs);
virtual const char* GetFileName();
virtual const char* GetBackupFileName();
#if !defined(EXCLUDE_NORMAL_LOG)
virtual void Log(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogAlways(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogWarning(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogError(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogPlus(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogToFile (const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogToFilePlus(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogToConsole(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void LogToConsolePlus(const char* command, ...) PRINTF_PARAMS(2, 3);
#else
virtual void Log(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogAlways(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogWarning(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogError(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogPlus(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogToFile (const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogToFilePlus(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogToConsole(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
virtual void LogToConsolePlus(const char* command, ...) PRINTF_PARAMS(2, 3) {
};
#endif // !defined(EXCLUDE_NORMAL_LOG)
virtual void UpdateLoadingScreen(const char* command, ...) PRINTF_PARAMS(2, 3);
virtual void SetVerbosity(int verbosity);
virtual int GetVerbosityLevel();
virtual void RegisterConsoleVariables();
virtual void UnregisterConsoleVariables();
virtual void AddCallback(ILogCallback* pCallback);
virtual void RemoveCallback(ILogCallback* pCallback);
virtual void LogV(const ELogType ineType, int flags, const char* szFormat, va_list args);
virtual void LogV(const ELogType ineType, const char* szFormat, va_list args);
virtual void Update();
virtual const char* GetModuleFilter();
virtual void FlushAndClose();
private: // -------------------------------------------------------------------
struct SLogMsg
{
enum class Destination
{
Default, //LogString, sends OnWrite to anycallback registered with AddCallback
Console,
File
};
char msg[512];
ELogType logType;
bool bAdd;
Destination destination;
void GetMemoryUsage([[maybe_unused]] ICrySizer* pSizer) const {}
};
void CheckAndPruneBackupLogs() const;
bool IsError(ELogType logType) const { return logType == ELogType::eError || logType == ELogType::eErrorAlways || logType == ELogType::eWarning || logType == ELogType::eWarningAlways; }
//helper function to pass calls to LogString... to the main thread, returns false if you are on the main thread already, in which case just process the work.
bool LogToMainThread(const char* szString, ELogType logType, bool bAdd, SLogMsg::Destination destination);
enum class MessageQueueState
{
NotQueued,
Queued
};
#if !defined(EXCLUDE_NORMAL_LOG)
void LogString(const char* szString, ELogType logType);
void LogStringToFile(const char* szString, ELogType logType, bool bAdd, MessageQueueState queueState);
void LogStringToConsole(const char* szString, ELogType logType, bool bAdd);
#else
void LogString(const char* szString, ELogType logType) {}
void LogStringToFile(const char* szString, ELogType logType, bool bAdd, MessageQueueState queueState) {}
void LogStringToConsole(const char* szString, ELogType logType, bool bAdd) {}
#endif // !defined(EXCLUDE_NORMAL_LOG)
AZ::IO::HandleType OpenLogFile(const char* filename, const char* mode);
void CloseLogFile(bool force = false);
// will format the message into m_szTemp
void FormatMessage(const char* szCommand, ...) PRINTF_PARAMS(2, 3);
#if defined(SUPPORT_LOG_IDENTER)
void Indent(CLogIndenter* indenter);
void Unindent(CLogIndenter* indenter);
void BuildIndentString();
virtual void PushAssetScopeName(const char* sAssetType, const char* sName);
virtual void PopAssetScopeName();
virtual const char* GetAssetScopeString();
#endif
ISystem* m_pSystem; //
float m_fLastLoadingUpdateTime; // for non-frequent streamingEngine update
//char m_szTemp[MAX_TEMP_LENGTH_SIZE]; //
char m_szFilename[MAX_FILENAME_SIZE]; // can be with path
mutable char m_sBackupFilename[MAX_FILENAME_SIZE]; // can be with path
AZ::IO::HandleType m_logFileHandle;
CryStackStringT<char, 32> m_LogMode; //mode m_pLogFile has been opened with
AZ::IO::HandleType m_errFileHandle;
int m_nErrCount;
bool m_backupLogs;
#if defined(SUPPORT_LOG_IDENTER)
uint8 m_indentation;
LogStringType m_indentWithString;
class CLogIndenter* m_topIndenter;
struct SAssetScopeInfo
{
string sType;
string sName;
};
std::vector<SAssetScopeInfo> m_assetScopeQueue;
CryCriticalSection m_assetScopeQueueLock;
string m_assetScopeString;
#endif
ICVar* m_pLogIncludeTime; //
IConsole* m_pConsole; //
CryCriticalSection m_logCriticalSection;
struct SLogHistoryItem
{
char str[MAX_WARNING_LENGTH];
const char* ptr;
ELogType type;
float time;
};
SLogHistoryItem m_history[16];
int m_iLastHistoryItem;
static bool CheckLogFormatter(const char* formatter);
#if defined(KEEP_LOG_FILE_OPEN)
static void LogFlushFile(IConsoleCmdArgs* pArgs);
bool m_bFirstLine;
#endif
public: // -------------------------------------------------------------------
void GetMemoryUsage(ICrySizer* pSizer) const
{
pSizer->AddObject(this, sizeof(*this));
pSizer->AddObject(m_pLogVerbosity);
pSizer->AddObject(m_pLogWriteToFile);
pSizer->AddObject(m_pLogWriteToFileVerbosity);
pSizer->AddObject(m_pLogVerbosityOverridesWriteToFile);
pSizer->AddObject(m_pLogSpamDelay);
pSizer->AddObject(m_threadSafeMsgQueue);
}
// checks the verbosity of the message and returns NULL if the message must NOT be
// logged, or the pointer to the part of the message that should be logged
const char* CheckAgainstVerbosity(const char* pText, bool& logtofile, bool& logtoconsole, const uint8 DefaultVerbosity = 2);
// create backup of log file, useful behavior - only on development platform
void CreateBackupFile() const;
ICVar* m_pLogVerbosity; //
ICVar* m_pLogWriteToFile; //
ICVar* m_pLogWriteToFileVerbosity; //
ICVar* m_pLogVerbosityOverridesWriteToFile; //
ICVar* m_pLogSpamDelay; //
ICVar* m_pLogModule; // Module filter for log
Callbacks m_callbacks; //
threadID m_nMainThreadId;
CryMT::queue<SLogMsg> m_threadSafeMsgQueue;
};
@@ -0,0 +1,163 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "MTSafeAllocator.h"
#include <IConsole.h>
extern CMTSafeHeap* g_pPakHeap;
// Uncomment this define to enable time tracing of the MTSAFE heap
#define MTSAFE_PROFILE 1
//#undef MTSAFE_PROFILE
namespace
{
class CSimpleTimer
{
LARGE_INTEGER& m_result;
LARGE_INTEGER m_start;
public:
CSimpleTimer(LARGE_INTEGER& li)
: m_result(li)
{ QueryPerformanceCounter(&m_start); }
~CSimpleTimer()
{
LARGE_INTEGER end;
QueryPerformanceCounter(&end);
m_result.QuadPart = end.QuadPart - m_start.QuadPart;
}
};
};
//////////////////////////////////////////////////////////////////////////
CMTSafeHeap::CMTSafeHeap()
: m_LiveTempAllocations()
, m_TotalAllocations()
, m_TempAllocationsFailed()
, m_TempAllocationsTime()
{
size_t allocated = 0;
m_pGeneralHeapStorage = (char*)CryMalloc(MTSAFE_GENERAL_HEAP_SIZE, allocated, MTSAFE_DEFAULT_ALIGNMENT);
m_pGeneralHeapStorageEnd = m_pGeneralHeapStorage + MTSAFE_GENERAL_HEAP_SIZE;
m_pGeneralHeap = CryGetIMemoryManager()->CreateGeneralMemoryHeap(m_pGeneralHeapStorage, MTSAFE_GENERAL_HEAP_SIZE, "MTSafeHeap");
}
//////////////////////////////////////////////////////////////////////////
CMTSafeHeap::~CMTSafeHeap()
{
SAFE_RELEASE(m_pGeneralHeap);
CryFree(m_pGeneralHeapStorage, MTSAFE_DEFAULT_ALIGNMENT);
}
//////////////////////////////////////////////////////////////////////////
size_t CMTSafeHeap::PersistentAllocSize(size_t nSize)
{
return nSize;
}
//////////////////////////////////////////////////////////////////////////
void* CMTSafeHeap::PersistentAlloc(size_t nSize)
{
size_t allocated = 0;
return CryMalloc(nSize, allocated, MTSAFE_DEFAULT_ALIGNMENT);
}
//////////////////////////////////////////////////////////////////////////
void CMTSafeHeap::FreePersistent(void* p)
{
CryFree(p, MTSAFE_DEFAULT_ALIGNMENT);
}
//////////////////////////////////////////////////////////////////////////
void* CMTSafeHeap::TempAlloc(size_t nSize, const char* szDbgSource, bool& bFallBackToMalloc, uint32 align)
{
# if MTSAFE_PROFILE
CSimpleTimer timer(m_TempAllocationsTime);
# endif
void* ptr = NULL;
if (align)
{
ptr = m_pGeneralHeap->Memalign(align, nSize, szDbgSource);
}
else
{
ptr = m_pGeneralHeap->Malloc(nSize, szDbgSource);
}
//explicit alignment not supported beyond this point, safer to return NULL
if (ptr || !bFallBackToMalloc)
{
bFallBackToMalloc = false;
return ptr;
}
bFallBackToMalloc = true;
# if MTSAFE_PROFILE
CryInterlockedAdd((volatile int*)&m_TempAllocationsFailed, (int)nSize);
# endif
return CryModuleMemalign(nSize, align > 0 ? align : MTSAFE_DEFAULT_ALIGNMENT);
}
//////////////////////////////////////////////////////////////////////////
void CMTSafeHeap::FreeTemporary(void* p)
{
# if MTSAFE_PROFILE
CSimpleTimer timer(m_TempAllocationsTime);
# endif
if (m_pGeneralHeap->IsInAddressRange(p))
{
m_pGeneralHeap->Free(p);
return;
}
// Fallback to free
CryModuleMemalignFree(p);
}
//////////////////////////////////////////////////////////////////////////
void* CMTSafeHeap::StaticAlloc([[maybe_unused]] void* pOpaque, unsigned nItems, unsigned nSize)
{
return g_pPakHeap->TempAlloc(nItems * nSize, "StaticAlloc");
}
//////////////////////////////////////////////////////////////////////////
void CMTSafeHeap::StaticFree ([[maybe_unused]] void* pOpaque, void* pAddress)
{
g_pPakHeap->FreeTemporary(pAddress);
}
//////////////////////////////////////////////////////////////////////////
void CMTSafeHeap::GetMemoryUsage(ICrySizer* pSizer)
{
SIZER_COMPONENT_NAME(pSizer, "FileSystem Pool");
}
void CMTSafeHeap::PrintStats()
{
# if MTSAFE_PROFILE
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
const double rFreq = 1. / static_cast<double>(freq.QuadPart);
CryLogAlways("mtsafe temporary pool failed for %" PRISIZE_T " bytes, time spent in allocations %3.08f seconds",
m_TempAllocationsFailed, static_cast<double>(m_TempAllocationsTime.QuadPart) * rFreq);
# endif
}
+108
View File
@@ -0,0 +1,108 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#pragma once
#if defined(LINUX)
# include "Linux_Win32Wrapper.h"
#endif
#include <ISystem.h>
////////////////////////////////////////////////////////////////////////////////
#if defined(AZ_RESTRICTED_PLATFORM)
#include AZ_RESTRICTED_FILE(MTSafeAllocator_h)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#elif defined(MOBILE) // IOS/Android
# define MTSAFE_DEFAULT_ALIGNMENT 8
# define MTSAFE_GENERAL_HEAP_SIZE ((1U << 20) + (1U << 19))
#elif defined(WIN32) || defined(WIN64) || defined(LINUX) || defined(MAC)
# define MTSAFE_GENERAL_HEAP_SIZE (12U << 20)
# define MTSAFE_DEFAULT_ALIGNMENT 8
#else
# error Unknown target platform
#endif
class CMTSafeHeap
{
public:
// Constructor
CMTSafeHeap();
// Destructor
~CMTSafeHeap();
// Performs a persisistent (in other words, non-temporary) allocation.
void* PersistentAlloc(size_t nSize);
// Retrieves system memory allocation size for any call to PersistentAlloc.
// Required to not count virtual memory usage inside CrySizer
size_t PersistentAllocSize(size_t nSize);
// Frees memory allocation
void FreePersistent(void* p);
// Perform a allocation that is considered temporary and will be handled by
// the pool itself.
// Note: It is important that these temporary allocations are actually
// temporary and do not persist for a long persiod of time.
void* TempAlloc (size_t nSize, const char* szDbgSource, uint32 align = 0)
{
bool bFallbackToMalloc = true;
return TempAlloc(nSize, szDbgSource, bFallbackToMalloc, align);
}
void* TempAlloc (size_t nSize, const char* szDbgSource, bool& bFallBackToMalloc, uint32 align = 0);
bool IsInGeneralHeap(const void* p)
{
return m_pGeneralHeapStorage <= p && p < m_pGeneralHeapStorageEnd;
}
// Free a temporary allocaton.
void FreeTemporary(void* p);
// The number of live allocations allocation within the temporary pool
size_t NumAllocations() const { return m_LiveTempAllocations; }
// The memory usage of the mtsafe allocator
void GetMemoryUsage(ICrySizer* pSizer);
// zlib-compatible stubs
static void* StaticAlloc (void* pOpaque, unsigned nItems, unsigned nSize);
static void StaticFree (void* pOpaque, void* pAddress);
// Dump some statistics to the cry log
void PrintStats();
private:
friend class CSystem;
IGeneralMemoryHeap* m_pGeneralHeap;
char* m_pGeneralHeapStorage;
char* m_pGeneralHeapStorageEnd;
// The number of temporary allocations currently active within the pool
size_t m_LiveTempAllocations;
// The total number of allocations performed in the pool
size_t m_TotalAllocations;
// The total bytes that weren't temporarily allocated
size_t m_TempAllocationsFailed;
// The total number of temporary allocations that fell back to global system memory
LARGE_INTEGER m_TempAllocationsTime;
};
@@ -0,0 +1,145 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "MemoryAddressRange.h"
#include "System.h"
#if defined(APPLE) || defined(LINUX)
#include <sys/mman.h>
#endif
CMemoryAddressRange::CMemoryAddressRange(char* pBaseAddress, size_t nPageSize, size_t nPageCount, [[maybe_unused]] const char* sName)
: m_pBaseAddress(pBaseAddress)
, m_nPageSize(nPageSize)
, m_nPageCount(nPageCount)
{
}
void CMemoryAddressRange::Release()
{
delete this;
}
char* CMemoryAddressRange::GetBaseAddress() const
{
return m_pBaseAddress;
}
size_t CMemoryAddressRange::GetPageCount() const
{
return m_nPageCount;
}
size_t CMemoryAddressRange::GetPageSize() const
{
return m_nPageSize;
}
#if AZ_LEGACY_CRYSYSTEM_TRAIT_MEMADDRESSRANGE_WINDOWS_STYLE
void* CMemoryAddressRange::ReserveSpace(size_t capacity)
{
return VirtualAlloc(NULL, capacity, MEM_RESERVE, PAGE_READWRITE);
}
size_t CMemoryAddressRange::GetSystemPageSize()
{
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
}
CMemoryAddressRange::CMemoryAddressRange(size_t capacity, [[maybe_unused]] const char* name)
{
m_nPageSize = GetSystemPageSize();
size_t algnCap = Align(capacity, m_nPageSize);
m_pBaseAddress = (char*)ReserveSpace(algnCap);
m_nPageCount = algnCap / m_nPageSize;
}
CMemoryAddressRange::~CMemoryAddressRange()
{
VirtualFree(m_pBaseAddress, 0, MEM_RELEASE);
}
void* CMemoryAddressRange::MapPage(size_t pageIdx)
{
void* pRet = VirtualAlloc(m_pBaseAddress + pageIdx * m_nPageSize, m_nPageSize, MEM_COMMIT, PAGE_READWRITE);
return pRet;
}
void CMemoryAddressRange::UnmapPage(size_t pageIdx)
{
char* pBase = m_pBaseAddress + pageIdx * m_nPageSize;
// Disable warning about only decommitting pages, and not releasing them
VirtualFree(pBase, m_nPageSize, MEM_DECOMMIT);
}
#elif defined(AZ_RESTRICTED_PLATFORM)
#include AZ_RESTRICTED_FILE(MemoryAddressRange_cpp)
#elif defined(APPLE) || defined(LINUX)
void* CMemoryAddressRange::ReserveSpace(size_t capacity)
{
return mmap(0, capacity, PROT_NONE, MAP_ANON | MAP_NORESERVE | MAP_PRIVATE, -1, 0);
}
size_t CMemoryAddressRange::GetSystemPageSize()
{
return sysconf(_SC_PAGESIZE);
}
CMemoryAddressRange::CMemoryAddressRange(size_t capacity, const char* name)
{
m_nPageSize = GetSystemPageSize();
m_allocatedSpace = Align(capacity, m_nPageSize);
m_pBaseAddress = (char*)ReserveSpace(m_allocatedSpace);
assert(m_pBaseAddress != MAP_FAILED);
m_nPageCount = m_allocatedSpace / m_nPageSize;
}
CMemoryAddressRange::~CMemoryAddressRange()
{
int ret = munmap(m_pBaseAddress, m_allocatedSpace);
(void) ret;
assert(ret == 0);
}
void* CMemoryAddressRange::MapPage(size_t pageIdx)
{
// There is no equivalent to this function with mmap, this
// happens automatically in the OS. We just return the
// correct address.
void* pRet = NULL;
if (0 == mprotect(m_pBaseAddress + (pageIdx * m_nPageSize), m_nPageSize, PROT_READ | PROT_WRITE))
{
pRet = m_pBaseAddress + (pageIdx * m_nPageSize);
}
return pRet;
}
void CMemoryAddressRange::UnmapPage(size_t pageIdx)
{
char* pBase = m_pBaseAddress + pageIdx * m_nPageSize;
int ret = mprotect(pBase, m_nPageSize, PROT_NONE);
(void) ret;
assert(ret == 0);
}
#endif
@@ -0,0 +1,61 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_MEMORYADDRESSRANGE_H
#define CRYINCLUDE_CRYSYSTEM_MEMORYADDRESSRANGE_H
#pragma once
#include "IMemory.h"
class CMemoryAddressRange
: public IMemoryAddressRange
{
public:
static void* ReserveSpace(size_t sz);
static size_t GetSystemPageSize();
public:
CMemoryAddressRange(char* pBaseAddress, size_t nPageSize, size_t nPageCount, const char* sName);
CMemoryAddressRange(size_t capacity, const char* name);
~CMemoryAddressRange();
ILINE bool IsInRange(void* p) const
{
return m_pBaseAddress <= p && p < (m_pBaseAddress + m_nPageSize * m_nPageCount);
}
public:
void Release();
char* GetBaseAddress() const;
size_t GetPageCount() const;
size_t GetPageSize() const;
void* MapPage(size_t pageIdx);
void UnmapPage(size_t pageIdx);
private:
CMemoryAddressRange(const CMemoryAddressRange&);
CMemoryAddressRange& operator = (const CMemoryAddressRange&);
private:
char* m_pBaseAddress;
size_t m_nPageSize;
size_t m_nPageCount;
#if defined(APPLE) || defined(LINUX)
size_t m_allocatedSpace; // Required to unmap latter on
#endif
};
#endif // CRYINCLUDE_CRYSYSTEM_MEMORYADDRESSRANGE_H
@@ -0,0 +1,325 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_MEMORYFRAGMENTATIONPROFILER_H
#define CRYINCLUDE_CRYSYSTEM_MEMORYFRAGMENTATIONPROFILER_H
#pragma once
// useful class to investigate memory fragmentation
// every time you call this from the console:
//
// #System.DumpMemoryCoverage()
//
// it adds a line to "MemoryCoverage.bmp" (generated the first time, there is a max line count)
// blue stripes mark some special positions (DLL positions)
// Dependencies: only CryLog()
#include <vector> // STL vector<>
#if defined(WIN32) || defined(WIN64)
class CMemoryFragmentationProfiler
{
public:
// constructor - clean file
CMemoryFragmentationProfiler()
: m_dwLine(0xffffffff) // 0xffffffff means not initialized yet
{
}
// call this if you want to add one line (on first call the file is generated)
void DumpMemoryCoverage()
{
if (m_dwLine == 0xffffffff)
{
Init();
}
const size_t nMinMemoryPerUnit = 4 * 1024; // down to a few KB
const size_t nUnitsPerLine = 1024 * 8; // amount of bits, should only occupy a few KB memory
const size_t nMaxMemoryPerUnit = 0x100000000 / nUnitsPerLine; // 4GB in total
static std::vector<bool> vCoverage;
vCoverage.clear();
vCoverage.resize(nUnitsPerLine, 0); // should occupy nUnitsPerLine/8 bytes (vector<bool> is specialized)
size_t nAvailableMem = 0, nUsedMem = 0;
const size_t nMallocOverhead = 24; // depends on used runtime (debug:32, release:24)
size_t nCurrentUnitSize = 256 * 1024 * 1024; // start with 256 MB blocks
void** pMemoryBlocks = 0; // linked list of memory blocks (to free them)
size_t nUnits = 0;
uint32 dwAllocCnt = 0, dwFreeCnt = 0;
while (nCurrentUnitSize >= nMinMemoryPerUnit)
{
size_t nLocalUnits = 0;
for (;; )
{
void** pMem = (void**)::malloc(nCurrentUnitSize - nMallocOverhead);
if (!pMem)
{
break;
}
++dwAllocCnt;
// update coverage (conservative)
{
size_t nStartUnit = ((size_t)pMem + nMaxMemoryPerUnit - 1) / (nMaxMemoryPerUnit);
size_t nEndUnit = ((size_t)pMem + nCurrentUnitSize) / (nMaxMemoryPerUnit);
if (nStartUnit > nUnitsPerLine)
{
nStartUnit = nUnitsPerLine;
}
if (nEndUnit > nUnitsPerLine)
{
nEndUnit = nUnitsPerLine;
}
for (size_t i = nStartUnit; i < nEndUnit; ++i)
{
vCoverage[i] = 1;
}
}
++nLocalUnits;
// insert in linked list
*pMem = pMemoryBlocks;
pMemoryBlocks = pMem;
}
nUnits += nLocalUnits;
nAvailableMem += nLocalUnits * nCurrentUnitSize;
nCurrentUnitSize /= 2;
nUnits *= 2;
}
// free all memory blocks allocated
while (pMemoryBlocks)
{
void* pNext = *pMemoryBlocks;
::free(pMemoryBlocks);
++dwFreeCnt;
pMemoryBlocks = (void**)pNext;
}
// _heapmin();
CryLog("CMemoryFragmentationProfiler Y=%d, available memory=%d MB, used memory=%d MB",
m_dwLine, (nAvailableMem + 1024 * 1024 - 1) / (1024 * 1024), (nUsedMem + 1024 * 1024 - 1) / (1024 * 1024));
LogCoverage(vCoverage);
DumpToRAWCoverage(vCoverage);
}
private: // ------------------------------------------------------------------
void Init()
{
FILE* out = nullptr;
azfopen(&out, "MemoryCoverage.bmp", "wb");
if (out)
{
BITMAPFILEHEADER pHeader;
BITMAPINFOHEADER pInfoHeader;
memset(&pHeader, 0, sizeof(BITMAPFILEHEADER));
memset(&pInfoHeader, 0, sizeof(BITMAPINFOHEADER));
pHeader.bfType = 0x4D42;
pHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + m_nPixelsPerLine * m_nLineCount * 3;
pHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
pInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
pInfoHeader.biWidth = m_nPixelsPerLine;
pInfoHeader.biHeight = m_nLineCount;
pInfoHeader.biPlanes = 1;
pInfoHeader.biBitCount = 24;
pInfoHeader.biCompression = 0;
pInfoHeader.biSizeImage = m_nPixelsPerLine * m_nLineCount;
fwrite(&pHeader, 1, sizeof(BITMAPFILEHEADER), out);
fwrite(&pInfoHeader, 1, sizeof(BITMAPINFOHEADER), out);
for (int y = 0; y < m_nLineCount; y++) // amount of lines
{
for (int x = 0; x < m_nPixelsPerLine; x++)
{
size_t nAddress = x * (0x100000000 / m_nPixelsPerLine);
if (nAddress == 0x30000000
|| nAddress == 0x30500000
|| nAddress == 0x31000000
|| nAddress == 0x31500000
|| nAddress == 0x32000000
|| nAddress == 0x32500000
|| nAddress == 0x33500000
|| nAddress == 0x34000000
|| nAddress == 0x35000000
|| nAddress == 0x35500000
|| nAddress == 0x36000000
|| nAddress == 0x36500000
|| nAddress == 0x38000000
|| nAddress == 0x39000000)
{
putc((unsigned char)100, out); // blue DLL start
}
else
{
putc((unsigned char)0, out); // black
}
putc((unsigned char)0, out);
putc((unsigned char)0, out);
}
}
fclose(out);
m_dwLine = 0;
}
}
void LogCoverage(std::vector<bool>& vCov)
{
const size_t nCharPerLine = 128; // readable amount
char szResult[nCharPerLine + 1], * pCursor = szResult;
szResult[nCharPerLine] = 0; // zero termination
size_t nSize = vCov.size();
size_t nUnitsPerChar = nSize / nCharPerLine;
for (size_t i = 0; i < nSize; )
{
unsigned int nLocalCov = 0;
for (size_t e = 0; e < nUnitsPerChar; ++e, ++i)
{
if (vCov[i])
{
++nLocalCov;
}
}
if (nLocalCov == 0)
{
*pCursor++ = '#'; // occupied
}
else if (nLocalCov == nUnitsPerChar)
{
*pCursor++ = '.'; // free
}
else
{
*pCursor++ = '+'; // partly
}
}
CryLog(" Coverage=%s", szResult);
}
void DumpToRAWCoverage(std::vector<bool>& vCov)
{
FILE* out = nullptr;
azfopen(&out, "MemoryCoverage.bmp", "rb+");
if (!out)
{
return;
}
if (fseek(out, sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 3 * (m_nLineCount - 1 - m_dwLine) * m_nPixelsPerLine, SEEK_SET) != 0)
{
fclose(out);
return;
}
size_t nSize = vCov.size();
size_t nUnitsPerChar = nSize / m_nPixelsPerLine;
for (size_t i = 0; i < nSize; )
{
unsigned int nLocalCov = 0;
for (size_t e = 0; e < nUnitsPerChar; ++e, ++i)
{
if (vCov[i])
{
++nLocalCov;
}
}
size_t Val = 256 - (256 * nLocalCov) / nUnitsPerChar;
if (Val > 0)
{
Val = 127 + Val / 2;
}
putc((unsigned char)Val, out);
putc((unsigned char)Val, out);
putc((unsigned char)Val, out); // grey
}
fclose(out);
++m_dwLine;
}
unsigned int m_dwLine; // [0..m_nLineCount-1], m_nLineCount means bitmap is full, 0xffffffff means not initialized yet
static const size_t m_nPixelsPerLine = 1024; // bitmap width
static const size_t m_nLineCount = 128; //
};
#else // defined(WIN32) || defined(WIN64)
class CMemoryFragmentationProfiler
{
public:
void DumpMemoryCoverage() {}
};
#endif // defined(WIN32) || defined(WIN64)
#endif // CRYINCLUDE_CRYSYSTEM_MEMORYFRAGMENTATIONPROFILER_H
+224
View File
@@ -0,0 +1,224 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "MemoryManager.h"
#include "platform.h"
#include "CustomMemoryHeap.h"
#include "GeneralMemoryHeap.h"
#include "PageMappingHeap.h"
#include "DefragAllocator.h"
#if defined(AZ_RESTRICTED_PLATFORM)
#undef AZ_RESTRICTED_SECTION
#define MEMORYMANAGER_CPP_SECTION_1 1
#endif
#if defined(WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Psapi.h>
#endif
#if defined(APPLE)
#include <mach/mach.h> // task_info
#endif
#if defined(APPLE) || defined(LINUX)
#include <sys/types.h> // required by mman.h
#include <sys/mman.h> //mmap - virtual memory manager
#endif
#ifdef MEMMAN_STATIC
CCryMemoryManager g_memoryManager;
#endif
//////////////////////////////////////////////////////////////////////////
CCryMemoryManager* CCryMemoryManager::GetInstance()
{
#ifdef MEMMAN_STATIC
return &g_memoryManager;
#else
static CCryMemoryManager memman;
return &memman;
#endif
}
//////////////////////////////////////////////////////////////////////////
bool CCryMemoryManager::GetProcessMemInfo(SProcessMemInfo& minfo)
{
ZeroStruct(minfo);
#if defined(WIN32)
MEMORYSTATUSEX mem;
mem.dwLength = sizeof(mem);
GlobalMemoryStatusEx (&mem);
minfo.TotalPhysicalMemory = mem.ullTotalPhys;
minfo.FreePhysicalMemory = mem.ullAvailPhys;
//////////////////////////////////////////////////////////////////////////
typedef BOOL (WINAPI * GetProcessMemoryInfoProc)(HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD);
PROCESS_MEMORY_COUNTERS pc;
ZeroStruct(pc);
pc.cb = sizeof(pc);
static HMODULE hPSAPI = LoadLibraryA("psapi.dll");
if (hPSAPI)
{
static GetProcessMemoryInfoProc pGetProcessMemoryInfo = (GetProcessMemoryInfoProc)GetProcAddress(hPSAPI, "GetProcessMemoryInfo");
if (pGetProcessMemoryInfo)
{
if (pGetProcessMemoryInfo(GetCurrentProcess(), &pc, sizeof(pc)))
{
minfo.PageFaultCount = pc.PageFaultCount;
minfo.PeakWorkingSetSize = pc.PeakWorkingSetSize;
minfo.WorkingSetSize = pc.WorkingSetSize;
minfo.QuotaPeakPagedPoolUsage = pc.QuotaPeakPagedPoolUsage;
minfo.QuotaPagedPoolUsage = pc.QuotaPagedPoolUsage;
minfo.QuotaPeakNonPagedPoolUsage = pc.QuotaPeakNonPagedPoolUsage;
minfo.QuotaNonPagedPoolUsage = pc.QuotaNonPagedPoolUsage;
minfo.PagefileUsage = pc.PagefileUsage;
minfo.PeakPagefileUsage = pc.PeakPagefileUsage;
return true;
}
}
}
return false;
#define AZ_RESTRICTED_SECTION_IMPLEMENTED
#elif defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION MEMORYMANAGER_CPP_SECTION_1
#include AZ_RESTRICTED_FILE(MemoryManager_cpp)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#elif defined(LINUX)
MEMORYSTATUS MemoryStatus;
GlobalMemoryStatus(&MemoryStatus);
minfo.PagefileUsage = minfo.PeakPagefileUsage = MemoryStatus.dwTotalPhys - MemoryStatus.dwAvailPhys;
minfo.FreePhysicalMemory = MemoryStatus.dwAvailPhys;
minfo.TotalPhysicalMemory = MemoryStatus.dwTotalPhys;
#if defined(ANDROID)
// On Android, mallinfo() is an EXTREMELY time consuming operation. Nearly 80% CPU time will be spent
// on this operation once -memreplay is given. Since WorkingSetSize is only used for statistics and
// debugging purpose, it's simply ignored.
minfo.WorkingSetSize = 0;
#else
struct mallinfo meminfo = mallinfo();
minfo.WorkingSetSize = meminfo.usmblks + meminfo.uordblks;
#endif
#elif defined(APPLE)
MEMORYSTATUS MemoryStatus;
GlobalMemoryStatus(&MemoryStatus);
minfo.PagefileUsage = minfo.PeakPagefileUsage = MemoryStatus.dwTotalPhys - MemoryStatus.dwAvailPhys;
minfo.FreePhysicalMemory = MemoryStatus.dwAvailPhys;
minfo.TotalPhysicalMemory = MemoryStatus.dwTotalPhys;
// Retrieve WorkingSetSize from task_info
task_basic_info kTaskInfo;
mach_msg_type_number_t uInfoCount(sizeof(kTaskInfo) / sizeof(natural_t));
if (task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&kTaskInfo, &uInfoCount) != 0)
{
gEnv->pLog->LogError("task_info failed\n");
return false;
}
minfo.WorkingSetSize = kTaskInfo.resident_size;
#else
return false;
#endif
return true;
}
//////////////////////////////////////////////////////////////////////////
CCryMemoryManager::HeapHandle CCryMemoryManager::TraceDefineHeap([[maybe_unused]] const char* heapName, [[maybe_unused]] size_t size, [[maybe_unused]] const void* pBase)
{
return 0;
}
//////////////////////////////////////////////////////////////////////////
void CCryMemoryManager::TraceHeapAlloc([[maybe_unused]] HeapHandle heap, [[maybe_unused]] void* mem, [[maybe_unused]] size_t size, [[maybe_unused]] size_t blockSize, [[maybe_unused]] const char* sUsage, [[maybe_unused]] const char* sNameHint)
{
}
//////////////////////////////////////////////////////////////////////////
void CCryMemoryManager::TraceHeapFree([[maybe_unused]] HeapHandle heap, [[maybe_unused]] void* mem, [[maybe_unused]] size_t blockSize)
{
}
//////////////////////////////////////////////////////////////////////////
void CCryMemoryManager::TraceHeapSetColor([[maybe_unused]] uint32 color)
{
}
//////////////////////////////////////////////////////////////////////////
void CCryMemoryManager::TraceHeapSetLabel([[maybe_unused]] const char* sLabel)
{
}
//////////////////////////////////////////////////////////////////////////
uint32 CCryMemoryManager::TraceHeapGetColor()
{
return 0;
}
//////////////////////////////////////////////////////////////////////////
ICustomMemoryHeap* const CCryMemoryManager::CreateCustomMemoryHeapInstance(IMemoryManager::EAllocPolicy const eAllocPolicy)
{
return new CCustomMemoryHeap(eAllocPolicy);
}
IGeneralMemoryHeap* CCryMemoryManager::CreateGeneralExpandingMemoryHeap(size_t upperLimit, size_t reserveSize, const char* sUsage)
{
return new CGeneralMemoryHeap(static_cast<UINT_PTR>(0), upperLimit, reserveSize, sUsage);
}
IGeneralMemoryHeap* CCryMemoryManager::CreateGeneralMemoryHeap(void* base, size_t sz, const char* sUsage)
{
return new CGeneralMemoryHeap(base, sz, sUsage);
}
IMemoryAddressRange* CCryMemoryManager::ReserveAddressRange(size_t capacity, const char* sName)
{
return new CMemoryAddressRange(capacity, sName);
}
IPageMappingHeap* CCryMemoryManager::CreatePageMappingHeap(size_t addressSpace, const char* sName)
{
return new CPageMappingHeap(addressSpace, sName);
}
IDefragAllocator* CCryMemoryManager::CreateDefragAllocator()
{
return new CDefragAllocator();
}
extern "C"
{
CRYMEMORYMANAGER_API void CryGetIMemoryManagerInterface(void** pIMemoryManager)
{
// Static instance of the memory manager
*pIMemoryManager = CCryMemoryManager::GetInstance();
}
};
+54
View File
@@ -0,0 +1,54 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_MEMORYMANAGER_H
#define CRYINCLUDE_CRYSYSTEM_MEMORYMANAGER_H
#pragma once
#include "ISystem.h"
//////////////////////////////////////////////////////////////////////////
// Class that implements IMemoryManager interface.
//////////////////////////////////////////////////////////////////////////
#ifndef MEMMAN_STATIC
class CCryMemoryManager
: public IMemoryManager
{
public:
// Singleton
static CCryMemoryManager* GetInstance();
//////////////////////////////////////////////////////////////////////////
virtual bool GetProcessMemInfo(SProcessMemInfo& minfo);
virtual HeapHandle TraceDefineHeap(const char* heapName, size_t size, const void* pBase);
virtual void TraceHeapAlloc(HeapHandle heap, void* mem, size_t size, size_t blockSize, const char* sUsage, const char* sNameHint = 0);
virtual void TraceHeapFree(HeapHandle heap, void* mem, size_t blockSize);
virtual void TraceHeapSetColor(uint32 color);
virtual uint32 TraceHeapGetColor();
virtual void TraceHeapSetLabel(const char* sLabel);
virtual ICustomMemoryHeap* const CreateCustomMemoryHeapInstance(IMemoryManager::EAllocPolicy const eAllocPolicy);
virtual IGeneralMemoryHeap* CreateGeneralExpandingMemoryHeap(size_t upperLimit, size_t reserveSize, const char* sUsage);
virtual IGeneralMemoryHeap* CreateGeneralMemoryHeap(void* base, size_t sz, const char* sUsage);
virtual IMemoryAddressRange* ReserveAddressRange(size_t capacity, const char* sName);
virtual IPageMappingHeap* CreatePageMappingHeap(size_t addressSpace, const char* sName);
virtual IDefragAllocator* CreateDefragAllocator();
};
#else
typedef IMemoryManager CCryMemoryManager;
#endif
#endif // CRYINCLUDE_CRYSYSTEM_MEMORYMANAGER_H
@@ -0,0 +1,162 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "DrawContext.h"
#include <ISystem.h>
#include <IRenderer.h>
#include <IRenderAuxGeom.h>
MINIGUI_BEGIN
//////////////////////////////////////////////////////////////////////////
CDrawContext::CDrawContext(SMetrics* pMetrics)
{
m_currentStackLevel = 0;
m_x = 0;
m_y = 0;
m_pMetrics = pMetrics;
m_color = ColorB(0, 0, 0, 0);
m_defaultZ = 0.0f;
m_pAuxRender = gEnv->pRenderer->GetIRenderAuxGeom();
m_frameWidth = (float)gEnv->pRenderer->GetWidth();
m_frameHeight = (float)gEnv->pRenderer->GetHeight();
}
//////////////////////////////////////////////////////////////////////////
void CDrawContext::SetColor(ColorB color)
{
m_color = color;
}
//////////////////////////////////////////////////////////////////////////
void CDrawContext::DrawLine(float x0, float y0, float x1, float y1, float thickness /*= 1.0f */)
{
m_pAuxRender->DrawLine(Vec3(m_x + x0, m_y + y0, m_defaultZ), m_color, Vec3(m_x + x1, m_y + y1, m_defaultZ), m_color, thickness);
}
//////////////////////////////////////////////////////////////////////////
void CDrawContext::DrawTriangle(float x0, float y0, float x1, float y1, float x2, float y2)
{
m_pAuxRender->DrawTriangle(Vec3(m_x + x0, m_y + y0, m_defaultZ), m_color, Vec3(m_x + x1, m_y + y1, m_defaultZ), m_color, Vec3(m_x + x2, m_y + y2, m_defaultZ), m_color);
}
//////////////////////////////////////////////////////////////////////////
void CDrawContext::DrawRect(const Rect& rc)
{
m_pAuxRender->DrawTriangle(Vec3(m_x + rc.left, m_y + rc.top, m_defaultZ), m_color, Vec3(m_x + rc.left, m_y + rc.bottom, m_defaultZ), m_color, Vec3(m_x + rc.right, m_y + rc.top, m_defaultZ), m_color);
m_pAuxRender->DrawTriangle(Vec3(m_x + rc.left, m_y + rc.bottom, m_defaultZ), m_color, Vec3(m_x + rc.right, m_y + rc.bottom, m_defaultZ), m_color, Vec3(m_x + rc.right, m_y + rc.top, m_defaultZ), m_color);
}
//////////////////////////////////////////////////////////////////////////
void CDrawContext::DrawFrame(const Rect& rc, ColorB lineColor, ColorB solidColor, float thickness)
{
ColorB prevColor = m_color;
SetColor(solidColor);
DrawRect(rc);
SetColor(lineColor);
uint32 curFlags = m_pAuxRender->GetRenderFlags().m_renderFlags;
m_pAuxRender->SetRenderFlags(curFlags | e_DrawInFrontOn);
DrawLine(rc.left, rc.top, rc.right, rc.top, thickness);
DrawLine(rc.right, rc.top, rc.right, rc.bottom, thickness);
DrawLine(rc.left, rc.top, rc.left, rc.bottom, thickness);
DrawLine(rc.left, rc.bottom, rc.right, rc.bottom, thickness);
m_pAuxRender->SetRenderFlags(curFlags);
m_color = prevColor;
}
//////////////////////////////////////////////////////////////////////////
void CDrawContext::StartDrawing()
{
uint32 width = gEnv->pRenderer->GetWidth();
uint32 height = gEnv->pRenderer->GetHeight();
gEnv->pRenderer->Set2DMode(width, height, m_backupSceneMatrices);
m_prevRenderFlags = m_pAuxRender->GetRenderFlags().m_renderFlags;
m_pAuxRender->SetRenderFlags(e_Mode3D | e_AlphaBlended | e_FillModeSolid | e_CullModeBack | e_DepthWriteOff | e_DepthTestOff);
}
//////////////////////////////////////////////////////////////////////////
void CDrawContext::StopDrawing()
{
// Restore old flags that where set before our draw context.
m_pAuxRender->SetRenderFlags(m_prevRenderFlags);
int width = gEnv->pRenderer->GetWidth();
int height = gEnv->pRenderer->GetHeight();
gEnv->pRenderer->Unset2DMode(m_backupSceneMatrices);
}
//////////////////////////////////////////////////////////////////////////
void CDrawContext::DrawString(float x, float y, float font_size, ETextAlign align, const char* format, ...)
{
//text will be off screen
if (y > m_frameHeight || x > m_frameWidth)
{
return;
}
va_list args;
va_start(args, format);
SDrawTextInfo ti;
ti.xscale = ti.yscale = font_size / 12.0f; // font size in pixels to text scale.
ti.flags = eDrawText_Monospace | eDrawText_2D | eDrawText_FixedSize | eDrawText_IgnoreOverscan;
if (align == eTextAlign_Left)
{
}
else if (align == eTextAlign_Right)
{
ti.flags |= eDrawText_Right;
}
else if (align == eTextAlign_Center)
{
ti.flags |= eDrawText_Center;
}
ti.color[0] = (float)m_color.r / 255.0f;
ti.color[1] = (float)m_color.g / 255.0f;
ti.color[2] = (float)m_color.b / 255.0f;
ti.color[3] = (float)m_color.a / 255.0f;
gEnv->pRenderer->DrawTextQueued(Vec3(m_x + x, m_y + y, m_defaultZ), ti, format, args);
va_end(args);
}
//////////////////////////////////////////////////////////////////////////
void CDrawContext::PushClientRect(const Rect& rc)
{
m_currentStackLevel++;
assert(m_currentStackLevel < MAX_ORIGIN_STACK);
m_clientRectStack[m_currentStackLevel] = rc;
m_x += rc.left;
m_y += rc.top;
}
//////////////////////////////////////////////////////////////////////////
void CDrawContext::PopClientRect()
{
if (m_currentStackLevel > 0)
{
Rect& rc = m_clientRectStack[m_currentStackLevel];
m_x -= rc.left;
m_y -= rc.top;
m_currentStackLevel--;
}
}
MINIGUI_END
@@ -0,0 +1,89 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : DrawContext helper class for MiniGUI
#ifndef CRYINCLUDE_CRYSYSTEM_MINIGUI_DRAWCONTEXT_H
#define CRYINCLUDE_CRYSYSTEM_MINIGUI_DRAWCONTEXT_H
#pragma once
#include "ICryMiniGUI.h"
#include <Cry_Color.h>
struct IRenderAuxGeom;
MINIGUI_BEGIN
enum ETextAlign
{
eTextAlign_Left,
eTextAlign_Right,
eTextAlign_Center
};
//////////////////////////////////////////////////////////////////////////
// Context of MiniGUI drawing.
//////////////////////////////////////////////////////////////////////////
class CDrawContext
{
public:
CDrawContext(SMetrics* pMetrics);
// Must be called before any drawing happens
void StartDrawing();
// Must be called after all drawing have been complete.
void StopDrawing();
void PushClientRect(const Rect& rc);
void PopClientRect();
SMetrics& Metrics() { return *m_pMetrics; }
void SetColor(ColorB color);
void DrawLine(float x0, float y0, float x1, float y1, float thickness = 1.0f);
void DrawTriangle(float x0, float y0, float x1, float y1, float x2, float y2);
void DrawRect(const Rect& rc);
void DrawFrame(const Rect& rc, ColorB lineColor, ColorB solidColor, float thickness = 1.0f);
void DrawString(float x, float y, float font_size, ETextAlign align, const char* format, ...);
protected:
SMetrics* m_pMetrics;
ColorB m_color;
float m_defaultZ;
IRenderAuxGeom* m_pAuxRender;
uint32 m_prevRenderFlags;
enum
{
MAX_ORIGIN_STACK = 16
};
int m_currentStackLevel;
float m_x, m_y; // Reference X,Y positions
Rect m_clientRectStack[MAX_ORIGIN_STACK];
float m_frameWidth;
float m_frameHeight;
private:
TransformationMatrices m_backupSceneMatrices;
};
MINIGUI_END
#endif // CRYINCLUDE_CRYSYSTEM_MINIGUI_DRAWCONTEXT_H
@@ -0,0 +1,316 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Button implementation in the MiniGUI
#include "CrySystem_precompiled.h"
#include "MiniButton.h"
#include "DrawContext.h"
#include <ISystem.h>
#include <IConsole.h>
MINIGUI_BEGIN
CMiniButton::CMiniButton()
{
m_pCVar = NULL;
m_fCVarValue[0] = 0;
m_fCVarValue[1] = 1;
m_saveStateOn = false;
m_clickCallback = NULL;
m_pCallbackData = NULL;
m_pConnectedCtrl = NULL;
}
//////////////////////////////////////////////////////////////////////////
void CMiniButton::Reset()
{
/*if (m_pCVar)
{
pCVar->Set( m_fCVarValue[0] );
}*/
/*if(m_pConnectedCtrl)
{
m_pConnectedCtrl->SetVisible(false);
}*/
clear_flag(eCtrl_Checked);
}
//////////////////////////////////////////////////////////////////////////
void CMiniButton::SaveState()
{
if (is_flag(eCtrl_Checked))
{
if (m_pConnectedCtrl && m_pConnectedCtrl->CheckFlag(eCtrl_Hidden))
{
m_saveStateOn = false;
}
else
{
m_saveStateOn = true;
}
}
else
{
m_saveStateOn = false;
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniButton::RestoreState()
{
if (m_pCVar)
{
if (m_pCVar->GetFVal() == m_fCVarValue[1])
{
set_flag(eCtrl_Checked);
//Restoring CVars has caused a few issues, especially when the user is changing
//CVars through the console while perfHud is active, removing for now
//pCVar->Set( m_saveStateOn ? m_fCVarValue[1] : m_fCVarValue[0] );
}
else
{
clear_flag(eCtrl_Checked);
}
}
else
{
//connected controls (tables / info boxes etc) now look after themselves
/*if(m_pConnectedCtrl)
{
m_pConnectedCtrl->SetVisible(m_saveStateOn);
}*/
if (CheckFlag(eCtrl_CheckButton))
{
if (m_saveStateOn)
{
set_flag(eCtrl_Checked);
}
else
{
clear_flag(eCtrl_Checked);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniButton::OnPaint(CDrawContext& dc)
{
ColorB bkgColor = dc.Metrics().clrBackground;
if (is_flag(eCtrl_Highlight))
{
bkgColor = dc.Metrics().clrBackgroundHighlight;
}
else if (is_flag(eCtrl_Checked))
{
//if connected control has been hidden, this button should not be checked
if (m_pConnectedCtrl && m_pConnectedCtrl->CheckFlag(eCtrl_Hidden))
{
clear_flag(eCtrl_Checked);
}
else
{
bkgColor = dc.Metrics().clrBackgroundSelected;
}
}
float borderThickness = 1.0f;
if (is_flag(eCtrl_Focus))
{
borderThickness = 3.0f;
}
ColorB borderCol = dc.Metrics().clrFrameBorder;
if (!m_pGUI->InFocus())
{
borderCol = dc.Metrics().clrFrameBorderOutOfFocus;
bkgColor.a = dc.Metrics().outOfFocusAlpha;
}
dc.DrawFrame(m_rect, borderCol, bkgColor, borderThickness);
ColorB textColor = dc.Metrics().clrText;
if (is_flag(eCtrl_Checked | eCtrl_Highlight))
{
textColor = dc.Metrics().clrTextSelected;
}
dc.SetColor(textColor);
ETextAlign align;
float startX;
if (is_flag(eCtrl_TextAlignCentre))
{
startX = (m_rect.left + m_rect.right) / 2.f;
align = eTextAlign_Center;
}
else
{
startX = m_rect.left + 5.f;
align = eTextAlign_Left;
}
dc.DrawString(startX, m_rect.top, dc.Metrics().fTitleSize, align, GetTitle());
//Check not very obvious
#if 0
if (is_flag(eCtrl_Checked))
{
// Draw checked mark.
float checkX = m_rect.left + 4;
float checkY = (m_rect.bottom + m_rect.top) * 0.5f;
dc.SetColor(dc.Metrics().clrChecked);
dc.DrawLine(checkX, checkY, checkX + 3, checkY + 3);
dc.DrawLine(checkX + 3, checkY + 3, checkX + 7, checkY - 6);
}
#endif
}
//////////////////////////////////////////////////////////////////////////
void CMiniButton::SetRect(const Rect& rc)
{
Rect newrc = rc;
newrc.bottom = newrc.top + m_pGUI->Metrics().fTitleSize + 2;
CMiniCtrl::SetRect(newrc);
}
//////////////////////////////////////////////////////////////////////////
void CMiniButton::OnEvent(float x, float y, EMiniCtrlEvent event)
{
switch (event)
{
case eCtrlEvent_LButtonDown:
{
SCommand cmd;
if (CheckFlag(eCtrl_CheckButton))
{
if (!CheckFlag(eCtrl_Checked))
{
cmd.command = eCommand_ButtonChecked;
}
else
{
cmd.command = eCommand_ButtonUnchecked;
}
}
else
{
cmd.command = eCommand_ButtonPress;
}
cmd.nCtrlID = GetId();
cmd.pCtrl = this;
GetGUI()->OnCommand(cmd);
if (CheckFlag(eCtrl_CheckButton))
{
bool bOn = false;
if (CheckFlag(eCtrl_Checked))
{
bOn = false;
ClearFlag(eCtrl_Checked);
}
else
{
bOn = true;
SetFlag(eCtrl_Checked);
}
if (m_pCVar)
{
m_pCVar->Set(m_fCVarValue[bOn ? 1 : 0]);
}
if (m_pConnectedCtrl)
{
m_pConnectedCtrl->SetVisible(bOn);
}
}
else
{
//cross button behavior
if (m_pConnectedCtrl)
{
m_pConnectedCtrl->SetVisible(false);
}
}
if (m_clickCallback)
{
m_clickCallback(m_pCallbackData, true);
}
}
break;
case eCtrlEvent_MouseOff:
{
if (m_pParent)
{
m_pParent->OnEvent(x, y, eCtrlEvent_MouseOff);
}
}
break;
}
}
//////////////////////////////////////////////////////////////////////////
bool CMiniButton::SetControlCVar(const char* sCVarName, float fOffValue, float fOnValue)
{
m_pCVar = GetISystem()->GetIConsole()->GetCVar(sCVarName);
if (!m_pCVar)
{
CryLogAlways("failed to find CVar: %s\n", sCVarName);
}
m_fCVarValue[0] = fOffValue;
m_fCVarValue[1] = fOnValue;
if (m_pCVar && m_pCVar->GetFVal() == fOnValue)
{
set_flag(eCtrl_Checked);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
bool CMiniButton::SetClickCallback(ClickCallback callback, void* pCallbackData)
{
m_clickCallback = callback;
m_pCallbackData = pCallbackData;
return true;
}
//////////////////////////////////////////////////////////////////////////
bool CMiniButton::SetConnectedCtrl(IMiniCtrl* pConnectedCtrl)
{
m_pConnectedCtrl = pConnectedCtrl;
return true;
}
MINIGUI_END
@@ -0,0 +1,60 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Button implementation in the MiniGUI
#ifndef CRYINCLUDE_CRYSYSTEM_MINIGUI_MINIBUTTON_H
#define CRYINCLUDE_CRYSYSTEM_MINIGUI_MINIBUTTON_H
#pragma once
#include "MiniGUI.h"
MINIGUI_BEGIN
//////////////////////////////////////////////////////////////////////////
// Root window all other controls derive from
class CMiniButton
: public CMiniCtrl
{
public:
CMiniButton();
//////////////////////////////////////////////////////////////////////////
// CMiniCtrl interface implementation.
//////////////////////////////////////////////////////////////////////////
virtual EMiniCtrlType GetType() const { return eCtrlType_Button; }
virtual void SetRect(const Rect& rc);
virtual void OnPaint(CDrawContext& dc);
virtual void OnEvent(float x, float y, EMiniCtrlEvent event);
virtual void Reset();
virtual void SaveState();
virtual void RestoreState();
//////////////////////////////////////////////////////////////////////////
virtual bool SetControlCVar(const char* sCVarName, float fOffValue, float fOnValue);
virtual bool SetClickCallback(ClickCallback callback, void* pCallbackData);
virtual bool SetConnectedCtrl(IMiniCtrl* pConnectedCtrl);
protected:
ICVar* m_pCVar;
float m_fCVarValue[2];
ClickCallback m_clickCallback;
void* m_pCallbackData;
IMiniCtrl* m_pConnectedCtrl;
};
MINIGUI_END
#endif // CRYINCLUDE_CRYSYSTEM_MINIGUI_MINIBUTTON_H
@@ -0,0 +1,862 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Implementation of the MiniGUI class
#include "CrySystem_precompiled.h"
#include "MiniGUI.h"
#include "DrawContext.h"
#include "MiniButton.h"
#include "MiniMenu.h"
#include "MiniInfoBox.h"
#include "MiniTable.h"
#include <ISystem.h>
#include <IRenderer.h>
#include <LyShine/Bus/UiCursorBus.h>
#include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
namespace minigui
{
CRYREGISTER_SINGLETON_CLASS(CMiniGUI)
}
MINIGUI_BEGIN
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::InitMetrics()
{
m_metrics.clrText = ColorB(255, 255, 255, 255);
m_metrics.clrTextSelected = ColorB(0, 255, 0, 255);
m_metrics.fTextSize = 12.0f;
m_metrics.clrTitle = ColorB(255, 255, 255, 255);
m_metrics.fTitleSize = 14.0f;
const int backgroundAlpha = 255;
m_metrics.clrBackground = ColorB(20, 20, 20, backgroundAlpha);
m_metrics.clrBackgroundHighlight = ColorB(10, 10, 150, backgroundAlpha);
m_metrics.clrBackgroundSelected = ColorB(10, 120, 10, backgroundAlpha);
m_metrics.clrFrameBorder = ColorB(255, 0, 0, 255);
m_metrics.clrFrameBorderHighlight = ColorB(255, 255, 0, 255);
m_metrics.clrFrameBorderOutOfFocus = ColorB(0, 0, 0, 255);
m_metrics.clrChecked = ColorB(0, 0, 0, 255);
m_metrics.outOfFocusAlpha = 32;
}
class CMiniCtrlRoot
: public CMiniCtrl
{
public:
CMiniCtrlRoot() {};
virtual EMiniCtrlType GetType() const { return eCtrlType_Unknown; };
virtual void OnPaint([[maybe_unused]] class CDrawContext& dc) {};
};
//////////////////////////////////////////////////////////////////////////
CMiniGUI::CMiniGUI()
: m_enabled(false)
, m_inFocus(true)
, m_pDPadMenu(NULL)
, m_pMovingCtrl(NULL)
{
}
//////////////////////////////////////////////////////////////////////////
CMiniGUI::~CMiniGUI()
{
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::Init()
{
m_pEventListener = NULL;
InitMetrics();
AzFramework::InputChannelEventListener::Connect();
m_pRootCtrl = new CMiniCtrlRoot;
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::Reset()
{
m_pRootCtrl->Reset();
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::SaveState()
{
m_pRootCtrl->SaveState();
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::RestoreState()
{
m_pRootCtrl->RestoreState();
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::SetEnabled(bool status)
{
m_enabled = status;
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::SetInFocus(bool status)
{
if (status)
{
m_inFocus = true;
}
else
{
CloseDPadMenu();
m_inFocus = false;
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::Done()
{
AzFramework::InputChannelEventListener::Disconnect();
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::Draw()
{
FUNCTION_PROFILER_FAST(GetISystem(), PROFILE_SYSTEM, g_bProfilerEnabled);
// When console opened hide MiniGui
bool bConsoleOpened = gEnv->pConsole->IsOpened();
if (m_enabled && !bConsoleOpened)
{
ProcessInput();
CDrawContext dc(&m_metrics);
dc.StartDrawing();
{
// Draw all controls.
m_pRootCtrl->DrawCtrl(dc);
}
dc.StopDrawing();
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::ProcessInput()
{
//check we are not in digital selection mode
if (!m_pDPadMenu)
{
float mx(0), my(0);
AZ::Vector2 systemCursorPositionNormalized = AZ::Vector2::CreateZero();
AzFramework::InputSystemCursorRequestBus::EventResult(systemCursorPositionNormalized,
AzFramework::InputDeviceMouse::Id,
&AzFramework::InputSystemCursorRequests::GetSystemCursorPositionNormalized);
mx = systemCursorPositionNormalized.GetX() * gEnv->pRenderer->GetWidth();
my = systemCursorPositionNormalized.GetY() * gEnv->pRenderer->GetHeight();
//update moving control
if (m_pMovingCtrl)
{
m_pMovingCtrl->Move(mx, my);
}
IMiniCtrl* pCtrl = GetCtrlFromPoint(mx, my);
if (pCtrl)
{
SetHighlight(pCtrl, true, mx, my);
}
else
{
SetHighlight(m_highlightedCtrl, false, mx, my);
}
}
}
//////////////////////////////////////////////////////////////////////////
IMiniCtrl* CMiniGUI::GetCtrlFromPoint(float x, float y) const
{
// Draw all controls.
return m_pRootCtrl->GetCtrlFromPoint(x, y);
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::SetHighlight(IMiniCtrl* pCtrl, bool bEnable, float x, float y)
{
if (pCtrl)
{
if (m_highlightedCtrl && m_highlightedCtrl != pCtrl)
{
m_highlightedCtrl->OnEvent(x, y, eCtrlEvent_MouseOff);
m_highlightedCtrl->ClearFlag(eCtrl_Highlight);
}
if (bEnable)
{
pCtrl->OnEvent(x, y, eCtrlEvent_MouseOver);
pCtrl->SetFlag(eCtrl_Highlight);
m_highlightedCtrl = pCtrl;
}
else
{
pCtrl->OnEvent(x, y, eCtrlEvent_MouseOff);
pCtrl->ClearFlag(eCtrl_Highlight);
m_highlightedCtrl = NULL;
}
}
else
{
assert(bEnable == false);
if (m_highlightedCtrl)
{
m_highlightedCtrl->OnEvent(x, y, eCtrlEvent_MouseOff);
m_highlightedCtrl->ClearFlag(eCtrl_Highlight);
m_highlightedCtrl = NULL;
}
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::SetFocus(IMiniCtrl* pCtrl, bool bEnable)
{
if (m_focusCtrl)
{
m_focusCtrl->ClearFlag(eCtrl_Focus);
}
m_focusCtrl = pCtrl;
if (m_focusCtrl)
{
if (bEnable)
{
m_focusCtrl->SetFlag(eCtrl_Focus);
}
else
{
m_focusCtrl->ClearFlag(eCtrl_Focus);
}
}
}
//////////////////////////////////////////////////////////////////////////
SMetrics& CMiniGUI::Metrics()
{
return m_metrics;
}
//////////////////////////////////////////////////////////////////////////
IMiniCtrl* CMiniGUI::CreateCtrl(IMiniCtrl* pParentCtrl, int nCtrlID, EMiniCtrlType type, int nCtrlFlags, const Rect& rc, const char* title)
{
CMiniCtrl* pCtrl = 0;
// Test code.
switch (type)
{
case eCtrlType_Button:
pCtrl = new CMiniButton;
break;
case eCtrlType_Menu:
pCtrl = new CMiniMenu;
break;
case eCtrlType_InfoBox:
pCtrl = new CMiniInfoBox;
break;
case eCtrlType_Table:
pCtrl = new CMiniTable;
break;
default:
assert(0 && "Unknown MiniGUI control type");
break;
}
;
if (pCtrl)
{
pCtrl->SetGUI(this);
pCtrl->SetFlag(nCtrlFlags);
pCtrl->SetTitle(title);
pCtrl->SetRect(rc);
pCtrl->SetId(nCtrlID);
if (pCtrl->CheckFlag(eCtrl_AutoResize))
{
pCtrl->AutoResize();
}
if (pCtrl->CheckFlag(eCtrl_CloseButton))
{
pCtrl->CreateCloseButton();
}
if (pParentCtrl)
{
pParentCtrl->AddSubCtrl(pCtrl);
}
else
{
m_pRootCtrl->AddSubCtrl(pCtrl);
}
if (type == eCtrlType_Menu && pParentCtrl == NULL)
{
m_rootMenus.push_back(pCtrl);
}
}
return pCtrl;
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::OnCommand(SCommand& cmd)
{
if (m_pEventListener)
{
m_pEventListener->OnCommand(cmd);
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::OnMouseInputEvent(const AzFramework::InputChannel& inputChannel)
{
if (!m_inFocus || !m_enabled)
{
return;
}
const AzFramework::InputChannel::PositionData2D* positionData2D = inputChannel.GetCustomData<AzFramework::InputChannel::PositionData2D>();
if (!positionData2D)
{
return;
}
const float mx = positionData2D->m_normalizedPosition.GetX() * gEnv->pRenderer->GetWidth();
const float my = positionData2D->m_normalizedPosition.GetY() * gEnv->pRenderer->GetHeight();
IMiniCtrl* pCtrl = GetCtrlFromPoint(mx, my);
if (pCtrl)
{
const AzFramework::InputChannelId& channelId = inputChannel.GetInputChannelId();
if (channelId == AzFramework::InputDeviceMouse::Button::Left)
{
if (inputChannel.IsStateBegan())
{
pCtrl->OnEvent(mx, my, eCtrlEvent_LButtonDown);
}
else if (inputChannel.IsStateEnded())
{
pCtrl->OnEvent(mx, my, eCtrlEvent_LButtonUp);
}
}
}
}
void CMiniGUI::SetDPadMenu(IMiniCtrl* pMenu)
{
m_pDPadMenu = (CMiniMenu*)pMenu;
UiCursorBus::Broadcast(&UiCursorInterface::DecrementVisibleCounter);
}
void CMiniGUI::CloseDPadMenu()
{
if (m_pDPadMenu)
{
CMiniMenu* closeMenu = m_pDPadMenu;
//close menu and all parent menus
do
{
closeMenu->Close();
closeMenu = (CMiniMenu*)closeMenu->GetParent();
} while (closeMenu->GetType() == eCtrlType_Menu);
m_pDPadMenu->ClearFlag(eCtrl_Highlight);
m_pDPadMenu = NULL;
UiCursorBus::Broadcast(&UiCursorInterface::IncrementVisibleCounter);
}
}
void CMiniGUI::UpdateDPadMenu(const AzFramework::InputChannel& inputChannel)
{
const AzFramework::InputChannelId& channelId = inputChannel.GetInputChannelId();
const bool isPressed = inputChannel.IsStateBegan();
if (m_pDPadMenu)
{
if (channelId == AzFramework::InputDeviceGamepad::Button::B)
{
CloseDPadMenu();
return;
}
if (isPressed)
{
if (channelId == AzFramework::InputDeviceGamepad::Button::DD ||
channelId == AzFramework::InputDeviceGamepad::ThumbStickDirection::LD)
{
m_pDPadMenu = m_pDPadMenu->UpdateSelection(eCtrlEvent_DPadDown);
}
else if (channelId == AzFramework::InputDeviceGamepad::Button::DU ||
channelId == AzFramework::InputDeviceGamepad::ThumbStickDirection::LU)
{
m_pDPadMenu = m_pDPadMenu->UpdateSelection(eCtrlEvent_DPadUp);
}
else if (channelId == AzFramework::InputDeviceGamepad::Button::DL ||
channelId == AzFramework::InputDeviceGamepad::ThumbStickDirection::LL)
{
CMiniMenu* pNewMenu = m_pDPadMenu->UpdateSelection(eCtrlEvent_DPadLeft);
//get previous root menu
if (pNewMenu == NULL)
{
int i = 0, nRootMenus = m_rootMenus.size();
for (i = 0; i < nRootMenus; i++)
{
if (m_rootMenus[i] == m_pDPadMenu)
{
break;
}
}
if (i > 0)
{
m_pDPadMenu->Close();
m_pDPadMenu->ClearFlag(eCtrl_Highlight);
m_pDPadMenu = (CMiniMenu*)m_rootMenus[i - 1];
m_pDPadMenu->Open();
m_pDPadMenu->SetFlag(eCtrl_Highlight);
}
//else selected menu remains the same
}
else
{
m_pDPadMenu = pNewMenu;
}
}
else if (channelId == AzFramework::InputDeviceGamepad::Button::DR ||
channelId == AzFramework::InputDeviceGamepad::ThumbStickDirection::LR)
{
CMiniMenu* pNewMenu = m_pDPadMenu->UpdateSelection(eCtrlEvent_DPadRight);
//get next root menu
if (pNewMenu == NULL)
{
int i = 0, nRootMenus = m_rootMenus.size();
for (i = 0; i < nRootMenus; i++)
{
if (m_rootMenus[i] == m_pDPadMenu)
{
break;
}
}
if (i < nRootMenus - 1)
{
m_pDPadMenu->Close();
m_pDPadMenu->ClearFlag(eCtrl_Highlight);
m_pDPadMenu = (CMiniMenu*)m_rootMenus[i + 1];
m_pDPadMenu->Open();
m_pDPadMenu->SetFlag(eCtrl_Highlight);
}
//else selected menu remains the same
}
else
{
m_pDPadMenu = pNewMenu;
}
}
else if (channelId == AzFramework::InputDeviceGamepad::Button::A)
{
m_pDPadMenu = m_pDPadMenu->UpdateSelection(eCtrlEvent_LButtonDown);
}
}
}
}
bool CMiniGUI::OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel)
{
const AzFramework::InputDeviceId& deviceId = inputChannel.GetInputDevice().GetInputDeviceId();
if (AzFramework::InputDeviceMouse::IsMouseDevice(deviceId))
{
OnMouseInputEvent(inputChannel);
return false;
}
if (!m_inFocus)
{
return false;
}
if (!AzFramework::InputDeviceGamepad::IsGamepadDevice(deviceId))
{
return false;
}
if (m_pDPadMenu)
{
UpdateDPadMenu(inputChannel);
}
else
{
float posX = 0.0f;
float posY = 0.0f;
const AzFramework::InputChannel::PositionData2D* positionData2D = inputChannel.GetCustomData<AzFramework::InputChannel::PositionData2D>();
if (positionData2D)
{
posX = positionData2D->m_normalizedPosition.GetX() * gEnv->pRenderer->GetWidth();
posY = positionData2D->m_normalizedPosition.GetY() * gEnv->pRenderer->GetHeight();
}
IMiniCtrl* pCtrl = GetCtrlFromPoint(posX, posY);
if (pCtrl)
{
const AzFramework::InputChannelId& channelId = inputChannel.GetInputChannelId();
if (channelId == AzFramework::InputDeviceGamepad::Button::A)
{
switch (inputChannel.GetState())
{
case AzFramework::InputChannel::State::Began:
pCtrl->OnEvent(posX, posY, eCtrlEvent_LButtonDown);
break;
case AzFramework::InputChannel::State::Ended:
pCtrl->OnEvent(posX, posY, eCtrlEvent_LButtonUp);
break;
case AzFramework::InputChannel::State::Updated:
pCtrl->OnEvent(posX, posY, eCtrlEvent_LButtonPressed);
//if we've clicked on a menu, enter menu selection mode, disable mouse
if (pCtrl->GetType() == eCtrlType_Menu)
{
SetDPadMenu(pCtrl);
}
break;
}
}
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::SetEventListener(IMiniGUIEventListener* pListener)
{
m_pEventListener = pListener;
}
//////////////////////////////////////////////////////////////////////////
void CMiniGUI::RemoveAllCtrl()
{
m_highlightedCtrl = NULL;
//reset all console variables to default state
Reset();
m_pRootCtrl->RemoveAllSubCtrl();
}
//////////////////////////////////////////////////////////////////////////
//CMiniCtrl
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::Reset()
{
for (int i = 0, num = GetSubCtrlCount(); i < num; i++)
{
IMiniCtrl* pSubCtrl = GetSubCtrl(i);
pSubCtrl->Reset();
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::SaveState()
{
for (int i = 0, num = GetSubCtrlCount(); i < num; i++)
{
IMiniCtrl* pSubCtrl = GetSubCtrl(i);
pSubCtrl->SaveState();
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::RestoreState()
{
for (int i = 0, num = GetSubCtrlCount(); i < num; i++)
{
IMiniCtrl* pSubCtrl = GetSubCtrl(i);
pSubCtrl->RestoreState();
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::AddSubCtrl(IMiniCtrl* pCtrl)
{
assert(pCtrl);
_smart_ptr<IMiniCtrl> pTempCtrl(pCtrl);
IMiniCtrl* pParent = pCtrl->GetParent();
if (pParent)
{
pParent->RemoveSubCtrl(pCtrl);
}
static_cast<CMiniCtrl*>(pCtrl)->m_pParent = this;
m_subCtrls.push_back(pCtrl);
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::RemoveSubCtrl(IMiniCtrl* pCtrl)
{
assert(pCtrl);
_smart_ptr<IMiniCtrl> pTempCtrl(pCtrl);
IMiniCtrl* pParent = pCtrl->GetParent();
if (pParent == this)
{
static_cast<CMiniCtrl*>(pCtrl)->m_pParent = 0;
for (int i = 0, num = (int)m_subCtrls.size(); i < num; i++)
{
if (m_subCtrls[i] == pCtrl)
{
m_subCtrls.erase(m_subCtrls.begin() + i);
break;
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::RemoveAllSubCtrl()
{
int nSubCtrls = m_subCtrls.size();
if (nSubCtrls)
{
for (int i = 0; i < nSubCtrls; i++)
{
IMiniCtrl* pSubCtrl = m_subCtrls[i].get();
pSubCtrl->RemoveAllSubCtrl();
}
m_subCtrls.clear();
}
}
//////////////////////////////////////////////////////////////////////////
IMiniCtrl* CMiniCtrl::GetCtrlFromPoint(float x, float y)
{
if (is_flag(eCtrl_Hidden))
{
return 0;
}
for (int i = 0, num = (int)m_subCtrls.size(); i < num; i++)
{
float lx = x - m_rect.left;
float ly = y - m_rect.top;
IMiniCtrl* pHit = m_subCtrls[i]->GetCtrlFromPoint(lx, ly);
if (pHit)
{
return pHit;
}
}
if (m_rect.IsPointInside(x, y))
{
return this;
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::DrawCtrl(CDrawContext& dc)
{
OnPaint(dc);
dc.PushClientRect(m_rect);
for (int i = 0, num = (int)m_subCtrls.size(); i < num; i++)
{
CMiniCtrl* pCtrl = static_cast<CMiniCtrl*>((IMiniCtrl*)m_subCtrls[i]);
if (!pCtrl->is_flag(eCtrl_Hidden))
{
pCtrl->DrawCtrl(dc);
}
}
dc.PopClientRect();
}
//////////////////////////////////////////////////////////////////////////
int CMiniCtrl::GetSubCtrlCount() const
{
return (int)m_subCtrls.size();
}
//////////////////////////////////////////////////////////////////////////
IMiniCtrl* CMiniCtrl::GetSubCtrl(int nIndex) const
{
assert(nIndex >= 0 && nIndex < (int)m_subCtrls.size());
return m_subCtrls[nIndex];
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::SetRect(const Rect& rc)
{
m_rect = rc;
if (m_pCloseButton)
{
float width = rc.Width();
//relative position of cross box
Rect closeRect(width - 20.f, 0.f, width, 20.f);
m_pCloseButton->SetRect(closeRect);
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::SetVisible(bool state)
{
if (state)
{
clear_flag(eCtrl_Hidden);
}
else
{
set_flag(eCtrl_Hidden);
}
if (m_pCloseButton)
{
m_pCloseButton->SetVisible(state);
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::AutoResize()
{
uint32 stringLen = m_title.length();
if (stringLen)
{
//just an approximation for now - should take into account font size / kerning
m_rect.right = m_rect.left + (8.5f * stringLen);
}
m_requiresResize = false;
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::StartMoving(float x, float y)
{
if (!m_moving)
{
m_prevX = x;
m_prevY = y;
m_moving = true;
m_pGUI->SetMovingCtrl(this);
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::StopMoving()
{
if (m_moving)
{
m_moving = false;
m_pGUI->SetMovingCtrl(NULL);
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::Move(float x, float y)
{
if (m_moving)
{
float moveX = x - m_prevX;
float moveY = y - m_prevY;
m_rect.top += moveY;
m_rect.bottom += moveY;
m_rect.left += moveX;
m_rect.right += moveX;
m_prevX = x;
m_prevY = y;
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::OnEvent(float x, float y, EMiniCtrlEvent event)
{
switch (event)
{
case eCtrlEvent_LButtonDown:
{
if (is_flag(eCtrl_Highlight | eCtrl_Moveable))
{
StartMoving(x, y);
}
}
break;
case eCtrlEvent_LButtonUp:
if (m_moving)
{
StopMoving();
}
break;
case eCtrlEvent_MouseOver:
break;
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniCtrl::CreateCloseButton()
{
if (m_pGUI)
{
m_pCloseButton = m_pGUI->CreateCtrl(this, 100, eCtrlType_Button, 0, Rect(0, 0, 100, 20), "X");
if (m_pCloseButton)
{
m_pCloseButton->SetConnectedCtrl(this);
float width = m_rect.Width();
//relative position of cross box
Rect closeRect(width - 20.f, 0.f, width, 20.f);
m_pCloseButton->SetRect(closeRect);
}
}
}
MINIGUI_END
+219
View File
@@ -0,0 +1,219 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Interface to the Mini GUI subsystem
#ifndef CRYINCLUDE_CRYSYSTEM_MINIGUI_MINIGUI_H
#define CRYINCLUDE_CRYSYSTEM_MINIGUI_MINIGUI_H
#pragma once
#include "ICryMiniGUI.h"
#include <Cry_Color.h>
#include <AzFramework/Input/Events/InputChannelEventListener.h>
#include <CryExtension/Impl/ClassWeaver.h>
MINIGUI_BEGIN
class CMiniMenu;
//////////////////////////////////////////////////////////////////////////
// Root window all other controls derive from
class CMiniCtrl
: public IMiniCtrl
{
public:
CMiniCtrl()
: m_nFlags(0)
, m_id(0)
, m_renderCallback(NULL)
, m_fTextSize(12.f)
, m_prevX(0.f)
, m_prevY(0.f)
, m_moving(false)
, m_requiresResize(false)
, m_pCloseButton(NULL)
, m_saveStateOn(false)
{};
//////////////////////////////////////////////////////////////////////////
// IMiniCtrl interface implementation.
//////////////////////////////////////////////////////////////////////////
virtual void Reset();
virtual void SaveState();
virtual void RestoreState();
virtual void SetGUI(IMiniGUI* pGUI) { m_pGUI = pGUI; };
virtual IMiniGUI* GetGUI() const { return m_pGUI; };
virtual int GetId() const { return m_id; };
virtual void SetId(int id) { m_id = id; };
virtual const char* GetTitle() const { return m_title; };
virtual void SetTitle(const char* title) { m_title = title; };
virtual Rect GetRect() const { return m_rect; }
virtual void SetRect(const Rect& rc);
virtual void SetFlag(uint32 flag) { set_flag(flag); }
virtual void ClearFlag(uint32 flag) { clear_flag(flag); };
virtual bool CheckFlag(uint32 flag) const { return is_flag(flag); }
virtual void AddSubCtrl(IMiniCtrl* pCtrl);
virtual void RemoveSubCtrl(IMiniCtrl* pCtrl);
virtual void RemoveAllSubCtrl();
virtual int GetSubCtrlCount() const;
virtual IMiniCtrl* GetSubCtrl(int nIndex) const;
virtual IMiniCtrl* GetParent() const { return m_pParent; };
virtual IMiniCtrl* GetCtrlFromPoint(float x, float y);
virtual void SetVisible(bool state);
virtual void OnEvent(float x, float y, EMiniCtrlEvent);
virtual bool SetRenderCallback(RenderCallback callback) { m_renderCallback = callback; return true; };
// Not implemented in base control
virtual bool SetControlCVar([[maybe_unused]] const char* sCVarName, [[maybe_unused]] float fOffValue, [[maybe_unused]] float fOnValue) { assert(0); return false; };
virtual bool SetClickCallback([[maybe_unused]] ClickCallback callback, [[maybe_unused]] void* pCallbackData) { assert(0); return false; };
virtual bool SetConnectedCtrl([[maybe_unused]] IMiniCtrl* pConnectedCtrl) { assert(0); return false; };
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
virtual void AutoResize();
//////////////////////////////////////////////////////////////////////////
virtual void CreateCloseButton();
void DrawCtrl(CDrawContext& dc);
virtual void Move(float x, float y);
protected:
void set_flag(uint32 flag) { m_nFlags |= flag; }
void clear_flag(uint32 flag) { m_nFlags &= ~flag; };
bool is_flag(uint32 flag) const { return (m_nFlags & flag) == flag; }
//dynamic movement
void StartMoving(float x, float y);
void StopMoving();
protected:
int m_id;
IMiniGUI* m_pGUI;
uint32 m_nFlags;
CryFixedStringT<32> m_title;
Rect m_rect;
_smart_ptr<IMiniCtrl> m_pParent;
std::vector<IMiniCtrlPtr> m_subCtrls;
RenderCallback m_renderCallback;
float m_fTextSize;
//optional close 'X' button on controls, ref counted by m_subCtrls
IMiniCtrl* m_pCloseButton;
//dynamic movement
float m_prevX;
float m_prevY;
bool m_moving;
bool m_requiresResize;
bool m_saveStateOn;
};
//////////////////////////////////////////////////////////////////////////
class CMiniGUI
: public IMiniGUI
, public AzFramework::InputChannelEventListener
{
public:
CRYINTERFACE_BEGIN()
CRYINTERFACE_ADD(IMiniGUI)
CRYINTERFACE_END()
CRYGENERATE_SINGLETONCLASS(CMiniGUI, "MiniGUI", 0x1a049b879a4e4b58, 0xac14026e17e6255e)
public:
void InitMetrics();
void ProcessInput();
//////////////////////////////////////////////////////////////////////////
// IMiniGUI interface implementation.
//////////////////////////////////////////////////////////////////////////
virtual void Init();
virtual void Done();
virtual void Draw();
virtual void Reset();
virtual void SaveState();
virtual void RestoreState();
virtual void SetEnabled(bool status);
virtual void SetInFocus(bool status);
virtual bool InFocus() {return m_inFocus; }
virtual void SetEventListener(IMiniGUIEventListener* pListener);
virtual SMetrics& Metrics();
virtual void OnCommand(SCommand& cmd);
virtual void RemoveAllCtrl();
virtual IMiniCtrl* CreateCtrl(IMiniCtrl* pParentCtrl, int nCtrlID, EMiniCtrlType type, int nCtrlFlags, const Rect& rc, const char* title);
virtual IMiniCtrl* GetCtrlFromPoint(float x, float y) const;
void SetHighlight(IMiniCtrl* pCtrl, bool bEnable, float x, float y);
void SetFocus(IMiniCtrl* pCtrl, bool bEnable);
// AzFramework::InputChannelEventListener
bool OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel) override;
AZ::s32 GetPriority() const override { return AzFramework::InputChannelEventListener::GetPriorityUI(); }
virtual void SetMovingCtrl(IMiniCtrl* pCtrl)
{
m_pMovingCtrl = pCtrl;
}
protected:
void OnMouseInputEvent(const AzFramework::InputChannel& inputChannel);
//DPad menu navigation
void UpdateDPadMenu(const AzFramework::InputChannel& inputChannel);
void SetDPadMenu(IMiniCtrl* pMenu);
void CloseDPadMenu();
protected:
bool m_enabled;
bool m_inFocus;
SMetrics m_metrics;
_smart_ptr<CMiniCtrl> m_pRootCtrl;
_smart_ptr<IMiniCtrl> m_highlightedCtrl;
_smart_ptr<IMiniCtrl> m_focusCtrl;
IMiniGUIEventListener* m_pEventListener;
CMiniMenu* m_pDPadMenu;
IMiniCtrl* m_pMovingCtrl;
std::vector<minigui::IMiniCtrl*> m_rootMenus;
};
MINIGUI_END
#endif // CRYINCLUDE_CRYSYSTEM_MINIGUI_MINIGUI_H
@@ -0,0 +1,174 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Button implementation in the MiniGUI
#include "CrySystem_precompiled.h"
#include "MiniInfoBox.h"
#include "DrawContext.h"
#include <ISystem.h>
MINIGUI_BEGIN
CMiniInfoBox::CMiniInfoBox()
: m_fTextIndent(4)
{
}
//////////////////////////////////////////////////////////////////////////
void CMiniInfoBox::SaveState()
{
if (CheckFlag(eCtrl_Hidden))
{
m_saveStateOn = false;
}
else
{
m_saveStateOn = true;
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniInfoBox::Reset()
{
SetFlag(eCtrl_Hidden);
}
//////////////////////////////////////////////////////////////////////////
void CMiniInfoBox::RestoreState()
{
if (m_saveStateOn)
{
ClearFlag(eCtrl_Hidden);
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniInfoBox::OnPaint(CDrawContext& dc)
{
if (m_requiresResize)
{
AutoResize();
}
ColorB borderCol = dc.Metrics().clrFrameBorder;
ColorB backgroundCol = dc.Metrics().clrBackground;
if (!m_pGUI->InFocus())
{
borderCol = dc.Metrics().clrFrameBorderOutOfFocus;
backgroundCol.a = dc.Metrics().outOfFocusAlpha;
}
else if (m_moving)
{
borderCol = dc.Metrics().clrFrameBorderHighlight;
}
dc.DrawFrame(m_rect, borderCol, backgroundCol);
dc.SetColor(dc.Metrics().clrTitle);
dc.DrawString(m_rect.left + 4, m_rect.top, dc.Metrics().fTitleSize, eTextAlign_Left, GetTitle());
float fTextSize = m_fTextSize;
if (fTextSize == 0)
{
fTextSize = dc.Metrics().fTextSize;
}
float x = m_fTextIndent + m_rect.left + 8;
float y = m_rect.top + fTextSize + fTextSize;
// Draw entries.
for (int i = 0, num = (int)m_entries.size(); i < num; i++)
{
SInfoEntry& info = m_entries[i];
dc.SetColor(info.color);
dc.DrawString(x, y, info.textSize, eTextAlign_Left, info.text);
y += info.textSize * 0.8f;
if (y + info.textSize > m_rect.bottom)
{
break;
}
}
if (m_renderCallback)
{
m_renderCallback(m_rect.left, m_rect.top);
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniInfoBox::OnEvent(float x, float y, EMiniCtrlEvent event)
{
CMiniCtrl::OnEvent(x, y, event);
}
void CMiniInfoBox::AddEntry(const char* str, ColorB col, float textSize)
{
SInfoEntry info;
info.color = col;
info.textSize = textSize;
cry_strcpy(info.text, str);
m_entries.push_back(info);
if (CheckFlag(eCtrl_AutoResize))
{
//set dirty flag instead of resizing for every elem
m_requiresResize = true; //AutoResize();
}
}
void CMiniInfoBox::ClearEntries()
{
m_entries.clear();
m_requiresResize = true;
}
//////////////////////////////////////////////////////////////////////////
void CMiniInfoBox::AutoResize()
{
float width = 0.f;
float height = 32.f;
//must be at least the size of title and cross box
width = m_fTextIndent + ((float)m_title.size() * 14.f) + 30.f;
for (int i = 0, num = (int)m_entries.size(); i < num; i++)
{
SInfoEntry& info = m_entries[i];
uint32 strLength = strlen(info.text);
float strSize = info.textSize * strLength;
width = max(strSize, width);
height += info.textSize * 0.8f;
}
//scale width, could do with kerning info
width *= 0.6f;
Rect newRect = m_rect;
newRect.right = newRect.left + width;
newRect.bottom = newRect.top + height;
SetRect(newRect);
m_requiresResize = false;
}
MINIGUI_END
@@ -0,0 +1,79 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Button implementation in the MiniGUI
#ifndef CRYINCLUDE_CRYSYSTEM_MINIGUI_MINIINFOBOX_H
#define CRYINCLUDE_CRYSYSTEM_MINIGUI_MINIINFOBOX_H
#pragma once
#include "MiniGUI.h"
MINIGUI_BEGIN
//////////////////////////////////////////////////////////////////////////
// Root window all other controls derive from
class CMiniInfoBox
: public CMiniCtrl
, public IMiniInfoBox
{
public:
CMiniInfoBox();
//////////////////////////////////////////////////////////////////////////
// CMiniCtrl interface implementation.
//////////////////////////////////////////////////////////////////////////
virtual EMiniCtrlType GetType() const { return eCtrlType_InfoBox; }
virtual void OnPaint(CDrawContext& dc);
virtual void OnEvent(float x, float y, EMiniCtrlEvent event);
virtual void Reset();
virtual void SaveState();
virtual void RestoreState();
virtual void AutoResize();
//////////////////////////////////////////////////////////////////////////
virtual void SetTextIndent(float x) { m_fTextIndent = x; }
virtual void SetTextSize(float sz) { m_fTextSize = sz; }
virtual void ClearEntries();
virtual void AddEntry(const char* str, ColorB col, float textSize);
//////////////////////////////////////////////////////////////////////////
// IMiniTable interface implementation.
//////////////////////////////////////////////////////////////////////////
virtual bool IsHidden() { return CheckFlag(eCtrl_Hidden); }
virtual void Hide(bool stat) { SetVisible(!stat); }
public:
//////////////////////////////////////////////////////////////////////////
static const int MAX_TEXT_LENGTH = 64;
struct SInfoEntry
{
char text[MAX_TEXT_LENGTH];
ColorB color;
float textSize;
};
//////////////////////////////////////////////////////////////////////////
protected:
std::vector<SInfoEntry> m_entries;
float m_fTextIndent;
};
MINIGUI_END
#endif // CRYINCLUDE_CRYSYSTEM_MINIGUI_MINIINFOBOX_H
@@ -0,0 +1,364 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Button implementation in the MiniGUI
#include "CrySystem_precompiled.h"
#include "MiniMenu.h"
#include "DrawContext.h"
MINIGUI_BEGIN
CMiniMenu::CMiniMenu()
{
m_bVisible = false;
m_bSubMenu = false;
m_menuWidth = 0.f;
m_selectionIndex = -1;
}
//////////////////////////////////////////////////////////////////////////
void CMiniMenu::OnPaint(CDrawContext& dc)
{
CMiniButton::OnPaint(dc);
if (m_bSubMenu)
{
// Draw checked mark.
float x1 = m_rect.right - 12;
float y1 = m_rect.top + 3;
float x2 = m_rect.right - 3;
float y2 = (m_rect.bottom + m_rect.top) / 2;
float x3 = m_rect.right - 12;
float y3 = m_rect.bottom - 3;
dc.SetColor(ColorB(0, 0, 0, 255));
dc.DrawLine(x1, y1, x2, y2, 2.f);
dc.DrawLine(x2, y2, x3, y3, 2.f);
x1 -= 1;
x2 -= 1;
x3 -= 1;
y1 -= 1;
y2 -= 1;
y3 -= 1;
dc.SetColor(ColorB(255, 255, 255, 255));
dc.DrawLine(x1, y1, x2, y2, 2.f);
dc.DrawLine(x2, y2, x3, y3, 2.f);
}
//dc.DrawFrame( m_rect,dc.Metrics().clrFrameBorder,dc.Metrics().clrBackground );
}
//////////////////////////////////////////////////////////////////////////
void CMiniMenu::SetRect(const Rect& rc)
{
Rect newrc = rc;
newrc.bottom = newrc.top + m_pGUI->Metrics().fTitleSize + 2;
CMiniCtrl::SetRect(newrc);
}
//////////////////////////////////////////////////////////////////////////
void CMiniMenu::OnEvent(float x, float y, EMiniCtrlEvent event)
{
switch (event)
{
case eCtrlEvent_LButtonDown:
{
if (m_bVisible)
{
Close();
}
else
{
Open();
}
}
break;
case eCtrlEvent_MouseOff:
{
bool closeMenu = true;
IMiniCtrl* pCtrl = m_pGUI->GetCtrlFromPoint(x, y);
//check if the cursor is still in one of the menu's children
if (pCtrl)
{
for (int i = 0, num = GetSubCtrlCount(); i < num; i++)
{
if (pCtrl == GetSubCtrl(i))
{
closeMenu = false;
break;
}
}
}
if (closeMenu)
{
Close();
if (m_pParent)
{
m_pParent->OnEvent(x, y, eCtrlEvent_MouseOff);
}
}
}
break;
}
}
CMiniMenu* CMiniMenu::UpdateSelection(EMiniCtrlEvent event)
{
CMiniMenu* pNewMenu = this;
IMiniCtrl* pCurrentSelection = NULL;
if (m_selectionIndex != -1)
{
pCurrentSelection = m_subCtrls[m_selectionIndex];
}
switch (event)
{
case eCtrlEvent_DPadLeft: //move to parent
if (!pCurrentSelection)
{
//move to previous root menu
pNewMenu = NULL;
}
else if (m_bSubMenu)
{
Close();
pNewMenu = (CMiniMenu*)m_pParent.get();
//pNewMenu->SetFlag(eCtrl_Highlight);
}
break;
case eCtrlEvent_DPadRight: //move to child
if (!pCurrentSelection)
{
//move to next root menu
pNewMenu = NULL;
}
else if (pCurrentSelection->GetType() == eCtrlType_Menu)
{
pNewMenu = (CMiniMenu*)pCurrentSelection;
pNewMenu->Open();
pNewMenu->ClearFlag(eCtrl_Highlight);
}
break;
case eCtrlEvent_DPadUp: //move up list
if (m_bSubMenu)
{
if (m_selectionIndex > 0)
{
m_selectionIndex--;
}
}
else
{
if (m_selectionIndex >= 0)
{
m_selectionIndex--;
if (m_selectionIndex == -1)
{
SetFlag(eCtrl_Highlight);
}
}
}
break;
case eCtrlEvent_DPadDown: //move down the list
if (m_selectionIndex < (int)(m_subCtrls.size() - 1))
{
if (m_selectionIndex == -1)
{
ClearFlag(eCtrl_Highlight);
}
m_selectionIndex++;
}
break;
case eCtrlEvent_LButtonDown: //pass on button press
{
if (pCurrentSelection)
{
if (pCurrentSelection->GetType() == eCtrlType_Menu)
{
pNewMenu = (CMiniMenu*)pCurrentSelection;
}
pCurrentSelection->OnEvent(0, 0, eCtrlEvent_LButtonDown);
}
else
{
OnEvent(0, 0, eCtrlEvent_LButtonDown);
}
}
break;
}
if (pCurrentSelection)
{
pCurrentSelection->ClearFlag(eCtrl_Highlight);
}
if (m_selectionIndex >= 0)
{
m_subCtrls[m_selectionIndex]->SetFlag(eCtrl_Highlight);
}
return pNewMenu;
}
//////////////////////////////////////////////////////////////////////////
void CMiniMenu::Open()
{
m_bVisible = true;
Rect rc(0, 0, m_menuWidth, 1);
//render sub menu to the right
if (m_bSubMenu)
{
CMiniMenu* pParent = static_cast<CMiniMenu*>(m_pParent.get());
rc.left = pParent->m_menuWidth; //rcParent.right;
rc.right = rc.left + m_menuWidth;
}
else
{
Rect rcThis = GetRect();
rc.top = rcThis.Height();
}
for (int i = 0, num = GetSubCtrlCount(); i < num; i++)
{
IMiniCtrl* pSubCtrl = GetSubCtrl(i);
pSubCtrl->ClearFlag(eCtrl_Hidden);
float h = pSubCtrl->GetRect().Height();
Rect rcCtrl = rc;
rcCtrl.top = rc.top;
rcCtrl.bottom = rcCtrl.top + h;
pSubCtrl->SetRect(rcCtrl);
rc.top += h;
}
//highlight first item when opened
if (m_bSubMenu)
{
m_selectionIndex = 0;
m_subCtrls[m_selectionIndex]->SetFlag(eCtrl_Highlight);
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniMenu::Close()
{
m_bVisible = false;
for (int i = 0, num = GetSubCtrlCount(); i < num; i++)
{
IMiniCtrl* pSubCtrl = GetSubCtrl(i);
pSubCtrl->SetFlag(eCtrl_Hidden);
}
if (m_selectionIndex != -1)
{
m_subCtrls[m_selectionIndex]->ClearFlag(eCtrl_Highlight);
}
m_selectionIndex = -1;
}
//////////////////////////////////////////////////////////////////////////
void CMiniMenu::Reset()
{
m_bVisible = false;
for (int i = 0, num = GetSubCtrlCount(); i < num; i++)
{
IMiniCtrl* pSubCtrl = GetSubCtrl(i);
pSubCtrl->Reset();
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniMenu::SaveState()
{
for (int i = 0, num = GetSubCtrlCount(); i < num; i++)
{
IMiniCtrl* pSubCtrl = GetSubCtrl(i);
pSubCtrl->SaveState();
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniMenu::RestoreState()
{
for (int i = 0, num = GetSubCtrlCount(); i < num; i++)
{
IMiniCtrl* pSubCtrl = GetSubCtrl(i);
pSubCtrl->RestoreState();
}
}
//////////////////////////////////////////////////////////////////////////
void CMiniMenu::AddSubCtrl(IMiniCtrl* pCtrl)
{
assert(pCtrl);
pCtrl->SetFlag(eCtrl_Hidden);
pCtrl->SetFlag(eCtrl_NoBorder);
bool bSubMenu = false;
if (pCtrl->GetType() == eCtrlType_Menu)
{
CMiniMenu* pSubMenu = static_cast<CMiniMenu*>(pCtrl);
pSubMenu->m_bSubMenu = true;
bSubMenu = true;
}
if (!m_bSubMenu)
{
//menu is at least the size of title bar
m_menuWidth = max(m_menuWidth, strlen(GetTitle()) * 8.5f);
}
const char* title = pCtrl->GetTitle();
if (title)
{
uint32 titleLen = strlen(title);
float width = (float)titleLen * 8.5f;
if (bSubMenu)
{
//increase width for submenu arrow
width += 10.f;
}
m_menuWidth = max(m_menuWidth, width);
}
// Call parent
CMiniButton::AddSubCtrl(pCtrl);
}
MINIGUI_END

Some files were not shown because too many files have changed in this diff Show More