Initial commit of existing code.

* All renderdoc code up to this point was written by me, history is available by request
This commit is contained in:
baldurk
2014-05-02 08:14:55 +01:00
parent 04b1549c0f
commit c38affcded
695 changed files with 230316 additions and 0 deletions
+180
View File
@@ -0,0 +1,180 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "common.h"
#include <stdarg.h>
#include <string.h>
#include "os/os_specific.h"
#include "common/threading.h"
#include "string_utils.h"
#include <string>
using std::string;
template<> const wchar_t* pathSeparator() { return L"\\/"; }
template<> const char* pathSeparator() { return "\\/"; }
template<> const wchar_t* curdir() { return L"."; }
template<> const char* curdir() { return "."; }
std::wstring widen(std::string str)
{
return std::wstring(str.begin(), str.end());
}
std::string narrow(std::wstring str)
{
return std::string(str.begin(), str.end());
}
void rdcassert(const char *condition, const char *file, unsigned int line, const char *func)
{
rdclog_int(RDCLog_Error, file, line, "Assertion failed: '%hs'", condition, file, line);
}
static wstring &logfile()
{
static wstring fn;
return fn;
}
const wchar_t *rdclog_getfilename()
{
return logfile().c_str();
}
void rdclog_filename(const wchar_t *filename)
{
logfile() = L"";
if(filename && filename[0])
logfile() = filename;
}
void rdclog_delete()
{
if(!logfile().empty())
FileIO::UnlinkFileW(logfile().c_str());
}
void rdclog_flush()
{
}
void rdclog_int(LogType type, const char *file, unsigned int line, const char *fmt, ...)
{
if(type <= RDCLog_First || type >= RDCLog_NumTypes)
{
RDCFATAL("Unexpected log type");
return;
}
va_list args;
va_start(args, fmt);
const char *name = "RENDERDOC: ";
char timestamp[64] = {0};
#if defined(INCLUDE_TIMESTAMP_IN_LOG)
StringFormat::sntimef(timestamp, 63, "[%H:%M:%S] ");
#endif
char location[64] = {0};
#if defined(INCLUDE_LOCATION_IN_LOG)
string loc;
loc = basename(string(file));
StringFormat::snprintf(location, 63, "% 20s(%4d) - ", loc.c_str(), line);
#endif
const char *typestr[RDCLog_NumTypes] = {
"Debug ",
"Log ",
"Warning",
"Error ",
"Fatal ",
};
const size_t outBufSize = 4*1024;
char outputBuffer[outBufSize+1];
outputBuffer[outBufSize] = 0;
char *output = outputBuffer;
size_t available = outBufSize;
int numWritten = StringFormat::snprintf(output, available, "%hs %hs%hs%hs - ", name, timestamp, location, typestr[type]);
if(numWritten < 0)
{
va_end(args);
return;
}
output += numWritten;
available -= numWritten;
numWritten = StringFormat::vsnprintf(output, available, fmt, args);
va_end(args);
if(numWritten < 0)
return;
output += numWritten;
available -= numWritten;
if(available < 2)
return;
*output = '\n';
*(output+1) = 0;
{
static Threading::CriticalSection lock;
SCOPED_LOCK(lock);
#if defined(OUTPUT_LOG_TO_DEBUG_OUT)
OSUtility::DebugOutputA(outputBuffer);
#endif
#if defined(OUTPUT_LOG_TO_STDOUT)
fprintf(stdout, "%hs", outputBuffer); fflush(stdout);
#endif
#if defined(OUTPUT_LOG_TO_STDERR)
fprintf(stderr, "%hs", outputBuffer); fflush(stderr);
#endif
#if defined(OUTPUT_LOG_TO_DISK)
if(!logfile().empty())
{
FILE *f = FileIO::fopen(logfile().c_str(), L"a");
if(f)
{
FileIO::fwrite(outputBuffer, 1, strlen(outputBuffer), f);
FileIO::fclose(f);
}
}
#endif
}
}
+178
View File
@@ -0,0 +1,178 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#pragma once
#include "globalconfig.h"
/////////////////////////////////////////////////
// Utility macros
#ifndef SAFE_DELETE
#define SAFE_DELETE(p) do { if (p) { delete (p); (p)=NULL; } } while(0)
#endif
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) do { if (p) { delete[] (p); (p)=NULL; } } while(0)
#endif
#ifndef SAFE_ADDREF
#define SAFE_ADDREF(p) do { if (p) { (p)->AddRef(); } } while(0)
#endif
#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p) do { if (p) { (p)->Release(); (p)=NULL; } } while(0)
#define SAFE_RELEASE_NOCLEAR(p) do { if (p) { (p)->Release(); } } while(0)
#endif
#ifndef ARRAY_COUNT
#define ARRAY_COUNT(arr) (sizeof(arr)/sizeof(arr[0]))
#endif
#define RANDF(mn, mx) ((float(rand())/float(RAND_MAX))*((mx)-(mn))+(mn))
#define STRINGIZE2(a) #a
#define STRINGIZE(a) STRINGIZE2(a)
#define CONCAT2(a, b) a##b
#define CONCAT(a, b) CONCAT2(a, b)
#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
#include "os/os_specific.h"
#define RDCEraseMem(a, b) memset(a, 0, b)
#define RDCEraseEl(a) memset(&a, 0, sizeof(a))
template<typename T>
T RDCCLAMP(const T &val, const T &mn, const T &mx) { return val < mn ? mn : (val > mx ? mx : val); }
template<typename T>
T RDCMIN(const T &a, const T &b) { return a < b ? a : b; }
template<typename T>
T RDCMAX(const T &a, const T &b) { return a > b ? a : b; }
template<typename T>
inline T AlignUp4(T x) { return (x+0x3) & (~0x3); }
template<typename T>
inline T AlignUp16(T x) { return (x+0xf) & (~0xf); }
#define MAKE_FOURCC(a, b, c, d) (((uint32_t)(d) << 24) | ((uint32_t)(c) << 16) | ((uint32_t)(b) << 8) | (uint32_t)(a))
/////////////////////////////////////////////////
// Debugging features
#define RDCDUMP() do { OSUtility::ForceCrash(); } while(0)
#if !defined(RELEASE) || defined(FORCE_DEBUGBREAK)
#define RDCBREAK() do { if(OSUtility::DebuggerPresent()) OSUtility::DebugBreak(); else RDCDUMP(); } while(0)
#else
#define RDCBREAK() do { } while(0)
#endif
#define RDCUNIMPLEMENTED(...) do { rdclog(RDCLog_Warning, "Unimplemented: " __VA_ARGS__); RDCBREAK(); } while(0)
//
// Logging
//
enum LogType
{
RDCLog_First = -1,
RDCLog_Debug,
RDCLog_Comment,
RDCLog_Warning,
RDCLog_Error,
RDCLog_Fatal,
RDCLog_NumTypes,
};
#if defined(STRIP_LOG)
#define RDCLOGFILE(fn) do { } while(0)
#define RDCLOGDELETE() do { } while(0)
#define RDCDEBUG(...) do { } while(0)
#define RDCLOG(...) do { } while(0)
#define RDCWARN(...) do { } while(0)
#define RDCERR(...) do { } while(0)
#define RDCFATAL(...) do { RDCDUMP(); exit(0); } while(0)
#else
void rdclog_flush();
void rdclog_int(LogType type, const char *file, unsigned int line, const char *fmt, ...);
#define rdclog(type, ...) rdclog_int(type, __FILE__, __LINE__, __VA_ARGS__)
const wchar_t *rdclog_getfilename();
void rdclog_filename(const wchar_t *filename);
void rdclog_delete();
#define RDCLOGFILE(fn) rdclog_filename(fn)
#define RDCGETLOGFILE() rdclog_getfilename()
#define RDCLOGDELETE() rdclog_delete()
#if ( !defined(RELEASE) || defined(FORCE_DEBUG_LOGS) ) && !defined(STRIP_DEBUG_LOGS)
#define RDCDEBUG(...) rdclog(RDCLog_Debug, __VA_ARGS__)
#else
#define RDCDEBUG(...) do { } while(0)
#endif
#define RDCLOG(...) rdclog(RDCLog_Comment, __VA_ARGS__)
#define RDCWARN(...) rdclog(RDCLog_Warning, __VA_ARGS__)
#if defined(DEBUGBREAK_ON_ERROR_LOG)
#define RDCERR(...) do { rdclog(RDCLog_Error, __VA_ARGS__); rdclog_flush(); RDCBREAK(); } while(0)
#else
#define RDCERR(...) rdclog(RDCLog_Error, __VA_ARGS__)
#endif
#define RDCFATAL(...) do { rdclog(RDCLog_Fatal, __VA_ARGS__); rdclog_flush(); RDCDUMP(); exit(0); } while(0)
#endif
//
// Assert
//
#if !defined(RELEASE) || defined(FORCE_ASSERTS)
void rdcassert(const char *condition, const char *file, unsigned int line, const char *func);
#define RDCASSERT(cond) do { if(!(cond)) { rdcassert(#cond, __FILE__, __LINE__, __PRETTY_FUNCTION_SIGNATURE__); rdclog_flush(); RDCBREAK(); } } while(0)
#else
#define RDCASSERT(cond) do { } while(0)
#endif
//
// Compile asserts
//
#if defined(STRIP_COMPILE_ASSERTS)
#define RDCCOMPILE_ASSERT(condition, message) do { } while(0)
#else
#define RDCCOMPILE_ASSERT(condition, message) static_assert(condition, message)
#endif
typedef unsigned char byte;
+95
View File
@@ -0,0 +1,95 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#pragma once
/////////////////////////////////////////////////
// Global constants
enum
{
RenderDoc_FirstCaptureNetworkPort = 38920,
RenderDoc_LastCaptureNetworkPort = RenderDoc_FirstCaptureNetworkPort + 7,
RenderDoc_ReplayNetworkPort = 39920,
};
/////////////////////////////////////////////////
// Debugging features configuration
// remove all logging code
//#define STRIP_LOG
// remove all compile time asserts. Normally done even in release
// but this would speed up compilation
//#define STRIP_COMPILE_ASSERTS
// force asserts regardless of debug/release mode
#define FORCE_ASSERTS
// force debugbreaks regardless of debug/release mode
//#define FORCE_DEBUGBREAK
// synchronously (ie. flushed to disk) write out the text-mode only serialised
// data of ALL serialising. Helps in debugging crashes to know exactly which
// calls have been made
//#define DEBUG_TEXT_SERIALISER
/////////////////////////////////////////////////
// Logging configuration
// error logs trigger a breakpoint
#define DEBUGBREAK_ON_ERROR_LOG
// whether to include timestamp on log lines
#define INCLUDE_TIMESTAMP_IN_LOG
// whether to include file and line on log lines
#define INCLUDE_LOCATION_IN_LOG
// logs go to stdout/stderr
#define OUTPUT_LOG_TO_STDOUT
//#define OUTPUT_LOG_TO_STDERR
// logs go to debug output (visual studio output window)
#define OUTPUT_LOG_TO_DEBUG_OUT
// logs go to disk
#define OUTPUT_LOG_TO_DISK
// normally only in a debug build do we
// include debug logs. This prints them all the time
//#define FORCE_DEBUG_LOGS
// this strips them completely
//#define STRIP_DEBUG_LOGS
/////////////////////////////////////////////////
// optional features
#if defined(NVIDIA_PERFKIT_DIR)
#define ENABLE_NVIDIA_PERFKIT
#endif
#if defined(AMD_PERFAPI_DIR)
#define ENABLE_AMD_PERFAPI
#endif
+138
View File
@@ -0,0 +1,138 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#pragma once
#include <algorithm>
#include <string>
#include <vector>
#include <stdint.h>
#include <ctype.h>
using std::basic_string;
using std::vector;
// in common.cpp
template<class charType> const charType* pathSeparator();
template<class charType> const charType* curdir();
template<class strType> strType basename(const strType &path)
{
strType base = path;
if(base.length() == 0)
return base;
if(base[base.length()-1] == '/' || base[base.length()-1] == '\\')
base.erase(base.size()-1);
size_t offset = base.find_last_of(pathSeparator<typename strType::value_type>());
if(offset == strType::npos)
return base;
return base.substr(offset+1);
}
template<class strType> strType dirname(const strType &path)
{
strType base = path;
if(base.length() == 0)
return base;
if(base[base.length()-1] == '/' || base[base.length()-1] == '\\')
base.erase(base.size()-1);
size_t offset = base.find_last_of(pathSeparator<typename strType::value_type>());
if(offset == strType::npos)
return curdir<typename strType::value_type>();
return base.substr(0, offset);
}
template <class CharType>
void strreplace(basic_string<CharType>& str, const basic_string<CharType>& toFind, const basic_string<CharType>& replacement, typename basic_string<CharType>::size_type index = 0)
{
typename basic_string<CharType>::size_type length = toFind.length();
while(index < str.length())
{
index = str.find(toFind, index);
if(index < str.length())
{
str.replace(index, length, replacement);
}
}
}
template <typename CharType>
basic_string<CharType> strlower(const basic_string<CharType>& str)
{
basic_string<CharType> newstr(str);
transform(newstr.begin(), newstr.end(), newstr.begin(), tolower);
return newstr;
}
template <typename CharType>
basic_string<CharType> strupper(const basic_string<CharType>& str)
{
basic_string<CharType> newstr(str);
transform(newstr.begin(), newstr.end(), newstr.begin(), (int(*)(int))toupper);
return newstr;
}
template <class CharType>
void split(const basic_string<CharType>& in, vector<basic_string<CharType> >& out, const CharType sep)
{
basic_string<CharType> work = in;
typename basic_string<CharType>::size_type offset = work.find(sep);
while(offset != basic_string<CharType>::npos)
{
out.push_back(work.substr(0, offset));
work = work.substr(offset+1);
offset = work.find(sep);
}
if(work.size() && work[0] != 0)
out.push_back(work);
}
template <class CharType>
void merge(const vector<basic_string<CharType> >& in, basic_string<CharType>& out, const CharType sep)
{
out = basic_string<CharType>();
for(size_t i=0; i < in.size(); i++)
{
out += in[i];
out += sep;
}
}
std::wstring widen(std::string str);
std::string narrow(std::wstring str);
+67
View File
@@ -0,0 +1,67 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#pragma once
#include "os/os_specific.h"
namespace Threading
{
class ScopedLock
{
public:
ScopedLock(CriticalSection &cs)
: m_CS(&cs)
{ m_CS->Lock(); }
~ScopedLock()
{ m_CS->Unlock(); }
private:
CriticalSection *m_CS;
};
class TryScopedLock
{
public:
TryScopedLock(CriticalSection &cs)
: m_CS(&cs)
{ m_Owned = m_CS->Trylock(); }
~TryScopedLock()
{ if(m_Owned) m_CS->Unlock(); }
bool HasLock()
{
return m_Owned;
}
private:
CriticalSection *m_CS;
bool m_Owned;
};
};
#define SCOPED_LOCK(cs) Threading::ScopedLock CONCAT(scopedlock, __LINE__)(cs);
+93
View File
@@ -0,0 +1,93 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#pragma once
#include <string>
using std::string;
#include <stdint.h>
#include <stdarg.h>
#include "common.h"
#include "os/os_specific.h"
class PerformanceTimer
{
public:
PerformanceTimer()
{
m_CounterFrequency = Timing::GetTickFrequency();
Restart();
}
double GetMilliseconds()
{
return double(Timing::GetTick()-m_Start)/m_CounterFrequency;
}
void Restart()
{
m_Start = Timing::GetTick();
}
private:
double m_CounterFrequency;
uint64_t m_Start;
};
class ScopedTimer
{
public:
ScopedTimer(const char *file, unsigned int line, const char *fmt, ...)
{
m_File = file;
m_Line = line;
va_list args;
va_start(args, fmt);
char buf[1024];
buf[1023] = 0;
StringFormat::vsnprintf(buf, 1023, fmt, args);
m_Message = buf;
va_end(args);
}
~ScopedTimer()
{
rdclog_int(RDCLog_Comment, m_File, m_Line, "Timer %hs - %.3lf ms", m_Message.c_str(), m_Timer.GetMilliseconds());
}
private:
const char *m_File;
unsigned int m_Line;
string m_Message;
PerformanceTimer m_Timer;
};
#define SCOPED_TIMER(...) ScopedTimer CONCAT(timer, __LINE__) (__FILE__, __LINE__, __VA_ARGS__);
+263
View File
@@ -0,0 +1,263 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#pragma once
#include <stdint.h>
#include "common.h"
#include "threading.h"
#if !defined(_RELEASE)
#define INCLUDE_TYPE_NAMES
#endif
#ifdef INCLUDE_TYPE_NAMES
template<class T> class GetTypeName { public: static const char *Name(); };
#endif
template<class C> class FriendMaker { public: typedef C Type; };
// allocate each class in its own pool so we can identify the type by the pointer
template<typename WrapType, int PoolCount = 8192, int MaxPoolByteSize = 1024*1024>
class WrappingPool
{
public:
void *Allocate()
{
SCOPED_LOCK(m_Lock);
// try and allocate from immediate pool
void *ret = m_ImmediatePool.Allocate();
if(ret != NULL) return ret;
// fall back to additional pools, if there are any
for(size_t i=0; i < m_AdditionalPools.size(); i++)
{
ret = m_AdditionalPools[i]->Allocate();
if(ret != NULL)
return ret;
}
// warn when we need to allocate an additional pool
#ifdef INCLUDE_TYPE_NAMES
RDCWARN("Ran out of free slots in %hs pool!", GetTypeName<WrapType>::Name());
#else
RDCWARN("Ran out of free slots in pool 0x%p!", &m_ImmediatePool.items[0]);
#endif
// allocate a new additional pool and use that to allocate from
m_AdditionalPools.push_back(new ItemPool());
#ifdef INCLUDE_TYPE_NAMES
RDCDEBUG("WrappingPool[%d]<%hs>: %p -> %p", (uint32_t)m_AdditionalPools.size() - 1, GetTypeName<WrapType>::Name(),
&m_AdditionalPools.back()->items[0], &m_AdditionalPools.back()->items[AllocCount-1]);
#endif
return m_AdditionalPools.back()->Allocate();
}
bool IsAlloc(void *p)
{
// we can check the immediate pool without locking
if(m_ImmediatePool.IsAlloc(p))
return true;
// if we have additional pools, lock and check them.
// TODO: Check for additional pools in a lock-free manner,
// to prevent the cost of locking if there are no more pools.
{
SCOPED_LOCK(m_Lock);
for(size_t i=0; i < m_AdditionalPools.size(); i++)
if(m_AdditionalPools[i]->IsAlloc(p))
return true;
}
return false;
}
void Deallocate(void *p)
{
SCOPED_LOCK(m_Lock);
// try immediate pool
if(m_ImmediatePool.IsAlloc(p))
{
m_ImmediatePool.Deallocate(p);
return;
}
else if(!m_AdditionalPools.empty())
{
// fall back and try additional pools
for(size_t i=0; i < m_AdditionalPools.size(); i++)
{
if(m_AdditionalPools[i]->IsAlloc(p))
{
m_AdditionalPools[i]->Deallocate(p);
return;
}
}
}
// this is an error - deleting an object that we don't recognise
#ifdef INCLUDE_TYPE_NAMES
RDCERR("Resource being deleted through wrong pool - 0x%p not a member of %hs", p, GetTypeName<WrapType>::Name());
#else
RDCERR("Resource being deleted through wrong pool - 0x%p not a member of 0x%p", p, &m_ImmediatePool.items[0]));
#endif
}
private:
WrappingPool()
{
#ifdef INCLUDE_TYPE_NAMES
RDCDEBUG("WrappingPool<%hs>: %p -> %p", GetTypeName<WrapType>::Name(), &m_ImmediatePool.items[0], &m_ImmediatePool.items[AllocCount-1]);
#endif
RDCCOMPILE_ASSERT(PoolCount*AllocByteSize <= MaxPoolByteSize, "Pool is bigger than max pool size cap");
RDCCOMPILE_ASSERT(PoolCount > 2, "Pool isn't greater than 2 in size. Bad parameters?"); // make sure parameters are sane
}
~WrappingPool()
{
for(size_t i=0; i < m_AdditionalPools.size(); i++)
delete m_AdditionalPools[i];
m_AdditionalPools.clear();
}
static const size_t AllocCount = PoolCount;
static const size_t AllocByteSize;
Threading::CriticalSection m_Lock;
struct ItemPool
{
ItemPool()
{
lastAllocIdx = 0;
RDCEraseEl(allocated);
items = (WrapType *)(new uint8_t[AllocCount*AllocByteSize]);
}
~ItemPool()
{
delete[] (uint8_t *)items;
}
void *Allocate()
{
int lastAlloc = lastAllocIdx;
if(allocated[lastAlloc])
{
int end = lastAlloc;
do
{
lastAlloc = (lastAlloc+1) % PoolCount;
} while (allocated[lastAlloc] && lastAlloc != end);
if(allocated[lastAlloc])
{
return NULL;
}
}
void *ret = (void *)&items[lastAlloc];
allocated[lastAlloc] = true;
#if !defined(RELEASE)
memset(ret, 0xb0, AllocByteSize);
#endif
lastAllocIdx = lastAlloc;
return ret;
}
void Deallocate(void *p)
{
RDCASSERT(IsAlloc(p));
#if !defined(RELEASE)
if(!IsAlloc(p))
{
RDCERR("Resource being deleted through wrong pool - 0x%p not a memory of 0x%p", p, &items[0]);
return;
}
#endif
size_t idx = (WrapType *)p-&items[0];
allocated[idx] = false;
#if !defined(RELEASE)
memset(p, 0xfe, AllocByteSize);
#endif
}
bool IsAlloc(void *p)
{
return p >= &items[0] && p < &items[PoolCount];
}
WrapType *items;
// could possibly make this uint32s and check via bitmasks, but
// we'll see if it shows up in profiling
bool allocated[PoolCount];
// store the last allocations index. Good place to start from and we
// go through the pool in a ring. Good performance when the pool is empty
// or contiguously allocated, poorer performance when the pool gets filled up.
// It also has the bonus of handling repeated new/free well by reallocating the
// same element.
int lastAllocIdx;
};
ItemPool m_ImmediatePool;
std::vector<ItemPool *> m_AdditionalPools;
friend typename FriendMaker<WrapType>::Type;
};
#define ALLOCATE_WITH_WRAPPED_POOL(...) \
typedef WrappingPool<__VA_ARGS__> PoolType; \
static PoolType m_Pool; \
void* operator new(size_t sz) { return m_Pool.Allocate(); } \
void operator delete(void* p) { m_Pool.Deallocate(p); } \
static bool IsAlloc(void *p) { return m_Pool.IsAlloc(p); }
#ifdef INCLUDE_TYPE_NAMES
#define DECL_TYPENAME(a) template<> const char *GetTypeName< a >::Name() { return #a; }
#else
#define DECL_TYPENAME(a)
#endif
#define WRAPPED_POOL_INST(a) \
a::PoolType a::m_Pool; \
const size_t a::PoolType::AllocByteSize = sizeof(a); \
DECL_TYPENAME(a);