From 9ccee73baca0fd7ecb95c90cb983133b737c6c55 Mon Sep 17 00:00:00 2001 From: Egor Yusov Date: Tue, 20 Oct 2015 20:46:28 -0700 Subject: Release v1.0.0 --- Common/include/BasicFileStream.h | 66 ++++++ Common/include/BasicTypes.h | 52 +++++ Common/include/DataBlobImpl.h | 58 +++++ Common/include/DebugUtilities.h | 66 ++++++ Common/include/Errors.h | 67 ++++++ Common/include/FileWrapper.h | 91 ++++++++ Common/include/FormatMessage.h | 44 ++++ Common/include/HashUtils.h | 195 +++++++++++++++++ Common/include/InterfaceID.h | 50 +++++ Common/include/LockHelper.h | 133 ++++++++++++ Common/include/ObjectBase.h | 83 ++++++++ Common/include/RefCntAutoPtr.h | 391 ++++++++++++++++++++++++++++++++++ Common/include/RefCountedObjectImpl.h | 332 +++++++++++++++++++++++++++++ Common/include/StringTools.h | 75 +++++++ Common/include/Timer.h | 41 ++++ Common/include/UniqueIdentifier.h | 79 +++++++ Common/include/ValidatedCast.h | 33 +++ Common/include/pch.h | 35 +++ 18 files changed, 1891 insertions(+) create mode 100644 Common/include/BasicFileStream.h create mode 100644 Common/include/BasicTypes.h create mode 100644 Common/include/DataBlobImpl.h create mode 100644 Common/include/DebugUtilities.h create mode 100644 Common/include/Errors.h create mode 100644 Common/include/FileWrapper.h create mode 100644 Common/include/FormatMessage.h create mode 100644 Common/include/HashUtils.h create mode 100644 Common/include/InterfaceID.h create mode 100644 Common/include/LockHelper.h create mode 100644 Common/include/ObjectBase.h create mode 100644 Common/include/RefCntAutoPtr.h create mode 100644 Common/include/RefCountedObjectImpl.h create mode 100644 Common/include/StringTools.h create mode 100644 Common/include/Timer.h create mode 100644 Common/include/UniqueIdentifier.h create mode 100644 Common/include/ValidatedCast.h create mode 100644 Common/include/pch.h (limited to 'Common/include') diff --git a/Common/include/BasicFileStream.h b/Common/include/BasicFileStream.h new file mode 100644 index 00000000..85f685a5 --- /dev/null +++ b/Common/include/BasicFileStream.h @@ -0,0 +1,66 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::BasicFileStream class + +#include "FileStream.h" +#include "ObjectBase.h" +#include "RefCountedObjectImpl.h" +#include "FileWrapper.h" +#include "DataBlob.h" + +namespace Diligent +{ + +/// Basic file stream implementation +class BasicFileStream : public ObjectBase +{ +public: + typedef ObjectBase TBase; + + BasicFileStream(const Diligent::Char *Path, + EFileAccessMode Access = EFileAccessMode::Read); + + virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface )override; + + /// Reads data from the stream + virtual void Read( Diligent::IDataBlob *pData )override; + + /// Reads data from the stream + virtual bool Read( void *Data, size_t BufferSize )override; + + /// Writes data to the stream + virtual bool Write( const void *Data, size_t Size )override; + + virtual size_t GetSize()override; + + virtual bool IsValid()override; + +private: + Diligent::FileWrapper m_FileWrpr; +}; + +} diff --git a/Common/include/BasicTypes.h b/Common/include/BasicTypes.h new file mode 100644 index 00000000..7d3bea3d --- /dev/null +++ b/Common/include/BasicTypes.h @@ -0,0 +1,52 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include +#include + +namespace Diligent +{ + typedef float Float32; + + typedef int64_t Int64; + typedef int32_t Int32; + typedef int16_t Int16; + typedef int8_t Int8; + + typedef uint64_t Uint64; + typedef uint32_t Uint32; + typedef uint16_t Uint16; + typedef uint8_t Uint8; + + typedef size_t SizeType; + typedef void* PVoid; + + typedef bool Bool; + static const Bool False = false; + static const Bool True = true; + + typedef char Char; + typedef std::basic_string String; +} diff --git a/Common/include/DataBlobImpl.h b/Common/include/DataBlobImpl.h new file mode 100644 index 00000000..d9441588 --- /dev/null +++ b/Common/include/DataBlobImpl.h @@ -0,0 +1,58 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation for the IDataBlob interface + +#include "DataBlob.h" +#include "BasicTypes.h" +#include "ObjectBase.h" +#include + +namespace Diligent +{ + +/// Base interface for a file stream +class DataBlobImpl : public Diligent::ObjectBase +{ +public: + typedef Diligent::ObjectBase TBase; + + virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface )override; + + /// Sets the size of the internal data buffer + virtual void Resize( size_t NewSize )override; + + /// Returns the size of the internal data buffer + virtual size_t GetSize()override; + + /// Returns the pointer to the internal data buffer + virtual void* GetDataPtr()override; + +private: + std::vector m_DataBuff; +}; + +} diff --git a/Common/include/DebugUtilities.h b/Common/include/DebugUtilities.h new file mode 100644 index 00000000..d57fd06b --- /dev/null +++ b/Common/include/DebugUtilities.h @@ -0,0 +1,66 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include "FormatMessage.h" +#include "PlatformDebug.h" + +#ifdef _DEBUG + +// This function is only requried to ensure that Message argument passed to the macro +// is actually string and not something else +inline void EnsureStr( const char* ){} +# define VERIFY(Expr, Message, ...)\ + { \ + if( !(Expr) ) \ + { \ + Diligent::MsgStream ms; \ + Diligent::FormatMsg( ms, Message, ##__VA_ARGS__);\ + PlatformDebug::AssertionFailed( ms.str().c_str(), __FUNCTION__, __FILE__, __LINE__); \ + } \ + EnsureStr(Message); \ + } +# define UNEXPECTED(Message, ...) { VERIFY(false, Message, ##__VA_ARGS__); } +# define UNSUPPORTED(Message, ...) { VERIFY(false, Message, ##__VA_ARGS__); } + +# define VERIFY_EXPR(Expr) VERIFY(Expr, "Debug exression failed:\n", #Expr) + + +template +void CheckDynamicType( SrcType *pSrcPtr ) +{ + VERIFY( pSrcPtr == nullptr || dynamic_cast (pSrcPtr) != nullptr, "Dynamic type cast failed!" ); +} +# define CHECK_DYNAMIC_TYPE(DstType, pSrcPtr) CheckDynamicType(pSrcPtr) + + +#else + +# define CHECK_DYNAMIC_TYPE(...){} +# define VERIFY(...){} +# define UNEXPECTED(...){} +# define UNSUPPORTED(...){} +# define VERIFY_EXPR(...){} + +#endif diff --git a/Common/include/Errors.h b/Common/include/Errors.h new file mode 100644 index 00000000..658adb0e --- /dev/null +++ b/Common/include/Errors.h @@ -0,0 +1,67 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include + +#include "PlatformDebug.h" +#include "FormatMessage.h" +#include "FileSystem.h" + +template +void LogError( const char *strFunctionName, const char *strFullFilePath, int Line, const FirstArgType& first, const RestArgsType&... RestArgs ) +{ + std::string FileName; + FileSystem::SplitFilePath( strFullFilePath, nullptr, &FileName ); + Diligent::MsgStream ss; + ss << "The following error occured in the " << strFunctionName << "() function (" << FileName << ", line " << Line << "):\n"; + Diligent::FormatMsg( ss, first, RestArgs... ); + auto strFullMessage = ss.str(); + PlatformDebug::OutputDebugMessage( bThrowException ? PlatformDebug::DebugMessageSeverity::FatalError : PlatformDebug::DebugMessageSeverity::Error, strFullMessage.c_str() ); + if( bThrowException ) + { + throw std::runtime_error( strFullMessage ); + } +} + +#define LOG_ERROR(...)\ +{ \ + LogError(__FUNCTION__, __FILE__, __LINE__, ##__VA_ARGS__); \ +} + +#define LOG_ERROR_AND_THROW(...) \ +{ \ + LogError(__FUNCTION__, __FILE__, __LINE__, ##__VA_ARGS__); \ +} + +#define LOG_DEBUG_MESSAGE(Severity, ...) \ +{ \ + Diligent::MsgStream ss; \ + Diligent::FormatMsg( ss, ##__VA_ARGS__ ); \ + PlatformDebug::OutputDebugMessage( Severity, ss.str().c_str() ); \ +} + +#define LOG_ERROR_MESSAGE(...) LOG_DEBUG_MESSAGE(PlatformDebug::DebugMessageSeverity::Error, ##__VA_ARGS__) +#define LOG_WARNING_MESSAGE(...) LOG_DEBUG_MESSAGE(PlatformDebug::DebugMessageSeverity::Warning, ##__VA_ARGS__) +#define LOG_INFO_MESSAGE(...) LOG_DEBUG_MESSAGE(PlatformDebug::DebugMessageSeverity::Info, ##__VA_ARGS__) diff --git a/Common/include/FileWrapper.h b/Common/include/FileWrapper.h new file mode 100644 index 00000000..85851d89 --- /dev/null +++ b/Common/include/FileWrapper.h @@ -0,0 +1,91 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include "FileSystem.h" +#include "Errors.h" +#include "DebugUtilities.h" + +namespace Diligent +{ + +class FileWrapper +{ +public: + FileWrapper( ) : + m_pFile(nullptr) + {} + + FileWrapper( const Diligent::Char *Path, + EFileAccessMode Access = EFileAccessMode::Read) : + m_pFile( nullptr ) + { + FileOpenAttribs OpenAttribs(Path, Access); + Open(OpenAttribs); + } + + ~FileWrapper() + { + Close(); + } + + void Open(const FileOpenAttribs& OpenAttribs) + { + VERIFY( !m_pFile, "Another file already attached" ); + Close(); + m_pFile = FileSystem::OpenFile( OpenAttribs ); + } + + CFile *Detach() + { + CFile *pFile = m_pFile; + m_pFile = NULL; + return pFile; + } + + void Attach(CFile *pFile) + { + VERIFY(!m_pFile, "Another file already attached"); + Close(); + m_pFile = pFile; + } + + void Close() + { + if( m_pFile ) + FileSystem::ReleaseFile(m_pFile); + m_pFile = nullptr; + } + + operator CFile*(){return m_pFile;} + CFile* operator->(){return m_pFile;} + +private: + FileWrapper(const FileWrapper&); + const FileWrapper& operator=(const FileWrapper&); + + CFile *m_pFile; +}; + +} diff --git a/Common/include/FormatMessage.h b/Common/include/FormatMessage.h new file mode 100644 index 00000000..a20435ae --- /dev/null +++ b/Common/include/FormatMessage.h @@ -0,0 +1,44 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include + +namespace Diligent +{ + typedef std::stringstream MsgStream; + + template + void FormatMsg( SSType &ss, const ArgType& Arg ) + { + ss << Arg; + } + + template + void FormatMsg( SSType &ss, const FirstArgType& FirstArg, const RestArgsType&... RestArgs ) + { + FormatMsg( ss, FirstArg ); + FormatMsg( ss, RestArgs... ); // recursive call using pack expansion syntax + } +} \ No newline at end of file diff --git a/Common/include/HashUtils.h b/Common/include/HashUtils.h new file mode 100644 index 00000000..03cf54b6 --- /dev/null +++ b/Common/include/HashUtils.h @@ -0,0 +1,195 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include +#include + +#define LOG_HASH_CONFLICTS 1 + +namespace Diligent +{ + // http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html + template + void HashCombine(std::size_t &Seed, const T& Val) + { + Seed ^= std::hash()(Val) + 0x9e3779b9 + (Seed << 6) + (Seed >> 2); + } + + template + void HashCombine( std::size_t &Seed, const FirstArgType& FirstArg, const RestArgsType&... RestArgs ) + { + HashCombine( Seed, FirstArg ); + HashCombine( Seed, RestArgs... ); // recursive call using pack expansion syntax + } + + template + std::size_t ComputeHash( const ArgsType&... Args ) + { + std::size_t Seed = 0; + HashCombine( Seed, Args... ); + return Seed; + } + + template + struct CStringHash + { + size_t operator()( const CharType *str ) const + { + // http://www.cse.yorku.ca/~oz/hash.html + std::size_t Seed = 0; + while( size_t Ch = *(str++) ) + Seed = Seed * 65599 + Ch; + return Seed; + } + }; + + template + struct CStringCompare + { + bool operator()( const Char *str1, const Char *str2 )const + { + UNSUPPORTED( "Template specialization is not implemented" ) + return false; + } + }; + + template<> + struct CStringCompare + { + bool operator()( const Char *str1, const Char *str2 )const + { + return strcmp( str1, str2 ) == 0; + } + }; + + /// This helper structure is intended to facilitate using strings as a + /// hash table key. It provides constructors that can make a copy of the + /// source string or just keep pointer to it, which enables searching in + /// the hash using raw const Char* pointers. + struct HashMapStringKey + { + public: + // This constructor can perform implicit const Char* -> HashMapStringKey + // conversion without copying the string + HashMapStringKey(const Char* Str, bool bMakeCopy = false) : + StrPtr(nullptr), + Hash(0) + { + VERIFY( Str, "String pointer cannot be null" ); + if( bMakeCopy ) + { + MakeCopy( Str ); + } + else + { + StrPtr = Str; + } + } + + explicit // Make this constructor explicit to avoid unintentional string copies + HashMapStringKey(const String &Str) : + StrPtr( nullptr ), + Hash(0) + { + MakeCopy( Str.c_str() ); + } + + HashMapStringKey(HashMapStringKey &&Key) : + StringBuff( std::move(Key.StringBuff) ), + StrPtr( std::move(Key.StrPtr) ), + Hash(0) + { + Key.StrPtr = nullptr; + Key.Hash = 0; + } + + // Disable copy constuctor and assignments. The struct is designed + // to be initialized at creation time only + HashMapStringKey( const HashMapStringKey& ) = delete; + HashMapStringKey& operator = ( const HashMapStringKey& ) = delete; + HashMapStringKey& operator = ( HashMapStringKey&& ) = delete; + + // Comparison operator + bool operator == (const HashMapStringKey& RHS)const + { + if( StrPtr == RHS.StrPtr ) + return true; + + // Hash member might not have been initialized + if( (Hash != 0 && RHS.Hash !=0 && Hash != RHS.Hash) || StrPtr == nullptr || RHS.StrPtr == nullptr ) + return false; + + bool IsEqual = strcmp( StrPtr, RHS.StrPtr ) == 0; + +#if LOG_HASH_CONFLICTS + if( Hash != 0 && RHS.Hash !=0 && Hash == RHS.Hash && !IsEqual ) + { + LOG_WARNING_MESSAGE("Unequal strings \"", StrPtr, "\" and \"", RHS.StrPtr, "\" hashed to the same bucket. " + "You may want to use better hash function. You may disable this warning by defining LOG_HASH_CONFLICTS to 0"); + } +#endif + return IsEqual; + } + + size_t GetHash()const + { + if( Hash == 0 ) + Hash = CStringHash()(StrPtr); + + return Hash; + } + + const Char* GetStr()const{ return StrPtr; } + + private: + void MakeCopy( const Char* Str ) + { + auto LenWithZeroTerm = strlen( Str ) + 1; + StringBuff.reset( new char[ LenWithZeroTerm ] ); + memcpy( StringBuff.get(), Str, LenWithZeroTerm ); + StrPtr = StringBuff.get(); + } + + // !!! WARNING !!! + // We can't use String to store the buffer, because String default + // constructor always allocates memory even when the string is empty, + // nor can we use vector for the same reason + std::unique_ptr< Char[] > StringBuff; // Must be declared first + const Char* StrPtr;// Must be declared after StringBuff + mutable size_t Hash; + }; +} + +namespace std +{ + template<> + struct hash + { + size_t operator()( const Diligent::HashMapStringKey &Key ) const + { + return Key.GetHash(); + } + }; +} diff --git a/Common/include/InterfaceID.h b/Common/include/InterfaceID.h new file mode 100644 index 00000000..ce426b5e --- /dev/null +++ b/Common/include/InterfaceID.h @@ -0,0 +1,50 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include "BasicTypes.h" + +/// Unique identification structures +namespace Diligent +{ + /// Describes unique identifier + struct INTERFACE_ID + { + Diligent::Uint32 Data1; + Diligent::Uint16 Data2; + Diligent::Uint16 Data3; + Diligent::Uint8 Data4[8]; + + bool operator == (const INTERFACE_ID& rhs)const + { + return Data1 == rhs.Data1 && + Data2 == rhs.Data2 && + Data3 == rhs.Data3 && + memcmp(Data4, rhs.Data4, sizeof(Data4)) == 0; + } + }; + + /// Unknown interface + static const INTERFACE_ID IID_Unknown = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } }; +} diff --git a/Common/include/LockHelper.h b/Common/include/LockHelper.h new file mode 100644 index 00000000..5f27aad3 --- /dev/null +++ b/Common/include/LockHelper.h @@ -0,0 +1,133 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include "Atomics.h" + +namespace ThreadingTools +{ + +class LockFlag +{ +public: + enum {LOCK_FLAG_UNLOCKED = 0, LOCK_FLAG_LOCKED = 1}; + LockFlag(Atomics::Long InitFlag = LOCK_FLAG_UNLOCKED) + { + m_Flag.store(InitFlag); + } + + operator Atomics::Long()const{return m_Flag;} + +private: + friend class LockHelper; + Atomics::AtomicLong m_Flag; +}; + +class LockHelper +{ +public: + + LockHelper() : + m_pLockFlag(nullptr) + { + } + LockHelper(LockFlag &LockFlag) : + m_pLockFlag(nullptr) + { + Lock(LockFlag); + } + + LockHelper( LockHelper &&LockHelper ) : + m_pLockFlag( std::move(LockHelper.m_pLockFlag) ) + { + LockHelper.m_pLockFlag = nullptr; + } + + const LockHelper& operator = (LockHelper &&LockHelper) + { + m_pLockFlag = std::move( LockHelper.m_pLockFlag ); + LockHelper.m_pLockFlag = nullptr; + return *this; + } + + ~LockHelper() + { + Unlock(); + } + + static bool UnsafeTryLock(LockFlag &LockFlag) + { + return Atomics::AtomicCompareExchange( LockFlag.m_Flag, + static_cast( LockFlag::LOCK_FLAG_LOCKED ), + static_cast( LockFlag::LOCK_FLAG_UNLOCKED) ) == LockFlag::LOCK_FLAG_UNLOCKED; + } + + bool TryLock(LockFlag &LockFlag) + { + if( UnsafeTryLock( LockFlag) ) + { + m_pLockFlag = &LockFlag; + return true; + } + else + return false; + } + + static void UnsafeLock(LockFlag &LockFlag) + { + while( !UnsafeTryLock( LockFlag ) ) + /*Sleep(5)*/ + // TODO + ; + } + + void Lock(LockFlag &LockFlag) + { + VERIFY( m_pLockFlag == NULL, "Object already locked" ); + // Wait for the flag to become unlocked and lock it + while( !TryLock( LockFlag ) ) + /*Sleep(5)*/ + // TODO + ; + } + + static void UnsafeUnlock(LockFlag &LockFlag) + { + LockFlag.m_Flag = LockFlag::LOCK_FLAG_UNLOCKED; + } + + void Unlock() + { + if( m_pLockFlag ) + UnsafeUnlock(*m_pLockFlag); + m_pLockFlag = NULL; + } + +private: + LockFlag *m_pLockFlag; + LockHelper( const LockHelper &LockHelper ); + const LockHelper& operator = ( const LockHelper &LockHelper ); +}; + +} diff --git a/Common/include/ObjectBase.h b/Common/include/ObjectBase.h new file mode 100644 index 00000000..c7de28c8 --- /dev/null +++ b/Common/include/ObjectBase.h @@ -0,0 +1,83 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the Diligent::ObjectBase template class + +#include "Object.h" +#include "RefCountedObjectImpl.h" + +namespace Diligent +{ + + +#define IMPLEMENT_QUERY_INTERFACE_BODY(InterfaceID, ParentClassName) \ +{ \ + if( ppInterface == nullptr ) \ + return; \ + if( IID == InterfaceID ) \ + { \ + *ppInterface = this; \ + (*ppInterface)->AddRef(); \ + } \ + else \ + { \ + ParentClassName::QueryInterface( IID, ppInterface ); \ + } \ +} + +#define IMPLEMENT_QUERY_INTERFACE(ClassName, InterfaceID, ParentClassName) \ + void ClassName :: QueryInterface(const Diligent::INTERFACE_ID &IID, IObject **ppInterface) \ + IMPLEMENT_QUERY_INTERFACE_BODY(InterfaceID, ParentClassName) + +#define IMPLEMENT_QUERY_INTERFACE_IN_PLACE(InterfaceID, ParentClassName) \ + virtual void QueryInterface(const Diligent::INTERFACE_ID &IID, IObject **ppInterface) \ + IMPLEMENT_QUERY_INTERFACE_BODY(InterfaceID, ParentClassName) + + +/// Template class implementing base functionality for an object +template +class ObjectBase : public RefCountedObject +{ +public: + ObjectBase(IObject *pOwner = nullptr) : + RefCountedObject( pOwner ) + {} + + virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface ) + { + if( ppInterface == nullptr ) + return; + + *ppInterface = nullptr; + if( IID == Diligent::IID_Unknown ) + { + *ppInterface = this; + (*ppInterface)->AddRef(); + } + } +}; + +} diff --git a/Common/include/RefCntAutoPtr.h b/Common/include/RefCntAutoPtr.h new file mode 100644 index 00000000..7df6f246 --- /dev/null +++ b/Common/include/RefCntAutoPtr.h @@ -0,0 +1,391 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include "DebugUtilities.h" +#include "LockHelper.h" +#include "Atomics.h" +#include "ValidatedCast.h" +#include "ReferenceCounters.h" +#include "Object.h" + +namespace Diligent +{ + + +template +class RefCntWeakPtr; + +// The main advantage of RefCntAutoPtr over the std::shared_ptr is that you can +// attach the same raw pointer to different smart pointers. +// +// For instance, the following code will crash since p will be released twice: +// +// auto *p = new char; +// std::shared_ptr pTmp1(p); +// std::shared_ptr pTmp2(p); +// ... + +// This code, in contrast, works perfectly fine: +// +// ObjectBase *pRawPtr(new ObjectBase); +// RefCntAutoPtr pSmartPtr1(pRawPtr); +// RefCntAutoPtr pSmartPtr2(pRawPtr); +// ... + +// Other advantage is that weak pointers remain valid until the +// object is alive, even if all smart pointers were destroyed: +// +// RefCntWeakPtr pWeakPtr(pSmartPtr1); +// pSmartPtr1.Release(); +// auto pSmartPtr3 = pWeakPtr.Lock(); +// .. + +// Weak pointers can also be attached directly to the object: +// RefCntWeakPtr pWeakPtr(pRawPtr); +// + +template +class DefaultBlockAddRefRelease : public T +{ +private: + // Note that the null pointer constant nullptr or any other value of type std::nullptr_t + // cannot be converted to a pointer with reinterpret_cast: implicit conversion or + // static_cast should be used for this purpose. + virtual decltype( static_cast(nullptr)->AddRef() ) AddRef()override = 0; + virtual decltype( static_cast(nullptr)->Release() ) Release()override = 0; +}; + +template class BlockAddRefRelease = DefaultBlockAddRefRelease> +class RefCntAutoPtr +{ +public: + explicit RefCntAutoPtr(T *pObj = nullptr) : + m_pObject(pObj) + { + if( m_pObject ) + m_pObject->AddRef(); + } + + RefCntAutoPtr(const RefCntAutoPtr &AutoPtr) : + m_pObject(AutoPtr.m_pObject) + { + if(m_pObject) + m_pObject->AddRef(); + } + + RefCntAutoPtr(RefCntAutoPtr &&AutoPtr) : + m_pObject(std::move(AutoPtr.m_pObject)) + { + //Make sure original pointer has no references to the object + AutoPtr.m_pObject = nullptr; + } + + ~RefCntAutoPtr() + { + Release(); + } + + void swap(RefCntAutoPtr &AutoPtr) + { + std::swap(m_pObject, AutoPtr.m_pObject); + } + + void Attach(T *pObj) + { + Release(); + m_pObject = pObj; + } + + T* Detach() + { + T* pObj = m_pObject; + m_pObject = nullptr; + return pObj; + } + + void Release() + { + if( m_pObject ) + { + m_pObject->Release(); + m_pObject = nullptr; + } + } + + RefCntAutoPtr& operator = (T *pObj) + { + if( static_cast(*this) == pObj ) + return *this; + + return operator= (RefCntAutoPtr(pObj)); + } + + RefCntAutoPtr& operator = (const RefCntAutoPtr &AutoPtr) + { + if( *this == AutoPtr ) + return *this; + + Release(); + m_pObject = AutoPtr.m_pObject; + if(m_pObject) + m_pObject->AddRef(); + + return *this; + } + + RefCntAutoPtr& operator = (RefCntAutoPtr &&AutoPtr) + { + if( *this == AutoPtr ) + return *this; + + Release(); + m_pObject = std::move( AutoPtr.m_pObject ); + //Make sure original pointer has no references to the object + AutoPtr.m_pObject = nullptr; + return *this; + } + + // All the access functions do not require locking reference counters pointer because if it is valid, + // the smart pointer holds strong reference to the object and it thus cannot be released by + // ohter thread + bool operator ! () const{return m_pObject == nullptr;} + operator bool () const{return m_pObject != nullptr;} + bool operator == (const RefCntAutoPtr& Ptr)const{return m_pObject == Ptr.m_pObject;} + bool operator != (const RefCntAutoPtr& Ptr)const{return m_pObject != Ptr.m_pObject;} + bool operator < (const RefCntAutoPtr& Ptr)const{return static_cast(*this) < static_cast(Ptr);} + + T& operator * () { return *m_pObject; } + const T& operator * ()const { return *m_pObject; } + + T* RawPtr() { return m_pObject; } + const T* RawPtr()const{ return m_pObject; } + + operator T* () { return RawPtr(); } + operator const T* ()const { return RawPtr(); } + + + BlockAddRefRelease* operator -> () { return static_cast*> (m_pObject); } + const BlockAddRefRelease* operator -> ()const{ return static_cast*> (m_pObject); } + +private: + // Note that the DoublePtrHelper is a private class, and can be created only by RefCntWeakPtr + // Thus if no special effort is made, the lifetime of the instances of this class cannot be + // longer than the lifetime of the creating object + class DoublePtrHelper + { + public: + DoublePtrHelper(RefCntAutoPtr &AutoPtr) : + NewRawPtr( static_cast(AutoPtr) ), + m_pAutoPtr( std::addressof(AutoPtr) ) + { + } + + DoublePtrHelper(DoublePtrHelper&& Helper) : + NewRawPtr(Helper.NewRawPtr), + m_pAutoPtr(Helper.m_pAutoPtr) + { + Helper.m_pAutoPtr = nullptr; + Helper.NewRawPtr = nullptr; + } + + ~DoublePtrHelper() + { + if( m_pAutoPtr && static_cast(*m_pAutoPtr) != NewRawPtr ) + { + m_pAutoPtr->Attach(NewRawPtr); + } + } + + T*& operator*(){return NewRawPtr;} + const T* operator*()const{return NewRawPtr;} + + operator T**(){return &NewRawPtr;} + operator const T**()const{return &NewRawPtr;} + private: + T *NewRawPtr; + RefCntAutoPtr *m_pAutoPtr; + DoublePtrHelper(const DoublePtrHelper&); + DoublePtrHelper& operator = (const DoublePtrHelper&); + DoublePtrHelper& operator = (DoublePtrHelper&&); + }; + +public: + DoublePtrHelper operator& () + { + return DoublePtrHelper(*this); + } + + const DoublePtrHelper operator& ()const + { + return DoublePtrHelper(*this); + } + +private: + T *m_pObject; +}; + +/// Implementation of weak pointers +template +class RefCntWeakPtr +{ +public: + explicit RefCntWeakPtr(T *pObj = nullptr) : + m_pObject(pObj), + m_pRefCounters(nullptr) + { + if( m_pObject ) + { + m_pRefCounters = m_pObject->GetReferenceCounters(); + m_pRefCounters->AddWeakRef(); + } + } + + ~RefCntWeakPtr() + { + Release(); + } + + RefCntWeakPtr(const RefCntWeakPtr& WeakPtr) : + m_pObject(WeakPtr.m_pObject), + m_pRefCounters(WeakPtr.m_pRefCounters) + { + if( m_pRefCounters ) + m_pRefCounters->AddWeakRef(); + } + + RefCntWeakPtr(RefCntWeakPtr&& WeakPtr) : + m_pObject(std::move(WeakPtr.m_pObject)), + m_pRefCounters(std::move(WeakPtr.m_pRefCounters)) + { + WeakPtr.m_pRefCounters = nullptr; + WeakPtr.m_pObject = nullptr; + } + + explicit RefCntWeakPtr(RefCntAutoPtr& AutoPtr) : + m_pObject( static_cast(AutoPtr) ), + m_pRefCounters(AutoPtr ? AutoPtr->GetReferenceCounters() : nullptr) + { + if( m_pRefCounters ) + m_pRefCounters->AddWeakRef(); + } + + RefCntWeakPtr& operator = (const RefCntWeakPtr& WeakPtr) + { + if( *this == WeakPtr ) + return *this; + + Release(); + m_pObject = WeakPtr.m_pObject; + m_pRefCounters = WeakPtr.m_pRefCounters; + if( m_pRefCounters ) + m_pRefCounters->AddWeakRef(); + return *this; + } + + RefCntWeakPtr& operator = (T *pObj) + { + return operator= (RefCntWeakPtr(pObj)); + } + + RefCntWeakPtr& operator = (RefCntWeakPtr&& WeakPtr) + { + if( *this == WeakPtr ) + return *this; + + Release(); + m_pObject = std::move(WeakPtr.m_pObject); + m_pRefCounters = std::move(WeakPtr.m_pRefCounters); + WeakPtr.m_pRefCounters = nullptr; + WeakPtr.m_pObject = nullptr; + return *this; + } + + RefCntWeakPtr& operator = (RefCntAutoPtr& AutoPtr) + { + Release(); + m_pObject = static_cast( AutoPtr ); + m_pRefCounters = m_pObject ? m_pObject->GetReferenceCounters() : nullptr; + if( m_pRefCounters ) + m_pRefCounters->AddWeakRef(); + return *this; + } + + void Release() + { + if( m_pRefCounters ) + m_pRefCounters->ReleaseWeakRef(); + m_pRefCounters = nullptr; + m_pObject = nullptr; + } + + /// \note This method may not be reliable in a multithreaded environment. + /// However, when false is returned, the strong pointer created from + /// this weak pointer will reliably be empty. + bool IsValid() + { + return m_pObject != nullptr && m_pRefCounters != nullptr && m_pRefCounters->GetNumStrongRefs() > 0; + } + + /// Obtains a strong reference to the object + RefCntAutoPtr Lock() + { + RefCntAutoPtr spObj; + if( m_pRefCounters ) + { + // Try to obtain pointer to the owner object. + // spOwner is only used to keep the object + // alive while obtaining strong reference from + // the raw pointer m_pObject + RefCntAutoPtr spOwner; + m_pRefCounters->GetObject( &spOwner ); + if( spOwner ) + { + // If owner is alive, we can use our RAW pointer to + // create strong reference + spObj = m_pObject; + } + else + { + // Owner object has been destroyed. There is no reason + // to keep this weak reference anymore + Release(); + } + } + return spObj; + } + + bool operator == (const RefCntWeakPtr& Ptr)const{return m_pRefCounters == Ptr.m_pRefCounters;} + bool operator != (const RefCntWeakPtr& Ptr)const{return m_pRefCounters != Ptr.m_pRefCounters;} + +protected: + Diligent::IReferenceCounters *m_pRefCounters; + // We need to store raw pointer to object itself, + // because if the object is owned by another object, + // m_pRefCounters->GetObject( &pObj ) will return + // pointer to owner, which is not what we need. + T *m_pObject; +}; + +} diff --git a/Common/include/RefCountedObjectImpl.h b/Common/include/RefCountedObjectImpl.h new file mode 100644 index 00000000..dc5f7233 --- /dev/null +++ b/Common/include/RefCountedObjectImpl.h @@ -0,0 +1,332 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Implementation of the template base class for reference counting objects + +#include "Object.h" +#include "Atomics.h" +#include "DebugUtilities.h" +#include "LockHelper.h" +#include "ValidatedCast.h" + +namespace Diligent +{ + +/// Base class for reference counting objects +template +class RefCountedObject : public Base +{ +public: + RefCountedObject(IObject *pOwner = nullptr) : + m_pRefCounters(nullptr) + { + if( pOwner ) + { + auto *pRefCounters = pOwner->GetReferenceCounters(); + VERIFY(pRefCounters, "Reference counters are not initialized in the owner object"); + m_pRefCounters = ValidatedCast( pRefCounters ); + } + else + { + m_pRefCounters = RefCountersImpl::Create(this); + } + }; + + virtual ~RefCountedObject() + { + // WARNING! m_pRefCounters pointer might be expired in scenarios like this: + // + // A ==sp==> B ---wp---> A + // + // RefCounters_A.ReleaseStrongRef(){ // NumStrongRef == 0, NumWeakRef == 1 + // delete A{ + // A.~dtor(){ + // B.~dtor(){ + // wpA.ReleaseWeakRef(){ // NumStrongRef == 0, NumWeakRef == 0 + // delete RefCounters_A; + // ... + // VERIFY( m_pRefCounters->GetNumStrongRefs() == 0 // Access violation! + + // This also may happen if one thread is executing ReleaseStrongRef(), while + // another one is simultaneously running ReleaseWeakRef(). + + //VERIFY( m_pRefCounters->GetNumStrongRefs() == 0, + // "There remain strong references to the object being destroyed" ); + }; + + virtual IReferenceCounters* GetReferenceCounters()const override + { + return m_pRefCounters; + } + + virtual Atomics::Long AddRef()override + { + return m_pRefCounters->AddStrongRef(); + } + + virtual Atomics::Long Release()override + { + return m_pRefCounters->ReleaseStrongRef(); + } + + +private: + + class RefCountersImpl : public IReferenceCounters + { + public: + static RefCountersImpl* Create( RefCountedObject *pOwner ) + { + return new RefCountersImpl( pOwner ); + } + + virtual Atomics::Long AddStrongRef()override + { + VERIFY( m_pObject, "Attempting to increment strong reference counter for a destroyed object!" ); + return Atomics::AtomicIncrement(m_lNumStrongReferences); + } + + virtual Atomics::Long ReleaseStrongRef()override + { + // If the object is about to be destroyed, we must assure that no other + // thread is accessing the ENTIRE REFERENCE COUNTERS OBJECT at the same time. + // (Protecting only the pointer is not sufficient!) + // The problem may arise if a weak pointer in some other thread is trying to + // obtain access to the object. + // The safest way is to always protect the entire function: + ThreadingTools::LockHelper Lock(m_LockFlag); + + // It is unsafe to not always lock. + // For instance, locking if there is only one strong reference left + // if( m_lNumStrongReferences == 1 ) + // Lock.Lock( m_LockFlag ); + // may fail. Suppose the following scenario: + // + // This thread | Another thread + // | 1. Start releasing another strong + // | reference to this object + // 1. Read m_lNumStrongReferences==2 | 2. Read m_lNumStrongReferences==2 + // No lock acquired | 3. Decrement the counter, + // | m_lNumStrongReferences==1 + // 2. Decrement the counter, | + // m_lNumStrongReferences==0, | + // and the object will be | + // destroyed without locking | + + + // Likewise locking if there is at least one weak reference + // if( m_lNumWeakReferences > 0 ) + // Lock.Lock( m_LockFlag ); + // may also fail. Suppose the following scenario: + // + // This thread | Another thread + // | + // 1. Read m_lNumWeakReferences==0 | 1. Start creating weak reference + // No lock acquired | from another strong reference object + // | 2. Call AddWeakRef(), + // | m_lNumWeakReferences==1 + // | 3. Call Release() on the original strong + // | referenceo bject, m_lNumStrongReferences==1 + // | 4. Start creating another strong reference + // | from the weak pointer, acquired lock and + // | read m_pObject + // 2. Destroy the object | + // | 5. Attempt to create strong reference from + // | invalidated pointer + + // Both situations are unlikely to happen. However, they show that + // conditional locking is not safe. There might be other more probable + // situations + auto RefCount = Atomics::AtomicDecrement(m_lNumStrongReferences); + VERIFY( RefCount >= 0, "Inconsistent call to ReleaseStrongRef()" ); + if( RefCount == 0 ) + { + // Locking the object here is also not safe as antoher thread + // may be running GetObject(). If it obtains the lock first, it will + // get the pointer to the object which will then be destroyed by this + // thread + + VERIFY(m_pObject, "Object pointer is null, which means it has already been destroyed"); + // There are no more STRONG references to the object and it is about to be + // destroyed. There could be weak references, so reference counters + // can remain alive after the object itself is destroyed. + + // Note that since reference counters are locked, no weak pointers can access + // m_pObject while the object is being deleted. + + // We cannot destroy the object while reference counters are locked as this will + // cause a deadlock in cases like this: + // + // A ==sp==> B ---wp---> A + // + // RefCounters_A.Lock(); + // delete A{ + // A.~dtor(){ + // B.~dtor(){ + // wpA.ReleaseWeakRef(){ + // RefCounters_A.Lock(); // Deadlock + // + + // So we store the pointer to the object and destory it after unlocking the + // reference counters + auto *pObj = m_pObject; + + // In a multithreaded environment, reference counters object may + // be destroyed at any time while m_pObject->~dtor() is running. + // NOTE: m_pObject may not be the only object referencing m_pRefCounters. + // All objects that are owned by m_pObject will point to the same + // reference counters object. + m_pObject->m_pRefCounters = nullptr; + + // Note that this is the only place where m_pObject member can be modified + // after the reference counters object has been created + m_pObject = nullptr; + // The object is now detached from the reference counters and it is if + // it was destroyed since no one can obtain access to it. + + // It is essentially important to check the number of weak references + // while the object is locked. Otherwise reference counters object + // may be destroyed twice if ReleaseWeakRef() is executed by other thread: + // + // This thread | Another thread - ReleaseWeakRef() + // | + // 1. Decrement m_lNumStrongReferences,| 1. Decrement m_lNumWeakReferences, + // m_lNumStrongReferences==0 | m_lNumWeakReferences == 0 + // | + // 2. Destroy the object | 2. Destroy the object + // + bool bDestroyThis = m_lNumWeakReferences == 0; + + // We must explicitly unlock the object now to avoid deadlocks. Also, + // if this is deleted, this->m_LockFlag will expire, which will cause + // Lock.~LockHelper() to crash + Lock.Unlock(); + + // Destroy referenced object + delete pObj; + + // Note that this may be destroyed here already, + // see comments in ~RefCountedObject() + if( bDestroyThis ) + delete this; + } + + return RefCount; + } + + virtual Atomics::Long AddWeakRef()override + { + return Atomics::AtomicIncrement(m_lNumWeakReferences); + } + + virtual Atomics::Long ReleaseWeakRef()override + { + ThreadingTools::LockHelper Lock(m_LockFlag); + // It is essentially important to check the number of references + // while the object is locked. Otherwise reference counters object + // may be destroyed twice if ReleaseStrongRef() is executed by other + // thread. + auto NumWeakReferences = Atomics::AtomicDecrement(m_lNumWeakReferences); + VERIFY( NumWeakReferences >= 0, "Inconsistent call to ReleaseWeakRef()" ); + if( NumWeakReferences == 0 && m_lNumStrongReferences == 0 ) + { + // There are no more references to the ref counters object. + // We can safely unlock it and destroy. + // If we do not unlock it, this->m_LockFlag will expire, + // which will cause Lock.~LockHelper() to crash. + Lock.Unlock(); + delete this; + } + return NumWeakReferences; + } + + virtual void GetObject( class IObject **ppObject )override + { + // We need to lock the object before accessing it to prevent + // deletion of the referenced object in another thread. + // The thread which is about to delete the object locks it and + // decrements the reference counter only after it gets exclusive access. + // So if we obtain mutex first, we will increase the reference counter + // before the other thread decrements it. Thus the object will not + // be deleted. + ThreadingTools::LockHelper Lock(m_LockFlag); + if( m_pObject ) + { + // QueryInterface() must not lock the object, or a deadlock happens. + // The only other two methods that lock the object are ReleaseStrongRef() + // and ReleaseWeakRef(), which are never called by QueryInterface() + m_pObject->QueryInterface(Diligent::IID_Unknown, ppObject); + } + } + + virtual Atomics::Long GetNumStrongRefs()const override + { + return m_lNumStrongReferences; + } + + virtual Atomics::Long GetNumWeakRefs()const override + { + return m_lNumWeakReferences; + } + + private: + RefCountersImpl(RefCountedObject *pOwner) : + m_pObject(pOwner) + { + VERIFY(m_pObject, "Owner must not be null"); + m_lNumStrongReferences = 0; + m_lNumWeakReferences = 0; + } + + ~RefCountersImpl() + { + VERIFY( m_lNumStrongReferences == 0 && m_lNumWeakReferences == 0, + "There exist outstanding references to the object being destroyed" ); + } + + // No copies/moves + RefCountersImpl(const RefCountersImpl&); + RefCountersImpl(RefCountersImpl&&); + RefCountersImpl& operator = (const RefCountersImpl&); + RefCountersImpl& operator = (RefCountersImpl&&); + + // It is crucially important that the type of the pointer + // is RefCountedObject and not IObject, since the latter + // does not have virtual dtor. + RefCountedObject *m_pObject; + Atomics::AtomicLong m_lNumStrongReferences; + Atomics::AtomicLong m_lNumWeakReferences; + ThreadingTools::LockFlag m_LockFlag; + }; + + // Reference counters pointer cannot be of type RefCountersImpl*, + // because when an owner pointer is provided to RefCountedObject(), + // the type of pOwner->GetReferenceCounters() may not be convertible + // to RefCountedObject::RefCountersImpl*. + IReferenceCounters *m_pRefCounters; +}; + +} diff --git a/Common/include/StringTools.h b/Common/include/StringTools.h new file mode 100644 index 00000000..687eeef8 --- /dev/null +++ b/Common/include/StringTools.h @@ -0,0 +1,75 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include +#include +#include + +namespace Diligent +{ + +inline std::string NarrowString(const std::wstring &WideStr) +{ + std::string NarrowStr; + const std::ctype& ctfacet = std::use_facet< std::ctype >( std::wstringstream().getloc() ) ; + for( std::wstring::const_iterator CurrWChar = WideStr.begin(); + CurrWChar != WideStr.end(); + CurrWChar++ ) + NarrowStr.push_back( ctfacet.narrow( *CurrWChar, 0 ) ); + + return NarrowStr; +} + +inline std::wstring WidenString(const std::string &Str) +{ + std::wstring WideStr; + const std::ctype& ctfacet = std::use_facet< std::ctype >( std::wstringstream().getloc() ) ; + for( std::string::const_iterator CurrChar = Str.begin(); + CurrChar != Str.end(); + CurrChar++ ) + WideStr.push_back( ctfacet.widen( *CurrChar ) ); + + return WideStr; +} + +inline int StrCmpNoCase(const char* Str1, const char* Str2, size_t NumChars) +{ +#ifdef PLATFORM_ANDROID +# define _strnicmp strncasecmp +#endif + + return _strnicmp( Str1, Str2, NumChars ); +} + +inline int StrCmpNoCase(const char* Str1, const char* Str2) +{ +#ifdef PLATFORM_ANDROID +# define _stricmp strcasecmp +#endif + + return _stricmp( Str1, Str2 ); +} + +} diff --git a/Common/include/Timer.h b/Common/include/Timer.h new file mode 100644 index 00000000..5e3dfe54 --- /dev/null +++ b/Common/include/Timer.h @@ -0,0 +1,41 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include + +namespace Diligent +{ + class Timer + { + public: + Timer(); + void Restart(); + double GetElapsedTime()const; + float GetElapsedTimef()const; + + private: + std::chrono::high_resolution_clock::time_point m_StartTime; + }; +} diff --git a/Common/include/UniqueIdentifier.h b/Common/include/UniqueIdentifier.h new file mode 100644 index 00000000..e4fd7159 --- /dev/null +++ b/Common/include/UniqueIdentifier.h @@ -0,0 +1,79 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include "Atomics.h" + +namespace Diligent +{ + typedef Atomics::Long UniqueIdentifier; + + // Template switch is used to have distinct counters + // for unrelated groups of objects + template + class UniqueIdHelper + { + public: + UniqueIdHelper() : + m_ID(0), + m_bIsInitialized( false ) + {} + + UniqueIdHelper( UniqueIdHelper&& RHS) : + m_ID( RHS.m_ID ), + m_bIsInitialized( RHS.m_bIsInitialized ) + { + RHS.m_ID = 0; + RHS.m_bIsInitialized = false; + } + + const UniqueIdHelper& operator = (UniqueIdHelper&& RHS) + { + m_ID = RHS.m_ID; + m_bIsInitialized = RHS.m_bIsInitialized; + RHS.m_ID = 0; + RHS.m_bIsInitialized = false; + + return *this; + } + + UniqueIdentifier GetID()const + { + if( !m_bIsInitialized ) + { + static Atomics::AtomicLong sm_GlobalCounter; + m_ID = Atomics::AtomicIncrement(sm_GlobalCounter); + m_bIsInitialized = true; + } + return m_ID; + } + + private: + UniqueIdHelper( const UniqueIdHelper& ); + const UniqueIdHelper& operator = ( const UniqueIdHelper& ); + + mutable UniqueIdentifier m_ID; + mutable bool m_bIsInitialized; + }; +} diff --git a/Common/include/ValidatedCast.h b/Common/include/ValidatedCast.h new file mode 100644 index 00000000..916148c4 --- /dev/null +++ b/Common/include/ValidatedCast.h @@ -0,0 +1,33 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include "DebugUtilities.h" + +template +DstType* ValidatedCast( SrcType *Ptr ) +{ + CHECK_DYNAMIC_TYPE( DstType, Ptr ); + return static_cast( Ptr ); +} diff --git a/Common/include/pch.h b/Common/include/pch.h new file mode 100644 index 00000000..8b5e042b --- /dev/null +++ b/Common/include/pch.h @@ -0,0 +1,35 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "PlatformDefinitions.h" +#include "BasicTypes.h" +#include "Errors.h" +#include "FileWrapper.h" +#include "RefCntAutoPtr.h" \ No newline at end of file -- cgit v1.2.3