diff options
Diffstat (limited to 'src/foundation')
124 files changed, 28985 insertions, 0 deletions
diff --git a/src/foundation/AutoDeallocatorAllocator.h b/src/foundation/AutoDeallocatorAllocator.h new file mode 100644 index 0000000..5cbbc8b --- /dev/null +++ b/src/foundation/AutoDeallocatorAllocator.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DS_FOUNDATION_AUTO_DEALLOCATOR_ALLOCATOR_H +#define QT3DS_FOUNDATION_AUTO_DEALLOCATOR_ALLOCATOR_H +#pragma once + +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSContainers.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" + +namespace qt3ds { +namespace foundation { + + using eastl::make_pair; + using eastl::pair; + + struct SSAutoDeallocatorAllocator : public NVAllocatorCallback + { + NVAllocatorCallback &m_Allocator; + nvhash_map<void *, size_t> m_Allocations; + SSAutoDeallocatorAllocator(NVFoundationBase &inFnd) + : m_Allocator(inFnd.getAllocator()) + , m_Allocations(inFnd.getAllocator(), "SSAutoDeallocatorAllocator::m_Allocations") + { + } + + SSAutoDeallocatorAllocator(NVAllocatorCallback &inAlloc) + : m_Allocator(inAlloc) + , m_Allocations(inAlloc, "SSAutoDeallocatorAllocator::m_Allocations") + { + } + + // Automatically deallocates everything that hasn't already been deallocated. + ~SSAutoDeallocatorAllocator() { deallocateAllAllocations(); } + + void deallocateAllAllocations() + { + for (nvhash_map<void *, size_t>::iterator iter = m_Allocations.begin(), + end = m_Allocations.end(); + iter != end; ++iter) + m_Allocator.deallocate(iter->first); + m_Allocations.clear(); + } + + void *allocate(size_t size, const char *typeName, const char *filename, int line, + int flags = 0) override + { + void *value = m_Allocator.allocate(size, typeName, filename, line, flags); + m_Allocations.insert(make_pair(value, size)); + return value; + } + + void *allocate(size_t size, const char *typeName, const char *filename, int line, + size_t alignment, size_t alignmentOffset) override + { + void *value = + m_Allocator.allocate(size, typeName, filename, line, alignment, alignmentOffset); + m_Allocations.insert(make_pair(value, size)); + return value; + } + void deallocate(void *ptr) override + { + nvhash_map<void *, size_t>::iterator iter = m_Allocations.find(ptr); + QT3DS_ASSERT(iter != m_Allocations.end()); + m_Allocator.deallocate(iter->first); + m_Allocations.erase(iter); + } + }; +} +} + +#endif diff --git a/src/foundation/ConvertUTF.cpp b/src/foundation/ConvertUTF.cpp new file mode 100644 index 0000000..a71a3ae --- /dev/null +++ b/src/foundation/ConvertUTF.cpp @@ -0,0 +1,661 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +* Copyright 2001-2004 Unicode, Inc. +* +* Disclaimer +* +* This source code is provided as is by Unicode, Inc. No claims are +* made as to fitness for any particular purpose. No warranties of any +* kind are expressed or implied. The recipient agrees to determine +* applicability of information provided. If this file has been +* purchased on magnetic or optical media from Unicode, Inc., the +* sole remedy for any claim will be exchange of defective media +* within 90 days of receipt. +* +* Limitations on Rights to Redistribute This Code +* +* Unicode, Inc. hereby grants the right to freely use the information +* supplied in this file in the creation of products supporting the +* Unicode Standard, and to make copies of this file in any form +* for internal or external distribution as long as this notice +* remains attached. +*/ + +/* --------------------------------------------------------------------- + Conversions between UTF32, UTF-16, and UTF-8. Source code file. + Author: Mark E. Davis, 1994. + Rev History: Rick McGowan, fixes & updates May 2001. + Sept 2001: fixed const & error conditions per + mods suggested by S. Parent & A. Lillich. + June 2002: Tim Dodd added detection and handling of incomplete + source sequences, enhanced error detection, added casts + to eliminate compiler warnings. + July 2003: slight mods to back out aggressive FFFE detection. + Jan 2004: updated switches in from-UTF8 conversions. + Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. + See the header file "ConvertUTF.h" for complete documentation. +------------------------------------------------------------------------ */ + +#include "foundation/ConvertUTF.h" + +#ifdef _MSC_VER +#pragma warning(disable : 4365) // warnings on conversion from unsigned int to int +#endif + +#ifdef CVTUTF_DEBUG +#include <stdio.h> +#endif + +static const int halfShift = 10; /* used for shifting by 10 bits */ + +static const UTF32 halfBase = 0x0010000UL; +static const UTF32 halfMask = 0x3FFUL; + +#define UNI_SUR_HIGH_START (UTF32)0xD800 +#define UNI_SUR_HIGH_END (UTF32)0xDBFF +#define UNI_SUR_LOW_START (UTF32)0xDC00 +#define UNI_SUR_LOW_END (UTF32)0xDFFF +#define false 0 +#define true 1 + +/* --------------------------------------------------------------------- */ + +ConversionResult Q3DSConvertUTF32toUTF16(const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, + ConversionFlags flags) +{ + ConversionResult result = conversionOK; + const UTF32 *source = *sourceStart; + UTF16 *target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + if (target >= targetEnd) { + result = targetExhausted; + break; + } + ch = *source++; + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved + * values */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_LEGAL_UTF32) { + if (flags == strictConversion) { + result = sourceIllegal; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + --source; /* Back up source pointer! */ + result = targetExhausted; + break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult Q3DSConvertUTF16toUTF32(const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, + ConversionFlags flags) +{ + ConversionResult result = conversionOK; + const UTF16 *source = *sourceStart; + UTF32 *target = *targetStart; + UTF32 ch, ch2; + while (source < sourceEnd) { + const UTF16 *oldSource = + source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + (ch2 - UNI_SUR_LOW_START) + + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + if (target >= targetEnd) { + source = oldSource; /* Back up source pointer! */ + result = targetExhausted; + break; + } + *target++ = ch; + } + *sourceStart = source; + *targetStart = target; +#ifdef CVTUTF_DEBUG + if (result == sourceIllegal) { + fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); + fflush(stderr); + } +#endif + return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Index into the table below with the first byte of a UTF-8 sequence to + * get the number of trailing bytes that are supposed to follow it. + * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is + * left as-is for anyone who may want to do such conversion, which was + * allowed in earlier algorithms. + */ +static const char trailingBytesForUTF8[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 +}; + +/* + * Magic values subtracted from a buffer value during UTF8 conversion. + * This table contains as many values as there might be trailing bytes + * in a UTF-8 sequence. + */ +static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; + +/* + * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed + * into the first byte, depending on how many bytes follow. There are + * as many entries in this table as there are UTF-8 sequence types. + * (I.e., one byte sequence, two byte... etc.). Remember that sequencs + * for *legal* UTF-8 will be 4 or fewer bytes total. + */ +static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +/* --------------------------------------------------------------------- */ + +/* The interface converts a whole buffer to avoid function-call overhead. + * Constants have been gathered. Loops & conditionals have been removed as + * much as possible for efficiency, in favor of drop-through switches. + * (See "Note A" at the bottom of the file for equivalent code.) + * If your compiler supports it, the "isLegalUTF8" call can be turned + * into an inline function. + */ + +/* --------------------------------------------------------------------- */ + +ConversionResult Q3DSConvertUTF16toUTF8(const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) +{ + ConversionResult result = conversionOK; + const UTF16 *source = *sourceStart; + UTF8 *target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + const UTF16 *oldSource = + source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + UTF32 ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + (ch2 - UNI_SUR_LOW_START) + + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* Figure out how many bytes the result will require */ + if (ch < (UTF32)0x80) { + bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { + bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { + bytesToWrite = 3; + } else if (ch < (UTF32)0x110000) { + bytesToWrite = 4; + } else { + bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + } + + target += bytesToWrite; + if (target > targetEnd) { + source = oldSource; /* Back up source pointer! */ + target -= bytesToWrite; + result = targetExhausted; + break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 3: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 2: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 1: + *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Utility routine to tell whether a sequence of bytes is legal UTF-8. + * This must be called with the length pre-determined by the first byte. + * If not calling this from ConvertUTF8to*, then the length can be set by: + * length = trailingBytesForUTF8[*source]+1; + * and the sequence is illegal right away if there aren't that many bytes + * available. + * If presented with a length > 4, this returns false. The Unicode + * definition of UTF-8 goes up to 4-byte sequences. + */ + +static Boolean isLegalUTF8(const UTF8 *source, int length) +{ + UTF8 a; + const UTF8 *srcptr = source + length; + switch (length) { + default: + return false; + /* Everything else falls through when "true"... */ + case 4: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) + return false; + case 3: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) + return false; + case 2: + if ((a = (*--srcptr)) > 0xBF) + return false; + + switch (*source) { + /* no fall-through in this inner switch */ + case 0xE0: + if (a < 0xA0) + return false; + break; + case 0xED: + if (a > 0x9F) + return false; + break; + case 0xF0: + if (a < 0x90) + return false; + break; + case 0xF4: + if (a > 0x8F) + return false; + break; + default: + if (a < 0x80) + return false; + } + + case 1: + if (*source >= 0x80 && *source < 0xC2) + return false; + } + if (*source > 0xF4) + return false; + return true; +} + +/* --------------------------------------------------------------------- */ + +/* + * Exported function to return whether a UTF-8 sequence is legal or not. + * This is not used here; it's just exported. + */ +Boolean Q3DSIsLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) +{ + int length = trailingBytesForUTF8[*source] + 1; + if (source + length > sourceEnd) { + return false; + } + return isLegalUTF8(source, length); +} + +/* --------------------------------------------------------------------- */ + +ConversionResult Q3DSConvertUTF8toUTF16(const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, + ConversionFlags flags) +{ + ConversionResult result = conversionOK; + const UTF8 *source = *sourceStart; + UTF16 *target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; + break; + } + /* Do this check whether lenient or strict */ + if (!isLegalUTF8(source, extraBytesToRead + 1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: + ch += *source++; + ch <<= 6; /* remember, illegal UTF-8 */ + case 4: + ch += *source++; + ch <<= 6; /* remember, illegal UTF-8 */ + case 3: + ch += *source++; + ch <<= 6; + case 2: + ch += *source++; + ch <<= 6; + case 1: + ch += *source++; + ch <<= 6; + case 0: + ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead + 1); /* Back up source pointer! */ + result = targetExhausted; + break; + } + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead + 1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_UTF16) { + if (flags == strictConversion) { + result = sourceIllegal; + source -= (extraBytesToRead + 1); /* return to the start */ + break; /* Bail out; shouldn't continue */ + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + source -= (extraBytesToRead + 1); /* Back up source pointer! */ + result = targetExhausted; + break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult Q3DSConvertUTF32toUTF8(const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) +{ + ConversionResult result = conversionOK; + const UTF32 *source = *sourceStart; + UTF8 *target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + ch = *source++; + if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* + * Figure out how many bytes the result will require. Turn any + * illegally large UTF32 things (> Plane 17) into replacement chars. + */ + if (ch < (UTF32)0x80) { + bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { + bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { + bytesToWrite = 3; + } else if (ch <= UNI_MAX_LEGAL_UTF32) { + bytesToWrite = 4; + } else { + bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + result = sourceIllegal; + } + + target += bytesToWrite; + if (target > targetEnd) { + --source; /* Back up source pointer! */ + target -= bytesToWrite; + result = targetExhausted; + break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 3: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 2: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 1: + *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult Q3DSConvertUTF8toUTF32(const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, + ConversionFlags flags) +{ + ConversionResult result = conversionOK; + const UTF8 *source = *sourceStart; + UTF32 *target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; + break; + } + /* Do this check whether lenient or strict */ + if (!isLegalUTF8(source, extraBytesToRead + 1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: + ch += *source++; + ch <<= 6; + case 4: + ch += *source++; + ch <<= 6; + case 3: + ch += *source++; + ch <<= 6; + case 2: + ch += *source++; + ch <<= 6; + case 1: + ch += *source++; + ch <<= 6; + case 0: + ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead + 1); /* Back up the source pointer! */ + result = targetExhausted; + break; + } + if (ch <= UNI_MAX_LEGAL_UTF32) { + /* + * UTF-16 surrogate values are illegal in UTF-32, and anything + * over Plane 17 (> 0x10FFFF) is illegal. + */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead + 1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = ch; + } + } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ + result = sourceIllegal; + *target++ = UNI_REPLACEMENT_CHAR; + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- + + Note A. + The fall-through switches in UTF-8 reading code save a + temp variable, some decrements & conditionals. The switches + are equivalent to the following loop: + { + int tmpBytesToRead = extraBytesToRead+1; + do { + ch += *source++; + --tmpBytesToRead; + if (tmpBytesToRead) ch <<= 6; + } while (tmpBytesToRead > 0); + } + In UTF-8 writing code, the switches on "bytesToWrite" are + similarly unrolled loops. + + --------------------------------------------------------------------- */ diff --git a/src/foundation/ConvertUTF.h b/src/foundation/ConvertUTF.h new file mode 100644 index 0000000..93614a0 --- /dev/null +++ b/src/foundation/ConvertUTF.h @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +/* --------------------------------------------------------------------- + + Conversions between UTF32, UTF-16, and UTF-8. Header file. + + Several funtions are included here, forming a complete set of + conversions between the three formats. UTF-7 is not included + here, but is handled in a separate source file. + + Each of these routines takes pointers to input buffers and output + buffers. The input buffers are const. + + Each routine converts the text between *sourceStart and sourceEnd, + putting the result into the buffer between *targetStart and + targetEnd. Note: the end pointers are *after* the last item: e.g. + *(sourceEnd - 1) is the last item. + + The return result indicates whether the conversion was successful, + and if not, whether the problem was in the source or target buffers. + (Only the first encountered problem is indicated.) + + After the conversion, *sourceStart and *targetStart are both + updated to point to the end of last text successfully converted in + the respective buffers. + + Input parameters: + sourceStart - pointer to a pointer to the source buffer. + The contents of this are modified on return so that + it points at the next thing to be converted. + targetStart - similarly, pointer to pointer to the target buffer. + sourceEnd, targetEnd - respectively pointers to the ends of the + two buffers, for overflow checking only. + + These conversion functions take a ConversionFlags argument. When this + flag is set to strict, both irregular sequences and isolated surrogates + will cause an error. When the flag is set to lenient, both irregular + sequences and isolated surrogates are converted. + + Whether the flag is strict or lenient, all illegal sequences will cause + an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>, + or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code + must check for illegal sequences. + + When the flag is set to lenient, characters over 0x10FFFF are converted + to the replacement character; otherwise (when the flag is set to strict) + they constitute an error. + + Output parameters: + The value "sourceIllegal" is returned from some routines if the input + sequence is malformed. When "sourceIllegal" is returned, the source + value will point to the illegal value that caused the problem. E.g., + in UTF-8 when a sequence is malformed, it points to the start of the + malformed sequence. + + Author: Mark E. Davis, 1994. + Rev History: Rick McGowan, fixes & updates May 2001. + Fixes & updates, Sept 2001. + +------------------------------------------------------------------------ */ + +/* --------------------------------------------------------------------- + The following 4 definitions are compiler-specific. + The C standard does not guarantee that wchar_t has at least + 16 bits, so wchar_t is no less portable than unsigned short! + All should be unsigned values to avoid sign extension during + bit mask & shift operations. +------------------------------------------------------------------------ */ + +typedef unsigned int UTF32; /* at least 32 bits */ +typedef unsigned short UTF16; /* at least 16 bits */ +typedef unsigned char UTF8; /* typically 8 bits */ +typedef unsigned char Boolean; /* 0 or 1 */ + +/* Some fundamental constants */ + +#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD +#define UNI_MAX_BMP (UTF32)0x0000FFFF +#define UNI_MAX_UTF16 (UTF32)0x0010FFFF +#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF +#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF + +typedef enum { + conversionOK, /* conversion successful */ + sourceExhausted, /* partial character in source, but hit end */ + targetExhausted, /* insuff. room in target for conversion */ + sourceIllegal /* source sequence is illegal/malformed */ +} ConversionResult; + +typedef enum { strictConversion = 0, lenientConversion } ConversionFlags; + +/* This is for C++ and does no harm in C */ +#ifdef __cplusplus +extern "C" { +#endif + +ConversionResult Q3DSConvertUTF8toUTF16(const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, + ConversionFlags flags); + +ConversionResult Q3DSConvertUTF16toUTF8(const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, + ConversionFlags flags); + +ConversionResult Q3DSConvertUTF8toUTF32(const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, + ConversionFlags flags); + +ConversionResult Q3DSConvertUTF32toUTF8(const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, + ConversionFlags flags); + +ConversionResult Q3DSConvertUTF16toUTF32(const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, + ConversionFlags flags); + +ConversionResult Q3DSConvertUTF32toUTF16(const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, + ConversionFlags flags); + +Boolean Q3DSIsLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); + +#ifdef __cplusplus +} +#endif + +/* --------------------------------------------------------------------- */ diff --git a/src/foundation/EASTL_new.cpp b/src/foundation/EASTL_new.cpp new file mode 100644 index 0000000..61e8e0b --- /dev/null +++ b/src/foundation/EASTL_new.cpp @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// we have some build dependencies which forces us to include +// this on linux to recognize malloc +#include <stdio.h> +#include <stdarg.h> + +#if !defined(_MSC_VER) +#include <stdlib.h> +#endif + +#include "EASTL/allocator.h" + +void *operator new[](size_t size, const char *, int, unsigned, const char *, int) +{ + return malloc(size); +} + +void *operator new[](size_t size, size_t, size_t, const char *, int, unsigned, const char *, int) +{ + return malloc(size); +} +// EASTL also wants us to define this (see string.h line 197) +int Vsnprintf8(char8_t *pDestination, size_t n, const char8_t *pFormat, va_list arguments) +{ +#ifdef _MSC_VER + return _vsnprintf(pDestination, n, pFormat, arguments); +#else + return vsnprintf(pDestination, n, pFormat, arguments); +#endif +} +int Vsnprintf16(char8_t *pDestination, size_t n, const char16_t *pFormat, va_list arguments) +{ +#ifdef _MSC_VER + return _vsnprintf(pDestination, n, (char *)pFormat, arguments); +#else + return vsnprintf(pDestination, n, (char *)pFormat, arguments); +#endif +} +int Vsnprintf32(char8_t *pDestination, size_t n, const char32_t *pFormat, va_list arguments) +{ +#ifdef _MSC_VER + return _vsnprintf(pDestination, n, (char *)pFormat, arguments); +#else + return vsnprintf(pDestination, n, (char *)pFormat, arguments); +#endif +} diff --git a/src/foundation/FastAllocator.h b/src/foundation/FastAllocator.h new file mode 100644 index 0000000..c296bcb --- /dev/null +++ b/src/foundation/FastAllocator.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "foundation/Qt3DSAllocatorCallback.h" +#include "foundation/Qt3DSContainers.h" + +namespace qt3ds { +namespace foundation { + + /** + * Allocator that allocates slabs of a certain size and objects then + * get allocated from those slabs. This allocator is designed to reset, + * meaning every frame you can call reset and the objects get freed but + * not destructed. This allocator does not call destructors!! + */ + template <QT3DSU32 alignmentInBytes = 4, QT3DSU32 slabSize = 8192> + struct SFastAllocator : NVAllocatorCallback + { + ForwardingAllocator m_Allocator; + nvvector<QT3DSU8 *> m_Slabs; + QT3DSU32 m_Offset; + + enum { + SlabSize = slabSize, + }; + + static size_t getSlabSize() { return slabSize; } + + SFastAllocator(NVAllocatorCallback &alloc, const char *memName) + : m_Allocator(alloc, memName) + , m_Slabs(alloc, "SFastAllocator::m_Slabs") + , m_Offset(0) + { + } + + ~SFastAllocator() + { + for (QT3DSU32 idx = 0, end = m_Slabs.size(); idx < end; ++idx) + m_Allocator.deallocate(m_Slabs[idx]); + m_Slabs.clear(); + m_Offset = 0; + } + void *allocate(size_t inSize, const char *inFile, int inLine, int inFlags = 0) + { + (void)inFlags; + if (inSize > slabSize) { + QT3DS_ASSERT(false); + return NULL; + } + QT3DSU32 misalign = m_Offset % alignmentInBytes; + if (misalign) + m_Offset = m_Offset + (alignmentInBytes - misalign); + + QT3DSU32 currentSlab = m_Offset / slabSize; + QT3DSU32 slabOffset = m_Offset % slabSize; + QT3DSU32 amountLeftInSlab = slabSize - slabOffset; + if (inSize > amountLeftInSlab) { + ++currentSlab; + slabOffset = 0; + m_Offset = currentSlab * slabSize; + } + while (currentSlab >= m_Slabs.size()) { + m_Slabs.push_back((QT3DSU8 *)m_Allocator.allocate(slabSize, inFile, inLine)); + } + QT3DSU8 *data = m_Slabs[currentSlab] + slabOffset; + // This would indicate the underlying allocator isn't handing back aligned memory. + QT3DS_ASSERT(reinterpret_cast<size_t>(data) % alignmentInBytes == 0); + m_Offset += (QT3DSU32)inSize; + return data; + } + void *allocate(size_t size, const char * /*typeName*/, const char *filename, int line, + int flags = 0) override + { + return allocate(size, filename, line, flags); + } + void *allocate(size_t size, const char * /*typeName*/, const char *filename, int line, + size_t alignment, size_t /*alignmentOffset*/) override + { + QT3DS_ASSERT(alignment == alignmentInBytes); + if (alignment == alignmentInBytes) + return allocate(size, filename, line); + return NULL; + } + // only reset works with deallocation + void deallocate(void *) override {} + void reset() { m_Offset = 0; } + }; +} +} diff --git a/src/foundation/FileTools.cpp b/src/foundation/FileTools.cpp new file mode 100644 index 0000000..c1733a6 --- /dev/null +++ b/src/foundation/FileTools.cpp @@ -0,0 +1,550 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "foundation/FileTools.h" +#include "foundation/Utils.h" +#include <string.h> +# +#ifdef EA_PLATFORM_WINDOWS +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#else // posix +#include <stdio.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#endif +#include <QDir> +#include <QFile> +#include <QUrl> + +using namespace qt3ds::foundation; + +namespace { +// State machine where you can add a character +// and it will tell you how many characters to erase +struct SPathStateMachine +{ + struct States + { + enum Enum { + NoState = 0, // Don't care + Slash, // Last char was either a forward or backward slash + Period, // Last char was a period + TwoPeriods, // Last two characters were periods + }; + }; + struct Actions + { + enum Enum { + NoAction = 0, + DeleteBack1Slash, + DeleteBack2Slashes, + }; + }; + + States::Enum m_State; + + SPathStateMachine() + : m_State(States::NoState) + { + } + + Actions::Enum AnalyzeChar(char32_t inChar) + { + switch (inChar) { + case '\\': + case '/': + switch (m_State) { + case States::NoState: + m_State = States::Slash; + break; + case States::Period: + m_State = States::Slash; + return Actions::DeleteBack1Slash; + + case States::TwoPeriods: + m_State = States::Slash; + return Actions::DeleteBack2Slashes; + case States::Slash: + return Actions::DeleteBack1Slash; + } + break; + case '.': + switch (m_State) { + case States::Slash: + case States::NoState: + m_State = States::Period; + break; + case States::Period: + m_State = States::TwoPeriods; + break; + case States::TwoPeriods: + break; + } + break; + default: + m_State = States::NoState; + break; + } + return Actions::NoAction; + } +}; + +template <typename TStrType> +inline bool DoDeleteBack1Slash(TStr::size_type &idx, TStrType &ioPath) +{ + TStr::size_type slashLoc = ioPath.rfind('/', idx - 1); + if ((slashLoc != TStr::npos) && (slashLoc > 2) + // and the next *two* characters aren't both dots. + && ((ioPath[slashLoc - 1] != '.') || (ioPath[slashLoc - 2] != '.'))) { + + ioPath.erase(ioPath.begin() + slashLoc, ioPath.begin() + idx); + idx = slashLoc; + return true; + } + return false; +} + +template <typename TStrType> +void NormalizePathT(TStrType &ioPath) +{ + TStr::size_type pathLen = ioPath.size(); + SPathStateMachine theStateMachine; + for (TStr::size_type idx = 0; idx < pathLen; ++idx) { + char8_t ¤tChar = ioPath[idx]; + if (currentChar == '\\') + currentChar = '/'; + SPathStateMachine::Actions::Enum action = theStateMachine.AnalyzeChar(currentChar); + switch (action) { + case SPathStateMachine::Actions::DeleteBack2Slashes: + if (DoDeleteBack1Slash(idx, ioPath)) + DoDeleteBack1Slash(idx, ioPath); + pathLen = ioPath.size(); + break; + + case SPathStateMachine::Actions::DeleteBack1Slash: + DoDeleteBack1Slash(idx, ioPath); + pathLen = ioPath.size(); + break; + default: + break; + } + } +} + +bool IsAbsolute(const char8_t *inPath, size_t inLen) +{ + if (inLen > 2 && inPath[1] == ':') + return true; + else if (inLen > 1 && (inPath[0] == '\\' || inPath[0] == '/')) + return true; + return false; +} + +template <typename TStrType> +void CombineBaseAndRelativeT(const char8_t *inBase, const char8_t *inRelative, TStrType &outString) +{ + if (IsAbsolute(inRelative, StrLen(inRelative))) { + outString.assign(nonNull(inRelative)); + } else { + if (inRelative && *inRelative) { + if (inRelative[0] == '#') + outString.assign(inRelative); + else { + if (IsAbsolute(inRelative, strlen(inRelative))) { + outString.assign(inRelative); + } else { + outString = inBase ? inBase : ""; + if (outString.size()) + outString.append("/"); + outString.append(inRelative ? inRelative : (const char8_t *)L""); + } + NormalizePathT(outString); + } + } + } +} + +template <typename TStrType> +void GetRelativeFromBaseT(TStrType &inBaseStr, TStrType &inRelativeStr, TStrType &outString) +{ + outString.clear(); + NormalizePathT(inBaseStr); + NormalizePathT(inRelativeStr); + if (inBaseStr.size() == 0) { + outString.assign(inRelativeStr.c_str()); + return; + } + if (inRelativeStr.size() == 0) { + outString.clear(); + return; + } + // find longest common string + const char8_t *inBase = inBaseStr.c_str(); + const char8_t *baseEnd = inBaseStr.c_str() + inBaseStr.size(); + const char8_t *inRelative = inRelativeStr.c_str(); + size_t relativeLen = inRelativeStr.size(); + const char8_t *relativeEnd = inRelative + relativeLen; + + for (; inRelative < relativeEnd && inBase < baseEnd && *inRelative == *inBase; + ++inRelative, ++inBase) + ; + + // They had nothing in common. + if (inBase == inBaseStr.c_str()) { + outString.assign(inRelativeStr.c_str()); + return; + } + + if (inRelative && (*inRelative == '\\' || *inRelative == '/')) + ++inRelative; + + const char *common = inBase; + if (common == NULL || *common == 0) { + outString.assign("./"); + outString.append(inRelative); + NormalizePathT(outString); + return; + } + // Backtrack to the nearest slash. + while (*common && *common != '\\' && *common != '/') + --common; + + bool foundNonSlash = false; + for (; common != baseEnd; ++common) { + if (*common != '\\' && *common != '/') { + if (foundNonSlash == false) + outString.append("..\\"); + foundNonSlash = true; + } else + foundNonSlash = false; + } + if (inRelative < relativeEnd) { + if (outString.size() == 0) + outString.assign("./"); + outString.append(inRelative); + } + NormalizePathT(outString); +} +} + +void CFileTools::NormalizePath(TStr &ioPath) +{ + NormalizePathT(ioPath); +} + +void CFileTools::NormalizePath(eastl::string &ioPath) +{ + NormalizePathT(ioPath); +} + +QString CFileTools::NormalizePathForQtUsage(const QString &path) +{ + // path can be a file path or a qrc URL string. + + QString filePath = QDir::cleanPath(path); + + filePath.replace(QLatin1Char('\\'), QLatin1Char('/')); + + if (filePath.startsWith(QLatin1String("./"))) + return filePath.mid(2); + + if (filePath.startsWith(QLatin1String("qrc:/"))) + return filePath.mid(3); + else + return filePath; +} + +void CFileTools::CombineBaseAndRelative(const char8_t *inBase, const char8_t *inRelative, + TStr &outString) +{ + CombineBaseAndRelativeT(inBase, inRelative, outString); +} + +void CFileTools::CombineBaseAndRelative(const char8_t *inBase, const char8_t *inRelative, + eastl::string &outString) +{ + CombineBaseAndRelativeT(inBase, inRelative, outString); +} + +void CFileTools::GetRelativeFromBase(TStr &inBaseStr, TStr &inRelativeStr, TStr &outString) +{ + GetRelativeFromBaseT(inBaseStr, inRelativeStr, outString); +} + +void CFileTools::GetRelativeFromBase(eastl::string &inBaseStr, eastl::string &inRelativeStr, + eastl::string &outString) +{ + GetRelativeFromBaseT(inBaseStr, inRelativeStr, outString); +} + +bool CFileTools::RequiresCombineBaseAndRelative(const char8_t *inPath) +{ + if (inPath && *inPath) + return inPath[0] == '.'; + return false; +} + +template <typename TStrType> +void ToPlatformPathT(TStrType &outString) +{ +#ifndef EA_PLATFORM_WINDOWS + for (TStr::size_type pos = outString.find('\\'); pos != TStr::npos; + pos = outString.find('\\', pos + 1)) + outString.replace(outString.begin() + pos, outString.begin() + pos + 1, "/"); +#else + (void)outString; +#endif +} + +void CFileTools::ToPlatformPath(TStr &outString) +{ + ToPlatformPathT(outString); +} + +void CFileTools::ToPlatformPath(eastl::string &outString) +{ + ToPlatformPathT(outString); +} + +CRegisteredString CFileTools::RemapPathToBinaryFormat(TStr &inPath, TStr &inPresentationDir, + TStr &ioWorkspaceStr, + IStringTable &inStringTable) +{ + GetRelativeFromBase(inPresentationDir, inPath, ioWorkspaceStr); + CRegisteredString theNewStr = inStringTable.RegisterStr(ioWorkspaceStr.c_str()); + theNewStr.Remap(inStringTable.GetRemapMap()); + return theNewStr; +} + +CRegisteredString CFileTools::RemapPathFromBinaryFormat(CRegisteredString inPath, + const char8_t *inPresDir, + TStr &ioWorkspaceStr, + const CStrTableOrDataRef &inRef, + IStringTable &inStringTable) +{ + inPath.Remap(inRef); + if (RequiresCombineBaseAndRelative(inPath.c_str())) { + CombineBaseAndRelative(inPresDir, inPath, ioWorkspaceStr); + return inStringTable.RegisterStr(ioWorkspaceStr.c_str()); + } + return inPath; +} + +void CFileTools::GetDirectory(eastl::string &ioPath) +{ + eastl::string::size_type theSlashPos = ioPath.find_last_of("\\/"); + if (theSlashPos == eastl::string::npos) { + ioPath.clear(); + return; + } + ioPath.resize(theSlashPos); +} + +bool CFileTools::DirectoryExists(const char8_t *inPath) +{ +#ifdef EA_PLATFORM_WINDOWS + DWORD theAtts = GetFileAttributesA(inPath); + return theAtts != INVALID_FILE_ATTRIBUTES && (theAtts & FILE_ATTRIBUTE_DIRECTORY); +#else // Posix style check for directory + int status; + struct stat st_buf; + status = stat(inPath, &st_buf); + if (status == 0 && S_ISDIR(st_buf.st_mode)) + return true; + return false; +#endif +} + +bool CFileTools::FileExists(const char8_t *inPath) +{ +#ifdef EA_PLATFORM_WINDOWS + DWORD theAtts = GetFileAttributesA(inPath); + return theAtts != INVALID_FILE_ATTRIBUTES; +#else // Posix style check for directory + int status; + struct stat st_buf; + status = stat(inPath, &st_buf); + if (status == 0) + return true; + return false; +#endif +} + +eastl::string CFileTools::GetFileOrAssetPath(const char8_t *inPath) +{ + QFile tmp(inPath); + if (tmp.exists()) + return inPath; + return eastl::string("assets:/") + inPath; +} + +void CFileTools::SetStreamPosition(QIODevice& device, qint64 inOffset, + qt3ds::foundation::SeekPosition::Enum inEnum) +{ + if (inEnum == qt3ds::foundation::SeekPosition::Begin) + device.seek(inOffset); + else if (inEnum == qt3ds::foundation::SeekPosition::Current) + device.seek(device.pos() + inOffset); + else if (inEnum == qt3ds::foundation::SeekPosition::End) + device.seek(device.size() + inOffset); +} + +void CFileTools::GetExtension(const char8_t *inPath, eastl::string &outExt) +{ + outExt.assign(nonNull(inPath)); + size_t dotPos = outExt.find_last_of('.'); + if (dotPos != eastl::string::npos) + outExt.erase(outExt.begin(), outExt.begin() + dotPos + 1); +} + +void CFileTools::Split(const char8_t *inPath, eastl::string &outDir, eastl::string &outFileStem, + eastl::string &outExtension) +{ + outDir.assign(nonNull(inPath)); + NormalizePath(outDir); + outFileStem = outDir; + GetDirectory(outDir); + size_t lenDiff = outFileStem.size() - outDir.size(); + if (lenDiff > 0) { + if (outDir.size()) + outFileStem = outFileStem.substr(outDir.size() + 1); + + eastl::string::size_type lastDot = outFileStem.find_last_of('.'); + if (lastDot != eastl::string::npos) { + outExtension = outFileStem.substr(lastDot + 1); + outFileStem.resize(lastDot); + } + } +} + +#ifdef EA_PLATFORM_WINDOWS +void CFileTools::GetDirectoryEntries(const eastl::string &inPath, + eastl::vector<eastl::string> &outFiles) +{ + if (inPath.size() == 0) + return; + eastl::string tempPath(inPath); + NormalizePath(tempPath); + for (eastl::string::size_type pos = tempPath.find_first_of('/'); pos != eastl::string::npos; + pos = tempPath.find_first_of('/', pos + 1)) + tempPath[pos] = '\\'; + if (tempPath.back() != '\\') + tempPath.append("\\"); + tempPath.append(1, '*'); + WIN32_FIND_DATAA ffd; + HANDLE hFind = FindFirstFileA(tempPath.c_str(), &ffd); + outFiles.clear(); + if (INVALID_HANDLE_VALUE == hFind) + return; + + do { + if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) + continue; + outFiles.push_back(eastl::string(ffd.cFileName)); + } while (FindNextFileA(hFind, &ffd) != 0); +} +#else +void CFileTools::GetDirectoryEntries(const eastl::string &inPath, + eastl::vector<eastl::string> &outFiles) +{ + if (inPath.size() == 0) + return; + eastl::string tempPath(inPath); + NormalizePath(tempPath); + struct dirent *dent; + DIR *srcdir = opendir(tempPath.c_str()); + if (srcdir) { + while ((dent = readdir(srcdir)) != NULL) { + if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) + continue; + outFiles.push_back(eastl::string(dent->d_name)); + } + closedir(srcdir); + } +} +#endif + +bool CFileTools::CreateDir(const eastl::string &inPath, bool inRecurse) +{ + if (DirectoryExists(inPath.c_str())) + return true; + + eastl::string temp(inPath); + GetDirectory(temp); + if (temp.size() && !DirectoryExists(temp.c_str())) { + if (inRecurse) + CreateDir(temp, inRecurse); + else + return false; + } + +#ifdef EA_PLATFORM_WINDOWS + BOOL result = CreateDirectoryA(inPath.c_str(), NULL); + return result != 0; +#else + int result = mkdir(inPath.c_str(), 0777); + return result == 0; +#endif +} + +void CFileTools::AppendDirectoryInPathToFile(eastl::string &ioPath, const char8_t *dirName) +{ + eastl::string::size_type lastSlash = ioPath.find_last_of("\\/"); + if (lastSlash != eastl::string::npos) { + if (dirName == NULL) + dirName = ""; // avoid crashes on null strings + ioPath.insert(lastSlash + 1, "/"); + ioPath.insert(lastSlash + 1, dirName); + } else { + ioPath.insert(0, "/"); + ioPath.insert(0, dirName); + } +} + +void CFileTools::RemoveLastDirectoryInPathToFile(eastl::string &ioPath) +{ + eastl::string::size_type lastSlash = ioPath.find_last_of("\\/"); + if (lastSlash != eastl::string::npos) { + eastl::string::size_type secondToLastSlash = ioPath.find_last_of("\\/", lastSlash - 1); + if (secondToLastSlash != eastl::string::npos) + ioPath = ioPath.erase(secondToLastSlash, lastSlash - secondToLastSlash); + } +} + +void CFileTools::SetExtension(eastl::string &ioPath, const char8_t *inExt) +{ + eastl::string::size_type thePos = ioPath.find_last_of("."); + if (thePos != eastl::string::npos) { + ++thePos; + ioPath = ioPath.replace(thePos, ioPath.size() - thePos, inExt); + } +} diff --git a/src/foundation/FileTools.h b/src/foundation/FileTools.h new file mode 100644 index 0000000..5745525 --- /dev/null +++ b/src/foundation/FileTools.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_FOUNDATION_FILE_TOOLS_H +#define QT3DS_FOUNDATION_FILE_TOOLS_H + +#include "EASTL/string.h" +#include "EASTL/vector.h" +#include "foundation/Qt3DSAllocator.h" +#include "foundation/StringTable.h" +#include "foundation/IOStreams.h" +#include <QtGlobal> + +QT_FORWARD_DECLARE_CLASS(QIODevice) + +namespace qt3ds { +namespace foundation { + + typedef eastl::basic_string<char8_t, ForwardingAllocator> TStr; + + class CFileTools + { + public: + // Normalizes all slashes to be windows-like + static void NormalizePath(TStr &ioPath); + static void NormalizePath(eastl::string &ioPath); + + // This is kind of the opposite of the 'NormalizePath' methods above in + // that it forces usage of unix style directory separators rather than + // windows separators. In addition, the path here can be a file path or + // a qrc URL string. This function will eventually be refactored away + // when we've converted to using Qt's file system abstraction throughout + // the code base and use QUrl's throughout instead of raw strings to + // represent resources. + static QString NormalizePathForQtUsage(const QString &path); + + static void CombineBaseAndRelative(const char8_t *inBase, const char8_t *inRelative, + TStr &outString); + static void CombineBaseAndRelative(const char8_t *inBase, const char8_t *inRelative, + eastl::string &outString); + + // inBase and inRelative will get normalized + // This algorithm changes based on the platform. On windows it is not case sensitive, on + // not-windows it is. + static void GetRelativeFromBase(TStr &inBase, TStr &inRelative, TStr &outString); + static void GetRelativeFromBase(eastl::string &inBase, eastl::string &inRelative, + eastl::string &outString); + + // A remapped path is a file path that starts with a '.' or a '/'. GetRelativeFromBase + // *always* + // places a '.' or a '/' at the front of the path, so if you *know* the path came from + // GetRelativeFromBase then you also know this function returns true of GetRelativeFromBase + // actually generated a path that needs CombineBaseAndRelative. + static bool RequiresCombineBaseAndRelative(const char8_t *inPath); + + // Search/replace so that all slashes are unix-like but only on non-windows platforms + // Assumes the incoming path has been normalized + static void ToPlatformPath(TStr &ioPath); + static void ToPlatformPath(eastl::string &ioPath); + + static CRegisteredString RemapPathToBinaryFormat(TStr &inPath, TStr &inPresentationDir, + TStr &ioWorkspaceStr, + IStringTable &inStringTable); + static CRegisteredString RemapPathFromBinaryFormat(CRegisteredString inPath, + const char8_t *inPresDir, + TStr &ioWorkspaceStr, + const CStrTableOrDataRef &inRef, + IStringTable &inStringTable); + // I + static void GetDirectory(eastl::string &ioPath); + static bool DirectoryExists(const char8_t *inPath); + static void GetExtension(const char8_t *inPath, eastl::string &outExt); + static void Split(const char8_t *inPath, eastl::string &outDir, eastl::string &outFileStem, + eastl::string &outExtension); + + // Only implemented for windows. Does not return '.' and '..' special entries + // inPath is mangled in a platform specific way + static void GetDirectoryEntries(const eastl::string &inPath, + eastl::vector<eastl::string> &outFiles); + + static bool CreateDir(const eastl::string &inPath, bool inRecurse = true); + + // Given a/b.txt, we will end up with a/dirName/b.txt + static void AppendDirectoryInPathToFile(eastl::string &ioPath, const char8_t *dirName); + static void RemoveLastDirectoryInPathToFile(eastl::string &ioPath); + + static void SetExtension(eastl::string &ioPath, const char8_t *inExt); + + static bool FileExists(const char8_t *inPath); + static eastl::string GetFileOrAssetPath(const char8_t *inPath); + static void SetStreamPosition(QIODevice& device, qint64 inOffset, + qt3ds::foundation::SeekPosition::Enum inEnum); + }; +} +} + +#endif diff --git a/src/foundation/IOStreams.cpp b/src/foundation/IOStreams.cpp new file mode 100644 index 0000000..f1adfbc --- /dev/null +++ b/src/foundation/IOStreams.cpp @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "foundation/IOStreams.h" +#include "foundation/FileTools.h" +#include "foundation/StrConvertUTF.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/Qt3DSMutex.h" +#include "foundation/Qt3DSSync.h" +#include "foundation/Qt3DSThread.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSMemoryBuffer.h" + +using namespace qt3ds::foundation; + +#ifndef _WIN32 + +inline int _fseeki64(FILE *inFile, int64_t pos, int seekFlags) +{ + return fseek(inFile, (int32_t)pos, seekFlags); +} + +inline int64_t _ftelli64(FILE *inFile) +{ + return ftell(inFile); +} + +#endif + +CFileSeekableIOStream::CFileSeekableIOStream(const char *inFileName, FileOpenFlags inFlags) +{ + openFile(QString(inFileName), inFlags); +} + +#ifdef WIDE_IS_DIFFERENT_TYPE_THAN_CHAR16_T + +CFileSeekableIOStream::CFileSeekableIOStream(const wchar_t *inFileName, FileOpenFlags inFlags) +{ + openFile(QString::fromWCharArray(inFileName), inFlags); +} + +#endif + +CFileSeekableIOStream::CFileSeekableIOStream(const char16_t *inWideName, FileOpenFlags inFlags) +{ + openFile(QString::fromUtf16(inWideName), inFlags); +} + +CFileSeekableIOStream::CFileSeekableIOStream(const QString &inFIle, FileOpenFlags inFlags) +{ + openFile(inFIle, inFlags); +} + +void CFileSeekableIOStream::openFile(const QString &path, FileOpenFlags inFlags) +{ + if (path.isEmpty()) + return; + + QIODevice::OpenMode fileFlags = QIODevice::ReadOnly; + if (inFlags & FileOpenFlagValues::Write) + fileFlags = QIODevice::ReadWrite; + if (inFlags & FileOpenFlagValues::Truncate) + fileFlags |= QIODevice::Truncate; + + m_File.setFileName(CFileTools::NormalizePathForQtUsage(path)); + if (!m_File.open(fileFlags)) { + qCCritical(INTERNAL_ERROR) << "failed to open file" + << path << "with error" << m_File.errorString(); + QT3DS_ASSERT(false); + } +} + +CFileSeekableIOStream::~CFileSeekableIOStream() +{ + m_File.close(); +} + +bool CFileSeekableIOStream::IsOpen() +{ + return m_File.isOpen(); +} + +void CFileSeekableIOStream::SetPosition(QT3DSI64 inOffset, SeekPosition::Enum inEnum) +{ + if (inOffset > QT3DS_MAX_I32 || inOffset < QT3DS_MIN_I32) { + qCCritical(INVALID_OPERATION, "Attempt to seek further than platform allows"); + QT3DS_ASSERT(false); + return; + } else { + CFileTools::SetStreamPosition(m_File, inOffset, inEnum); + } +} + +QT3DSI64 CFileSeekableIOStream::GetPosition() const +{ + return m_File.pos(); +} + +QT3DSU32 CFileSeekableIOStream::Read(NVDataRef<QT3DSU8> data) +{ + return m_File.read((char *)data.begin(), data.size()); +} + +bool CFileSeekableIOStream::Write(NVConstDataRef<QT3DSU8> data) +{ + if (!m_File.isOpen()) { + QT3DS_ASSERT(false); + return 0; + } + qint64 numBytes = m_File.write((char*)data.begin(), data.size()); + return numBytes == data.size(); +} + +CMemorySeekableIOStream::CMemorySeekableIOStream(NVAllocatorCallback &inAlloc, + const char *inAllocName) + : m_Allocator(inAlloc) + , m_AllocationName(inAllocName) + , m_Data(NULL) + , m_Size(0) + , m_Offset(0) + , m_Capacity(0) +{ +} + +CMemorySeekableIOStream::~CMemorySeekableIOStream() +{ + if (m_Data) + m_Allocator.deallocate(m_Data); + m_Data = NULL; + m_Size = 0; + m_Capacity = 0; + m_Offset = 0; +} + +void CMemorySeekableIOStream::SetPosition(QT3DSI64 inOffset, SeekPosition::Enum inEnum) +{ + QT3DSI64 startPos = 0; + + switch (inEnum) { + case SeekPosition::Begin: + startPos = 0; + break; + case SeekPosition::Current: + startPos = m_Offset; + break; + case SeekPosition::End: + startPos = m_Size; + break; + default: + QT3DS_ASSERT(false); + break; + } + + startPos += inOffset; + if (m_Size == 0 && inOffset != 0) { + QT3DS_ASSERT(false); + return; + } + if (startPos < 0) { + QT3DS_ASSERT(false); + startPos = 0; + } + if (startPos >= m_Size && startPos != 0) { + QT3DS_ASSERT(false); + startPos = m_Size - 1; + } + m_Offset = static_cast<QT3DSU32>(startPos); +} + +QT3DSU32 CMemorySeekableIOStream::Read(NVDataRef<QT3DSU8> data) +{ + if (m_Data == NULL) + return 0; + QT3DSU32 amountLeft = m_Size - m_Offset; + QT3DSU32 numBytes = NVMin(amountLeft, data.size()); + intrinsics::memCopy(data.begin(), m_Data + m_Offset, numBytes); + m_Offset += numBytes; + return numBytes; +} + +bool CMemorySeekableIOStream::Write(NVConstDataRef<QT3DSU8> data) +{ + reserve(data.size() + m_Offset); + intrinsics::memCopy(m_Data + m_Offset, data.begin(), data.size()); + m_Offset += data.size(); + m_Size = NVMax(m_Size, m_Offset); + return true; +} + +void CMemorySeekableIOStream::reserve(QT3DSU32 inNewSize) +{ + if (inNewSize > m_Capacity) { + if (inNewSize < 100000) + inNewSize *= 2; + QT3DSU8 *newData = + (QT3DSU8 *)m_Allocator.allocate(inNewSize, m_AllocationName, __FILE__, __LINE__); + if (m_Size) { + intrinsics::memCopy(newData, m_Data, m_Size); + m_Allocator.deallocate(m_Data); + } + m_Data = newData; + m_Capacity = inNewSize; + } +} + +namespace { + +struct WriteBufferedStreamImpl; +struct WriteBufferThread : public Thread +{ + // When a buffer is available + WriteBufferedStreamImpl &m_Impl; + WriteBufferThread(NVFoundationBase &fnd, WriteBufferedStreamImpl &i) + : Thread(fnd) + , m_Impl(i) + { + setName("WriteBufferThread"); + } + void execute() override; +}; + +#ifdef _WIN32 +#pragma warning(disable : 4355) +#endif +/* Double buffered stream implementation with a sending thread constantly + * pulling data from the main thread and writing it out to socket. + */ +struct WriteBufferedStreamImpl : public WriteBufferedOutStream +{ + NVFoundationBase &m_Foundation; + IOutStream &m_Stream; + MemoryBuffer<> m_Buf1; + MemoryBuffer<> m_Buf2; + MemoryBuffer<> *m_CurrentBuffer; + MemoryBuffer<> *m_WriteBuffer; + QT3DSU32 m_BufferSize; + volatile bool m_StreamValid; + Mutex m_BufferLock; + Sync m_DataAvailable; + Sync m_WriteFinished; + WriteBufferThread m_Thread; + QT3DSI32 mRefCount; + + WriteBufferedStreamImpl(NVFoundationBase &fnd, QT3DSU32 totalBufSize, IOutStream &s) + : m_Foundation(fnd) + , m_Stream(s) + , m_Buf1(ForwardingAllocator(fnd.getAllocator(), "WriteBufferedStreamImpl::buffer")) + , m_Buf2(ForwardingAllocator(fnd.getAllocator(), "WriteBufferedStreamImpl::buffer")) + , m_CurrentBuffer(&m_Buf1) + , m_WriteBuffer(NULL) + , m_BufferSize(totalBufSize / 2) + , m_StreamValid(true) + , m_BufferLock(fnd.getAllocator()) + , m_DataAvailable(fnd.getAllocator()) + , m_WriteFinished(fnd.getAllocator()) + , m_Thread(fnd, *this) + , mRefCount(0) + { + m_Buf1.reserve(m_BufferSize); + m_Buf2.reserve(m_BufferSize); + } + ~WriteBufferedStreamImpl() + { + m_Thread.signalQuit(); + m_DataAvailable.set(); + m_Thread.waitForQuit(); + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation.getAllocator()) + + bool Write(NVConstDataRef<QT3DSU8> data) override + { + while (data.size() && m_StreamValid) { + QT3DSU32 currentBufferSize; + QT3DSU32 amountCanWrite; + { + Mutex::ScopedLock locker(m_BufferLock); + currentBufferSize = m_CurrentBuffer->size(); + amountCanWrite = NVMin(data.size(), m_BufferSize - currentBufferSize); + m_CurrentBuffer->write(data.begin(), amountCanWrite); + currentBufferSize += amountCanWrite; + } + m_DataAvailable.set(); + if (currentBufferSize == m_BufferSize) { + m_WriteFinished.wait(); + m_WriteFinished.reset(); + // Blocking call if we are already sending data. + data = NVConstDataRef<QT3DSU8>(data.begin() + amountCanWrite, + data.size() - amountCanWrite); + } + } + return m_StreamValid; + } + + IOutStream &wrappedStream() override { return m_Stream; } + + QT3DSU32 getTotalBufferSize() + { + Mutex::ScopedLock locker(m_BufferLock); + QT3DSU32 retval = m_CurrentBuffer->size(); + if (m_WriteBuffer) + retval += m_WriteBuffer->size(); + return retval; + } + + QT3DSU32 getWriteBufferSize() + { + Mutex::ScopedLock locker(m_BufferLock); + if (m_WriteBuffer) + return m_WriteBuffer->size(); + return 0; + } + + void flush() override + { + while (getTotalBufferSize()) { + m_WriteFinished.wait(); + m_WriteFinished.reset(); + } + } +}; + +void WriteBufferThread::execute() +{ + while (!quitIsSignalled()) { + m_Impl.m_DataAvailable.wait(); + + if (!quitIsSignalled() && m_Impl.m_StreamValid) { + m_Impl.m_DataAvailable.reset(); + { + Mutex::ScopedLock locker(m_Impl.m_BufferLock); + m_Impl.m_WriteBuffer = m_Impl.m_CurrentBuffer; + m_Impl.m_CurrentBuffer = + m_Impl.m_CurrentBuffer == &m_Impl.m_Buf1 ? &m_Impl.m_Buf2 : &m_Impl.m_Buf1; + QT3DS_ASSERT(m_Impl.m_WriteBuffer != m_Impl.m_CurrentBuffer); + } + NVConstDataRef<QT3DSU8> dataBuffer(*m_Impl.m_WriteBuffer); + if (dataBuffer.size()) { + m_Impl.m_StreamValid = m_Impl.m_Stream.Write(dataBuffer); + { + Mutex::ScopedLock locker(m_Impl.m_BufferLock); + m_Impl.m_WriteBuffer->clear(); + m_Impl.m_WriteBuffer = NULL; + } + } + m_Impl.m_WriteFinished.set(); + } + } + quit(); +} +} + +NVScopedRefCounted<WriteBufferedOutStream> +WriteBufferedOutStreamCreate(NVFoundationBase &fnd, IOutStream &stream, QT3DSU32 totalBufferSize) +{ + return QT3DS_NEW(fnd.getAllocator(), WriteBufferedStreamImpl)(fnd, totalBufferSize, stream); +} diff --git a/src/foundation/IOStreams.h b/src/foundation/IOStreams.h new file mode 100644 index 0000000..855e520 --- /dev/null +++ b/src/foundation/IOStreams.h @@ -0,0 +1,278 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_FOUNDATION_IO_STREAMS_H +#define QT3DS_FOUNDATION_IO_STREAMS_H + +#include "EABase/eabase.h" +#include "foundation/Qt3DSDataRef.h" +#include "foundation/Qt3DSAssert.h" +#include "foundation/Qt3DSIntrinsics.h" +#include "foundation/TrackingAllocator.h" +#include "foundation/Qt3DSMath.h" +#include "foundation/Qt3DSFlags.h" +#include "foundation/Utils.h" +#include "foundation/Qt3DSRefCounted.h" + +#include <QFile> +#include <QString> + +namespace qt3ds { +class NVFoundationBase; + +namespace foundation { + + class IOutStream + { + protected: + virtual ~IOutStream() {} + public: + virtual bool Write(NVConstDataRef<QT3DSU8> data) = 0; + void WriteWithLen(NVConstDataRef<QT3DSU8> data) + { + Write(data.size()); + Write(data); + } + template <typename TDataType> + void Write(const TDataType &type) + { + Write(toU8ConstDataRef(type)); + } + template <typename TDataType> + void Write(const TDataType *data, QT3DSU32 numItems) + { + Write(toU8ConstDataRef(data, numItems)); + } + void Write(const wchar_t *data) + { + if (data == NULL) + data = L""; + // Write the null character at the end of the string. + // This just makes reading and debugging a lot less error prone. + // at the expense of 2 bytes. + Write(data, (QT3DSU32)StrLen(data) + 1); + } + template <typename TDataType> + void WriteWithLen(const TDataType *data, QT3DSU32 numItems) + { + WriteWithLen(toU8ConstDataRef(data, numItems)); + } + }; + + class IInStream + { + protected: + virtual ~IInStream() {} + public: + // Semantics are precisely that you return the amount read + virtual QT3DSU32 Read(NVDataRef<QT3DSU8> data) = 0; + + QT3DSU32 SafeRead(NVDataRef<QT3DSU8> data) + { + QT3DSU32 amountRead = Read(data); + QT3DS_ASSERT(amountRead == data.size()); + if (amountRead < data.size()) + intrinsics::memZero(data.begin() + amountRead, data.size() - amountRead); + return amountRead; + } + QT3DSU32 ReadWithLen(NVDataRef<QT3DSU8> data) + { + QT3DSU32 len = 0; + Read(len); + return SafeRead(toDataRef(data.begin(), len)); + } + template <typename TDataType> + QT3DSU32 Read(TDataType &type) + { + return SafeRead(toU8DataRef(type)); + } + template <typename TDataType> + QT3DSU32 Read(TDataType *data, QT3DSU32 numItems) + { + return SafeRead(toU8DataRef(data, numItems)); + } + }; + + struct SeekPosition + { + enum Enum { + Unknown, + Begin, + Current, + End, + }; + }; + + class ISeekable + { + protected: + virtual ~ISeekable() {} + public: + virtual void SetPosition(QT3DSI64 inOffset, SeekPosition::Enum inEnum) = 0; + virtual QT3DSI64 GetPosition() const = 0; + virtual QT3DSI64 GetLength() const + { + ISeekable &seekable(const_cast<ISeekable &>(*this)); + QT3DSI64 currentPos(GetPosition()); + seekable.SetPosition(0, SeekPosition::End); + QT3DSI64 retval(GetPosition()); + seekable.SetPosition(currentPos, SeekPosition::Begin); + return retval; + } + }; + + class ISeekableIOStream : public IInStream, public IOutStream, public ISeekable + { + }; + + struct FileOpenFlagValues + { + enum Enum { + Open = 1, // Without this flag, function fails if file exists + Truncate = 1 << 1, // Truncate the file so an immediate close will empty it. + Create = 1 << 2, + Write = 1 << 3, + }; + }; + + typedef NVFlags<FileOpenFlagValues::Enum, int> FileOpenFlags; + + static inline FileOpenFlags FileReadFlags() { return FileOpenFlags(FileOpenFlagValues::Open); } + + static inline FileOpenFlags FileWriteFlags() + { + return FileOpenFlags(FileOpenFlagValues::Create | FileOpenFlagValues::Open + | FileOpenFlagValues::Write | FileOpenFlagValues::Truncate); + } + static inline FileOpenFlags FileAppendFlags() + { + return FileOpenFlags(FileOpenFlagValues::Create | FileOpenFlagValues::Open + | FileOpenFlagValues::Write); + } + + class CFileSeekableIOStream : public ISeekableIOStream + { + protected: + QFile m_File; + + public: + // Enabling append also enables reading from the file while being able to + // write to it. + CFileSeekableIOStream(const char *inFile, FileOpenFlags inFlags); +#ifdef WIDE_IS_DIFFERENT_TYPE_THAN_CHAR16_T + CFileSeekableIOStream(const wchar_t *inFile, FileOpenFlags inFlags); +#endif + + CFileSeekableIOStream(const char16_t *inFile, FileOpenFlags inFlags); + CFileSeekableIOStream(const QString &inFIle, FileOpenFlags inFlags); + virtual ~CFileSeekableIOStream(); + virtual bool IsOpen(); + void SetPosition(QT3DSI64 inOffset, SeekPosition::Enum inEnum) override; + QT3DSI64 GetPosition() const override; + QT3DSU32 Read(NVDataRef<QT3DSU8> data) override; + bool Write(NVConstDataRef<QT3DSU8> data) override; + + private: + void openFile(const QString &path, FileOpenFlags inFlags); + }; + + class CMemorySeekableIOStream : public ISeekableIOStream + { + protected: + NVAllocatorCallback &m_Allocator; + const char *m_AllocationName; + QT3DSU8 *m_Data; + QT3DSU32 m_Size; + QT3DSU32 m_Offset; + QT3DSU32 m_Capacity; + + public: + CMemorySeekableIOStream(NVAllocatorCallback &inAlloc, const char *inAllocName); + virtual ~CMemorySeekableIOStream(); + void SetPosition(QT3DSI64 inOffset, SeekPosition::Enum inEnum) override; + QT3DSI64 GetPosition() const override { return m_Offset; } + QT3DSI64 GetLength() const override { return m_Size; } + QT3DSU32 Read(NVDataRef<QT3DSU8> data) override; + bool Write(NVConstDataRef<QT3DSU8> data) override; + + // Add in the standard container functions + QT3DSU8 *begin() { return m_Data; } + QT3DSU8 *end() { return m_Data + m_Size; } + const QT3DSU8 *begin() const { return m_Data; } + const QT3DSU8 *end() const { return m_Data + m_Size; } + void clear() + { + m_Offset = 0; + m_Size = 0; + } + QT3DSU32 size() const { return m_Size; } + void reserve(QT3DSU32 inNewSize); + void resize(QT3DSU32 inNewSize) + { + reserve(inNewSize); + m_Size = inNewSize; + } + }; + + // Simple, one way input stream. + struct SMemoryInStream : public IInStream + { + const QT3DSU8 *m_Begin; + const QT3DSU8 *m_End; + SMemoryInStream(const QT3DSU8 *b, const QT3DSU8 *e) + : m_Begin(b) + , m_End(e) + { + } + + QT3DSU32 Read(NVDataRef<QT3DSU8> data) override + { + size_t available = m_End - m_Begin; + size_t requested = data.size(); + size_t amount = NVMin(available, requested); + qt3ds::intrinsics::memCopy(data.mData, m_Begin, (QT3DSU32)amount); + m_Begin += amount; + return (QT3DSU32)amount; + } + }; + + class WriteBufferedOutStream : public IOutStream, public NVRefCounted + { + public: + virtual IOutStream &wrappedStream() = 0; + virtual void flush() = 0; + + static NVScopedRefCounted<WriteBufferedOutStream> + Create(NVFoundationBase &fnd, IOutStream &stream, QT3DSU32 totalBufferSize = 128 * 1024); + }; +} +} + +#endif diff --git a/src/foundation/LICENCE_SOCKET.TXT b/src/foundation/LICENCE_SOCKET.TXT new file mode 100644 index 0000000..9b9ab80 --- /dev/null +++ b/src/foundation/LICENCE_SOCKET.TXT @@ -0,0 +1,20 @@ +LuaSocket 3.0 license +Copyright (C) 2004-2013 Diego Nehab + +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. diff --git a/src/foundation/LICENSE_CONVERTUTF.TXT b/src/foundation/LICENSE_CONVERTUTF.TXT new file mode 100644 index 0000000..254acdf --- /dev/null +++ b/src/foundation/LICENSE_CONVERTUTF.TXT @@ -0,0 +1,19 @@ +Copyright 2001-2004 Unicode, Inc. + +Disclaimer + +This source code is provided as is by Unicode, Inc. No claims are +made as to fitness for any particular purpose. No warranties of any +kind are expressed or implied. The recipient agrees to determine +applicability of information provided. If this file has been +purchased on magnetic or optical media from Unicode, Inc., the +sole remedy for any claim will be exchange of defective media +within 90 days of receipt. + +Limitations on Rights to Redistribute This Code + +Unicode, Inc. hereby grants the right to freely use the information +supplied in this file in the creation of products supporting the +Unicode Standard, and to make copies of this file in any form +for internal or external distribution as long as this notice +remains attached. diff --git a/src/foundation/PoolingAllocator.h b/src/foundation/PoolingAllocator.h new file mode 100644 index 0000000..9d939fd --- /dev/null +++ b/src/foundation/PoolingAllocator.h @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DS_FOUNDATION_POOLING_ALLOCATOR_H +#define QT3DS_FOUNDATION_POOLING_ALLOCATOR_H +#pragma once + +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSContainers.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/Qt3DSPool.h" +#include "foundation/AutoDeallocatorAllocator.h" +#include "foundation/Qt3DSMutex.h" + +// Pooling allocator. Not designed for small allocations +// starting at 64 bytes and up to 4 K, allocator uses pools. +// Above that, uses default allocator. THis object is absolutely not threadsafe. +// This addes 8 bytes to each allocation in order to safely track the allocation size. +// There is no strict requirement to deallocate; this allocator automatically deallocates +// anything allocated through it. +namespace qt3ds { +namespace foundation { + struct SPoolingAllocator : public NVAllocatorCallback + { + typedef Mutex TMutexType; + typedef Mutex::ScopedLock TLockType; + + NVAllocatorCallback &m_Allocator; + TMutexType m_Mutex; + + struct SAllocationTag + { + QT3DSU32 m_Tag; + size_t m_Size; + SAllocationTag(size_t inSize = 0) + : m_Tag(0xAB5534CD) + , m_Size(inSize) + { + } + }; + + template <size_t TObjSize> + struct SPoolObj + { + QT3DSU32 m_Buffer[TObjSize / 4]; + SPoolObj() {} + }; +#define ITERATE_POOLING_ALLOCATOR_POOL_SIZES \ + HANDLE_POOLING_ALLOCATOR_POOL_SIZE(64) \ + HANDLE_POOLING_ALLOCATOR_POOL_SIZE(128) \ + HANDLE_POOLING_ALLOCATOR_POOL_SIZE(256) \ + HANDLE_POOLING_ALLOCATOR_POOL_SIZE(512) \ + HANDLE_POOLING_ALLOCATOR_POOL_SIZE(1024) + +#define HANDLE_POOLING_ALLOCATOR_POOL_SIZE(sz) \ + Pool<SPoolObj<sz>, ForwardingAllocator, 4> m_Pool##sz; + ITERATE_POOLING_ALLOCATOR_POOL_SIZES +#undef HANDLE_POOLING_ALLOCATOR_POOL_SIZE + + SSAutoDeallocatorAllocator m_LargeAllocator; + +#define HANDLE_POOLING_ALLOCATOR_POOL_SIZE(sz) \ + , m_Pool##sz(ForwardingAllocator(inAllocator, "PoolingAllocatorPool")) + + SPoolingAllocator(NVAllocatorCallback &inAllocator) + : m_Allocator(inAllocator) + , m_Mutex(inAllocator) ITERATE_POOLING_ALLOCATOR_POOL_SIZES + , m_LargeAllocator(inAllocator) + { + } +#undef HANDLE_POOLING_ALLOCATOR_POOL_SIZE + + void *doAllocateFromPool(size_t size) + { + TLockType locker(m_Mutex); + +#define HANDLE_POOLING_ALLOCATOR_POOL_SIZE(sz) \ + if (size <= sz) \ + return m_Pool##sz.allocate(__FILE__, __LINE__); + ITERATE_POOLING_ALLOCATOR_POOL_SIZES +#undef HANDLE_POOLING_ALLOCATOR_POOL_SIZE + + return m_LargeAllocator.allocate(size, "largetype", __FILE__, __LINE__, 0); + } + + void *doAllocate(size_t size) + { + size += sizeof(SAllocationTag); + SAllocationTag *tag = reinterpret_cast<SAllocationTag *>(doAllocateFromPool(size)); + new (tag) SAllocationTag(size); + QT3DSU8 *data = reinterpret_cast<QT3DSU8 *>(tag); + return data + sizeof(SAllocationTag); + } + + void *allocate(size_t size, const char * /*typeName*/, const char * /*filename*/, + int /*line*/, int /*flags*/ = 0) override + { + return doAllocate(size); + } + + void *allocate(size_t size, const char * /*typeName*/, const char * /*filename*/, + int /*line*/, size_t /*alignment*/, size_t /*alignmentOffset*/) override + { + return doAllocate(size); + } + + /** + \brief Frees memory previously allocated by allocate(). + + <b>Threading:</b> This function should be thread safe as it can be called in the context of + the user thread + and physics processing thread(s). + + \param ptr Memory to free. + */ + void deallocate(void *ptr) override + { + TLockType locker(m_Mutex); + + // Deallocate on null is fine. + if (ptr == NULL) + return; + + SAllocationTag tempTag; + QT3DSU8 *dataPtr = reinterpret_cast<QT3DSU8 *>(ptr); + SAllocationTag *theTag = + reinterpret_cast<SAllocationTag *>(dataPtr - sizeof(SAllocationTag)); + if (theTag->m_Tag != tempTag.m_Tag) { + QT3DS_ASSERT(false); + return; + } + + size_t size = theTag->m_Size; + + // We add this offset at allocation time + ptr = dataPtr - sizeof(SAllocationTag); + +#define HANDLE_POOLING_ALLOCATOR_POOL_SIZE(sz) \ + if (size <= sz) { \ + m_Pool##sz.deallocate(ptr); \ + return; \ + } + ITERATE_POOLING_ALLOCATOR_POOL_SIZES +#undef HANDLE_POOLING_ALLOCATOR_POOL_SIZE + m_LargeAllocator.deallocate(ptr); + } + }; +} +} + +#endif diff --git a/src/foundation/PreAllocatedAllocator.h b/src/foundation/PreAllocatedAllocator.h new file mode 100644 index 0000000..aea0357 --- /dev/null +++ b/src/foundation/PreAllocatedAllocator.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DS_RENDER_PRE_ALLOCATED_ALLOCATOR_H +#define QT3DS_RENDER_PRE_ALLOCATED_ALLOCATOR_H +#include "foundation/Qt3DSAllocatorCallback.h" +#include "foundation/Qt3DSDataRef.h" + +namespace qt3ds { +namespace foundation { + + // Ignores deallocate calls if they originate withing the pre-allocation block + struct SPreAllocatedAllocator : public NVAllocatorCallback + { + NVAllocatorCallback &m_Allocator; + NVDataRef<QT3DSU8> m_PreAllocatedBlock; + bool m_OwnsMemory; // then we attempt to deallocate on destruction + + SPreAllocatedAllocator(NVAllocatorCallback &inAllocator) + : m_Allocator(inAllocator) + , m_OwnsMemory(false) + { + } + + SPreAllocatedAllocator(NVAllocatorCallback &inAllocator, NVDataRef<QT3DSU8> inData, + bool inOwnsMemory) + : m_Allocator(inAllocator) + , m_PreAllocatedBlock(inData) + , m_OwnsMemory(inOwnsMemory) + { + } + + virtual ~SPreAllocatedAllocator() + { + if (m_OwnsMemory) + m_Allocator.deallocate(m_PreAllocatedBlock.begin()); + } + + void *allocate(size_t size, const char *typeName, const char *filename, int line, + int flags = 0) override + { + return m_Allocator.allocate(size, typeName, filename, line, flags); + } + + void *allocate(size_t size, const char *typeName, const char *filename, int line, + size_t alignment, size_t alignmentOffset) override + { + return m_Allocator.allocate(size, typeName, filename, line, alignment, alignmentOffset); + } + + void deallocate(void *ptr) override + { + if (!ptr) + return; + if (ptr < m_PreAllocatedBlock.begin() || ptr >= m_PreAllocatedBlock.end()) + m_Allocator.deallocate(ptr); + } + }; +} +} + +#endif diff --git a/src/foundation/Qt3DS.h b/src/foundation/Qt3DS.h new file mode 100644 index 0000000..041923c --- /dev/null +++ b/src/foundation/Qt3DS.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_H +#define QT3DS_H +#include "EABase/eabase.h" +#include "foundation/Qt3DSPreprocessor.h" +#include "foundation/Qt3DSSimpleTypes.h" +#include "foundation/Qt3DSAssert.h" + +namespace eastl { +} + +namespace qt3ds { +class NVEmpty +{ +}; +class QT3DSMat33; +class QT3DSMat44; +class QT3DSVec2; +class QT3DSVec3; +class QT3DSVec4; +class NVTransform; + +#if !defined QT3DS_DONT_PRAGMA_WARNINGS && defined EA_COMPILER_MSVC +#pragma warning(disable : 4512) // assignment operator not generated +#endif + +#define QT3DS_SIGN_BITMASK 0x80000000 +} + +#endif diff --git a/src/foundation/Qt3DSAllocator.h b/src/foundation/Qt3DSAllocator.h new file mode 100644 index 0000000..cdbbe5f --- /dev/null +++ b/src/foundation/Qt3DSAllocator.h @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_PSALLOCATOR_H +#define QT3DS_FOUNDATION_PSALLOCATOR_H + +#include "foundation/Qt3DSAllocatorCallback.h" +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSAssert.h" + +#if (defined(QT3DS_WINDOWS) | defined(QT3DS_X360)) +#include <typeinfo.h> +#endif +#if (defined(QT3DS_APPLE)) +#include <typeinfo> +#endif + +#include <new> + +// Allocation macros going through user allocator +#define QT3DS_ALLOC(alloc, n, name) alloc.allocate(n, name, __FILE__, __LINE__) +#define QT3DS_ALLOC_TEMP(alloc, n, name) QT3DS_ALLOC(n, name) +#define QT3DS_FREE(alloc, x) alloc.deallocate(x) +#define QT3DS_FREE_AND_RESET(x) \ +{ \ + QT3DS_FREE(x); \ + x = 0; \ + } + +// The following macros support plain-old-types and classes derived from UserAllocated. +#define QT3DS_NEW(alloc, T) new (QT3DS_ALLOC(alloc, sizeof(T), #T)) T +#define QT3DS_NEW_TEMP(alloc, T) QT3DS_NEW(alloc, T) +#define QT3DS_DELETE_POD(x) \ +{ \ + QT3DS_FREE(x); \ + x = 0; \ + } + +namespace qt3ds { +namespace foundation { +template <typename TObjType> +inline void NVDelete(NVAllocatorCallback &alloc, TObjType *item) +{ + if (item) { + item->~TObjType(); + alloc.deallocate(item); + } +} +} +} + +//! placement new macro to make it easy to spot bad use of 'new' +#define QT3DS_PLACEMENT_NEW(p, T) new (p) T + +// Don't use inline for alloca !!! +#ifdef QT3DS_WINDOWS +#include <malloc.h> +#define NVAlloca(x) _alloca(x) +#elif defined(QT3DS_LINUX) || defined(QT3DS_ANDROID) || defined(QT3DS_QNX) +#if defined(__INTEGRITY) +#include <alloca.h> +#else +#include <malloc.h> +#endif +#define NVAlloca(x) alloca(x) +#elif defined(QT3DS_PSP2) +#include <alloca.h> +#define NVAlloca(x) alloca(x) +#elif defined(QT3DS_APPLE) +#include <stdlib.h> +#include <alloca.h> +#define NVAlloca(x) alloca(x) +#elif defined(QT3DS_PS3) +#include <alloca.h> +#define NVAlloca(x) alloca(x) +#elif defined(QT3DS_X360) +#include <malloc.h> +#define NVAlloca(x) _alloca(x) +#elif defined(QT3DS_WII) +#include <alloca.h> +#define NVAlloca(x) alloca(x) +#endif + +namespace qt3ds { +namespace foundation { +/* + * Bootstrap allocator using malloc/free. + * Don't use unless your objects get allocated before foundation is initialized. + */ +class RawAllocator +{ +public: + RawAllocator(const char * = 0) {} + void *allocate(size_t size, const char *, int) + { +#if defined(QT3DS_APPLE) + // malloc returns valid pointer for size==0, no need to check + return malloc(size); +#else + // malloc returns valid pointer for size==0, no need to check + return ::malloc(size); +#endif + } + void deallocate(void *ptr) + { +#if defined(QT3DS_APPLE) + free(ptr); +#else + // free(0) is guaranteed to have no side effect, no need to check + ::free(ptr); +#endif + } +}; + +struct ForwardingAllocator +{ + NVAllocatorCallback *mAllocator; + const char *mTypeName; + ForwardingAllocator(const char *typeName) + : mTypeName(typeName) + { + } + ForwardingAllocator(NVAllocatorCallback &alloc, const char *typeName) + : mAllocator(&alloc) + , mTypeName(typeName) + { + } + ForwardingAllocator(NVAllocatorCallback *alloc = NULL) + : mAllocator(alloc) + , mTypeName("__error__") + { + QT3DS_ASSERT(false); + } + ForwardingAllocator(const ForwardingAllocator &other) + : mAllocator(other.mAllocator) + , mTypeName(other.mTypeName) + { + } + ForwardingAllocator &operator=(const ForwardingAllocator &other) + { + mAllocator = other.mAllocator; + mTypeName = other.mTypeName; + return *this; + } + bool operator==(const ForwardingAllocator &other) const + { + return mAllocator == other.mAllocator; + } + NVAllocatorCallback &getAllocator() { return *mAllocator; } + // flags are unused + void *allocate(size_t size, const char *filename, int line, int flags = 0) + { + return getAllocator().allocate(size, mTypeName, filename, line, flags); + } + void *allocate(size_t size, const char *filename, int line, size_t alignment, + size_t alignmentOffset) + { + return getAllocator().allocate(size, mTypeName, filename, line, alignment, + alignmentOffset); + } + void deallocate(void *ptr, size_t) { getAllocator().deallocate(ptr); } + void deallocate(void *ptr) { getAllocator().deallocate(ptr); } +}; + +} // namespace foundation +} // namespace qt3ds + +#endif diff --git a/src/foundation/Qt3DSAllocatorCallback.h b/src/foundation/Qt3DSAllocatorCallback.h new file mode 100644 index 0000000..ebbf819 --- /dev/null +++ b/src/foundation/Qt3DSAllocatorCallback.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_ALLOCATOR_CALLBACK_H +#define QT3DS_FOUNDATION_QT3DS_ALLOCATOR_CALLBACK_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/Qt3DS.h" +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif + +/** +\brief Abstract base class for an application defined memory allocator that can be used by the NV +library. + +\note The SDK state should not be modified from within any allocation/free function. + +<b>Threading:</b> All methods of this class should be thread safe as it can be called from the user +thread +or the physics processing thread(s). +*/ + +class NVAllocatorCallback +{ +public: + /** + \brief destructor + */ + virtual ~NVAllocatorCallback() {} + + /** + \brief Allocates size bytes of memory, which must be 16-byte aligned. + + This method should never return NULL. If you run out of memory, then + you should terminate the app or take some other appropriate action. + + <b>Threading:</b> This function should be thread safe as it can be called in the context of the + user thread + and physics processing thread(s). + + \param size Number of bytes to allocate. + \param typeName Name of the datatype that is being allocated + \param filename The source file which allocated the memory + \param line The source line which allocated the memory + \return The allocated block of memory. + */ + virtual void *allocate(size_t size, const char *typeName, const char *filename, int line, + int flags = 0) = 0; + virtual void *allocate(size_t size, const char *typeName, const char *filename, int line, + size_t alignment, size_t alignmentOffset) = 0; + + /** + \brief Frees memory previously allocated by allocate(). + + <b>Threading:</b> This function should be thread safe as it can be called in the context of the + user thread + and physics processing thread(s). + + \param ptr Memory to free. + */ + virtual void deallocate(void *ptr) = 0; +}; + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** @} */ +#endif diff --git a/src/foundation/Qt3DSAssert.h b/src/foundation/Qt3DSAssert.h new file mode 100644 index 0000000..a628159 --- /dev/null +++ b/src/foundation/Qt3DSAssert.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_ASSERT_H +#define QT3DS_ASSERT_H +#include "foundation/Qt3DSPreprocessor.h" + +// Force the user to define the Qt3DSAssert function +namespace qt3ds { +void Qt3DSAssert(const char *exp, const char *file, int line, bool *ignore); +} + +#ifdef __CUDACC__ +#define QT3DS_ASSERT(exp) ((void)0) +#define QT3DS_ALWAYS_ASSERT_MESSAGE(exp) ((void)0) +#else // __CUDACC__ +#ifndef NDEBUG +#define QT3DS_ASSERT(exp) \ + { \ + static bool ignore = false; \ + (void)((!!(exp)) || (qt3ds::Qt3DSAssert(#exp, __FILE__, __LINE__, &ignore), false)); \ + } +#else // NDEBUG +#define QT3DS_ASSERT(exp) ((void)0) +#endif // NDEBUG +#define QT3DS_ALWAYS_ASSERT_MESSAGE(exp) \ + { \ + static bool ignore = false; \ + (void)((qt3ds::Qt3DSAssert(exp, __FILE__, __LINE__, &ignore), false)); \ + } +#endif // __CUDACC__ + +#define QT3DS_ALWAYS_ASSERT() QT3DS_ASSERT(0) + +#endif diff --git a/src/foundation/Qt3DSAtomic.h b/src/foundation/Qt3DSAtomic.h new file mode 100644 index 0000000..69c7d3b --- /dev/null +++ b/src/foundation/Qt3DSAtomic.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_PSATOMIC_H +#define QT3DS_FOUNDATION_PSATOMIC_H + +#include "foundation/Qt3DS.h" + +namespace qt3ds { +namespace foundation { + /* set *dest equal to val. Return the old value of *dest */ + QT3DS_AUTOTEST_EXPORT QT3DSI32 atomicExchange(volatile QT3DSI32 *dest, QT3DSI32 val); + + /* if *dest == comp, replace with exch. Return original value of *dest */ + QT3DS_AUTOTEST_EXPORT QT3DSI32 atomicCompareExchange(volatile QT3DSI32 *dest, QT3DSI32 exch, QT3DSI32 comp); + + /* if *dest == comp, replace with exch. Return original value of *dest */ + QT3DS_AUTOTEST_EXPORT void *atomicCompareExchangePointer(volatile void **dest, void *exch, + void *comp); + + /* increment the specified location. Return the incremented value */ + QT3DS_AUTOTEST_EXPORT QT3DSI32 atomicIncrement(volatile QT3DSI32 *val); + + /* decrement the specified location. Return the decremented value */ + QT3DS_AUTOTEST_EXPORT QT3DSI32 atomicDecrement(volatile QT3DSI32 *val); + + /* add delta to *val. Return the new value */ + QT3DS_AUTOTEST_EXPORT QT3DSI32 atomicAdd(volatile QT3DSI32 *val, QT3DSI32 delta); + + /* compute the maximum of dest and val. Return the new value */ + QT3DS_AUTOTEST_EXPORT QT3DSI32 atomicMax(volatile QT3DSI32 *val, QT3DSI32 val2); + +} // namespace foundation +} // namespace qt3ds + +#endif diff --git a/src/foundation/Qt3DSBasicTemplates.h b/src/foundation/Qt3DSBasicTemplates.h new file mode 100644 index 0000000..f14c7b3 --- /dev/null +++ b/src/foundation/Qt3DSBasicTemplates.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_PSBASICTEMPLATES_H +#define QT3DS_FOUNDATION_PSBASICTEMPLATES_H + +#include "foundation/Qt3DS.h" + +namespace qt3ds { +namespace foundation { + template <typename A> + struct Equal + { + bool operator()(const A &a, const A &b) const { return a == b; } + }; + + template <typename A> + struct Less + { + bool operator()(const A &a, const A &b) const { return a < b; } + }; + + template <typename A> + struct Greater + { + bool operator()(const A &a, const A &b) const { return a > b; } + }; + + template <class F, class S> + class Pair + { + public: + F first; + S second; + Pair() + : first(F()) + , second(S()) + { + } + Pair(const F &f, const S &s) + : first(f) + , second(s) + { + } + Pair(const Pair &p) + : first(p.first) + , second(p.second) + { + } + // CN - fix for /.../Qt3DSBasicTemplates.h(61) : warning C4512: 'nv::foundation::Pair<F,S>' : + // assignment operator could not be generated + Pair &operator=(const Pair &p) + { + first = p.first; + second = p.second; + return *this; + } + bool operator==(const Pair &p) const { return first == p.first && second == p.second; } + }; + + template <unsigned int A> + struct LogTwo + { + static const unsigned int value = LogTwo<(A >> 1)>::value + 1; + }; + template <> + struct LogTwo<1> + { + static const unsigned int value = 0; + }; + + template <typename T> + struct UnConst + { + typedef T Type; + }; + template <typename T> + struct UnConst<const T> + { + typedef T Type; + }; + +} // namespace foundation +} // namespace qt3ds + +#endif diff --git a/src/foundation/Qt3DSBounds3.h b/src/foundation/Qt3DSBounds3.h new file mode 100644 index 0000000..fda4a6d --- /dev/null +++ b/src/foundation/Qt3DSBounds3.h @@ -0,0 +1,443 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_BOUNDS3_H +#define QT3DS_FOUNDATION_QT3DS_BOUNDS3_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/Qt3DSTransform.h" +#include "foundation/Qt3DSMat33.h" +#include "foundation/Qt3DSMat44.h" + +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif + +typedef QT3DSVec3 TNVBounds2BoxPoints[8]; + +/** +\brief Class representing 3D range or axis aligned bounding box. + +Stored as minimum and maximum extent corners. Alternate representation +would be center and dimensions. +May be empty or nonempty. If not empty, minimum <= maximum has to hold. +*/ +class NVBounds3 +{ +public: + /** + \brief Default constructor, not performing any initialization for performance reason. + \remark Use empty() function below to construct empty bounds. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVBounds3() {} + + /** + \brief Construct from two bounding points + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVBounds3(const QT3DSVec3 &minimum, const QT3DSVec3 &maximum); + + /** + \brief Return empty bounds. + */ + static QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVBounds3 empty(); + + /** + \brief returns the AABB containing v0 and v1. + \param v0 first point included in the AABB. + \param v1 second point included in the AABB. + */ + static QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVBounds3 boundsOfPoints(const QT3DSVec3 &v0, + const QT3DSVec3 &v1); + + /** + \brief returns the AABB from center and extents vectors. + \param center Center vector + \param extent Extents vector + */ + static QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVBounds3 centerExtents(const QT3DSVec3 ¢er, + const QT3DSVec3 &extent); + + /** + \brief Construct from center, extent, and (not necessarily orthogonal) basis + */ + static QT3DS_CUDA_CALLABLE QT3DS_INLINE NVBounds3 basisExtent(const QT3DSVec3 ¢er, + const QT3DSMat33 &basis, + const QT3DSVec3 &extent); + + /** + \brief Construct from pose and extent + */ + static QT3DS_CUDA_CALLABLE QT3DS_INLINE NVBounds3 poseExtent(const NVTransform &pose, + const QT3DSVec3 &extent); + + /** + \brief gets the transformed bounds of the passed AABB (resulting in a bigger AABB). + \param[in] matrix Transform to apply, can contain scaling as well + \param[in] bounds The bounds to transform. + */ + static QT3DS_CUDA_CALLABLE QT3DS_INLINE NVBounds3 transform(const QT3DSMat33 &matrix, + const NVBounds3 &bounds); + + /** + \brief gets the transformed bounds of the passed AABB (resulting in a bigger AABB). + \param[in] transform Transform to apply, can contain scaling as well + \param[in] bounds The bounds to transform. + */ + static QT3DS_CUDA_CALLABLE QT3DS_INLINE NVBounds3 transform(const NVTransform &transform, + const NVBounds3 &bounds); + + /** + \brief Sets empty to true + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void setEmpty(); + + /** + \brief Sets infinite bounds + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void setInfinite(); + + /** + \brief expands the volume to include v + \param v Point to expand to. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void include(const QT3DSVec3 &v); + + /** + \brief expands the volume to include b. + \param b Bounds to perform union with. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void include(const NVBounds3 &b); + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool isEmpty() const; + + /** + \brief indicates whether the intersection of this and b is empty or not. + \param b Bounds to test for intersection. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool intersects(const NVBounds3 &b) const; + + /** + \brief computes the 1D-intersection between two AABBs, on a given axis. + \param a the other AABB + \param axis the axis (0, 1, 2) + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool intersects1D(const NVBounds3 &a, QT3DSU32 axis) const; + + /** + \brief indicates if these bounds contain v. + \param v Point to test against bounds. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool contains(const QT3DSVec3 &v) const; + + /** + \brief checks a box is inside another box. + \param box the other AABB + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool isInside(const NVBounds3 &box) const; + + /** + \brief returns the center of this axis aligned box. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 getCenter() const; + + /** + \brief get component of the box's center along a given axis + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float getCenter(QT3DSU32 axis) const; + + /** + \brief get component of the box's extents along a given axis + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float getExtents(QT3DSU32 axis) const; + + /** + \brief returns the dimensions (width/height/depth) of this axis aligned box. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 getDimensions() const; + + /** + \brief returns the extents, which are half of the width/height/depth. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 getExtents() const; + + /** + \brief scales the AABB. + \param scale Factor to scale AABB by. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void scale(QT3DSF32 scale); + + /** + fattens the AABB in all 3 dimensions by the given distance. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void fatten(NVReal distance); + + /** + checks that the AABB values are not NaN + */ + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool isFinite() const; + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void expand(TNVBounds2BoxPoints &outPoints) const; + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void transform(const QT3DSMat44 &inMatrix); + + QT3DSVec3 minimum, maximum; +}; + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVBounds3::NVBounds3(const QT3DSVec3 &minimum, const QT3DSVec3 &maximum) + : minimum(minimum) + , maximum(maximum) +{ +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVBounds3 NVBounds3::empty() +{ + return NVBounds3(QT3DSVec3(QT3DS_MAX_REAL), QT3DSVec3(-QT3DS_MAX_REAL)); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool NVBounds3::isFinite() const +{ + return minimum.isFinite() && maximum.isFinite(); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVBounds3 NVBounds3::boundsOfPoints(const QT3DSVec3 &v0, + const QT3DSVec3 &v1) +{ + return NVBounds3(v0.minimum(v1), v0.maximum(v1)); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVBounds3 NVBounds3::centerExtents(const QT3DSVec3 ¢er, + const QT3DSVec3 &extent) +{ + return NVBounds3(center - extent, center + extent); +} + +QT3DS_CUDA_CALLABLE QT3DS_INLINE NVBounds3 NVBounds3::basisExtent(const QT3DSVec3 ¢er, + const QT3DSMat33 &basis, + const QT3DSVec3 &extent) +{ + // extended basis vectors + QT3DSVec3 c0 = basis.column0 * extent.x; + QT3DSVec3 c1 = basis.column1 * extent.y; + QT3DSVec3 c2 = basis.column2 * extent.z; + + QT3DSVec3 w; + // find combination of base vectors that produces max. distance for each component = sum of + // abs() + w.x = NVAbs(c0.x) + NVAbs(c1.x) + NVAbs(c2.x); + w.y = NVAbs(c0.y) + NVAbs(c1.y) + NVAbs(c2.y); + w.z = NVAbs(c0.z) + NVAbs(c1.z) + NVAbs(c2.z); + + return NVBounds3(center - w, center + w); +} + +QT3DS_CUDA_CALLABLE QT3DS_INLINE NVBounds3 NVBounds3::poseExtent(const NVTransform &pose, + const QT3DSVec3 &extent) +{ + return basisExtent(pose.p, QT3DSMat33(pose.q), extent); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void NVBounds3::setEmpty() +{ + minimum = QT3DSVec3(QT3DS_MAX_REAL); + maximum = QT3DSVec3(-QT3DS_MAX_REAL); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void NVBounds3::setInfinite() +{ + minimum = QT3DSVec3(-QT3DS_MAX_REAL); + maximum = QT3DSVec3(QT3DS_MAX_REAL); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void NVBounds3::include(const QT3DSVec3 &v) +{ + QT3DS_ASSERT(isFinite()); + minimum = minimum.minimum(v); + maximum = maximum.maximum(v); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void NVBounds3::include(const NVBounds3 &b) +{ + QT3DS_ASSERT(isFinite()); + minimum = minimum.minimum(b.minimum); + maximum = maximum.maximum(b.maximum); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool NVBounds3::isEmpty() const +{ + QT3DS_ASSERT(isFinite()); + // Consistency condition for (Min, Max) boxes: minimum < maximum + return minimum.x > maximum.x || minimum.y > maximum.y || minimum.z > maximum.z; +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool NVBounds3::intersects(const NVBounds3 &b) const +{ + QT3DS_ASSERT(isFinite() && b.isFinite()); + return !(b.minimum.x > maximum.x || minimum.x > b.maximum.x || b.minimum.y > maximum.y + || minimum.y > b.maximum.y || b.minimum.z > maximum.z || minimum.z > b.maximum.z); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool NVBounds3::intersects1D(const NVBounds3 &a, QT3DSU32 axis) const +{ + QT3DS_ASSERT(isFinite() && a.isFinite()); + return maximum[axis] >= a.minimum[axis] && a.maximum[axis] >= minimum[axis]; +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool NVBounds3::contains(const QT3DSVec3 &v) const +{ + QT3DS_ASSERT(isFinite()); + + return !(v.x < minimum.x || v.x > maximum.x || v.y < minimum.y || v.y > maximum.y + || v.z < minimum.z || v.z > maximum.z); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool NVBounds3::isInside(const NVBounds3 &box) const +{ + QT3DS_ASSERT(isFinite() && box.isFinite()); + if (box.minimum.x > minimum.x) + return false; + if (box.minimum.y > minimum.y) + return false; + if (box.minimum.z > minimum.z) + return false; + if (box.maximum.x < maximum.x) + return false; + if (box.maximum.y < maximum.y) + return false; + if (box.maximum.z < maximum.z) + return false; + return true; +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 NVBounds3::getCenter() const +{ + QT3DS_ASSERT(isFinite()); + return (minimum + maximum) * NVReal(0.5); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float NVBounds3::getCenter(QT3DSU32 axis) const +{ + QT3DS_ASSERT(isFinite()); + return (minimum[axis] + maximum[axis]) * NVReal(0.5); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float NVBounds3::getExtents(QT3DSU32 axis) const +{ + QT3DS_ASSERT(isFinite()); + return (maximum[axis] - minimum[axis]) * NVReal(0.5); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 NVBounds3::getDimensions() const +{ + QT3DS_ASSERT(isFinite()); + return maximum - minimum; +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 NVBounds3::getExtents() const +{ + QT3DS_ASSERT(isFinite()); + return getDimensions() * NVReal(0.5); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void NVBounds3::scale(QT3DSF32 scale) +{ + QT3DS_ASSERT(isFinite()); + *this = centerExtents(getCenter(), getExtents() * scale); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void NVBounds3::fatten(NVReal distance) +{ + QT3DS_ASSERT(isFinite()); + minimum.x -= distance; + minimum.y -= distance; + minimum.z -= distance; + + maximum.x += distance; + maximum.y += distance; + maximum.z += distance; +} + +QT3DS_CUDA_CALLABLE QT3DS_INLINE NVBounds3 NVBounds3::transform(const QT3DSMat33 &matrix, + const NVBounds3 &bounds) +{ + QT3DS_ASSERT(bounds.isFinite()); + return bounds.isEmpty() ? bounds : NVBounds3::basisExtent(matrix * bounds.getCenter(), matrix, + bounds.getExtents()); +} + +QT3DS_CUDA_CALLABLE QT3DS_INLINE NVBounds3 NVBounds3::transform(const NVTransform &transform, + const NVBounds3 &bounds) +{ + QT3DS_ASSERT(bounds.isFinite()); + return bounds.isEmpty() ? bounds + : NVBounds3::basisExtent(transform.transform(bounds.getCenter()), + QT3DSMat33(transform.q), bounds.getExtents()); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void NVBounds3::expand(TNVBounds2BoxPoints &outPoints) const +{ + if (isEmpty()) { + for (QT3DSU32 idx = 0; idx < 8; ++idx) + outPoints[idx] = QT3DSVec3(0, 0, 0); + } else { + // Min corner of box + outPoints[0] = QT3DSVec3(minimum[0], minimum[1], minimum[2]); + outPoints[1] = QT3DSVec3(maximum[0], minimum[1], minimum[2]); + outPoints[2] = QT3DSVec3(minimum[0], maximum[1], minimum[2]); + outPoints[3] = QT3DSVec3(minimum[0], minimum[1], maximum[2]); + + // Max corner of box + outPoints[4] = QT3DSVec3(maximum[0], maximum[1], maximum[2]); + outPoints[5] = QT3DSVec3(minimum[0], maximum[1], maximum[2]); + outPoints[6] = QT3DSVec3(maximum[0], minimum[1], maximum[2]); + outPoints[7] = QT3DSVec3(maximum[0], maximum[1], minimum[2]); + } +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void NVBounds3::transform(const QT3DSMat44 &inMatrix) +{ + if (!isEmpty()) { + TNVBounds2BoxPoints thePoints; + expand(thePoints); + setEmpty(); + for (QT3DSU32 idx = 0; idx < 8; ++idx) + include(inMatrix.transform(thePoints[idx])); + } +} + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** @} */ +#endif // QT3DS_FOUNDATION_QT3DS_BOUNDS3_H diff --git a/src/foundation/Qt3DSBroadcastingAllocator.h b/src/foundation/Qt3DSBroadcastingAllocator.h new file mode 100644 index 0000000..dc6ae7b --- /dev/null +++ b/src/foundation/Qt3DSBroadcastingAllocator.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_BROADCASTING_ALLOCATOR_H +#define QT3DS_FOUNDATION_QT3DS_BROADCASTING_ALLOCATOR_H + +#include "foundation/Qt3DSAllocatorCallback.h" + +/** \addtogroup foundation + @{ +*/ +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif + +/** +\brief Abstract listener class that listens to allocation and deallocation events from the + foundation memory system. + +<b>Threading:</b> All methods of this class should be thread safe as it can be called from the user +thread +or the physics processing thread(s). +*/ +class NVAllocationListener +{ +protected: + virtual ~NVAllocationListener() {} + +public: + /** + \brief callback when memory is allocated. + \param size Size of the allocation in bytes. + \param typeName Type this data is being allocated for. + \param filename File the allocation came from. + \param line the allocation came from. + \param allocatedMemory memory that will be returned from the allocation. + */ + virtual void onAllocation(size_t size, const char *typeName, const char *filename, int line, + void *allocatedMemory) = 0; + + /** + /brief callback when memory is deallocated. + /param allocatedMemory memory just before allocation. + */ + virtual void onDeallocation(void *allocatedMemory) = 0; +}; + +/** +\brief Abstract base class for an application defined memory allocator that allows an external +listener +to audit the memory allocations. + +<b>Threading:</b> Register/deregister are *not* threadsafe!!! +You need to be sure multiple threads are using this allocator when you are adding +new listeners. +*/ +class NVBroadcastingAllocator : public NVAllocatorCallback +{ +protected: + virtual ~NVBroadcastingAllocator() {} + +public: + /** + \brief Register an allocation listener. This object will be notified whenever an + allocation happens. + + <b>Threading:</b>Not threadsafe if you are allocating and deallocating in another + thread using this allocator. + */ + virtual void registerAllocationListener(NVAllocationListener &inListener) = 0; + /** + \brief Deregister an allocation listener. This object will no longer receive + notifications upon allocation. + + <b>Threading:</b>Not threadsafe if you are allocating and deallocating in another + thread using this allocator. + */ + virtual void deregisterAllocationListener(NVAllocationListener &inListener) = 0; +}; + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** @} */ +#endif // QT3DS_FOUNDATION_QT3DS_BROADCASTING_ALLOCATOR_H diff --git a/src/foundation/Qt3DSContainers.h b/src/foundation/Qt3DSContainers.h new file mode 100644 index 0000000..de86d85 --- /dev/null +++ b/src/foundation/Qt3DSContainers.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once +#ifndef QT3DS_CONTAINERS_H +#define QT3DS_CONTAINERS_H +#include "foundation/Qt3DS.h" +#include "EASTL/vector.h" +#include "EASTL/hash_map.h" +#include "EASTL/hash_set.h" +#include "foundation/Qt3DSAllocator.h" +#include "foundation/Qt3DSDataRef.h" + +namespace qt3ds { +namespace foundation { + + template <typename T> + class nvvector : public eastl::vector<T, ForwardingAllocator> + { + typedef eastl::vector<T, ForwardingAllocator> base_type; + + public: + using base_type::data; + using base_type::size; + + nvvector(NVAllocatorCallback &inAllocator, const char *inTypeName) + : eastl::vector<T, ForwardingAllocator>(ForwardingAllocator(inAllocator, inTypeName)) + { + } + nvvector(const nvvector<T> &inOther) + : eastl::vector<T, ForwardingAllocator>(inOther) + { + } + nvvector<T> &operator=(const nvvector<T> &inOther) + { + eastl::vector<T, ForwardingAllocator>::operator=(inOther); + return *this; + } + operator NVConstDataRef<T>() const { return NVConstDataRef<T>(data(), size()); } + operator NVDataRef<T>() { return NVDataRef<T>(data(), size()); } + }; + + template <typename TKey, typename TValue, typename THash = eastl::hash<TKey>, + typename TPredicate = eastl::equal_to<TKey>> + class nvhash_map : public eastl::hash_map<TKey, TValue, THash, TPredicate, ForwardingAllocator> + { + typedef eastl::hash_map<TKey, TValue, THash, TPredicate, ForwardingAllocator> base_type; + + public: + using base_type::find; + using base_type::end; + + nvhash_map(NVAllocatorCallback &inAllocator, const char *inTypeName) + : eastl::hash_map<TKey, TValue, THash, TPredicate, ForwardingAllocator>( + ForwardingAllocator(inAllocator, inTypeName)) + { + } + nvhash_map(const nvhash_map<TKey, TValue, THash, TPredicate> &inOther) + : eastl::hash_map<TKey, TValue, THash, TPredicate, ForwardingAllocator>(inOther) + { + } + nvhash_map<TKey, TValue, THash, TPredicate> & + operator=(const nvhash_map<TKey, TValue, THash, TPredicate> &inOther) + { + eastl::hash_map<TKey, TValue, THash, TPredicate, ForwardingAllocator>::operator=( + inOther); + return *this; + } + bool contains(const TKey &inKey) const { return find(inKey) != end(); } + }; + + template <typename TKey, typename THash = eastl::hash<TKey>, + typename TPredicate = eastl::equal_to<TKey>> + class nvhash_set : public eastl::hash_set<TKey, THash, TPredicate, ForwardingAllocator> + { + typedef eastl::hash_set<TKey, THash, TPredicate, ForwardingAllocator> base_type; + + public: + using base_type::find; + using base_type::end; + + nvhash_set(NVAllocatorCallback &inAllocator, const char *inTypeName) + : eastl::hash_set<TKey, THash, TPredicate, ForwardingAllocator>( + ForwardingAllocator(inAllocator, inTypeName)) + { + } + nvhash_set(const nvhash_map<TKey, THash, TPredicate> &inOther) + : eastl::hash_set<TKey, THash, TPredicate, ForwardingAllocator>(inOther) + { + } + nvhash_set<TKey, THash, TPredicate> & + operator=(const nvhash_set<TKey, THash, TPredicate> &inOther) + { + eastl::hash_set<TKey, THash, TPredicate, ForwardingAllocator>::operator=(inOther); + return *this; + } + bool contains(const TKey &inKey) const { return find(inKey) != end(); } + }; +} +} + +#endif diff --git a/src/foundation/Qt3DSDataRef.h b/src/foundation/Qt3DSDataRef.h new file mode 100644 index 0000000..ad860ce --- /dev/null +++ b/src/foundation/Qt3DSDataRef.h @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_DATA_REF_H +#define QT3DS_FOUNDATION_DATA_REF_H + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSAssert.h" + +namespace qt3ds { +namespace foundation { + + template <typename TDataType> + struct NVConstDataRef + { + const TDataType *mData; + QT3DSU32 mSize; + + NVConstDataRef(const TDataType *inData, QT3DSU32 inSize) + : mData(inData) + , mSize(inSize) + { + } + NVConstDataRef() + : mData(0) + , mSize(0) + { + } + + QT3DSU32 size() const { return mSize; } + + const TDataType *begin() const { return mData; } + const TDataType *end() const { return mData + mSize; } + + const TDataType &operator[](QT3DSU32 index) const + { + QT3DS_ASSERT(index < mSize); + return mData[index]; + } + }; + + template <typename TDataType> + inline NVConstDataRef<TDataType> toConstDataRef(const TDataType &type) + { + return NVConstDataRef<TDataType>(&type, 1); + } + + template <typename TDataType> + inline NVConstDataRef<QT3DSU8> toU8ConstDataRef(const TDataType &type) + { + return NVConstDataRef<QT3DSU8>(reinterpret_cast<const QT3DSU8 *>(&type), sizeof(TDataType)); + } + + template <typename TDataType> + inline NVConstDataRef<TDataType> toConstDataRef(const TDataType *type, QT3DSU32 count) + { + return NVConstDataRef<TDataType>(type, count); + } + + template <typename TDataType> + inline NVConstDataRef<QT3DSU8> toU8ConstDataRef(const TDataType *type, QT3DSU32 count) + { + return NVConstDataRef<QT3DSU8>(reinterpret_cast<const QT3DSU8 *>(type), + sizeof(TDataType) * count); + } + + template <typename TDataType> + struct NVDataRef + { + TDataType *mData; + QT3DSU32 mSize; + + NVDataRef(TDataType *inData, QT3DSU32 inSize) + : mData(inData) + , mSize(inSize) + { + } + NVDataRef() + : mData(0) + , mSize(0) + { + } + QT3DSU32 size() const { return mSize; } + + TDataType *begin() { return mData; } + TDataType *end() { return mData + mSize; } + + TDataType *begin() const { return mData; } + TDataType *end() const { return mData + mSize; } + + TDataType &operator[](QT3DSU32 index) + { + QT3DS_ASSERT(index < mSize); + return mData[index]; + } + + const TDataType &operator[](QT3DSU32 index) const + { + QT3DS_ASSERT(index < mSize); + return mData[index]; + } + + operator NVConstDataRef<TDataType>() const + { + return NVConstDataRef<TDataType>(mData, mSize); + } + }; + + template <typename TDataType> + inline NVDataRef<TDataType> toDataRef(TDataType &type) + { + return NVDataRef<TDataType>(&type, 1); + } + + template <typename TDataType> + inline NVDataRef<QT3DSU8> toU8DataRef(TDataType &type) + { + return NVDataRef<QT3DSU8>(reinterpret_cast<QT3DSU8 *>(&type), sizeof(TDataType)); + } + + template <typename TDataType> + inline NVDataRef<TDataType> toDataRef(TDataType *type, QT3DSU32 count) + { + return NVDataRef<TDataType>(type, count); + } + + template <typename TDataType> + inline NVDataRef<QT3DSU8> toU8DataRef(TDataType *type, QT3DSU32 count) + { + return NVDataRef<QT3DSU8>(reinterpret_cast<QT3DSU8 *>(type), sizeof(TDataType) * count); + } +} +} + +#endif
\ No newline at end of file diff --git a/src/foundation/Qt3DSDiscriminatedUnion.h b/src/foundation/Qt3DSDiscriminatedUnion.h new file mode 100644 index 0000000..1957eb2 --- /dev/null +++ b/src/foundation/Qt3DSDiscriminatedUnion.h @@ -0,0 +1,335 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_DISCRIMINATED_UNION_H +#define QT3DS_FOUNDATION_DISCRIMINATED_UNION_H +#include "Qt3DSAssert.h" +#include "foundation/Qt3DSSimpleTypes.h" +#include "foundation/Qt3DSIntrinsics.h" +#include "foundation/Qt3DSUnionCast.h" + +#ifdef WIN32 +#pragma warning(disable : 4100) +#endif + +namespace qt3ds { +namespace foundation { + + template <typename TUnionTraits, int TBufferSize> + class DiscriminatedUnion + { + public: + typedef DiscriminatedUnion<TUnionTraits, TBufferSize> TThisType; + typedef TUnionTraits TTraits; + typedef typename TTraits::TIdType TIdType; + + protected: + char m_Data[TBufferSize]; + // Id type must include a no-data type. + TIdType m_DataType; + + public: + DiscriminatedUnion() { TTraits::defaultConstruct(m_Data, m_DataType); } + + DiscriminatedUnion(const TThisType &inOther) + : m_DataType(inOther.m_DataType) + { + TTraits::copyConstruct(m_Data, inOther.m_Data, m_DataType); + } + + template <typename TDataType> + DiscriminatedUnion(const TDataType &inType) + { + TTraits::copyConstruct(m_Data, m_DataType, inType); + } + + ~DiscriminatedUnion() { TTraits::destruct(m_Data, m_DataType); } + + TThisType &operator=(const TThisType &inType) + { + if (this != &inType) { + TTraits::destruct(m_Data, m_DataType); + m_DataType = inType.m_DataType; + TTraits::copyConstruct(m_Data, inType.m_Data, inType.m_DataType); + } + return *this; + } + + typename TTraits::TIdType getType() const { return m_DataType; } + + template <typename TDataType> + const TDataType *getDataPtr() const + { + return TTraits::template getDataPtr<TDataType>(m_Data, m_DataType); + } + + template <typename TDataType> + TDataType *getDataPtr() + { + return TTraits::template getDataPtr<TDataType>(m_Data, m_DataType); + } + + template <typename TDataType> + TDataType getData() const + { + const TDataType *dataPtr = getDataPtr<TDataType>(); + if (dataPtr) + return *dataPtr; + QT3DS_ASSERT(false); + return TDataType(); + } + + bool operator==(const TThisType &inOther) const + { + return m_DataType == inOther.m_DataType + && TTraits::areEqual(m_Data, inOther.m_Data, m_DataType); + } + + bool operator!=(const TThisType &inOther) const + { + return m_DataType != inOther.m_DataType + || TTraits::areEqual(m_Data, inOther.m_Data, m_DataType) == false; + } + + template <typename TRetType, typename TVisitorType> + TRetType visit(TVisitorType inVisitor) + { + return TTraits::template visit<TRetType>(m_Data, m_DataType, inVisitor); + } + + template <typename TRetType, typename TVisitorType> + TRetType visit(TVisitorType inVisitor) const + { + return TTraits::template visit<TRetType>(m_Data, m_DataType, inVisitor); + } + }; + + // Helper system to enable quicker and correct construction of union traits types + + struct CopyConstructVisitor + { + const char *m_Src; + CopyConstructVisitor(const char *inSrc) + : m_Src(inSrc) + { + } + + template <typename TDataType> + void operator()(TDataType &inDst) + { + new (&inDst) TDataType(*NVUnionCast<const TDataType *>(m_Src)); + } + void operator()() { QT3DS_ASSERT(false); } + }; + + template <typename TDataType> + struct DestructTraits + { + void destruct(TDataType &inType) { inType.~TDataType(); } + }; + + // Until compilers improve a bit, you need this for POD types else you get + // unused parameter warnings. + template <> + struct DestructTraits<QT3DSU8> + { + void destruct(QT3DSU8 &) {} + }; + template <> + struct DestructTraits<QT3DSI8> + { + void destruct(QT3DSI8 &) {} + }; + template <> + struct DestructTraits<QT3DSU16> + { + void destruct(QT3DSU16 &) {} + }; + template <> + struct DestructTraits<QT3DSI16> + { + void destruct(QT3DSI16 &) {} + }; + template <> + struct DestructTraits<QT3DSU32> + { + void destruct(QT3DSU32 &) {} + }; + template <> + struct DestructTraits<QT3DSI32> + { + void destruct(QT3DSI32 &) {} + }; + template <> + struct DestructTraits<QT3DSU64> + { + void destruct(QT3DSU64 &) {} + }; + template <> + struct DestructTraits<QT3DSI64> + { + void destruct(QT3DSI64 &) {} + }; + template <> + struct DestructTraits<QT3DSF32> + { + void destruct(QT3DSF32 &) {} + }; + template <> + struct DestructTraits<QT3DSF64> + { + void destruct(QT3DSF64 &) {} + }; + template <> + struct DestructTraits<bool> + { + void destruct(bool &) {} + }; + template <> + struct DestructTraits<void *> + { + void destruct(void *&) {} + }; +#ifdef __INTEGRITY + template <> + struct DestructTraits<QT3DSVec2> + { + void destruct(QT3DSVec2 &) {} + }; + template <> + struct DestructTraits<QT3DSVec3> + { + void destruct(QT3DSVec3 &) {} + }; +#endif + + struct DestructVisitor + { + template <typename TDataType> + void operator()(TDataType &inDst) + { + DestructTraits<TDataType>().destruct(inDst); + } + void operator()() { QT3DS_ASSERT(false); } + }; + + template <typename TDataType> + struct EqualVisitorTraits + { + bool operator()(const TDataType &lhs, const TDataType &rhs) { return lhs == rhs; } + }; + + struct EqualVisitor + { + const char *m_Rhs; + EqualVisitor(const char *rhs) + : m_Rhs(rhs) + { + } + template <typename TDataType> + bool operator()(const TDataType &lhs) + { + const TDataType &rhs(*NVUnionCast<const TDataType *>(m_Rhs)); + return EqualVisitorTraits<TDataType>()(lhs, rhs); + } + bool operator()() + { + QT3DS_ASSERT(false); + return true; + } + }; + + template <typename TBase, QT3DSU32 TBufferSize> + struct DiscriminatedUnionGenericBase : public TBase + { + typedef typename TBase::TIdType TIdType; + + static void zeroBuf(char *outDst) { qt3ds::intrinsics::memZero(outDst, TBufferSize); } + + static void defaultConstruct(char *outDst, TIdType &outType) + { + zeroBuf(outDst); + outType = TBase::getNoDataId(); + } + + template <typename TDataType> + static void copyConstruct(char *outDst, TIdType &outType, const TDataType &inSrc) + { + zeroBuf(outDst); + StaticAssert<sizeof(TDataType) <= TBufferSize>::valid_expression(); + outType = TBase::template getType<TDataType>(); + new (outDst) TDataType(inSrc); + } + + static void copyConstruct(char *inDst, const char *inSrc, TIdType inType) + { + if (inType == TBase::getNoDataId()) + zeroBuf(inDst); + else + TBase::template visit<void>(inDst, inType, CopyConstructVisitor(inSrc)); + } + + static void destruct(char *inDst, TIdType inType) + { + if (inType != TBase::getNoDataId()) + TBase::template visit<void>(inDst, inType, DestructVisitor()); + zeroBuf(inDst); + } + + template <typename TDataType> + static const TDataType *getDataPtr(const char *inData, const TIdType &inType) + { + if (TBase::template getType<TDataType>() == inType) + return NVUnionCast<const TDataType *>(inData); + QT3DS_ASSERT(false); + return NULL; + } + + template <typename TDataType> + static TDataType *getDataPtr(char *inData, const TIdType &inType) + { + if (TBase::template getType<TDataType>() == inType) + return NVUnionCast<TDataType *>(inData); + QT3DS_ASSERT(false); + return NULL; + } + + static bool areEqual(const char *inLhs, const char *inRhs, TIdType inType) + { + if (inType != TBase::getNoDataId()) + return TBase::template visit<bool>(inLhs, inType, EqualVisitor(inRhs)); + else + return true; + } + }; +} +} + +#endif diff --git a/src/foundation/Qt3DSFPU.h b/src/foundation/Qt3DSFPU.h new file mode 100644 index 0000000..e4a47f0 --- /dev/null +++ b/src/foundation/Qt3DSFPU.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_PSFPU_H +#define QT3DS_FOUNDATION_PSFPU_H + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSUnionCast.h" + +// unsigned integer representation of a floating-point value. +#ifdef QT3DS_PS3 +QT3DS_FORCE_INLINE unsigned int QT3DS_IR(const float x) +{ + return qt3ds::NVUnionCast<unsigned int, float>(x); +} +#else +#define QT3DS_IR(x) ((QT3DSU32 &)(x)) +#endif + +// signed integer representation of a floating-point value. +#ifdef QT3DS_PS3 +QT3DS_FORCE_INLINE int QT3DS_SIR(const float x) +{ + return qt3ds::NVUnionCast<int, float>(x); +} +#else +#define QT3DS_SIR(x) ((QT3DSI32 &)(x)) +#endif + +// Floating-point representation of a integer value. +#ifdef QT3DS_PS3 +QT3DS_FORCE_INLINE float QT3DS_FR(const unsigned int x) +{ + return qt3ds::NVUnionCast<float, unsigned int>(x); +} +#else +#define QT3DS_FR(x) ((QT3DSF32 &)(x)) +#endif + +#ifdef QT3DS_PS3 +QT3DS_FORCE_INLINE float *QT3DS_FPTR(unsigned int *x) +{ + return qt3ds::NVUnionCast<float *, unsigned int *>(x); +} + +QT3DS_FORCE_INLINE float *QT3DS_FPTR(int *x) +{ + return qt3ds::NVUnionCast<float *, int *>(x); +} +#else +#define QT3DS_FPTR(x) ((QT3DSF32 *)(x)) +#endif + +#define QT3DS_SIGN_BITMASK 0x80000000 + +namespace qt3ds { +namespace foundation { +class QT3DS_FOUNDATION_API FPUGuard +{ +public: + FPUGuard(); // set fpu control word for PhysX + ~FPUGuard(); // restore fpu control word +private: + QT3DSU32 mControlWords[8]; +}; + +} // namespace foundation +} // namespace qt3ds + +#endif diff --git a/src/foundation/Qt3DSFastIPC.h b/src/foundation/Qt3DSFastIPC.h new file mode 100644 index 0000000..800c3df --- /dev/null +++ b/src/foundation/Qt3DSFastIPC.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FAST_IPC_H +#define QT3DS_FAST_IPC_H + +#include "foundation/Qt3DSSimpleTypes.h" +#include "foundation/Qt3DSIPC.h" + +namespace qt3ds { +namespace foundation { + + NVIPC *createFastIPC( + NVIPC::ErrorCode &errorCode, // error code return if creation fails + NVIPC::ConnectionType connectionType = + NVIPC::CT_CLIENT_OR_SERVER, // how to establish the connection + const char *mappingObject = "Global\\FastIPC", // Name of communications channel + QT3DSU32 serverRingBufferSize = 1024 * 4, // buffer size for outgoing server messages. + QT3DSU32 clientRingBufferSize = 1024 * 4, // buffer size for incoming client messages. + bool allowLongMessages = true, + bool bufferSends = false, + QT3DSU32 maxBufferSendSize = 1024 * 8); // True if we want to buffer sends, if true, then + // pumpSendBuffers should be called by the send thread. + + void releaseFastIPC(NVIPC *f); + +}; // end of namespace +}; // end of namespace + +#endif diff --git a/src/foundation/Qt3DSFlags.h b/src/foundation/Qt3DSFlags.h new file mode 100644 index 0000000..765f386 --- /dev/null +++ b/src/foundation/Qt3DSFlags.h @@ -0,0 +1,360 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_FLAGS_H +#define QT3DS_FOUNDATION_QT3DS_FLAGS_H + +/** \addtogroup foundation + @{ +*/ + +#include "foundation/Qt3DS.h" + +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif +/** +\brief Container for bitfield flag variables associated with a specific enum type. + +This allows for type safe manipulation for bitfields. + +<h3>Example</h3> + // enum that defines each bit... + struct MyEnum + { + enum Enum + { + eMAN = 1, + eBEAR = 2, + ePIG = 4, + }; + }; + + // implements some convenient global operators. + QT3DS_FLAGS_OPERATORS(MyEnum::Enum, QT3DSU8); + + NVFlags<MyEnum::Enum, QT3DSU8> myFlags; + myFlags |= MyEnum::eMAN; + myFlags |= MyEnum::eBEAR | MyEnum::ePIG; + if(myFlags & MyEnum::eBEAR) + { + doSomething(); + } +*/ +template <typename enumtype, typename storagetype = QT3DSU32> +class NVFlags +{ +public: + QT3DS_INLINE explicit NVFlags(const NVEmpty &) {} + QT3DS_INLINE NVFlags(void); + QT3DS_INLINE NVFlags(enumtype e); + QT3DS_INLINE NVFlags(const NVFlags<enumtype, storagetype> &f); + QT3DS_INLINE explicit NVFlags(storagetype b); + + QT3DS_INLINE bool operator==(enumtype e) const; + QT3DS_INLINE bool operator==(const NVFlags<enumtype, storagetype> &f) const; + QT3DS_INLINE bool operator==(bool b) const; + QT3DS_INLINE bool operator!=(enumtype e) const; + QT3DS_INLINE bool operator!=(const NVFlags<enumtype, storagetype> &f) const; + + QT3DS_INLINE NVFlags<enumtype, storagetype> &operator=(enumtype e); + + QT3DS_INLINE NVFlags<enumtype, storagetype> &operator|=(enumtype e); + QT3DS_INLINE NVFlags<enumtype, storagetype> &operator|=(const NVFlags<enumtype, storagetype> &f); + QT3DS_INLINE NVFlags<enumtype, storagetype> operator|(enumtype e) const; + QT3DS_INLINE NVFlags<enumtype, storagetype> + operator|(const NVFlags<enumtype, storagetype> &f) const; + + QT3DS_INLINE NVFlags<enumtype, storagetype> &operator&=(enumtype e); + QT3DS_INLINE NVFlags<enumtype, storagetype> &operator&=(const NVFlags<enumtype, storagetype> &f); + QT3DS_INLINE NVFlags<enumtype, storagetype> operator&(enumtype e) const; + QT3DS_INLINE NVFlags<enumtype, storagetype> + operator&(const NVFlags<enumtype, storagetype> &f) const; + + QT3DS_INLINE NVFlags<enumtype, storagetype> &operator^=(enumtype e); + QT3DS_INLINE NVFlags<enumtype, storagetype> &operator^=(const NVFlags<enumtype, storagetype> &f); + QT3DS_INLINE NVFlags<enumtype, storagetype> operator^(enumtype e) const; + QT3DS_INLINE NVFlags<enumtype, storagetype> + operator^(const NVFlags<enumtype, storagetype> &f) const; + + QT3DS_INLINE NVFlags<enumtype, storagetype> operator~(void)const; + + QT3DS_INLINE operator bool(void) const; + QT3DS_INLINE operator QT3DSU8(void) const; + QT3DS_INLINE operator QT3DSU16(void) const; + QT3DS_INLINE operator QT3DSU32(void) const; + + QT3DS_INLINE void clear(enumtype e); + + QT3DS_INLINE void clearOrSet(bool value, enumtype enumVal); + +public: + friend QT3DS_INLINE NVFlags<enumtype, storagetype> operator&(enumtype a, + NVFlags<enumtype, storagetype> &b) + { + NVFlags<enumtype, storagetype> out; + out.mBits = a & b.mBits; + return out; + } + +private: + storagetype mBits; +}; + +#define QT3DS_FLAGS_OPERATORS(enumtype, storagetype) \ + QT3DS_INLINE NVFlags<enumtype, storagetype> operator|(enumtype a, enumtype b) \ +{ \ + NVFlags<enumtype, storagetype> r(a); \ + r |= b; \ + return r; \ +} \ + QT3DS_INLINE NVFlags<enumtype, storagetype> operator&(enumtype a, enumtype b) \ +{ \ + NVFlags<enumtype, storagetype> r(a); \ + r &= b; \ + return r; \ +} \ + QT3DS_INLINE NVFlags<enumtype, storagetype> operator~(enumtype a) \ +{ \ + return ~NVFlags<enumtype, storagetype>(a); \ +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype>::NVFlags(void) +{ + mBits = 0; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype>::NVFlags(enumtype e) +{ + mBits = static_cast<storagetype>(e); +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype>::NVFlags(const NVFlags<enumtype, storagetype> &f) +{ + mBits = f.mBits; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype>::NVFlags(storagetype b) +{ + mBits = b; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE bool NVFlags<enumtype, storagetype>::operator==(enumtype e) const +{ + return mBits == static_cast<storagetype>(e); +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE bool NVFlags<enumtype, storagetype>:: +operator==(const NVFlags<enumtype, storagetype> &f) const +{ + return mBits == f.mBits; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE bool NVFlags<enumtype, storagetype>::operator==(bool b) const +{ + return ((bool)*this) == b; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE bool NVFlags<enumtype, storagetype>::operator!=(enumtype e) const +{ + return mBits != static_cast<storagetype>(e); +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE bool NVFlags<enumtype, storagetype>:: +operator!=(const NVFlags<enumtype, storagetype> &f) const +{ + return mBits != f.mBits; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype> &NVFlags<enumtype, storagetype>::operator=(enumtype e) +{ + mBits = static_cast<storagetype>(e); + return *this; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype> &NVFlags<enumtype, storagetype>::operator|=(enumtype e) +{ + mBits |= static_cast<storagetype>(e); + return *this; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype> &NVFlags<enumtype, storagetype>:: +operator|=(const NVFlags<enumtype, storagetype> &f) +{ + mBits |= f.mBits; + return *this; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype> NVFlags<enumtype, storagetype>::operator|(enumtype e) const +{ + NVFlags<enumtype, storagetype> out(*this); + out |= e; + return out; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype> NVFlags<enumtype, storagetype>:: +operator|(const NVFlags<enumtype, storagetype> &f) const +{ + NVFlags<enumtype, storagetype> out(*this); + out |= f; + return out; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype> &NVFlags<enumtype, storagetype>::operator&=(enumtype e) +{ + mBits &= static_cast<storagetype>(e); + return *this; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype> &NVFlags<enumtype, storagetype>:: +operator&=(const NVFlags<enumtype, storagetype> &f) +{ + mBits &= f.mBits; + return *this; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype> NVFlags<enumtype, storagetype>::operator&(enumtype e) const +{ + NVFlags<enumtype, storagetype> out = *this; + out.mBits &= static_cast<storagetype>(e); + return out; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype> NVFlags<enumtype, storagetype>:: +operator&(const NVFlags<enumtype, storagetype> &f) const +{ + NVFlags<enumtype, storagetype> out = *this; + out.mBits &= f.mBits; + return out; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype> &NVFlags<enumtype, storagetype>::operator^=(enumtype e) +{ + mBits ^= static_cast<storagetype>(e); + return *this; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype> &NVFlags<enumtype, storagetype>:: +operator^=(const NVFlags<enumtype, storagetype> &f) +{ + mBits ^= f.mBits; + return *this; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype> NVFlags<enumtype, storagetype>::operator^(enumtype e) const +{ + NVFlags<enumtype, storagetype> out = *this; + out.mBits ^= static_cast<storagetype>(e); + return out; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype> NVFlags<enumtype, storagetype>:: +operator^(const NVFlags<enumtype, storagetype> &f) const +{ + NVFlags<enumtype, storagetype> out = *this; + out.mBits ^= f.mBits; + return out; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype> NVFlags<enumtype, storagetype>::operator~(void)const +{ + NVFlags<enumtype, storagetype> out; + out.mBits = ~mBits; + return out; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype>::operator bool(void) const +{ + return mBits ? true : false; +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype>::operator QT3DSU8(void) const +{ + return static_cast<QT3DSU8>(mBits); +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype>::operator QT3DSU16(void) const +{ + return static_cast<QT3DSU16>(mBits); +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE NVFlags<enumtype, storagetype>::operator QT3DSU32(void) const +{ + return static_cast<QT3DSU32>(mBits); +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE void NVFlags<enumtype, storagetype>::clear(enumtype e) +{ + mBits &= ~static_cast<storagetype>(e); +} + +template <typename enumtype, typename storagetype> +QT3DS_INLINE void NVFlags<enumtype, storagetype>::clearOrSet(bool value, enumtype enumVal) +{ + if (value) + this->operator|=(enumVal); + else + clear(enumVal); +} + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** @} */ +#endif // #ifndef QT3DS_FOUNDATION_QT3DS_FLAGS_H diff --git a/src/foundation/Qt3DSFoundation.cpp b/src/foundation/Qt3DSFoundation.cpp new file mode 100644 index 0000000..8687cd6 --- /dev/null +++ b/src/foundation/Qt3DSFoundation.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DSFoundation.h" + +#include "foundation/Qt3DSQuat.h" +#include "foundation/Qt3DSThread.h" +#include "foundation/Qt3DSUtilities.h" +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSLogging.h" +#include "foundation/Qt3DSMutex.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "EASTL/hash_map.h" + +#include <stdio.h> +#ifdef _WIN32 +#pragma warning(disable : 4996) // intentionally suppressing this warning message +#endif +namespace qt3ds { +namespace foundation { + using namespace intrinsics; + union TempAllocatorChunk; + + class NVAllocatorListenerManager; + + class QT3DS_FOUNDATION_API Foundation : public NVFoundation + { + + Foundation(NVAllocatorCallback &alloc); + ~Foundation(); + + public: + void addRef() override; + void release() override; + + // factory + static Foundation *createInstance(QT3DSU32 version, NVAllocatorCallback &alloc); + + NVBroadcastingAllocator &getAllocator() const override { return mAllocator; } + NVAllocatorCallback &getAllocatorCallback() const override; + NVAllocatorCallback &getCheckedAllocator() { return mAllocator; } + + private: + class AlignCheckAllocator : public NVBroadcastingAllocator + { + static const QT3DSU32 MaxListenerCount = 5; + + public: + AlignCheckAllocator(NVAllocatorCallback &originalAllocator) + : mAllocator(originalAllocator) + , mListenerCount(0) + { + } + + void deallocate(void *ptr) override + { + // So here, for performance reasons I don't grab the mutex. + // The listener array is very rarely changing; for most situations + // only at startup. So it is unlikely that using the mutex + // will help a lot but it could have serious perf implications. + QT3DSU32 theCount = mListenerCount; + for (QT3DSU32 idx = 0; idx < theCount; ++idx) + mListeners[idx]->onDeallocation(ptr); + mAllocator.deallocate(ptr); + } + void *allocate(size_t size, const char *typeName, const char *filename, int line, + int flags) override; + void *allocate(size_t size, const char *typeName, const char *filename, int line, + size_t alignment, size_t alignmentOffset) override; + NVAllocatorCallback &getBaseAllocator() const { return mAllocator; } + void registerAllocationListener(NVAllocationListener &inListener) override + { + QT3DS_ASSERT(mListenerCount < MaxListenerCount); + if (mListenerCount < MaxListenerCount) { + mListeners[mListenerCount] = &inListener; + ++mListenerCount; + } + } + void deregisterAllocationListener(NVAllocationListener &inListener) override + { + for (QT3DSU32 idx = 0; idx < mListenerCount; ++idx) { + if (mListeners[idx] == &inListener) { + mListeners[idx] = mListeners[mListenerCount - 1]; + --mListenerCount; + break; + } + } + } + + private: + NVAllocatorCallback &mAllocator; + // I am not sure about using a NVArray here. + // For now, this is fine. + NVAllocationListener *mListeners[MaxListenerCount]; + volatile QT3DSU32 mListenerCount; + }; + + mutable AlignCheckAllocator mAllocator; + QT3DSU32 mRefCount; + Mutex mRefCountMutex; + }; + + Foundation::Foundation(NVAllocatorCallback &alloc) + : mAllocator(alloc) + , mRefCount(0) + , mRefCountMutex(alloc) + + { + } + + Foundation::~Foundation() {} + + NVAllocatorCallback &Foundation::getAllocatorCallback() const + { + return mAllocator.getBaseAllocator(); + } + + Foundation *Foundation::createInstance(QT3DSU32 version, NVAllocatorCallback &alloc) + { + if (version != QT3DS_FOUNDATION_VERSION) { + qCCritical(INVALID_PARAMETER, "Wrong version: foundation version is %d, tried to create %d", + QT3DS_FOUNDATION_VERSION, version); + return 0; + } + Foundation *mInstance = NULL; + + if (!mInstance) { + // if we don't assign this here, the Foundation object can't create member + // subobjects which require the allocator + + mInstance = reinterpret_cast<Foundation *>( + alloc.allocate(sizeof(Foundation), "Foundation", __FILE__, __LINE__)); + + if (mInstance) { + QT3DS_PLACEMENT_NEW(mInstance, Foundation)(alloc); + + QT3DS_ASSERT(mInstance->mRefCount == 0); + + return mInstance; + } else { + qCCritical(INTERNAL_ERROR, "Memory allocation for foundation object failed."); + } + } else { + qCCritical( + INVALID_OPERATION, + "Foundation object exists already. Only one instance per process can be created."); + } + + return 0; + } + + void Foundation::addRef() + { + mRefCountMutex.lock(); + ++mRefCount; + mRefCountMutex.unlock(); + } + + void Foundation::release() + { + mRefCountMutex.lock(); + if (mRefCount) + --mRefCount; + QT3DSU32 refCount = mRefCount; + mRefCountMutex.unlock(); + if (!refCount) { + NVAllocatorCallback &alloc = mAllocator.getBaseAllocator(); + this->~Foundation(); + alloc.deallocate(this); + } + } + + void *Foundation::AlignCheckAllocator::allocate(size_t size, const char *typeName, + const char *filename, int line, int) + { + void *addr = mAllocator.allocate(size, typeName, filename, line); + + if (!addr) + qFatal("User allocator returned NULL."); + + if (!(reinterpret_cast<size_t>(addr) & 15)) { + // Same comment as before in the allocation system. + // We don't lock the listener array mutex because of an assumption + // where the listener array is rarely changing. + QT3DSU32 theCount = mListenerCount; + for (QT3DSU32 idx = 0; idx < theCount; ++idx) + mListeners[idx]->onAllocation(size, typeName, filename, line, addr); + return addr; + } + + qFatal("Allocations for qt3ds::foundation must be 16-byte aligned."); + return 0; + } + + void *Foundation::AlignCheckAllocator::allocate(size_t size, const char *typeName, + const char *filename, int line, + size_t /*alignment*/, + size_t /*alignmentOffset*/) + { + return allocate(size, typeName, filename, line, 0); + } + +} // namespace foundation +} // namespace qt3ds + +qt3ds::NVFoundation *NVCreateFoundation(qt3ds::QT3DSU32 version, qt3ds::NVAllocatorCallback &allocator) +{ + return qt3ds::foundation::Foundation::createInstance(version, allocator); +} diff --git a/src/foundation/Qt3DSFoundation.h b/src/foundation/Qt3DSFoundation.h new file mode 100644 index 0000000..19065d2 --- /dev/null +++ b/src/foundation/Qt3DSFoundation.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_FOUNDATION_H +#define QT3DS_FOUNDATION_QT3DS_FOUNDATION_H + +/** \addtogroup foundation + @{ +*/ + +#include <stdarg.h> +#include "foundation/Qt3DSAllocator.h" +#include "foundation/Qt3DSVersionNumber.h" +#include "foundation/Qt3DSLogging.h" + +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif + +class NVAllocatorCallback; +class NVProfilingZone; +class NVBroadcastingAllocator; + +class NVFoundationBase +{ +public: + /** + retrieves the current allocator. + */ + virtual NVBroadcastingAllocator &getAllocator() const = 0; +}; + +namespace foundation { +template <typename TObjType> +inline void NVDelete(NVFoundationBase &alloc, TObjType *item) +{ + NVDelete(alloc.getAllocator(), item); +} +} + +/** +\brief Foundation SDK singleton class. + +You need to have an instance of this class to instance the higher level SDKs. +*/ +class QT3DS_FOUNDATION_API NVFoundation : public NVFoundationBase +{ +public: + virtual void addRef() = 0; + /** + \brief Destroys the instance it is called on. + + The operation will fail, if there are still modules referencing the foundation object. Release + all dependent modules prior + to calling this method. + + @see NVCreateFoundation() + */ + virtual void release() = 0; + + /** + Retrieves the allocator this object was created with. + */ + virtual NVAllocatorCallback &getAllocatorCallback() const = 0; + +protected: + virtual ~NVFoundation() {} +}; + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** +\brief Creates an instance of the foundation class + +\param version Version number we are expecting (should be QT3DS_FOUNDATION_VERSION) +\param allocator User supplied interface for allocating memory(see #NVAllocatorCallback) +\return Foundation instance on success, NULL if operation failed + +@see NVFoundation +*/ + +#ifdef QT3DS_FOUNDATION_NO_EXPORTS +QT3DS_AUTOTEST_EXPORT +#else +QT3DS_FOUNDATION_API +#endif +qt3ds::NVFoundation *QT3DS_CALL_CONV NVCreateFoundation( + qt3ds::QT3DSU32 version, qt3ds::NVAllocatorCallback &allocator); + +/** @} */ +#endif // QT3DS_FOUNDATION_QT3DS_FOUNDATION_H diff --git a/src/foundation/Qt3DSIPC.h b/src/foundation/Qt3DSIPC.h new file mode 100644 index 0000000..9bcea1a --- /dev/null +++ b/src/foundation/Qt3DSIPC.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_IPC_H +#define QT3DS_IPC_H + +#include "foundation/Qt3DS.h" + +namespace qt3ds { +namespace foundation { + + class NVIPC + { + public: + enum ConnectionType { + CT_CLIENT, // start up as a client, will succeed even if the server has not yet been + // found. + CT_CLIENT_REQUIRE_SERVER, // start up as a client, but only if the server already + // exists. + CT_SERVER, // will start up as a server, will fail if an existing server is already + // open. + CT_CLIENT_OR_SERVER, // connect as either a client or server, don't care who is created + // first. + CT_LAST + }; + + enum ErrorCode { + EC_OK, // no error. + EC_FAIL, // generic failure. + EC_SERVER_ALREADY_EXISTS, // couldn't create a server, because the server already + // exists. + EC_CLIENT_ALREADY_EXISTS, // couldn't create a client, because an existing client is + // already registered. + EC_CLIENT_SERVER_ALREADY_EXISTS, // both the client and server channels are already used + EC_SERVER_NOT_FOUND, // client opened with a required server, which was not found. + EC_BUFFER_MISSMATCH, // the reserved buffers for client/server do not match up. + EC_MAPFILE_CREATE, // failed to create the shared memory map file. + EC_MAPFILE_VIEW, // failed to map the memory view of he + // communications errors. + EC_SEND_DATA_EXCEEDS_MAX_BUFFER, // trying to send more data than can even fit in the + // sednd buffe. + EC_SEND_DATA_TOO_LARGE, // the data we tried to send exceeds the available room int the + // output ring buffer. + EC_SEND_BUFFER_FULL, // the send buffer is completely full. + EC_SEND_FROM_WRONG_THREAD, // Tried to do a send from a different thread + EC_RECEIVE_FROM_WRONG_THREAD, // Tried to do a recieve from a different thread + EC_NO_RECEIVE_PENDING, // tried to acknowledge a receive but none was pending. + }; + + virtual bool pumpPendingSends(void) = 0; // give up a time slice to pending sends; returns + // true if there are still pends sending. + virtual ErrorCode sendData(const void *data, QT3DSU32 data_len, bool bufferIfFull) = 0; + virtual const void *receiveData(QT3DSU32 &data_len) = 0; + virtual ErrorCode receiveAcknowledge(void) = 0; // acknowledge that we have processed the + // incoming message and can advance the read + // buffer. + + virtual bool isServer(void) const = 0; // returns true if we are opened as a server. + + virtual bool haveConnection(void) const = 0; + + virtual bool canSend(QT3DSU32 len) = 0; // return true if we can send a message of this size. + + virtual void release(void) = 0; + + protected: + virtual ~NVIPC(void){} + }; +}; // end of namespace +}; // end of namespace + +#endif diff --git a/src/foundation/Qt3DSIndexableLinkedList.h b/src/foundation/Qt3DSIndexableLinkedList.h new file mode 100644 index 0000000..ff36b42 --- /dev/null +++ b/src/foundation/Qt3DSIndexableLinkedList.h @@ -0,0 +1,309 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_INDEXABLE_LINKED_LIST_H +#define QT3DS_FOUNDATION_INDEXABLE_LINKED_LIST_H +#include "foundation/Qt3DSPool.h" + +namespace qt3ds { +namespace foundation { + // Set of helper functions for manipulation of lists that are + // somewhat indexable but also amenable to using a pool structure. + template <typename TNodeType, typename TObjType, QT3DSU32 TObjCount> + struct IndexableLinkedList + { + typedef TNodeType SNode; + + typedef Pool<SNode, ForwardingAllocator> TPoolType; + + static TObjType &Create(SNode *&inInitialNode, QT3DSU32 &ioCurrentCount, TPoolType &ioPool) + { + QT3DSU32 idx = ioCurrentCount; + ++ioCurrentCount; + QT3DSU32 numGroups = (ioCurrentCount + TObjCount - 1) / TObjCount; + QT3DSU32 localIdx = idx % TObjCount; + + SNode *theCurrentNode = inInitialNode; + for (QT3DSU32 idx = 0, end = numGroups; idx < end; ++idx) { + if (idx == 0) { + if (theCurrentNode == NULL) + inInitialNode = ioPool.construct(__FILE__, __LINE__); + + theCurrentNode = inInitialNode; + } else { + if (theCurrentNode->m_NextNode == NULL) + theCurrentNode->m_NextNode = ioPool.construct(__FILE__, __LINE__); + theCurrentNode = theCurrentNode->m_NextNode; + } + } + return theCurrentNode->m_Data[localIdx]; + } + + static void CreateAll(SNode *&inInitialNode, QT3DSU32 inCount, TPoolType &ioPool) + { + QT3DSU32 numGroups = (inCount + TObjCount - 1) / TObjCount; + SNode *lastNode = NULL; + for (QT3DSU32 idx = 0, end = numGroups; idx < end; ++idx) { + SNode *nextNode = ioPool.construct(__FILE__, __LINE__); + if (idx == 0) + inInitialNode = nextNode; + else + lastNode->m_NextNode = nextNode; + lastNode = nextNode; + } + } + + static void DeleteList(SNode *&inInitialNode, TPoolType &ioPool) + { + SNode *theNode = inInitialNode; + inInitialNode = NULL; + while (theNode) { + SNode *theNext = theNode->m_NextNode; + theNode->m_NextNode = NULL; + ioPool.deallocate(theNode); + theNode = theNext; + } + } + + // Performs no checking, so you need to have already allocated what you need. + static const TObjType &GetObjAtIdx(const SNode *inInitialNode, QT3DSU32 inIdx) + { + QT3DSU32 groupIdx = inIdx / TObjCount; + QT3DSU32 localIdx = inIdx % TObjCount; + for (QT3DSU32 idx = 0; idx < groupIdx; ++idx) + inInitialNode = inInitialNode->m_NextNode; + return inInitialNode->m_Data[inIdx]; + } + + static TObjType &GetObjAtIdx(SNode *inInitialNode, QT3DSU32 inIdx) + { + QT3DSU32 groupIdx = inIdx / TObjCount; + QT3DSU32 localIdx = inIdx % TObjCount; + for (QT3DSU32 idx = 0; idx < groupIdx; ++idx) + inInitialNode = inInitialNode->m_NextNode; + return inInitialNode->m_Data[localIdx]; + } + + struct SIteratorType + { + QT3DSU32 m_Idx; + QT3DSU32 m_Count; + SNode *m_Node; + + SIteratorType() + : m_Idx(0) + , m_Count(0) + , m_Node(NULL) + { + } + SIteratorType(const SIteratorType &iter) + : m_Idx(iter.m_Idx) + , m_Count(iter.m_Count) + , m_Node(iter.m_Node) + { + } + SIteratorType(SNode *inNode, QT3DSU32 inCount, QT3DSU32 inIdx) + : m_Idx(inIdx) + , m_Count(inCount) + , m_Node(inNode) + { + } + + bool operator==(const SIteratorType &iter) const { return m_Idx == iter.m_Idx; } + + bool operator!=(const SIteratorType &iter) const { return m_Idx != iter.m_Idx; } + + TObjType &operator*() + { + QT3DSU32 localIdx = m_Idx % TObjCount; + return m_Node->m_Data[localIdx]; + } + + SIteratorType &operator++() + { + ++m_Idx; + if ((m_Idx % TObjCount) == 0) + m_Node = m_Node->m_NextNode; + + return *this; + } + }; + + struct SConstIteratorType + { + QT3DSU32 m_Idx; + QT3DSU32 m_Count; + const SNode *m_Node; + + SConstIteratorType() + : m_Idx(0) + , m_Count(0) + , m_Node(NULL) + { + } + SConstIteratorType(const SIteratorType &iter) + : m_Idx(iter.m_Idx) + , m_Count(iter.m_Count) + , m_Node(iter.m_Node) + { + } + SConstIteratorType(const SConstIteratorType &iter) + : m_Idx(iter.m_Idx) + , m_Count(iter.m_Count) + , m_Node(iter.m_Node) + { + } + SConstIteratorType(const SNode *inNode, QT3DSU32 inCount, QT3DSU32 inIdx) + : m_Idx(inIdx) + , m_Count(inCount) + , m_Node(inNode) + { + } + + bool operator==(const SConstIteratorType &iter) const { return m_Idx == iter.m_Idx; } + + bool operator!=(const SConstIteratorType &iter) const { return m_Idx != iter.m_Idx; } + + const TObjType &operator*() + { + QT3DSU32 localIdx = m_Idx % TObjCount; + return m_Node->m_Data[localIdx]; + } + + SConstIteratorType &operator++() + { + ++m_Idx; + if ((m_Idx % TObjCount) == 0) + m_Node = m_Node->m_NextNode; + + return *this; + } + }; + + typedef SIteratorType iterator; + typedef SConstIteratorType const_iterator; + + static iterator begin(SNode *inInitialNode, QT3DSU32 inItemCount) + { + return SIteratorType(inInitialNode, inItemCount, 0); + } + + static iterator end(SNode * /*inInitialNode*/, QT3DSU32 inItemCount) + { + return SIteratorType(NULL, inItemCount, inItemCount); + } + + static const_iterator begin(const SNode *inInitialNode, QT3DSU32 inItemCount) + { + return SConstIteratorType(inInitialNode, inItemCount, 0); + } + + static const_iterator end(const SNode * /*inInitialNode*/, QT3DSU32 inItemCount) + { + return SConstIteratorType(NULL, inItemCount, inItemCount); + } + + struct SNodeIteratorType + { + SNode *m_Node; + + SNodeIteratorType() + : m_Node(NULL) + { + } + SNodeIteratorType(const SNodeIteratorType &iter) + : m_Node(iter.m_Node) + { + } + SNodeIteratorType(SNode *inNode) + : m_Node(inNode) + { + } + + bool operator==(const SNodeIteratorType &iter) const { return m_Node == iter.m_Node; } + + bool operator!=(const SNodeIteratorType &iter) const { return m_Node != iter.m_Node; } + + TNodeType &operator*() { return *m_Node; } + + SNodeIteratorType &operator++() + { + if (m_Node) + m_Node = m_Node->m_NextNode; + + return *this; + } + }; + + typedef SNodeIteratorType node_iterator; + static node_iterator node_begin(SNode *inFirstNode) { return node_iterator(inFirstNode); } + static node_iterator node_end(SNode * /*inFirstNode*/) { return node_iterator(NULL); } + + // Iterates through the number of nodes required for a given count of objects + struct SCountIteratorType + { + QT3DSU32 m_Idx; + QT3DSU32 m_Count; + + SCountIteratorType() + : m_Idx(0) + , m_Count(0) + { + } + SCountIteratorType(const SCountIteratorType &iter) + : m_Idx(iter.m_Idx) + , m_Count(iter.m_Count) + { + } + SCountIteratorType(QT3DSU32 idx, QT3DSU32 count) + : m_Idx(idx) + , m_Count(count) + { + } + + bool operator==(const SCountIteratorType &iter) const { return m_Idx == iter.m_Idx; } + + bool operator!=(const SCountIteratorType &iter) const { return m_Idx != iter.m_Idx; } + + SCountIteratorType &operator++() + { + m_Idx = NVMin(m_Idx + TObjCount, m_Count); + return *this; + } + }; + + typedef SCountIteratorType count_iterator; + static count_iterator count_begin(QT3DSU32 count) { return SCountIteratorType(0, count); } + static count_iterator count_end(QT3DSU32 count) { return SCountIteratorType(count, count); } + }; +} +} + +#endif
\ No newline at end of file diff --git a/src/foundation/Qt3DSIntrinsics.h b/src/foundation/Qt3DSIntrinsics.h new file mode 100644 index 0000000..1d6ac7a --- /dev/null +++ b/src/foundation/Qt3DSIntrinsics.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_INTRINSICS_H +#define QT3DS_FOUNDATION_QT3DS_INTRINSICS_H + +#include "foundation/Qt3DSPreprocessor.h" + +#if defined QT3DS_WINDOWS || defined QT3DS_WIN8ARM +#include "windows/Qt3DSWindowsIntrinsics.h" +#elif defined QT3DS_X360 +#include "xbox360/NVXbox360Intrinsics.h" +#elif (defined QT3DS_LINUX || defined QT3DS_ANDROID || defined QT3DS_APPLE || defined QT3DS_QNX) +#include "linux/Qt3DSLinuxIntrinsics.h" +#elif defined QT3DS_PS3 +#include "ps3/NVPS3Intrinsics.h" +#elif defined QT3DS_PSP2 +#include "psp2/NVPSP2Intrinsics.h" +#else +#error "Platform not supported!" +#endif + +#endif // QT3DS_FOUNDATION_QT3DS_INTRINSICS_H diff --git a/src/foundation/Qt3DSInvasiveLinkedList.h b/src/foundation/Qt3DSInvasiveLinkedList.h new file mode 100644 index 0000000..da90e12 --- /dev/null +++ b/src/foundation/Qt3DSInvasiveLinkedList.h @@ -0,0 +1,361 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_INVASIVE_LINKED_LIST_H +#define QT3DS_FOUNDATION_INVASIVE_LINKED_LIST_H +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSAllocator.h" +#include "foundation/Qt3DSAllocatorCallback.h" +#include "foundation/Qt3DSContainers.h" + +namespace qt3ds { +namespace foundation { + + // Base linked list without an included head or tail member. + template <typename TObjType, typename TObjHeadOp, typename TObjTailOp> + struct SInvasiveLinkListBase + { + TObjType *tail(TObjType *inObj) + { + if (inObj) + return TObjTailOp().get(inObj); + return NULL; + } + + TObjType *head(TObjType *inObj) + { + if (inObj) + return TObjHeadOp().get(inObj); + return NULL; + } + + const TObjType *tail(const TObjType *inObj) + { + if (inObj) + return TObjTailOp().get(inObj); + return NULL; + } + + const TObjType *head(const TObjType *inObj) + { + if (inObj) + return TObjHeadOp().get(inObj); + return NULL; + } + + void remove(TObjType &inObj) + { + TObjHeadOp theHeadOp; + TObjTailOp theTailOp; + TObjType *theHead = theHeadOp.get(inObj); + TObjType *theTail = theTailOp.get(inObj); + if (theHead) + theTailOp.set(*theHead, theTail); + if (theTail) + theHeadOp.set(*theTail, theHead); + theHeadOp.set(inObj, NULL); + theTailOp.set(inObj, NULL); + } + + void insert_after(TObjType &inPosition, TObjType &inObj) + { + TObjTailOp theTailOp; + TObjType *theHead = &inPosition; + TObjType *theTail = theTailOp.get(inPosition); + insert(theHead, theTail, inObj); + } + + void insert_before(TObjType &inPosition, TObjType &inObj) + { + TObjHeadOp theHeadOp; + TObjType *theHead = theHeadOp.get(inPosition); + TObjType *theTail = &inPosition; + insert(theHead, theTail, inObj); + } + + void insert(TObjType *inHead, TObjType *inTail, TObjType &inObj) + { + TObjHeadOp theHeadOp; + TObjTailOp theTailOp; + if (inHead) + theTailOp.set(*inHead, &inObj); + if (inTail) + theHeadOp.set(*inTail, &inObj); + theHeadOp.set(inObj, inHead); + theTailOp.set(inObj, inTail); + } + }; + + template <typename TObjType, typename TObjTailOp> + struct SLinkedListIterator + { + typedef SLinkedListIterator<TObjType, TObjTailOp> TMyType; + TObjType *m_Obj; + SLinkedListIterator(TObjType *inObj = NULL) + : m_Obj(inObj) + { + } + + bool operator!=(const TMyType &inIter) const { return m_Obj != inIter.m_Obj; } + bool operator==(const TMyType &inIter) const { return m_Obj == inIter.m_Obj; } + + TMyType &operator++() + { + if (m_Obj) + m_Obj = TObjTailOp().get(*m_Obj); + return *this; + } + + TMyType &operator++(int) + { + TMyType retval(*this); + ++(*this); + return retval; + } + + TObjType &operator*() { return *m_Obj; } + TObjType *operator->() { return m_Obj; } + }; + + // Used for singly linked list where + // items have either no head or tail ptr. + template <typename TObjType> + struct SNullOp + { + void set(TObjType &, TObjType *) {} + TObjType *get(const TObjType &) { return NULL; } + }; + + template <typename TObjType, typename TObjTailOp> + struct InvasiveSingleLinkedList + : public SInvasiveLinkListBase<TObjType, SNullOp<TObjType>, TObjTailOp> + { + typedef InvasiveSingleLinkedList<TObjType, TObjTailOp> TMyType; + typedef SInvasiveLinkListBase<TObjType, SNullOp<TObjType>, TObjTailOp> TBaseType; + typedef SLinkedListIterator<TObjType, TObjTailOp> iterator; + typedef iterator const_iterator; + TObjType *m_Head; + InvasiveSingleLinkedList() + : m_Head(NULL) + { + } + InvasiveSingleLinkedList(const TMyType &inOther) + : m_Head(inOther.m_Head) + { + } + TMyType &operator=(const TMyType &inOther) + { + m_Head = inOther.m_Head; + return *this; + } + + TObjType &front() const { return *m_Head; } + + void push_front(TObjType &inObj) + { + if (m_Head != NULL) + TBaseType::insert_before(*m_Head, inObj); + m_Head = &inObj; + } + + void push_back(TObjType &inObj) + { + if (m_Head == NULL) + m_Head = &inObj; + else { + TObjType *lastObj = NULL; + for (iterator iter = begin(), endIter = end(); iter != endIter; ++iter) + lastObj = &(*iter); + + QT3DS_ASSERT(lastObj); + if (lastObj) + TObjTailOp().set(*lastObj, &inObj); + } + } + + void remove(TObjType &inObj) + { + if (m_Head == &inObj) + m_Head = TObjTailOp().get(inObj); + TBaseType::remove(inObj); + } + + bool empty() const { return m_Head == NULL; } + + iterator begin() { return iterator(m_Head); } + iterator end() { return iterator(NULL); } + + const_iterator begin() const { return iterator(m_Head); } + const_iterator end() const { return iterator(NULL); } + }; + + template <typename TObjType, typename TObjHeadOp, typename TObjTailOp> + struct InvasiveLinkedList : public SInvasiveLinkListBase<TObjType, TObjHeadOp, TObjTailOp> + { + typedef InvasiveLinkedList<TObjType, TObjHeadOp, TObjTailOp> TMyType; + typedef SInvasiveLinkListBase<TObjType, TObjHeadOp, TObjTailOp> TBaseType; + typedef SLinkedListIterator<TObjType, TObjTailOp> iterator; + typedef iterator const_iterator; + typedef SLinkedListIterator<TObjType, TObjHeadOp> reverse_iterator; + typedef reverse_iterator const_reverse_iterator; + + TObjType *m_Head; + TObjType *m_Tail; + + InvasiveLinkedList() + : m_Head(NULL) + , m_Tail(NULL) + { + } + InvasiveLinkedList(const TMyType &inOther) + : m_Head(inOther.m_Head) + , m_Tail(inOther.m_Tail) + { + } + TMyType &operator=(const TMyType &inOther) + { + m_Head = inOther.m_Head; + m_Tail = inOther.m_Tail; + return *this; + } + + TObjType &front() const + { + QT3DS_ASSERT(m_Head); + return *m_Head; + } + TObjType &back() const + { + QT3DS_ASSERT(m_Tail); + return *m_Tail; + } + + TObjType *front_ptr() const { return m_Head; } + TObjType *back_ptr() const { return m_Tail; } + + void push_front(TObjType &inObj) + { + if (m_Head != NULL) + TBaseType::insert_before(*m_Head, inObj); + m_Head = &inObj; + + if (m_Tail == NULL) + m_Tail = &inObj; + } + + void push_back(TObjType &inObj) + { + if (m_Tail != NULL) + TBaseType::insert_after(*m_Tail, inObj); + m_Tail = &inObj; + + if (m_Head == NULL) + m_Head = &inObj; + } + + void remove(TObjType &inObj) + { + if (m_Head == &inObj) + m_Head = TObjTailOp().get(inObj); + if (m_Tail == &inObj) + m_Tail = TObjHeadOp().get(inObj); + + TBaseType::remove(inObj); + } + + bool empty() const { return m_Head == NULL; } + + iterator begin() { return iterator(m_Head); } + iterator end() { return iterator(NULL); } + + const_iterator begin() const { return iterator(m_Head); } + const_iterator end() const { return iterator(NULL); } + + reverse_iterator rbegin() { return reverse_iterator(m_Tail); } + reverse_iterator rend() { return reverse_iterator(NULL); } + + const_reverse_iterator rbegin() const { return reverse_iterator(m_Tail); } + const_reverse_iterator rend() const { return reverse_iterator(NULL); } + }; + +// Macros to speed up the definitely of invasive linked lists. +#define DEFINE_INVASIVE_SINGLE_LIST(type) \ + struct S##type; \ + struct S##type##NextOp \ + { \ + S##type *get(S##type &s); \ + const S##type *get(const S##type &s) const; \ + void set(S##type &inItem, S##type *inNext); \ + }; \ + typedef InvasiveSingleLinkedList<S##type, S##type##NextOp> T##type##List; + +#define DEFINE_INVASIVE_LIST(type) \ + struct S##type; \ + struct S##type##NextOp \ + { \ + S##type *get(S##type &s); \ + const S##type *get(const S##type &s) const; \ + void set(S##type &inItem, S##type *inNext); \ + }; \ + struct S##type##PreviousOp \ + { \ + S##type *get(S##type &s); \ + const S##type *get(const S##type &s) const; \ + void set(S##type &inItem, S##type *inNext); \ + }; \ + typedef InvasiveLinkedList<S##type, S##type##PreviousOp, S##type##NextOp> T##type##List; + +#define IMPLEMENT_INVASIVE_LIST(type, prevMember, nextMember) \ + inline S##type *S##type##NextOp::get(S##type &s) { return s.nextMember; } \ + inline const S##type *S##type##NextOp::get(const S##type &s) const { return s.nextMember; } \ + inline void S##type##NextOp::set(S##type &inItem, S##type *inNext) \ + { \ + inItem.nextMember = inNext; \ + } \ + inline S##type *S##type##PreviousOp::get(S##type &s) { return s.prevMember; } \ + inline const S##type *S##type##PreviousOp::get(const S##type &s) const \ + { \ + return s.prevMember; \ + } \ + inline void S##type##PreviousOp::set(S##type &inItem, S##type *inNext) \ + { \ + inItem.prevMember = inNext; \ + } + +#define IMPLEMENT_INVASIVE_SINGLE_LIST(type, nextMember) \ + inline S##type *S##type##NextOp::get(S##type &s) { return s.nextMember; } \ + inline const S##type *S##type##NextOp::get(const S##type &s) const { return s.nextMember; } \ + inline void S##type##NextOp::set(S##type &inItem, S##type *inNext) \ + { \ + inItem.nextMember = inNext; \ + } +} +} +#endif
\ No newline at end of file diff --git a/src/foundation/Qt3DSInvasiveSet.h b/src/foundation/Qt3DSInvasiveSet.h new file mode 100644 index 0000000..772a230 --- /dev/null +++ b/src/foundation/Qt3DSInvasiveSet.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_INVASIVE_SET_H +#define QT3DS_FOUNDATION_INVASIVE_SET_H +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSAllocator.h" +#include "foundation/Qt3DSAllocatorCallback.h" +#include "foundation/Qt3DSContainers.h" + +namespace qt3ds { +namespace foundation { + + template <typename TObjectType, typename TGetSetIndexOp, typename TSetSetIndexOp> + class InvasiveSet + { + nvvector<TObjectType *> mSet; + + InvasiveSet(const InvasiveSet &other); + InvasiveSet &operator=(const InvasiveSet &other); + + public: + InvasiveSet(NVAllocatorCallback &callback, const char *allocName) + : mSet(callback, allocName) + { + } + + bool insert(TObjectType &inObject) + { + QT3DSU32 currentIdx = TGetSetIndexOp()(inObject); + if (currentIdx == QT3DS_MAX_U32) { + TSetSetIndexOp()(inObject, mSet.size()); + mSet.push_back(&inObject); + return true; + } + return false; + } + + bool remove(TObjectType &inObject) + { + QT3DSU32 currentIdx = TGetSetIndexOp()(inObject); + if (currentIdx != QT3DS_MAX_U32) { + TObjectType *theEnd = mSet.back(); + TObjectType *theObj = &inObject; + if (theEnd != theObj) { + TSetSetIndexOp()(*theEnd, currentIdx); + mSet[currentIdx] = theEnd; + } + mSet.pop_back(); + TSetSetIndexOp()(inObject, QT3DS_MAX_U32); + return true; + } + return false; + } + + bool contains(TObjectType &inObject) { return TGetSetIndexOp()(inObject) != QT3DS_MAX_U32; } + + void clear() + { + for (QT3DSU32 idx = 0; idx < mSet.size(); ++idx) + TSetSetIndexOp()(*(mSet[idx]), QT3DS_MAX_U32); + mSet.clear(); + } + + TObjectType *operator[](QT3DSU32 idx) { return mSet[idx]; } + const TObjectType *operator[](QT3DSU32 idx) const { return mSet[idx]; } + QT3DSU32 size() const { return mSet.size(); } + TObjectType **begin() { return mSet.begin(); } + TObjectType **end() { return mSet.end(); } + const TObjectType **begin() const { return mSet.begin(); } + const TObjectType **end() const { return mSet.end(); } + const TObjectType *back() const { return mSet.back(); } + TObjectType *back() { return mSet.back(); } + }; +} +} +#endif
\ No newline at end of file diff --git a/src/foundation/Qt3DSLogging.cpp b/src/foundation/Qt3DSLogging.cpp new file mode 100644 index 0000000..257b39b --- /dev/null +++ b/src/foundation/Qt3DSLogging.cpp @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DSLogging.h" + +namespace qt3ds { + +Q_LOGGING_CATEGORY(GL_ERROR, "qt3ds.gl_error") +Q_LOGGING_CATEGORY(INVALID_PARAMETER, "qt3ds.invalid_parameter") +Q_LOGGING_CATEGORY(INVALID_OPERATION, "qt3ds.invalid_operation") +Q_LOGGING_CATEGORY(OUT_OF_MEMORY, "qt3ds.out_of_memory") +Q_LOGGING_CATEGORY(INTERNAL_ERROR, "qt3ds.internal_error") +Q_LOGGING_CATEGORY(PERF_WARNING, "qt3ds.perf_warning") +Q_LOGGING_CATEGORY(PERF_INFO, "qt3ds.perf_info") +Q_LOGGING_CATEGORY(TRACE_INFO, "qt3ds.trace_info") +Q_LOGGING_CATEGORY(WARNING, "qt3ds.warning") + +} // namespace qt3ds diff --git a/src/foundation/Qt3DSLogging.h b/src/foundation/Qt3DSLogging.h new file mode 100644 index 0000000..4fcb77b --- /dev/null +++ b/src/foundation/Qt3DSLogging.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_ERRORS_H +#define QT3DS_FOUNDATION_QT3DS_ERRORS_H + +#include <QtCore/qdebug.h> +#include <QtCore/qloggingcategory.h> + +namespace qt3ds { + +Q_DECLARE_LOGGING_CATEGORY(GL_ERROR) +Q_DECLARE_LOGGING_CATEGORY(INVALID_PARAMETER) +Q_DECLARE_LOGGING_CATEGORY(INVALID_OPERATION) +Q_DECLARE_LOGGING_CATEGORY(OUT_OF_MEMORY) +Q_DECLARE_LOGGING_CATEGORY(INTERNAL_ERROR) +Q_DECLARE_LOGGING_CATEGORY(PERF_WARNING) +Q_DECLARE_LOGGING_CATEGORY(PERF_INFO) +Q_DECLARE_LOGGING_CATEGORY(TRACE_INFO) +Q_DECLARE_LOGGING_CATEGORY(WARNING) + +} // namespace qt3ds + +#endif diff --git a/src/foundation/Qt3DSMat33.h b/src/foundation/Qt3DSMat33.h new file mode 100644 index 0000000..6fa69eb --- /dev/null +++ b/src/foundation/Qt3DSMat33.h @@ -0,0 +1,385 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_MAT33_H +#define QT3DS_FOUNDATION_QT3DS_MAT33_H +/** \addtogroup foundation +@{ +*/ + +#include "foundation/Qt3DSVec3.h" +#include "foundation/Qt3DSQuat.h" + +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif +/*! +\brief 3x3 matrix class + +Some clarifications, as there have been much confusion about matrix formats etc in the past. + +Short: +- Matrix have base vectors in columns (vectors are column matrices, 3x1 matrices). +- Matrix is physically stored in column major format +- Matrices are concaternated from left + +Long: +Given three base vectors a, b and c the matrix is stored as + +|a.x b.x c.x| +|a.y b.y c.y| +|a.z b.z c.z| + +Vectors are treated as columns, so the vector v is + +|x| +|y| +|z| + +And matrices are applied _before_ the vector (pre-multiplication) +v' = M*v + +|x'| |a.x b.x c.x| |x| |a.x*x + b.x*y + c.x*z| +|y'| = |a.y b.y c.y| * |y| = |a.y*x + b.y*y + c.y*z| +|z'| |a.z b.z c.z| |z| |a.z*x + b.z*y + c.z*z| + + +Physical storage and indexing: +To be compatible with popular 3d rendering APIs (read D3d and OpenGL) +the physical indexing is + +|0 3 6| +|1 4 7| +|2 5 8| + +index = column*3 + row + +which in C++ translates to M[column][row] + +The mathematical indexing is M_row,column and this is what is used for _-notation +so _12 is 1st row, second column and operator(row, column)! + +@see QT3DSMat44 + +*/ +class QT3DSMat33 +{ +public: + //! Default constructor + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat33() {} + + //! Construct from three base vectors + QT3DS_CUDA_CALLABLE QT3DSMat33(const QT3DSVec3 &col0, const QT3DSVec3 &col1, const QT3DSVec3 &col2) + : column0(col0) + , column1(col1) + , column2(col2) + { + } + + //! Construct from float[9] + QT3DS_CUDA_CALLABLE explicit QT3DS_INLINE QT3DSMat33(NVReal values[]) + : column0(values[0], values[1], values[2]) + , column1(values[3], values[4], values[5]) + , column2(values[6], values[7], values[8]) + { + } + + //! Construct from a quaternion + QT3DS_CUDA_CALLABLE explicit QT3DS_FORCE_INLINE QT3DSMat33(const QT3DSQuat &q) + { + const NVReal x = q.x; + const NVReal y = q.y; + const NVReal z = q.z; + const NVReal w = q.w; + + const NVReal x2 = x + x; + const NVReal y2 = y + y; + const NVReal z2 = z + z; + + const NVReal xx = x2 * x; + const NVReal yy = y2 * y; + const NVReal zz = z2 * z; + + const NVReal xy = x2 * y; + const NVReal xz = x2 * z; + const NVReal xw = x2 * w; + + const NVReal yz = y2 * z; + const NVReal yw = y2 * w; + const NVReal zw = z2 * w; + + column0 = QT3DSVec3(1.0f - yy - zz, xy + zw, xz - yw); + column1 = QT3DSVec3(xy - zw, 1.0f - xx - zz, yz + xw); + column2 = QT3DSVec3(xz + yw, yz - xw, 1.0f - xx - yy); + } + + //! Copy constructor + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat33(const QT3DSMat33 &other) + : column0(other.column0) + , column1(other.column1) + , column2(other.column2) + { + } + + //! Assignment operator + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSMat33 &operator=(const QT3DSMat33 &other) + { + column0 = other.column0; + column1 = other.column1; + column2 = other.column2; + return *this; + } + + //! Set to identity matrix + QT3DS_CUDA_CALLABLE QT3DS_INLINE static QT3DSMat33 createIdentity() + { + return QT3DSMat33(QT3DSVec3(1, 0, 0), QT3DSVec3(0, 1, 0), QT3DSVec3(0, 0, 1)); + } + + //! Set to zero matrix + QT3DS_CUDA_CALLABLE QT3DS_INLINE static QT3DSMat33 createZero() + { + return QT3DSMat33(QT3DSVec3(0.0f), QT3DSVec3(0.0f), QT3DSVec3(0.0f)); + } + + //! Construct from diagonal, off-diagonals are zero. + QT3DS_CUDA_CALLABLE QT3DS_INLINE static QT3DSMat33 createDiagonal(const QT3DSVec3 &d) + { + return QT3DSMat33(QT3DSVec3(d.x, 0.0f, 0.0f), QT3DSVec3(0.0f, d.y, 0.0f), QT3DSVec3(0.0f, 0.0f, d.z)); + } + + //! Get transposed matrix + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSMat33 getTranspose() const + { + const QT3DSVec3 v0(column0.x, column1.x, column2.x); + const QT3DSVec3 v1(column0.y, column1.y, column2.y); + const QT3DSVec3 v2(column0.z, column1.z, column2.z); + + return QT3DSMat33(v0, v1, v2); + } + + //! Get the real inverse + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat33 getInverse() const + { + const NVReal det = getDeterminant(); + QT3DSMat33 inverse; + + if (det != 0) { + const NVReal invDet = 1.0f / det; + + inverse.column0[0] = invDet * (column1[1] * column2[2] - column2[1] * column1[2]); + inverse.column0[1] = invDet * -(column0[1] * column2[2] - column2[1] * column0[2]); + inverse.column0[2] = invDet * (column0[1] * column1[2] - column0[2] * column1[1]); + + inverse.column1[0] = invDet * -(column1[0] * column2[2] - column1[2] * column2[0]); + inverse.column1[1] = invDet * (column0[0] * column2[2] - column0[2] * column2[0]); + inverse.column1[2] = invDet * -(column0[0] * column1[2] - column0[2] * column1[0]); + + inverse.column2[0] = invDet * (column1[0] * column2[1] - column1[1] * column2[0]); + inverse.column2[1] = invDet * -(column0[0] * column2[1] - column0[1] * column2[0]); + inverse.column2[2] = invDet * (column0[0] * column1[1] - column1[0] * column0[1]); + + return inverse; + } else { + return createIdentity(); + } + } + + //! Get determinant + QT3DS_CUDA_CALLABLE QT3DS_INLINE NVReal getDeterminant() const + { + return column0.dot(column1.cross(column2)); + } + + //! Unary minus + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat33 operator-() const + { + return QT3DSMat33(-column0, -column1, -column2); + } + + //! Add + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat33 operator+(const QT3DSMat33 &other) const + { + return QT3DSMat33(column0 + other.column0, column1 + other.column1, column2 + other.column2); + } + + //! Subtract + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat33 operator-(const QT3DSMat33 &other) const + { + return QT3DSMat33(column0 - other.column0, column1 - other.column1, column2 - other.column2); + } + + //! Scalar multiplication + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat33 operator*(NVReal scalar) const + { + return QT3DSMat33(column0 * scalar, column1 * scalar, column2 * scalar); + } + + friend QT3DSMat33 operator*(NVReal, const QT3DSMat33 &); + + //! Matrix vector multiplication (returns 'this->transform(vec)') + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec3 operator*(const QT3DSVec3 &vec) const { return transform(vec); } + + //! Matrix multiplication + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSMat33 operator*(const QT3DSMat33 &other) const + { + // Rows from this <dot> columns from other + // column0 = transform(other.column0) etc + return QT3DSMat33(transform(other.column0), transform(other.column1), + transform(other.column2)); + } + + // a <op>= b operators + + //! Equals-add + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat33 &operator+=(const QT3DSMat33 &other) + { + column0 += other.column0; + column1 += other.column1; + column2 += other.column2; + return *this; + } + + //! Equals-sub + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat33 &operator-=(const QT3DSMat33 &other) + { + column0 -= other.column0; + column1 -= other.column1; + column2 -= other.column2; + return *this; + } + + //! Equals scalar multiplication + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat33 &operator*=(NVReal scalar) + { + column0 *= scalar; + column1 *= scalar; + column2 *= scalar; + return *this; + } + + //! Element access, mathematical way! + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal operator()(unsigned int row, unsigned int col) const + { + return (*this)[col][row]; + } + + //! Element access, mathematical way! + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal &operator()(unsigned int row, unsigned int col) + { + return (*this)[col][row]; + } + + // Transform etc + + //! Transform vector by matrix, equal to v' = M*v + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 transform(const QT3DSVec3 &other) const + { + return column0 * other.x + column1 * other.y + column2 * other.z; + } + + //! Transform vector by matrix transpose, v' = M^t*v + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec3 transformTranspose(const QT3DSVec3 &other) const + { + return QT3DSVec3(column0.dot(other), column1.dot(other), column2.dot(other)); + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE const NVReal *front() const { return &column0.x; } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 &operator[](int num) { return (&column0)[num]; } + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE const QT3DSVec3 &operator[](int num) const + { + return (&column0)[num]; + } + + // Data, see above for format! + + QT3DSVec3 column0, column1, column2; // the three base vectors +}; + +// implementation from Qt3DSQuat.h +QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSQuat::QT3DSQuat(const QT3DSMat33 &m) +{ + NVReal tr = m(0, 0) + m(1, 1) + m(2, 2), h; + if (tr >= 0) { + h = NVSqrt(tr + 1); + w = NVReal(0.5) * h; + h = NVReal(0.5) / h; + + x = (m(2, 1) - m(1, 2)) * h; + y = (m(0, 2) - m(2, 0)) * h; + z = (m(1, 0) - m(0, 1)) * h; + } else { + int i = 0; + if (m(1, 1) > m(0, 0)) + i = 1; + if (m(2, 2) > m(i, i)) + i = 2; + switch (i) { + case 0: + h = NVSqrt((m(0, 0) - (m(1, 1) + m(2, 2))) + 1); + x = NVReal(0.5) * h; + h = NVReal(0.5) / h; + + y = (m(0, 1) + m(1, 0)) * h; + z = (m(2, 0) + m(0, 2)) * h; + w = (m(2, 1) - m(1, 2)) * h; + break; + case 1: + h = NVSqrt((m(1, 1) - (m(2, 2) + m(0, 0))) + 1); + y = NVReal(0.5) * h; + h = NVReal(0.5) / h; + + z = (m(1, 2) + m(2, 1)) * h; + x = (m(0, 1) + m(1, 0)) * h; + w = (m(0, 2) - m(2, 0)) * h; + break; + case 2: + h = NVSqrt((m(2, 2) - (m(0, 0) + m(1, 1))) + 1); + z = NVReal(0.5) * h; + h = NVReal(0.5) / h; + + x = (m(2, 0) + m(0, 2)) * h; + y = (m(1, 2) + m(2, 1)) * h; + w = (m(1, 0) - m(0, 1)) * h; + break; + default: // Make compiler happy + x = y = z = w = 0; + break; + } + } +} + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** @} */ +#endif // QT3DS_FOUNDATION_QT3DS_MAT33_H diff --git a/src/foundation/Qt3DSMat44.h b/src/foundation/Qt3DSMat44.h new file mode 100644 index 0000000..53a66e2 --- /dev/null +++ b/src/foundation/Qt3DSMat44.h @@ -0,0 +1,497 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_MAT44_H +#define QT3DS_FOUNDATION_QT3DS_MAT44_H +/** \addtogroup foundation +@{ +*/ + +#include "foundation/Qt3DSQuat.h" +#include "foundation/Qt3DSVec4.h" +#include "foundation/Qt3DSMat33.h" +#include "foundation/Qt3DSTransform.h" + +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif + +/*! +\brief 4x4 matrix class + +This class is layout-compatible with D3D and OpenGL matrices. More notes on layout are given in the QT3DSMat33 +Graphics matrices always (by which I mean DX and OpenGL) have the rotation basis vectors stored contiguously +in 0-2, 3-5, 6-8 and the the translation stored contiguously in 12-14. However, DX calls this row major and GL calls it column-major. +http://www.mindcontrol.org/~hplus/graphics/matrix-layout.html + +you can blat these directly into GL (or DX) + +--Dilip Sequeira + +@see QT3DSMat33 NVTransform +*/ + +class QT3DSMat44 +{ +public: + //! Default constructor + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44() {} + + //! Construct from four 4-vectors + QT3DS_CUDA_CALLABLE QT3DSMat44(const QT3DSVec4 &col0, const QT3DSVec4 &col1, const QT3DSVec4 &col2, + const QT3DSVec4 &col3) + : column0(col0) + , column1(col1) + , column2(col2) + , column3(col3) + { + } + + //! Construct from three base vectors and a translation + QT3DS_CUDA_CALLABLE QT3DSMat44(const QT3DSVec3 &column0, const QT3DSVec3 &column1, const QT3DSVec3 &column2, + const QT3DSVec3 &column3) + : column0(column0, 0) + , column1(column1, 0) + , column2(column2, 0) + , column3(column3, 1) + { + } + + //! Construct from float[16] + explicit QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44(NVReal values[]) + : column0(values[0], values[1], values[2], values[3]) + , column1(values[4], values[5], values[6], values[7]) + , column2(values[8], values[9], values[10], values[11]) + , column3(values[12], values[13], values[14], values[15]) + { + } + + //! Construct from a quaternion + explicit QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44(const QT3DSQuat &q) + { + const NVReal x = q.x; + const NVReal y = q.y; + const NVReal z = q.z; + const NVReal w = q.w; + + const NVReal x2 = x + x; + const NVReal y2 = y + y; + const NVReal z2 = z + z; + + const NVReal xx = x2 * x; + const NVReal yy = y2 * y; + const NVReal zz = z2 * z; + + const NVReal xy = x2 * y; + const NVReal xz = x2 * z; + const NVReal xw = x2 * w; + + const NVReal yz = y2 * z; + const NVReal yw = y2 * w; + const NVReal zw = z2 * w; + + column0 = QT3DSVec4(1.0f - yy - zz, xy + zw, xz - yw, 0.0f); + column1 = QT3DSVec4(xy - zw, 1.0f - xx - zz, yz + xw, 0.0f); + column2 = QT3DSVec4(xz + yw, yz - xw, 1.0f - xx - yy, 0.0f); + column3 = QT3DSVec4(0.0f, 0.0f, 0.0f, 1.0f); + } + + //! Construct from a diagonal vector + explicit QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44(const QT3DSVec4 &diagonal) + : column0(diagonal.x, 0.0f, 0.0f, 0.0f) + , column1(0.0f, diagonal.y, 0.0f, 0.0f) + , column2(0.0f, 0.0f, diagonal.z, 0.0f) + , column3(0.0f, 0.0f, 0.0f, diagonal.w) + { + } + + QT3DS_CUDA_CALLABLE QT3DSMat44(const QT3DSMat33 &orientation, const QT3DSVec3 &position) + : column0(orientation.column0, 0.0f) + , column1(orientation.column1, 0.0f) + , column2(orientation.column2, 0.0f) + , column3(position, 1) + { + } + + QT3DS_CUDA_CALLABLE QT3DSMat44(const NVTransform &t) { *this = QT3DSMat44(QT3DSMat33(t.q), t.p); } + + //! Copy constructor + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44(const QT3DSMat44 &other) + : column0(other.column0) + , column1(other.column1) + , column2(other.column2) + , column3(other.column3) + { + } + + //! Assignment operator + QT3DS_CUDA_CALLABLE QT3DS_INLINE const QT3DSMat44 &operator=(const QT3DSMat44 &other) + { + column0 = other.column0; + column1 = other.column1; + column2 = other.column2; + column3 = other.column3; + return *this; + } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE static QT3DSMat44 createIdentity() + { + return QT3DSMat44(QT3DSVec4(1.0f, 0.0f, 0.0f, 0.0f), QT3DSVec4(0.0f, 1.0f, 0.0f, 0.0f), + QT3DSVec4(0.0f, 0.0f, 1.0f, 0.0f), QT3DSVec4(0.0f, 0.0f, 0.0f, 1.0f)); + } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE static QT3DSMat44 createZero() + { + return QT3DSMat44(QT3DSVec4(0.0f), QT3DSVec4(0.0f), QT3DSVec4(0.0f), QT3DSVec4(0.0f)); + } + + //! Get transposed matrix + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44 getTranspose() const + { + return QT3DSMat44(QT3DSVec4(column0.x, column1.x, column2.x, column3.x), + QT3DSVec4(column0.y, column1.y, column2.y, column3.y), + QT3DSVec4(column0.z, column1.z, column2.z, column3.z), + QT3DSVec4(column0.w, column1.w, column2.w, column3.w)); + } + + //! Unary minus + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44 operator-() const + { + return QT3DSMat44(-column0, -column1, -column2, -column3); + } + + //! Add + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44 operator+(const QT3DSMat44 &other) const + { + return QT3DSMat44(column0 + other.column0, column1 + other.column1, column2 + other.column2, + column3 + other.column3); + } + + //! Subtract + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44 operator-(const QT3DSMat44 &other) const + { + return QT3DSMat44(column0 - other.column0, column1 - other.column1, column2 - other.column2, + column3 - other.column3); + } + + //! Scalar multiplication + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44 operator*(NVReal scalar) const + { + return QT3DSMat44(column0 * scalar, column1 * scalar, column2 * scalar, column3 * scalar); + } + + friend QT3DSMat44 operator*(NVReal, const QT3DSMat44 &); + + //! Matrix multiplication + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44 operator*(const QT3DSMat44 &other) const + { + // Rows from this <dot> columns from other + // column0 = transform(other.column0) etc + return QT3DSMat44(transform(other.column0), transform(other.column1), transform(other.column2), + transform(other.column3)); + } + + // a <op>= b operators + + //! Equals-add + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44 &operator+=(const QT3DSMat44 &other) + { + column0 += other.column0; + column1 += other.column1; + column2 += other.column2; + column3 += other.column3; + return *this; + } + + //! Equals-sub + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44 &operator-=(const QT3DSMat44 &other) + { + column0 -= other.column0; + column1 -= other.column1; + column2 -= other.column2; + column3 -= other.column3; + return *this; + } + + //! Equals scalar multiplication + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44 &operator*=(NVReal scalar) + { + column0 *= scalar; + column1 *= scalar; + column2 *= scalar; + column3 *= scalar; + return *this; + } + + //! Element access, mathematical way! + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal operator()(unsigned int row, unsigned int col) const + { + return (*this)[col][row]; + } + + //! Element access, mathematical way! + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal &operator()(unsigned int row, unsigned int col) + { + return (*this)[col][row]; + } + + //! Transform vector by matrix, equal to v' = M*v + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 transform(const QT3DSVec4 &other) const + { + return column0 * other.x + column1 * other.y + column2 * other.z + column3 * other.w; + } + + //! Transform vector by matrix, equal to v' = M*v + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec3 transform(const QT3DSVec3 &other) const + { + return transform(QT3DSVec4(other, 1)).getXYZ(); + } + + //! Rotate vector by matrix, equal to v' = M*v + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 rotate(const QT3DSVec4 &other) const + { + return column0 * other.x + column1 * other.y + column2 * other.z; // + column3*0; + } + + //! Rotate vector by matrix, equal to v' = M*v + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec3 rotate(const QT3DSVec3 &other) const + { + return rotate(QT3DSVec4(other, 1)).getXYZ(); + } + + /** +* Shamelessly pulled from gl-matrix.js + * Rotates a matrix by the given angle around the specified axis +* +* @param angle Angle (in radians) to rotate +* @param axis vec3 representing the axis to rotate around +*/ + QT3DS_CUDA_CALLABLE QT3DS_INLINE bool rotate(QT3DSF32 angleRadians, QT3DSVec3 axis) + { + QT3DSF32 x = axis[0], y = axis[1], z = axis[2], len = x * x + y * y + z * z, s, c, t, a00, a01, + a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, b00, b01, b02, b10, b11, b12, b20, + b21, b22; + + if (!len) { + return false; + } + if (len != 1.0f) { + len = 1 / (NVSqrt(len)); + x *= len; + y *= len; + z *= len; + } + + s = NVSin(angleRadians); + c = NVCos(angleRadians); + t = 1 - c; + + QT3DSF32 *dataPtr(front()); + +// inverse algorithm was written for row-major matrixes. +#define mat(idx) dataPtr[idx] + + a00 = mat(0); + a01 = mat(1); + a02 = mat(2); + a03 = mat(3); + a10 = mat(4); + a11 = mat(5); + a12 = mat(6); + a13 = mat(7); + a20 = mat(8); + a21 = mat(9); + a22 = mat(10); + a23 = mat(11); +#undef mat + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; + b01 = y * x * t + z * s; + b02 = z * x * t - y * s; + b10 = x * y * t - z * s; + b11 = y * y * t + c; + b12 = z * y * t + x * s; + b20 = x * z * t + y * s; + b21 = y * z * t - x * s; + b22 = z * z * t + c; + +// Perform rotation-specific matrix multiplication + +#define dest(idx) dataPtr[idx] + + dest(0) = a00 * b00 + a10 * b01 + a20 * b02; + dest(1) = a01 * b00 + a11 * b01 + a21 * b02; + dest(2) = a02 * b00 + a12 * b01 + a22 * b02; + dest(3) = a03 * b00 + a13 * b01 + a23 * b02; + + dest(4) = a00 * b10 + a10 * b11 + a20 * b12; + dest(5) = a01 * b10 + a11 * b11 + a21 * b12; + dest(6) = a02 * b10 + a12 * b11 + a22 * b12; + dest(7) = a03 * b10 + a13 * b11 + a23 * b12; + + dest(8) = a00 * b20 + a10 * b21 + a20 * b22; + dest(9) = a01 * b20 + a11 * b21 + a21 * b22; + dest(10) = a02 * b20 + a12 * b21 + a22 * b22; + dest(11) = a03 * b20 + a13 * b21 + a23 * b22; + +#undef dest + + return true; + }; + + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec3 getBasis(int num) const + { + QT3DS_ASSERT(num >= 0 && num < 3); + return (&column0)[num].getXYZ(); + } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec3 getPosition() const { return column3.getXYZ(); } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE void setPosition(const QT3DSVec3 &position) + { + column3.x = position.x; + column3.y = position.y; + column3.z = position.z; + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal *front() { return &column0.x; } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE const NVReal *front() const { return &column0.x; } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec4 &operator[](int num) { return (&column0)[num]; } + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE const QT3DSVec4 &operator[](int num) const + { + return (&column0)[num]; + } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE void scale(const QT3DSVec4 &p) + { + column0 *= p.x; + column1 *= p.y; + column2 *= p.z; + column3 *= p.w; + } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44 inverseRT(void) const + { + QT3DSVec3 r0(column0.x, column1.x, column2.x), r1(column0.y, column1.y, column2.y), + r2(column0.z, column1.z, column2.z); + + return QT3DSMat44(r0, r1, r2, -(r0 * column3.x + r1 * column3.y + r2 * column3.z)); + } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat44 getInverse(void) const + { + // inverse algorithm was written for row-major matrixes. + const QT3DSF32 *myPtr(front()); +#define mat(idx) myPtr[idx] + + QT3DSF32 a00 = mat(0), a01 = mat(1), a02 = mat(2), a03 = mat(3), a10 = mat(4), a11 = mat(5), + a12 = mat(6), a13 = mat(7), a20 = mat(8), a21 = mat(9), a22 = mat(10), a23 = mat(11), + a30 = mat(12), a31 = mat(13), a32 = mat(14), a33 = mat(15), +#undef mat + + b00 = a00 * a11 - a01 * a10, b01 = a00 * a12 - a02 * a10, b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, b04 = a01 * a13 - a03 * a11, b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, b07 = a20 * a32 - a22 * a30, b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, b10 = a21 * a33 - a23 * a31, b11 = a22 * a33 - a23 * a32, + + d = (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06), invDet; + + // Calculate the determinant + if (!d) { + QT3DS_ASSERT(false); + return QT3DSMat44::createIdentity(); + } + invDet = 1 / d; + + QT3DSMat44 retval; + QT3DSF32 *destPtr = retval.front(); + +#define dest(idx) destPtr[idx] + dest(0) = (a11 * b11 - a12 * b10 + a13 * b09) * invDet; + dest(1) = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet; + dest(2) = (a31 * b05 - a32 * b04 + a33 * b03) * invDet; + dest(3) = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet; + dest(4) = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet; + dest(5) = (a00 * b11 - a02 * b08 + a03 * b07) * invDet; + dest(6) = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet; + dest(7) = (a20 * b05 - a22 * b02 + a23 * b01) * invDet; + dest(8) = (a10 * b10 - a11 * b08 + a13 * b06) * invDet; + dest(9) = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet; + dest(10) = (a30 * b04 - a31 * b02 + a33 * b00) * invDet; + dest(11) = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet; + dest(12) = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet; + dest(13) = (a00 * b09 - a01 * b07 + a02 * b06) * invDet; + dest(14) = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet; + dest(15) = (a20 * b03 - a21 * b01 + a22 * b00) * invDet; +#undef dest + + return retval; + } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE bool isFinite() const + { + return column0.isFinite() && column1.isFinite() && column2.isFinite() && column3.isFinite(); + } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat33 getUpper3x3() const + { + return QT3DSMat33(column0.getXYZ(), column1.getXYZ(), column2.getXYZ()); + } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSMat33 getUpper3x3InverseTranspose() const + { + return getUpper3x3().getInverse().getTranspose(); + } + + // Data, see above for format! + + QT3DSVec4 column0, column1, column2, column3; // the four base vectors +}; + +// implementation from Qt3DSTransform.h +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVTransform::NVTransform(const QT3DSMat44 &m) +{ + QT3DSVec3 column0 = QT3DSVec3(m.column0.x, m.column0.y, m.column0.z); + QT3DSVec3 column1 = QT3DSVec3(m.column1.x, m.column1.y, m.column1.z); + QT3DSVec3 column2 = QT3DSVec3(m.column2.x, m.column2.y, m.column2.z); + + q = QT3DSQuat(QT3DSMat33(column0, column1, column2)); + p = QT3DSVec3(m.column3.x, m.column3.y, m.column3.z); +} + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** @} */ +#endif // QT3DS_FOUNDATION_QT3DS_MAT44_H diff --git a/src/foundation/Qt3DSMath.h b/src/foundation/Qt3DSMath.h new file mode 100644 index 0000000..931852e --- /dev/null +++ b/src/foundation/Qt3DSMath.h @@ -0,0 +1,323 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_MATH_H +#define QT3DS_FOUNDATION_QT3DS_MATH_H + +/** \addtogroup foundation +@{ +*/ + +#include <math.h> +#include <float.h> +#include <stdlib.h> + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSIntrinsics.h" +#include "foundation/Qt3DSAssert.h" + +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif + +// constants +static const NVReal NVPi = NVReal(3.141592653589793); +static const NVReal NVHalfPi = NVReal(1.57079632679489661923); +static const NVReal NVTwoPi = NVReal(6.28318530717958647692); +static const NVReal NVInvPi = NVReal(0.31830988618379067154); + +/** +\brief The return value is the greater of the two specified values. +*/ +template <class T> +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE T NVMax(T a, T b) +{ + return a < b ? b : a; +} + +//! overload for float to use fsel on xbox +template <> +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float NVMax(float a, float b) +{ + return intrinsics::selectMax(a, b); +} + +/** +\brief The return value is the lesser of the two specified values. +*/ +template <class T> +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE T NVMin(T a, T b) +{ + return a < b ? a : b; +} + +template <> +//! overload for float to use fsel on xbox +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float NVMin(float a, float b) +{ + return intrinsics::selectMin(a, b); +} + +/* +Many of these are just implemented as QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE calls to the C lib right now, +but later we could replace some of them with some approximations or more +clever stuff. +*/ + +/** +\brief abs returns the absolute value of its argument. +*/ +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 NVAbs(QT3DSF32 a) +{ + return intrinsics::abs(a); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool NVEquals(QT3DSF32 a, QT3DSF32 b, QT3DSF32 epsilon) +{ + return (NVAbs(a - b) < epsilon); +} + +/** +\brief abs returns the absolute value of its argument. +*/ +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 NVAbs(QT3DSF64 a) +{ + return ::fabs(a); +} + +/** +\brief abs returns the absolute value of its argument. +*/ +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSI32 NVAbs(QT3DSI32 a) +{ + return ::abs(a); +} + +/** +\brief Clamps v to the range [hi,lo] +*/ +template <class T> +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE T NVClamp(T v, T lo, T hi) +{ + QT3DS_ASSERT(lo <= hi); + return NVMin(hi, NVMax(lo, v)); +} + +//! \brief Square root. +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 NVSqrt(QT3DSF32 a) +{ + return intrinsics::sqrt(a); +} + +//! \brief Square root. +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 NVSqrt(QT3DSF64 a) +{ + return ::sqrt(a); +} + +//! \brief reciprocal square root. +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 NVRecipSqrt(QT3DSF32 a) +{ + return intrinsics::recipSqrt(a); +} + +//! \brief reciprocal square root. +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 NVRecipSqrt(QT3DSF64 a) +{ + return 1 / ::sqrt(a); +} + +//!trigonometry -- all angles are in radians. + +//! \brief Sine of an angle ( <b>Unit:</b> Radians ) +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 NVSin(QT3DSF32 a) +{ + return intrinsics::sin(a); +} + +//! \brief Sine of an angle ( <b>Unit:</b> Radians ) +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 NVSin(QT3DSF64 a) +{ + return ::sin(a); +} + +//! \brief Cosine of an angle (<b>Unit:</b> Radians) +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 NVCos(QT3DSF32 a) +{ + return intrinsics::cos(a); +} + +//! \brief Cosine of an angle (<b>Unit:</b> Radians) +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 NVCos(QT3DSF64 a) +{ + return ::cos(a); +} + +/** +\brief Tangent of an angle. +<b>Unit:</b> Radians +*/ +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 NVTan(QT3DSF32 a) +{ + return QT3DSF32(::tan(a)); +} + +/** +\brief Tangent of an angle. +<b>Unit:</b> Radians +*/ +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 NVTan(QT3DSF64 a) +{ + return ::tan(a); +} + +/** +\brief Arcsine. +Returns angle between -PI/2 and PI/2 in radians +<b>Unit:</b> Radians +*/ +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 NVAsin(QT3DSF32 f) +{ + return QT3DSF32(::asin(NVClamp(f, -1.0f, 1.0f))); +} + +/** +\brief Arcsine. +Returns angle between -PI/2 and PI/2 in radians +<b>Unit:</b> Radians +*/ +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 NVAsin(QT3DSF64 f) +{ + return ::asin(NVClamp(f, -1.0, 1.0)); +} + +/** +\brief Arccosine. +Returns angle between 0 and PI in radians +<b>Unit:</b> Radians +*/ +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 NVAcos(QT3DSF32 f) +{ + return QT3DSF32(::acos(NVClamp(f, -1.0f, 1.0f))); +} + +/** +\brief Arccosine. +Returns angle between 0 and PI in radians +<b>Unit:</b> Radians +*/ +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 NVAcos(QT3DSF64 f) +{ + return ::acos(NVClamp(f, -1.0, 1.0)); +} + +/** +\brief ArcTangent. +Returns angle between -PI/2 and PI/2 in radians +<b>Unit:</b> Radians +*/ +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 NVAtan(QT3DSF32 a) +{ + return QT3DSF32(::atan(a)); +} + +/** +\brief ArcTangent. +Returns angle between -PI/2 and PI/2 in radians +<b>Unit:</b> Radians +*/ +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 NVAtan(QT3DSF64 a) +{ + return ::atan(a); +} + +/** +\brief Arctangent of (x/y) with correct sign. +Returns angle between -PI and PI in radians +<b>Unit:</b> Radians +*/ +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 NVAtan2(QT3DSF32 x, QT3DSF32 y) +{ + return QT3DSF32(::atan2(x, y)); +} + +/** +\brief Arctangent of (x/y) with correct sign. +Returns angle between -PI and PI in radians +<b>Unit:</b> Radians +*/ +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 NVAtan2(QT3DSF64 x, QT3DSF64 y) +{ + return ::atan2(x, y); +} + +//! \brief returns true if the passed number is a finite floating point number as opposed to INF, NAN, etc. +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool NVIsFinite(QT3DSF32 f) +{ + return intrinsics::isFinite(f); +} + +//! \brief returns true if the passed number is a finite floating point number as opposed to INF, NAN, etc. +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool NVIsFinite(QT3DSF64 f) +{ + return intrinsics::isFinite(f); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 NVFloor(QT3DSF32 a) +{ + return ::floorf(a); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 NVExp(QT3DSF32 a) +{ + return ::expf(a); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 NVCeil(QT3DSF32 a) +{ + return ::ceilf(a); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 NVSign(QT3DSF32 a) +{ + return qt3ds::intrinsics::sign(a); +} + +QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 NVPow(QT3DSF32 x, QT3DSF32 y) +{ + return ::powf(x, y); +}; + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** @} */ +#endif // QT3DS_FOUNDATION_QT3DS_MATH_H diff --git a/src/foundation/Qt3DSMathUtils.cpp b/src/foundation/Qt3DSMathUtils.cpp new file mode 100644 index 0000000..7eee740 --- /dev/null +++ b/src/foundation/Qt3DSMathUtils.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DSMathUtils.h" +#include "foundation/Qt3DSUtilities.h" +#include "foundation/Qt3DSMat33.h" + +using namespace qt3ds; +using namespace qt3ds::foundation; +using namespace qt3ds::intrinsics; + +QT3DSQuat qt3ds::foundation::computeQuatFromNormal(const QT3DSVec3 &n) +{ + // parallel or anti-parallel + if (n.x > 0.9999f) { + // parallel + return QT3DSQuat::createIdentity(); + } else if (n.x < -0.9999f) { + // anti-parallel + // contactQuaternion.fromAngleAxisFast(PXD_PI, Vector3(0.0f, 1.0f, 0.0f)); + return QT3DSQuat(0.0f, 1.0f, 0.0f, 0.0f); + } else { + QT3DSVec3 rotVec(0.0f, -n.z, n.y); + + // Convert to quat + NVReal angle = rotVec.magnitude(); + rotVec *= 1.0f / angle; + // if(angle > 1.0f) angle = 1.0f; + angle = selectMin(angle, 1.0f); + + // djs: injudiciously imbecilic use of trig functions, good thing Adam is going to trample + // this path like a + // frustrated rhinoceros in mating season + + angle = NVAsin(angle); + + // if(n.x < 0) + // angle = NVPi - angle; + angle = fsel(n.x, angle, NVPi - angle); + + return QT3DSQuat(angle, rotVec); + } +} + +/** +\brief computes a oriented bounding box around the scaled basis. +\param basis Input = skewed basis, Output = (normalized) orthogonal basis. +\return Bounding box extent. +*/ +QT3DSVec3 qt3ds::foundation::optimizeBoundingBox(QT3DSMat33 &basis) +{ + QT3DSVec3 *QT3DS_RESTRICT vec = &basis[0]; // PT: don't copy vectors if not needed... + + // PT: since we store the magnitudes to memory, we can avoid the FCMNV afterwards + QT3DSVec3 magnitude(vec[0].magnitudeSquared(), vec[1].magnitudeSquared(), + vec[2].magnitudeSquared()); + +// find indices sorted by magnitude +#ifdef QT3DS_X360 + int i = (QT3DSU32 &)(magnitude[1]) > (QT3DSU32 &)(magnitude[0]) ? 1 : 0; + int j = (QT3DSU32 &)(magnitude[2]) > (QT3DSU32 &)(magnitude[1 - i]) ? 2 : 1 - i; +#else + int i = magnitude[1] > magnitude[0] ? 1 : 0; + int j = magnitude[2] > magnitude[1 - i] ? 2 : 1 - i; +#endif + const int k = 3 - i - j; +#ifdef QT3DS_X360 + if ((QT3DSU32 &)(magnitude[i]) < (QT3DSU32 &)(magnitude[j])) +#else + if (magnitude[i] < magnitude[j]) +#endif + swap(i, j); + + // ortho-normalize basis + + NVReal invSqrt = NVRecipSqrt(magnitude[i]); + magnitude[i] *= invSqrt; + vec[i] *= invSqrt; // normalize the first axis + NVReal dotij = vec[i].dot(vec[j]); + NVReal dotik = vec[i].dot(vec[k]); + magnitude[i] += NVAbs(dotij) + NVAbs(dotik); // elongate the axis by projection of the other two + vec[j] -= vec[i] * dotij; // orthogonize the two remaining axii relative to vec[i] + vec[k] -= vec[i] * dotik; + + magnitude[j] = vec[j].normalize(); + NVReal dotjk = vec[j].dot(vec[k]); + magnitude[j] += NVAbs(dotjk); // elongate the axis by projection of the other one + vec[k] -= vec[j] * dotjk; // orthogonize vec[k] relative to vec[j] + + magnitude[k] = vec[k].normalize(); + + return magnitude; +} + +QT3DSQuat qt3ds::foundation::slerp(const NVReal t, const QT3DSQuat &left, const QT3DSQuat &right) +{ + const NVReal quatEpsilon = (NVReal(1.0e-8f)); + + NVReal cosine = left.dot(right); + NVReal sign = NVReal(1); + if (cosine < 0) { + cosine = -cosine; + sign = NVReal(-1); + } + + NVReal sine = NVReal(1) - cosine * cosine; + + if (sine >= quatEpsilon * quatEpsilon) { + sine = NVSqrt(sine); + const NVReal angle = NVAtan2(sine, cosine); + const NVReal i_sin_angle = NVReal(1) / sine; + + const NVReal leftw = NVSin(angle * (NVReal(1) - t)) * i_sin_angle; + const NVReal rightw = NVSin(angle * t) * i_sin_angle * sign; + + return left * leftw + right * rightw; + } + + return left; +} + +void qt3ds::foundation::integrateTransform(const NVTransform &curTrans, const QT3DSVec3 &linvel, + const QT3DSVec3 &angvel, NVReal timeStep, NVTransform &result) +{ + result.p = curTrans.p + linvel * timeStep; + + // from void NVsDynamicsContext::integrateAtomPose(NVsRigidBody* atom, Cm::BitMap + // &shapeChangedMap) const: + // Integrate the rotation using closed form quaternion integrator + NVReal w = angvel.magnitudeSquared(); + + if (w != 0.0f) { + w = NVSqrt(w); + if (w != 0.0f) { + const NVReal v = timeStep * w * 0.5f; + const NVReal q = NVCos(v); + const NVReal s = NVSin(v) / w; + + const QT3DSVec3 pqr = angvel * s; + const QT3DSQuat quatVel(pqr.x, pqr.y, pqr.z, 0); + QT3DSQuat out; // need to have temporary, otherwise we may overwrite input if &curTrans == + // &result. + out = quatVel * curTrans.q; + out.x += curTrans.q.x * q; + out.y += curTrans.q.y * q; + out.z += curTrans.q.z * q; + out.w += curTrans.q.w * q; + result.q = out; + return; + } + } + // orientation stays the same - convert from quat to matrix: + result.q = curTrans.q; +} diff --git a/src/foundation/Qt3DSMathUtils.h b/src/foundation/Qt3DSMathUtils.h new file mode 100644 index 0000000..6d3d0ab --- /dev/null +++ b/src/foundation/Qt3DSMathUtils.h @@ -0,0 +1,571 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_PSMATHUTILS_H +#define QT3DS_FOUNDATION_PSMATHUTILS_H + +#include "foundation/Qt3DSTransform.h" +#include "foundation/Qt3DSMat33.h" +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSIntrinsics.h" +#include <stdlib.h> + +// General guideline is: if it's an abstract math function, it belongs here. +// If it's a math function where the inputs have specific semantics (e.g. +// separateSwingTwist) it doesn't. + +namespace qt3ds { +namespace foundation { + using namespace intrinsics; + /** + \brief sign returns the sign of its argument. The sign of zero is undefined. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 sign(const QT3DSF32 a) { + return intrinsics::sign(a); + } + + /** + \brief sign returns the sign of its argument. The sign of zero is undefined. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 sign(const QT3DSF64 a) { return (a >= 0.0) ? 1.0 : -1.0; } + + /** + \brief sign returns the sign of its argument. The sign of zero is undefined. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSI32 sign(const QT3DSI32 a) { return (a >= 0) ? 1 : -1; } + + /** + \brief Returns true if the two numbers are within eps of each other. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool equals(const QT3DSF32 a, const QT3DSF32 b, const QT3DSF32 eps) + { + return (NVAbs(a - b) < eps); + } + + /** + \brief Returns true if the two numbers are within eps of each other. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool equals(const QT3DSF64 a, const QT3DSF64 b, const QT3DSF64 eps) + { + return (NVAbs(a - b) < eps); + } + + /** + \brief The floor function returns a floating-point value representing the largest integer that + is less than or equal to x. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 floor(const QT3DSF32 a) { return floatFloor(a); } + + /** + \brief The floor function returns a floating-point value representing the largest integer that + is less than or equal to x. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 floor(const QT3DSF64 a) { return ::floor(a); } + + /** + \brief The ceil function returns a single value representing the smallest integer that is + greater than or equal to x. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 ceil(const QT3DSF32 a) { return ::ceilf(a); } + + /** + \brief The ceil function returns a double value representing the smallest integer that is + greater than or equal to x. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 ceil(const QT3DSF64 a) { return ::ceil(a); } + + /** + \brief mod returns the floating-point remainder of x / y. + + If the value of y is 0.0, mod returns a quiet NaN. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 mod(const QT3DSF32 x, const QT3DSF32 y) + { + return (QT3DSF32)::fmod(x, y); + } + + /** + \brief mod returns the floating-point remainder of x / y. + + If the value of y is 0.0, mod returns a quiet NaN. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 mod(const QT3DSF64 x, const QT3DSF64 y) + { + return ::fmod(x, y); + } + + /** + \brief Square. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 sqr(const QT3DSF32 a) { return a * a; } + + /** + \brief Square. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 sqr(const QT3DSF64 a) { return a * a; } + + /** + \brief Calculates x raised to the power of y. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 pow(const QT3DSF32 x, const QT3DSF32 y) + { + return ::powf(x, y); + } + + /** + \brief Calculates x raised to the power of y. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 pow(const QT3DSF64 x, const QT3DSF64 y) { return ::pow(x, y); } + + /** + \brief Calculates e^n + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 exp(const QT3DSF32 a) { return QT3DSF32(::exp(a)); } + /** + + \brief Calculates e^n + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 exp(const QT3DSF64 a) { return ::exp(a); } + + /** + \brief Calculates logarithms. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 logE(const QT3DSF32 a) { return QT3DSF32(::log(a)); } + + /** + \brief Calculates logarithms. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 logE(const QT3DSF64 a) { return ::log(a); } + + /** + \brief Calculates logarithms. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 log2(const QT3DSF32 a) + { + return QT3DSF32(::log(a)) / 0.693147180559945309417f; + } + + /** + \brief Calculates logarithms. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 log2(const QT3DSF64 a) + { + return ::log(a) / 0.693147180559945309417; + } + + /** + \brief Calculates logarithms. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 log10(const QT3DSF32 a) { return (QT3DSF32)::log10(a); } + + /** + \brief Calculates logarithms. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 log10(const QT3DSF64 a) { return ::log10(a); } + + /** + \brief Converts degrees to radians. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 degToRad(const QT3DSF32 a) + { + return (QT3DSF32)0.01745329251994329547 * a; + } + + /** + \brief Converts degrees to radians. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 degToRad(const QT3DSF64 a) + { + return (QT3DSF64)0.01745329251994329547 * a; + } + + /** + \brief Converts radians to degrees. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 radToDeg(const QT3DSF32 a) + { + return (QT3DSF32)57.29577951308232286465 * a; + } + + /** + \brief Converts radians to degrees. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF64 radToDeg(const QT3DSF64 a) + { + return (QT3DSF64)57.29577951308232286465 * a; + } + + //! \brief compute sine and cosine at the same time. There is a 'fsincos' on PC that we probably want to use here + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void sincos(const QT3DSF32 radians, QT3DSF32 &sin, QT3DSF32 &cos) + { + /* something like: + _asm fld Local + _asm fsincos + _asm fstp LocalCos + _asm fstp LocalSin + */ + sin = NVSin(radians); + cos = NVCos(radians); + } + + /** + \brief uniform random number in [a,b] + */ + QT3DS_FORCE_INLINE QT3DSI32 rand(const QT3DSI32 a, const QT3DSI32 b) + { + return a + (QT3DSI32)(::rand() % (b - a + 1)); + } + + /** + \brief uniform random number in [a,b] + */ + QT3DS_FORCE_INLINE QT3DSF32 rand(const QT3DSF32 a, const QT3DSF32 b) + { + return a + (b - a) * ::rand() / RAND_MAX; + } + + //! \brief return angle between two vectors in radians + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSF32 angle(const QT3DSVec3 &v0, const QT3DSVec3 &v1) + { + const QT3DSF32 cos = v0.dot(v1); // |v0|*|v1|*Cos(Angle) + const QT3DSF32 sin = (v0.cross(v1)).magnitude(); // |v0|*|v1|*Sin(Angle) + return NVAtan2(sin, cos); + } + + //! If possible use instead fsel on the dot product /*fsel(d.dot(p),onething,anotherthing);*/ + //! Compares orientations (more readable, user-friendly function) + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool sameDirection(const QT3DSVec3 &d, const QT3DSVec3 &p) + { + return d.dot(p) >= 0.0f; + } + + //! Checks 2 values have different signs + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE IntBool differentSign(NVReal f0, NVReal f1) + { + union { + QT3DSU32 u; + NVReal f; + } u1, u2; + u1.f = f0; + u2.f = f1; + return (u1.u ^ u2.u) & QT3DS_SIGN_BITMASK; + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSMat33 star(const QT3DSVec3 &v) + { + return QT3DSMat33(QT3DSVec3(0, v.z, -v.y), QT3DSVec3(-v.z, 0, v.x), QT3DSVec3(v.y, -v.x, 0)); + } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec3 log(const QT3DSQuat &q) + { + const NVReal s = q.getImaginaryPart().magnitude(); + if (s < 1e-12) + return QT3DSVec3(0.0f); + // force the half-angle to have magnitude <= pi/2 + NVReal halfAngle = q.w < 0 ? NVAtan2(-s, -q.w) : NVAtan2(s, q.w); + QT3DS_ASSERT(halfAngle >= -NVPi / 2 && halfAngle <= NVPi / 2); + + return q.getImaginaryPart().getNormalized() * 2 * halfAngle; + } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSQuat exp(const QT3DSVec3 &v) + { + const NVReal m = v.magnitudeSquared(); + return m < 1e-24 ? QT3DSQuat::createIdentity() : QT3DSQuat(NVSqrt(m), v * NVRecipSqrt(m)); + } + + // quat to rotate v0 t0 v1 + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSQuat rotationArc(const QT3DSVec3 &v0, const QT3DSVec3 &v1) + { + const QT3DSVec3 cross = v0.cross(v1); + const NVReal d = v0.dot(v1); + if (d <= -0.99999f) + return (NVAbs(v0.x) < 0.1f ? QT3DSQuat(0.0f, v0.z, -v0.y, 0.0f) + : QT3DSQuat(v0.y, -v0.x, 0.0, 0.0)) + .getNormalized(); + + const NVReal s = NVSqrt((1 + d) * 2), r = 1 / s; + + return QT3DSQuat(cross.x * r, cross.y * r, cross.z * r, s * 0.5f).getNormalized(); + } + + //! Computes the maximum delta to another transform + QT3DS_CUDA_CALLABLE QT3DS_INLINE NVReal maxComponentDelta(const NVTransform &t0, + const NVTransform &t1) + { + NVReal delta = NVAbs(t0.p.x - t1.p.x); + delta = NVMax(delta, NVAbs(t0.p.y - t1.p.y)); + delta = NVMax(delta, NVAbs(t0.p.z - t1.p.z)); + delta = NVMax(delta, NVAbs(t0.q.x - t1.q.x)); + delta = NVMax(delta, NVAbs(t0.q.y - t1.q.y)); + delta = NVMax(delta, NVAbs(t0.q.z - t1.q.z)); + delta = NVMax(delta, NVAbs(t0.q.w - t1.q.w)); + + return delta; + } + + /** + \brief returns largest axis + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSU32 largestAxis(const QT3DSVec3 &v) + { + QT3DSU32 m = v.y > v.x ? 1 : 0; + return v.z > v[m] ? 2 : m; + } + + /** + \brief returns axis with smallest absolute value + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSU32 closestAxis(const QT3DSVec3 &v) + { + QT3DSU32 m = NVAbs(v.y) > NVAbs(v.x) ? 1 : 0; + return NVAbs(v.z) > NVAbs(v[m]) ? 2 : m; + } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSU32 closestAxis(const QT3DSVec3 &v, QT3DSU32 &j, QT3DSU32 &k) + { + // find largest 2D plane projection + const QT3DSF32 absNV = NVAbs(v.x); + const QT3DSF32 absNy = NVAbs(v.y); + const QT3DSF32 absNz = NVAbs(v.z); + + QT3DSU32 m = 0; // x biggest axis + j = 1; + k = 2; + if (absNy > absNV && absNy > absNz) { + // y biggest + j = 2; + k = 0; + m = 1; + } else if (absNz > absNV) { + // z biggest + j = 0; + k = 1; + m = 2; + } + return m; + } + + /*! + Extend an edge along its length by a factor + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void makeFatEdge(QT3DSVec3 &p0, QT3DSVec3 &p1, NVReal fatCoeff) + { + QT3DSVec3 delta = p1 - p0; + + const NVReal m = delta.magnitude(); + if (m > 0.0f) { + delta *= fatCoeff / m; + p0 -= delta; + p1 += delta; + } + } + + //! Compute point as combination of barycentric coordinates + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 computeBarycentricPoint(const QT3DSVec3 &p0, + const QT3DSVec3 &p1, + const QT3DSVec3 &p2, NVReal u, + NVReal v) + { + // This seems to confuse the compiler... + // return (1.0f - u - v)*p0 + u*p1 + v*p2; + const QT3DSF32 w = 1.0f - u - v; + return QT3DSVec3(w * p0.x + u * p1.x + v * p2.x, w * p0.y + u * p1.y + v * p2.y, + w * p0.z + u * p1.z + v * p2.z); + } + + // generates a pair of quaternions (swing, twist) such that in = swing * twist, with + // swing.x = 0 + // twist.y = twist.z = 0, and twist is a unit quat + QT3DS_FORCE_INLINE void separateSwingTwist(const QT3DSQuat &q, QT3DSQuat &swing, QT3DSQuat &twist) + { + twist = q.x != 0.0f ? QT3DSQuat(q.x, 0, 0, q.w).getNormalized() : QT3DSQuat::createIdentity(); + swing = q * twist.getConjugate(); + } + + // generate two tangent vectors to a given normal + QT3DS_FORCE_INLINE void normalToTangents(const QT3DSVec3 &normal, QT3DSVec3 &tangent0, QT3DSVec3 &tangent1) + { + tangent0 = NVAbs(normal.x) < 0.70710678f ? QT3DSVec3(0, -normal.z, normal.y) + : QT3DSVec3(-normal.y, normal.x, 0); + tangent0.normalize(); + tangent1 = normal.cross(tangent0); + } + + // todo: what is this function doing? + QT3DS_FOUNDATION_API QT3DSQuat computeQuatFromNormal(const QT3DSVec3 &n); + + /** + \brief computes a oriented bounding box around the scaled basis. + \param basis Input = skewed basis, Output = (normalized) orthogonal basis. + \return Bounding box extent. + */ + QT3DS_FOUNDATION_API QT3DSVec3 optimizeBoundingBox(QT3DSMat33 &basis); + + QT3DS_FOUNDATION_API QT3DSQuat slerp(const NVReal t, const QT3DSQuat &left, const QT3DSQuat &right); + + QT3DS_INLINE QT3DSVec3 ellipseClamp(const QT3DSVec3 &point, const QT3DSVec3 &radii) + { + // This function need to be implemented in the header file because + // it is included in a spu shader program. + + // finds the closest point on the ellipse to a given point + + // (p.y, p.z) is the input point + // (e.y, e.z) are the radii of the ellipse + + // lagrange multiplier method with Newton/Halley hybrid root-finder. + // see http://www.geometrictools.com/Documentation/DistancePointToEllipse2.pdf + // for proof of Newton step robustness and initial estimate. + // Halley converges much faster but sometimes overshoots - when that happens we take + // a newton step instead + + // converges in 1-2 iterations where D&C works well, and it's good with 4 iterations + // with any ellipse that isn't completely crazy + + const QT3DSU32 MAX_ITERATIONS = 20; + const NVReal convergenceThreshold = 1e-4f; + + // iteration requires first quadrant but we recover generality later + + QT3DSVec3 q(0, NVAbs(point.y), NVAbs(point.z)); + const NVReal tinyEps = + (NVReal)(1e-6f); // very close to minor axis is numerically problematic but trivial + if (radii.y >= radii.z) { + if (q.z < tinyEps) + return QT3DSVec3(0, point.y > 0 ? radii.y : -radii.y, 0); + } else { + if (q.y < tinyEps) + return QT3DSVec3(0, 0, point.z > 0 ? radii.z : -radii.z); + } + + QT3DSVec3 denom, e2 = radii.multiply(radii), eq = radii.multiply(q); + + // we can use any initial guess which is > maximum(-e.y^2,-e.z^2) and for which f(t) is > 0. + // this guess works well near the axes, but is weak along the diagonals. + + NVReal t = NVMax(eq.y - e2.y, eq.z - e2.z); + + for (QT3DSU32 i = 0; i < MAX_ITERATIONS; i++) { + denom = QT3DSVec3(0, 1 / (t + e2.y), 1 / (t + e2.z)); + QT3DSVec3 denom2 = eq.multiply(denom); + + QT3DSVec3 fv = denom2.multiply(denom2); + NVReal f = fv.y + fv.z - 1; + + // although in exact arithmetic we are guaranteed f>0, we can get here + // on the first iteration via catastrophic cancellation if the point is + // very close to the origin. In that case we just behave as if f=0 + + if (f < convergenceThreshold) + return e2.multiply(point).multiply(denom); + + NVReal df = fv.dot(denom) * -2.0f; + t = t - f / df; + } + + // we didn't converge, so clamp what we have + QT3DSVec3 r = e2.multiply(point).multiply(denom); + return r * NVRecipSqrt(sqr(r.y / radii.y) + sqr(r.z / radii.z)); + } + + QT3DS_INLINE NVReal tanHalf(NVReal sin, NVReal cos) { return sin / (1 + cos); } + + QT3DS_INLINE QT3DSQuat quatFromTanQVector(const QT3DSVec3 &v) + { + NVReal v2 = v.dot(v); + if (v2 < 1e-12f) + return QT3DSQuat::createIdentity(); + NVReal d = 1 / (1 + v2); + return QT3DSQuat(v.x * 2, v.y * 2, v.z * 2, 1 - v2) * d; + } + + QT3DS_FORCE_INLINE QT3DSVec3 cross100(const QT3DSVec3 &b) { return QT3DSVec3(0.0f, -b.z, b.y); } + QT3DS_FORCE_INLINE QT3DSVec3 cross010(const QT3DSVec3 &b) { return QT3DSVec3(b.z, 0.0f, -b.x); } + QT3DS_FORCE_INLINE QT3DSVec3 cross001(const QT3DSVec3 &b) { return QT3DSVec3(-b.y, b.x, 0.0f); } + + QT3DS_INLINE void decomposeVector(QT3DSVec3 &normalCompo, QT3DSVec3 &tangentCompo, + const QT3DSVec3 &outwardDir, const QT3DSVec3 &outwardNormal) + { + normalCompo = outwardNormal * (outwardDir.dot(outwardNormal)); + tangentCompo = outwardDir - normalCompo; + } + + //! \brief Return (i+1)%3 + // Avoid variable shift for XBox: + // QT3DS_INLINE QT3DSU32 NV::getNextIndex3(QT3DSU32 i) { return (1<<i) & 3; + // } + QT3DS_INLINE QT3DSU32 getNextIndex3(QT3DSU32 i) { return (i + 1 + (i >> 1)) & 3; } + + QT3DS_INLINE QT3DSMat33 rotFrom2Vectors(const QT3DSVec3 &from, const QT3DSVec3 &to) + { + // See bottom of + // http://www.euclideanspace.com/maths/algebra/matrix/orthogonal/rotation/index.htm + + // Early exit if to = from + if ((from - to).magnitudeSquared() < 1e-4f) + return QT3DSMat33::createIdentity(); + + // Early exit if to = -from + if ((from + to).magnitudeSquared() < 1e-4f) + return QT3DSMat33::createDiagonal(QT3DSVec3(1.0f, -1.0f, -1.0f)); + + QT3DSVec3 n = from.cross(to); + + NVReal C = from.dot(to), S = NVSqrt(1 - C * C), CC = 1 - C; + + NVReal xx = n.x * n.x, yy = n.y * n.y, zz = n.z * n.z, xy = n.x * n.y, yz = n.y * n.z, + xz = n.x * n.z; + + QT3DSMat33 R; + + R(0, 0) = 1 + CC * (xx - 1); + R(0, 1) = -n.z * S + CC * xy; + R(0, 2) = n.y * S + CC * xz; + + R(1, 0) = n.z * S + CC * xy; + R(1, 1) = 1 + CC * (yy - 1); + R(1, 2) = -n.x * S + CC * yz; + + R(2, 0) = -n.y * S + CC * xz; + R(2, 1) = n.x * S + CC * yz; + R(2, 2) = 1 + CC * (zz - 1); + + return R; + } + + QT3DS_FOUNDATION_API void integrateTransform(const NVTransform &curTrans, const QT3DSVec3 &linvel, + const QT3DSVec3 &angvel, NVReal timeStep, + NVTransform &result); + +} // namespace foundation +} // namespace qt3ds + +#endif diff --git a/src/foundation/Qt3DSMemoryBuffer.h b/src/foundation/Qt3DSMemoryBuffer.h new file mode 100644 index 0000000..bee2f0c --- /dev/null +++ b/src/foundation/Qt3DSMemoryBuffer.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_MEMORYBUFFER_H +#define QT3DS_FOUNDATION_MEMORYBUFFER_H +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSAllocator.h" +#include "foundation/Qt3DSDataRef.h" +#include "foundation/Qt3DSIntrinsics.h" + +#ifdef __INTEGRITY +#define __restrict +#endif + +namespace qt3ds { +namespace foundation { + using namespace intrinsics; + + template <typename TAllocator = ForwardingAllocator> + class MemoryBuffer : public TAllocator + { + QT3DSU8 *mBegin; + QT3DSU8 *mEnd; + QT3DSU8 *mCapacityEnd; + + public: + MemoryBuffer(const TAllocator &inAlloc = TAllocator()) + : TAllocator(inAlloc) + , mBegin(0) + , mEnd(0) + , mCapacityEnd(0) + { + } + ~MemoryBuffer() + { + if (mBegin) + TAllocator::deallocate(mBegin); + } + QT3DSU32 size() const { return static_cast<QT3DSU32>(mEnd - mBegin); } + QT3DSU32 capacity() const { return static_cast<QT3DSU32>(mCapacityEnd - mBegin); } + QT3DSU8 *begin() { return mBegin; } + QT3DSU8 *end() { return mEnd; } + const QT3DSU8 *begin() const { return mBegin; } + const QT3DSU8 *end() const { return mEnd; } + void clear() { mEnd = mBegin; } + void write(QT3DSU8 inValue) { *growBuf(1) = inValue; } + + template <typename TDataType> + void write(const TDataType &inValue) + { + const QT3DSU8 *__restrict readPtr = reinterpret_cast<const QT3DSU8 *>(&inValue); + QT3DSU8 *__restrict writePtr = growBuf(sizeof(TDataType)); + for (QT3DSU32 idx = 0; idx < sizeof(TDataType); ++idx) + writePtr[idx] = readPtr[idx]; + } + + template <typename TDataType> + void write(const TDataType *inValue, QT3DSU32 inLength) + { + using namespace qt3ds::intrinsics; + if (inValue && inLength) { + QT3DSU32 writeSize = inLength * sizeof(TDataType); + memCopy(growBuf(writeSize), inValue, writeSize); + } + if (inLength && !inValue) { + QT3DS_ASSERT(false); + // You can't not write something, because that will cause + // the receiving end to crash. + QT3DSU32 writeSize = inLength * sizeof(TDataType); + for (QT3DSU32 idx = 0; idx < writeSize; ++idx) + write((QT3DSU8)0); + } + } + + void writeStrided(const QT3DSU8 *__restrict inData, QT3DSU32 inItemSize, QT3DSU32 inLength, + QT3DSU32 inStride) + { + if (inStride == 0 || inStride == inItemSize) + write(inData, inLength * inItemSize); + else if (inData && inLength) { + QT3DSU32 writeSize = inLength * inItemSize; + QT3DSU8 *__restrict writePtr = growBuf(writeSize); + for (QT3DSU32 idx = 0; idx < inLength; + ++idx, writePtr += inItemSize, inData += inStride) + memCopy(writePtr, inData, inItemSize); + } + } + QT3DSU8 *growBuf(QT3DSU32 inAmount) + { + QT3DSU32 offset = size(); + QT3DSU32 newSize = offset + inAmount; + reserve(newSize); + mEnd += inAmount; + return mBegin + offset; + } + void writeZeros(QT3DSU32 inAmount) + { + QT3DSU32 offset = size(); + growBuf(inAmount); + qt3ds::foundation::memZero(begin() + offset, inAmount); + } + void align(QT3DSU32 inAmount) + { + QT3DSU32 leftover = size() % inAmount; + if (leftover) + writeZeros(inAmount - leftover); + } + void reserve(QT3DSU32 newSize) + { + using namespace qt3ds::intrinsics; + QT3DSU32 currentSize = size(); + if (newSize && newSize >= capacity()) { + QT3DSU32 newDataSize = newSize * 2; + if (newDataSize > 8192) + newDataSize = (QT3DSU32)((QT3DSU32)newSize * 1.2f); + QT3DSU8 *newData = + static_cast<QT3DSU8 *>(TAllocator::allocate(newDataSize, __FILE__, __LINE__)); + if (mBegin) { + memCopy(newData, mBegin, currentSize); + TAllocator::deallocate(mBegin); + } + mBegin = newData; + mEnd = mBegin + currentSize; + mCapacityEnd = mBegin + newDataSize; + } + } + operator NVDataRef<QT3DSU8>() { return NVDataRef<QT3DSU8>(begin(), size()); } + operator NVConstDataRef<QT3DSU8>() const { return NVConstDataRef<QT3DSU8>(begin(), size()); } + }; +} +} + +#endif
\ No newline at end of file diff --git a/src/foundation/Qt3DSMutex.h b/src/foundation/Qt3DSMutex.h new file mode 100644 index 0000000..5b717e5 --- /dev/null +++ b/src/foundation/Qt3DSMutex.h @@ -0,0 +1,376 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_PSMUTEX_H +#define QT3DS_FOUNDATION_PSMUTEX_H + +#include "foundation/Qt3DSAllocator.h" +#include "foundation/Qt3DSNoCopy.h" + +/* + * This <new> inclusion is a best known fix for gcc 4.4.1 error: + * Creating object file for apex/src/NVAllocator.cpp ... + * In file included from apex/include/Qt3DSFoundation.h:30, + * from apex/src/NVAllocator.cpp:26: + * apex/include/Qt3DSMutex.h: In constructor 'nv::foundation::MutexT<Alloc>::MutexT(const Alloc&)': + * apex/include/Qt3DSMutex.h:92: error: no matching function for call to 'operator new(unsigned int, + * qt3ds::foundation::MutexImpl*&)' + * <built-in>:0: note: candidates are: void* operator new(unsigned int) + */ +#include <new> + +namespace qt3ds { +namespace foundation { +#ifdef QT3DS_FOUNDATION_NO_EXPORTS + class QT3DS_AUTOTEST_EXPORT MutexImpl +#else + class QT3DS_FOUNDATION_API MutexImpl +#endif + { + public: + /** + The constructor for Mutex creates a mutex. It is initially unlocked. + */ + MutexImpl(); + + /** + The destructor for Mutex deletes the mutex. + */ + ~MutexImpl(); + + /** + Acquire (lock) the mutex. If the mutex is already locked + by another thread, this method blocks until the mutex is + unlocked. + */ + bool lock(); + + /** + Acquire (lock) the mutex. If the mutex is already locked + by another thread, this method returns false without blocking. + */ + bool trylock(); + + /** + Release (unlock) the mutex. + */ + bool unlock(); + + /** + Size of this class. + */ + static const QT3DSU32 size; + }; + + class Mutex + { + public: + class ScopedLock : private NoCopy + { + Mutex &mMutex; + + public: + QT3DS_INLINE ScopedLock(Mutex &mutex) + : mMutex(mutex) + { + mMutex.lock(); + } + QT3DS_INLINE ~ScopedLock() { mMutex.unlock(); } + }; + + /** + The constructor for Mutex creates a mutex. It is initially unlocked. + */ + Mutex(NVAllocatorCallback &alloc) + : mAllocator(alloc) + { + mImpl = (MutexImpl *)QT3DS_ALLOC(alloc, MutexImpl::size, "MutexImpl"); + QT3DS_PLACEMENT_NEW(mImpl, MutexImpl)(); + } + + /** + The destructor for Mutex deletes the mutex. + */ + ~Mutex() + { + mImpl->~MutexImpl(); + QT3DS_FREE(mAllocator, mImpl); + } + + /** + Acquire (lock) the mutex. If the mutex is already locked + by another thread, this method blocks until the mutex is + unlocked. + */ + bool lock() const { return mImpl->lock(); } + + /** + Acquire (lock) the mutex. If the mutex is already locked + by another thread, this method returns false without blocking. + */ + bool trylock() const { return mImpl->trylock(); } + + /** + Release (unlock) the mutex. + */ + bool unlock() const { return mImpl->unlock(); } + + private: + NVAllocatorCallback &mAllocator; + MutexImpl *mImpl; + }; + + class QT3DS_FOUNDATION_API ReadWriteLock : private NoCopy + { + public: + ReadWriteLock(NVAllocatorCallback &alloc); + ~ReadWriteLock(); + + void lockReader(); + void lockWriter(); + + void unlockReader(); + void unlockWriter(); + + private: + class ReadWriteLockImpl *mImpl; + }; + + class ScopedReadLock : private NoCopy + { + public: + QT3DS_INLINE ScopedReadLock(ReadWriteLock &lock) + : mLock(lock) + { + mLock.lockReader(); + } + QT3DS_INLINE ~ScopedReadLock() { mLock.unlockReader(); } + + private: + ReadWriteLock &mLock; + }; + + class ScopedWriteLock : private NoCopy + { + public: + QT3DS_INLINE ScopedWriteLock(ReadWriteLock &lock) + : mLock(lock) + { + mLock.lockWriter(); + } + QT3DS_INLINE ~ScopedWriteLock() { mLock.unlockWriter(); } + + private: + ReadWriteLock &mLock; + }; + +/* + * Use this type of lock for mutex behaviour that must operate on SPU and PPU + * On non-PS3 platforms, it is implemented using Mutex + */ +#ifndef QT3DS_PS3 + + class AtomicLock : private NoCopy + { + Mutex mMutex; + + public: + AtomicLock(NVAllocatorCallback &alloc) + : mMutex(alloc) + { + } + + bool lock() { return mMutex.lock(); } + + bool trylock() { return mMutex.trylock(); } + + bool unlock() { return mMutex.unlock(); } + }; + + class AtomicLockCopy + { + AtomicLock *pLock; + + public: + AtomicLockCopy() + : pLock(NULL) + { + } + + AtomicLockCopy &operator=(AtomicLock &lock) + { + pLock = &lock; + return *this; + } + + bool lock() { return pLock->lock(); } + + bool trylock() { return pLock->trylock(); } + + bool unlock() { return pLock->unlock(); } + }; +#else + struct AtomicLockImpl + { + QT3DS_ALIGN(128, QT3DSU32 m_Lock); + QT3DSI32 m_LockId; + QT3DSU32 m_LockCount; + + AtomicLockImpl(); + }; + class AtomicLock //: private NoCopy + { + friend class AtomicLockCopy; + AtomicLockImpl *m_pImpl; + + public: + AtomicLock(); + + ~AtomicLock(); + + bool lock(); + + bool trylock(); + + bool unlock(); + }; + + // if an AtomicLock is copied and then the copy goes out of scope, it'll delete the atomic + // primitive + // (just a 128-byte aligned int) and cause a crash when it tries to delete it again + // This class just uses the atomic primitive without releasing it in the end. + + class AtomicLockCopy + { + AtomicLockImpl *m_pImpl; + + public: + AtomicLockCopy() + : m_pImpl(NULL) + { + } + + AtomicLockCopy(const AtomicLock &lock) + : m_pImpl(lock.m_pImpl) + { + } + + ~AtomicLockCopy() {} + + AtomicLockCopy &operator=(const AtomicLock &lock) + { + m_pImpl = lock.m_pImpl; + return *this; + } + + bool lock(); + + bool trylock(); + + bool unlock(); + }; +#endif + +#ifndef QT3DS_PS3 + + class AtomicRwLock : private NoCopy + { + ReadWriteLock m_Lock; + + public: + AtomicRwLock(NVAllocatorCallback &alloc) + : m_Lock(alloc) + { + } + + void lockReader() { m_Lock.lockReader(); } + void lockWriter() { m_Lock.lockWriter(); } + + bool tryLockReader() + { + // Todo - implement this + m_Lock.lockReader(); + return true; + } + + void unlockReader() { m_Lock.unlockReader(); } + void unlockWriter() { m_Lock.unlockWriter(); } + }; +#else + + struct AtomicRwLockImpl + { + QT3DS_ALIGN(128, volatile QT3DSU32 m_Lock); + QT3DS_ALIGN(128, volatile QT3DSU32 m_ReadCounter); + QT3DSI32 m_LockId; + QT3DSU32 m_LockCount; + + AtomicRwLockImpl(); + }; + + class AtomicRwLock : private NoCopy + { + AtomicRwLockImpl *m_pImpl; + + public: + AtomicRwLock(); + + ~AtomicRwLock(); + + void lockReader(); + + bool tryLockReader(); + + void lockWriter(); + + void unlockReader(); + + void unlockWriter(); + }; + +#endif + + class ScopedAtomicLock : private NoCopy + { + QT3DS_INLINE ScopedAtomicLock(AtomicLock &lock) + : mLock(lock) + { + mLock.lock(); + } + QT3DS_INLINE ~ScopedAtomicLock() { mLock.unlock(); } + + private: + AtomicLock &mLock; + }; + +} // namespace foundation +} // namespace qt3ds + +#endif diff --git a/src/foundation/Qt3DSNoCopy.h b/src/foundation/Qt3DSNoCopy.h new file mode 100644 index 0000000..817f5e1 --- /dev/null +++ b/src/foundation/Qt3DSNoCopy.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_PSNOCOPY_H +#define QT3DS_FOUNDATION_PSNOCOPY_H + +#include "foundation/Qt3DS.h" + +namespace qt3ds { +namespace foundation { + class NoCopy + { + NoCopy(const NoCopy &c); + NoCopy &operator=(const NoCopy &c); + + public: + NoCopy() {} + }; + +} // namespace foundation +} // namespace qt3ds + +#endif diff --git a/src/foundation/Qt3DSOption.h b/src/foundation/Qt3DSOption.h new file mode 100644 index 0000000..18d6c3b --- /dev/null +++ b/src/foundation/Qt3DSOption.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_OPTION_H +#define QT3DS_FOUNDATION_OPTION_H + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSAssert.h" + +namespace qt3ds { +namespace foundation { + + struct Empty + { + }; + + template <typename TDataType> + class Option + { + TDataType mData; + bool mHasValue; + + public: + Option(const TDataType &data) + : mData(data) + , mHasValue(true) + { + } + Option(const Empty &) + : mHasValue(false) + { + } + Option() + : mHasValue(false) + { + } + Option(const Option &other) + : mData(other.mData) + , mHasValue(other.mHasValue) + { + } + Option &operator=(const Option &other) + { + mData = other.mData; + mHasValue = other.mHasValue; + return *this; + } + + bool isEmpty() const { return !mHasValue; } + void setEmpty() { mHasValue = false; } + bool hasValue() const { return mHasValue; } + + const TDataType &getValue() const + { + QT3DS_ASSERT(mHasValue); + return mData; + } + TDataType &getValue() + { + QT3DS_ASSERT(mHasValue); + return mData; + } + TDataType &unsafeGetValue() { return mData; } + + operator const TDataType &() const { return getValue(); } + operator TDataType &() { return getValue(); } + + const TDataType *operator->() const { return &getValue(); } + TDataType *operator->() { return &getValue(); } + + const TDataType &operator*() const { return getValue(); } + TDataType &operator*() { return getValue(); } + }; +} +} + +#endif
\ No newline at end of file diff --git a/src/foundation/Qt3DSPerfTimer.cpp b/src/foundation/Qt3DSPerfTimer.cpp new file mode 100644 index 0000000..06fb5d3 --- /dev/null +++ b/src/foundation/Qt3DSPerfTimer.cpp @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "foundation/Qt3DSPerfTimer.h" +#include "foundation/Qt3DSMutex.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "EASTL/hash_map.h" +#include "EASTL/string.h" +#include "EASTL/vector.h" +#include "EASTL/sort.h" + +using namespace qt3ds::foundation; +using namespace qt3ds; + +namespace { +struct STimerEntry +{ + QT3DSU64 m_Total; + QT3DSU64 m_Max; + QT3DSU32 m_UpdateCount; + CRegisteredString m_Tag; + size_t m_Order; + + STimerEntry(CRegisteredString tag, size_t order) + : m_Total(0) + , m_Max(0) + , m_UpdateCount(0) + , m_Tag(tag) + , m_Order(order) + { + } + void Update(QT3DSU64 increment) + { + m_Total += increment; + m_Max = increment > m_Max ? increment : m_Max; + ++m_UpdateCount; + } + + void Output(NVFoundationBase &fnd, QT3DSU32 inFramesPassed) + { + Q_UNUSED(fnd) + if (m_Total) { + QT3DSU64 tensNanos = Time::sCounterFreq.toTensOfNanos(m_Total); + QT3DSU64 maxNanos = Time::sCounterFreq.toTensOfNanos(m_Max); + + double milliseconds = tensNanos / 100000.0; + double maxMilliseconds = maxNanos / 100000.0; + if (inFramesPassed == 0) + qCWarning(WARNING, PERF_INFO, "%s - %fms", m_Tag.c_str(), milliseconds); + else { + milliseconds /= inFramesPassed; + qCWarning(WARNING, PERF_INFO, "%s - %fms/frame-total %fms-max %u hits", + m_Tag.c_str(), milliseconds, maxMilliseconds, m_UpdateCount); + } + } + } + + void Reset() + { + m_Total = 0; + m_Max = 0; + m_UpdateCount = 0; + } + + bool operator<(const STimerEntry &other) const { return m_Order < other.m_Order; } +}; +struct SPerfTimer : public IPerfTimer +{ + typedef eastl::hash_map<CRegisteredString, STimerEntry> THashMapType; + NVFoundationBase &m_Foundation; + // This object needs its own string table because it is used during the binary load process with + // the application string table gets booted up. + NVScopedRefCounted<IStringTable> m_StringTable; + THashMapType m_Entries; + eastl::vector<STimerEntry> m_PrintEntries; + Mutex m_Mutex; + QT3DSI32 mRefCount; + + SPerfTimer(NVFoundationBase &fnd) + : m_Foundation(fnd) + , m_StringTable(IStringTable::CreateStringTable(fnd.getAllocator())) + , m_Mutex(fnd.getAllocator()) + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation.getAllocator()) + + void Update(const char *inId, QT3DSU64 inAmount) override + { + Mutex::ScopedLock __locker(m_Mutex); + CRegisteredString theStr(m_StringTable->RegisterStr(inId)); + THashMapType::iterator theFind = + m_Entries.insert(eastl::make_pair(theStr, STimerEntry(theStr, m_Entries.size()))).first; + theFind->second.Update(inAmount); + } + + // Dump current summation of timer data. + void OutputTimerData(QT3DSU32 inFramesPassed = 0) override + { + Mutex::ScopedLock __locker(m_Mutex); + m_PrintEntries.clear(); + for (THashMapType::iterator iter = m_Entries.begin(), end = m_Entries.end(); iter != end; + ++iter) { + m_PrintEntries.push_back(iter->second); + iter->second.Reset(); + } + + eastl::sort(m_PrintEntries.begin(), m_PrintEntries.end()); + + for (QT3DSU32 idx = 0, end = (QT3DSU32)m_PrintEntries.size(); idx < end; ++idx) { + m_PrintEntries[idx].Output(m_Foundation, inFramesPassed); + } + } + + void ResetTimerData() override + { + Mutex::ScopedLock __locker(m_Mutex); + for (THashMapType::iterator iter = m_Entries.begin(), end = m_Entries.end(); iter != end; + ++iter) { + iter->second.Reset(); + } + } + + virtual void ClearPerfKeys() + { + Mutex::ScopedLock __locker(m_Mutex); + m_Entries.clear(); + } +}; +} + +IPerfTimer &IPerfTimer::CreatePerfTimer(NVFoundationBase &fnd) +{ + return *QT3DS_NEW(fnd.getAllocator(), SPerfTimer)(fnd); +} diff --git a/src/foundation/Qt3DSPerfTimer.h b/src/foundation/Qt3DSPerfTimer.h new file mode 100644 index 0000000..c63091c --- /dev/null +++ b/src/foundation/Qt3DSPerfTimer.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_PERFTIMER_H +#define QT3DS_FOUNDATION_PERFTIMER_H + +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSTime.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/StringTable.h" + +namespace qt3ds { +namespace foundation { + + class IPerfTimer : public NVRefCounted + { + protected: + virtual ~IPerfTimer() {} + public: + // amount is in counter frequency units + virtual void Update(const char *inTag, QT3DSU64 inAmount) = 0; + // Dump current summation of timer data. + virtual void OutputTimerData(QT3DSU32 inFrameCount = 0) = 0; + virtual void ResetTimerData() = 0; + + static IPerfTimer &CreatePerfTimer(NVFoundationBase &inFoundation); + }; + + // Specialize this struct to get the perf timer in different contexts. + template <typename TTimerProvider> + struct STimerProvider + { + static IPerfTimer &getPerfTimer(TTimerProvider &inProvider) + { + return inProvider.getPerfTimer(); + } + }; + + template <typename TTimerProvider> + IPerfTimer &getPerfTimer(TTimerProvider &inProvider) + { + return STimerProvider<TTimerProvider>::getPerfTimer(inProvider); + } + + struct SStackPerfTimer + { + IPerfTimer *m_Timer; + QT3DSU64 m_Start; + const char *m_Id; + + SStackPerfTimer(IPerfTimer &destination, const char *inId) + : m_Timer(&destination) + , m_Start(Time::getCurrentCounterValue()) + , m_Id(inId) + { + } + + SStackPerfTimer(IPerfTimer *destination, const char *inId) + : m_Timer(destination) + , m_Start(Time::getCurrentCounterValue()) + , m_Id(inId) + { + } + + ~SStackPerfTimer() + { + if (m_Timer) { + QT3DSU64 theStop = Time::getCurrentCounterValue(); + QT3DSU64 theAmount = theStop - m_Start; + m_Timer->Update(m_Id, theAmount); + } + } + }; +} +} + +#define QT3DS_FOUNDATION_PERF_SCOPED_TIMER(context, name) \ + SStackPerfTimer __perfTimer(getPerfTimer(context), #name); + +#endif
\ No newline at end of file diff --git a/src/foundation/Qt3DSPlane.h b/src/foundation/Qt3DSPlane.h new file mode 100644 index 0000000..378c3bc --- /dev/null +++ b/src/foundation/Qt3DSPlane.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_PLANE_H +#define QT3DS_FOUNDATION_QT3DS_PLANE_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/Qt3DSMath.h" +#include "foundation/Qt3DSVec3.h" + +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif + +/** +\brief Representation of a plane. + + Plane equation used: n.dot(v) + d = 0 +*/ +class NVPlane +{ +public: + /** + \brief Constructor + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVPlane() {} + + /** + \brief Constructor from a normal and a distance + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVPlane(NVReal nx, NVReal ny, NVReal nz, NVReal distance) + : n(nx, ny, nz) + , d(distance) + { + } + + /** + \brief Constructor from a normal and a distance + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVPlane(const QT3DSVec3 &normal, NVReal distance) + : n(normal) + , d(distance) + { + } + + /** + \brief Constructor from a point on the plane and a normal + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVPlane(const QT3DSVec3 &point, const QT3DSVec3 &normal) + : n(normal) + , d(-point.dot(n)) // p satisfies normal.dot(p) + d = 0 + { + } + + /** + \brief Constructor from three points + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVPlane(const QT3DSVec3 &p0, const QT3DSVec3 &p1, const QT3DSVec3 &p2) + { + n = (p1 - p0).cross(p2 - p0).getNormalized(); + d = -p0.dot(n); + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal distance(const QT3DSVec3 &p) const { return p.dot(n) + d; } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool contains(const QT3DSVec3 &p) const + { + return NVAbs(distance(p)) < (1.0e-7f); + } + + /** + \brief projects p into the plane + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 project(const QT3DSVec3 &p) const + { + return p - n * distance(p); + } + + /** + \brief find an arbitrary point in the plane + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 pointInPlane() const { return -n * d; } + + /** + \brief equivalent plane with unit normal + */ + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void normalize() + { + NVReal denom = 1.0f / n.magnitude(); + n *= denom; + d *= denom; + } + + QT3DSVec3 n; //!< The normal to the plane + NVReal d; //!< The distance from the origin +}; + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** @} */ +#endif diff --git a/src/foundation/Qt3DSPool.h b/src/foundation/Qt3DSPool.h new file mode 100644 index 0000000..5bd07f8 --- /dev/null +++ b/src/foundation/Qt3DSPool.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_POOL_H +#define QT3DS_POOL_H +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSMath.h" //NVMax +#include "EASTL/vector.h" + +namespace qt3ds { +namespace foundation { + // Pool of fixed size objects. + template <typename TObjType, typename TAllocator = EASTLAllocatorType, + QT3DSU32 alignmentInBytes = 4, QT3DSU32 slabSize = 4096> + class Pool + { + protected: + TAllocator mAllocator; + struct Link + { + Link *mNext; + Link(Link *next = NULL) + : mNext(next) + { + } + }; + eastl::vector<void *, TAllocator> mSlabs; + Link *mFreeList; + + public: + Pool(const TAllocator &alloc = TAllocator()) + : mAllocator(alloc) + , mSlabs(alloc) + , mFreeList(NULL) + { + StaticAssert<sizeof(TObjType) < slabSize>::valid_expression(); + } + ~Pool() + { + for (QT3DSU32 idx = 0, end = mSlabs.size(); idx < end; ++idx) + mAllocator.deallocate(mSlabs[idx], slabSize); + mSlabs.clear(); + mFreeList = NULL; + } + void appendToFreeList(Link *inLink) + { + inLink->mNext = mFreeList; + mFreeList = inLink; + } + void appendSlabToFreeList(void *slab) + { + QT3DSU8 *newMem = reinterpret_cast<QT3DSU8 *>(slab); + QT3DSU32 objSize = (QT3DSU32)qt3ds::NVMax(sizeof(TObjType), sizeof(Link)); + // align the memory correctly + if (objSize % alignmentInBytes) + objSize += alignmentInBytes - (objSize % alignmentInBytes); + + QT3DSU32 numObjsInSlab = slabSize / objSize; + for (QT3DSU32 idx = 0; idx < numObjsInSlab; ++idx) { + QT3DSU8 *objPtr = newMem + idx * objSize; + appendToFreeList(reinterpret_cast<Link *>(objPtr)); + } + } + void allocateSlab(const char *file, int line) + { + QT3DSU32 objSize = (QT3DSU32)NVMax(sizeof(TObjType), sizeof(Link)); + // align the memory correctly + if (objSize % alignmentInBytes) + objSize += alignmentInBytes - (objSize % alignmentInBytes); + + QT3DSU8 *newMem = (QT3DSU8 *)mAllocator.allocate(slabSize, file, line); + if (newMem == NULL) + return; // out of mem, bad error + mSlabs.push_back(newMem); + appendSlabToFreeList(newMem); + } + + void *allocate(const char *file, int line) + { + if (!mFreeList) + allocateSlab(file, line); + + if (mFreeList) { + Link *retval = mFreeList; + mFreeList = retval->mNext; + return retval; + } + + return NULL; + } + + void deallocate(void *inPtr) + { +#if _DEBUG + // Ensure inPtr came from a known slab. + bool found = false; + for (QT3DSU32 idx = 0, end = mSlabs.size(); idx < end && !found; ++idx) { + QT3DSU8 *slabMem = reinterpret_cast<QT3DSU8 *>(mSlabs[idx]); + QT3DSU8 *slabEnd = slabMem + slabSize; + QT3DSU8 *memPtr = reinterpret_cast<QT3DSU8 *>(inPtr); + + if (memPtr >= mSlabs[idx] && memPtr < slabEnd) + found = true; + } + QT3DS_ASSERT(found); +#endif + appendToFreeList(reinterpret_cast<Link *>(inPtr)); + } + + template <typename TArg1, typename TArg2> + TObjType *construct(const TArg1 &arg1, const TArg2 &arg2, const char *file, int line) + { + TObjType *newMem = (TObjType *)allocate(file, line); + return new (newMem) TObjType(arg1, arg2); + } + + template <typename TArg1> + TObjType *construct(const TArg1 &arg1, const char *file, int line) + { + TObjType *newMem = (TObjType *)allocate(file, line); + return new (newMem) TObjType(arg1); + } + + TObjType *construct(const char *file, int line) + { + TObjType *newMem = (TObjType *)allocate(file, line); + return new (newMem) TObjType(); + } + }; +} +} +#endif diff --git a/src/foundation/Qt3DSPreprocessor.h b/src/foundation/Qt3DSPreprocessor.h new file mode 100644 index 0000000..371d3e6 --- /dev/null +++ b/src/foundation/Qt3DSPreprocessor.h @@ -0,0 +1,375 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_PREPROCESSOR_H +#define QT3DS_FOUNDATION_QT3DS_PREPROCESSOR_H + +#include <stddef.h> +/** \addtogroup foundation + @{ +*/ + +/** +List of preprocessor defines used to configure the SDK +- NDEBUG/_DEBUG: enable asserts (exactly one needs to be defined) +- QT3DS_CHECKED: enable run time checks, mostly unused or equiv. to _DEBUG +- QT3DS_SUPPORT_VISUAL_DEBUGGER: ... +- AG_PERFMON: ... +*/ + +/** +Compiler define +*/ +#ifdef _MSC_VER +#define QT3DS_VC +#if _MSC_VER >= 1600 +#define QT3DS_VC10 +#elif _MSC_VER >= 1500 +#define QT3DS_VC9 +#elif _MSC_VER >= 1400 +#define QT3DS_VC8 +#elif _MSC_VER >= 1300 +#define QT3DS_VC7 +#else +#define QT3DS_VC6 +#endif +#elif __GNUC__ || __SNC__ +#define QT3DS_GNUC +#elif defined(__MWERKS__) +#define QT3DS_CW +#elif defined(__ghs__) +#define QT3DS_GHS +#else +#error "Unknown compiler" +#endif + +/** +Platform define +*/ +#ifdef QT3DS_VC +#ifdef _M_IX86 +#define QT3DS_X86 +#define QT3DS_WINDOWS +#elif defined(_M_X64) +#define QT3DS_X64 +#define QT3DS_WINDOWS +#elif defined(_M_PPC) +#define QT3DS_PPC +#define QT3DS_X360 +#define QT3DS_VMX +#elif defined(_M_ARM) +#define QT3DS_ARM +#define QT3DS_WIN8ARM +#define QT3DS_ARM_NEON +#else +#error "Unknown platform" +#endif +#elif defined QT3DS_GNUC +#ifdef __CELLOS_LV2__ +#define QT3DS_PS3 +#define QT3DS_VMX +#elif defined(__arm__) +#define QT3DS_ARM +#if defined(__SNC__) +#define QT3DS_PSP2 +#endif +#if defined(__ARM_PCS_VFP) +#define QT3DS_ARM_HARDFP +#else +#define QT3DS_ARM_SOFTFP +#endif +#elif defined(__aarch64__) +#define QT3DS_ARM +#elif defined(__i386__) +#define QT3DS_X86 +#define QT3DS_VMX +#elif defined(__x86_64__) +#define QT3DS_X64 +#elif defined(__ppc__) +#define QT3DS_PPC +#elif defined(__ppc64__) +#define QT3DS_PPC +#define QT3DS_PPC64 +//# elif defined(__aarch64__) +//# define QT3DS_ARM_64 +#else +#error "Unknown platform" +#endif +#if defined(ANDROID) +#define QT3DS_ANDROID +#elif defined(__linux__) +#define QT3DS_LINUX +#elif defined(__APPLE__) +#define QT3DS_APPLE +#if defined(__arm__) +#define QT3DS_APPLE_IOS +#endif +#elif defined(__CYGWIN__) +#define QT3DS_CYGWIN +#define QT3DS_LINUX +#elif defined(__QNX__) +#define QT3DS_QNX +#elif defined(_WIN32) +#define QT3DS_WINDOWS +#else +#error "Unkown OS" +#endif +#elif defined QT3DS_CW +#if defined(__PPCGEKKO__) +#if defined(RVL) +#define QT3DS_WII +#else +#define QT3DS_GC +#endif +#else +#error "Unknown platform" +#endif +#elif defined QT3DS_GHS +#define QT3DS_LINUX // INTEGRITY deviations flagged with __INTEGRITY +#if defined(__arm__) || defined(__aarch64__) || defined(__ARM64__) +#define QT3DS_ARM +#endif +#endif + +/** +DLL export macros +*/ +#ifndef QT3DS_C_EXPORT +#define QT3DS_C_EXPORT extern "C" +#endif + +/** +Define API function declaration + +QT3DS_FOUNDATION_EXPORTS - used by the DLL library (PhysXCommon) to export the API +QT3DS_FOUNDATION_NO_EXPORTS - exists because there are windows configurations where + the QT3DS_FOUNDATION_API is linked through standard +static linking +no definition - this will allow DLLs and libraries to use the exported API from PhysXCommon + +*/ +#if defined(QT3DS_WINDOWS) && !defined(__CUDACC__) +#if defined QT3DS_FOUNDATION_EXPORTS +#define QT3DS_FOUNDATION_API __declspec(dllexport) +#elif defined QT3DS_FOUNDATION_NO_EXPORTS +#define QT3DS_FOUNDATION_API +#else +#define QT3DS_FOUNDATION_API __declspec(dllimport) +#endif +#else +#define QT3DS_FOUNDATION_API +#endif + + +#if defined(QT3DS_AUTOTESTS_ENABLED) +#include <qglobal.h> +#if defined(QT3DS_BUILDING_LIBRARY) +#define QT3DS_AUTOTEST_EXPORT Q_DECL_EXPORT +#else +#define QT3DS_AUTOTEST_EXPORT Q_DECL_IMPORT +#endif +#else +#define QT3DS_AUTOTEST_EXPORT +#endif + +/** +Calling convention +*/ +#ifndef QT3DS_CALL_CONV +#if defined QT3DS_WINDOWS +#define QT3DS_CALL_CONV __cdecl +#else +#define QT3DS_CALL_CONV +#endif +#endif + +/** +Pack macros - disabled on SPU because they are not supported +*/ +#if defined(QT3DS_VC) +#define QT3DS_PUSH_PACK_DEFAULT __pragma(pack(push, 8)) +#define QT3DS_POP_PACK __pragma(pack(pop)) +#elif defined(QT3DS_GNUC) && !defined(__SPU__) +#define QT3DS_PUSH_PACK_DEFAULT _Pragma("pack(push, 8)") +#define QT3DS_POP_PACK _Pragma("pack(pop)") +#else +#define QT3DS_PUSH_PACK_DEFAULT +#define QT3DS_POP_PACK +#endif + +/** +Inline macro +*/ +#if defined(QT3DS_WINDOWS) || defined(QT3DS_X360) +#define QT3DS_INLINE inline +#ifdef QT3DS_VC +#pragma inline_depth(255) +#endif +#else +#define QT3DS_INLINE inline +#endif + +/** +Force inline macro +*/ +#if defined(QT3DS_VC) +#define QT3DS_FORCE_INLINE __forceinline +#elif defined(QT3DS_LINUX) \ + || defined(QT3DS_QNX) // Workaround; Fedora Core 3 do not agree with force inline and NVcPool +#define QT3DS_FORCE_INLINE inline +#elif defined(QT3DS_GNUC) +#define QT3DS_FORCE_INLINE inline __attribute__((always_inline)) +#else +#define QT3DS_FORCE_INLINE inline +#endif + +/** +Noinline macro +*/ +#if defined QT3DS_WINDOWS +#define QT3DS_NOINLINE __declspec(noinline) +#elif defined(QT3DS_GNUC) +#define QT3DS_NOINLINE __attribute__((noinline)) +#else +#define QT3DS_NOINLINE +#endif + +/*! restrict macro */ +#if __CUDACC__ +#define QT3DS_RESTRICT __restrict__ +#elif defined(QT3DS_GNUC) || defined(QT3DS_VC) +#define QT3DS_RESTRICT __restrict +#elif defined(QT3DS_CW) && __STDC_VERSION__ >= 199901L +#define QT3DS_RESTRICT restrict +#else +#define QT3DS_RESTRICT +#endif + +#if defined(QT3DS_WINDOWS) || defined(QT3DS_X360) +#define QT3DS_NOALIAS __declspec(noalias) +#else +#define QT3DS_NOALIAS +#endif + +/** +Alignment macros + +QT3DS_ALIGN_PREFIX and QT3DS_ALIGN_SUFFIX can be used for type alignment instead of aligning individual +variables as follows: +QT3DS_ALIGN_PREFIX(16) +struct A { +... +} QT3DS_ALIGN_SUFFIX(16); +This declaration style is parsed correctly by Visual Assist. + +*/ +#ifndef QT3DS_ALIGN +#if defined(QT3DS_VC) +#define QT3DS_ALIGN(alignment, decl) __declspec(align(alignment)) decl +#define QT3DS_ALIGN_PREFIX(alignment) __declspec(align(alignment)) +#define QT3DS_ALIGN_SUFFIX(alignment) +#elif defined(QT3DS_GNUC) +#define QT3DS_ALIGN(alignment, decl) decl __attribute__((aligned(alignment))) +#define QT3DS_ALIGN_PREFIX(alignment) +#define QT3DS_ALIGN_SUFFIX(alignment) __attribute__((aligned(alignment))) +#elif defined(QT3DS_CW) +#define QT3DS_ALIGN(alignment, decl) decl __attribute__((aligned(alignment))) +#define QT3DS_ALIGN_PREFIX(alignment) +#define QT3DS_ALIGN_SUFFIX(alignment) __attribute__((aligned(alignment))) +#else +#define QT3DS_ALIGN(alignment, decl) +#define QT3DS_ALIGN_PREFIX(alignment) +#define QT3DS_ALIGN_SUFFIX(alignment) +#endif +#endif + +/** +Deprecated marco +*/ +#if 0 // set to 1 to create warnings for deprecated functions +#define QT3DS_DEPRECATED __declspec(deprecated) +#else +#define QT3DS_DEPRECATED +#endif + +// VC6 no '__FUNCTION__' workaround +#if defined QT3DS_VC6 && !defined __FUNCTION__ +#define __FUNCTION__ "Undefined" +#endif + +/** +General defines +*/ + +// Assertion type +template <bool b> +struct StaticAssert +{ +}; +// Specialisation with member function +template <> +struct StaticAssert<true> +{ +public: + static void valid_expression(){} +}; + +// static assert +#define QT3DS_COMPILE_TIME_ASSERT(exp) typedef char NVCompileTimeAssert_Dummy[(exp) ? 1 : -1] + +#ifdef QT3DS_GNUC +#define QT3DS_OFFSET_OF(X, Y) __builtin_offsetof(X, Y) +#else +#define QT3DS_OFFSET_OF(X, Y) offsetof(X, Y) +#endif + +// avoid unreferenced parameter warning (why not just disable it?) +// PT: or why not just omit the parameter's name from the declaration???? +#define QT3DS_FORCE_PARAMETER_REFERENCE(_P) (void)(_P); +#define QT3DS_UNUSED(_P) QT3DS_FORCE_PARAMETER_REFERENCE(_P) + +// check that exactly one of NDEBUG and _DEBUG is defined +#if !(defined NDEBUG ^ defined _DEBUG) +#error Exactly one of NDEBUG and _DEBUG needs to be defined by preprocessor +#endif + +// make sure QT3DS_CHECKED is defined in all _DEBUG configurations as well +#if !defined(QT3DS_CHECKED) && _DEBUG +#define QT3DS_CHECKED +#endif + +#ifdef __CUDACC__ +#define QT3DS_CUDA_CALLABLE __host__ __device__ +#else +#define QT3DS_CUDA_CALLABLE +#endif + +/** @} */ +#endif // QT3DS_FOUNDATION_QT3DS_PREPROCESSOR_H diff --git a/src/foundation/Qt3DSQuat.h b/src/foundation/Qt3DSQuat.h new file mode 100644 index 0000000..2792f97 --- /dev/null +++ b/src/foundation/Qt3DSQuat.h @@ -0,0 +1,381 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_QUAT_H +#define QT3DS_FOUNDATION_QT3DS_QUAT_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/Qt3DSVec3.h" + +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif + +/** +\brief This is a quaternion class. For more information on quaternion mathematics +consult a mathematics source on complex numbers. + +*/ + +class QT3DSQuat +{ +public: + /** + \brief Default constructor, does not do any initialization. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSQuat() {} + + /** + \brief Constructor. Take note of the order of the elements! + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSQuat(NVReal nx, NVReal ny, NVReal nz, NVReal nw) + : x(nx) + , y(ny) + , z(nz) + , w(nw) + { + } + + /** + \brief Creates from angle-axis representation. + + Axis must be normalized! + + Angle is in radians! + + <b>Unit:</b> Radians + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSQuat(NVReal angleRadians, const QT3DSVec3 &unitAxis) + { + QT3DS_ASSERT(NVAbs(1.0f - unitAxis.magnitude()) < 1e-3f); + const NVReal a = angleRadians * 0.5f; + const NVReal s = NVSin(a); + w = NVCos(a); + x = unitAxis.x * s; + y = unitAxis.y * s; + z = unitAxis.z * s; + } + + /** + \brief Copy ctor. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSQuat(const QT3DSQuat &v) + : x(v.x) + , y(v.y) + , z(v.z) + , w(v.w) + { + } + + /** + \brief Creates from orientation matrix. + + \param[in] m Rotation matrix to extract quaternion from. + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE explicit QT3DSQuat(const QT3DSMat33 &m); /* defined in Qt3DSMat33.h */ + + /** + \brief returns true if all elements are finite (not NAN or INF, etc.) + */ + QT3DS_CUDA_CALLABLE bool isFinite() const + { + return NVIsFinite(x) && NVIsFinite(y) && NVIsFinite(z) && NVIsFinite(w); + } + + /** + \brief returns true if finite and magnitude is close to unit + */ + + QT3DS_CUDA_CALLABLE bool isUnit() const + { + const NVReal unitTolerance = NVReal(1e-4); + return isFinite() && NVAbs(magnitude() - 1) < unitTolerance; + } + + /** + \brief returns true if finite and magnitude is reasonably close to unit to allow for some + accumulation of error vs isValid + */ + + QT3DS_CUDA_CALLABLE bool isSane() const + { + const NVReal unitTolerance = NVReal(1e-2); + return isFinite() && NVAbs(magnitude() - 1) < unitTolerance; + } + + /** + \brief converts this quaternion to angle-axis representation + */ + + QT3DS_CUDA_CALLABLE QT3DS_INLINE void toRadiansAndUnitAxis(NVReal &angle, QT3DSVec3 &axis) const + { + const NVReal quatEpsilon = (NVReal(1.0e-8f)); + const NVReal s2 = x * x + y * y + z * z; + if (s2 < quatEpsilon * quatEpsilon) // can't extract a sensible axis + { + angle = 0; + axis = QT3DSVec3(1, 0, 0); + } else { + const NVReal s = NVRecipSqrt(s2); + axis = QT3DSVec3(x, y, z) * s; + angle = w < quatEpsilon ? NVPi : NVAtan2(s2 * s, w) * 2; + } + } + + /** + \brief Gets the angle between this quat and the identity quaternion. + + <b>Unit:</b> Radians + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE NVReal getAngle() const { return NVAcos(w) * NVReal(2); } + + /** + \brief Gets the angle between this quat and the argument + + <b>Unit:</b> Radians + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE NVReal getAngle(const QT3DSQuat &q) const + { + return NVAcos(dot(q)) * NVReal(2); + } + + /** + \brief This is the squared 4D vector length, should be 1 for unit quaternions. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal magnitudeSquared() const + { + return x * x + y * y + z * z + w * w; + } + + /** + \brief returns the scalar product of this and other. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal dot(const QT3DSQuat &v) const + { + return x * v.x + y * v.y + z * v.z + w * v.w; + } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSQuat getNormalized() const + { + const NVReal s = 1.0f / magnitude(); + return QT3DSQuat(x * s, y * s, z * s, w * s); + } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE float magnitude() const { return NVSqrt(magnitudeSquared()); } + + // modifiers: + /** + \brief maps to the closest unit quaternion. + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE NVReal normalize() // convert this QT3DSQuat to a unit quaternion + { + const NVReal mag = magnitude(); + if (mag) { + const NVReal imag = NVReal(1) / mag; + + x *= imag; + y *= imag; + z *= imag; + w *= imag; + } + return mag; + } + + /* + \brief returns the conjugate. + + \note for unit quaternions, this is the inverse. + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSQuat getConjugate() const { return QT3DSQuat(-x, -y, -z, w); } + + /* + \brief returns imaginary part. + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec3 getImaginaryPart() const { return QT3DSVec3(x, y, z); } + + /** brief computes rotation of x-axis */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 getBasisVector0() const + { + // return rotate(QT3DSVec3(1,0,0)); + const QT3DSF32 x2 = x * 2.0f; + const QT3DSF32 w2 = w * 2.0f; + return QT3DSVec3((w * w2) - 1.0f + x * x2, (z * w2) + y * x2, (-y * w2) + z * x2); + } + + /** brief computes rotation of y-axis */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 getBasisVector1() const + { + // return rotate(QT3DSVec3(0,1,0)); + const QT3DSF32 y2 = y * 2.0f; + const QT3DSF32 w2 = w * 2.0f; + return QT3DSVec3((-z * w2) + x * y2, (w * w2) - 1.0f + y * y2, (x * w2) + z * y2); + } + + /** brief computes rotation of z-axis */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 getBasisVector2() const + { + // return rotate(QT3DSVec3(0,0,1)); + const QT3DSF32 z2 = z * 2.0f; + const QT3DSF32 w2 = w * 2.0f; + return QT3DSVec3((y * w2) + x * z2, (-x * w2) + y * z2, (w * w2) - 1.0f + z * z2); + } + + /** + rotates passed vec by this (assumed unitary) + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE const QT3DSVec3 rotate(const QT3DSVec3 &v) const + // QT3DS_CUDA_CALLABLE QT3DS_INLINE const QT3DSVec3 rotate(const QT3DSVec3& v) const + { + const QT3DSF32 vx = 2.0f * v.x; + const QT3DSF32 vy = 2.0f * v.y; + const QT3DSF32 vz = 2.0f * v.z; + const QT3DSF32 w2 = w * w - 0.5f; + const QT3DSF32 dot2 = (x * vx + y * vy + z * vz); + return QT3DSVec3((vx * w2 + (y * vz - z * vy) * w + x * dot2), + (vy * w2 + (z * vx - x * vz) * w + y * dot2), + (vz * w2 + (x * vy - y * vx) * w + z * dot2)); + /* + const QT3DSVec3 qv(x,y,z); + return (v*(w*w-0.5f) + (qv.cross(v))*w + qv*(qv.dot(v)))*2; + */ + } + + /** + inverse rotates passed vec by this (assumed unitary) + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE const QT3DSVec3 rotateInv(const QT3DSVec3 &v) const + // QT3DS_CUDA_CALLABLE QT3DS_INLINE const QT3DSVec3 rotateInv(const QT3DSVec3& v) const + { + const QT3DSF32 vx = 2.0f * v.x; + const QT3DSF32 vy = 2.0f * v.y; + const QT3DSF32 vz = 2.0f * v.z; + const QT3DSF32 w2 = w * w - 0.5f; + const QT3DSF32 dot2 = (x * vx + y * vy + z * vz); + return QT3DSVec3((vx * w2 - (y * vz - z * vy) * w + x * dot2), + (vy * w2 - (z * vx - x * vz) * w + y * dot2), + (vz * w2 - (x * vy - y * vx) * w + z * dot2)); + // const QT3DSVec3 qv(x,y,z); + // return (v*(w*w-0.5f) - (qv.cross(v))*w + qv*(qv.dot(v)))*2; + } + + /** + \brief Assignment operator + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSQuat &operator=(const QT3DSQuat &p) + { + x = p.x; + y = p.y; + z = p.z; + w = p.w; + return *this; + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSQuat &operator*=(const QT3DSQuat &q) + { + const NVReal tx = w * q.x + q.w * x + y * q.z - q.y * z; + const NVReal ty = w * q.y + q.w * y + z * q.x - q.z * x; + const NVReal tz = w * q.z + q.w * z + x * q.y - q.x * y; + + w = w * q.w - q.x * x - y * q.y - q.z * z; + x = tx; + y = ty; + z = tz; + + return *this; + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSQuat &operator+=(const QT3DSQuat &q) + { + x += q.x; + y += q.y; + z += q.z; + w += q.w; + return *this; + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSQuat &operator-=(const QT3DSQuat &q) + { + x -= q.x; + y -= q.y; + z -= q.z; + w -= q.w; + return *this; + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSQuat &operator*=(const NVReal s) + { + x *= s; + y *= s; + z *= s; + w *= s; + return *this; + } + + /** quaternion multiplication */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSQuat operator*(const QT3DSQuat &q) const + { + return QT3DSQuat(w * q.x + q.w * x + y * q.z - q.y * z, w * q.y + q.w * y + z * q.x - q.z * x, + w * q.z + q.w * z + x * q.y - q.x * y, w * q.w - x * q.x - y * q.y - z * q.z); + } + + /** quaternion addition */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSQuat operator+(const QT3DSQuat &q) const + { + return QT3DSQuat(x + q.x, y + q.y, z + q.z, w + q.w); + } + + /** quaternion subtraction */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSQuat operator-() const { return QT3DSQuat(-x, -y, -z, -w); } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSQuat operator-(const QT3DSQuat &q) const + { + return QT3DSQuat(x - q.x, y - q.y, z - q.z, w - q.w); + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSQuat operator*(NVReal r) const + { + return QT3DSQuat(x * r, y * r, z * r, w * r); + } + + static QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSQuat createIdentity() { return QT3DSQuat(0, 0, 0, 1); } + + /** the quaternion elements */ + NVReal x, y, z, w; +}; + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** @} */ +#endif // QT3DS_FOUNDATION_QT3DS_QUAT_H diff --git a/src/foundation/Qt3DSRefCounted.h b/src/foundation/Qt3DSRefCounted.h new file mode 100644 index 0000000..b500b5f --- /dev/null +++ b/src/foundation/Qt3DSRefCounted.h @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_REFCOUNTED_H +#define QT3DS_FOUNDATION_QT3DS_REFCOUNTED_H +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSNoCopy.h" + +namespace qt3ds { +namespace foundation { + /** + Marker class for objects that have a release method that is expected + to destroy or release the object. + */ + class NVReleasable + { + protected: + virtual ~NVReleasable() {} + public: + virtual void release() = 0; + }; + + template <typename TObjType> + inline void NVSafeRelease(TObjType *&item) + { + if (item) { + item->release(); + item = NULL; + } + } + + /**Scoped pointer that releases its data + when it is being destroyed*/ + template <typename TObjType> + struct NVScopedReleasable : public NoCopy + { + TObjType *mPtr; + NVScopedReleasable() + : mPtr(NULL) + { + } + NVScopedReleasable(TObjType *item) + : mPtr(item) + { + } + NVScopedReleasable(TObjType &item) + : mPtr(&item) + { + } + ~NVScopedReleasable() { NVSafeRelease(mPtr); } + + NVScopedReleasable &operator=(TObjType *inItem) + { + if (inItem != mPtr) { + if (mPtr) + mPtr->release(); + mPtr = inItem; + } + return *this; + } + + NVScopedReleasable &operator=(const NVScopedReleasable<TObjType> inItem) + { + QT3DS_ASSERT(false); + // try to do the right thing. + mPtr = inItem.mPtr; + const_cast<NVScopedReleasable<TObjType> &>(inItem).mPtr = NULL; + return *this; + } + + TObjType *forget_unsafe() + { + mPtr = NULL; + return mPtr; + } + + TObjType *operator->() { return mPtr; } + const TObjType *operator->() const { return mPtr; } + TObjType &operator*() { return *mPtr; } + const TObjType &operator*() const { return *mPtr; } + operator TObjType *() { return mPtr; } + operator const TObjType *() const { return mPtr; } + }; + + // Marker class for objects that are ref counted. + class NVRefCounted : public NVReleasable + { + public: + virtual void addRef() = 0; + }; + +/**Helpers to make implementing ref counted objects as concise as possible*/ +#define QT3DS_IMPLEMENT_REF_COUNT_RELEASE(alloc) \ + QT3DSI32 value = atomicDecrement(&mRefCount); \ + if (value <= 0) \ + NVDelete(alloc, this); + +#define QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(alloc) \ + void addRef() { atomicIncrement(&mRefCount); } \ + void release() { QT3DS_IMPLEMENT_REF_COUNT_RELEASE(alloc); } + + +#define QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(alloc) \ + void addRef() override { atomicIncrement(&mRefCount); } \ + void release() override { QT3DS_IMPLEMENT_REF_COUNT_RELEASE(alloc); } + + /**Safe function that checks for null before addrefing the object*/ + template <typename TObjType> + inline TObjType *NVSafeAddRef(TObjType *item) + { + if (item) { + item->addRef(); + } + return item; + } + + /**Scoped pointer that addref's its data upon acquisition and releases its data + when it is being destroyed*/ + template <typename TObjType> + struct NVScopedRefCounted + { + TObjType *mPtr; + ~NVScopedRefCounted() { NVSafeRelease(mPtr); } + NVScopedRefCounted(TObjType *item = NULL) + : mPtr(item) + { + NVSafeAddRef(mPtr); + } + NVScopedRefCounted(TObjType &item) + : mPtr(&item) + { + NVSafeAddRef(mPtr); + } + NVScopedRefCounted(const NVScopedRefCounted<TObjType> &other) + : mPtr(const_cast<TObjType *>(other.mPtr)) + { + NVSafeAddRef(mPtr); + } + NVScopedRefCounted<TObjType> &operator=(const NVScopedRefCounted<TObjType> &other) + { + if (other.mPtr != mPtr) { + NVSafeRelease(mPtr); + mPtr = const_cast<TObjType *>(other.mPtr); + NVSafeAddRef(mPtr); + } + return *this; + } + TObjType *forget_unsafe() + { + mPtr = NULL; + return mPtr; + } + + TObjType *operator->() { return mPtr; } + const TObjType *operator->() const { return mPtr; } + TObjType &operator*() { return *mPtr; } + const TObjType &operator*() const { return *mPtr; } + operator TObjType *() { return mPtr; } + operator const TObjType *() const { return mPtr; } + bool operator==(NVScopedRefCounted<TObjType> &inOther) const + { + return mPtr == inOther.mPtr; + } + bool operator!=(NVScopedRefCounted<TObjType> &inOther) const + { + return mPtr != inOther.mPtr; + } + }; +} +} + +#endif diff --git a/src/foundation/Qt3DSSemaphore.h b/src/foundation/Qt3DSSemaphore.h new file mode 100644 index 0000000..f58bb6e --- /dev/null +++ b/src/foundation/Qt3DSSemaphore.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_PSSEMAPHORE_H +#define QT3DS_FOUNDATION_PSSEMAPHORE_H + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSAllocatorCallback.h" + +namespace qt3ds { +namespace foundation { + class QT3DS_FOUNDATION_API Semaphore + { + public: + static const QT3DSU32 waitForever = 0xffffffff; + + Semaphore(NVAllocatorCallback &alloc, QT3DSU32 initialCount, QT3DSU32 maxCount); + + ~Semaphore(); + + /**Decrements (locks) the semaphore. If the semaphore's value is greater than zero, + * then the decrement proceeds, and the function returns, immediately. Otherwise + * Wait for at most the given number of ms. Returns true if the Semaphore is signaled. + * Semaphore::waitForever will block forever or until the semaphore is signaled. + */ + + bool wait(QT3DSU32 milliseconds = waitForever); + + /** increments (unlocks) the semaphore */ + + void post(); + + private: + class SemaphoreImpl *mImpl; + }; + +} // namespace foundation +} // namespace qt3ds + +#endif diff --git a/src/foundation/Qt3DSSimpleTypes.h b/src/foundation/Qt3DSSimpleTypes.h new file mode 100644 index 0000000..05ac8d9 --- /dev/null +++ b/src/foundation/Qt3DSSimpleTypes.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_FOUNDATION_QT3DS_SIMPLE_TYPES_H +#define QT3DS_FOUNDATION_QT3DS_SIMPLE_TYPES_H + +/** \addtogroup foundation + @{ +*/ + +// Platform specific types: +// Design note: Its OK to use int for general loop variables and temps. + +#include <QtCore/qglobal.h> +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSPreprocessor.h" +#include "EABase/eabase.h" +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif //#ifndef QT3DS_DOXYGEN + +typedef quint8 QT3DSU8; +typedef qint8 QT3DSI8; +typedef quint16 QT3DSU16; +typedef qint16 QT3DSI16; +typedef quint32 QT3DSU32; +typedef qint32 QT3DSI32; + +// Android's definition of GLuint64 as unsigned long (64-bits) requires this workaround +#if Q_PROCESSOR_WORDSIZE == 8 && (defined(Q_OS_ANDROID) || defined(Q_OS_INTEGRITY)) +typedef unsigned long QT3DSU64; +#else +typedef quint64 QT3DSU64; +#endif + +typedef qint64 QT3DSI64; +typedef float QT3DSF32; +typedef double QT3DSF64; +typedef QT3DSI32 IntBool; + +struct QT3DSF16 +{ + QT3DSU16 mData; +}; + +QT3DS_COMPILE_TIME_ASSERT(sizeof(QT3DSI8) == 1); +QT3DS_COMPILE_TIME_ASSERT(sizeof(QT3DSU8) == 1); +QT3DS_COMPILE_TIME_ASSERT(sizeof(QT3DSI16) == 2); +QT3DS_COMPILE_TIME_ASSERT(sizeof(QT3DSU16) == 2); +QT3DS_COMPILE_TIME_ASSERT(sizeof(QT3DSI32) == 4); +QT3DS_COMPILE_TIME_ASSERT(sizeof(QT3DSU32) == 4); +QT3DS_COMPILE_TIME_ASSERT(sizeof(QT3DSI64) == 8); +QT3DS_COMPILE_TIME_ASSERT(sizeof(QT3DSU64) == 8); + +// Type ranges +#define QT3DS_MAX_I8 127 // maximum possible sbyte value, 0x7f +#define QT3DS_MIN_I8 (-128) // minimum possible sbyte value, 0x80 +#define QT3DS_MAX_U8 255U // maximum possible ubyte value, 0xff +#define QT3DS_MIN_U8 0 // minimum possible ubyte value, 0x00 +#define QT3DS_MAX_I16 32767 // maximum possible sword value, 0x7fff +#define QT3DS_MIN_I16 (-32768) // minimum possible sword value, 0x8000 +#define QT3DS_MAX_U16 65535U // maximum possible uword value, 0xffff +#define QT3DS_MIN_U16 0 // minimum possible uword value, 0x0000 +#define QT3DS_MAX_I32 2147483647 // maximum possible sdword value, 0x7fffffff +#define QT3DS_MIN_I32 (-2147483647 - 1) // minimum possible sdword value, 0x80000000 +#define QT3DS_MAX_U32 4294967295U // maximum possible udword value, 0xffffffff +#define QT3DS_MIN_U32 0 // minimum possible udword value, 0x00000000 +#define QT3DS_MAX_F32 3.4028234663852885981170418348452e+38F +// maximum possible float value +#define QT3DS_MAX_F64 DBL_MAX // maximum possible double value + +#define QT3DS_ENV_F32 FLT_EPSILON // maximum relative error of float rounding +#define QT3DS_ENV_F64 DBL_EPSILON // maximum relative error of double rounding + +#ifndef QT3DS_FOUNDATION_USE_F64 + +typedef QT3DSF32 NVReal; + +#define QT3DS_MAX_REAL QT3DS_MAX_F32 +#define QT3DS_ENV_REAL QT3DS_ENV_F32 +#define QT3DS_NORMALIZATION_EPSILON NVReal(1e-20f) + +#else + +typedef QT3DSF64 NVReal; + +#define QT3DS_MAX_REAL QT3DS_MAX_F64 +#define QT3DS_ENV_REAL QT3DS_ENV_F64 +#define QT3DS_NORMALIZATION_EPSILON NVReal(1e-180) + +#endif + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** @} */ +#endif // QT3DS_FOUNDATION_QT3DS_SIMPLE_TYPES_H diff --git a/src/foundation/Qt3DSStringTokenizer.h b/src/foundation/Qt3DSStringTokenizer.h new file mode 100644 index 0000000..5229bab --- /dev/null +++ b/src/foundation/Qt3DSStringTokenizer.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_STRING_TOKENIZER_H +#define QT3DS_FOUNDATION_STRING_TOKENIZER_H + +#include "EASTL/string.h" + +namespace qt3ds { +namespace foundation { + + template <typename TStrType> + class QT3DS_FOUNDATION_API StringTokenizer + { + typedef typename eastl::basic_string<TStrType>::size_type size_t; + + public: + StringTokenizer(const eastl::basic_string<TStrType> &inString, + const eastl::basic_string<TStrType> &inToken) + { + eastl::basic_string<TStrType> mStr = inString; + eastl::basic_string<TStrType> theTempString = inString.substr(0, inToken.length()); + + while (inToken.length() && theTempString.compare(inToken) == 0) { + mStr = mStr.substr(inToken.length()); + theTempString = mStr.substr(0, inToken.length()); + } + + theTempString = mStr.substr(mStr.length() - 1 - inToken.length()); + while (theTempString.compare(inToken) == 0) { + mStr = mStr.substr(0, mStr.length() - inToken.length()); + theTempString = mStr.substr(mStr.length() - 1 - inToken.length()); + } + + m_OriginalString = mStr; + m_Token = inToken; + m_Index = 0; + } + + eastl::basic_string<TStrType> GetCurrentPartition() + { + eastl::basic_string<TStrType> theReturnString; + if (m_Index != eastl::basic_string<TStrType>::npos) { + size_t theCurrentTokenIndex = m_OriginalString.find(m_Token, m_Index); + if (theCurrentTokenIndex == eastl::basic_string<TStrType>::npos) { + theReturnString = m_OriginalString.substr(m_Index); + } else { + theReturnString = + m_OriginalString.substr(m_Index, theCurrentTokenIndex - m_Index); + } + } + + return theReturnString; + } + + bool HasNextPartition() { return m_Index != eastl::basic_string<TStrType>::npos; } + + void operator++() + { + if (m_Index != eastl::basic_string<TStrType>::npos) { + size_t theCurrentTokenIndex = m_OriginalString.find(m_Token, m_Index); + if (theCurrentTokenIndex == eastl::basic_string<TStrType>::npos) { + m_Index = eastl::basic_string<TStrType>::npos; + } else { + m_Index = theCurrentTokenIndex + m_Token.length(); + if (m_Index > m_OriginalString.length()) + m_Index = eastl::basic_string<TStrType>::npos; + } + } + } + + private: + size_t m_Index; + eastl::basic_string<TStrType> m_Token; + eastl::basic_string<TStrType> m_OriginalString; + }; + +} // namespace foundation +} // namespace qt3ds + +#endif diff --git a/src/foundation/Qt3DSSync.h b/src/foundation/Qt3DSSync.h new file mode 100644 index 0000000..a36afae --- /dev/null +++ b/src/foundation/Qt3DSSync.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_PSSYNC_H +#define QT3DS_FOUNDATION_PSSYNC_H + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSAllocatorCallback.h" + +namespace qt3ds { +namespace foundation { + class QT3DS_FOUNDATION_API Sync + { + public: + static const QT3DSU32 waitForever = 0xffffffff; + + Sync(NVAllocatorCallback &alloc); + + ~Sync(); + + /** Wait on the object for at most the given number of ms. Returns + * true if the object is signaled. Sync::waitForever will block forever + * or until the object is signaled. + */ + + bool wait(QT3DSU32 milliseconds = waitForever); + + /** Signal the synchronization object, waking all threads waiting on it */ + + void set(); + + /** Reset the synchronization object */ + + void reset(); + + private: + class SyncImpl *mImpl; + }; + +} // namespace foundation +} // namespace qt3ds + +#endif diff --git a/src/foundation/Qt3DSSystem.cpp b/src/foundation/Qt3DSSystem.cpp new file mode 100644 index 0000000..e87a25e --- /dev/null +++ b/src/foundation/Qt3DSSystem.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DSSystem.h" +#include "foundation/Qt3DSPreprocessor.h" +#include "EASTL/string.h" + +using namespace qt3ds; +using namespace qt3ds::foundation; + +#if defined(QT3DS_ANDROID) +const char *qt3ds::foundation::System::g_OS = "android"; +const char *qt3ds::foundation::System::g_DLLExtension = ".so"; +#elif defined(QT3DS_APPLE) +const char *qt3ds::foundation::System::g_OS = "osx"; +const char *qt3ds::foundation::System::g_DLLExtension = ".dylib"; +#elif defined(QT3DS_LINUX) +const char *qt3ds::foundation::System::g_OS = "linux"; +const char *qt3ds::foundation::System::g_DLLExtension = ".so"; +#elif defined(QT3DS_QNX) +const char *qt3ds::foundation::System::g_OS = "qnx"; +const char *qt3ds::foundation::System::g_DLLExtension = ".so"; +#elif defined(QT3DS_WINDOWS) +const char *qt3ds::foundation::System::g_OS = "windows"; +const char *qt3ds::foundation::System::g_DLLExtension = ".dll"; +#else +#error "Unknown Operating System" +#endif + +#if defined(QT3DS_X86) +const char *qt3ds::foundation::System::g_Processor = "x86"; +const char *qt3ds::foundation::System::g_BitWidth = "32"; +const char *qt3ds::foundation::System::g_FloatingPointModel = ""; +#elif defined(QT3DS_X64) +const char *qt3ds::foundation::System::g_Processor = "x64"; +const char *qt3ds::foundation::System::g_BitWidth = "64"; +const char *qt3ds::foundation::System::g_FloatingPointModel = ""; +#elif defined(QT3DS_ARM) +#if defined(__aarch64__) || defined(__ARM64__) +const char *qt3ds::foundation::System::g_Processor = "arm"; +const char *qt3ds::foundation::System::g_BitWidth = "64"; +const char *qt3ds::foundation::System::g_FloatingPointModel = "softfp"; +#else +const char *qt3ds::foundation::System::g_Processor = "arm"; +const char *qt3ds::foundation::System::g_BitWidth = "32"; +#if defined(QT3DS_ARM_HARDFP) +const char *qt3ds::foundation::System::g_FloatingPointModel = "hardfp"; +#elif defined(QT3DS_ARM_SOFTFP) +const char *qt3ds::foundation::System::g_FloatingPointModel = "softfp"; +#else +#error "Unknown floating point model!" +#endif +#endif +#else +#error "Unknown Platform" +#endif + +#if defined(QT3DS_ARM) +#if defined(QT3DS_GRAPHICS_API_GLES2) +const char *qt3ds::foundation::System::g_GPUType = "gles2"; +#elif defined(QT3DS_GRAPHICS_API_GL) +const char *qt3ds::foundation::System::g_GPUType = "gl"; +#elif defined(QT3DS_GRAPHICS_API_GLES3) +const char *qt3ds::foundation::System::g_GPUType = "gles3"; +#else +#error \ + "Must define a GPU type for arm platforms (QT3DS_GRAPHICS_API_GLES2, QT3DS_GRAPHICS_API_GLES3, QT3DS_GRAPHICS_API_GL)" +#endif +#elif defined(QT3DS_X86) +const char *qt3ds::foundation::System::g_GPUType = ""; +#elif defined(QT3DS_X64) +const char *qt3ds::foundation::System::g_GPUType = ""; +#else +#error "Must define a processor type (QT3DS_ARM or QT3DS_X86)" +#endif + +namespace { +static const unsigned SYSTEM_STR_SIZE = 100; +void SystemAppendString(eastl::string &str, const char *delim, const char *string) +{ + if (string && *string) { + str.append(delim); + str.append(string); + } +} +} +const char *System::getPlatformStr() +{ + static char text[SYSTEM_STR_SIZE]; + { + eastl::string str(g_Processor); + SystemAppendString(str, "_", g_BitWidth); + SystemAppendString(str, "_", g_FloatingPointModel); + SystemAppendString(str, "_", g_OS); + strcpy(text, str.c_str()); + } + return text; +} + +const char *System::getPlatformGLStr() +{ + static char text[SYSTEM_STR_SIZE]; + { + eastl::string str(g_Processor); + SystemAppendString(str, "_", g_BitWidth); + SystemAppendString(str, "_", g_FloatingPointModel); + SystemAppendString(str, "_", g_GPUType); + SystemAppendString(str, "_", g_OS); + strcpy(text, str.c_str()); + } + return text; +}
\ No newline at end of file diff --git a/src/foundation/Qt3DSSystem.h b/src/foundation/Qt3DSSystem.h new file mode 100644 index 0000000..84ca87d --- /dev/null +++ b/src/foundation/Qt3DSSystem.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once +#ifndef QT3DS_FOUNDATION_SYSTEM_H +#define QT3DS_FOUNDATION_SYSTEM_H + +#include "foundation/Qt3DS.h" + +namespace qt3ds { +namespace foundation { + class QT3DS_FOUNDATION_API System + { + public: + static const char *g_OS; + static const char *g_Processor; + static const char *g_BitWidth; + static const char *g_FloatingPointModel; + static const char *g_GPUType; + static const char *g_DLLExtension; + static const char *getPlatformStr(); + static const char *getPlatformGLStr(); + + private: + System(); + }; + +} // namespace foundation +} // namespace qt3ds + +#endif diff --git a/src/foundation/Qt3DSThread.h b/src/foundation/Qt3DSThread.h new file mode 100644 index 0000000..84e169d --- /dev/null +++ b/src/foundation/Qt3DSThread.h @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_PSTHREAD_H +#define QT3DS_FOUNDATION_PSTHREAD_H + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSFoundation.h" + +// dsequeira: according to existing comment here (David Black would be my guess) +// "This is useful to reduce bus contention on tight spin locks. And it needs +// to be a macro as the xenon compiler often ignores even __forceinline." What's not +// clear is why a pause function needs inlining...? (TODO: check with XBox team) + +// todo: these need to go somewhere else + +#if defined(QT3DS_WINDOWS) +#define NVSpinLockPause() __asm pause +#elif defined(QT3DS_X360) +#define NVSpinLockPause() __asm nop +#elif defined(QT3DS_LINUX) || defined(QT3DS_ANDROID) || defined(QT3DS_APPLE) || defined(QT3DS_QNX) +#define NVSpinLockPause() asm("nop") +#elif defined(QT3DS_PS3) +#define NVSpinLockPause() asm("nop") // don't know if it's correct yet... +#define QT3DS_TLS_MAX_SLOTS 64 +#elif defined(QT3DS_WII) +#define NVSpinLockPause() asm { nop } // don't know if it's correct yet... +#endif + +namespace qt3ds { +namespace foundation { + struct ThreadPriority // todo: put in some other header file + { + enum Enum { + /** + \brief High priority + */ + eHIGH = 0, + + /** + \brief Above Normal priority + */ + eABOVE_NORMAL = 1, + + /** + \brief Normal/default priority + */ + eNORMAL = 2, + + /** + \brief Below Normal priority + */ + eBELOW_NORMAL = 3, + + /** + \brief Low priority. + */ + eLOW = 4, + + eFORCE_DWORD = 0xffFFffFF + }; + }; + + /** + Thread abstraction API + */ + + class QT3DS_FOUNDATION_API Thread + { + public: + static const QT3DSU32 DEFAULT_STACK_SIZE; + typedef size_t Id; // space for a pointer or an integer + typedef void *(*ExecuteFn)(void *); + + static Id getId(); + + /** + Construct (but do not start) the thread object. Executes in the context + of the spawning thread + */ + + Thread(NVFoundationBase &foundation); + + /** + Construct and start the the thread, passing the given arg to the given fn. (pthread style) + */ + + Thread(NVFoundationBase &foundation, ExecuteFn fn, void *arg); + + /** + Deallocate all resources associated with the thread. Should be called in the + context of the spawning thread. + */ + + virtual ~Thread(); + + /** + start the thread running. Called in the context of the spawning thread. + */ + + void start(QT3DSU32 stackSize); + + /** + Violently kill the current thread. Blunt instrument, not recommended since + it can leave all kinds of things unreleased (stack, memory, mutexes...) Should + be called in the context of the spawning thread. + */ + + void kill(); + + /** + The virtual execute() method is the user defined function that will + run in the new thread. Called in the context of the spawned thread. + */ + + virtual void execute(void); + + /** + stop the thread. Signals the spawned thread that it should stop, so the + thread should check regularly + */ + + void signalQuit(); + + /** + Wait for a thread to stop. Should be called in the context of the spawning + thread. Returns false if the thread has not been started. + */ + + bool waitForQuit(); + + /** + check whether the thread is signalled to quit. Called in the context of the + spawned thread. + */ + + bool quitIsSignalled(); + + /** + Cleanly shut down this thread. Called in the context of the spawned thread. + */ + void quit(); + + /** + Change the affinity mask for this thread. + On Xbox360, sets the hardware thread to the first non-zero bit. + + Returns previous mask if successful, or zero on failure + */ + virtual QT3DSU32 setAffinityMask(QT3DSU32 mask); + + static ThreadPriority::Enum getPriority(Id threadId); + + /** Set thread priority. */ + void setPriority(ThreadPriority::Enum prio); + + /** set the thread's name */ + void setName(const char *name); + + /** Put the current thread to sleep for the given number of milliseconds */ + static void sleep(QT3DSU32 ms); + + /** Yield the current thread's slot on the CPU */ + static void yield(); + + private: + class ThreadImpl *mImpl; + }; + + QT3DS_FOUNDATION_API QT3DSU32 TlsAlloc(); + QT3DS_FOUNDATION_API void TlsFree(QT3DSU32 index); + QT3DS_FOUNDATION_API void *TlsGet(QT3DSU32 index); + QT3DS_FOUNDATION_API QT3DSU32 TlsSet(QT3DSU32 index, void *value); + +} // namespace foundation +} // namespace qt3ds + +#endif diff --git a/src/foundation/Qt3DSTime.h b/src/foundation/Qt3DSTime.h new file mode 100644 index 0000000..955bd7e --- /dev/null +++ b/src/foundation/Qt3DSTime.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_PSTIME_H +#define QT3DS_FOUNDATION_PSTIME_H + +#include "foundation/Qt3DS.h" + +#if defined(QT3DS_LINUX) || defined(QT3DS_ANDROID) || defined(QT3DS_QNX) +#include <time.h> +#endif + +namespace qt3ds { +namespace foundation { + + struct CounterFrequencyToTensOfNanos + { + QT3DSU64 mNumerator; + QT3DSU64 mDenominator; + CounterFrequencyToTensOfNanos(QT3DSU64 inNum, QT3DSU64 inDenom) + : mNumerator(inNum) + , mDenominator(inDenom) + { + } + + // quite slow. + QT3DSU64 toTensOfNanos(QT3DSU64 inCounter) const + { + return (inCounter * mNumerator) / mDenominator; + } + }; + + class QT3DS_FOUNDATION_API Time + { + public: + typedef double Second; + static const QT3DSU64 sNumTensOfNanoSecondsInASecond = 100000000; + // This is supposedly guaranteed to not change after system boot + // regardless of processors, speedstep, etc. + static const CounterFrequencyToTensOfNanos sCounterFreq; + + static CounterFrequencyToTensOfNanos getCounterFrequency(); + + static QT3DSU64 getCurrentCounterValue(); + + // SLOW!! + // Thar be a 64 bit divide in thar! + static QT3DSU64 getCurrentTimeInTensOfNanoSeconds() + { + QT3DSU64 ticks = getCurrentCounterValue(); + return sCounterFreq.toTensOfNanos(ticks); + } + + Time(); + Second getElapsedSeconds(); + Second peekElapsedSeconds(); + Second getLastTime() const; + + private: +#if defined(QT3DS_LINUX) || defined(QT3DS_ANDROID) || defined(QT3DS_APPLE) || defined(QT3DS_PSP2) \ + || defined(QT3DS_QNX) + Second mLastTime; +#else + QT3DSI64 mTickCount; +#endif + }; +} // namespace foundation +} // namespace qt3ds + +#endif diff --git a/src/foundation/Qt3DSTransform.h b/src/foundation/Qt3DSTransform.h new file mode 100644 index 0000000..0fee0a8 --- /dev/null +++ b/src/foundation/Qt3DSTransform.h @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_TRANSFORM_H +#define QT3DS_FOUNDATION_QT3DS_TRANSFORM_H +/** \addtogroup foundation + @{ +*/ + +#include "foundation/Qt3DSQuat.h" +#include "foundation/Qt3DSPlane.h" + +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif + +/*! +\brief class representing a rigid euclidean transform as a quaternion and a vector +*/ + +class NVTransform +{ +public: + QT3DSQuat q; + QT3DSVec3 p; + + //#define PXTRANSFORM_DEFAULT_CONSTRUCT_NAN + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVTransform() +#ifdef PXTRANSFORM_DEFAULT_CONSTRUCT_IDENTITY + : q(0, 0, 0, 1) + , p(0, 0, 0) +#elif defined(PXTRANSFORM_DEFAULT_CONSTRUCT_NAN) +#define invalid NVSqrt(-1.0f) + : q(invalid, invalid, invalid, invalid) + , p(invalid, invalid, invalid) +#undef invalid +#endif + { + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE explicit NVTransform(const QT3DSVec3 &position) + : q(0, 0, 0, 1) + , p(position) + { + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE explicit NVTransform(const QT3DSQuat &orientation) + : q(orientation) + , p(0, 0, 0) + { + QT3DS_ASSERT(orientation.isSane()); + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVTransform(const QT3DSVec3 &p0, const QT3DSQuat &q0) + : q(q0) + , p(p0) + { + QT3DS_ASSERT(q0.isSane()); + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE explicit NVTransform(const QT3DSMat44 &m); // defined in Qt3DSMat44.h + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVTransform operator*(const NVTransform &x) const + { + QT3DS_ASSERT(x.isSane()); + return transform(x); + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVTransform getInverse() const + { + QT3DS_ASSERT(isFinite()); + return NVTransform(q.rotateInv(-p), q.getConjugate()); + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 transform(const QT3DSVec3 &input) const + { + QT3DS_ASSERT(isFinite()); + return q.rotate(input) + p; + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 transformInv(const QT3DSVec3 &input) const + { + QT3DS_ASSERT(isFinite()); + return q.rotateInv(input - p); + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 rotate(const QT3DSVec3 &input) const + { + QT3DS_ASSERT(isFinite()); + return q.rotate(input); + } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 rotateInv(const QT3DSVec3 &input) const + { + QT3DS_ASSERT(isFinite()); + return q.rotateInv(input); + } + + //! Transform transform to parent (returns compound transform: first src, then *this) + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVTransform transform(const NVTransform &src) const + { + QT3DS_ASSERT(src.isSane()); + QT3DS_ASSERT(isSane()); + // src = [srct, srcr] -> [r*srct + t, r*srcr] + return NVTransform(q.rotate(src.p) + p, q * src.q); + } + + /** + \brief returns true if finite and q is a unit quaternion + */ + + QT3DS_CUDA_CALLABLE bool isValid() const { return p.isFinite() && q.isFinite() && q.isUnit(); } + + /** + \brief returns true if finite and quat magnitude is reasonably close to unit to allow for some + accumulation of error vs isValid + */ + + QT3DS_CUDA_CALLABLE bool isSane() const { return isFinite() && q.isSane(); } + + /** + \brief returns true if all elems are finite (not NAN or INF, etc.) + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool isFinite() const { return p.isFinite() && q.isFinite(); } + + //! Transform transform from parent (returns compound transform: first src, then this->inverse) + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVTransform transformInv(const NVTransform &src) const + { + QT3DS_ASSERT(src.isSane()); + QT3DS_ASSERT(isFinite()); + // src = [srct, srcr] -> [r^-1*(srct-t), r^-1*srcr] + QT3DSQuat qinv = q.getConjugate(); + return NVTransform(qinv.rotate(src.p - p), qinv * src.q); + } + + QT3DS_CUDA_CALLABLE static QT3DS_FORCE_INLINE NVTransform createIdentity() + { + return NVTransform(QT3DSVec3(0)); + } + + /** + \brief transform plane + */ + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVPlane transform(const NVPlane &plane) const + { + QT3DSVec3 transformedNormal = rotate(plane.n); + return NVPlane(transformedNormal, plane.d - p.dot(transformedNormal)); + } + + /** + \brief inverse-transform plane + */ + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVPlane inverseTransform(const NVPlane &plane) const + { + QT3DSVec3 transformedNormal = rotateInv(plane.n); + return NVPlane(transformedNormal, plane.d + p.dot(plane.n)); + } +}; + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** @} */ +#endif // QT3DS_FOUNDATION_QT3DS_TRANSFORM_H diff --git a/src/foundation/Qt3DSUnionCast.h b/src/foundation/Qt3DSUnionCast.h new file mode 100644 index 0000000..a3d0355 --- /dev/null +++ b/src/foundation/Qt3DSUnionCast.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_UNION_CAST_H +#define QT3DS_FOUNDATION_QT3DS_UNION_CAST_H + +#include "foundation/Qt3DSPreprocessor.h" + +/** \addtogroup foundation +@{ +*/ + +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif + +template <class A, class B> +QT3DS_FORCE_INLINE A NVUnionCast(B b) +{ + union AB { + AB(B bb) + : _b(bb) + { + } + B _b; + A _a; + } u(b); + return u._a; +} + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** @} */ + +#endif diff --git a/src/foundation/Qt3DSUtilities.h b/src/foundation/Qt3DSUtilities.h new file mode 100644 index 0000000..cb9ae6a --- /dev/null +++ b/src/foundation/Qt3DSUtilities.h @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_PSUTILITIES_H +#define QT3DS_FOUNDATION_PSUTILITIES_H + +#include "foundation/Qt3DSVec3.h" +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSIntrinsics.h" + +namespace qt3ds { +namespace foundation { + + // PT: checked casts + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSU16 to16(QT3DSU32 value) + { + QT3DS_ASSERT(value <= 0xffff); + return QT3DSU16(value); + } + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSU8 to8(QT3DSU16 value) + { + QT3DS_ASSERT(value <= 0xff); + return QT3DSU8(value); + } + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSU8 to8(QT3DSU32 value) + { + QT3DS_ASSERT(value <= 0xff); + return QT3DSU8(value); + } + + template <class T> + QT3DS_CUDA_CALLABLE QT3DS_INLINE void swap(T &x, T &y) + { +// crash with optimizations enabled, ticket in Sony +#ifdef QT3DS_PSP2 +#pragma control % push O = 0 +#endif + T tmp = x; + x = y; + y = tmp; +#ifdef QT3DS_PSP2 +#pragma control % pop O +#endif + } + + /*! +Get number of elements in array +*/ + template <typename T, size_t N> + char (&ArraySizeHelper(T (&array)[N]))[N]; +#define QT3DS_ARRAY_SIZE(_array) (sizeof(qt3ds::foundation::ArraySizeHelper(_array))) + + /*! +Sort two elements using operator< + +On return x will be the smaller of the two +*/ + template <class T> + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void order(T &x, T &y) + { + if (y < x) + swap(x, y); + } + + // most architectures can do predication on real comparisons, and on VMX, it matters + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void order(NVReal &x, NVReal &y) + { + NVReal newX = NVMin(x, y); + NVReal newY = NVMax(x, y); + x = newX; + y = newY; + } + + /*! + Sort two elements using operator< and also keep order + of any extra data + */ + template <class T, class E1> + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE void order(T &x, T &y, E1 &xe1, E1 &ye1) + { + if (y < x) { + swap(x, y); + swap(xe1, ye1); + } + } + + QT3DS_INLINE void debugBreak() + { +#if defined(QT3DS_WINDOWS) + __debugbreak(); +#elif defined(QT3DS_LINUX) || defined(QT3DS_ANDROID) || defined(QT3DS_QNX) + asm("int $3"); +#elif defined(QT3DS_GNUC) + __builtin_trap(); +#else + QT3DS_ASSERT(false); +#endif + } + + bool checkValid(const float &); + bool checkValid(const QT3DSVec3 &); + bool checkValid(const QT3DSQuat &); + bool checkValid(const QT3DSMat33 &); + bool checkValid(const NVTransform &); + bool checkValid(const char *); + + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSI32 getPadding2(size_t value, QT3DSU32 alignment) + { + const QT3DSI32 mask = alignment - 1; + const QT3DSI32 overhead = QT3DSI32(value) & mask; + return (alignment - overhead) & mask; + } + + // PT: "After doing a dcbz128, there is a delay of about 140 cycles before writes to that cache + // line can proceed without stalling. + // This is much faster than an L2 cache miss, but for ideal performance, it is best to avoid + // this stall by doing the cache-line + // zeroing a few cache lines ahead of where you are writing." + QT3DS_FORCE_INLINE void invalidateCache(void *QT3DS_RESTRICT voidPtr, QT3DSI32 size) + { +#ifdef QT3DS_X360 + QT3DSU8 *QT3DS_RESTRICT ptr = reinterpret_cast<QT3DSU8 *>(voidPtr); + const QT3DSI32 padding = getPadding2(size_t(ptr), 128); + const QT3DSI32 sizeToCover = size - padding; + if (sizeToCover >= 128) { + QT3DSU8 *ptr128 = ptr + padding; + QT3DSU32 nb128 = sizeToCover / 128; + while (nb128--) { + // NV::memZero128(ptr128); + qt3ds::foundation::memZero128(ptr128); + ptr128 += 128; + } + } +#else + (void)voidPtr; + (void)size; +#endif + } + + // equivalent to std::max_element + template <typename T> + inline const T *maxElement(const T *first, const T *last) + { + const T *m = first; + for (const T *it = first + 1; it < last; ++it) + if (*m < *it) + m = it; + + return m; + } + +} // namespace foundation +} // namespace qt3ds + +#define QT3DS_STRINGIZE_HELPER(X) #X +#define QT3DS_STRINGIZE(X) QT3DS_STRINGIZE_HELPER(X) + +#define QT3DS_CONCAT_HELPER(X, Y) X##Y +#define QT3DS_CONCAT(X, Y) QT3DS_CONCAT_HELPER(X, Y) + +#endif diff --git a/src/foundation/Qt3DSVec2.h b/src/foundation/Qt3DSVec2.h new file mode 100644 index 0000000..0d86c91 --- /dev/null +++ b/src/foundation/Qt3DSVec2.h @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_VEC2_H +#define QT3DS_FOUNDATION_QT3DS_VEC2_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/Qt3DSMath.h" + +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif + +/** +\brief 2 Element vector class. + +This is a vector class with public data members. +This is not nice but it has become such a standard that hiding the xy data members +makes it difficult to reuse external code that assumes that these are public in the library. +The vector class can be made to use float or double precision by appropriately defining NVReal. +This has been chosen as a cleaner alternative to a template class. +*/ +class QT3DSVec2 +{ +public: + /** + \brief default constructor leaves data uninitialized. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2() {} + + /** + \brief Assigns scalar parameter to all elements. + + Useful to initialize to zero or one. + + \param[in] a Value to assign to elements. + */ + explicit QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2(NVReal a) + : x(a) + , y(a) + { + } + + /** + \brief Initializes from 2 scalar parameters. + + \param[in] nx Value to initialize X component. + \param[in] ny Value to initialize Y component. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2(NVReal nx, NVReal ny) + : x(nx) + , y(ny) + { + } + + /** + \brief Copy ctor. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2(const QT3DSVec2 &v) + : x(v.x) + , y(v.y) + { + } + + // Operators + + /** + \brief Assignment operator + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2 &operator=(const QT3DSVec2 &p) + { + x = p.x; + y = p.y; + return *this; + } + + /** + \brief element access + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal &operator[](int index) + { + QT3DS_ASSERT(index >= 0 && index <= 1); + return (&x)[index]; + } + + /** + \brief element access + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE const NVReal &operator[](int index) const + { + QT3DS_ASSERT(index >= 0 && index <= 1); + return (&x)[index]; + } + + /** + \brief returns true if the two vectors are exactly equal. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool operator==(const QT3DSVec2 &v) const + { + return x == v.x && y == v.y; + } + + /** + \brief returns true if the two vectors are not exactly equal. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool operator!=(const QT3DSVec2 &v) const + { + return x != v.x || y != v.y; + } + + /** + \brief tests for exact zero vector + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool isZero() const { return x == 0.0f && y == 0.0f; } + + /** + \brief returns true if all 2 elems of the vector are finite (not NAN or INF, etc.) + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE bool isFinite() const { return NVIsFinite(x) && NVIsFinite(y); } + + /** + \brief is normalized - used by API parameter validation + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool isNormalized() const + { + const float unitTolerance = NVReal(1e-4); + return isFinite() && NVAbs(magnitude() - 1) < unitTolerance; + } + + /** + \brief returns the squared magnitude + + Avoids calling NVSqrt()! + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal magnitudeSquared() const { return x * x + y * y; } + + /** + \brief returns the magnitude + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal magnitude() const { return NVSqrt(magnitudeSquared()); } + + /** + \brief negation + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2 operator-() const { return QT3DSVec2(-x, -y); } + + /** + \brief vector addition + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2 operator+(const QT3DSVec2 &v) const + { + return QT3DSVec2(x + v.x, y + v.y); + } + + /** + \brief vector difference + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2 operator-(const QT3DSVec2 &v) const + { + return QT3DSVec2(x - v.x, y - v.y); + } + + /** + \brief scalar post-multiplication + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2 operator*(NVReal f) const + { + return QT3DSVec2(x * f, y * f); + } + + /** + \brief scalar division + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2 operator/(NVReal f) const + { + f = NVReal(1) / f; // PT: inconsistent notation with operator /= + return QT3DSVec2(x * f, y * f); + } + + /** + \brief vector addition + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2 &operator+=(const QT3DSVec2 &v) + { + x += v.x; + y += v.y; + return *this; + } + + /** + \brief vector difference + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2 &operator-=(const QT3DSVec2 &v) + { + x -= v.x; + y -= v.y; + return *this; + } + + /** + \brief scalar multiplication + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2 &operator*=(NVReal f) + { + x *= f; + y *= f; + return *this; + } + /** + \brief scalar division + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2 &operator/=(NVReal f) + { + f = 1.0f / f; // PT: inconsistent notation with operator / + x *= f; + y *= f; + return *this; + } + + /** + \brief returns the scalar product of this and other. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal dot(const QT3DSVec2 &v) const { return x * v.x + y * v.y; } + + /** return a unit vector */ + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2 getNormalized() const + { + const NVReal m = magnitudeSquared(); + return m > 0 ? *this * NVRecipSqrt(m) : QT3DSVec2(0, 0); + } + + /** + \brief normalizes the vector in place + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal normalize() + { + const NVReal m = magnitude(); + if (m > 0) + *this /= m; + return m; + } + + /** + \brief a[i] * b[i], for all i. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2 multiply(const QT3DSVec2 &a) const + { + return QT3DSVec2(x * a.x, y * a.y); + } + + /** + \brief element-wise minimum + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2 minimum(const QT3DSVec2 &v) const + { + return QT3DSVec2(NVMin(x, v.x), NVMin(y, v.y)); + } + + /** + \brief returns MIN(x, y); + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float minElement() const { return NVMin(x, y); } + + /** + \brief element-wise maximum + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec2 maximum(const QT3DSVec2 &v) const + { + return QT3DSVec2(NVMax(x, v.x), NVMax(y, v.y)); + } + + /** + \brief returns MAX(x, y); + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float maxElement() const { return NVMax(x, y); } + + NVReal x, y; +}; + +QT3DS_CUDA_CALLABLE static QT3DS_FORCE_INLINE QT3DSVec2 operator*(NVReal f, const QT3DSVec2 &v) +{ + return QT3DSVec2(f * v.x, f * v.y); +} + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** @} */ +#endif // QT3DS_FOUNDATION_QT3DS_VEC2_H diff --git a/src/foundation/Qt3DSVec3.h b/src/foundation/Qt3DSVec3.h new file mode 100644 index 0000000..24ab823 --- /dev/null +++ b/src/foundation/Qt3DSVec3.h @@ -0,0 +1,377 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_VEC3_H +#define QT3DS_FOUNDATION_QT3DS_VEC3_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/Qt3DSMath.h" + +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif + +/** +\brief 3 Element vector class. + +This is a vector class with public data members. +This is not nice but it has become such a standard that hiding the xyz data members +makes it difficult to reuse external code that assumes that these are public in the library. +The vector class can be made to use float or double precision by appropriately defining NVReal. +This has been chosen as a cleaner alternative to a template class. +*/ +class QT3DSVec3 +{ +public: + /** + \brief default constructor leaves data uninitialized. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3() {} + + /** + \brief Assigns scalar parameter to all elements. + + Useful to initialize to zero or one. + + \param[in] a Value to assign to elements. + */ + explicit QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3(NVReal a) + : x(a) + , y(a) + , z(a) + { + } + + /** + \brief Initializes from 3 scalar parameters. + + \param[in] nx Value to initialize X component. + \param[in] ny Value to initialize Y component. + \param[in] nz Value to initialize Z component. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3(NVReal nx, NVReal ny, NVReal nz) + : x(nx) + , y(ny) + , z(nz) + { + } + + /** + \brief Copy ctor. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3(const QT3DSVec3 &v) + : x(v.x) + , y(v.y) + , z(v.z) + { + } + + // Operators + + /** + \brief Assignment operator + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 &operator=(const QT3DSVec3 &p) + { + x = p.x; + y = p.y; + z = p.z; + return *this; + } + + /** + \brief element access + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal &operator[](int index) + { + QT3DS_ASSERT(index >= 0 && index <= 2); + return (&x)[index]; + } + + /** + \brief element access + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE const NVReal &operator[](int index) const + { + QT3DS_ASSERT(index >= 0 && index <= 2); + return (&x)[index]; + } + + /** + \brief returns true if the two vectors are exactly equal. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool operator==(const QT3DSVec3 &v) const + { + return x == v.x && y == v.y && z == v.z; + } + + /** + \brief returns true if the two vectors are not exactly equal. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool operator!=(const QT3DSVec3 &v) const + { + return x != v.x || y != v.y || z != v.z; + } + + /** + \brief tests for exact zero vector + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool isZero() const + { + return x == 0.0f && y == 0.0f && z == 0.0f; + } + + /** + \brief returns true if all 3 elems of the vector are finite (not NAN or INF, etc.) + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE bool isFinite() const + { + return NVIsFinite(x) && NVIsFinite(y) && NVIsFinite(z); + } + + /** + \brief is normalized - used by API parameter validation + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool isNormalized() const + { + const float unitTolerance = NVReal(1e-4); + return isFinite() && NVAbs(magnitude() - 1) < unitTolerance; + } + + /** + \brief returns the squared magnitude + + Avoids calling NVSqrt()! + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal magnitudeSquared() const + { + return x * x + y * y + z * z; + } + + /** + \brief returns the magnitude + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal magnitude() const { return NVSqrt(magnitudeSquared()); } + + /** + \brief negation + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 operator-() const { return QT3DSVec3(-x, -y, -z); } + + /** + \brief vector addition + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 operator+(const QT3DSVec3 &v) const + { + return QT3DSVec3(x + v.x, y + v.y, z + v.z); + } + + /** + \brief vector difference + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 operator-(const QT3DSVec3 &v) const + { + return QT3DSVec3(x - v.x, y - v.y, z - v.z); + } + + /** + \brief scalar post-multiplication + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 operator*(NVReal f) const + { + return QT3DSVec3(x * f, y * f, z * f); + } + + /** + \brief scalar division + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 operator/(NVReal f) const + { + f = NVReal(1) / f; // PT: inconsistent notation with operator /= + return QT3DSVec3(x * f, y * f, z * f); + } + + /** + \brief vector addition + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 &operator+=(const QT3DSVec3 &v) + { + x += v.x; + y += v.y; + z += v.z; + return *this; + } + + /** + \brief vector difference + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 &operator-=(const QT3DSVec3 &v) + { + x -= v.x; + y -= v.y; + z -= v.z; + return *this; + } + + /** + \brief scalar multiplication + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 &operator*=(NVReal f) + { + x *= f; + y *= f; + z *= f; + return *this; + } + /** + \brief scalar division + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 &operator/=(NVReal f) + { + f = 1.0f / f; // PT: inconsistent notation with operator / + x *= f; + y *= f; + z *= f; + return *this; + } + + /** + \brief returns the scalar product of this and other. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal dot(const QT3DSVec3 &v) const + { + return x * v.x + y * v.y + z * v.z; + } + + /** + \brief cross product + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 cross(const QT3DSVec3 &v) const + { + return QT3DSVec3(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x); + } + + /** return a unit vector */ + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 getNormalized() const + { + const NVReal m = magnitudeSquared(); + return m > 0 ? *this * NVRecipSqrt(m) : QT3DSVec3(0, 0, 0); + } + + /** + \brief normalizes the vector in place + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal normalize() + { + const NVReal m = magnitude(); + if (m > 0) + *this /= m; + return m; + } + + /** + \brief normalizes the vector in place. Does nothing if vector magnitude is under + QT3DS_NORMALIZATION_EPSILON. + Returns vector magnitude if >= QT3DS_NORMALIZATION_EPSILON and 0.0f otherwise. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal normalizeSafe() + { + const NVReal mag = magnitude(); + if (mag < QT3DS_NORMALIZATION_EPSILON) + return 0.0f; + *this *= NVReal(1) / mag; + return mag; + } + + /** + \brief normalizes the vector in place. Asserts if vector magnitude is under + QT3DS_NORMALIZATION_EPSILON. + returns vector magnitude. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE NVReal normalizeFast() + { + const NVReal mag = magnitude(); + QT3DS_ASSERT(mag >= QT3DS_NORMALIZATION_EPSILON); + *this *= NVReal(1) / mag; + return mag; + } + + /** + \brief a[i] * b[i], for all i. + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 multiply(const QT3DSVec3 &a) const + { + return QT3DSVec3(x * a.x, y * a.y, z * a.z); + } + + /** + \brief element-wise minimum + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 minimum(const QT3DSVec3 &v) const + { + return QT3DSVec3(NVMin(x, v.x), NVMin(y, v.y), NVMin(z, v.z)); + } + + /** + \brief returns MIN(x, y, z); + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float minElement() const { return NVMin(x, NVMin(y, z)); } + + /** + \brief element-wise maximum + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE QT3DSVec3 maximum(const QT3DSVec3 &v) const + { + return QT3DSVec3(NVMax(x, v.x), NVMax(y, v.y), NVMax(z, v.z)); + } + + /** + \brief returns MAX(x, y, z); + */ + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float maxElement() const { return NVMax(x, NVMax(y, z)); } + + NVReal x, y, z; +}; + +QT3DS_CUDA_CALLABLE static QT3DS_FORCE_INLINE QT3DSVec3 operator*(NVReal f, const QT3DSVec3 &v) +{ + return QT3DSVec3(f * v.x, f * v.y, f * v.z); +} + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** @} */ +#endif // QT3DS_FOUNDATION_QT3DS_VEC3_H diff --git a/src/foundation/Qt3DSVec4.h b/src/foundation/Qt3DSVec4.h new file mode 100644 index 0000000..acccd48 --- /dev/null +++ b/src/foundation/Qt3DSVec4.h @@ -0,0 +1,373 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_VEC4_H +#define QT3DS_FOUNDATION_QT3DS_VEC4_H +/** \addtogroup foundation +@{ +*/ +#include "foundation/Qt3DSMath.h" +#include "foundation/Qt3DSVec3.h" +#include "foundation/Qt3DSAssert.h" + +/** +\brief 4 Element vector class. + +This is a vector class with public data members. +This is not nice but it has become such a standard that hiding the xyz data members +makes it difficult to reuse external code that assumes that these are public in the library. +The vector class can be made to use float or double precision by appropriately defining NVReal. +This has been chosen as a cleaner alternative to a template class. +*/ +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif + +class QT3DSVec4 +{ +public: + /** + \brief default constructor leaves data uninitialized. + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4() {} + + /** + \brief Assigns scalar parameter to all elements. + + Useful to initialize to zero or one. + + \param[in] a Value to assign to elements. + */ + explicit QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4(NVReal a) + : x(a) + , y(a) + , z(a) + , w(a) + { + } + + /** + \brief Initializes from 3 scalar parameters. + + \param[in] nx Value to initialize X component. + \param[in] ny Value to initialize Y component. + \param[in] nz Value to initialize Z component. + \param[in] nw Value to initialize W component. + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4(NVReal nx, NVReal ny, NVReal nz, NVReal nw) + : x(nx) + , y(ny) + , z(nz) + , w(nw) + { + } + + /** + \brief Initializes from 3 scalar parameters. + + \param[in] v Value to initialize the X, Y, and Z components. + \param[in] nw Value to initialize W component. + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4(const QT3DSVec3 &v, NVReal nw) + : x(v.x) + , y(v.y) + , z(v.z) + , w(nw) + { + } + + /** + \brief Initializes from an array of scalar parameters. + + \param[in] v Value to initialize with. + */ + explicit QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4(const NVReal v[]) + : x(v[0]) + , y(v[1]) + , z(v[2]) + , w(v[3]) + { + } + + /** + \brief Copy ctor. + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4(const QT3DSVec4 &v) + : x(v.x) + , y(v.y) + , z(v.z) + , w(v.w) + { + } + + // Operators + + /** + \brief Assignment operator + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 &operator=(const QT3DSVec4 &p) + { + x = p.x; + y = p.y; + z = p.z; + w = p.w; + return *this; + } + + /** + \brief element access + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE NVReal &operator[](int index) + { + QT3DS_ASSERT(index >= 0 && index <= 3); + return (&x)[index]; + } + + /** + \brief element access + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE const NVReal &operator[](int index) const + { + QT3DS_ASSERT(index >= 0 && index <= 3); + return (&x)[index]; + } + + /** + \brief returns true if the two vectors are exactly equal. + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE bool operator==(const QT3DSVec4 &v) const + { + return x == v.x && y == v.y && z == v.z && w == v.w; + } + + /** + \brief returns true if the two vectors are not exactly equal. + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE bool operator!=(const QT3DSVec4 &v) const + { + return x != v.x || y != v.y || z != v.z || w != v.w; + } + + /** + \brief tests for exact zero vector + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE bool isZero() const { return x == 0 && y == 0 && z == 0 && w == 0; } + + /** + \brief returns true if all 3 elems of the vector are finite (not NAN or INF, etc.) + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE bool isFinite() const + { + return NVIsFinite(x) && NVIsFinite(y) && NVIsFinite(z) && NVIsFinite(w); + } + + /** + \brief is normalized - used by API parameter validation + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE bool isNormalized() const + { + const float unitTolerance = NVReal(1e-4); + return isFinite() && NVAbs(magnitude() - 1) < unitTolerance; + } + + /** + \brief returns the squared magnitude + + Avoids calling NVSqrt()! + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE NVReal magnitudeSquared() const + { + return x * x + y * y + z * z + w * w; + } + + /** + \brief returns the magnitude + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE NVReal magnitude() const { return NVSqrt(magnitudeSquared()); } + + /** + \brief negation + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 operator-() const { return QT3DSVec4(-x, -y, -z, -w); } + + /** + \brief vector addition + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 operator+(const QT3DSVec4 &v) const + { + return QT3DSVec4(x + v.x, y + v.y, z + v.z, w + v.w); + } + + /** + \brief vector difference + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 operator-(const QT3DSVec4 &v) const + { + return QT3DSVec4(x - v.x, y - v.y, z - v.z, w - v.w); + } + + /** + \brief scalar post-multiplication + */ + + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 operator*(NVReal f) const + { + return QT3DSVec4(x * f, y * f, z * f, w * f); + } + + /** + \brief scalar division + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 operator/(NVReal f) const + { + f = NVReal(1) / f; + return QT3DSVec4(x * f, y * f, z * f, w * f); + } + + /** + \brief vector addition + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 &operator+=(const QT3DSVec4 &v) + { + x += v.x; + y += v.y; + z += v.z; + w += v.w; + return *this; + } + + /** + \brief vector difference + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 &operator-=(const QT3DSVec4 &v) + { + x -= v.x; + y -= v.y; + z -= v.z; + w -= v.w; + return *this; + } + + /** + \brief scalar multiplication + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 &operator*=(NVReal f) + { + x *= f; + y *= f; + z *= f; + w *= f; + return *this; + } + /** + \brief scalar division + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 &operator/=(NVReal f) + { + f = 1.0f / f; + x *= f; + y *= f; + z *= f; + w *= f; + return *this; + } + + /** + \brief returns the scalar product of this and other. + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE NVReal dot(const QT3DSVec4 &v) const + { + return x * v.x + y * v.y + z * v.z + w * v.w; + } + + /** return a unit vector */ + + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 getNormalized() const + { + NVReal m = magnitudeSquared(); + return m > 0 ? *this * NVRecipSqrt(m) : QT3DSVec4(0, 0, 0, 0); + } + + /** + \brief normalizes the vector in place + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE NVReal normalize() + { + NVReal m = magnitude(); + if (m > 0) + *this /= m; + return m; + } + + /** + \brief a[i] * b[i], for all i. + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 multiply(const QT3DSVec4 &a) const + { + return QT3DSVec4(x * a.x, y * a.y, z * a.z, w * a.w); + } + + /** + \brief element-wise minimum + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 minimum(const QT3DSVec4 &v) const + { + return QT3DSVec4(NVMin(x, v.x), NVMin(y, v.y), NVMin(z, v.z), NVMin(w, v.w)); + } + + /** + \brief element-wise maximum + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec4 maximum(const QT3DSVec4 &v) const + { + return QT3DSVec4(NVMax(x, v.x), NVMax(y, v.y), NVMax(z, v.z), NVMax(w, v.w)); + } + + QT3DS_CUDA_CALLABLE QT3DS_INLINE QT3DSVec3 getXYZ() const { return QT3DSVec3(x, y, z); } + + /** + \brief set vector elements to zero + */ + QT3DS_CUDA_CALLABLE QT3DS_INLINE void setZero() { x = y = z = w = NVReal(0); } + + NVReal x, y, z, w; +}; + +QT3DS_CUDA_CALLABLE static QT3DS_INLINE QT3DSVec4 operator*(NVReal f, const QT3DSVec4 &v) +{ + return QT3DSVec4(f * v.x, f * v.y, f * v.z, f * v.w); +} + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +/** @} */ +#endif // QT3DS_FOUNDATION_QT3DS_VEC4_H diff --git a/src/foundation/Qt3DSVersionNumber.h b/src/foundation/Qt3DSVersionNumber.h new file mode 100644 index 0000000..5942ef7 --- /dev/null +++ b/src/foundation/Qt3DSVersionNumber.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +VersionNumbers: The combination of these +numbers uniquely identifies the API, and should +be incremented when the SDK API changes. This may +include changes to file formats. + +This header is included in the main SDK header files +so that the entire SDK and everything that builds on it +is completely rebuilt when this file changes. Thus, +this file is not to include a frequently changing +build number. See BuildNumber.h for that. + +Each of these three values should stay below 255 because +sometimes they are stored in a byte. +*/ +/** \addtogroup foundation + @{ +*/ +#ifndef QT3DS_FOUNDATION_QT3DS_VERSION_NUMBER_H +#define QT3DS_FOUNDATION_QT3DS_VERSION_NUMBER_H + +#define QT3DS_FOUNDATION_VERSION_MAJOR 3 +#define QT3DS_FOUNDATION_VERSION_MINOR 3 +#define QT3DS_FOUNDATION_VERSION_BUGFIX 0 + +/** +The constant QT3DS_FOUNDATION_VERSION is used when creating certain PhysX module objects. +This is to ensure that the application is using the same header version as the library was built +with. +*/ +#define QT3DS_FOUNDATION_VERSION \ + ((QT3DS_FOUNDATION_VERSION_MAJOR << 24) + (QT3DS_FOUNDATION_VERSION_MINOR << 16) \ + + (QT3DS_FOUNDATION_VERSION_BUGFIX << 8) + 0) + +#endif // QT3DS_FOUNDATION_QT3DS_VERSION_NUMBER_H + +/** @} */ diff --git a/src/foundation/SerializationTypes.h b/src/foundation/SerializationTypes.h new file mode 100644 index 0000000..c7fe5ce --- /dev/null +++ b/src/foundation/SerializationTypes.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_SERIALIZATION_TYPES_H +#define QT3DS_RENDER_SERIALIZATION_TYPES_H +#include "EASTL/hash_map.h" +#include "foundation/Qt3DSDataRef.h" +#include "foundation/Qt3DSMemoryBuffer.h" +#include "foundation/Qt3DSContainers.h" + +namespace qt3ds { +namespace foundation { + + struct SStrRemapMap : public nvhash_map<char8_t *, QT3DSU32> + { + typedef nvhash_map<char8_t *, QT3DSU32> TBaseType; + SStrRemapMap(NVAllocatorCallback &inAlloc, const char *inName) + : TBaseType(inAlloc, inName) + { + } + }; + + struct SPtrOffsetMap : public nvhash_map<const void *, QT3DSU32> + { + typedef nvhash_map<const void *, QT3DSU32> TBaseType; + SPtrOffsetMap(NVAllocatorCallback &inAlloc, const char *inName) + : TBaseType(inAlloc, inName) + { + } + }; + + struct SWriteBuffer : public MemoryBuffer<> + { + SWriteBuffer(NVAllocatorCallback &inAlloc, const char *inName) + : MemoryBuffer<>(ForwardingAllocator(inAlloc, inName)) + { + } + }; + + // Simple data reader that mimics a string + struct SDataReader + { + QT3DSU8 *m_CurrentPtr; + QT3DSU8 *m_EndPtr; + SDataReader(QT3DSU8 *inStartPtr, QT3DSU8 *inEndPtr) + : m_CurrentPtr(inStartPtr) + , m_EndPtr(inEndPtr) + { + } + + template <typename TDataType> + TDataType *Load() + { + if ((m_CurrentPtr + sizeof(TDataType)) > m_EndPtr) { + QT3DS_ASSERT(false); + return NULL; + } + TDataType *theType = reinterpret_cast<TDataType *>(m_CurrentPtr); + m_CurrentPtr += sizeof(TDataType); + return theType; + } + + template <typename TDataType> + void MemCopy(TDataType *dt, QT3DSU32 count) + { + if (count) { + QT3DSU32 loadSize = count * sizeof(TDataType); + QT3DSU32 amountLeft = (QT3DSU32)(m_EndPtr - m_CurrentPtr); + QT3DSU32 amountLoaded = NVMin(loadSize, amountLeft); + memCopy(dt, m_CurrentPtr, amountLoaded); + m_CurrentPtr += amountLoaded; + if (amountLoaded < loadSize) { + QT3DS_ASSERT(false); + QT3DSU8 *rawPtr = (QT3DSU8 *)dt; + rawPtr += amountLoaded; + size_t leftover = loadSize - amountLoaded; + // zeroing things out at least is an attempt to provide ordered + // data that may prevent a crash in some cases. + memZero(rawPtr, (QT3DSU32)leftover); + } + } + } + + // Don't make a habit of using this a lot. + template <typename TDataType> + TDataType &LoadRef() + { + TDataType *retval = Load<TDataType>(); + if (!retval) + QT3DS_ASSERT(false); + return *retval; + } + + void Align(size_t alignment) + { + size_t offset = (size_t)(m_CurrentPtr); + if (offset % alignment) + m_CurrentPtr += (alignment - (offset % alignment)); + } + + void Align() { Align(sizeof(void *)); } + }; +} +} +#endif
\ No newline at end of file diff --git a/src/foundation/Socket.cpp b/src/foundation/Socket.cpp new file mode 100644 index 0000000..df00e60 --- /dev/null +++ b/src/foundation/Socket.cpp @@ -0,0 +1,472 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +LuaSocket 3.0 license +Copyright � 2004-2013 Diego Nehab + +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 "foundation/Socket.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSMutex.h" +#include "foundation/Qt3DSSync.h" +#include "foundation/Qt3DSMemoryBuffer.h" + +#if defined QT3DS_WINDOWS || defined QT3DS_WIN8ARM +#include "windows/SocketImpl.h" +#else +#include "linux/SocketImpl.h" +#endif + +using namespace qt3ds; +using namespace qt3ds::foundation; +using namespace qt3ds::foundation::socketimpl; + +namespace { + +#if defined QT3DS_WINDOWS || defined QT3DS_WIN8ARM +/*-------------------------------------------------------------------------*\ +* Some systems do not provide this so that we provide our own. It's not +* marvelously fast, but it works just fine. +\*-------------------------------------------------------------------------*/ +int inet_aton(const char *cp, struct in_addr *inp) +{ + unsigned int a = 0, b = 0, c = 0, d = 0; + int n = 0, r; + unsigned long int addr = 0; + r = sscanf(cp, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n); + if (r == 0 || n == 0) + return 0; + cp += n; + if (*cp) + return 0; + if (a > 255 || b > 255 || c > 255 || d > 255) + return 0; + if (inp) { + addr += a; + addr <<= 8; + addr += b; + addr <<= 8; + addr += c; + addr <<= 8; + addr += d; + inp->s_addr = htonl(addr); + } + return 1; +} +#endif + +/*-------------------------------------------------------------------------*\ +* Tries to connect to remote address (address, port) +\*-------------------------------------------------------------------------*/ +int inet_tryconnect(p_socket ps, const char *address, unsigned short port, QT3DSU32 tm, SA *remoteAddr) +{ + struct sockaddr_in remote; + int err; + memset(&remote, 0, sizeof(remote)); + remote.sin_family = AF_INET; + remote.sin_port = htons(port); + if (strcmp(address, "*")) { + if (!inet_aton(address, &remote.sin_addr)) { + struct hostent *hp = NULL; + struct in_addr **addr; + err = socket_gethostbyname(address, &hp); + if (err != IO_DONE) + return err; + addr = (struct in_addr **)hp->h_addr_list; + memcpy(&remote.sin_addr, *addr, sizeof(struct in_addr)); + } + } else + remote.sin_family = AF_UNSPEC; + if (remoteAddr) + memcpy(remoteAddr, &remote, sizeof(remote)); + err = socket_connect(ps, (SA *)&remote, sizeof(remote), tm); + return err; +} + +/*-------------------------------------------------------------------------*\ +* Tries to bind socket to (address, port) +\*-------------------------------------------------------------------------*/ +int inet_trybind(p_socket ps, const char *address, unsigned short port) +{ + struct sockaddr_in local; + int err; + memset(&local, 0, sizeof(local)); + /* address is either wildcard or a valid ip address */ + local.sin_addr.s_addr = htonl(INADDR_ANY); + local.sin_port = htons(port); + local.sin_family = AF_INET; + if (strcmp(address, "*") && !inet_aton(address, &local.sin_addr)) { + struct hostent *hp = NULL; + struct in_addr **addr; + err = socket_gethostbyname(address, &hp); + if (err != IO_DONE) + return err; + addr = (struct in_addr **)hp->h_addr_list; + memcpy(&local.sin_addr, *addr, sizeof(struct in_addr)); + } + err = socket_bind(ps, (SA *)&local, sizeof(local)); + if (err != IO_DONE) + socket_destroy(ps); + return err; +} + +static bool is_socket_error(int errcode) +{ + return errcode != IO_DONE && errcode != IO_TIMEOUT; +} + +const char *generalized_strerror(int err) +{ + if (err <= 0) + return io_strerror(err); + else + return socket_strerror(err); +} + +struct SocketSystemCore : public NVRefCounted +{ + NVFoundationBase &m_Foundation; + QT3DSI32 mRefCount; + SocketSystemCore(NVFoundationBase &fnd) + : m_Foundation(fnd) + , mRefCount(0) + { + } + ~SocketSystemCore() { socket_close(); } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + static NVScopedRefCounted<SocketSystemCore> Create(NVFoundationBase &fnd) + { + int success = socket_open(); + if (!success) { + qCCritical(INVALID_OPERATION, "Failed to initialize network subsystem"); + return NVScopedRefCounted<SocketSystemCore>(); + } + return QT3DS_NEW(fnd.getAllocator(), SocketSystemCore)(fnd); + } +}; + +struct SocketStreamImpl : public SocketStream +{ + NVScopedRefCounted<SocketSystemCore> m_SocketCore; + NVFoundationBase &m_Foundation; + t_socket m_Socket; + SA m_Destination; + bool m_Connected; + QT3DSU32 m_Timeout; + QT3DSI32 mRefCount; + SocketStreamImpl(SocketSystemCore &core, NVFoundationBase &fnd, t_socket s, SA dest) + : m_SocketCore(core) + , m_Foundation(fnd) + , m_Socket(s) + , m_Destination(dest) + , m_Connected(true) + , m_Timeout(10000) + , mRefCount(0) + { + // We use wait functions in order to block. + socket_setnonblocking(&m_Socket); + } + + ~SocketStreamImpl() + { + shutdown(); + socket_destroy(&m_Socket); + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation.getAllocator()) + + void setTimeout(QT3DSU32 milliseconds) override { m_Timeout = milliseconds; } + + bool Write(NVConstDataRef<QT3DSU8> data) override + { + if (m_Connected == false) + return false; + + size_t totalSent = 0; + const char *writePtr(reinterpret_cast<const char *>(data.begin())); + size_t amountLeft = data.size(); + do { + + size_t amountSent = 0; + int errcode = + socket_send(&m_Socket, writePtr + totalSent, amountLeft, &amountSent, m_Timeout); + + if (is_socket_error(errcode)) { + m_Connected = false; + qCWarning(WARNING, "Networking error during send: %s", generalized_strerror(errcode)); + return false; + } + totalSent += amountSent; + amountLeft -= amountSent; + } while (amountLeft); + return true; + } + + QT3DSU32 Read(NVDataRef<QT3DSU8> data) override + { + if (m_Connected == false) + return 0; + size_t amountReceived = 0; + int errcode = socket_recv(&m_Socket, reinterpret_cast<char *>(data.begin()), data.size(), + &amountReceived, m_Timeout); + if (is_socket_error(errcode)) { + m_Connected = false; + qCWarning(WARNING, "Networking error during receive: %s", generalized_strerror(errcode)); + return false; + } + return static_cast<QT3DSU32>(amountReceived); + } + + QT3DSU32 nonBlockingRead(NVDataRef<QT3DSU8> data) override + { + if (m_Connected == false) + return 0; + size_t amountReceived = 0; + int errcode = socket_recv(&m_Socket, reinterpret_cast<char *>(data.begin()), data.size(), + &amountReceived, 0); + if (is_socket_error(errcode)) { + m_Connected = false; + qCWarning(WARNING, "Networking error during receive: %s", + generalized_strerror(errcode)); + return false; + } + return static_cast<QT3DSU32>(amountReceived); + } + + bool connected() override { return m_Connected; } + + void shutdown() override + { + if (m_Connected) { + socket_shutdown(&m_Socket, 2); + m_Connected = false; + } + } +}; + +struct FileSocketStreamImpl : public SocketStream +{ + NVFoundationBase &m_Foundation; + CFileSeekableIOStream m_Stream; + FileOpenFlags m_FileFlags; + bool m_Connected; + QT3DSI32 mRefCount; + + FileSocketStreamImpl(NVFoundationBase &fnd, const char *fname, FileOpenFlags fileFlags) + : m_Foundation(fnd) + , m_Stream(fname, fileFlags) + , m_FileFlags(fileFlags) + , m_Connected(false) + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation.getAllocator()) + + bool IsOpen() { return m_Connected && m_Stream.IsOpen(); } + + QT3DSU32 Read(NVDataRef<QT3DSU8> data) override + { + if (IsOpen()) { + QT3DSU32 retval = m_Stream.Read(data); + if (retval < data.size()) + m_Connected = false; + return retval; + } + + return 0; + } + + bool Write(NVConstDataRef<QT3DSU8> data) override + { + bool canWrite = m_FileFlags & FileOpenFlagValues::Write; + if (IsOpen() && canWrite) { + m_Stream.Write(data); + return true; + } + return false; + } + + void setTimeout(QT3DSU32) override {} + + QT3DSU32 nonBlockingRead(NVDataRef<QT3DSU8> data) override { return Read(data); } + + bool connected() override { return m_Connected; } + + void shutdown() override { m_Connected = false; } +}; + +struct SocketServerImpl : public SocketServer +{ + NVScopedRefCounted<SocketSystemCore> m_SocketCore; + NVFoundationBase &m_Foundation; + t_socket m_Socket; + int m_Port; + QT3DSU32 m_Timeout; + QT3DSI32 mRefCount; + SocketServerImpl(SocketSystemCore &core, NVFoundationBase &fnd, t_socket s, int port) + : m_SocketCore(core) + , m_Foundation(fnd) + , m_Socket(s) + , m_Port(port) + , m_Timeout(10000) + , mRefCount(0) + { + } + + ~SocketServerImpl() { socket_destroy(&m_Socket); } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Foundation.getAllocator()) + + int port() override { return m_Port; } + + void setTimeout(QT3DSU32 milliseconds) override { m_Timeout = milliseconds; } + + NVScopedRefCounted<SocketStream> nextClient() override + { + SA daddr; + memset(&daddr, 0, sizeof(daddr)); + t_socket new_socket; + int errcode = socket_accept(&m_Socket, &new_socket, NULL, NULL, m_Timeout); + + if (is_socket_error(errcode)) { + return NVScopedRefCounted<SocketStream>(); + } else if (errcode == IO_DONE) { + return QT3DS_NEW(m_Foundation.getAllocator(), + SocketStreamImpl)(*m_SocketCore, m_Foundation, new_socket, daddr); + } + return NVScopedRefCounted<SocketStream>(); + } +}; + +struct SocketSystemImpl : public SocketSystem +{ + NVScopedRefCounted<SocketSystemCore> m_SocketCore; + NVFoundationBase &m_Foundation; + QT3DSI32 mRefCount; + SocketSystemImpl(SocketSystemCore &core, NVFoundationBase &fnd) + : m_SocketCore(core) + , m_Foundation(fnd) + , mRefCount(0) + + { + } + ~SocketSystemImpl() {} + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + NVScopedRefCounted<SocketStream> createStream(const char *host, QT3DSI32 port, + QT3DSU32 timeoutMilliseconds) override + { + t_socket newSocket(SOCKET_INVALID); + int errcode = socket_create(&newSocket, PF_INET, SOCK_STREAM, 0); + if (is_socket_error(errcode)) { + qCWarning(WARNING, "Error during connect: %s", generalized_strerror(errcode)); + return NVScopedRefCounted<SocketStream>(); + } + SA remoteAddr; + memset(&remoteAddr, 0, sizeof(SA)); + errcode = inet_tryconnect(&newSocket, host, (unsigned short)port, timeoutMilliseconds, + &remoteAddr); + if (is_socket_error(errcode)) { + qCWarning(WARNING, "Error during connect: %s", generalized_strerror(errcode)); + return NVScopedRefCounted<SocketStream>(); + } else if (errcode == IO_DONE) { + return QT3DS_NEW(m_Foundation.getAllocator(), + SocketStreamImpl)(*m_SocketCore, m_Foundation, newSocket, remoteAddr); + } + return NVScopedRefCounted<SocketStream>(); + } + + NVScopedRefCounted<SocketServer> createServer(QT3DSI32 port) override + { + t_socket newSocket(SOCKET_INVALID); + int errcode = socket_create(&newSocket, PF_INET, SOCK_STREAM, 0); + if (is_socket_error(errcode)) { + qCWarning(WARNING, "Error during create server create socket: %s", + generalized_strerror(errcode)); + return NVScopedRefCounted<SocketServer>(); + } + errcode = inet_trybind(&newSocket, "*", (unsigned short)port); + if (is_socket_error(errcode)) { + qCWarning(WARNING, "Error during create server bind: %s", + generalized_strerror(errcode)); + return NVScopedRefCounted<SocketServer>(); + } else if (errcode == IO_DONE) { + errcode = socket_listen(&newSocket, 10); + if (errcode == IO_DONE) { + return QT3DS_NEW(m_Foundation.getAllocator(), + SocketServerImpl)(*m_SocketCore, m_Foundation, newSocket, port); + } else + qCWarning(WARNING, "Error during create server listen: %s", + generalized_strerror(errcode)); + } + return NVScopedRefCounted<SocketServer>(); + } +}; +} + +NVScopedRefCounted<SocketStream> +SocketStream::CreateFileStream(NVFoundationBase &fnd, const char *fname, FileOpenFlags flags) +{ + return QT3DS_NEW(fnd.getAllocator(), FileSocketStreamImpl)(fnd, fname, flags); +} + +NVScopedRefCounted<SocketSystem> SocketSystem::createSocketSystem(NVFoundationBase &fnd) +{ + NVScopedRefCounted<SocketSystemCore> theCore = SocketSystemCore::Create(fnd); + if (theCore) { + return QT3DS_NEW(fnd.getAllocator(), SocketSystemImpl)(*theCore, fnd); + } + return NVScopedRefCounted<SocketSystem>(); +} diff --git a/src/foundation/Socket.h b/src/foundation/Socket.h new file mode 100644 index 0000000..f561ab8 --- /dev/null +++ b/src/foundation/Socket.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_FOUNDATION_SOCKET_H +#define QT3DS_FOUNDATION_SOCKET_H +#include "foundation/IOStreams.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/Qt3DSMemoryBuffer.h" + +namespace qt3ds { + +class NVFoundationBase; +namespace foundation { + + class SocketStream : public IInStream, public IOutStream, public NVRefCounted + { + public: + // set timeout for write and blocking read operations + // Setting to zero makes all operations nonblocking. + virtual void setTimeout(QT3DSU32 milliseconds) = 0; + virtual QT3DSU32 nonBlockingRead(NVDataRef<QT3DSU8> ioBuffer) = 0; + virtual bool connected() = 0; + virtual void shutdown() = 0; + + // Useful to testing systems without going into the issues that a network connection can + // cause. + static NVScopedRefCounted<SocketStream> + CreateFileStream(NVFoundationBase &fnd, const char *fname, FileOpenFlags flags); + }; + + class SocketServer : public NVRefCounted + { + public: + virtual int port() = 0; + virtual void setTimeout(QT3DSU32 milliseconds) = 0; + virtual NVScopedRefCounted<SocketStream> nextClient() = 0; + }; + + class SocketSystem : public NVRefCounted + { + public: + // timeout of 0 means wait forever + virtual NVScopedRefCounted<SocketStream> + createStream(const char *host, QT3DSI32 port, QT3DSU32 timeoutMilliseconds = 10000) = 0; + virtual NVScopedRefCounted<SocketServer> createServer(QT3DSI32 port) = 0; + + static NVScopedRefCounted<SocketSystem> createSocketSystem(NVFoundationBase &fnd); + }; +} +} + +#endif
\ No newline at end of file diff --git a/src/foundation/StrConvertUTF.h b/src/foundation/StrConvertUTF.h new file mode 100644 index 0000000..965867c --- /dev/null +++ b/src/foundation/StrConvertUTF.h @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once +#ifndef QT3DS_RENDER_STR_CONVERT_UTF_H +#define QT3DS_RENDER_STR_CONVERT_UTF_H +#include <EASTL/string.h> +#include "foundation/ConvertUTF.h" +#include "foundation/Qt3DSAssert.h" +#include "foundation/Qt3DSSimpleTypes.h" + +namespace qt3ds { +namespace foundation { + + using namespace qt3ds; + template <typename TSrcType> + struct ConverterType + { + }; + template <> + struct ConverterType<char8_t> + { + typedef UTF8 TConverterType; + }; + template <> + struct ConverterType<char16_t> + { + typedef UTF16 TConverterType; + }; + template <> + struct ConverterType<char32_t> + { + typedef UTF32 TConverterType; + }; + + template <QT3DSU32 TWCharSize> + struct WCharType + { + }; + + template <> + struct WCharType<2> + { + typedef char16_t TCharType; + }; + + template <> + struct WCharType<4> + { + typedef char32_t TCharType; + }; + + typedef WCharType<sizeof(wchar_t)> TWCharEASTLConverter; + + template <typename TSrcType, typename TDestType> + struct UTFConversionSelector + { + }; + + template <> + struct UTFConversionSelector<char8_t, char16_t> + { + static ConversionResult Convert(const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, + ConversionFlags flags) + { + return Q3DSConvertUTF8toUTF16(sourceStart, sourceEnd, targetStart, targetEnd, flags); + } + }; + + template <> + struct UTFConversionSelector<char8_t, char32_t> + { + static ConversionResult Convert(const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, + ConversionFlags flags) + { + return Q3DSConvertUTF8toUTF32(sourceStart, sourceEnd, targetStart, targetEnd, flags); + } + }; + template <> + struct UTFConversionSelector<char16_t, char8_t> + { + static ConversionResult Convert(const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) + { + return Q3DSConvertUTF16toUTF8(sourceStart, sourceEnd, targetStart, targetEnd, flags); + } + }; + template <> + struct UTFConversionSelector<char16_t, char32_t> + { + static ConversionResult Convert(const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, + ConversionFlags flags) + { + return Q3DSConvertUTF16toUTF32(sourceStart, sourceEnd, targetStart, targetEnd, flags); + } + }; + template <> + struct UTFConversionSelector<char32_t, char8_t> + { + static ConversionResult Convert(const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) + { + return Q3DSConvertUTF32toUTF8(sourceStart, sourceEnd, targetStart, targetEnd, flags); + } + }; + template <> + struct UTFConversionSelector<char32_t, char16_t> + { + static ConversionResult Convert(const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, + ConversionFlags flags) + { + return Q3DSConvertUTF32toUTF16(sourceStart, sourceEnd, targetStart, targetEnd, flags); + } + }; + + // Convert into an EASTL string type. + // inSrcLen may be zero in which case we analyze the string looking for the zero element. + template <typename TSrcType, typename TDestType, typename TAllocType> + bool ConvertUTF(const TSrcType *inSrc, size_t inSrcLen, + eastl::basic_string<TDestType, TAllocType> &outString) + { + typedef typename ConverterType<TDestType>::TConverterType TDestUTFType; + typedef typename ConverterType<TSrcType>::TConverterType TSrcUTFType; + if (inSrc == 0 || *inSrc == 0) { + outString.clear(); + return true; + } + + if (inSrcLen == 0) { + // empty loop intentional. + for (const TSrcType *ptr = inSrc; ptr && *ptr; ++ptr, ++inSrcLen) { + } + } + + typename eastl::basic_string<TDestType, TAllocType>::size_type capacity = + outString.capacity(); + if (capacity == 0) + outString.resize((QT3DSU32)inSrcLen * 2); + else + outString.resize(capacity); + + ConversionResult theConversionResult(conversionOK); + TDestUTFType *writePtr = NULL; + do { + writePtr = reinterpret_cast<TDestUTFType *>(const_cast<TDestType *>(outString.data())); + TDestUTFType *writeEnd(writePtr + outString.size()); + const TSrcUTFType *readPtr(reinterpret_cast<const TSrcUTFType *>(inSrc)); + const TSrcUTFType *readEnd = readPtr + inSrcLen; + theConversionResult = UTFConversionSelector<TSrcType, TDestType>::Convert( + &readPtr, readEnd, &writePtr, writeEnd, lenientConversion); + if (theConversionResult == targetExhausted) { + capacity = outString.capacity() * 2; + outString.resize(capacity); + } + } while (theConversionResult == targetExhausted); + + if (theConversionResult == conversionOK) { + TDestUTFType *writeStart = + reinterpret_cast<TDestUTFType *>(const_cast<TDestType *>(outString.data())); + outString.resize((QT3DSU32)(writePtr - writeStart)); + return true; + } else { + outString.clear(); + QT3DS_ASSERT(false); + return false; + } + } + +#ifdef WIDE_IS_DIFFERENT_TYPE_THAN_CHAR16_T + + template <typename TDestType, typename TAllocType> + bool ConvertWideUTF(const wchar_t *inSrc, size_t inSrcLen, + eastl::basic_string<TDestType, TAllocType> &outString) + { + return ConvertUTF((const TWCharEASTLConverter::TCharType *)inSrc, inSrcLen, outString); + } + +#endif +} +} + +#endif diff --git a/src/foundation/StringConversion.h b/src/foundation/StringConversion.h new file mode 100644 index 0000000..a55ebb3 --- /dev/null +++ b/src/foundation/StringConversion.h @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_FOUNDATION_STRING_CONVERSION_H +#define QT3DS_FOUNDATION_STRING_CONVERSION_H +#include "foundation/Qt3DSSimpleTypes.h" +#include "foundation/Qt3DSMemoryBuffer.h" +#include "foundation/Qt3DSAllocator.h" +#include "foundation/Utils.h" + +namespace qt3ds { +namespace foundation { + + // Template base class so that we can convert items to and from char* and wchar_t* + // representations + template <typename TDataType> + struct StringConversion + { + bool force_compile_error; + }; + + // Write the char8_t but exlude the null terminator + // Meant to write data model values. + // Memory buffer contains a non-null-terminated + // char8_t string + struct Char8TWriter + { + MemoryBuffer<ForwardingAllocator> &m_Buffer; + Char8TWriter(MemoryBuffer<ForwardingAllocator> &buf) + : m_Buffer(buf) + { + } + void Write(const char8_t *value, QT3DSU32 len = 0) + { + if (isTrivial(value)) + return; + if (len == 0) + len = (QT3DSU32)StrLen(value); + m_Buffer.write(value, len); + } + void Write(char8_t value) { m_Buffer.write(value); } + void Write(bool value) { Write(value ? "True" : "False"); } + + // Takes care of long and float + template <typename TDataType> + void Write(TDataType value) + { + char8_t buf[256]; + QT3DSU32 numWritten = + StringConversion<TDataType>().ToStr(value, NVDataRef<char8_t>(buf, 256)); + if (numWritten) + Write((const char8_t *)buf); + else { + QT3DS_ASSERT(false); + } + } + + template <typename TDataType> + void Write(NVConstDataRef<TDataType> values, QT3DSU32 grouping = 6, QT3DSU32 tabCount = 0) + { + for (QT3DSU32 idx = 0; idx < values.size(); ++idx) { + if (idx) { + if ((idx % grouping) == 0) { + Write((char8_t)'\n'); + for (QT3DSU32 tabIdx = 0; tabIdx < tabCount; ++tabIdx) + Write((char8_t)'\t'); + } else + Write((char8_t)' '); + } + Write(values[idx]); + } + } + + void Write(NVConstDataRef<char8_t> values, QT3DSU32 ignored = 6) + { + (void)ignored; + if (values.size() && values[0] != 0) { + QT3DSU32 lastItem = values.size() - 1; + if (values[lastItem] == 0) + --lastItem; + Write(values.begin(), lastItem + 1); + } + } + }; + + inline bool IsWhite(char8_t value) + { + return value == '\n' || value == '\r' || value == ' ' || value == '\t'; + } + + // skip until we find whitespace. + inline char8_t *FindNextWhitespace(char8_t *input) + { + if (input == NULL) + return input; + char8_t *marker = input; + // Empty loop intentional + for (; *marker && !IsWhite(*marker); ++marker) + ; + return marker; + } + + // skip until we find something that isn't whitespace. + inline char8_t *FindNextNonWhitespace(char8_t *input) + { + if (input == NULL) + return input; + char8_t *marker = input; + // Empty loop intentional + for (; *marker && IsWhite(*marker); ++marker) + ; + return marker; + } + + // Reading is destructive in the case of floating point lists, so we may + // destroy the incoming string. + // We are assuming the string is null-terminated at end ptr. + struct Char8TReader + { + char8_t *m_StartPtr; + // Buffer used for temp storage + MemoryBuffer<ForwardingAllocator> &m_Buffer; + Char8TReader(char8_t *sp, MemoryBuffer<ForwardingAllocator> &buf) + : m_StartPtr(sp) + , m_Buffer(buf) + { + } + void Read(const char8_t *&outPtr) { outPtr = m_StartPtr; } + + template <typename TDataType> + void Read(TDataType &data) + { + bool success = StringConversion<TDataType>().StrTo(m_StartPtr, data); + QT3DS_ASSERT(success); + (void)success; + } + // Destructive operation because we can't trust + // strtod to do the right thing. On windows, for long strings, + // it calls strlen every operation thus leading to basically N^2 + // behavior + template <typename TDataType> + void ReadRef(NVDataRef<TDataType> data) + { + QT3DSU32 idx = 0; + m_StartPtr = FindNextNonWhitespace(m_StartPtr); + for (; idx < data.size() && m_StartPtr && *m_StartPtr; ++idx) { + char8_t *nextPtr = FindNextWhitespace(m_StartPtr); + if (nextPtr && *nextPtr) + *nextPtr = 0; + else + nextPtr = NULL; + StringConversion<TDataType>().StrTo(m_StartPtr, data[idx]); + m_StartPtr = nextPtr; + if (m_StartPtr) + m_StartPtr = FindNextNonWhitespace(m_StartPtr + 1); + } + QT3DS_ASSERT(idx == data.size()); + } + + void ReadBuffer(NVConstDataRef<char8_t> &outBuffer) + { + if (m_StartPtr && *m_StartPtr) { + QT3DSU32 len = (QT3DSU32)strlen(m_StartPtr); + outBuffer = NVConstDataRef<char8_t>(m_StartPtr, len + 1); + } + } + + // Destructive operation because we can't trust + // strtod to do the right thing. On windows, for long strings, + // it calls strlen every operation thus leading to basically N^2 + // behavior + template <typename TDataType> + void ReadBuffer(NVConstDataRef<TDataType> &outBuffer) + { + m_Buffer.clear(); + m_StartPtr = FindNextNonWhitespace(m_StartPtr); + while (m_StartPtr && *m_StartPtr) { + char8_t *nextPtr = FindNextWhitespace(m_StartPtr); + if (nextPtr && *nextPtr) + *nextPtr = 0; + else + nextPtr = NULL; + TDataType temp; + StringConversion<TDataType>().StrTo(m_StartPtr, temp); + m_Buffer.write(temp); + m_StartPtr = nextPtr; + if (m_StartPtr) + m_StartPtr = FindNextNonWhitespace(m_StartPtr + 1); + } + QT3DSU32 numItems = m_Buffer.size() / sizeof(TDataType); + if (numItems) + outBuffer = NVConstDataRef<TDataType>((TDataType *)m_Buffer.begin(), numItems); + else + outBuffer = NVConstDataRef<TDataType>(); + } + }; +} +} +#endif
\ No newline at end of file diff --git a/src/foundation/StringConversionImpl.h b/src/foundation/StringConversionImpl.h new file mode 100644 index 0000000..c5063e6 --- /dev/null +++ b/src/foundation/StringConversionImpl.h @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_FOUNDATION_STRING_CONVERSION_IMPL_H +#define QT3DS_FOUNDATION_STRING_CONVERSION_IMPL_H +#include "foundation/StringConversion.h" +#include "foundation/Qt3DSMemoryBuffer.h" +#include "EABase/eabase.h" +#include "foundation/StringTable.h" +#include "foundation/Utils.h" +#include "stdlib.h" +#include "stdio.h" //snprintf +#include <QLocale> + +#if !defined EA_PLATFORM_WINDOWS +#define _snprintf snprintf +#endif + +namespace qt3ds { +namespace foundation { + + template <> + struct StringConversion<bool> + { + QT3DSU32 ToStr(bool item, NVDataRef<char8_t> buffer) + { + return static_cast<QT3DSU32>( + _snprintf(buffer.begin(), buffer.size(), "%s", item ? "True" : "False")); + } + bool StrTo(const char8_t *buffer, bool &item) + { + if (AreEqualCaseless(buffer, "True")) + item = true; + else + item = false; + return true; + } + }; + + template <> + struct StringConversion<QT3DSU8> + { + QT3DSU32 ToStr(QT3DSU8 item, NVDataRef<char8_t> buffer) + { + return static_cast<QT3DSU32>( + _snprintf(buffer.begin(), buffer.size(), "%hu", static_cast<QT3DSU16>(item))); + } + bool StrTo(const char8_t *buffer, QT3DSU8 &item) + { + item = static_cast<QT3DSU8>(strtoul(buffer, NULL, 10)); + return true; + } + }; + template <> + struct StringConversion<QT3DSI8> + { + QT3DSU32 ToStr(QT3DSI8 item, NVDataRef<char8_t> buffer) + { + return static_cast<QT3DSU32>( + _snprintf(buffer.begin(), buffer.size(), "%hd", static_cast<QT3DSI16>(item))); + } + bool StrTo(const char8_t *buffer, QT3DSI8 &item) + { + item = static_cast<QT3DSI8>(strtol(buffer, NULL, 10)); + return true; + } + }; + template <> + struct StringConversion<QT3DSU16> + { + QT3DSU32 ToStr(QT3DSU16 item, NVDataRef<char8_t> buffer) + { + return static_cast<QT3DSU32>(_snprintf(buffer.begin(), buffer.size(), "%hu", item)); + } + bool StrTo(const char8_t *buffer, QT3DSU16 &item) + { + item = static_cast<QT3DSU16>(strtoul(buffer, NULL, 10)); + return true; + } + }; + template <> + struct StringConversion<QT3DSI16> + { + QT3DSU32 ToStr(QT3DSI16 item, NVDataRef<char8_t> buffer) + { + return static_cast<QT3DSU32>(_snprintf(buffer.begin(), buffer.size(), "%hd", item)); + } + bool StrTo(const char8_t *buffer, QT3DSI16 &item) + { + item = static_cast<QT3DSI16>(strtol(buffer, NULL, 10)); + return true; + } + }; + + template <> + struct StringConversion<QT3DSU32> + { + QT3DSU32 ToStr(QT3DSU32 item, NVDataRef<char8_t> buffer) + { + // hope the buffer is big enough... + return static_cast<QT3DSU32>(_snprintf(buffer.begin(), buffer.size(), "%u", item)); + } + bool StrTo(const char8_t *buffer, QT3DSU32 &item) + { + item = strtoul(buffer, NULL, 10); + return true; + } + }; + template <> + struct StringConversion<QT3DSI32> + { + QT3DSU32 ToStr(QT3DSI32 item, NVDataRef<char8_t> buffer) + { + return static_cast<QT3DSU32>(_snprintf(buffer.begin(), buffer.size(), "%d", (int)item)); + } + bool StrTo(const char8_t *buffer, QT3DSI32 &item) + { + item = strtol(buffer, NULL, 10); + return true; + } + }; + template <> + struct StringConversion<QT3DSU64> + { + QT3DSU32 ToStr(QT3DSU64 item, NVDataRef<char8_t> buffer) + { +#ifdef _WIN32 + return static_cast<QT3DSU32>(_snprintf(buffer.begin(), buffer.size(), "%I64u", item)); +#else + return static_cast<QT3DSU32>(_snprintf(buffer.begin(), buffer.size(), "%lu", item)); +#endif + } + bool StrTo(const char8_t *buffer, QT3DSU64 &item) + { + item = strtoul(buffer, NULL, 10); + return true; + } + }; + template <> + struct StringConversion<QT3DSI64> + { + QT3DSU32 ToStr(QT3DSI64 item, NVDataRef<char8_t> buffer) + { +#ifdef _WIN32 + return static_cast<QT3DSU32>(_snprintf(buffer.begin(), buffer.size(), "%I64d", item)); +#else + return static_cast<QT3DSU32>(_snprintf(buffer.begin(), buffer.size(), "%ld", item)); +#endif + } + bool StrTo(const char8_t *buffer, QT3DSI64 &item) + { + item = strtol(buffer, NULL, 10); + return true; + } + }; + template <> + struct StringConversion<QT3DSF32> + { + QT3DSU32 ToStr(QT3DSF32 item, NVDataRef<char8_t> buffer) + { + QString s = QLocale::c().toString(item); + strncpy(buffer.begin(), s.toStdString().c_str(), buffer.size()); + return s.length(); + } + bool StrTo(const char8_t *buffer, QT3DSF32 &item) + { + bool ok; + item = QLocale::c().toFloat(buffer, &ok); + return ok; + } + }; + + template <> + struct StringConversion<QT3DSF64> + { + QT3DSU32 ToStr(QT3DSF64 item, NVDataRef<char8_t> buffer) + { + QString s = QLocale::c().toString(item); + strncpy(buffer.begin(), s.toStdString().c_str(), buffer.size()); + return s.length(); + } + bool StrTo(const char8_t *buffer, QT3DSF64 &item) + { + bool ok; + item = QLocale::c().toDouble(buffer, &ok); + return ok; + } + }; +} +} + +#endif diff --git a/src/foundation/StringTable.cpp b/src/foundation/StringTable.cpp new file mode 100644 index 0000000..47130dc --- /dev/null +++ b/src/foundation/StringTable.cpp @@ -0,0 +1,700 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "foundation/StringTable.h" +#include "foundation/Qt3DSContainers.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Utils.h" +#include "EASTL/string.h" +#include "EASTL/sort.h" +#include "foundation/StrConvertUTF.h" +#include "foundation/PreAllocatedAllocator.h" +#include "foundation/SerializationTypes.h" +#include "foundation/Qt3DSMutex.h" + +using namespace qt3ds; +using namespace qt3ds::foundation; +using namespace eastl; + +namespace eastl { +struct SCharAndHash +{ + const char8_t *m_Data; + size_t m_Hash; + // Cache the generated hash code. + SCharAndHash(const char8_t *inData) + : m_Data(inData) + , m_Hash(eastl::hash<const char8_t *>()(inData)) + { + } + SCharAndHash() + : m_Data(nullptr) + , m_Hash(0) + { + } + operator const char *() const { return m_Data; } +}; +template <> +struct hash<SCharAndHash> +{ + size_t operator()(const SCharAndHash &inItem) const { return inItem.m_Hash; } +}; + +template <> +struct equal_to<SCharAndHash> +{ + bool operator()(const SCharAndHash &inLhs, const SCharAndHash &inRhs) const + { + return char_equal_to()(inLhs.m_Data, inRhs.m_Data); + } +}; +} + +void CRegisteredString::Remap(const IStringTable &inTable) +{ + Remap(const_cast<IStringTable &>(inTable).GetRemapMap()); +} + +void CRegisteredString::Remap(const SStrRemapMap &inMap) +{ + if (IsValid()) { + SStrRemapMap::const_iterator theIter = inMap.find((char8_t *)m_String); + if (theIter != inMap.end()) + m_String = reinterpret_cast<const char8_t *>(theIter->second); + else { + QT3DS_ASSERT(false); + // Ensure a failure here doesn't *guarantee* a crash somewhere else. + m_String = nullptr; + } + } else { + // Indicates an invalid string. + m_String = reinterpret_cast<const char8_t *>(QT3DS_MAX_U32); + } +} + +void CRegisteredString::Remap(NVDataRef<QT3DSU8> inDataPtr) +{ + size_t theOffset = reinterpret_cast<size_t>(m_String); + if (theOffset >= QT3DS_MAX_U32) + m_String = ""; + else { + if (theOffset < inDataPtr.size()) + m_String = reinterpret_cast<char8_t *>(inDataPtr.begin() + theOffset); + else { + QT3DS_ASSERT(false); + m_String = ""; + } + } +} + +namespace { + +const char16_t g_char16EmptyStr[] = { 0, 0, 0, 0 }; +const char32_t g_char32EmptyStr[] = { 0, 0, 0, 0 }; + +typedef eastl::basic_string<char8_t, ForwardingAllocator> TNarrowStr; +typedef eastl::basic_string<TWCharEASTLConverter::TCharType, ForwardingAllocator> TWideStr; + +inline bool isTrivialWide(const wchar_t *str) +{ + return str == nullptr || *str == 0; +} + +QT3DSU8 *AlignPointer(QT3DSU8 *inStart, QT3DSU8 *inPtr) +{ + QT3DSU32 alignment = sizeof(void *); + size_t numBytes = (size_t)(inPtr - inStart); + QT3DS_ASSERT(inStart < inPtr); + if (numBytes % alignment) + inPtr += alignment - (numBytes % alignment); + return inPtr; +} + +QT3DSU32 Align(QT3DSU32 inValue) +{ + QT3DSU32 alignment = sizeof(void *); + QT3DSU32 leftover = inValue % alignment; + if (leftover) + inValue += alignment - leftover; + return inValue; +} + +// Structure that is written out to the file. +// Directly after this is the string character data. +// TWideStr& inConvertStr +struct SStringFileData +{ + QT3DSU32 m_StartOffset; + QT3DSU32 m_StrLen; + QT3DSU32 m_Handle; + char8_t *m_Str; + SStringFileData(QT3DSU32 len, QT3DSU32 off, QT3DSU32 handle, char8_t *data) + : m_StartOffset(off) + , m_StrLen(len) + , m_Handle(handle) + , m_Str(data) + { + } + SStringFileData() + : m_StartOffset(0) + , m_StrLen(0) + , m_Handle(0) + , m_Str(nullptr) + { + } + void Deallocate(NVAllocatorCallback &inAlloc) + { + inAlloc.deallocate(m_Str); + m_Str = nullptr; + } + const char8_t *GetNarrow() { return m_Str; } + operator CRegisteredString() const + { + return CRegisteredString::ISwearThisHasBeenRegistered(m_Str); + } + QT3DSU32 EndOffset() const { return m_StartOffset + m_StrLen; } + + // Used only for the lower bound operation to find a given object by offset. + bool operator<(const SStringFileData &inOther) const + { + return m_StartOffset < inOther.m_StartOffset; + } +}; + +struct SCharAndHandle +{ + const char8_t *m_Data; + QT3DSU32 m_Handle; + SCharAndHandle() + : m_Data(nullptr) + , m_Handle(0) + { + } + SCharAndHandle(const char8_t *data, QT3DSU32 hdl) + : m_Data(data) + , m_Handle(hdl) + { + } + operator const char *() const { return m_Data; } +}; + +struct SStringFileHeader +{ + QT3DSU32 m_NumStrings; + QT3DSU32 m_NextHandleValue; + // offset to the items of fixed size. Variable sized data is stored + // up front. + QT3DSU32 m_FixedBufferOffset; + SStringFileHeader() + : m_NumStrings(0) + , m_NextHandleValue(0) + , m_FixedBufferOffset(0) + { + } + + SStringFileHeader(QT3DSU32 ns, QT3DSU32 hv, QT3DSU32 fbo) + : m_NumStrings(ns) + , m_NextHandleValue(hv) + , m_FixedBufferOffset(fbo) + { + } +}; + +// This is the core of the string table. +struct SStringFileDataList +{ + typedef nvhash_map<SCharAndHash, SCharAndHandle> TMapType; + typedef nvhash_map<QT3DSU32, const char8_t *> THandleMapType; + + mutable SPreAllocatedAllocator m_Allocator; + + // When we load from a file, we get a these items + NVConstDataRef<SStringFileData> m_DataBlock; + NVConstDataRef<eastl::pair<size_t, QT3DSU32>> m_HashDataBlockOffset; + NVConstDataRef<eastl::pair<QT3DSU32, QT3DSU32>> m_HandleDataBlockOffset; + + // When we are running normally, this items get mangled + nvvector<SStringFileData> m_AppendedStrings; + +protected: + // Built roughly on demand + SStrRemapMap m_StrRemapMap; + TMapType m_HashToStrMap; + THandleMapType m_HandleToStrMap; + QT3DSU32 m_NextHandleValue; + +public: + SStringFileDataList(NVAllocatorCallback &inAlloc) + : m_Allocator(inAlloc) + , m_AppendedStrings(inAlloc, "StringTable::m_AppendedStrings") + , m_StrRemapMap(inAlloc, "StringTable::m_StrRemapMap") + , m_HashToStrMap(inAlloc, "StringTable::m_HashToStrMap") + , m_HandleToStrMap(inAlloc, "StringTable::m_HashToStrMap") + , m_NextHandleValue(1) + { + } + ~SStringFileDataList() + { + for (QT3DSU32 idx = 0, end = m_AppendedStrings.size(); idx < end; ++idx) + m_AppendedStrings[idx].Deallocate(m_Allocator); + } + + SStrRemapMap &GetRemapMap() + { + if (m_StrRemapMap.empty()) { + for (QT3DSU32 idx = 0, end = m_DataBlock.size(); idx < end; ++idx) + m_StrRemapMap.insert( + eastl::make_pair(m_DataBlock[idx].m_Str, m_DataBlock[idx].m_StartOffset)); + for (QT3DSU32 idx = 0, end = m_AppendedStrings.size(); idx < end; ++idx) + m_StrRemapMap.insert(eastl::make_pair(m_AppendedStrings[idx].m_Str, + m_AppendedStrings[idx].m_StartOffset)); + } + return m_StrRemapMap; + } + + struct SHashPairComparator + { + bool operator()(const eastl::pair<size_t, QT3DSU32> &lhs, + const eastl::pair<size_t, QT3DSU32> &rhs) const + { + return lhs.first < rhs.first; + } + }; + + struct SHandlePairComparator + { + bool operator()(const eastl::pair<QT3DSU32, QT3DSU32> &lhs, + const eastl::pair<QT3DSU32, QT3DSU32> &rhs) const + { + return lhs.first < rhs.first; + } + }; + + SCharAndHandle DoFindStr(SCharAndHash hashCode) + { + if (isTrivial(hashCode.m_Data)) + return SCharAndHandle(); + TMapType::iterator theFind = m_HashToStrMap.find(hashCode); + if (theFind == m_HashToStrMap.end()) { + SCharAndHandle newStr = FindStrByHash(hashCode); + if (!newStr.m_Data) + newStr = Append(hashCode); + + // It may not be obvious why we have to reset the data member. + // we have to do this so the hashtable keys don't change if the user isn't + // passing in a persistent string. + hashCode.m_Data = newStr; + + theFind = m_HashToStrMap.insert(eastl::make_pair(hashCode, newStr)).first; + m_HandleToStrMap.insert(eastl::make_pair(theFind->second.m_Handle, newStr)); + } + return theFind->second; + } + + const CRegisteredString FindStr(SCharAndHash hashCode) + { + SCharAndHandle result = DoFindStr(hashCode); + if (result.m_Data) + return CRegisteredString::ISwearThisHasBeenRegistered(result.m_Data); + return CRegisteredString(); + } + + const CStringHandle FindStrHandle(SCharAndHash hashCode) + { + SCharAndHandle result = DoFindStr(hashCode); + return CStringHandle::ISwearThisHasBeenRegistered(result.m_Handle); + } + + const CRegisteredString FindStrByHandle(QT3DSU32 handle) + { + if (handle == 0) + return CRegisteredString(); + + THandleMapType::iterator theFind = m_HandleToStrMap.find(handle); + if (theFind == m_HandleToStrMap.end()) { + const char8_t *newStr = FindStrByHandleData(handle); + if (!newStr) + return CRegisteredString(); + theFind = m_HandleToStrMap.insert(eastl::make_pair(handle, newStr)).first; + } + return CRegisteredString::ISwearThisHasBeenRegistered(theFind->second); + } + + void Save(SWriteBuffer &ioBuffer) const + { + // Buffer should be aligned before we get to it. + QT3DS_ASSERT(ioBuffer.size() % 4 == 0); + + QT3DSU32 numStrs = Size(); + size_t bufferBegin = ioBuffer.size(); + SStringFileHeader theHeader; + ioBuffer.write(theHeader); + QT3DSU32 startOffset = ioBuffer.size(); + + eastl::vector<eastl::pair<size_t, QT3DSU32>> hashToStringIndex; + eastl::vector<eastl::pair<QT3DSU32, QT3DSU32>> handleToStringIndex; + hashToStringIndex.reserve(numStrs); + handleToStringIndex.reserve(numStrs); + WriteStringBlockData(m_DataBlock.begin(), m_DataBlock.end(), ioBuffer, hashToStringIndex, + handleToStringIndex); + WriteStringBlockData(m_AppendedStrings.data(), + m_AppendedStrings.data() + m_AppendedStrings.size(), ioBuffer, + hashToStringIndex, handleToStringIndex); + ioBuffer.align(4); + + QT3DSU32 fixedOffset = ioBuffer.size() - startOffset; + WriteStringBlock(m_DataBlock.begin(), m_DataBlock.end(), ioBuffer); + WriteStringBlock(m_AppendedStrings.data(), + m_AppendedStrings.data() + m_AppendedStrings.size(), ioBuffer); + + // sort by hash code so we can do binary lookups later. + eastl::sort(hashToStringIndex.begin(), hashToStringIndex.end(), SHashPairComparator()); + ioBuffer.write(hashToStringIndex.data(), (QT3DSU32)hashToStringIndex.size()); + + eastl::sort(handleToStringIndex.begin(), handleToStringIndex.end(), + SHandlePairComparator()); + ioBuffer.write(handleToStringIndex.data(), (QT3DSU32)handleToStringIndex.size()); + + SStringFileHeader *fixedOffsetPtr = + reinterpret_cast<SStringFileHeader *>(ioBuffer.begin() + bufferBegin); + *fixedOffsetPtr = SStringFileHeader(numStrs, m_NextHandleValue, fixedOffset); + } + + void Load(NVDataRef<QT3DSU8> inMemory) + { + QT3DS_ASSERT(m_AppendedStrings.empty()); + m_Allocator.m_PreAllocatedBlock = inMemory; + m_Allocator.m_OwnsMemory = false; + SDataReader theReader(inMemory.begin(), inMemory.end()); + SStringFileHeader theHeader = theReader.LoadRef<SStringFileHeader>(); + QT3DSU32 numStrs = theHeader.m_NumStrings; + m_NextHandleValue = theHeader.m_NextHandleValue; + QT3DSU32 fixedOffset = theHeader.m_FixedBufferOffset; + theReader.m_CurrentPtr += fixedOffset; + m_DataBlock = NVConstDataRef<SStringFileData>( + reinterpret_cast<const SStringFileData *>(theReader.m_CurrentPtr), numStrs); + theReader.m_CurrentPtr += sizeof(SStringFileData) * numStrs; + + m_HashDataBlockOffset = NVConstDataRef<eastl::pair<size_t, QT3DSU32>>( + reinterpret_cast<const eastl::pair<size_t, QT3DSU32> *>(theReader.m_CurrentPtr), numStrs); + theReader.m_CurrentPtr += sizeof(eastl::pair<size_t, QT3DSU32>) * numStrs; + + m_HandleDataBlockOffset = NVConstDataRef<eastl::pair<QT3DSU32, QT3DSU32>>( + reinterpret_cast<const eastl::pair<QT3DSU32, QT3DSU32> *>(theReader.m_CurrentPtr), numStrs); + + for (QT3DSU32 idx = 0, end = m_DataBlock.size(); idx < end; ++idx) { + SStringFileData &theData = const_cast<SStringFileData &>(m_DataBlock[idx]); + theData.m_Str = reinterpret_cast<char8_t *>(inMemory.mData + theData.m_StartOffset); + } + } + +protected: + // When we load from a file, we do not put every string into the hash because building + // hashtables takes + // time. We only put them into the hash on demand; this avoids building a giant hashtable upon + // load. + // and since in general we do not need to lookup all hash values again it avoids building the + // hashtable + // in general. + SCharAndHandle FindStrByHash(SCharAndHash hashCode) const + { + const eastl::pair<size_t, QT3DSU32> *iter = + eastl::lower_bound(m_HashDataBlockOffset.begin(), m_HashDataBlockOffset.end(), + eastl::make_pair(hashCode.m_Hash, (QT3DSU32)0), SHashPairComparator()); + + for (; iter != m_HashDataBlockOffset.end() && iter->first == hashCode.m_Hash; ++iter) { + if (AreEqual(m_DataBlock[iter->second].m_Str, hashCode.m_Data)) + return SCharAndHandle(m_DataBlock[iter->second].m_Str, + m_DataBlock[iter->second].m_Handle); + } + return SCharAndHandle(); + } + + const char8_t *FindStrByHandleData(QT3DSU32 handle) const + { + const eastl::pair<QT3DSU32, QT3DSU32> *iter = + eastl::lower_bound(m_HandleDataBlockOffset.begin(), m_HandleDataBlockOffset.end(), + eastl::make_pair(handle, (QT3DSU32)0), SHandlePairComparator()); + + if (iter != m_HandleDataBlockOffset.end() && iter->first == handle + && m_DataBlock[iter->second].m_Handle == handle) + return m_DataBlock[iter->second].m_Str; + return nullptr; + } + + void WriteStringBlockData(const SStringFileData *begin, const SStringFileData *end, + SWriteBuffer &ioBuffer, + eastl::vector<eastl::pair<size_t, QT3DSU32>> &ioHashToStringIndex, + eastl::vector<eastl::pair<QT3DSU32, QT3DSU32>> &ioHandleToStringIndex) const + { + for (const SStringFileData *iter = begin; iter != end; ++iter) { + size_t idx = ioHashToStringIndex.size(); + SCharAndHash theHash(iter->m_Str); + ioHashToStringIndex.push_back(eastl::make_pair(theHash.m_Hash, (QT3DSU32)idx)); + ioHandleToStringIndex.push_back(eastl::make_pair(iter->m_Handle, (QT3DSU32)idx)); + ioBuffer.write(iter->m_Str, iter->m_StrLen); + } + } + + void WriteStringBlock(const SStringFileData *begin, const SStringFileData *end, + SWriteBuffer &ioBuffer) const + { + for (const SStringFileData *iter = begin; iter != end; ++iter) + ioBuffer.write(*iter); + } + + SCharAndHandle Append(SCharAndHash inStrHash) + { + if (isTrivial(inStrHash.m_Data)) + return SCharAndHandle(); + + const char8_t *inStr(inStrHash.m_Data); + QT3DSU32 len = (QT3DSU32)strlen(inStr) + 1; + char8_t *newStr = (char8_t *)m_Allocator.allocate(len, "StringData", __FILE__, __LINE__); + memCopy(newStr, inStr, len); + + // We write the number of strings to the file just after the header. + QT3DSU32 theOffset = sizeof(SStringFileHeader); + if (Size()) + theOffset = Back().EndOffset(); + + QT3DSU32 handleValue = m_NextHandleValue; + ++m_NextHandleValue; + m_AppendedStrings.push_back(SStringFileData(len, theOffset, handleValue, newStr)); + + // Only add to the str map if necessary because building it could be expensive. + if (m_StrRemapMap.empty() == false) + m_StrRemapMap.insert(eastl::make_pair(newStr, theOffset)); + + return SCharAndHandle(newStr, handleValue); + } + // precondition is that size != 0 + const SStringFileData &Back() const + { + QT3DS_ASSERT(Size()); + + if (m_AppendedStrings.size()) { + return m_AppendedStrings.back(); + } else { + return m_DataBlock[m_DataBlock.size() - 1]; + } + } + + QT3DSU32 Size() const { return static_cast<QT3DSU32>(m_AppendedStrings.size() + m_DataBlock.size()); } +}; + +struct SStringTableMutexScope +{ + Mutex *m_Mutex; + SStringTableMutexScope(Mutex *inM) + : m_Mutex(inM) + { + if (m_Mutex) + m_Mutex->lock(); + } + ~SStringTableMutexScope() + { + if (m_Mutex) + m_Mutex->unlock(); + } +}; + +#define STRING_TABLE_MULTITHREADED_METHOD SStringTableMutexScope __locker(m_MultithreadMutex) + +class StringTable : public IStringTable +{ + typedef nvhash_map<SCharAndHash, QT3DSU32> TMapType; + typedef nvhash_map<CRegisteredString, TWideStr> TNarrowToWideMapType; + SStringFileDataList m_FileData; + NVAllocatorCallback &m_Allocator; + TNarrowToWideMapType m_StrWideMap; + volatile QT3DSI32 mRefCount; // fnd's naming convention + eastl::basic_string<char8_t, ForwardingAllocator> m_ConvertBuffer; + TWideStr m_WideConvertBuffer; + Mutex m_MultithreadMutexBacker; + Mutex *m_MultithreadMutex; + // Data that will be written out to the file. + +public: + StringTable(NVAllocatorCallback &alloc) + : m_FileData(alloc) + , m_Allocator(m_FileData.m_Allocator) + , m_StrWideMap(alloc, "StringTable::m_StrWideMap") + , mRefCount(0) + , m_ConvertBuffer(ForwardingAllocator(alloc, "StringTable::m_ConvertBuffer")) + , m_WideConvertBuffer(ForwardingAllocator(alloc, "StringTable::m_WideConvertBuffer")) + , m_MultithreadMutexBacker(alloc) + , m_MultithreadMutex(nullptr) + { + } + + virtual ~StringTable() override {} + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_FileData.m_Allocator.m_Allocator) + + void EnableMultithreadedAccess() override + { + m_MultithreadMutex = &m_MultithreadMutexBacker; + } + + void DisableMultithreadedAccess() override + { + m_MultithreadMutex = nullptr; + } + + CRegisteredString RegisterStr(const QString &str) override + { + STRING_TABLE_MULTITHREADED_METHOD; + if (str.isEmpty()) + return CRegisteredString(); + return RegisterStr(str.toUtf8()); + } + + CRegisteredString RegisterStr(const QByteArray &str) override + { + STRING_TABLE_MULTITHREADED_METHOD; + if (str.isEmpty()) + return CRegisteredString(); + return RegisterStr(str.constData()); + } + + CRegisteredString RegisterStr(const eastl::string &string) override + { + STRING_TABLE_MULTITHREADED_METHOD; + return RegisterStr(string.c_str()); + } + + CRegisteredString RegisterStr(Qt3DSBCharPtr str) override + { + STRING_TABLE_MULTITHREADED_METHOD; + if (isTrivial(str)) + return CRegisteredString(); + return m_FileData.FindStr(str); + } + + // utf-16->utf-8 + CRegisteredString RegisterStr(const char16_t *str) override + { + STRING_TABLE_MULTITHREADED_METHOD; + qt3ds::foundation::ConvertUTF(str, 0, m_ConvertBuffer); + return RegisterStr(m_ConvertBuffer.c_str()); + } + // utf-32->utf-8 + CRegisteredString RegisterStr(const char32_t *str) override + { + STRING_TABLE_MULTITHREADED_METHOD; + qt3ds::foundation::ConvertUTF(str, 0, m_ConvertBuffer); + return RegisterStr(m_ConvertBuffer.c_str()); + } + + CRegisteredString RegisterStr(const wchar_t *str) override + { + STRING_TABLE_MULTITHREADED_METHOD; + if (isTrivialWide(str)) + return CRegisteredString(); + qt3ds::foundation::ConvertUTF( + reinterpret_cast<const TWCharEASTLConverter::TCharType *>(str), 0, + m_ConvertBuffer); + return RegisterStr(m_ConvertBuffer.c_str()); + } + + CStringHandle GetHandle(Qt3DSBCharPtr str) override + { + STRING_TABLE_MULTITHREADED_METHOD; + return m_FileData.FindStrHandle(str); + } + + CRegisteredString HandleToStr(QT3DSU32 strHandle) override + { + STRING_TABLE_MULTITHREADED_METHOD; + return m_FileData.FindStrByHandle(strHandle); + } + + const wchar_t *GetWideStr(CRegisteredString theStr) + { + STRING_TABLE_MULTITHREADED_METHOD; + eastl::pair<TNarrowToWideMapType::iterator, bool> pair_iter = m_StrWideMap.insert( + eastl::make_pair(theStr, TWideStr(ForwardingAllocator(m_Allocator, "WideString")))); + if (pair_iter.second) + qt3ds::foundation::ConvertUTF(theStr.c_str(), 0, pair_iter.first->second); + return reinterpret_cast<const wchar_t *>(pair_iter.first->second.c_str()); + } + + const wchar_t *GetWideStr(Qt3DSBCharPtr src) override + { + STRING_TABLE_MULTITHREADED_METHOD; + if (isTrivial(src)) + return L""; + return GetWideStr(RegisterStr(src)); + } + + const wchar_t *GetWideStr(const wchar_t *str) override + { + STRING_TABLE_MULTITHREADED_METHOD; + if (isTrivialWide(str)) + return L""; + qt3ds::foundation::ConvertUTF( + reinterpret_cast<const TWCharEASTLConverter::TCharType *>(str), 0, + m_ConvertBuffer); + return GetWideStr(RegisterStr(m_ConvertBuffer.c_str())); + } + + const SStrRemapMap &GetRemapMap() override { return m_FileData.GetRemapMap(); } + + NVAllocatorCallback &GetAllocator() override { return m_FileData.m_Allocator.m_Allocator; } + + // Save to a block of memory. It is up the callers to deallocate using allocator + // from GetAllocator(). Returns a remap map that takes existing strings and changes their + // address such that they are offsets into the block of memory where this object + // started saving. Returns a map that converts registered strings into offsets of the + // written buffer. + void Save(SWriteBuffer &ioBuffer) const override + { + STRING_TABLE_MULTITHREADED_METHOD; + m_FileData.Save(ioBuffer); + } + + // Load all the strings from inMemory. inMemory is not freed by this object + // Finally, you will need to take inMemory and call remap on all strings + // from offsets back into their correct address into inMemory. + void Load(qt3ds::foundation::NVDataRef<QT3DSU8> inMemory) override + { + STRING_TABLE_MULTITHREADED_METHOD; + m_FileData.Load(inMemory); + } +}; +} + +const char16_t *char16EmptyStr = g_char16EmptyStr; +const char32_t *char32EmptyStr = g_char32EmptyStr; + +IStringTable &IStringTable::CreateStringTable(NVAllocatorCallback &alloc) +{ + return *QT3DS_NEW(alloc, StringTable)(alloc); +} diff --git a/src/foundation/StringTable.h b/src/foundation/StringTable.h new file mode 100644 index 0000000..d46c111 --- /dev/null +++ b/src/foundation/StringTable.h @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_STRING_TABLE_H +#define QT3DS_RENDER_STRING_TABLE_H + +#if defined(_WIN32) || defined(__QNXNTO__) +#include <wchar.h> +#ifndef WIDE_IS_DIFFERENT_TYPE_THAN_CHAR16_T +#define WIDE_IS_DIFFERENT_TYPE_THAN_CHAR16_T +#endif +#endif + +#include <QtCore/qstring.h> +#include <QtCore/qbytearray.h> + +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/Qt3DSAllocator.h" +#include "EASTL/string.h" +#include "EASTL/functional.h" +#include "EABase/eabase.h" //char16_t definition +#include "foundation/Qt3DSDataRef.h" +#include "foundation/Qt3DSOption.h" + +#include <QtCore/qstring.h> +#include <QtCore/qhashfunctions.h> + +namespace qt3ds { +namespace foundation { + typedef char8_t Qt3DSBChar; + typedef const char8_t *Qt3DSBCharPtr; + class IStringTable; + + // Serialization types, NVRenderSerializationTypes.h + struct SStrRemapMap; + struct SWriteBuffer; + + class CRegisteredString; + + // Discriminated union of either data ref or str table + // used so clients can test serialization without actually saving + // the string table out and rebooting the entire system. + class CStrTableOrDataRef + { + Option<NVDataRef<QT3DSU8>> m_DataRef; + IStringTable *m_StrTable; + + public: + CStrTableOrDataRef() + : m_StrTable(nullptr) + { + } + CStrTableOrDataRef(NVDataRef<QT3DSU8> inData) + : m_DataRef(inData) + { + } + CStrTableOrDataRef(IStringTable &inStrTable) + : m_StrTable(&inStrTable) + { + } + CStrTableOrDataRef(const CStrTableOrDataRef &inOther) + : m_DataRef(inOther.m_DataRef) + , m_StrTable(inOther.m_StrTable) + { + } + CStrTableOrDataRef &operator=(const CStrTableOrDataRef &inOther) + { + m_DataRef = inOther.m_DataRef; + m_StrTable = inOther.m_StrTable; + return *this; + } + bool HasStrTable() const { return m_StrTable != nullptr; } + bool HasDataRef() const { return m_DataRef.hasValue(); } + NVDataRef<QT3DSU8> GetDataRef() const { return *m_DataRef; } + IStringTable *GetStringTable() const + { + QT3DS_ASSERT(m_StrTable); + return m_StrTable; + } + }; + + // String that can only be constructed from strings in the string table. + // These strings are valid utf-8 strings + class CRegisteredString + { + Qt3DSBCharPtr m_String; + + public: + CRegisteredString() + : m_String("") + { + } + CRegisteredString(const CRegisteredString &inOther) + : m_String(inOther.m_String) + { + } + CRegisteredString &operator=(const CRegisteredString &inOther) + { + m_String = inOther.m_String; + return *this; + } + + bool operator==(const CRegisteredString &inStr) const { return m_String == inStr.m_String; } + // If you use this in a real map then you will get them sorted roughly by the correct + // order. + bool operator<(const char *inStr) const + { + // Ensure non-null strings to strcmp. + const char *myStr = m_String ? m_String : ""; + inStr = inStr ? inStr : ""; + int answer; + while (*myStr && *inStr) { + answer = *inStr - *myStr; + if (answer) + return answer < 0; + ++myStr; + ++inStr; + } + answer = *inStr - *myStr; + return answer < 0; + } + size_t hash() const { return eastl::hash<size_t>()(reinterpret_cast<size_t>(m_String)); } + operator Qt3DSBCharPtr() const { return c_str(); } + Qt3DSBCharPtr c_str() const { return m_String ? m_String : ""; } + bool IsValid() const { return m_String && *m_String; } + + // If this string is in the map, changes it to the map value. + void Remap(const SStrRemapMap &inMap); + + void Remap(const IStringTable &inTable); + + // Using m_String as an offset, if possible remap into + // inDataPtr's block of memory + void Remap(NVDataRef<QT3DSU8> inDataPtr); + + void Remap(const CStrTableOrDataRef &inRef) + { + if (inRef.HasDataRef()) + Remap(inRef.GetDataRef()); + else if (inRef.HasStrTable()) + Remap(*inRef.GetStringTable()); + else { + QT3DS_ASSERT(false); + } + } + + static CRegisteredString ISwearThisHasBeenRegistered(Qt3DSBCharPtr str) + { + CRegisteredString retval; + retval.m_String = str; + return retval; + } + }; + + inline uint qHash(const CRegisteredString &rString) + { + // Note that we are intentionally hashing a pointer to the string rather than its content, + // as CRegisteredString strings reside in the string table, which guarantees two instances + // with same string point to same string table string. + return QT_PREPEND_NAMESPACE(qHash(rString.c_str())); + } + + class IStringTable; + + class CStringHandle + { + QT3DSU32 m_Data; + + CStringHandle(QT3DSU32 hdl) + : m_Data(hdl) + { + } + + public: + CStringHandle() + : m_Data(0) + { + } + CStringHandle(const CStringHandle &other) + : m_Data(other.m_Data) + { + } + CStringHandle &operator=(const CStringHandle &other) + { + m_Data = other.m_Data; + return *this; + } + bool IsValid() const { return m_Data != 0; } + QT3DSU32 handle() const { return m_Data; } + inline const char8_t *c_str(IStringTable &strTable) const; + operator QT3DSU32() const { return m_Data; } + static CStringHandle ISwearThisHasBeenRegistered(QT3DSU32 strHandle) + { + return CStringHandle(strHandle); + } + }; + + // String table stores strings in utf-8 format and does valid utf-16,utf-32 -> utf-8 + // also converts utf8 -> either utf-32 or utf-16, depending on sizeof( wchar_t ) if + // requested. + // UICStrConvertUTF.h + // Also generates offsets that are consistent so clients can convert their strings + // to offsets during save and convert from offset to string during load regardless + // of if they load before or after this table. + class QT3DS_AUTOTEST_EXPORT IStringTable : public NVRefCounted + { + public: + // default state is for multithreaded access to be disabled. + // remember to implement in CNullStringManager in UICTestPresentation.hxx + virtual void EnableMultithreadedAccess() = 0; + virtual void DisableMultithreadedAccess() = 0; + + virtual CRegisteredString RegisterStr(const QByteArray &str) = 0; + virtual CRegisteredString RegisterStr(const QString &str) = 0; + virtual CRegisteredString RegisterStr(const eastl::string &string) = 0; + + virtual CRegisteredString RegisterStr(Qt3DSBCharPtr str) = 0; + // utf-16->utf-8 + virtual CRegisteredString RegisterStr(const char16_t *str) = 0; + // utf-32->utf-8 + virtual CRegisteredString RegisterStr(const char32_t *str) = 0; + + virtual CStringHandle GetHandle(Qt3DSBCharPtr str) = 0; + virtual CRegisteredString HandleToStr(QT3DSU32 strHandle) = 0; + + virtual CRegisteredString RegisterStr(const wchar_t *str) = 0; + + virtual const wchar_t *GetWideStr(Qt3DSBCharPtr src) = 0; + virtual const wchar_t *GetWideStr(const wchar_t *str) = 0; + + Qt3DSBCharPtr GetNarrowStr(const wchar_t *src) { return RegisterStr(src); } + Qt3DSBCharPtr GetNarrowStr(Qt3DSBCharPtr src) { return RegisterStr(src); } + + // The string table maintains a map that will tell clients where their strings will be + // in terms of offsets into the data that the string table writes. + // This map will change each time a new string is added to the string table. + // Conversion from string -> data offset. + virtual const SStrRemapMap &GetRemapMap() = 0; + + virtual NVAllocatorCallback &GetAllocator() = 0; + + // Save to a block of memory. It is up the callers to deallocate using allocator + // from GetAllocator(). Returns a remap map that takes existing strings and changes their + // address such that they are offsets into the block of memory where this object + // started saving. Returns a map that converts registered strings into offsets of the + // written buffer. + virtual void Save(SWriteBuffer &ioBuffer) const = 0; + + // Load all the strings from inMemory. inMemory is not freed by this object + // Finally, you will need to take inMemory and call remap on all strings + // from offsets back into their correct address into inMemory. + virtual void Load(qt3ds::foundation::NVDataRef<QT3DSU8> inMemory) = 0; + + static IStringTable &CreateStringTable(NVAllocatorCallback &alloc); + }; + + inline const char8_t *CStringHandle::c_str(IStringTable &strTable) const + { + return strTable.HandleToStr(m_Data); + } +} +} + +// eastl extensions to allow easy hashtable creation using string table strings. +namespace eastl { + +template <> +struct hash<qt3ds::foundation::CRegisteredString> +{ + size_t operator()(const qt3ds::foundation::CRegisteredString &str) const { return str.hash(); } +}; + +template <> +struct equal_to<qt3ds::foundation::CRegisteredString> +{ + bool operator()(const qt3ds::foundation::CRegisteredString &lhs, + const qt3ds::foundation::CRegisteredString &rhs) const + { + return lhs == rhs; + } +}; +} + +#endif diff --git a/src/foundation/StringTools.h b/src/foundation/StringTools.h new file mode 100644 index 0000000..43324b4 --- /dev/null +++ b/src/foundation/StringTools.h @@ -0,0 +1,614 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_STRING_H +#define QT3DS_RENDER_STRING_H +#include <QtCore/qstring.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qhash.h> +#include <string> + +#include "Qt3DSRender.h" +#include "EASTL/string.h" + +namespace qt3ds { +namespace foundation { + +class Qt3DSStringUtils { +public: + static inline QString ConvertUTFtoQString(const char16_t *string) + { + return QString::fromUtf16(string); + } + + static inline QString ConvertUTFtoQString(const char32_t *string) + { + return QString::fromUcs4(string); + } + + static inline QString ConvertUTFtoQString(const wchar_t *string) + { + return QString::fromWCharArray(string); + } + + static inline QString &append(QString &target, QString::iterator begin, + QString::iterator end) + { + for (QString::iterator iter = begin;iter != end;++iter) + target.append(*iter); + return target; + } + + static inline QString &append(QString &target, QString::const_iterator begin, + QString::const_iterator end) + { + for (QString::const_iterator iter = begin;iter != end;++iter) + target.append(*iter); + return target; + } + + /** + * @brief replace Replaces specified portion of a string. + * @param source Source text that will be used. Note: This is const and will + * not be directly modified. + * @param first First character in the range that will be replaced. + * @param last Last character in the range that will be replaced. + * @param replaceText Pointer to the character string to use for replacement. + * @param len Length of the replacement character string. + * @return Returns a new QString containing the modified result. + */ + static inline QString replace(const QString &source, + QString::const_iterator first, + QString::const_iterator last, + const char *replaceText, int len) + { + return replace(source, first, last, QString::fromUtf8(replaceText, len)); + } + + /** + * @brief replace Replaces specified portion of a string. + * @param source Source text that will be used. Note: This is const and will + * not be directly modified. + * @param first First character in the range that will be replaced. + * @param last Last character in the range that will be replaced. + * @param replaceText String to use for replacement. + * @return Returns a new QString containing the modified result. + */ + static inline QString replace(const QString &source, + QString::const_iterator first, + QString::const_iterator last, + const QString &replaceText) + { + QString newStr; + for (QString::const_iterator iter = source.constBegin(); iter != first; + iter++) + newStr.append(*iter); + + newStr.append(replaceText); + + for (QString::const_iterator iter = last; iter != source.constEnd(); + iter++) + newStr.append(*iter); + return newStr; + } + + /** + * @brief replace Replaces specified portion of a string. + * @param source Source text that will be used. Note: This is const and + * will not be directly modified. + * @param first First character in the range that will be replaced. + * @param last Last character in the range that will be replaced. + * @param replaceText Pointer to the character string to use for replacement. + * @param len Length of the replacement character string. + * @return Returns a temporary object allocated on the stack containing + * the modified result. + */ + static inline QString &replace(QString &target, + QString::iterator first, + QString::iterator last, + const char *replaceText, int len) + { + return replace(target, first, last, QString::fromUtf8(replaceText, len)); + } + + /** + * @brief replace Replaces specified portion of a string. + * @param target Target QString that will be modified. + * @param first First character in the range that will be replaced. + * @param last Last character in the range that will be replaced. + * @param replaceText String to use for replacement. + * @return Returns the string given in target, containing the modified result. + */ + static inline QString &replace(QString &target, + QString::iterator first, + QString::iterator last, + const QString &replaceText) + { + QString newStr; + for (QString::iterator iter = target.begin(); iter != first; iter++) + newStr.append(*iter); + + newStr.append(replaceText); + + for (QString::iterator iter = last; iter != target.end(); iter++) + newStr.append(*iter); + + target = newStr; + return target; + } +}; + +class Qt3DSString { +public: + inline Qt3DSString() {} + + Qt3DSString(QChar c) : m_string(QString(c)) {} + + Qt3DSString(int size, QChar c) : m_string(size, c) {} + + inline Qt3DSString(QLatin1String latin1) + : m_string(latin1) {} + + explicit Qt3DSString(const QChar *unicode, int size = -1) + : m_string(unicode,size) {} + + inline Qt3DSString(const QString &str) Q_DECL_NOTHROW + : m_string(str) {} + + inline Qt3DSString(const Qt3DSString &str) Q_DECL_NOTHROW + : m_string(str.m_string) {} + + ~Qt3DSString() {} + + inline operator QString() const + { + return m_string; + } + + inline void operator=(const Qt3DSString &text) + { + m_string = text.m_string; + m_isDirty = true; + } + + inline void operator=(const QString &text) + { + m_string = text; + m_isDirty = true; + } + + // QString method wrappers + static inline Qt3DSString fromUtf8(const char *str, int size = -1) + { + return Qt3DSString(QString::fromUtf8(str, size)); + } + + typedef int size_type; + typedef qptrdiff difference_type; + typedef const QChar & const_reference; + typedef QChar & reference; + typedef QChar *pointer; + typedef const QChar *const_pointer; + typedef QChar value_type; + typedef QChar *iterator; + typedef const QChar *const_iterator; + typedef iterator Iterator; + typedef const_iterator ConstIterator; + typedef std::reverse_iterator<iterator> reverse_iterator; + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + inline iterator begin() { return m_string.begin(); } + inline const_iterator begin() const { return m_string.begin(); } + inline const_iterator cbegin() const { return m_string.cbegin(); } + inline const_iterator constBegin() const { return m_string.constBegin(); } + inline iterator end() { return m_string.end(); } + inline const_iterator end() const { return m_string.end(); } + inline const_iterator cend() const { return m_string.cend(); } + inline const_iterator constEnd() const { return m_string.constEnd(); } + reverse_iterator rbegin() { return reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + const_reverse_iterator crbegin() const { return const_reverse_iterator(end()); } + const_reverse_iterator crend() const { return const_reverse_iterator(begin()); } + + inline void push_back(QChar c) + { + m_string.push_back(c); + m_isDirty = true; + } + + inline void push_back(const QString &s) + { + m_string.push_back(s); + m_isDirty = true; + } + + inline void push_front(QChar c) + { + m_string.push_front(c); + m_isDirty = true; + } + + inline void push_front(const QString &s) + { + m_string.push_front(s); + m_isDirty = true; + } + + inline bool operator==(const QString &rhs) + { + return rhs == m_string; + } + + inline bool operator<(const QString &rhs) + { + return m_string < rhs; + } + + inline bool operator!=(const QString &rhs) + { + return !(*this == rhs); + } + + inline bool operator>(const QString& rhs) + { + return (rhs < m_string); + } + + inline bool operator<=(const QString& rhs) + { + return !operator> (rhs); + } + + inline bool operator>=(const QString& rhs) + { + return !operator< (rhs); + } + + inline Qt3DSString& operator+=(const QString &rhs) + { + m_string += rhs; + m_isDirty = true; + return *this; + } + + inline Qt3DSString& operator+=(const char *s) + { + m_string += s; + m_isDirty = true; + return *this; + } + + inline bool isEmpty() const + { + return m_string.isEmpty(); + } + + int indexOf(const QString &str, int from = 0, + Qt::CaseSensitivity cs = Qt::CaseSensitive) const + { + return m_string.indexOf(str, from, cs); + } + + int indexOf(QChar ch, int from = 0, + Qt::CaseSensitivity cs = Qt::CaseSensitive) const + { + return m_string.indexOf(ch, from, cs); + } + + int indexOf(QLatin1String str, int from = 0, + Qt::CaseSensitivity cs = Qt::CaseSensitive) const + { + return m_string.indexOf(str, from, cs); + } + + int indexOf(const QStringRef &str, int from = 0, + Qt::CaseSensitivity cs = Qt::CaseSensitive) const + { + return m_string.indexOf(str, from, cs); + } + + int lastIndexOf(const QString &str, int from = -1, + Qt::CaseSensitivity cs = Qt::CaseSensitive) const + { + return m_string.lastIndexOf(str, from, cs); + } + + int lastIndexOf(QChar ch, int from = -1, + Qt::CaseSensitivity cs = Qt::CaseSensitive) const + { + return m_string.lastIndexOf(ch, from, cs); + } + + int lastIndexOf(QLatin1String str, int from = -1, + Qt::CaseSensitivity cs = Qt::CaseSensitive) const + { + return m_string.lastIndexOf(str, from, cs); + } + + int lastIndexOf(const QStringRef &str, int from = -1, + Qt::CaseSensitivity cs = Qt::CaseSensitive) const + { + return m_string.lastIndexOf(str, from, cs); + } + + inline void clear() + { + m_string.clear(); + m_isDirty = true; + } + + inline Qt3DSString &insert(int i, QChar c) + { + m_string.insert(i,c); + m_isDirty = true; + return *this; + } + inline Qt3DSString &insert(int i, const QChar *uc, int len) + { + m_string.insert(i,uc, len); + m_isDirty = true; + return *this; + } + inline Qt3DSString &insert(int i, const QString &s) + { + m_string.insert(i,s); + m_isDirty = true; + return *this; + } + inline Qt3DSString &insert(int i, const QStringRef &s) + { + m_string.insert(i,s); + m_isDirty = true; + return *this; + } + + inline Qt3DSString &insert(int i, QLatin1String s) + { + m_string.insert(i,s); + m_isDirty = true; + return *this; + } + + inline int size() const + { + return m_string.size(); + } + + inline int length() const { + return m_string.length(); + } + + inline Qt3DSString &append(QChar c) + { + m_string.append(c); + m_isDirty = true; + return *this; + } + + inline Qt3DSString &append(const QChar *uc, int len) + { + m_string.append(uc, len); + m_isDirty = true; + return *this; + } + + inline Qt3DSString &append(const QString &s) + { + m_string.append(s); + m_isDirty = true; + return *this; + } + + inline Qt3DSString &append(const QStringRef &s) + { + m_string.append(s); + m_isDirty = true; + return *this; + } + + inline Qt3DSString &append(QLatin1String s) + { + m_string.append(s); + m_isDirty = true; + return *this; + } + + inline int compare(const QString &s, + Qt::CaseSensitivity cs = Qt::CaseSensitive) const + { + return m_string.compare(s, cs); + } + + // Compatibility wrappers for std::basic_string and eastl::basic_string + // Required for now for the Qt 3D Studio's template classes. + static const int npos = -1; + + inline char operator[](int idx) const + { + return m_string[idx].toLatin1(); + } + + inline void assign(const char *text) + { + m_string = QString::fromUtf8(text); + m_isDirty = true; + } + + inline void assign(const char16_t *text) + { + m_string = QString::fromUtf16(text); + m_isDirty = true; + } + + inline void assign(const char32_t *text) + { + m_string = QString::fromUcs4(text); + m_isDirty = true; + } + + inline void assign(const wchar_t *text) + { + m_string = QString::fromWCharArray(text); + m_isDirty = true; + } + + inline void assign(const eastl::string &text) + { + m_string = QString::fromUtf8(text.c_str()); + m_isDirty = true; + } + + inline void operator=(const char *text) + { + assign(text); + } + + inline void operator=(const char16_t *text) + { + assign(text); + } + + inline void operator=(const char32_t *text) + { + assign(text); + } + + inline void operator=(const wchar_t *text) + { + assign(text); + } + + inline void operator=(const eastl::string &text) + { + assign(text); + } + + inline void operator=(const eastl::basic_string<char*> &text) + { + assign(*text.c_str()); + } + + inline Qt3DSString &append(QString::iterator first, + QString::iterator last) + { + Qt3DSStringUtils::append(m_string, first, last); + m_isDirty = true; + return *this; + } + + inline Qt3DSString &append(QString::const_iterator first, + QString::const_iterator last) + { + Qt3DSStringUtils::append(m_string, first, last); + m_isDirty = true; + return *this; + } + + inline Qt3DSString &append(const char *text, int size) + { + m_string.append(QString::fromUtf8(text, size)); + m_isDirty = true; + return *this; + } + + inline Qt3DSString &append(const char *text) + { + m_string.append(QString::fromUtf8(text)); + m_isDirty = true; + return *this; + } + + inline Qt3DSString &replace(QString::iterator replaceBegin, + QString::iterator replaceEnd, + const char *replaceText, int len) + { + return replace(replaceBegin, replaceEnd, + QString::fromUtf8(replaceText, len)); + } + + inline Qt3DSString &replace(QString::iterator replaceBegin, + QString::iterator replaceEnd, + const QString &replaceText) + { + m_string = Qt3DSStringUtils::replace(this->m_string, + replaceBegin, replaceEnd, + replaceText); + m_isDirty = true; + return *this; + } + + inline Qt3DSString &replace(QString::const_iterator replaceBegin, + QString::const_iterator replaceEnd, + const QString &replaceText) + { + m_string = Qt3DSStringUtils::replace(this->m_string, + replaceBegin, replaceEnd, + replaceText); + m_isDirty = true; + return *this; + } + + inline QByteArray toUtf8() const + { + updateCache(); + return m_array; + } + + inline const char *c_str() const + { + updateCache(); + return m_array.constData(); + } + + inline const char *c_str() + { + updateCache(); + return m_array.constData(); + } + +private: + inline void updateCache() const + { + if (m_isDirty) { + m_array = m_string.toUtf8(); + m_isDirty = false; + } + } + + QString m_string; + mutable bool m_isDirty = true; + mutable QByteArray m_array; +}; + +} +} + +#endif diff --git a/src/foundation/TaggedPointer.h b/src/foundation/TaggedPointer.h new file mode 100644 index 0000000..aa22725 --- /dev/null +++ b/src/foundation/TaggedPointer.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_FOUNDATION_TAGGED_POINTER_H +#define QT3DS_FOUNDATION_TAGGED_POINTER_H +#include "foundation/StringTable.h" + +namespace qt3ds { +namespace foundation { + + template <typename TDataType> + struct SPointerTag + { + /* Expected API for runtime RTTI + static CRegisteredString GetTag() { return g_dtype_specific_string; } + */ + }; + + // A pointer tagged with an identifier so we can have generic + // user data that is still somewhat typesafe. + struct STaggedPointer + { + void *m_UserData; + CRegisteredString m_Tag; + STaggedPointer() + : m_UserData(NULL) + { + } + + STaggedPointer(void *inUserData, CRegisteredString inTag) + : m_UserData(inUserData) + , m_Tag(inTag) + { + } + + template <typename TDataType> + STaggedPointer(TDataType *inType) + : m_UserData(reinterpret_cast<void *>(inType)) + , m_Tag(SPointerTag<TDataType>::GetTag()) + { + } + + template <typename TDataType> + TDataType *DynamicCast() const + { + if (m_Tag == SPointerTag<TDataType>::GetTag()) + return reinterpret_cast<TDataType *>(m_UserData); + return NULL; + } + }; +} +} +#endif
\ No newline at end of file diff --git a/src/foundation/ThreadSafeQueue.h b/src/foundation/ThreadSafeQueue.h new file mode 100644 index 0000000..462b6d4 --- /dev/null +++ b/src/foundation/ThreadSafeQueue.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DS_FOUNDATION_THREAD_SAFE_QUEUE_H +#define QT3DS_FOUNDATION_THREAD_SAFE_QUEUE_H +#include "foundation/Qt3DSMutex.h" +#include "foundation/Qt3DSSemaphore.h" +#include "EASTL/vector.h" + +namespace qt3ds { +namespace foundation { + + template <typename TObjType, typename TAllocator = EASTLAllocatorType> + class ThreadSafeQueue + { + public: + static const QT3DSU32 MaxQueue = 0xffffffff; + + protected: + eastl::vector<TObjType, TAllocator> mQueue; + Semaphore mGetSemaphore; + Semaphore mPutSemaphore; + Mutex mMutex; + const QT3DSU32 mMaxCount; + + public: + ThreadSafeQueue(NVAllocatorCallback &alloc, QT3DSU32 maxCount = MaxQueue) + : mMutex(alloc) + , mGetSemaphore(alloc, 0, maxCount) + , mPutSemaphore(alloc, maxCount, maxCount) + , mMaxCount(maxCount) + { + mQueue.clear(); + } + + bool push(const TObjType &obj, QT3DSU32 milliseconds = MaxQueue) + { + if (mPutSemaphore.wait(milliseconds)) { + Mutex::ScopedLock __locker(mMutex); + mQueue.push_back(obj); + mGetSemaphore.post(); + return true; + } + return false; + } + + bool pop(TObjType &obj, QT3DSU32 milliseconds = 0) + { + if (mGetSemaphore.wait(milliseconds)) { + Mutex::ScopedLock __locker(mMutex); + obj = mQueue.back(); + mQueue.pop_back(); + mPutSemaphore.post(); + return true; + } + return false; + } + + bool isFull() + { + Mutex::ScopedLock __locker(mMutex); + return mQueue.size() >= mMaxCount; + } + + bool isEmpty() + { + Mutex::ScopedLock __locker(mMutex); + return mQueue.size() == 0; + } + + void clear() + { + Mutex::ScopedLock __locker(mMutex); + while (!mQueue.empty()) { + if (mGetSemaphore.wait(MaxQueue)) { + mQueue.pop_back(); + mPutSemaphore.post(); + } + } + } + }; +} +} + +#endif
\ No newline at end of file diff --git a/src/foundation/TrackingAllocator.cpp b/src/foundation/TrackingAllocator.cpp new file mode 100644 index 0000000..242b0ac --- /dev/null +++ b/src/foundation/TrackingAllocator.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/TrackingAllocator.h" + +namespace qt3ds { +namespace foundation { + +CAllocator::~CAllocator() +{ + QT3DS_ASSERT(mAllocations.size() == 0); + MemInfo info; + for (PtrToInfoMap::iterator iter = mAllocations.begin(), end = mAllocations.end(); + iter != end; ++iter) { + info = iter->second; + qCCritical(INTERNAL_ERROR, "!!Outstanding allocation of %lu bytes from %s::%d, %s", + info.size, info.file, info.line, info.name ? info.name : ""); + } + QT3DS_UNUSED(info); +} + +} +} diff --git a/src/foundation/TrackingAllocator.h b/src/foundation/TrackingAllocator.h new file mode 100644 index 0000000..4fd5806 --- /dev/null +++ b/src/foundation/TrackingAllocator.h @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QT3DS_RENDER_ALLOCATOR_H +#define QT3DS_RENDER_ALLOCATOR_H +#include "foundation/Qt3DSAllocatorCallback.h" +#include "foundation/Qt3DSMutex.h" +#include "EASTL/hash_map.h" +#include "foundation/Qt3DSContainers.h" +#include "foundation/Qt3DSLogging.h" + +namespace qt3ds { +namespace foundation { + + // The simplest allocator. Performs no alignment nor memory checking. + struct MallocAllocator : public NVAllocatorCallback + { + void *allocate(size_t size, const char *, const char *, int, int = 0) override + { + return malloc(size); + } + void *allocate(size_t size, const char *, const char *, int, size_t, size_t) override + { + return malloc(size); + } + void deallocate(void *ptr) override { free(ptr); } + }; + + struct MemInfo + { + size_t size; + void *originalAddress; + const char *name; + const char *file; + int line; + MemInfo(size_t _size = 0, void *addr = 0, const char *_name = "", const char *_file = "", + int _line = 0) + : size(_size) + , originalAddress(addr) + , name(_name) + , file(_file) + , line(_line) + { + } + }; + + struct FileAndLine + { + const char *mFile; + QT3DSU32 mLine; + FileAndLine(const char *f, QT3DSU32 l) + : mFile(f) + , mLine(l) + { + } + }; +} // namespace foundation +} // namespace qt3ds + +namespace eastl { +template <> +struct hash<qt3ds::foundation::FileAndLine> +{ + size_t operator()(const qt3ds::foundation::FileAndLine &fl) const + { + return hash<const void *>()((const void *)fl.mFile) ^ hash<int>()(fl.mLine); + } +}; + +template <> +struct equal_to<qt3ds::foundation::FileAndLine> +{ + bool operator()(const qt3ds::foundation::FileAndLine &fl1, + const qt3ds::foundation::FileAndLine &fl2) const + { + size_t fl1File(reinterpret_cast<size_t>(fl1.mFile)); + size_t fl2File(reinterpret_cast<size_t>(fl2.mFile)); + + return fl1File == fl2File && fl1.mLine == fl2.mLine; + } +}; +} + +namespace qt3ds { +namespace foundation { + + struct MemInfoAndCount : MemInfo + { + QT3DSU32 mCount; + QT3DSU64 mTotalBytes; + MemInfoAndCount(const MemInfo &info) + : MemInfo(info) + , mCount(1) + , mTotalBytes(info.size) + { + } + void increment(const MemInfo &otherInfo) + { + ++mCount; + mTotalBytes += otherInfo.size; + } + }; + + typedef nvhash_map<FileAndLine, MemInfoAndCount> TFileAndLineMemHash; + + class AllocationCheckPrinter + { + protected: + virtual ~AllocationCheckPrinter() {} + public: + virtual void printMissing(const MemInfoAndCount &item) = 0; + virtual void printIncorrectCount(const MemInfoAndCount &later, + const MemInfoAndCount &earlier) = 0; + }; + + // All allocations are 16 byte aligned. + // Prints out unallocated memory at destruction. + class QT3DS_AUTOTEST_EXPORT CAllocator : public NVAllocatorCallback + { + public: + typedef Mutex TMutexType; + typedef Mutex::ScopedLock TLockType; + typedef nvhash_map<void *, MemInfo> PtrToInfoMap; + + private: + MallocAllocator mAlloc; + PtrToInfoMap mAllocations; + PtrToInfoMap mStoredAllocations; + TMutexType mMutex; + + public: + CAllocator() + : mAllocations(mAlloc, "MemInfo") + , mStoredAllocations(mAlloc, "MemInfo") + , mMutex(mAlloc) + { + } + virtual ~CAllocator(); + + void *allocate(size_t size, const char *tn, const char *fl, int ln, int flags) override + { + QT3DS_ASSERT(size); + if (size) { + size += 15; + TLockType locker(mMutex); + void *original = mAlloc.allocate(size, tn, fl, ln, flags); + size_t temp = (size_t)original; + temp = (temp + 15) & (~15); + void *retval = (void *)temp; + mAllocations.insert(eastl::make_pair(retval, MemInfo(size, original, tn, fl, ln))); + return retval; + } + return NULL; + } + void *allocate(size_t size, const char *tn, const char *fl, int ln, size_t, size_t) override + { + return allocate(size, tn, fl, ln, 0); + } + void deallocate(void *ptr) override + { + if (ptr) { + TLockType locker(mMutex); + PtrToInfoMap::iterator entry(mAllocations.find(ptr)); + if (entry != mAllocations.end()) { + mAlloc.deallocate(entry->second.originalAddress); + mAllocations.erase(ptr); + } else { + QT3DS_ASSERT(false); + } + } + } + const PtrToInfoMap &getOutstandingAllocations() const { return mAllocations; } + static void convert(const CAllocator::PtrToInfoMap &map, TFileAndLineMemHash &fl) + { + for (CAllocator::PtrToInfoMap::const_iterator iter = map.begin(), end = map.end(); + iter != end; ++iter) { + const MemInfo &info(iter->second); + FileAndLine flKey(info.file, info.line); + TFileAndLineMemHash::const_iterator find(fl.find(flKey)); + if (find == fl.end()) + fl.insert(eastl::make_pair(flKey, MemInfoAndCount(info))); + else + const_cast<MemInfoAndCount &>(find->second).increment(info); + } + } + static void copyMap(const CAllocator::PtrToInfoMap &map1, CAllocator::PtrToInfoMap &map2) + { + for (CAllocator::PtrToInfoMap::const_iterator iter = map1.begin(), end = map1.end(); + iter != end; ++iter) { + map2.insert(eastl::make_pair(iter->first, iter->second)); + } + } + + static void printDiffs(const TFileAndLineMemHash &fl1, const TFileAndLineMemHash &fl2, + AllocationCheckPrinter &printer) + { + using namespace std; + for (TFileAndLineMemHash::const_iterator iter = fl1.begin(), end = fl1.end(); + iter != end; ++iter) { + TFileAndLineMemHash::const_iterator entry = fl2.find(iter->first); + if (entry != fl2.end()) { + printer.printMissing(iter->second); + } else if (iter->second.mCount > entry->second.mCount) { + printer.printIncorrectCount(iter->second, entry->second); + } + } + } + + void printAllocationDiff(AllocationCheckPrinter &printer) + { + TFileAndLineMemHash map1fl(mAlloc, "printAllocationDiff"); + TFileAndLineMemHash map2fl(mAlloc, "printAllocationDiff"); + convert(mStoredAllocations, map1fl); + convert(mAllocations, map2fl); + printDiffs(map2fl, map1fl, printer); + } + void storeAllocations() + { + mStoredAllocations.clear(); + copyMap(mAllocations, mStoredAllocations); + } + bool hasStoredAllocations() { return mStoredAllocations.size() > 0; } + }; +} +} +#endif diff --git a/src/foundation/Utils.h b/src/foundation/Utils.h new file mode 100644 index 0000000..33c2400 --- /dev/null +++ b/src/foundation/Utils.h @@ -0,0 +1,382 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_RENDER_UTILS_H +#define QT3DS_RENDER_UTILS_H +#include "EASTL/hash_map.h" +#include "EASTL/hash_set.h" +#include "EASTL/vector.h" +#include "foundation/Qt3DSIntrinsics.h" +#include "foundation/Qt3DSDataRef.h" +#include "foundation/Qt3DSAllocator.h" +#include "foundation/Qt3DSContainers.h" +#include "foundation/Qt3DSMath.h" +#include <ctype.h> + +namespace qt3ds { +namespace foundation { + + // Defined in IStringTable.cpp + extern const char16_t *char16EmptyStr; + extern const char32_t *char32EmptyStr; + + inline const char *nonNull(const char *src) { return src == NULL ? "" : src; } + inline bool isTrivial(const char *str) { return str == NULL || *str == 0; } + inline const char16_t *nonNull(const char16_t *src) + { + return src == NULL ? char16EmptyStr : src; + } + inline bool isTrivial(const char16_t *str) { return str == NULL || *str == 0; } + inline const char32_t *nonNull(const char32_t *src) + { + return src == NULL ? char32EmptyStr : src; + } + inline bool isTrivial(const char32_t *str) { return str == NULL || *str == 0; } + + // Note this is safe to call on null strings. + template <typename TCharType> + inline QT3DSU32 StrLen(const TCharType *inType) + { + QT3DSU32 retval = 0; + while (inType && *inType) { + ++retval; + ++inType; + } + return retval; + } + + template <typename TCharType> + inline QT3DSI32 Compare(const TCharType *inLhs, const TCharType *inRhs) + { + inLhs = nonNull(inLhs); + inRhs = nonNull(inRhs); + while (*inLhs && *inRhs) { + QT3DSI32 diff = *inLhs - *inRhs; + if (diff) + return diff; + ++inLhs; + ++inRhs; + } + return *inLhs - *inRhs; + } + + template <typename TCharType> + inline QT3DSI32 CompareCaseless(const TCharType *inLhs, const TCharType *inRhs) + { + inLhs = nonNull(inLhs); + inRhs = nonNull(inRhs); + while (*inLhs && *inRhs) { + QT3DSI32 diff = tolower(*inLhs) - tolower(*inRhs); + if (diff) + return diff; + ++inLhs; + ++inRhs; + } + return *inLhs - *inRhs; + } + + template <typename TCharType> + inline QT3DSI32 CompareNChars(const TCharType *inLhs, const TCharType *inRhs, QT3DSU32 inNumChars) + { + inLhs = nonNull(inLhs); + inRhs = nonNull(inRhs); + while (*inLhs && *inRhs && inNumChars) { + QT3DSI32 diff = *inLhs - *inRhs; + if (diff) + return diff; + ++inLhs; + ++inRhs; + --inNumChars; + } + if (inNumChars) + return *inLhs - *inRhs; + return 0; + } + + template <typename TCharType> + bool AreEqual(const TCharType *inLhs, const TCharType *inRhs) + { + return Compare(inLhs, inRhs) == 0; + } + + template <typename TCharType> + bool AreEqualCaseless(const TCharType *inLhs, const TCharType *inRhs) + { + return CompareCaseless(inLhs, inRhs) == 0; + } + + inline QT3DSU32 IsBitSetInBitmap(NVConstDataRef<QT3DSU32> bitmap, QT3DSU32 entryIndex) + { + QT3DSU32 bitMapIndex = entryIndex / 32; + QT3DSU32 bitIndex = entryIndex % 32; + QT3DSU32 shouldApply = 0; + if (bitMapIndex < bitmap.mSize) + shouldApply = (1 << bitIndex) & bitmap[bitMapIndex]; + return shouldApply; + } + + inline void SetBitInBitmap(nvvector<QT3DSU32> &bitmap, QT3DSU32 entryIndex) + { + QT3DSU32 bitMapIndex = entryIndex / 32; + QT3DSU32 bitIndex = entryIndex % 32; + while (bitmap.size() <= bitMapIndex) + bitmap.push_back(0); + bitmap[bitMapIndex] |= (QT3DSU32)(1 << bitIndex); + } + inline void UnsetBitInBitmap(nvvector<QT3DSU32> &bitmap, QT3DSU32 entryIndex) + { + QT3DSU32 bitMapIndex = entryIndex / 32; + QT3DSU32 bitIndex = entryIndex % 32; + if (bitMapIndex < bitmap.size()) + bitmap[bitMapIndex] &= ~((QT3DSU32)(1 << bitIndex)); + } + +#define CHECK_CONDITION_AND_RETURN_FALSE(cond, errorType, message) \ + if (cond) { \ + m_Foundation.error(errorType, message); \ + QT3DS_ASSERT(false); \ + return false; \ + } +// Returns false if the property type doesn't match the incoming type. +#define CHECK_CONDITION_AND_RETURN(cond, errorType, message) \ + if (cond) { \ + m_Foundation.error(errorType, message); \ + QT3DS_ASSERT(false); \ + } + +#define QT3DS_FOREACH(varname, stop) for (QT3DSU32 varname = 0, end = stop; varname < end; ++varname) + + template <typename TKeyType, typename TValueType, typename THashType, typename TPredicate, + typename TBufType, typename TOperator> + QT3DSU32 GetMapKeysOp(const eastl::hash_map<TKeyType, TValueType, THashType, TPredicate, + ForwardingAllocator> &map, + TBufType *buffer, QT3DSU32 bufSize, QT3DSU32 startIdx, TOperator op) + { + QT3DSU32 numItems = static_cast<QT3DSU32>(map.size()); + if (numItems == 0 || bufSize == 0) + return 0; + + startIdx = NVMin(numItems - 1, startIdx); + QT3DSU32 retval = 0; + for (typename eastl::hash_map<TKeyType, TValueType, THashType, TPredicate, + ForwardingAllocator>::const_iterator iter = map.begin(), + end = map.end(); + iter != end && bufSize; ++iter) { + if (startIdx) + --startIdx; + else { + buffer[retval] = op(iter->first); + --bufSize; + ++retval; + } + } + return retval; + } + + struct IdOp + { + template <typename TDataType> + TDataType operator()(const TDataType &item) + { + return item; + } + }; + + template <typename TKeyType, typename TValueType, typename THashType, typename TPredicateType> + QT3DSU32 + GetMapKeys(const eastl::hash_map<TKeyType, TValueType, THashType, ForwardingAllocator> &map, + TKeyType *buffer, QT3DSU32 bufSize, QT3DSU32 startIdx) + { + return GetMapKeysOp(map, buffer, bufSize, startIdx, IdOp()); + } + + struct DerefOp + { + template <typename TDataType> + TDataType operator()(const TDataType *item) + { + return *item; + } + }; + + template <typename TKeyType, typename TValueType, typename THashType, typename TPredicateType, + typename TAllocatorType, typename TBufType, typename TOp> + QT3DSU32 GetMapValues( + const eastl::hash_map<TKeyType, TValueType, THashType, TPredicateType, TAllocatorType> &map, + TBufType *buffer, QT3DSU32 bufSize, QT3DSU32 startIdx, TOp op) + { + QT3DSU32 numItems = static_cast<QT3DSU32>(map.size()); + if (numItems == 0 || bufSize == 0) + return 0; + + startIdx = NVMin(numItems - 1, startIdx); + QT3DSU32 retval = 0; + for (typename eastl::hash_map<TKeyType, TValueType, THashType, TPredicateType, + TAllocatorType>::const_iterator iter = map.begin(), + end = map.end(); + iter != end && bufSize; ++iter) { + if (startIdx) + --startIdx; + else { + buffer[retval] = op(iter->second); + --bufSize; + ++retval; + } + } + return retval; + } + + template <typename TValueType, typename TBufType> + QT3DSU32 GetArrayEntries(const nvvector<TValueType> &data, TBufType *buffer, QT3DSU32 bufSize, + QT3DSU32 startIdx) + { + QT3DSU32 numItems = static_cast<QT3DSU32>(data.size()); + if (numItems == 0 || bufSize == 0) + return 0; + + startIdx = NVMin(numItems - 1, startIdx); + QT3DSU32 available = NVMin(numItems - startIdx, bufSize); + QT3DS_FOREACH(idx, available) + buffer[idx] = data[idx + startIdx]; + return available; + } + + template <typename TValueType, typename TBufType> + QT3DSU32 GetDataRefEntries(const NVDataRef<TValueType> &data, TBufType *buffer, QT3DSU32 bufSize, + QT3DSU32 startIdx) + { + QT3DSU32 numItems = static_cast<QT3DSU32>(data.size()); + if (numItems == 0 || bufSize == 0) + return 0; + + startIdx = NVMin(numItems - 1, startIdx); + QT3DSU32 available = NVMin(numItems - startIdx, bufSize); + QT3DS_FOREACH(idx, available) + buffer[idx] = data.mData[idx + startIdx]; + return available; + } + + template <typename TValueType, typename TBufType> + QT3DSU32 GetDataRefEntries(const NVConstDataRef<TValueType> &data, TBufType *buffer, QT3DSU32 bufSize, + QT3DSU32 startIdx) + { + QT3DSU32 numItems = static_cast<QT3DSU32>(data.size()); + if (numItems == 0 || bufSize == 0) + return 0; + + startIdx = NVMin(numItems - 1, startIdx); + QT3DSU32 available = NVMin(numItems - startIdx, bufSize); + QT3DS_FOREACH(idx, available) + buffer[idx] = data.mData[idx + startIdx]; + return available; + } + + template <typename TKeyType, typename THashType, typename TPredicate, typename TAllocator> + QT3DSU32 GetHashSetEntries(eastl::hash_set<TKeyType, THashType, TPredicate, TAllocator> &data, + TKeyType *buffer, QT3DSU32 bufSize, QT3DSU32 startIdx) + { + QT3DSU32 numItems = static_cast<QT3DSU32>(data.size()); + if (numItems == 0 || bufSize == 0) + return 0; + startIdx = NVMin(numItems - 1, startIdx); + QT3DSU32 available = NVMin(numItems - startIdx, bufSize); + QT3DSU32 idx = 0; + for (typename eastl::hash_set<TKeyType, THashType, TPredicate, TAllocator>::const_iterator + iter = data.begin(), + end = data.end(); + iter != end && idx < available; ++iter, ++idx) { + if (startIdx) { + --startIdx; + continue; + } + buffer[idx] = *iter; + } + return available; + } + + template <typename TDataType> + void memCopyT(TDataType *dest, const TDataType *src, QT3DSU32 size) + { + memCopy(dest, src, size * sizeof(TDataType)); + } + + template <typename TDataType> + NVDataRef<TDataType> PtrAtOffset(QT3DSU8 *baseData, QT3DSU32 offset, QT3DSU32 byteSize) + { + return NVDataRef<TDataType>(byteSize ? (TDataType *)(baseData + offset) : NULL, + byteSize / sizeof(TDataType)); + } + + struct char_hasher + { + size_t operator()(const char *item) const { return eastl::hash<const char8_t *>()(item); } + }; + + struct char_equal_to + { + bool operator()(const char *lhs, const char *rhs) const + { + if (lhs == NULL) + lhs = ""; + if (rhs == NULL) + rhs = ""; + return strcmp(lhs, rhs) == 0; + } + }; + + struct wide_char_hasher + { + size_t operator()(const char16_t *item) const + { + return eastl::hash<const char16_t *>()(item); + } + }; + + struct wide_char_equal_to + { + bool operator()(const char16_t *lhs, const char16_t *rhs) const + { + if (lhs == NULL) + lhs = reinterpret_cast<const char16_t *>(L""); + if (rhs == NULL) + rhs = reinterpret_cast<const char16_t *>(L""); + while (*lhs && *rhs) { + int answer = *lhs - *rhs; + if (answer) + return false; + ++lhs; + ++rhs; + } + return *lhs == *rhs; + } + }; +} +} +#endif diff --git a/src/foundation/XML.cpp b/src/foundation/XML.cpp new file mode 100644 index 0000000..bd12dfa --- /dev/null +++ b/src/foundation/XML.cpp @@ -0,0 +1,948 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "foundation/XML.h" +#include "foundation/Qt3DSPool.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/StringTable.h" +#include "foundation/IOStreams.h" +#include "foundation/StringConversionImpl.h" +#include "foundation/StrConvertUTF.h" +#include "foundation/StringTable.h" +#include "foundation/Utils.h" +#include "foundation/Qt3DSAtomic.h" +#include "EASTL/hash_map.h" + +#ifdef QT3DS_VC +#include <windows.h> //output debug string +#endif + +#include <QtCore/qxmlstream.h> + +using namespace qt3ds; +using namespace qt3ds::foundation; +using namespace qt3ds::intrinsics; + +typedef char XML_Char; + +namespace { + +const QT3DSU16 g_BOMMarker = (QT3DSU16)0xFEFF; + +// Some DOM parsing operations are destructive. If you need +// them to not be destructive, then we need to modify +// the reader. Specifically parsing lists of floats, due +// to a bug in strtod, is destructive. +struct SDOMReader : public IDOMReader +{ + SReaderScope m_TopElement; + eastl::vector<SReaderScope> m_ScopeStack; + NVScopedRefCounted<IDOMFactory> m_Factory; + volatile QT3DSI32 mRefCount; + + SDOMReader(NVAllocatorCallback &inAlloc, SDOMElement &te, NVScopedRefCounted<IStringTable> s, + NVScopedRefCounted<IDOMFactory> inFactory) + : IDOMReader(inAlloc, s) + , m_TopElement(&te) + , m_Factory(inFactory) + , mRefCount(0) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Factory->GetAllocator()) + + SDOMElement *Current() const { return m_TopElement.m_Element; } + + void PushScope() override { m_ScopeStack.push_back(SReaderScope(m_TopElement)); } + + void PopScope() override + { + if (m_ScopeStack.size()) { + m_TopElement = m_ScopeStack.back(); + m_ScopeStack.pop_back(); + } else + m_TopElement = SReaderScope(); + } + + SReaderScope GetScope() override { return m_TopElement; } + + void SetScope(SReaderScope inScope) override { m_TopElement = inScope; } + + SDOMElement *GetElement() const override { return m_TopElement.m_Element; } + + Option<CRegisteredString> RegisterNS(TXMLCharPtr ns) + { + if (ns) { + return m_StringTable->RegisterStr(ns); + } + return Empty(); + } + + bool UnregisteredAtt(TXMLCharPtr name, TXMLCharPtr &outValue, TXMLCharPtr ns = NULL) override + { + outValue = ""; + SDOMElement *current(Current()); + if (current) { + return current->AttValue(m_StringTable->RegisterStr(name), RegisterNS(ns), outValue); + } else { + QT3DS_ASSERT(false); + } + return false; + } + + bool Att(TXMLCharPtr name, CRegisteredString &outValue, TXMLCharPtr ns = NULL) override + { + const char8_t *unregValue = NULL; + if (UnregisteredAtt(name, unregValue, ns)) { + outValue = m_StringTable->RegisterStr(unregValue); + return true; + } + return false; + } + + QT3DSU32 CountChildren(TXMLCharPtr childName = NULL, TXMLCharPtr ns = NULL) override + { + SDOMElement *elem = Current(); + if (elem == NULL) { + QT3DS_ASSERT(false); + return 0; + } + return elem->GetNumChildren(m_StringTable->RegisterStr(childName), RegisterNS(ns)); + } + + SDOMAttribute *CurrentAtt() + { + if (m_TopElement.m_Attribute) + return m_TopElement.m_Attribute; + return NULL; + } + + SDOMAttribute *GetFirstAttribute() override + { + if (m_TopElement.m_Element) + m_TopElement.m_Attribute = m_TopElement.m_Element->m_Attributes.m_Head; + return m_TopElement.m_Attribute; + } + + SDOMAttribute *GetNextAttribute() override + { + if (m_TopElement.m_Attribute) + m_TopElement.m_Attribute = m_TopElement.m_Attribute->m_NextAttribute; + return CurrentAtt(); + } + + bool MoveToFirstChild(TXMLCharPtr childName = NULL, TXMLCharPtr ns = NULL) override + { + SDOMElement *elem = Current(); + if (elem == NULL) { + QT3DS_ASSERT(false); + return false; + } + SDOMElement *child = elem->FindChild(m_StringTable->RegisterStr(childName), RegisterNS(ns)); + if (child != NULL) { + m_TopElement = child; + return true; + } + return false; + } + + bool MoveToNextSibling(TXMLCharPtr childName = NULL, TXMLCharPtr ns = NULL) override + { + SDOMElement *elem = Current(); + if (elem == NULL) { + QT3DS_ASSERT(false); + return false; + } + SDOMElement *nextSibling = + elem->FindSibling(m_StringTable->RegisterStr(childName), RegisterNS(ns)); + if (nextSibling) { + m_TopElement = nextSibling; + return true; + } + return false; + } + // Leave element means go to its parent. + void Leave() override + { + QT3DS_ASSERT(m_TopElement.m_Element); + if (m_TopElement.m_Element) + m_TopElement = m_TopElement.m_Element->m_Parent; + } + + bool Value(TXMLCharPtr &outValue) override + { + SDOMElement *current(Current()); + if (!current) { + QT3DS_ASSERT(false); + return false; + } + outValue = current->m_Value; + return true; + } + + SDOMElement *GetTopElement() override + { + SDOMElement *current(Current()); + while (current && current->m_Parent) + current = current->m_Parent; + return current; + } + + virtual NVScopedRefCounted<IDOMFactory> GetFactory() { return m_Factory; } +}; + +struct SDOMWriter : public IDOMWriter, public SDOMReader +{ + NVScopedRefCounted<IDOMFactory> m_FactoryPtr; + IDOMFactory &m_Factory; + + SDOMWriter(NVScopedRefCounted<IDOMFactory> inDOMFactory, + NVScopedRefCounted<IStringTable> inStringTable, SDOMElement &inTopElem) + : SDOMReader(inDOMFactory->GetAllocator(), inTopElem, inStringTable, *inDOMFactory) + , m_FactoryPtr(inDOMFactory) + , m_Factory(*inDOMFactory) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Factory.GetAllocator()) + + void Begin(TXMLCharPtr inElemName, TXMLCharPtr inNamespace = NULL, int inLine = 0) override + { + if (!m_TopElement.m_Element) { + QT3DS_ASSERT(false); + return; + } + SDOMElement *current(Current()); + SDOMElement *newElement(m_Factory.NextElement(m_StringTable->RegisterStr(inElemName), + m_StringTable->RegisterStr(inNamespace), + inLine)); + current->AppendChild(*newElement); + m_TopElement = newElement; + } + + void Att(TXMLCharPtr name, TXMLCharPtr value, TXMLCharPtr ns, int inLine) override + { + if (!m_TopElement.m_Element) { + QT3DS_ASSERT(false); + return; + } + m_TopElement.m_Element->SetAttributeValue(m_StringTable->RegisterStr(name), RegisterNS(ns), + value, m_Factory, inLine); + } + + void Value(TXMLCharPtr value) override + { + if (!m_TopElement.m_Element) { + QT3DS_ASSERT(false); + return; + } + if (value == NULL) + value = ""; + size_t len = strlen(value); + m_Factory.AppendStrBuf(value, (QT3DSU32)len); + m_TopElement.m_Element->m_Value = m_Factory.FinalizeStrBuf(); + } + + void End() override + { + if (!m_TopElement.m_Element) { + QT3DS_ASSERT(false); + return; + } + Leave(); + } + void RemoveCurrent() override + { + SDOMElement *current(Current()); + if (!current) { + QT3DS_ASSERT(false); + return; + } + if (current->m_Parent) { + m_TopElement = current->m_Parent; + m_TopElement.m_Element->RemoveChild(*current); + } + } + void RemoveAttribute(TXMLCharPtr inItem, TXMLCharPtr inNamespace) override + { + SDOMElement *current(Current()); + if (!current) { + QT3DS_ASSERT(false); + return; + } + current->RemoveAttribute(m_StringTable->RegisterStr(inItem), RegisterNS(inNamespace)); + } + + void MoveBefore(TXMLCharPtr inItem, TXMLCharPtr inSibling, TXMLCharPtr inNs = NULL) override + { + SDOMElement *current(Current()); + if (!current) { + QT3DS_ASSERT(false); + return; + } + + SDOMElement *theItem = + current->FindChild(m_StringTable->RegisterStr(inItem), RegisterNS(inNs)); + SDOMElement *theSibling = + current->FindChild(m_StringTable->RegisterStr(inSibling), RegisterNS(inNs)); + QT3DS_ASSERT(theItem && theSibling); + if (theItem && theSibling) + current->PrependChild(*theItem, theSibling); + } + + // If current has no parent, then we are at the top + // of the tree and we should return 0. Or if there is no + // current. + // If there is one parent, we should return 1. + QT3DSU32 GetTabs() override + { + QT3DSU32 retval = 0; + SDOMElement *current(Current()); + do { + if (current) + current = current->m_Parent; + if (current) + ++retval; + } while (current); + return retval; + } + + SDOMElement *GetTopElement() override { return SDOMReader::GetTopElement(); } + + NVScopedRefCounted<IDOMFactory> GetFactory() override { return m_FactoryPtr; } + + NVAllocatorCallback &GetAllocator() override { return m_Factory.GetAllocator(); } +}; + +struct SimpleXmlWriter +{ + + struct SNameNS + { + TXMLCharPtr name; + TXMLCharPtr ns; + SNameNS(TXMLCharPtr _name = NULL, TXMLCharPtr _ns = NULL) + : name(_name) + , ns(_ns) + { + } + }; + IOutStream &m_Stream; + eastl::vector<eastl::pair<SNameNS, bool>> m_OpenElements; + bool m_ElementOpen; + char8_t m_PrintBuf[256]; + QT3DSU32 m_Tabs; + eastl::basic_string<char8_t, ForwardingAllocator> m_Buffer; + + SimpleXmlWriter(NVAllocatorCallback &inAlloc, IOutStream &stream, QT3DSU32 inTabs = 0) + : m_Stream(stream) + , m_ElementOpen(false) + , m_Tabs(inTabs) + , m_Buffer(ForwardingAllocator(inAlloc, "SimpleXMLWriter::m_Buffer")) + { + } + + void Write(const SNameNS &data) + { + if (data.ns && *data.ns) { + Write(data.ns); + Write(':'); + } + Write(data.name); + } + + void Write(const char8_t *data, QT3DSU32 inLen = 0) + { + if (!isTrivial(data)) { + if (inLen == 0) + inLen = StrLen(data); + m_Stream.Write(data, inLen); + } + } + void BeginWrite() { m_Buffer.clear(); } + void WriteTemp(char8_t data) { m_Buffer.append(1, data); } + void WriteTemp(const char8_t *data) { m_Buffer.append(data); } + void EndWrite() { Write(reinterpret_cast<const char8_t *>(m_Buffer.c_str()), m_Buffer.size()); } + void Write(char8_t data) { m_Stream.Write(data); } + void Tabs() + { + QT3DS_FOREACH(idx, (m_OpenElements.size() + m_Tabs)) + Write('\t'); + } + void Close(bool newline) + { + if (m_ElementOpen) { + Write(">"); + if (newline) + Write('\n'); + } + m_ElementOpen = false; + } + void Begin(TXMLCharPtr name, TXMLCharPtr ns) + { + Close(true); + Tabs(); + Write('<'); + SNameNS theName(name, ns); + Write(theName); + m_OpenElements.push_back(eastl::pair<SNameNS, bool>(theName, false)); + m_ElementOpen = true; + } + TXMLCharPtr ToStr(char8_t val) + { + m_PrintBuf[0] = val; + m_PrintBuf[1] = 0; + return m_PrintBuf; + } + template <typename TDataType> + TXMLCharPtr ToStr(TDataType val) + { + StringConversion<TDataType>().ToStr(val, NVDataRef<char8_t>(m_PrintBuf, 256)); + return m_PrintBuf; + } + void Att(TXMLCharPtr name, TXMLCharPtr ns, TXMLCharPtr value) + { + QT3DS_ASSERT(m_ElementOpen); + Write(' '); + Write(SNameNS(name, ns)); + + Write("=\""); + + QString str = QString::fromUtf8(nonNull(value)).toHtmlEscaped(); + Write(str.toUtf8().constData()); + Write("\""); + } + template <typename TData> + void Att(TXMLCharPtr name, TXMLCharPtr ns, TData value) + { + Att(name, ns, ToStr(value)); + } + + void Value(TXMLCharPtr value) + { + if (!isTrivial(value)) { + Close(false); + + QString str = QString::fromUtf8(nonNull(value)).toHtmlEscaped(); + Write(str.toUtf8().constData()); + m_OpenElements.back().second = true; + } + } + void ChildValue(TXMLCharPtr name, TXMLCharPtr ns, TXMLCharPtr value) + { + Begin(name, ns); + Value(value); + End(); + } + void End(bool newlineAfterClose = true) + { + QT3DS_ASSERT(m_OpenElements.size()); + eastl::pair<SNameNS, bool> topElem = m_OpenElements.back(); + m_OpenElements.pop_back(); + if (m_ElementOpen) + Write("/>"); + else { + if (topElem.second == false) + Tabs(); + Write("</"); + Write(topElem.first); + Write(">"); + } + m_ElementOpen = false; + if (newlineAfterClose == true) + Write('\n'); + } +}; + +struct DOMParser +{ + IDOMFactory &m_Factory; + SDOMElement *m_TopElement; + SDOMElement *m_FirstElement; + char8_t m_nsSeparator; + typedef eastl::basic_string<char8_t, ForwardingAllocator> TStrType; + TStrType m_NSBuffer; + TStrType m_NameBuffer; + CRegisteredString m_RegName; + CRegisteredString m_RegNS; + SNamespacePairNode *m_FirstPair; + SNamespacePairNode *m_LastPair; + QXmlStreamReader *m_XMLParser; + + DOMParser(IDOMFactory &factory, QXmlStreamReader &parser, char8_t nsSeparator) + : m_Factory(factory) + , m_FirstElement(NULL) + , m_nsSeparator(nsSeparator) + , m_NSBuffer(ForwardingAllocator(factory.GetAllocator(), "DOMParser::m_NSBuffer")) + , m_NameBuffer(ForwardingAllocator(factory.GetAllocator(), "DOMParser::m_NameBuffer")) + , m_FirstPair(NULL) + , m_LastPair(NULL) + , m_XMLParser(&parser) + { + } + + void ParseName(const XML_Char *name) + { + m_NSBuffer.assign(name); + eastl::string::size_type pos = m_NSBuffer.find(m_nsSeparator); + if (pos != TStrType::npos) { + m_NameBuffer.assign(m_NSBuffer.c_str() + pos + 1); + m_NSBuffer.resize(pos); + } else { + m_NameBuffer = m_NSBuffer; + m_NSBuffer.clear(); + // Find the namespace with no abbreviation. + for (SNamespacePairNode *theNode = m_FirstPair; theNode; + theNode = theNode->m_NextNode) { + if (theNode->m_Abbreviation.IsValid() == false) + m_NSBuffer.assign(theNode->m_Namespace.c_str()); + } + } + m_RegName = m_Factory.GetStringTable()->RegisterStr(m_NameBuffer.c_str()); + m_RegNS = m_Factory.GetStringTable()->RegisterStr(m_NSBuffer.c_str()); + } + + struct SimpleXmlErrorHandler : public CXmlErrorHandler + { + void OnXmlError(TXMLCharPtr errorName, int line, int column) override + { + char8_t buffer[1024]; + _snprintf(buffer, 1024, "%s(%d): Xml Error %s on line %d, column %d", __FILE__, + __LINE__, errorName, line, column); +#ifdef QT3DS_VC + OutputDebugStringA(buffer); +#endif + printf("%s\n", buffer); + } + }; + + template <QT3DSU32 THeaderLen> + struct SHeaderInStream : public IInStream + { + QT3DSU8 m_Header[THeaderLen]; + IInStream &m_InStream; + QT3DSU32 m_BytesRead; + + SHeaderInStream(IInStream &inStream) + : m_InStream(inStream) + , m_BytesRead(0) + { + } + bool readHeader() + { + QT3DSU32 amountRead = m_InStream.Read(NVDataRef<QT3DSU8>(m_Header, THeaderLen)); + return amountRead == THeaderLen; + } + QT3DSU32 Read(NVDataRef<QT3DSU8> data) override + { + if (data.size() == 0) + return 0; + QT3DSU8 *writePtr(data.begin()); + QT3DSU32 amountToRead(data.size()); + QT3DSU32 amountRead = 0; + if (m_BytesRead < THeaderLen) { + QT3DSU32 headerLeft = NVMin(THeaderLen - m_BytesRead, amountToRead); + memCopy(writePtr, m_Header + m_BytesRead, headerLeft); + writePtr += headerLeft; + amountToRead -= headerLeft; + amountRead += headerLeft; + } + if (amountToRead) + amountRead += m_InStream.Read(NVDataRef<QT3DSU8>(writePtr, amountToRead)); + m_BytesRead += amountRead; + return amountRead; + } + }; + + static eastl::pair<SNamespacePairNode *, SDOMElement *> + ParseXMLFile(IDOMFactory &factory, IInStream &inStream, CXmlErrorHandler *handler = NULL) + { + SimpleXmlErrorHandler simpleHandler; + if (handler == NULL) + handler = &simpleHandler; + + // look for BOM (Byte-Order-Marker) in the file + // if found, pass in UTF-16 as the format. else + // pass in UTF-8 as the format. + SHeaderInStream<sizeof(g_BOMMarker)> theInStream(inStream); + if (theInStream.readHeader() == false) { + QT3DS_ASSERT(false); + return NULL; + } + QT3DSU16 theHeaderData; + memCopy(&theHeaderData, theInStream.m_Header, sizeof(g_BOMMarker)); + + char8_t nsSeparator = '$'; + + if (theHeaderData == g_BOMMarker) + qFatal("UTF-16 files not supported"); + + QXmlStreamReader sreader; + DOMParser domParser(factory, sreader, nsSeparator); + QT3DSU8 dataBuf[2048]; + QT3DSU32 amountRead = 0; + do { + amountRead = theInStream.Read(toDataRef(dataBuf, 2048)); + if (amountRead) { + QByteArray tmp = QByteArray::fromRawData((char*)dataBuf,amountRead); + sreader.addData(tmp); + } + } while (amountRead > 0); + while (!sreader.atEnd()) { + QXmlStreamReader::TokenType token = sreader.readNext(); + if (token == QXmlStreamReader::StartElement) { + domParser.m_Factory.IgnoreStrBuf(); + domParser.ParseName((TXMLCharPtr)sreader.name().toUtf8().data()); + int line = sreader.lineNumber(); + SDOMElement *newElem = + domParser.m_Factory.NextElement(domParser.m_RegName, domParser.m_RegNS, line); + if (domParser.m_FirstElement == NULL) { + domParser.m_FirstElement = newElem; + domParser.m_TopElement = newElem; + } else { + domParser.m_TopElement->AppendChild(*newElem); + domParser.m_TopElement = newElem; + } + const QXmlStreamAttributes& attributes = sreader.attributes(); + for (auto attrib : attributes) { + domParser.ParseName((TXMLCharPtr)attrib.name().toUtf8().data()); + SDOMAttribute *att = + domParser.m_Factory.NextAttribute(domParser.m_RegName, + (TXMLCharPtr)attrib.value() + .toUtf8().data(), + domParser.m_RegNS, line); + newElem->m_Attributes.push_back(*att); + } + } else if (token == QXmlStreamReader::Characters) { + QByteArray text = sreader.text().toUtf8(); + domParser.m_Factory.AppendStrBuf(text.data(), text.length()); + } else if (token == QXmlStreamReader::EndElement) { + domParser.m_TopElement->m_Value = domParser.m_Factory.FinalizeStrBuf(); + domParser.m_TopElement = domParser.m_TopElement->m_Parent; + } + + if (sreader.hasError()) { + TXMLCharPtr error = (TXMLCharPtr)sreader.errorString().toUtf8().data(); + handler->OnXmlError(error, sreader.lineNumber(), sreader.columnNumber()); + return nullptr; + } + + } + return eastl::make_pair(domParser.m_FirstPair, domParser.m_FirstElement); + } +}; + +class SimpleDomFactory : public IDOMFactory +{ + typedef eastl::basic_string<char8_t, ForwardingAllocator> TNarrowStr; + NVAllocatorCallback &m_Allocator; + Pool<SDOMElement> m_ElementPool; + Pool<SDOMAttribute> m_AttributePool; + Pool<SNamespacePairNode> m_NamespaceNodePool; + eastl::vector<char8_t *> m_BigStrings; + NVScopedRefCounted<IStringTable> m_StringTable; + TNarrowStr m_StringBuilder; + volatile QT3DSI32 mRefCount; + +public: + SimpleDomFactory(NVAllocatorCallback &alloc, NVScopedRefCounted<IStringTable> strt) + : m_Allocator(alloc) + , m_StringTable(strt) + , m_StringBuilder(ForwardingAllocator(alloc, "SimpleDomFactory::m_StringBuilder")) + , mRefCount(0) + { + } + + ~SimpleDomFactory() + { + QT3DS_FOREACH(idx, m_BigStrings.size()) + m_Allocator.deallocate(m_BigStrings[idx]); + m_BigStrings.clear(); + } + + NVAllocatorCallback &GetAllocator() override { return m_Allocator; } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Allocator) + + TXMLCharPtr RegisterStr(TXMLCharPtr str) + { + if (str == NULL || *str == 0) + return ""; + return m_StringTable->RegisterStr(str); + } + + virtual void Release() { delete this; } + void AppendStrBuf(TXMLCharPtr str, QT3DSU32 len) override + { + if (len && *str) { + m_StringBuilder.append(str, str + len); + } + } + + // Null terminate what is there and return the buffer. + // This pointer needs to be persistent. + TXMLCharPtr FinalizeStrBuf() override + { + if (m_StringBuilder.size() == 0) + return ""; + QT3DSU32 len = m_StringBuilder.size() + 1; + QT3DSU32 numBytes = len * sizeof(char8_t); + char8_t *newMem = + (char8_t *)m_Allocator.allocate(numBytes, "DOMFactory::ValueStr", __FILE__, __LINE__); + memCopy(newMem, m_StringBuilder.c_str(), numBytes); + m_BigStrings.push_back(newMem); + m_StringBuilder.clear(); + return newMem; + } + void IgnoreStrBuf() override { m_StringBuilder.clear(); } + + SDOMAttribute *NextAttribute(CRegisteredString name, TXMLCharPtr val, + CRegisteredString ns, int inLine) override + { + TXMLCharPtr v(RegisterValue(val)); + SDOMAttribute *att = (SDOMAttribute *)m_AttributePool.allocate(__FILE__, __LINE__); + new (att) SDOMAttribute(name, ns, v, inLine); + return att; + } + + SDOMElement *NextElement(CRegisteredString name, CRegisteredString ns, int inLine) override + { + IgnoreStrBuf(); + SDOMElement *elem = (SDOMElement *)(m_ElementPool.allocate(__FILE__, __LINE__)); + new (elem) SDOMElement(name, ns, inLine); + return elem; + } + + SNamespacePairNode *NextNSPairNode(CRegisteredString ns, CRegisteredString abbr) override + { + return m_NamespaceNodePool.construct(SNamespacePair(ns, abbr), __FILE__, __LINE__); + } + + NVScopedRefCounted<IStringTable> GetStringTable() override { return m_StringTable; } +}; +} + +NVScopedRefCounted<IDOMReader> +IDOMReader::CreateDOMReader(NVAllocatorCallback &inAlloc, SDOMElement &inRootElement, + NVScopedRefCounted<IStringTable> inStringTable, + NVScopedRefCounted<IDOMFactory> inFactory) +{ + return QT3DS_NEW(inAlloc, SDOMReader)(inAlloc, inRootElement, inStringTable, inFactory); +} + +eastl::pair<NVScopedRefCounted<IDOMWriter>, NVScopedRefCounted<IDOMReader>> +IDOMWriter::CreateDOMWriter(NVScopedRefCounted<IDOMFactory> inFactory, SDOMElement &inRootElement, + NVScopedRefCounted<IStringTable> inStringTable) +{ + NVScopedRefCounted<SDOMWriter> writer( + QT3DS_NEW(inFactory->GetAllocator(), SDOMWriter)(inFactory, inStringTable, inRootElement)); + return eastl::make_pair(writer.mPtr, writer.mPtr); +} + +TXMLCharPtr IDOMFactory::RegisterValue(TXMLCharPtr inValue) +{ + if (isTrivial(inValue)) + return ""; + IgnoreStrBuf(); + AppendStrBuf(inValue, (QT3DSU32)strlen(inValue)); + return FinalizeStrBuf(); +} + +NVScopedRefCounted<IDOMFactory> +IDOMFactory::CreateDOMFactory(NVAllocatorCallback &inAlloc, + NVScopedRefCounted<IStringTable> inStrTable) +{ + return QT3DS_NEW(inAlloc, SimpleDomFactory)(inAlloc, inStrTable); +} + +void CDOMSerializer::WriteXMLHeader(IOutStream &inStream) +{ + const char8_t *header = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"; + QT3DSU32 len = (QT3DSU32)strlen(header); + inStream.Write(header, len); +} + +// Lexigraphically sort the attributes. +struct SAttributeComparator +{ + bool operator()(const SDOMAttribute *lhs, const SDOMAttribute *rhs) + { + int nsCmp = strcmp(lhs->m_Namespace, rhs->m_Namespace); + if (nsCmp) + return nsCmp < 0; + + return strcmp(lhs->m_Name, rhs->m_Name) < 0; + } +}; + +typedef eastl::vector<SDOMAttribute *, ForwardingAllocator> TAttVector; +typedef eastl::hash_map<CRegisteredString, CRegisteredString, eastl::hash<CRegisteredString>, + eastl::equal_to<CRegisteredString>, ForwardingAllocator> + TNSMap; + +CRegisteredString GetOrCreateNamespaceAbbr(NVAllocatorCallback &inAlloc, CRegisteredString theNs, + TNSMap &inNamespace, TNSMap &inReverseNamespaces, + IStringTable &inStrTable, bool &outCreated) +{ + CRegisteredString abbr; + if (theNs.IsValid()) { + TNSMap::iterator nsIter = inNamespace.find(theNs); + if (nsIter != inNamespace.end()) { + abbr = nsIter->second; + outCreated = false; + } else { + eastl::basic_string<char8_t, ForwardingAllocator> theStr( + ForwardingAllocator(inAlloc, "nsBuilderString")); + theStr.assign("a"); + eastl::basic_string<char8_t, ForwardingAllocator> theNsStr( + ForwardingAllocator(inAlloc, "nsBuilderString")); + CRegisteredString theNS = inStrTable.RegisterStr(theStr.c_str()); + while (inReverseNamespaces.find(theNS) != inReverseNamespaces.end()) { + for (int idx = 0; + idx < 26 && inReverseNamespaces.find(theNS) == inReverseNamespaces.end(); + ++idx) { + theNsStr = theStr; + theNsStr.append(1, (char8_t)('a' + idx)); + theNS = inStrTable.RegisterStr(theNsStr.c_str()); + } + theStr.append(1, 'a'); + } + inNamespace.insert(eastl::make_pair(theNs, theNS)); + abbr = theNS; + outCreated = true; + } + } + return abbr; +} + +// Write an element with attributes sorted by name so a file diff is effective. +void WriteElement(NVAllocatorCallback &inAlloc, SDOMElement &inElement, SimpleXmlWriter &inWriter, + TAttVector &inAttSorter, TNSMap &inNamespace, TNSMap &inReverseNamespaces, + IStringTable &inStrTable, bool inTrimEmptyElems, bool inWriteXMLNS) +{ + inAttSorter.clear(); + + // We decided that we don't want attribute sorting; the code that adds attributes needs + // to be consistent. + // This element doesn't add anything to the system in this case. + if (inTrimEmptyElems && inElement.m_Attributes.empty() && inElement.m_Children.empty() + && isTrivial(inElement.m_Value)) + return; + + for (TAttributeList::iterator iter = inElement.m_Attributes.begin(), + end = inElement.m_Attributes.end(); + iter != end; ++iter) + inAttSorter.push_back(&(*iter)); + + // Find the namespace for the element + bool createdAbbr = false; + CRegisteredString abbr = GetOrCreateNamespaceAbbr(inAlloc, inElement.m_Namespace, inNamespace, + inReverseNamespaces, inStrTable, createdAbbr); + + inWriter.Begin(inElement.m_Name, abbr); + eastl::basic_string<char8_t, ForwardingAllocator> theStr( + ForwardingAllocator(inAlloc, "nsBuilderString")); + if (inWriteXMLNS) { + for (TNSMap::iterator iter = inNamespace.begin(), end = inNamespace.end(); iter != end; + ++iter) { + theStr.assign("xmlns"); + if (iter->second.IsValid()) { + theStr.append(1, ':'); + theStr.append(iter->second.c_str()); + } + inWriter.Att(theStr.c_str(), NULL, iter->first.c_str()); + } + } else if (createdAbbr) { + theStr.assign("xmlns:"); + theStr.append(abbr.c_str()); + inWriter.Att(theStr.c_str(), NULL, inElement.m_Namespace.c_str()); + } + + const char8_t *theLastAttName = 0; + const char8_t *theLastAttNamespace = 0; + for (QT3DSU32 idx = 0, end = inAttSorter.size(); idx < end; ++idx) { + SDOMAttribute *theAtt(inAttSorter[idx]); + if (theAtt->m_Name != theLastAttName || theAtt->m_Namespace != theLastAttNamespace) { + abbr = GetOrCreateNamespaceAbbr(inAlloc, theAtt->m_Namespace, inNamespace, + inReverseNamespaces, inStrTable, createdAbbr); + if (createdAbbr) { + theStr.assign("xmlns:"); + theStr.append(abbr.c_str()); + inWriter.Att(theStr.c_str(), NULL, inElement.m_Namespace.c_str()); + } + + inWriter.Att(theAtt->m_Name, abbr, theAtt->m_Value); + } else { + QT3DS_ASSERT(false); + } + theLastAttName = theAtt->m_Name; + theLastAttNamespace = theAtt->m_Namespace; + } + // Elements can either have children or values but not both at this point. + if (inElement.m_Children.empty() == false) { + for (SDOMElement::TElementChildList::iterator iter = inElement.m_Children.begin(), + end = inElement.m_Children.end(); + iter != end; ++iter) + WriteElement(inAlloc, *iter, inWriter, inAttSorter, inNamespace, inReverseNamespaces, + inStrTable, inTrimEmptyElems, false); + + inWriter.End(); + } else { + if (!isTrivial(inElement.m_Value)) + inWriter.Value(inElement.m_Value); + + inWriter.End(); + } +} + +void CDOMSerializer::Write(NVAllocatorCallback &inAlloc, SDOMElement &inElement, + IOutStream &inStream, IStringTable &inStrTable, + NVConstDataRef<SNamespacePair> inNamespaces, bool inTrimEmptyElements, + QT3DSU32 inTabs, bool inWriteNamespaces) +{ + TAttVector theAttSorter(ForwardingAllocator(inAlloc, "CDOMSerializer::AttributeList")); + TNSMap theNamespaces(ForwardingAllocator(inAlloc, "CDOMSerializer::NamespaceMap")); + TNSMap theReverseNamespaces(ForwardingAllocator(inAlloc, "CDOMSerializer::NamespaceMap")); + for (QT3DSU32 idx = 0, end = inNamespaces.size(); idx < end; ++idx) { + theNamespaces.insert( + eastl::make_pair(inNamespaces[idx].m_Namespace, inNamespaces[idx].m_Abbreviation)); + theReverseNamespaces.insert( + eastl::make_pair(inNamespaces[idx].m_Abbreviation, inNamespaces[idx].m_Namespace)); + } + SimpleXmlWriter writer(inAlloc, inStream, inTabs); + WriteElement(inAlloc, inElement, writer, theAttSorter, theNamespaces, theReverseNamespaces, + inStrTable, inTrimEmptyElements, inWriteNamespaces); +} + +eastl::pair<SNamespacePairNode *, SDOMElement *> +CDOMSerializer::Read(IDOMFactory &inFactory, IInStream &inStream, CXmlErrorHandler *inErrorHandler) +{ + return DOMParser::ParseXMLFile(inFactory, inStream, inErrorHandler); +} diff --git a/src/foundation/XML.h b/src/foundation/XML.h new file mode 100644 index 0000000..360bfdd --- /dev/null +++ b/src/foundation/XML.h @@ -0,0 +1,680 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once +#ifndef QT3DS_FOUNDATION_XML_H +#define QT3DS_FOUNDATION_XML_H + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSMemoryBuffer.h" +#include "foundation/StringConversion.h" //Conversion between string and various datatypes. +#include "foundation/Qt3DSFlags.h" +#include "EASTL/algorithm.h" +#include "foundation/IOStreams.h" +#include "EASTL/string.h" +#include "foundation/Qt3DSAllocator.h" +#include "foundation/Qt3DSRefCounted.h" +#include "foundation/StringTable.h" +#include "foundation/Qt3DSInvasiveLinkedList.h" + +namespace qt3ds { +namespace foundation { + + class IStringTable; + + class IDOMFactory; + + class IInStream; + class IOutStream; + struct SDOMAttribute; + struct SDOMElement; + struct SNamespacePairNode; + + typedef const char8_t *TXMLCharPtr; + + typedef eastl::basic_string<char8_t, ForwardingAllocator> TXMLStr; + + // When the factor is destroyed, all elements and attributes are destroyed. + class IDOMFactory : public NVRefCounted + { + protected: + virtual ~IDOMFactory() {} + public: + // Str does not need to be null terminated. + virtual void AppendStrBuf(TXMLCharPtr str, QT3DSU32 len) = 0; + // Null terminate what is there and return the buffer. + // This pointer needs to be persistent. + virtual TXMLCharPtr FinalizeStrBuf() = 0; + virtual void IgnoreStrBuf() = 0; + + virtual SDOMAttribute *NextAttribute(CRegisteredString name, TXMLCharPtr val, + CRegisteredString ns = CRegisteredString(), + int inLine = 0) = 0; + virtual SDOMElement *NextElement(CRegisteredString name, + CRegisteredString ns = CRegisteredString(), + int inLine = 0) = 0; + virtual SNamespacePairNode *NextNSPairNode(CRegisteredString ns, + CRegisteredString abbr) = 0; + + TXMLCharPtr RegisterValue(TXMLCharPtr inValue); + + virtual NVScopedRefCounted<IStringTable> GetStringTable() = 0; + virtual NVAllocatorCallback &GetAllocator() = 0; + + static NVScopedRefCounted<IDOMFactory> + CreateDOMFactory(NVAllocatorCallback &inAllocator, + NVScopedRefCounted<IStringTable> inStrTable); + }; + + struct SDOMAttribute + { + CRegisteredString m_Namespace; + CRegisteredString m_Name; + const char8_t *m_Value; + SDOMAttribute *m_NextAttribute; + SDOMAttribute *m_PreviousAttribute; + int m_Line; + SDOMAttribute() + : m_Value(NULL) + , m_NextAttribute(NULL) + , m_PreviousAttribute(NULL) + , m_Line(0) + { + } + + SDOMAttribute(CRegisteredString inName, CRegisteredString inNs, const char8_t *inValue, + int line) + : m_Namespace(inNs) + , m_Name(inName) + , m_Value(inValue) + , m_NextAttribute(NULL) + , m_PreviousAttribute(NULL) + , m_Line(line) + { + } + + bool Value(const char8_t *&outValue) + { + outValue = m_Value; + return true; + } + + template <typename TDataType> + bool Value(TDataType &outValue) + { + StringConversion<TDataType>().StrTo(m_Value, outValue); + return true; + } + }; + + struct SNextAttOp + { + SDOMAttribute *get(SDOMAttribute &inAtt) { return inAtt.m_NextAttribute; } + const SDOMAttribute *get(const SDOMAttribute &inAtt) { return inAtt.m_NextAttribute; } + void set(SDOMAttribute &inAtt, SDOMAttribute *next) { inAtt.m_NextAttribute = next; } + }; + struct SPrevAttOp + { + SDOMAttribute *get(SDOMAttribute &inAtt) { return inAtt.m_PreviousAttribute; } + const SDOMAttribute *get(const SDOMAttribute &inAtt) { return inAtt.m_PreviousAttribute; } + void set(SDOMAttribute &inAtt, SDOMAttribute *prev) { inAtt.m_PreviousAttribute = prev; } + }; + + typedef InvasiveLinkedList<SDOMAttribute, SPrevAttOp, SNextAttOp> TAttributeList; + + struct SNamespacePair + { + CRegisteredString m_Namespace; + CRegisteredString m_Abbreviation; + SNamespacePair(CRegisteredString ns = CRegisteredString(), + CRegisteredString abbr = CRegisteredString()) + : m_Namespace(ns) + , m_Abbreviation(abbr) + { + } + }; + + struct SNamespacePairNode : public SNamespacePair + { + SNamespacePairNode *m_NextNode; + SNamespacePairNode(const SNamespacePair &inSrc = SNamespacePair()) + : SNamespacePair(inSrc) + , m_NextNode(NULL) + { + } + }; + + // Simplified dom element. All it has is one character value and list of attributes + // and children. So you can't mix elements and character data like you can in + // full xml, but this simplifies parsing. + struct SDOMElement + { + struct SNextElemOp + { + SDOMElement *get(SDOMElement &elem) { return elem.m_NextSibling; } + const SDOMElement *get(const SDOMElement &elem) { return elem.m_NextSibling; } + void set(SDOMElement &elem, SDOMElement *next) { elem.m_NextSibling = next; } + }; + struct SPrevElemOp + { + SDOMElement *get(SDOMElement &elem) { return elem.m_PreviousSibling; } + const SDOMElement *get(const SDOMElement &elem) { return elem.m_PreviousSibling; } + void set(SDOMElement &elem, SDOMElement *prev) { elem.m_PreviousSibling = prev; } + }; + typedef InvasiveLinkedList<SDOMElement, SPrevElemOp, SNextElemOp> TElementChildList; + + CRegisteredString m_Namespace; + CRegisteredString m_Name; + const char8_t *m_Value; + SDOMElement *m_Parent; + SDOMElement *m_NextSibling; + SDOMElement *m_PreviousSibling; + TAttributeList m_Attributes; + TElementChildList m_Children; + int m_Line; + + SDOMElement() + : m_Value(NULL) + , m_Parent(NULL) + , m_NextSibling(NULL) + , m_PreviousSibling(NULL) + , m_Line(0) + { + } + + SDOMElement(CRegisteredString name, CRegisteredString ns, int inLine) + : m_Namespace(ns) + , m_Name(name) + , m_Value(NULL) + , m_Parent(NULL) + , m_NextSibling(NULL) + , m_PreviousSibling(NULL) + , m_Line(inLine) + { + } + + void AppendChild(SDOMElement &inElem, SDOMElement *inPos = NULL) + { + if (inElem.m_Parent) + inElem.m_Parent->RemoveChild(inElem); + inElem.m_Parent = this; + if (inPos) { + QT3DS_ASSERT(inPos->m_Parent == this); + m_Children.insert_after(*inPos, inElem); + } else + m_Children.push_back(inElem); + } + + void PrependChild(SDOMElement &inElem, SDOMElement *inPos = NULL) + { + if (inElem.m_Parent) + inElem.m_Parent->RemoveChild(inElem); + inElem.m_Parent = this; + if (inPos) { + QT3DS_ASSERT(inPos->m_Parent == this); + m_Children.insert_before(*inPos, inElem); + } else + m_Children.push_front(inElem); + } + + void RemoveChild(SDOMElement &inElem) + { + if (inElem.m_Parent == this) { + m_Children.remove(inElem); + } else { + if (inElem.m_Parent) { + QT3DS_ASSERT(false); + } + } + } + SDOMElement *FindNext(CRegisteredString inName, Option<CRegisteredString> inNamespace, + TElementChildList::iterator iter) + { + for (TElementChildList::iterator end(NULL); iter != end; ++iter) { + if (inName.IsValid() == false || inName == iter->m_Name) { + if (inNamespace.hasValue() == false || *inNamespace == iter->m_Namespace) + return &(*iter); + } + } + return NULL; + } + // Search starts just *after inStartPos if provided + SDOMElement *FindChild(CRegisteredString inName, Option<CRegisteredString> inNamespace, + SDOMElement *inStartPos = NULL) + { + TElementChildList::iterator iter = m_Children.begin(); + if (inStartPos) { + QT3DS_ASSERT(inStartPos->m_Parent == this); + iter = TElementChildList::iterator(inStartPos->m_NextSibling); + } + return FindNext(inName, inNamespace, iter); + } + // Search starts just *after inStartPos if provided + SDOMElement *FindSibling(CRegisteredString inName, Option<CRegisteredString> inNamespace) + { + TElementChildList::iterator iter(m_NextSibling); + return FindNext(inName, inNamespace, iter); + } + + SDOMAttribute *FindAttribute(CRegisteredString inName, + Option<CRegisteredString> inNamespace) + { + for (TAttributeList::iterator iter = m_Attributes.begin(), end = m_Attributes.end(); + iter != end; ++iter) { + if (iter->m_Name == inName) { + if (inNamespace.hasValue() == false || *inNamespace == iter->m_Namespace) + return &(*iter); + } + } + return NULL; + } + + template <typename TDataType> + bool AttValue(CRegisteredString inName, Option<CRegisteredString> inNamespace, + TDataType &outValue) + { + SDOMAttribute *att = FindAttribute(inName, inNamespace); + if (att) + return att->Value(outValue); + return false; + } + + QT3DSU32 GetNumChildren(CRegisteredString inName, + Option<CRegisteredString> inNamespace = Empty()) const + { + QT3DSU32 count = 0; + if (inName.IsValid() == false) { + // empty loop intentional + for (TElementChildList::iterator iter = m_Children.begin(), end = m_Children.end(); + iter != end; ++iter, ++count) + ; + } else { + for (TElementChildList::iterator iter = m_Children.begin(), end = m_Children.end(); + iter != end; ++iter) { + if (iter->m_Name == inName + && (inNamespace.isEmpty() || iter->m_Namespace == *inNamespace)) + ++count; + } + } + return count; + } + + void SetAttributeValue(CRegisteredString inName, Option<CRegisteredString> inNamespace, + TXMLCharPtr inValue, IDOMFactory &inFactory, int inLine) + { + SDOMAttribute *att = FindAttribute(inName, inNamespace); + if (att) { + att->m_Value = inFactory.RegisterValue(inValue); + } else { + m_Attributes.push_back(*inFactory.NextAttribute( + inName, inValue, inNamespace.unsafeGetValue(), inLine)); + } + } + + void RemoveAttribute(CRegisteredString nm, Option<CRegisteredString> inNamespace) + { + SDOMAttribute *theAttribute = FindAttribute(nm, inNamespace); + if (theAttribute) + m_Attributes.remove(*theAttribute); + } + }; + + struct SReaderScope + { + SDOMElement *m_Element; + SDOMAttribute *m_Attribute; + SReaderScope(SDOMElement *inElem = NULL, SDOMAttribute *inAtt = NULL) + : m_Element(inElem) + , m_Attribute(inAtt) + { + } + }; + + class IDOMReader : public NVRefCounted + { + protected: + virtual ~IDOMReader() {} + public: + // Stack object to save the reader's state. + struct Scope + { + IDOMReader &m_Reader; + Scope(IDOMReader &reader) + : m_Reader(reader) + { + m_Reader.PushScope(); + } + Scope(NVScopedRefCounted<IDOMReader> reader) + : m_Reader(*reader) + { + m_Reader.PushScope(); + } + ~Scope() { m_Reader.PopScope(); } + }; + + // Parse buffer + MemoryBuffer<ForwardingAllocator> m_TempBuf; + NVScopedRefCounted<IStringTable> m_StringTable; + + IDOMReader(NVAllocatorCallback &inAlloc, NVScopedRefCounted<IStringTable> inStringTable) + : m_TempBuf(ForwardingAllocator(inAlloc, "IDOMReader::m_TempBuf")) + , m_StringTable(inStringTable) + { + } + + NVScopedRefCounted<IStringTable> GetStringTable() { return m_StringTable; } + + // Pushing scope saves your state so no matter where you are when + // you next pop scope, you come back to the same state. + virtual void PushScope() = 0; + virtual void PopScope() = 0; + + // Sometimes pushing and popping scope isn't enough and you need + // somewhat random access to scope. + // This scope does not remember the current attribute. + virtual SReaderScope GetScope() = 0; + virtual void SetScope(SReaderScope inScope) = 0; + // Return an attribute whose value is *not* registered with the string table. + // You can't hold onto this value for any length of time, but when you need to + // immediately convert to a different value this is the most efficient way. + virtual bool UnregisteredAtt(TXMLCharPtr name, TXMLCharPtr &outValue, + TXMLCharPtr ns = NULL) = 0; + // Return an attribute whose value *is* registered with the string table. + virtual bool Att(TXMLCharPtr name, CRegisteredString &outValue, TXMLCharPtr ns = NULL) = 0; + virtual SDOMAttribute *GetFirstAttribute() = 0; + virtual SDOMAttribute *GetNextAttribute() = 0; + virtual QT3DSU32 CountChildren(TXMLCharPtr childName = NULL, TXMLCharPtr ns = NULL) = 0; + virtual bool MoveToFirstChild(TXMLCharPtr childName = NULL, TXMLCharPtr ns = NULL) = 0; + virtual bool MoveToNextSibling(TXMLCharPtr siblingName = NULL, TXMLCharPtr ns = NULL) = 0; + // Leave element means go to its parent. + virtual void Leave() = 0; + virtual SDOMElement *GetElement() const = 0; + CRegisteredString GetElementName() const + { + SDOMElement *elem = GetElement(); + if (elem) + return elem->m_Name; + return CRegisteredString(); + } + + // Value is the concatentated text node values inside the element + virtual bool Value(TXMLCharPtr &outValue) = 0; + + // Get the element this reader was created with + virtual SDOMElement *GetTopElement() = 0; + + bool Att(TXMLCharPtr name, TXMLStr &outValue) + { + TXMLCharPtr temp; + if (UnregisteredAtt(name, temp)) { + outValue.assign(temp); + return true; + } + return false; + } + + template <typename TDataType> + bool Att(TXMLCharPtr name, TDataType &outValue) + { + TXMLCharPtr temp; + if (UnregisteredAtt(name, temp)) { + StringConversion<TDataType>().StrTo(temp, outValue); + return true; + } else { + return false; + } + } + + bool ChildValue(TXMLCharPtr name, TXMLCharPtr &value) + { + if (MoveToFirstChild(name)) { + Value(value); + Leave(); + return true; + } + return false; + } + + bool RegisteredChildValue(TXMLCharPtr name, TXMLCharPtr &value) + { + if (MoveToFirstChild(name)) { + RegisteredValue(value); + Leave(); + return true; + } + return false; + } + bool RegisteredValue(TXMLCharPtr &outValue) + { + bool retval = Value(outValue); + if (retval) + outValue = m_StringTable->RegisterStr(outValue); + return retval; + } + + template <typename TDataType> + bool Value(TDataType &outValue) + { + TXMLCharPtr value; + if (Value(value)) { + StringConversion<TDataType>().StrTo(value, outValue); + return true; + } + return false; + } + + // IDOMReader implementations + template <typename TDataType> + bool ValueList(NVDataRef<TDataType> data) + { + const char8_t *value; + if (Value(value)) { + Char8TReader reader(const_cast<char8_t *>(value), m_TempBuf); + reader.ReadRef(data); + } + return true; + } + + // Destructive operation because we can't trust + // strtod to do the right thing. On windows, for long strings, + // it calls strlen every operation thus leading to basically N^2 + // behavior + template <typename TDataType> + NVConstDataRef<TDataType> ChildValueList(TXMLCharPtr listName) + { + NVConstDataRef<TDataType> retval; + TXMLCharPtr childValue = NULL; + if (ChildValue(listName, childValue)) { + Char8TReader reader(const_cast<char8_t *>(childValue), m_TempBuf); + reader.ReadBuffer(retval); + } + return retval; + } + + template <typename TSerializer> + void Serialize(const char8_t *elemName, TSerializer &serializer, + const char8_t *inNamespace = NULL) + { + IDOMReader::Scope __theScope(*this); + if (MoveToFirstChild(elemName, inNamespace)) { + serializer.Serialize(*this); + } + } + // Optionally hold on to the factory to keep our elements in memory as long as we are. + static NVScopedRefCounted<IDOMReader> CreateDOMReader( + NVAllocatorCallback &inAlloc, SDOMElement &inRootElement, + NVScopedRefCounted<IStringTable> inStringTable, + NVScopedRefCounted<IDOMFactory> inFactory = NVScopedRefCounted<IDOMFactory>()); + }; + + // Write out data in an xml-like fasion without specifying exactly + // where that data is being written. + class IDOMWriter : public NVRefCounted + { + protected: + virtual ~IDOMWriter() {} + public: + // Control the element scope. + struct Scope + { + IDOMWriter &m_Writer; + Scope(IDOMWriter &writer, TXMLCharPtr inElemName, TXMLCharPtr inNamespace = NULL) + : m_Writer(writer) + { + m_Writer.Begin(inElemName, inNamespace); + } + ~Scope() { m_Writer.End(); } + }; + + char8_t m_NarrowBuf[256]; + + // There tend to be two types of elements. + // Containers (<a>\n\t<b/><b/>\n</a>) + // and Values <b>onetwothree</b> + virtual void Begin(TXMLCharPtr inElemName, TXMLCharPtr inNamespace = NULL, + int inLine = 0) = 0; + // Attributes. They may be sorted just before write + virtual void Att(TXMLCharPtr name, TXMLCharPtr value, TXMLCharPtr inNamespace = NULL, + int inLine = 0) = 0; + virtual void Value(TXMLCharPtr value) = 0; + virtual void End() = 0; + virtual void RemoveCurrent() = 0; + virtual void RemoveAttribute(TXMLCharPtr inItem, TXMLCharPtr inNamespace = NULL) = 0; + // Get the number of tabs required to line up the next line + // with the opening of the previous line + virtual QT3DSU32 GetTabs() = 0; + virtual SDOMElement *GetTopElement() = 0; + virtual NVScopedRefCounted<IDOMFactory> GetFactory() = 0; + // Move this item before this sibling. Function does not rearrange the + // tree in any major way and will not work if inItem and inSibling aren't + // siblings. + virtual void MoveBefore(TXMLCharPtr inItem, TXMLCharPtr inSibling, + TXMLCharPtr inNamespace = NULL) = 0; + virtual NVAllocatorCallback &GetAllocator() = 0; + + virtual void ChildValue(TXMLCharPtr name, TXMLCharPtr value, TXMLCharPtr inNamespace = NULL) + { + Begin(name, inNamespace); + Value(value); + End(); + } + + TXMLCharPtr ToStr(char8_t val) + { + m_NarrowBuf[0] = val; + m_NarrowBuf[1] = 0; + return m_NarrowBuf; + } + + template <typename TDataType> + TXMLCharPtr ToStr(TDataType val) + { + StringConversion<TDataType>().ToStr(val, NVDataRef<char8_t>(m_NarrowBuf, 256)); + return m_NarrowBuf; + } + + void Att(TXMLCharPtr name, const TXMLStr &inValue, TXMLCharPtr inNamespace = NULL) + { + return Att(name, inValue.c_str(), inNamespace); + } + + template <typename TData> + void Att(TXMLCharPtr name, TData value, TXMLCharPtr inNamespace = NULL) + { + Att(name, ToStr(value), inNamespace); + } + + template <typename TSerializer> + void Serialize(const char8_t *elemName, TSerializer &serializer, + TXMLCharPtr inNamespace = NULL) + { + IDOMWriter::Scope __theScope(*this, elemName, inNamespace); + serializer.Serialize(*this); + } + + NVScopedRefCounted<IDOMReader> CreateDOMReader() + { + NVScopedRefCounted<IDOMFactory> theFactory(GetFactory()); + return IDOMReader::CreateDOMReader(GetAllocator(), *GetTopElement(), + theFactory->GetStringTable(), theFactory); + } + + // Note that the default method of creating a writer also creates a reader; they can + // both manipulation the DOM hierarch. + static eastl::pair<NVScopedRefCounted<IDOMWriter>, NVScopedRefCounted<IDOMReader>> + CreateDOMWriter(NVScopedRefCounted<IDOMFactory> inFactory, SDOMElement &inRootElement, + NVScopedRefCounted<IStringTable> inStringTable); + + static eastl::pair<NVScopedRefCounted<IDOMWriter>, NVScopedRefCounted<IDOMReader>> + CreateDOMWriter(NVAllocatorCallback &inAlloc, const char8_t *inTopElemName, + NVScopedRefCounted<IStringTable> inStringTable, + const char8_t *inNamespace = NULL) + { + NVScopedRefCounted<IDOMFactory> theFactory( + IDOMFactory::CreateDOMFactory(inAlloc, inStringTable)); + SDOMElement *theRoot = + theFactory->NextElement(theFactory->GetStringTable()->RegisterStr(inTopElemName), + theFactory->GetStringTable()->RegisterStr(inNamespace)); + return CreateDOMWriter(theFactory, *theRoot, inStringTable); + } + }; + + class CXmlErrorHandler + { + protected: + virtual ~CXmlErrorHandler() {} + public: + virtual void OnXmlError(TXMLCharPtr errorName, int line, int column) = 0; + }; + + class CDOMSerializer + { + public: + static void WriteXMLHeader(IOutStream &inStream); + + // Write out the elements. These pairs will be used in order to abbreviate namespaces with + // nicer abbreviations. + // Any namespaces not found in those pairs will get auto-generated abbreviations + // The document will be assumed to be in the namespace that has no abbreviation. + // For fragments or snippets, it is better to avoid writing out the namespace declarations + static void + Write(NVAllocatorCallback &inAlloc, SDOMElement &inElement, IOutStream &inStream, + IStringTable &inStrTable, + NVConstDataRef<SNamespacePair> inNamespaces = NVConstDataRef<SNamespacePair>(), + bool inTrimEmptyElements = true, QT3DSU32 inTabs = 0, bool inWriteNamespaces = true); + + // Returns a pair of the namespace pair nodes and the dom element. The ns pair nodes allow + // us to at least keep the same abbreviations + // used if namespace are used. + static eastl::pair<SNamespacePairNode *, SDOMElement *> + Read(IDOMFactory &inFactory, IInStream &inStream, CXmlErrorHandler *inErrorHandler = NULL); + }; +} +} +#endif diff --git a/src/foundation/linux/LICENSE.TXT b/src/foundation/linux/LICENSE.TXT new file mode 100644 index 0000000..f40caef --- /dev/null +++ b/src/foundation/linux/LICENSE.TXT @@ -0,0 +1,7 @@ + Copyright (c) 2001 Intel Corporation. + +Permition is granted to use, copy, distribute and prepare derivative works +of this library for any purpose and without fee, provided, that the above +copyright notice and this statement appear in all copies. +Intel makes no representations about the suitability of this library for +any purpose, and specifically disclaims all warranties. diff --git a/src/foundation/linux/Qt3DSLinuxAoS.h b/src/foundation/linux/Qt3DSLinuxAoS.h new file mode 100644 index 0000000..6bcb440 --- /dev/null +++ b/src/foundation/linux/Qt3DSLinuxAoS.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_LINUX_AOS_H +#define QT3DS_LINUX_AOS_H + +// no includes here! this file should be included from NVcVecMath.h only!!! + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +// only SSE compatible platforms should reach this +// likely to be split up when we add NEON support. +#include <xmmintrin.h> + +typedef union UnionM128 { + UnionM128() {} + UnionM128(__m128 in) { m128 = in; } + + UnionM128(__m128i in) { m128i = in; } + + operator __m128() { return m128; } + + operator const __m128() const { return m128; } + + float m128_f32[4]; + __int8_t m128_i8[16]; + __int16_t m128_i16[8]; + __int32_t m128_i32[4]; + __int64_t m128_i64[2]; + __uint16_t m128_u16[8]; + __uint32_t m128_u32[4]; + __uint64_t m128_u64[2]; + __m128 m128; + __m128i m128i; +} UnionM128; + +typedef __m128 FloatV; +typedef __m128 Vec3V; +typedef __m128 Vec4V; +typedef __m128 BoolV; +typedef __m128 QuatV; +// typedef __m128 VecU32V; +// typedef __m128 VecI32V; +// typedef __m128 VecU16V; +// typedef __m128 VecI16V; +// typedef __m128 VecU8V; +typedef UnionM128 VecU32V; +typedef UnionM128 VecI32V; +typedef UnionM128 VecU16V; +typedef UnionM128 VecI16V; +typedef UnionM128 VecU8V; + +#define FloatVArg FloatV & +#define Vec3VArg Vec3V & +#define Vec4VArg Vec4V & +#define BoolVArg BoolV & +#define VecU32VArg VecU32V & +#define VecI32VArg VecI32V & +#define VecU16VArg VecU16V & +#define VecI16VArg VecI16V & +#define VecU8VArg VecU8V & +#define QuatVArg QuatV & + +QT3DS_ALIGN_PREFIX(16) +struct Mat33V +{ + Mat33V() {} + Mat33V(const Vec3V &c0, const Vec3V &c1, const Vec3V &c2) + : col0(c0) + , col1(c1) + , col2(c2) + { + } + Vec3V QT3DS_ALIGN(16, col0); + Vec3V QT3DS_ALIGN(16, col1); + Vec3V QT3DS_ALIGN(16, col2); +} QT3DS_ALIGN_SUFFIX(16); + +QT3DS_ALIGN_PREFIX(16) +struct Mat34V +{ + Mat34V() {} + Mat34V(const Vec3V &c0, const Vec3V &c1, const Vec3V &c2, const Vec3V &c3) + : col0(c0) + , col1(c1) + , col2(c2) + , col3(c3) + { + } + Vec3V QT3DS_ALIGN(16, col0); + Vec3V QT3DS_ALIGN(16, col1); + Vec3V QT3DS_ALIGN(16, col2); + Vec3V QT3DS_ALIGN(16, col3); +} QT3DS_ALIGN_SUFFIX(16); + +QT3DS_ALIGN_PREFIX(16) +struct Mat43V +{ + Mat43V() {} + Mat43V(const Vec4V &c0, const Vec4V &c1, const Vec4V &c2) + : col0(c0) + , col1(c1) + , col2(c2) + { + } + Vec4V QT3DS_ALIGN(16, col0); + Vec4V QT3DS_ALIGN(16, col1); + Vec4V QT3DS_ALIGN(16, col2); +} QT3DS_ALIGN_SUFFIX(16); + +QT3DS_ALIGN_PREFIX(16) +struct Mat44V +{ + Mat44V() {} + Mat44V(const Vec4V &c0, const Vec4V &c1, const Vec4V &c2, const Vec4V &c3) + : col0(c0) + , col1(c1) + , col2(c2) + , col3(c3) + { + } + Vec4V QT3DS_ALIGN(16, col0); + Vec4V QT3DS_ALIGN(16, col1); + Vec4V QT3DS_ALIGN(16, col2); + Vec4V QT3DS_ALIGN(16, col3); +} QT3DS_ALIGN_SUFFIX(16); + +#endif // QT3DS_LINUX_AOS_H diff --git a/src/foundation/linux/Qt3DSLinuxAtomic.cpp b/src/foundation/linux/Qt3DSLinuxAtomic.cpp new file mode 100644 index 0000000..f0045dd --- /dev/null +++ b/src/foundation/linux/Qt3DSLinuxAtomic.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSAtomic.h" +#ifdef __QNX__ +#include <atomic.h> +#endif +#ifdef __INTEGRITY +#include <QtCore/qmutex.h> +#endif + +#define PAUSE() asm("nop") + +namespace qt3ds { +namespace foundation { +#ifdef __QNX__ + QT3DSI32 atomicIncrement(volatile QT3DSI32 *val) + { + atomic_add((volatile unsigned *)val, 1); + return *val; + } + + QT3DSI32 atomicDecrement(volatile QT3DSI32 *val) + { + atomic_sub((volatile unsigned *)val, 1); + return *val; + } +#elif defined (__INTEGRITY) + // TestAndSet and AtomicModify take in volatile Address* leading to overflow error if used + + QT3DSI32 atomicCompareExchange(volatile QT3DSI32 *dest, QT3DSI32 exch, QT3DSI32 comp) + { + static QMutex mutex; + QMutexLocker lock(&mutex); + QT3DSI32 ret = *dest; + *dest = (*dest == comp) ? exch : ret; + return ret; + } + + QT3DSI32 atomicIncrement(volatile QT3DSI32 *val) + { + static QMutex mutex; + QMutexLocker lock(&mutex); + return ++(*val); + } + + QT3DSI32 atomicDecrement(volatile QT3DSI32 *val) + { + static QMutex mutex; + QMutexLocker lock(&mutex); + return --(*val); + } +#else + void *atomicCompareExchangePointer(volatile void **dest, void *exch, void *comp) + { + return __sync_val_compare_and_swap((void **)dest, comp, exch); + } + + QT3DSI32 atomicCompareExchange(volatile QT3DSI32 *dest, QT3DSI32 exch, QT3DSI32 comp) + { + return __sync_val_compare_and_swap(dest, comp, exch); + } + + QT3DSI32 atomicIncrement(volatile QT3DSI32 *val) { return __sync_add_and_fetch(val, 1); } + + QT3DSI32 atomicDecrement(volatile QT3DSI32 *val) { return __sync_sub_and_fetch(val, 1); } + + QT3DSI32 atomicAdd(volatile QT3DSI32 *val, QT3DSI32 delta) { return __sync_add_and_fetch(val, delta); } + + QT3DSI32 atomicMax(volatile QT3DSI32 *val, QT3DSI32 val2) + { + QT3DSI32 oldVal, newVal; + + do { + PAUSE(); + oldVal = *val; + + if (val2 > oldVal) + newVal = val2; + else + newVal = oldVal; + + } while (atomicCompareExchange(val, newVal, oldVal) != oldVal); + + return *val; + } + + QT3DSI32 atomicExchange(volatile QT3DSI32 *val, QT3DSI32 val2) + { + QT3DSI32 newVal, oldVal; + + do { + PAUSE(); + oldVal = *val; + newVal = val2; + } while (atomicCompareExchange(val, newVal, oldVal) != oldVal); + + return newVal; + } +#endif +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/linux/Qt3DSLinuxFPU.cpp b/src/foundation/linux/Qt3DSLinuxFPU.cpp new file mode 100644 index 0000000..c3eae21 --- /dev/null +++ b/src/foundation/linux/Qt3DSLinuxFPU.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "foundation/Qt3DSFPU.h" +#include <fenv.h> + +QT3DS_COMPILE_TIME_ASSERT(8 * sizeof(qt3ds::QT3DSU32) >= sizeof(fenv_t)); + +qt3ds::foundation::FPUGuard::FPUGuard() +{ +#ifdef __CYGWIN__ +#pragma message "FPUGuard::FPUGuard() is not implemented" +#else + fegetenv(reinterpret_cast<fenv_t *>(mControlWords)); + fesetenv(FE_DFL_ENV); +#endif +} + +qt3ds::foundation::FPUGuard::~FPUGuard() +{ +#ifdef __CYGWIN__ +#pragma message "FPUGuard::~FPUGuard() is not implemented" +#else + fesetenv(reinterpret_cast<fenv_t *>(mControlWords)); +#endif +} diff --git a/src/foundation/linux/Qt3DSLinuxFile.h b/src/foundation/linux/Qt3DSLinuxFile.h new file mode 100644 index 0000000..1369735 --- /dev/null +++ b/src/foundation/linux/Qt3DSLinuxFile.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_LINUX_FILE_H +#define QT3DS_FOUNDATION_QT3DS_LINUX_FILE_H + +#include "foundation/Qt3DS.h" +#include <stdio.h> + +namespace qt3ds { +namespace foundation { + QT3DS_INLINE int fopen_s(FILE **_File, const char *_Filename, const char *_Mode) + { + FILE *fp = ::fopen(_Filename, _Mode); + return fp ? *_File = fp, 0 : -1; + }; + +} // namespace foundation +} // namespace qt3ds + +#endif diff --git a/src/foundation/linux/Qt3DSLinuxInlineAoS.h b/src/foundation/linux/Qt3DSLinuxInlineAoS.h new file mode 100644 index 0000000..0443ba9 --- /dev/null +++ b/src/foundation/linux/Qt3DSLinuxInlineAoS.h @@ -0,0 +1,2666 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// Copyright (c) 2001 Intel Corporation. +// +// Permition is granted to use, copy, distribute and prepare derivative works +// of this library for any purpose and without fee, provided, that the above +// copyright notice and this statement appear in all copies. +// Intel makes no representations about the suitability of this library for +// any purpose, and specifically disclaims all warranties. +// See LEGAL.TXT for all the legal information. +// + +#ifndef QT3DS_LINUX_INLINE_AOS_H +#define QT3DS_LINUX_INLINE_AOS_H + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +// Remove this define when all platforms use simd solver. +#define QT3DS_SUPPORT_SIMD + +#define _QT3DS_FPCLASS_SNAN 0x0001 /* signaling NaN */ +#define _QT3DS_FPCLASS_QNAN 0x0002 /* quiet NaN */ +#define _QT3DS_FPCLASS_NINF 0x0004 /* negative infinity */ +#define _QT3DS_FPCLASS_PINF 0x0200 /* positive infinity */ + +QT3DS_FORCE_INLINE __m128 m128_I2F(__m128i n) +{ + return _mm_castsi128_ps(n); +} +QT3DS_FORCE_INLINE __m128i m128_F2I(__m128 n) +{ + return _mm_castps_si128(n); +} + +QT3DS_FORCE_INLINE QT3DSU32 BAllTrue4_R(const BoolV a) +{ + const QT3DSI32 moveMask = _mm_movemask_ps(a); + return moveMask == (0xf); +} + +QT3DS_FORCE_INLINE QT3DSU32 BAnyTrue4_R(const BoolV a) +{ + const QT3DSI32 moveMask = _mm_movemask_ps(a); + return moveMask != (0x0); +} + +QT3DS_FORCE_INLINE QT3DSU32 BAllTrue3_R(const BoolV a) +{ + const QT3DSI32 moveMask = _mm_movemask_ps(a); + return (moveMask & 0x7) == (0x7); +} + +QT3DS_FORCE_INLINE QT3DSU32 BAnyTrue3_R(const BoolV a) +{ + const QT3DSI32 moveMask = _mm_movemask_ps(a); + return (moveMask & 0x7) != (0x0); +} + +///////////////////////////////////////////////////////////////////// +////FUNCTIONS USED ONLY FOR ASSERTS IN VECTORISED IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +QT3DS_FORCE_INLINE QT3DSU32 FiniteTestEq(const Vec4V a, const Vec4V b) +{ + // This is a bit of a bodge. + //_mm_comieq_ss returns 1 if either value is nan so we need to re-cast a and b with true encoded + //as a non-nan number. + // There must be a better way of doing this in sse. + const BoolV one = FOne(); + const BoolV zero = FZero(); + const BoolV a1 = V4Sel(a, one, zero); + const BoolV b1 = V4Sel(b, one, zero); + return (_mm_comieq_ss(a1, b1) && _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(1, 1, 1, 1)), + _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(1, 1, 1, 1))) + && _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(2, 2, 2, 2)), + _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(2, 2, 2, 2))) + && _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(3, 3, 3, 3)), + _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(3, 3, 3, 3)))); +} + +QT3DS_FORCE_INLINE bool isValidFloatV(const FloatV a) +{ + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))) + && _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))) + && _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)))); +} + +QT3DS_FORCE_INLINE bool isValidVec3V(const Vec3V a) +{ + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)), FZero()) ? true : false); +} + +QT3DS_FORCE_INLINE bool isFiniteFloatV(const FloatV a) +{ + const QT3DSU32 badNumber = + (_QT3DS_FPCLASS_SNAN | _QT3DS_FPCLASS_QNAN | _QT3DS_FPCLASS_NINF | _QT3DS_FPCLASS_PINF); + const FloatV vBadNum = FloatV_From_F32((QT3DSF32 &)badNumber); + const BoolV vMask = BAnd(vBadNum, a); + return FiniteTestEq(vMask, BFFFF()) == 1; +} + +QT3DS_FORCE_INLINE bool isFiniteVec3V(const Vec3V a) +{ + const QT3DSU32 badNumber = + (_QT3DS_FPCLASS_SNAN | _QT3DS_FPCLASS_QNAN | _QT3DS_FPCLASS_NINF | _QT3DS_FPCLASS_PINF); + const Vec3V vBadNum = Vec3V_From_F32((QT3DSF32 &)badNumber); + const BoolV vMask = BAnd(BAnd(vBadNum, a), BTTTF()); + return FiniteTestEq(vMask, BFFFF()) == 1; +} + +QT3DS_FORCE_INLINE bool isFiniteVec4V(const Vec4V a) +{ + /*Vec4V a; + QT3DS_ALIGN(16, QT3DSF32 f[4]); + F32Array_Aligned_From_Vec4V(a, f); + return NVIsFinite(f[0]) + && NVIsFinite(f[1]) + && NVIsFinite(f[2]) + && NVIsFinite(f[3]);*/ + + const QT3DSU32 badNumber = + (_QT3DS_FPCLASS_SNAN | _QT3DS_FPCLASS_QNAN | _QT3DS_FPCLASS_NINF | _QT3DS_FPCLASS_PINF); + const Vec4V vBadNum = Vec4V_From_F32((QT3DSF32 &)badNumber); + const BoolV vMask = BAnd(vBadNum, a); + + return FiniteTestEq(vMask, BFFFF()) == 1; +} + +QT3DS_FORCE_INLINE bool hasZeroElementinFloatV(const FloatV a) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FloatV_From_F32(0.0f)) + ? true + : false); +} + +QT3DS_FORCE_INLINE bool hasZeroElementInVec3V(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FloatV_From_F32(0.0f)) + || _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)), FloatV_From_F32(0.0f)) + || _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)), FloatV_From_F32(0.0f))); +} + +QT3DS_FORCE_INLINE bool hasZeroElementInVec4V(const Vec4V a) +{ + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FloatV_From_F32(0.0f)) + || _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)), FloatV_From_F32(0.0f)) + || _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)), FloatV_From_F32(0.0f)) + || _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)), FloatV_From_F32(0.0f))); +} + +///////////////////////////////////////////////////////////////////// +////VECTORISED FUNCTION IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +QT3DS_FORCE_INLINE FloatV FloatV_From_F32(const QT3DSF32 f) +{ + return (_mm_load1_ps(&f)); +} + +QT3DS_FORCE_INLINE Vec3V Vec3V_From_F32(const QT3DSF32 f) +{ + return _mm_set_ps(0.0f, f, f, f); +} + +QT3DS_FORCE_INLINE Vec4V Vec4V_From_F32(const QT3DSF32 f) +{ + return (_mm_load1_ps(&f)); +} + +QT3DS_FORCE_INLINE BoolV BoolV_From_Bool32(const bool f) +{ + const QT3DSU32 i = -(QT3DSI32)f; + return _mm_load1_ps((float *)&i); +} + +QT3DS_FORCE_INLINE Vec3V Vec3V_From_NVVec3_Aligned(const QT3DSVec3 &f) +{ + VECMATHAOS_ASSERT(0 == ((size_t)&f & 0x0f)); + return (_mm_set_ps(0.0f, f.z, f.y, f.x)); +} + +QT3DS_FORCE_INLINE Vec3V Vec3V_From_NVVec3(const QT3DSVec3 &f) +{ + return (_mm_set_ps(0.0f, f.z, f.y, f.x)); +} + +QT3DS_FORCE_INLINE Vec3V Vec3V_From_NVVec3_WUndefined(const QT3DSVec3 &f) +{ + return (_mm_set_ps(0.0f, f.z, f.y, f.x)); +} + +QT3DS_FORCE_INLINE Vec3V Vec3V_From_Vec4V(Vec4V v) +{ + return V4SetW(v, V4Zero()); +} + +QT3DS_FORCE_INLINE Vec4V Vec4V_From_Vec3V(Vec3V f) +{ + return f; // ok if it is implemented as the same type. +} + +QT3DS_FORCE_INLINE Vec4V Vec4V_From_NVVec3_WUndefined(const QT3DSVec3 &f) +{ + return (_mm_set_ps(0.0f, f.z, f.y, f.x)); +} + +QT3DS_FORCE_INLINE Mat33V Mat33V_From_NVMat33(const QT3DSMat33 &m) +{ + return Mat33V(Vec3V_From_NVVec3(m.column0), Vec3V_From_NVVec3(m.column1), + Vec3V_From_NVVec3(m.column2)); +} + +QT3DS_FORCE_INLINE void NVMat33_From_Mat33V(const Mat33V &m, QT3DSMat33 &out) +{ + QT3DS_ASSERT((size_t(&out) & 15) == 0); + NVVec3_From_Vec3V(m.col0, out.column0); + NVVec3_From_Vec3V(m.col1, out.column1); + NVVec3_From_Vec3V(m.col2, out.column2); +} + +QT3DS_FORCE_INLINE Vec4V Vec4V_From_F32Array_Aligned(const QT3DSF32 *const f) +{ + VECMATHAOS_ASSERT(0 == ((QT3DSU64)f & 0x0f)); + return (_mm_load_ps(f)); +} + +QT3DS_FORCE_INLINE void F32Array_Aligned_From_Vec4V(Vec4V a, QT3DSF32 *f) +{ + VECMATHAOS_ASSERT(0 == ((QT3DSU64)f & 0x0f)); + _mm_store_ps(f, a); +} + +QT3DS_FORCE_INLINE Vec4V Vec4V_From_F32Array(const QT3DSF32 *const f) +{ + return (_mm_loadu_ps(f)); +} + +QT3DS_FORCE_INLINE BoolV BoolV_From_Bool32Array(const bool *const f) +{ + const QT3DS_ALIGN(16, QT3DSU32) b[4] = { -(QT3DSI32)f[0], -(QT3DSI32)f[1], -(QT3DSI32)f[2], -(QT3DSI32)f[3] }; + return _mm_load1_ps((float *)&b); +} + +QT3DS_FORCE_INLINE QT3DSF32 NVF32_From_FloatV(const FloatV a) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + QT3DSF32 f; + _mm_store_ss(&f, a); + return f; +} + +QT3DS_FORCE_INLINE void NVF32_From_FloatV(const FloatV a, QT3DSF32 *QT3DS_RESTRICT f) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + _mm_store_ss(f, a); +} + +QT3DS_FORCE_INLINE void NVVec3Aligned_From_Vec3V(const Vec3V a, QT3DSVec3 &f) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(0 == ((int)&a & 0x0F)); + VECMATHAOS_ASSERT(0 == ((int)&f & 0x0F)); + QT3DS_ALIGN(16, QT3DSF32) f2[4]; + _mm_store_ps(f2, a); + f = QT3DSVec3(f2[0], f2[1], f2[2]); +} + +QT3DS_FORCE_INLINE void NVVec3_From_Vec3V(const Vec3V a, QT3DSVec3 &f) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(0 == ((int)&a & 0x0F)); + QT3DS_ALIGN(16, QT3DSF32) f2[4]; + _mm_store_ps(f2, a); + f = QT3DSVec3(f2[0], f2[1], f2[2]); +} + +QT3DS_FORCE_INLINE bool _VecMathTests::allElementsEqualFloatV(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return (_mm_comieq_ss(a, b) != 0); +} + +QT3DS_FORCE_INLINE bool _VecMathTests::allElementsEqualVec3V(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return V3AllEq(a, b) != 0; +} + +QT3DS_FORCE_INLINE bool _VecMathTests::allElementsEqualVec4V(const Vec4V a, const Vec4V b) +{ + return V4AllEq(a, b) != 0; +} + +QT3DS_FORCE_INLINE bool _VecMathTests::allElementsEqualBoolV(const BoolV a, const BoolV b) +{ + return BAllTrue4_R(VecI32V_IsEq(a, b)) != 0; +} + +#define VECMATH_AOS_EPSILON (1e-3f) + +QT3DS_FORCE_INLINE bool _VecMathTests::allElementsNearEqualFloatV(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + const FloatV c = FSub(a, b); + const FloatV minError = FloatV_From_F32(-VECMATH_AOS_EPSILON); + const FloatV maxError = FloatV_From_F32(VECMATH_AOS_EPSILON); + return (_mm_comigt_ss(c, minError) && _mm_comilt_ss(c, maxError)); +} + +QT3DS_FORCE_INLINE bool _VecMathTests::allElementsNearEqualVec3V(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + const Vec3V c = V3Sub(a, b); + const Vec3V minError = Vec3V_From_F32(-VECMATH_AOS_EPSILON); + const Vec3V maxError = Vec3V_From_F32(VECMATH_AOS_EPSILON); + return (_mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), minError) + && _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), maxError) + && _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), minError) + && _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), maxError) + && _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), minError) + && _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), maxError)); +} + +QT3DS_FORCE_INLINE bool _VecMathTests::allElementsNearEqualVec4V(const Vec4V a, const Vec4V b) +{ + const Vec4V c = V4Sub(a, b); + const Vec4V minError = Vec4V_From_F32(-VECMATH_AOS_EPSILON); + const Vec4V maxError = Vec4V_From_F32(VECMATH_AOS_EPSILON); + return (_mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), minError) + && _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), maxError) + && _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), minError) + && _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), maxError) + && _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), minError) + && _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), maxError) + && _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3)), minError) + && _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3)), maxError)); +} + +////////////////////////////////// +// FLOATV +////////////////////////////////// + +QT3DS_FORCE_INLINE FloatV FZero() +{ + return FloatV_From_F32(0.0f); +} + +QT3DS_FORCE_INLINE FloatV FOne() +{ + return FloatV_From_F32(1.0f); +} + +QT3DS_FORCE_INLINE FloatV FHalf() +{ + return FloatV_From_F32(0.5f); +} + +QT3DS_FORCE_INLINE FloatV FEps() +{ + return FloatV_From_F32(QT3DS_ENV_REAL); +} + +QT3DS_FORCE_INLINE FloatV FEps6() +{ + return FloatV_From_F32(1e-6f); +} + +QT3DS_FORCE_INLINE FloatV FMax() +{ + return FloatV_From_F32(QT3DS_MAX_REAL); +} + +QT3DS_FORCE_INLINE FloatV FNegMax() +{ + return FloatV_From_F32(-QT3DS_MAX_REAL); +} + +QT3DS_FORCE_INLINE FloatV IZero() +{ + const QT3DSU32 zero = 0; + return _mm_load1_ps((QT3DSF32 *)&zero); +} + +QT3DS_FORCE_INLINE FloatV IOne() +{ + const QT3DSU32 one = 1; + return _mm_load1_ps((QT3DSF32 *)&one); +} + +QT3DS_FORCE_INLINE FloatV ITwo() +{ + const QT3DSU32 two = 2; + return _mm_load1_ps((QT3DSF32 *)&two); +} + +QT3DS_FORCE_INLINE FloatV IThree() +{ + const QT3DSU32 three = 3; + return _mm_load1_ps((QT3DSF32 *)&three); +} + +QT3DS_FORCE_INLINE FloatV IFour() +{ + QT3DSU32 four = 4; + return _mm_load1_ps((QT3DSF32 *)&four); +} + +QT3DS_FORCE_INLINE FloatV FNeg(const FloatV f) +{ + VECMATHAOS_ASSERT(isValidFloatV(f)); + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +QT3DS_FORCE_INLINE FloatV FAdd(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_add_ps(a, b); +} + +QT3DS_FORCE_INLINE FloatV FSub(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_sub_ps(a, b); +} + +QT3DS_FORCE_INLINE FloatV FMul(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_mul_ps(a, b); +} + +QT3DS_FORCE_INLINE FloatV FDiv(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_div_ps(a, b); +} + +QT3DS_FORCE_INLINE FloatV FDivFast(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +QT3DS_FORCE_INLINE FloatV FRecip(const FloatV a) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + return _mm_rcp_ps(a); +} + +QT3DS_FORCE_INLINE FloatV FRecipFast(const FloatV a) +{ + return FRecip(a); +} + +QT3DS_FORCE_INLINE FloatV FRsqrt(const FloatV a) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + return _mm_rsqrt_ps(a); +} + +QT3DS_FORCE_INLINE FloatV FSqrt(const FloatV a) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + return _mm_sqrt_ps(a); +} + +QT3DS_FORCE_INLINE FloatV FRsqrtFast(const FloatV a) +{ + return FRsqrt(a); +} + +QT3DS_FORCE_INLINE FloatV FScaleAdd(const FloatV a, const FloatV b, const FloatV c) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + VECMATHAOS_ASSERT(isValidFloatV(c)); + return FAdd(FMul(a, b), c); +} + +QT3DS_FORCE_INLINE FloatV FNegScaleSub(const FloatV a, const FloatV b, const FloatV c) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + VECMATHAOS_ASSERT(isValidFloatV(c)); + return FSub(c, FMul(a, b)); +} + +QT3DS_FORCE_INLINE FloatV FAbs(const FloatV a) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + QT3DS_ALIGN(16, const QT3DSU32) absMask[4] = { 0x7fFFffFF, 0x7fFFffFF, 0x7fFFffFF, 0x7fFFffFF }; + return _mm_and_ps(a, _mm_load_ps((QT3DSF32 *)absMask)); +} + +QT3DS_FORCE_INLINE FloatV FSel(const BoolV c, const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(_VecMathTests::allElementsEqualBoolV(c, BTTTT()) + || _VecMathTests::allElementsEqualBoolV(c, BFFFF())); + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +QT3DS_FORCE_INLINE BoolV FIsGrtr(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_cmpgt_ps(a, b); +} + +QT3DS_FORCE_INLINE BoolV FIsGrtrOrEq(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_cmpge_ps(a, b); +} + +QT3DS_FORCE_INLINE BoolV FIsEq(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_cmpeq_ps(a, b); +} + +QT3DS_FORCE_INLINE FloatV FMax(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_max_ps(a, b); +} + +QT3DS_FORCE_INLINE FloatV FMin(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_min_ps(a, b); +} + +QT3DS_FORCE_INLINE FloatV FClamp(const FloatV a, const FloatV minV, const FloatV maxV) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(minV)); + VECMATHAOS_ASSERT(isValidFloatV(maxV)); + return FMax(FMin(a, maxV), minV); +} + +QT3DS_FORCE_INLINE QT3DSU32 FAllGrtr(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return (_mm_comigt_ss(a, b)); +} + +QT3DS_FORCE_INLINE QT3DSU32 FAllGrtrOrEq(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + + return (_mm_comige_ss(a, b)); +} + +QT3DS_FORCE_INLINE QT3DSU32 FAllEq(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + + return (_mm_comieq_ss(a, b)); +} + +QT3DS_FORCE_INLINE FloatV FRound(const FloatV a) +{ + // return _mm_round_ps(a, 0x0); + const Vec3V half = Vec3V_From_F32(0.5f); + const Vec3V aPlusHalf = V3Add(a, half); + __m128i tmp = _mm_cvttps_epi32(aPlusHalf); + return _mm_cvtepi32_ps(tmp); +} + +QT3DS_FORCE_INLINE FloatV FSin(const FloatV a) +{ + // Vec4V V1, V2, V3, V5, V7, V9, V11, V13, V15, V17, V19, V21, V23; + // Vec4V S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11; + FloatV Result; + + // Modulo the range of the given angles such that -XM_PI <= Angles < XM_PI + const FloatV twoPi = Vec4V_From_F32Array_Aligned(g_PXReciprocalTwoPi.f); + const FloatV tmp = FMul(a, twoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegMulSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V3 = FMul(V2, V1); + const FloatV V5 = FMul(V3, V2); + const FloatV V7 = FMul(V5, V2); + const FloatV V9 = FMul(V7, V2); + const FloatV V11 = FMul(V9, V2); + const FloatV V13 = FMul(V11, V2); + const FloatV V15 = FMul(V13, V2); + const FloatV V17 = FMul(V15, V2); + const FloatV V19 = FMul(V17, V2); + const FloatV V21 = FMul(V19, V2); + const FloatV V23 = FMul(V21, V2); + + const Vec4V sinCoefficients0 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Result = FMulAdd(S1, V3, V1); + Result = FMulAdd(S2, V5, Result); + Result = FMulAdd(S3, V7, Result); + Result = FMulAdd(S4, V9, Result); + Result = FMulAdd(S5, V11, Result); + Result = FMulAdd(S6, V13, Result); + Result = FMulAdd(S7, V15, Result); + Result = FMulAdd(S8, V17, Result); + Result = FMulAdd(S9, V19, Result); + Result = FMulAdd(S10, V21, Result); + Result = FMulAdd(S11, V23, Result); + + return Result; +} + +QT3DS_FORCE_INLINE FloatV FCos(const FloatV a) +{ + // XMVECTOR V1, V2, V4, V6, V8, V10, V12, V14, V16, V18, V20, V22; + // XMVECTOR C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11; + FloatV Result; + + // Modulo the range of the given angles such that -XM_PI <= Angles < XM_PI + const FloatV twoPi = Vec4V_From_F32Array_Aligned(g_PXReciprocalTwoPi.f); + const FloatV tmp = FMul(a, twoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegMulSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V4 = FMul(V2, V2); + const FloatV V6 = FMul(V4, V2); + const FloatV V8 = FMul(V4, V4); + const FloatV V10 = FMul(V6, V4); + const FloatV V12 = FMul(V6, V6); + const FloatV V14 = FMul(V8, V6); + const FloatV V16 = FMul(V8, V8); + const FloatV V18 = FMul(V10, V8); + const FloatV V20 = FMul(V10, V10); + const FloatV V22 = FMul(V12, V10); + + const Vec4V cosCoefficients0 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Result = FMulAdd(C1, V2, V4One()); + Result = FMulAdd(C2, V4, Result); + Result = FMulAdd(C3, V6, Result); + Result = FMulAdd(C4, V8, Result); + Result = FMulAdd(C5, V10, Result); + Result = FMulAdd(C6, V12, Result); + Result = FMulAdd(C7, V14, Result); + Result = FMulAdd(C8, V16, Result); + Result = FMulAdd(C9, V18, Result); + Result = FMulAdd(C10, V20, Result); + Result = FMulAdd(C11, V22, Result); + + return Result; +} + +QT3DS_FORCE_INLINE QT3DSU32 FOutOfBounds(const FloatV a, const FloatV min, const FloatV max) +{ + const BoolV ffff = BFFFF(); + const BoolV c = BOr(FIsGrtr(a, max), FIsGrtr(min, a)); + return !BAllEq(c, ffff); +} + +QT3DS_FORCE_INLINE QT3DSU32 FInBounds(const FloatV a, const FloatV min, const FloatV max) +{ + const BoolV tttt = BTTTT(); + const BoolV c = BAnd(FIsGrtrOrEq(a, min), FIsGrtrOrEq(max, a)); + return BAllEq(c, tttt); +} + +QT3DS_FORCE_INLINE QT3DSU32 FOutOfBounds(const FloatV a, const FloatV bounds) +{ + return FOutOfBounds(a, FNeg(bounds), bounds); +} + +QT3DS_FORCE_INLINE QT3DSU32 FInBounds(const FloatV a, const FloatV bounds) +{ + return FInBounds(a, FNeg(bounds), bounds); +} + +////////////////////////////////// +// VEC3V +////////////////////////////////// + +QT3DS_FORCE_INLINE Vec3V V3Splat(const FloatV f) +{ + VECMATHAOS_ASSERT(isValidFloatV(f)); + const __m128 zero = FloatV_From_F32(0.0f); + const __m128 fff0 = _mm_move_ss(f, zero); + return _mm_shuffle_ps(fff0, fff0, _MM_SHUFFLE(0, 1, 2, 3)); +} + +QT3DS_FORCE_INLINE Vec3V V3Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z) +{ + VECMATHAOS_ASSERT(isValidFloatV(x)); + VECMATHAOS_ASSERT(isValidFloatV(y)); + VECMATHAOS_ASSERT(isValidFloatV(z)); + // static on zero causes compiler crash on x64 debug_opt + const __m128 zero = FloatV_From_F32(0.0f); + const __m128 xy = _mm_move_ss(x, y); + const __m128 z0 = _mm_move_ss(zero, z); + + return _mm_shuffle_ps(xy, z0, _MM_SHUFFLE(1, 0, 0, 1)); +} + +QT3DS_FORCE_INLINE Vec3V V3UnitX() +{ + const QT3DS_ALIGN(16, QT3DSF32) x[4] = { 1.0f, 0.0f, 0.0f, 0.0f }; + const __m128 x128 = _mm_load_ps(x); + return x128; +} + +QT3DS_FORCE_INLINE Vec3V V3UnitY() +{ + const QT3DS_ALIGN(16, QT3DSF32) y[4] = { 0.0f, 1.0f, 0.0f, 0.0f }; + const __m128 y128 = _mm_load_ps(y); + return y128; +} + +QT3DS_FORCE_INLINE Vec3V V3UnitZ() +{ + const QT3DS_ALIGN(16, QT3DSF32) z[4] = { 0.0f, 0.0f, 1.0f, 0.0f }; + const __m128 z128 = _mm_load_ps(z); + return z128; +} + +QT3DS_FORCE_INLINE FloatV V3GetX(const Vec3V f) +{ + VECMATHAOS_ASSERT(isValidVec3V(f)); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +QT3DS_FORCE_INLINE FloatV V3GetY(const Vec3V f) +{ + VECMATHAOS_ASSERT(isValidVec3V(f)); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +QT3DS_FORCE_INLINE FloatV V3GetZ(const Vec3V f) +{ + VECMATHAOS_ASSERT(isValidVec3V(f)); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +QT3DS_FORCE_INLINE Vec3V V3SetX(const Vec3V v, const FloatV f) +{ + VECMATHAOS_ASSERT(isValidVec3V(v)); + VECMATHAOS_ASSERT(isValidFloatV(f)); + return V3Sel(BFTTT(), v, f); +} + +QT3DS_FORCE_INLINE Vec3V V3SetY(const Vec3V v, const FloatV f) +{ + VECMATHAOS_ASSERT(isValidVec3V(v)); + VECMATHAOS_ASSERT(isValidFloatV(f)); + return V3Sel(BTFTT(), v, f); +} + +QT3DS_FORCE_INLINE Vec3V V3SetZ(const Vec3V v, const FloatV f) +{ + VECMATHAOS_ASSERT(isValidVec3V(v)); + VECMATHAOS_ASSERT(isValidFloatV(f)); + return V3Sel(BTTFT(), v, f); +} + +QT3DS_FORCE_INLINE Vec3V V3ColX(const Vec3V a, const Vec3V b, const Vec3V c) +{ + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 0, 3, 0)); + return V3SetY(r, V3GetX(b)); +} + +QT3DS_FORCE_INLINE Vec3V V3ColY(const Vec3V a, const Vec3V b, const Vec3V c) +{ + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 1, 3, 1)); + return V3SetY(r, V3GetY(b)); +} + +QT3DS_FORCE_INLINE Vec3V V3ColZ(const Vec3V a, const Vec3V b, const Vec3V c) +{ + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 2, 3, 2)); + return V3SetY(r, V3GetZ(b)); +} + +QT3DS_FORCE_INLINE Vec3V V3Zero() +{ + return Vec3V_From_F32(0.0f); +} + +QT3DS_FORCE_INLINE Vec3V V3Eps() +{ + return Vec3V_From_F32(QT3DS_ENV_REAL); +} +QT3DS_FORCE_INLINE Vec3V V3One() +{ + return Vec3V_From_F32(1.0f); +} + +QT3DS_FORCE_INLINE Vec3V V3Neg(const Vec3V f) +{ + VECMATHAOS_ASSERT(isValidVec3V(f)); + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +QT3DS_FORCE_INLINE Vec3V V3Add(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_add_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3Sub(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_sub_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3Scale(const Vec3V a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_mul_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3Mul(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_mul_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3ScaleInv(const Vec3V a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_div_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3Div(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + const __m128 one = Vec3V_From_F32(1.0f); + const __m128 tttf = BTTTF(); + const __m128 b1 = V3Sel(tttf, b, one); + return _mm_div_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3ScaleInvFast(const Vec3V a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +QT3DS_FORCE_INLINE Vec3V V3DivFast(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + const __m128 one = Vec3V_From_F32(1.0f); + const __m128 tttf = BTTTF(); + const __m128 b1 = V3Sel(tttf, b, one); + return _mm_mul_ps(a, _mm_rcp_ps(b1)); +} + +QT3DS_FORCE_INLINE Vec3V V3Recip(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + const __m128 zero = Vec3V_From_F32(0.0f); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_rcp_ps(a); + return V3Sel(tttf, recipA, zero); +} + +QT3DS_FORCE_INLINE Vec3V V3RecipFast(const Vec3V a) +{ + return V3Recip(a); +} + +QT3DS_FORCE_INLINE Vec3V V3Rsqrt(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + const __m128 zero = Vec3V_From_F32(0.0f); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_rsqrt_ps(a); + return V3Sel(tttf, recipA, zero); +} + +QT3DS_FORCE_INLINE Vec3V V3RsqrtFast(const Vec3V a) +{ + return V3Rsqrt(a); +} + +QT3DS_FORCE_INLINE Vec3V V3ScaleAdd(const Vec3V a, const FloatV b, const Vec3V c) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + VECMATHAOS_ASSERT(isValidVec3V(c)); + return V3Add(V3Scale(a, b), c); +} + +QT3DS_FORCE_INLINE Vec3V V3NegScaleSub(const Vec3V a, const FloatV b, const Vec3V c) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + VECMATHAOS_ASSERT(isValidVec3V(c)); + return V3Sub(c, V3Scale(a, b)); +} + +QT3DS_FORCE_INLINE Vec3V V3MulAdd(const Vec3V a, const Vec3V b, const Vec3V c) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + VECMATHAOS_ASSERT(isValidVec3V(c)); + return V3Add(V3Mul(a, b), c); +} + +QT3DS_FORCE_INLINE Vec3V V3NegMulSub(const Vec3V a, const Vec3V b, const Vec3V c) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + VECMATHAOS_ASSERT(isValidVec3V(c)); + return V3Sub(c, V3Scale(a, b)); +} + +QT3DS_FORCE_INLINE Vec3V V3Abs(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return V3Max( + a, V3Neg(a)); // sw/physx/shared/reviewed/3/feature/sdk/include/windows/Qt3DSWindowsInlineAoS.h +} + +QT3DS_FORCE_INLINE FloatV V3Dot(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + //__m128 dot1 = _mm_mul_ps(a, b); + ////w,z,y,x + //__m128 shuf1 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(2,1,0,3)); //z,y,x,w + //__m128 shuf2 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(1,0,3,2)); //y,x,w,z + //__m128 shuf3 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(0,3,2,1)); //x,w,z,y + // return _mm_add_ps(_mm_add_ps(shuf2, shuf3), _mm_add_ps(dot1,shuf1)); + + __m128 dot1 = _mm_mul_ps(a, b); + __m128 shuf1 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(0, 0, 0, 0)); // z,y,x,w + __m128 shuf2 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(1, 1, 1, 1)); // y,x,w,z + __m128 shuf3 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(2, 2, 2, 2)); // x,w,z,y + return _mm_add_ps(_mm_add_ps(shuf2, shuf3), shuf1); +} + +QT3DS_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + __m128 l1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + __m128 l2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + __m128 r1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + __m128 r2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + return _mm_sub_ps(_mm_mul_ps(l1, l2), _mm_mul_ps(r1, r2)); +} + +QT3DS_FORCE_INLINE FloatV V3Length(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return _mm_sqrt_ps(V3Dot(a, a)); +} + +QT3DS_FORCE_INLINE FloatV V3LengthSq(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return V3Dot(a, a); +} + +QT3DS_FORCE_INLINE Vec3V V3Normalize(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return V3Scale(a, _mm_rsqrt_ps(V3Dot(a, a))); +} + +QT3DS_FORCE_INLINE Vec3V V3NormalizeFast(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return V3Scale(a, _mm_rsqrt_ps(V3Dot(a, a))); +} + +QT3DS_FORCE_INLINE Vec3V V3NormalizeSafe(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + const __m128 zero = FloatV_From_F32(0.0f); + const __m128 length = V3Length(a); + const __m128 isGreaterThanZero = FIsGrtr(length, zero); + return V3Sel(isGreaterThanZero, V3ScaleInv(a, length), zero); +} + +QT3DS_FORCE_INLINE Vec3V V3Sel(const BoolV c, const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +QT3DS_FORCE_INLINE BoolV V3IsGrtr(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_cmpgt_ps(a, b); +} + +QT3DS_FORCE_INLINE BoolV V3IsGrtrOrEq(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_cmpge_ps(a, b); +} + +QT3DS_FORCE_INLINE BoolV V3IsEq(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_cmpeq_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3Max(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_max_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3Min(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_min_ps(a, b); +} + +// Extract the maximum value from a +QT3DS_FORCE_INLINE FloatV V3ExtractMax(const Vec3V a) +{ + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); + + return _mm_max_ps(_mm_max_ps(shuf1, shuf2), shuf3); +} + +// Extract the maximum value from a +QT3DS_FORCE_INLINE FloatV V3ExtractMin(const Vec3V a) +{ + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); + + return _mm_min_ps(_mm_min_ps(shuf1, shuf2), shuf3); +} + +// return (a >= 0.0f) ? 1.0f : -1.0f; +QT3DS_FORCE_INLINE Vec3V V3Sign(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + const __m128 zero = V3Zero(); + const __m128 one = V3One(); + const __m128 none = V3Neg(one); + return V3Sel(V3IsGrtrOrEq(a, zero), one, none); +} + +QT3DS_FORCE_INLINE Vec3V V3Clamp(const Vec3V a, const Vec3V minV, const Vec3V maxV) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(minV)); + VECMATHAOS_ASSERT(isValidVec3V(maxV)); + return V3Max(V3Min(a, maxV), minV); +} + +QT3DS_FORCE_INLINE QT3DSU32 V3AllGrtr(const Vec3V a, const Vec3V b) +{ + return BAllTrue3_R(V4IsGrtr(a, b)); +} + +QT3DS_FORCE_INLINE QT3DSU32 V3AllGrtrOrEq(const Vec3V a, const Vec3V b) +{ + return BAllTrue3_R(V4IsGrtrOrEq(a, b)); +} + +QT3DS_FORCE_INLINE QT3DSU32 V3AllEq(const Vec3V a, const Vec3V b) +{ + return BAllTrue3_R(V4IsEq(a, b)); +} + +QT3DS_FORCE_INLINE Vec3V V3Round(const Vec3V a) +{ + // return _mm_round_ps(a, 0x0); + const Vec3V half = Vec3V_From_F32(0.5f); + const Vec3V aPlusHalf = V3Add(a, half); + __m128i tmp = _mm_cvttps_epi32(aPlusHalf); + return _mm_cvtepi32_ps(tmp); +} + +QT3DS_FORCE_INLINE Vec3V V3Sin(const Vec3V a) +{ + // Vec4V V1, V2, V3, V5, V7, V9, V11, V13, V15, V17, V19, V21, V23; + // Vec4V S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11; + Vec3V Result; + + // Modulo the range of the given angles such that -XM_PI <= Angles < XM_PI + const Vec3V twoPi = Vec4V_From_F32Array_Aligned(g_PXReciprocalTwoPi.f); + const Vec3V tmp = V3Mul(a, twoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V3NegMulSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V3 = V3Mul(V2, V1); + const Vec3V V5 = V3Mul(V3, V2); + const Vec3V V7 = V3Mul(V5, V2); + const Vec3V V9 = V3Mul(V7, V2); + const Vec3V V11 = V3Mul(V9, V2); + const Vec3V V13 = V3Mul(V11, V2); + const Vec3V V15 = V3Mul(V13, V2); + const Vec3V V17 = V3Mul(V15, V2); + const Vec3V V19 = V3Mul(V17, V2); + const Vec3V V21 = V3Mul(V19, V2); + const Vec3V V23 = V3Mul(V21, V2); + + const Vec4V sinCoefficients0 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Result = V3MulAdd(S1, V3, V1); + Result = V3MulAdd(S2, V5, Result); + Result = V3MulAdd(S3, V7, Result); + Result = V3MulAdd(S4, V9, Result); + Result = V3MulAdd(S5, V11, Result); + Result = V3MulAdd(S6, V13, Result); + Result = V3MulAdd(S7, V15, Result); + Result = V3MulAdd(S8, V17, Result); + Result = V3MulAdd(S9, V19, Result); + Result = V3MulAdd(S10, V21, Result); + Result = V3MulAdd(S11, V23, Result); + + return Result; +} + +QT3DS_FORCE_INLINE Vec3V V3Cos(const Vec3V a) +{ + // XMVECTOR V1, V2, V4, V6, V8, V10, V12, V14, V16, V18, V20, V22; + // XMVECTOR C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11; + Vec3V Result; + + // Modulo the range of the given angles such that -XM_PI <= Angles < XM_PI + const Vec3V twoPi = Vec4V_From_F32Array_Aligned(g_PXReciprocalTwoPi.f); + const Vec3V tmp = V3Mul(a, twoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V3NegMulSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V4 = V3Mul(V2, V2); + const Vec3V V6 = V3Mul(V4, V2); + const Vec3V V8 = V3Mul(V4, V4); + const Vec3V V10 = V3Mul(V6, V4); + const Vec3V V12 = V3Mul(V6, V6); + const Vec3V V14 = V3Mul(V8, V6); + const Vec3V V16 = V3Mul(V8, V8); + const Vec3V V18 = V3Mul(V10, V8); + const Vec3V V20 = V3Mul(V10, V10); + const Vec3V V22 = V3Mul(V12, V10); + + const Vec4V cosCoefficients0 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Result = V3MulAdd(C1, V2, V4One()); + Result = V3MulAdd(C2, V4, Result); + Result = V3MulAdd(C3, V6, Result); + Result = V3MulAdd(C4, V8, Result); + Result = V3MulAdd(C5, V10, Result); + Result = V3MulAdd(C6, V12, Result); + Result = V3MulAdd(C7, V14, Result); + Result = V3MulAdd(C8, V16, Result); + Result = V3MulAdd(C9, V18, Result); + Result = V3MulAdd(C10, V20, Result); + Result = V3MulAdd(C11, V22, Result); + + return Result; +} + +QT3DS_FORCE_INLINE Vec3V V3PermYZZ(const Vec3V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 2, 2, 1)); +} + +QT3DS_FORCE_INLINE Vec3V V3PermXYX(const Vec3V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 1, 0)); +} + +QT3DS_FORCE_INLINE Vec3V V3PermYZX(const Vec3V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); +} + +QT3DS_FORCE_INLINE Vec3V V3PermZXY(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); +} + +QT3DS_FORCE_INLINE Vec3V V3PermZZY(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 2, 2)); +} + +QT3DS_FORCE_INLINE Vec3V V3PermYXX(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 0, 1)); +} + +QT3DS_FORCE_INLINE Vec3V V3Perm_Zero_1Z_0Y(const Vec3V v0, const Vec3V v1) +{ + return _mm_shuffle_ps(v1, v0, _MM_SHUFFLE(3, 1, 2, 3)); +} + +QT3DS_FORCE_INLINE Vec3V V3Perm_0Z_Zero_1X(const Vec3V v0, const Vec3V v1) +{ + return _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(3, 0, 3, 2)); +} + +QT3DS_FORCE_INLINE Vec3V V3Perm_1Y_0X_Zero(const Vec3V v0, const Vec3V v1) +{ + // There must be a better way to do this. + Vec3V v2 = V3Zero(); + FloatV y1 = V3GetY(v1); + FloatV x0 = V3GetX(v0); + v2 = V3SetX(v2, y1); + return V3SetY(v2, x0); +} + +QT3DS_FORCE_INLINE FloatV V3SumElems(const Vec3V a) +{ + __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); // z,y,x,w + __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); // y,x,w,z + __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); // x,w,z,y + return _mm_add_ps(_mm_add_ps(shuf1, shuf2), shuf3); +} + +QT3DS_FORCE_INLINE QT3DSU32 V3OutOfBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + const BoolV ffff = BFFFF(); + const BoolV c = BOr(V3IsGrtr(a, max), V3IsGrtr(min, a)); + return !BAllEq(c, ffff); +} + +QT3DS_FORCE_INLINE QT3DSU32 V3InBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + const BoolV tttt = BTTTT(); + const BoolV c = BAnd(V3IsGrtrOrEq(a, min), V3IsGrtrOrEq(max, a)); + return BAllEq(c, tttt); +} + +QT3DS_FORCE_INLINE QT3DSU32 V3OutOfBounds(const Vec3V a, const Vec3V bounds) +{ + return V3OutOfBounds(a, V3Neg(bounds), bounds); +} + +QT3DS_FORCE_INLINE QT3DSU32 V3InBounds(const Vec3V a, const Vec3V bounds) +{ + return V3InBounds(a, V3Neg(bounds), bounds); +} + +////////////////////////////////// +// VEC4V +////////////////////////////////// + +QT3DS_FORCE_INLINE Vec4V V4Splat(const FloatV f) +{ + VECMATHAOS_ASSERT(isValidFloatV(f)); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +QT3DS_FORCE_INLINE Vec4V V4Merge(const FloatV *const floatVArray) +{ + VECMATHAOS_ASSERT(isValidFloatV(floatVArray[0])); + VECMATHAOS_ASSERT(isValidFloatV(floatVArray[1])); + VECMATHAOS_ASSERT(isValidFloatV(floatVArray[2])); + VECMATHAOS_ASSERT(isValidFloatV(floatVArray[3])); + __m128 xw = _mm_move_ss(floatVArray[1], floatVArray[0]); // y, y, y, x + __m128 yz = _mm_move_ss(floatVArray[2], floatVArray[3]); // z, z, z, w + return (_mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0))); +} + +QT3DS_FORCE_INLINE Vec4V V4Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z, + const FloatVArg w) +{ + VECMATHAOS_ASSERT(isValidFloatV(x)); + VECMATHAOS_ASSERT(isValidFloatV(y)); + VECMATHAOS_ASSERT(isValidFloatV(z)); + VECMATHAOS_ASSERT(isValidFloatV(w)); + __m128 xw = _mm_move_ss(y, x); // y, y, y, x + __m128 yz = _mm_move_ss(z, w); // z, z, z, w + return (_mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0))); +} + +QT3DS_FORCE_INLINE Vec4V V4UnitW() +{ + const QT3DS_ALIGN(16, QT3DSF32) w[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; + const __m128 w128 = _mm_load_ps(w); + return w128; +} + +QT3DS_FORCE_INLINE Vec4V V4UnitX() +{ + const QT3DS_ALIGN(16, QT3DSF32) x[4] = { 1.0f, 0.0f, 0.0f, 0.0f }; + const __m128 x128 = _mm_load_ps(x); + return x128; +} + +QT3DS_FORCE_INLINE Vec4V V4UnitY() +{ + const QT3DS_ALIGN(16, QT3DSF32) y[4] = { 0.0f, 1.0f, 0.0f, 0.0f }; + const __m128 y128 = _mm_load_ps(y); + return y128; +} + +QT3DS_FORCE_INLINE Vec4V V4UnitZ() +{ + const QT3DS_ALIGN(16, QT3DSF32) z[4] = { 0.0f, 0.0f, 1.0f, 0.0f }; + const __m128 z128 = _mm_load_ps(z); + return z128; +} + +QT3DS_FORCE_INLINE FloatV V4GetW(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(3, 3, 3, 3)); +} + +QT3DS_FORCE_INLINE FloatV V4GetX(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +QT3DS_FORCE_INLINE FloatV V4GetY(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +QT3DS_FORCE_INLINE FloatV V4GetZ(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +QT3DS_FORCE_INLINE Vec4V V4SetW(const Vec4V v, const FloatV f) +{ + VECMATHAOS_ASSERT(isValidFloatV(f)); + return V4Sel(BTTTF(), v, f); +} + +QT3DS_FORCE_INLINE Vec4V V4SetX(const Vec4V v, const FloatV f) +{ + VECMATHAOS_ASSERT(isValidFloatV(f)); + return V4Sel(BFTTT(), v, f); +} + +QT3DS_FORCE_INLINE Vec4V V4SetY(const Vec4V v, const FloatV f) +{ + VECMATHAOS_ASSERT(isValidFloatV(f)); + return V4Sel(BTFTT(), v, f); +} + +QT3DS_FORCE_INLINE Vec4V V4SetZ(const Vec4V v, const FloatV f) +{ + VECMATHAOS_ASSERT(isValidVec3V(v)); + VECMATHAOS_ASSERT(isValidFloatV(f)); + return V4Sel(BTTFT(), v, f); +} + +QT3DS_FORCE_INLINE Vec4V V4Zero() +{ + return Vec4V_From_F32(0.0f); +} + +QT3DS_FORCE_INLINE Vec4V V4One() +{ + return Vec4V_From_F32(1.0f); +} + +QT3DS_FORCE_INLINE Vec3V V4Eps() +{ + return Vec3V_From_F32(QT3DS_ENV_REAL); +} + +QT3DS_FORCE_INLINE Vec4V V4Neg(const Vec4V f) +{ + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +QT3DS_FORCE_INLINE Vec4V V4Add(const Vec4V a, const Vec4V b) +{ + return _mm_add_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec4V V4Sub(const Vec4V a, const Vec4V b) +{ + return _mm_sub_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec4V V4Scale(const Vec4V a, const FloatV b) +{ + return _mm_mul_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec4V V4Mul(const Vec4V a, const Vec4V b) +{ + return _mm_mul_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec4V V4ScaleInv(const Vec4V a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_div_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec4V V4Div(const Vec4V a, const Vec4V b) +{ + return _mm_div_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec4V V4ScaleInvFast(const Vec4V a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +QT3DS_FORCE_INLINE Vec4V V4DivFast(const Vec4V a, const Vec4V b) +{ + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +QT3DS_FORCE_INLINE Vec4V V4Recip(const Vec4V a) +{ + return _mm_rcp_ps(a); +} + +QT3DS_FORCE_INLINE Vec4V V4RecipFast(const Vec4V a) +{ + return V4Recip(a); +} + +QT3DS_FORCE_INLINE Vec4V V4Rsqrt(const Vec4V a) +{ + return _mm_rsqrt_ps(a); +} + +QT3DS_FORCE_INLINE Vec4V V4RsqrtFast(const Vec4V a) +{ + return V4Rsqrt(a); +} + +QT3DS_FORCE_INLINE Vec4V V4ScaleAdd(const Vec4V a, const FloatV b, const Vec4V c) +{ + VECMATHAOS_ASSERT(isValidFloatV(b)); + return V4Add(V4Scale(a, b), c); +} + +QT3DS_FORCE_INLINE Vec4V V4NegScaleSub(const Vec4V a, const FloatV b, const Vec4V c) +{ + VECMATHAOS_ASSERT(isValidFloatV(b)); + return V4Sub(c, V4Scale(a, b)); +} + +QT3DS_FORCE_INLINE Vec4V V4MulAdd(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Add(V4Scale(a, b), c); +} + +QT3DS_FORCE_INLINE Vec4V V4NegMulSub(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Sub(c, V4Scale(a, b)); +} + +QT3DS_FORCE_INLINE Vec4V V4Abs(const Vec4V a) +{ + return V4Max(a, V3Neg(a)); +} + +QT3DS_FORCE_INLINE FloatV V4Dot(const Vec4V a, const Vec4V b) +{ + __m128 dot1 = _mm_mul_ps(a, b); // x,y,z,w + __m128 shuf1 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(2, 1, 0, 3)); // w,x,y,z + __m128 shuf2 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(1, 0, 3, 2)); // z,w,x,y + __m128 shuf3 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(0, 3, 2, 1)); // y,z,w,x + return _mm_add_ps(_mm_add_ps(shuf2, shuf3), _mm_add_ps(dot1, shuf1)); +} + +QT3DS_FORCE_INLINE FloatV V4Length(const Vec4V a) +{ + return _mm_sqrt_ps(V4Dot(a, a)); +} + +QT3DS_FORCE_INLINE FloatV V4LengthSq(const Vec4V a) +{ + return V4Dot(a, a); +} + +QT3DS_FORCE_INLINE Vec4V V4Normalize(const Vec4V a) +{ + return V4ScaleInv(a, _mm_sqrt_ps(V4Dot(a, a))); +} + +QT3DS_FORCE_INLINE Vec4V V4NormalizeFast(const Vec4V a) +{ + return V4ScaleInvFast(a, _mm_sqrt_ps(V4Dot(a, a))); +} + +QT3DS_FORCE_INLINE Vec4V V4NormalizeSafe(const Vec4V a) +{ + const __m128 zero = FloatV_From_F32(0.0f); + const __m128 length = V4Length(a); + const __m128 isGreaterThanZero = FIsGrtr(length, zero); + return V4Sel(isGreaterThanZero, V4ScaleInv(a, length), zero); +} + +QT3DS_FORCE_INLINE BoolV V4IsEqU32(const VecU32V a, const VecU32V b) +{ + return _mm_cmpeq_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec4V V4Sel(const BoolV c, const Vec4V a, const Vec4V b) +{ + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +QT3DS_FORCE_INLINE BoolV V4IsGrtr(const Vec4V a, const Vec4V b) +{ + return _mm_cmpgt_ps(a, b); +} + +QT3DS_FORCE_INLINE BoolV V4IsGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return _mm_cmpge_ps(a, b); +} + +QT3DS_FORCE_INLINE BoolV V4IsEq(const Vec4V a, const Vec4V b) +{ + return _mm_cmpeq_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V4Max(const Vec4V a, const Vec4V b) +{ + return _mm_max_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec4V V4Min(const Vec4V a, const Vec4V b) +{ + return _mm_min_ps(a, b); +} + +QT3DS_FORCE_INLINE FloatV V4ExtractMax(const Vec4V a) +{ + __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 1, 0, 3)); + __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2)); + __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 3, 2, 1)); + + return _mm_max_ps(_mm_max_ps(a, shuf1), _mm_max_ps(shuf2, shuf3)); +} + +QT3DS_FORCE_INLINE Vec4V V4Clamp(const Vec4V a, const Vec4V minV, const Vec4V maxV) +{ + return V4Max(V4Min(a, maxV), minV); +} + +QT3DS_FORCE_INLINE QT3DSU32 V4AllGrtr(const Vec4V a, const Vec4V b) +{ + return BAllTrue4_R(V4IsGrtr(a, b)); +} + +QT3DS_FORCE_INLINE QT3DSU32 V4AllGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return BAllTrue4_R(V4IsGrtrOrEq(a, b)); +} + +QT3DS_FORCE_INLINE QT3DSU32 V4AllEq(const Vec4V a, const Vec4V b) +{ + return BAllTrue4_R(V4IsEq(a, b)); +} + +QT3DS_FORCE_INLINE Vec4V V4Round(const Vec4V a) +{ + // return _mm_round_ps(a, 0x0); + const Vec3V half = Vec3V_From_F32(0.5f); + const Vec3V aPlusHalf = V3Add(a, half); + __m128i tmp = _mm_cvttps_epi32(aPlusHalf); + return _mm_cvtepi32_ps(tmp); +} + +QT3DS_FORCE_INLINE Vec4V V4Sin(const Vec4V a) +{ + // Vec4V V1, V2, V3, V5, V7, V9, V11, V13, V15, V17, V19, V21, V23; + // Vec4V S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11; + Vec4V Result; + + const Vec4V twoPi = Vec4V_From_F32Array_Aligned(g_PXReciprocalTwoPi.f); + const Vec4V tmp = V4Mul(a, twoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V3 = V4Mul(V2, V1); + const Vec4V V5 = V4Mul(V3, V2); + const Vec4V V7 = V4Mul(V5, V2); + const Vec4V V9 = V4Mul(V7, V2); + const Vec4V V11 = V4Mul(V9, V2); + const Vec4V V13 = V4Mul(V11, V2); + const Vec4V V15 = V4Mul(V13, V2); + const Vec4V V17 = V4Mul(V15, V2); + const Vec4V V19 = V4Mul(V17, V2); + const Vec4V V21 = V4Mul(V19, V2); + const Vec4V V23 = V4Mul(V21, V2); + + const Vec4V sinCoefficients0 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Result = V4MulAdd(S1, V3, V1); + Result = V4MulAdd(S2, V5, Result); + Result = V4MulAdd(S3, V7, Result); + Result = V4MulAdd(S4, V9, Result); + Result = V4MulAdd(S5, V11, Result); + Result = V4MulAdd(S6, V13, Result); + Result = V4MulAdd(S7, V15, Result); + Result = V4MulAdd(S8, V17, Result); + Result = V4MulAdd(S9, V19, Result); + Result = V4MulAdd(S10, V21, Result); + Result = V4MulAdd(S11, V23, Result); + + return Result; +} + +QT3DS_FORCE_INLINE Vec4V V4Cos(const Vec4V a) +{ + // XMVECTOR V1, V2, V4, V6, V8, V10, V12, V14, V16, V18, V20, V22; + // XMVECTOR C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11; + Vec4V Result; + + const Vec4V twoPi = Vec4V_From_F32Array_Aligned(g_PXReciprocalTwoPi.f); + const Vec4V tmp = V4Mul(a, twoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V4 = V4Mul(V2, V2); + const Vec4V V6 = V4Mul(V4, V2); + const Vec4V V8 = V4Mul(V4, V4); + const Vec4V V10 = V4Mul(V6, V4); + const Vec4V V12 = V4Mul(V6, V6); + const Vec4V V14 = V4Mul(V8, V6); + const Vec4V V16 = V4Mul(V8, V8); + const Vec4V V18 = V4Mul(V10, V8); + const Vec4V V20 = V4Mul(V10, V10); + const Vec4V V22 = V4Mul(V12, V10); + + const Vec4V cosCoefficients0 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Result = V4MulAdd(C1, V2, V4One()); + Result = V4MulAdd(C2, V4, Result); + Result = V4MulAdd(C3, V6, Result); + Result = V4MulAdd(C4, V8, Result); + Result = V4MulAdd(C5, V10, Result); + Result = V4MulAdd(C6, V12, Result); + Result = V4MulAdd(C7, V14, Result); + Result = V4MulAdd(C8, V16, Result); + Result = V4MulAdd(C9, V18, Result); + Result = V4MulAdd(C10, V20, Result); + Result = V4MulAdd(C11, V22, Result); + + return Result; +} + +////////////////////////////////// +// VEC4V +////////////////////////////////// + +QT3DS_FORCE_INLINE BoolV BFFFF() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0, 0, 0, 0 }; + const __m128 ffff = _mm_load_ps((float *)&f); + return ffff; +} + +QT3DS_FORCE_INLINE BoolV BFFFT() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0, 0, 0, 0xFFFFFFFF }; + const __m128 ffft = _mm_load_ps((float *)&f); + return ffft; +} + +QT3DS_FORCE_INLINE BoolV BFFTF() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0, 0, 0xFFFFFFFF, 0 }; + const __m128 fftf = _mm_load_ps((float *)&f); + return fftf; +} + +QT3DS_FORCE_INLINE BoolV BFFTT() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0, 0, 0xFFFFFFFF, 0xFFFFFFFF }; + const __m128 fftt = _mm_load_ps((float *)&f); + return fftt; +} + +QT3DS_FORCE_INLINE BoolV BFTFF() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0, 0xFFFFFFFF, 0, 0 }; + const __m128 ftff = _mm_load_ps((float *)&f); + return ftff; +} + +QT3DS_FORCE_INLINE BoolV BFTFT() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0, 0xFFFFFFFF, 0, 0xFFFFFFFF }; + const __m128 ftft = _mm_load_ps((float *)&f); + return ftft; +} + +QT3DS_FORCE_INLINE BoolV BFTTF() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0, 0xFFFFFFFF, 0xFFFFFFFF, 0 }; + const __m128 fttf = _mm_load_ps((float *)&f); + return fttf; +} + +QT3DS_FORCE_INLINE BoolV BFTTT() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + const __m128 fttt = _mm_load_ps((float *)&f); + return fttt; +} + +QT3DS_FORCE_INLINE BoolV BTFFF() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0xFFFFFFFF, 0, 0, 0 }; + const __m128 tfff = _mm_load_ps((float *)&f); + return tfff; +} + +QT3DS_FORCE_INLINE BoolV BTFFT() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0xFFFFFFFF, 0, 0, 0xFFFFFFFF }; + const __m128 tfft = _mm_load_ps((float *)&f); + return tfft; +} + +QT3DS_FORCE_INLINE BoolV BTFTF() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0xFFFFFFFF, 0, 0xFFFFFFFF, 0 }; + const __m128 tftf = _mm_load_ps((float *)&f); + return tftf; +} + +QT3DS_FORCE_INLINE BoolV BTFTT() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0xFFFFFFFF, 0, 0xFFFFFFFF, 0xFFFFFFFF }; + const __m128 tftt = _mm_load_ps((float *)&f); + return tftt; +} + +QT3DS_FORCE_INLINE BoolV BTTFF() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0, 0 }; + const __m128 ttff = _mm_load_ps((float *)&f); + return ttff; +} + +QT3DS_FORCE_INLINE BoolV BTTFT() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0, 0xFFFFFFFF }; + const __m128 ttft = _mm_load_ps((float *)&f); + return ttft; +} + +QT3DS_FORCE_INLINE BoolV BTTTF() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0 }; + const __m128 tttf = _mm_load_ps((float *)&f); + return tttf; +} + +QT3DS_FORCE_INLINE BoolV BTTTT() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + const __m128 tttt = _mm_load_ps((float *)&f); + return tttt; +} + +QT3DS_FORCE_INLINE BoolV BXMask() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0xFFFFFFFF, 0, 0, 0 }; + const __m128 tfff = _mm_load_ps((float *)&f); + return tfff; +} + +QT3DS_FORCE_INLINE BoolV BYMask() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0, 0xFFFFFFFF, 0, 0 }; + const __m128 ftff = _mm_load_ps((float *)&f); + return ftff; +} + +QT3DS_FORCE_INLINE BoolV BZMask() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0, 0, 0xFFFFFFFF, 0 }; + const __m128 fftf = _mm_load_ps((float *)&f); + return fftf; +} + +QT3DS_FORCE_INLINE BoolV BWMask() +{ + const QT3DS_ALIGN(16, QT3DSU32) f[4] = { 0, 0, 0, 0xFFFFFFFF }; + const __m128 ffft = _mm_load_ps((float *)&f); + return ffft; +} + +QT3DS_FORCE_INLINE BoolV BGetX(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +QT3DS_FORCE_INLINE BoolV BGetY(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +QT3DS_FORCE_INLINE BoolV BGetZ(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +QT3DS_FORCE_INLINE BoolV BGetW(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(3, 3, 3, 3)); +} + +QT3DS_FORCE_INLINE BoolV BAnd(const BoolV a, const BoolV b) +{ + return (_mm_and_ps(a, b)); +} + +QT3DS_FORCE_INLINE BoolV BNot(const BoolV a) +{ + const BoolV bAllTrue(BTTTT()); + return _mm_xor_ps(a, bAllTrue); +} + +QT3DS_FORCE_INLINE BoolV BAndNot(const BoolV a, const BoolV b) +{ + return (_mm_andnot_ps(a, b)); +} + +QT3DS_FORCE_INLINE BoolV BOr(const BoolV a, const BoolV b) +{ + return (_mm_or_ps(a, b)); +} + +QT3DS_FORCE_INLINE BoolV BAllTrue4(const BoolV a) +{ + const BoolV bTmp = _mm_and_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), + _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 2, 3))); + return _mm_and_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +QT3DS_FORCE_INLINE BoolV BAnyTrue4(const BoolV a) +{ + const BoolV bTmp = _mm_or_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), + _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 2, 3))); + return _mm_or_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +QT3DS_FORCE_INLINE BoolV BAllTrue3(const BoolV a) +{ + const BoolV bTmp = _mm_and_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), + _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + return _mm_and_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +QT3DS_FORCE_INLINE BoolV BAnyTrue3(const BoolV a) +{ + const BoolV bTmp = _mm_or_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), + _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + return _mm_or_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +QT3DS_FORCE_INLINE QT3DSU32 BAllEq(const BoolV a, const BoolV b) +{ + const BoolV bTest = m128_I2F(_mm_cmpeq_epi32(m128_F2I(a), m128_F2I(b))); + return BAllTrue4_R(bTest); +} + +////////////////////////////////// +// MAT33V +////////////////////////////////// + +QT3DS_FORCE_INLINE Vec3V M33MulV3(const Mat33V &a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +QT3DS_FORCE_INLINE Vec3V M33TrnspsMulV3(const Mat33V &a, const Vec3V b) +{ + const FloatV x = V3Dot(a.col0, b); + const FloatV y = V3Dot(a.col1, b); + const FloatV z = V3Dot(a.col2, b); + return V3Merge(x, y, z); +} + +QT3DS_FORCE_INLINE Vec3V M33MulV3AddV3(const Mat33V &A, const Vec3V b, const Vec3V c) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + Vec3V result = V3MulAdd(A.col0, x, c); + result = V3MulAdd(A.col1, y, result); + return V3MulAdd(A.col2, z, result); +} + +QT3DS_FORCE_INLINE Mat33V M33MulM33(const Mat33V &a, const Mat33V &b) +{ + return Mat33V(M33MulV3(a, b.col0), M33MulV3(a, b.col1), M33MulV3(a, b.col2)); +} + +QT3DS_FORCE_INLINE Mat33V M33Add(const Mat33V &a, const Mat33V &b) +{ + return Mat33V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2)); +} + +QT3DS_FORCE_INLINE Mat33V M33Inverse(const Mat33V &a) +{ + const BoolV tfft = BTFFT(); + const BoolV tttf = BTTTF(); + const FloatV zero = FloatV_From_F32(0.0f); + const Vec3V cross01 = V3Cross(a.col0, a.col1); + const Vec3V cross12 = V3Cross(a.col1, a.col2); + const Vec3V cross20 = V3Cross(a.col2, a.col0); + const FloatV dot = V3Dot(cross01, a.col2); + const FloatV invDet = _mm_rcp_ps(dot); + const Vec3V mergeh = _mm_unpacklo_ps(cross12, cross01); + const Vec3V mergel = _mm_unpackhi_ps(cross12, cross01); + Vec3V colInv0 = _mm_unpacklo_ps(mergeh, cross20); + colInv0 = _mm_or_ps(_mm_andnot_ps(tttf, zero), _mm_and_ps(tttf, colInv0)); + const Vec3V zppd = _mm_shuffle_ps(mergeh, cross20, _MM_SHUFFLE(3, 0, 0, 2)); + const Vec3V pbwp = _mm_shuffle_ps(cross20, mergeh, _MM_SHUFFLE(3, 3, 1, 0)); + const Vec3V colInv1 = _mm_or_ps(_mm_andnot_ps(BTFFT(), pbwp), _mm_and_ps(BTFFT(), zppd)); + const Vec3V xppd = _mm_shuffle_ps(mergel, cross20, _MM_SHUFFLE(3, 0, 0, 0)); + const Vec3V pcyp = _mm_shuffle_ps(cross20, mergel, _MM_SHUFFLE(3, 1, 2, 0)); + const Vec3V colInv2 = _mm_or_ps(_mm_andnot_ps(tfft, pcyp), _mm_and_ps(tfft, xppd)); + + return Mat33V(_mm_mul_ps(colInv0, invDet), _mm_mul_ps(colInv1, invDet), + _mm_mul_ps(colInv2, invDet)); +} + +QT3DS_FORCE_INLINE Mat33V M33Trnsps(const Mat33V &a) +{ + return Mat33V(V3Merge(V3GetX(a.col0), V3GetX(a.col1), V3GetX(a.col2)), + V3Merge(V3GetY(a.col0), V3GetY(a.col1), V3GetY(a.col2)), + V3Merge(V3GetZ(a.col0), V3GetZ(a.col1), V3GetZ(a.col2))); +} + +QT3DS_FORCE_INLINE Mat33V M33Identity() +{ + return Mat33V(V3UnitX(), V3UnitY(), V3UnitZ()); +} + +QT3DS_FORCE_INLINE Mat33V M33Sub(const Mat33V &a, const Mat33V &b) +{ + return Mat33V(V3Sub(a.col0, b.col0), V3Sub(a.col1, b.col1), V3Sub(a.col2, b.col2)); +} + +QT3DS_FORCE_INLINE Mat33V M33Neg(const Mat33V &a) +{ + return Mat33V(V3Neg(a.col0), V3Neg(a.col1), V3Neg(a.col2)); +} + +QT3DS_FORCE_INLINE Mat33V M33Abs(const Mat33V &a) +{ + return Mat33V(V3Abs(a.col0), V3Abs(a.col1), V3Abs(a.col2)); +} + +QT3DS_FORCE_INLINE Mat33V PromoteVec3V(const Vec3V v) +{ + const BoolV bTFFF = BTFFF(); + const BoolV bFTFF = BFTFF(); + const BoolV bFFTF = BTFTF(); + + const Vec3V zero = V3Zero(); + + return Mat33V(V3Sel(bTFFF, v, zero), V3Sel(bFTFF, v, zero), V3Sel(bFFTF, v, zero)); +} + +QT3DS_FORCE_INLINE Mat33V M33Diagonal(const Vec3VArg d) +{ + const FloatV x = V3Mul(V3UnitX(), d); + const FloatV y = V3Mul(V3UnitY(), d); + const FloatV z = V3Mul(V3UnitZ(), d); + return Mat33V(x, y, z); +} + +////////////////////////////////// +// MAT34V +////////////////////////////////// + +QT3DS_FORCE_INLINE Vec3V M34MulV3(const Mat34V &a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + const Vec3V v0PlusV1Plusv2 = V3Add(v0PlusV1, v2); + return (V3Add(v0PlusV1Plusv2, a.col3)); +} + +QT3DS_FORCE_INLINE Vec3V M34Mul33V3(const Mat34V &a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +QT3DS_FORCE_INLINE Vec3V M34TrnspsMul33V3(const Mat34V &a, const Vec3V b) +{ + const FloatV x = V3Dot(a.col0, b); + const FloatV y = V3Dot(a.col1, b); + const FloatV z = V3Dot(a.col2, b); + return V3Merge(x, y, z); +} + +QT3DS_FORCE_INLINE Mat34V M34MulM34(const Mat34V &a, const Mat34V &b) +{ + return Mat34V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2), + M34MulV3(a, b.col3)); +} + +QT3DS_FORCE_INLINE Mat33V M34MulM33(const Mat34V &a, const Mat33V &b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +QT3DS_FORCE_INLINE Mat33V M34Mul33MM34(const Mat34V &a, const Mat34V &b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +QT3DS_FORCE_INLINE Mat34V M34Add(const Mat34V &a, const Mat34V &b) +{ + return Mat34V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2), + V3Add(a.col3, b.col3)); +} + +QT3DS_FORCE_INLINE Mat34V M34InverseV(const Mat34V &a) +{ + Mat34V aInv; + const BoolV tfft = BTFFT(); + const BoolV tttf = BTTTF(); + const FloatV zero = FloatV_From_F32(0.0f); + const Vec3V cross01 = V3Cross(a.col0, a.col1); + const Vec3V cross12 = V3Cross(a.col1, a.col2); + const Vec3V cross20 = V3Cross(a.col2, a.col0); + const FloatV dot = V3Dot(cross01, a.col2); + const FloatV invDet = _mm_rcp_ps(dot); + const Vec3V mergeh = _mm_unpacklo_ps(cross12, cross01); + const Vec3V mergel = _mm_unpackhi_ps(cross12, cross01); + Vec3V colInv0 = _mm_unpacklo_ps(mergeh, cross20); + colInv0 = _mm_or_ps(_mm_andnot_ps(tttf, zero), _mm_and_ps(tttf, colInv0)); + const Vec3V zppd = _mm_shuffle_ps(mergeh, cross20, _MM_SHUFFLE(3, 0, 0, 2)); + const Vec3V pbwp = _mm_shuffle_ps(cross20, mergeh, _MM_SHUFFLE(3, 3, 1, 0)); + const Vec3V colInv1 = _mm_or_ps(_mm_andnot_ps(BTFFT(), pbwp), _mm_and_ps(BTFFT(), zppd)); + const Vec3V xppd = _mm_shuffle_ps(mergel, cross20, _MM_SHUFFLE(3, 0, 0, 0)); + const Vec3V pcyp = _mm_shuffle_ps(cross20, mergel, _MM_SHUFFLE(3, 1, 2, 0)); + const Vec3V colInv2 = _mm_or_ps(_mm_andnot_ps(tfft, pcyp), _mm_and_ps(tfft, xppd)); + aInv.col0 = _mm_mul_ps(colInv0, invDet); + aInv.col1 = _mm_mul_ps(colInv1, invDet); + aInv.col2 = _mm_mul_ps(colInv2, invDet); + aInv.col3 = M34Mul33V3(aInv, V3Neg(a.col3)); + return aInv; +} + +QT3DS_FORCE_INLINE Mat33V M34Trnsps33(const Mat34V &a) +{ + return Mat33V(V3Merge(V3GetX(a.col0), V3GetX(a.col1), V3GetX(a.col2)), + V3Merge(V3GetY(a.col0), V3GetY(a.col1), V3GetY(a.col2)), + V3Merge(V3GetZ(a.col0), V3GetZ(a.col1), V3GetZ(a.col2))); +} + +////////////////////////////////// +// MAT44V +////////////////////////////////// + +QT3DS_FORCE_INLINE Vec4V M44MulV4(const Mat44V &a, const Vec4V b) +{ + const FloatV x = V4GetX(b); + const FloatV y = V4GetY(b); + const FloatV z = V4GetZ(b); + const FloatV w = V4GetW(b); + + const Vec4V v0 = V4Scale(a.col0, x); + const Vec4V v1 = V4Scale(a.col1, y); + const Vec4V v2 = V4Scale(a.col2, z); + const Vec4V v3 = V4Scale(a.col3, w); + const Vec4V v0PlusV1 = V4Add(v0, v1); + const Vec4V v0PlusV1Plusv2 = V4Add(v0PlusV1, v2); + return (V4Add(v0PlusV1Plusv2, v3)); +} + +QT3DS_FORCE_INLINE Vec4V M44TrnspsMulV4(const Mat44V &a, const Vec4V b) +{ + QT3DS_ALIGN(16, FloatV) + dotProdArray[4] = { V4Dot(a.col0, b), V4Dot(a.col1, b), V4Dot(a.col2, b), V4Dot(a.col3, b) }; + return V4Merge(dotProdArray); +} + +QT3DS_FORCE_INLINE Mat44V M44MulM44(const Mat44V &a, const Mat44V &b) +{ + return Mat44V(M44MulV4(a, b.col0), M44MulV4(a, b.col1), M44MulV4(a, b.col2), + M44MulV4(a, b.col3)); +} + +QT3DS_FORCE_INLINE Mat44V M44Add(const Mat44V &a, const Mat44V &b) +{ + return Mat44V(V4Add(a.col0, b.col0), V4Add(a.col1, b.col1), V4Add(a.col2, b.col2), + V4Add(a.col3, b.col3)); +} + +QT3DS_FORCE_INLINE Mat44V M44Trnsps(const Mat44V &a) +{ + const Vec4V v0 = _mm_unpacklo_ps(a.col0, a.col2); + const Vec4V v1 = _mm_unpackhi_ps(a.col0, a.col2); + const Vec4V v2 = _mm_unpacklo_ps(a.col1, a.col3); + const Vec4V v3 = _mm_unpackhi_ps(a.col1, a.col3); + return Mat44V(_mm_unpacklo_ps(v0, v2), _mm_unpackhi_ps(v0, v2), _mm_unpacklo_ps(v1, v3), + _mm_unpackhi_ps(v1, v3)); +} + +QT3DS_FORCE_INLINE Mat44V M44Inverse(const Mat44V &a) +{ + __m128 minor0, minor1, minor2, minor3; + __m128 row0, row1, row2, row3; + __m128 det, tmp1; + + tmp1 = FloatV_From_F32(0.0f); + row1 = FloatV_From_F32(0.0f); + row3 = FloatV_From_F32(0.0f); + + row0 = a.col0; + row1 = _mm_shuffle_ps(a.col1, a.col1, _MM_SHUFFLE(1, 0, 3, 2)); + row2 = a.col2; + row3 = _mm_shuffle_ps(a.col3, a.col3, _MM_SHUFFLE(1, 0, 3, 2)); + + tmp1 = _mm_mul_ps(row2, row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor0 = _mm_mul_ps(row1, tmp1); + minor1 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(_mm_mul_ps(row1, tmp1), minor0); + minor1 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor1); + minor1 = _mm_shuffle_ps(minor1, minor1, 0x4E); + + tmp1 = _mm_mul_ps(row1, row2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor0 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor0); + minor3 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row3, tmp1)); + minor3 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor3); + minor3 = _mm_shuffle_ps(minor3, minor3, 0x4E); + + tmp1 = _mm_mul_ps(_mm_shuffle_ps(row1, row1, 0x4E), row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + row2 = _mm_shuffle_ps(row2, row2, 0x4E); + minor0 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor0); + minor2 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row2, tmp1)); + minor2 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor2); + minor2 = _mm_shuffle_ps(minor2, minor2, 0x4E); + + tmp1 = _mm_mul_ps(row0, row1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor2 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor2); + minor3 = _mm_sub_ps(_mm_mul_ps(row2, tmp1), minor3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor2 = _mm_sub_ps(_mm_mul_ps(row3, tmp1), minor2); + minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row2, tmp1)); + + tmp1 = _mm_mul_ps(row0, row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row2, tmp1)); + minor2 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor1 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor1); + minor2 = _mm_sub_ps(minor2, _mm_mul_ps(row1, tmp1)); + + tmp1 = _mm_mul_ps(row0, row2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor1 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor1); + minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row1, tmp1)); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row3, tmp1)); + minor3 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor3); + + det = _mm_mul_ps(row0, minor0); + det = _mm_add_ps(_mm_shuffle_ps(det, det, 0x4E), det); + det = _mm_add_ss(_mm_shuffle_ps(det, det, 0xB1), det); + tmp1 = _mm_rcp_ss(det); +#if 0 + det = _mm_sub_ss(_mm_add_ss(tmp1, tmp1), _mm_mul_ss(det, _mm_mul_ss(tmp1, tmp1))); + det = _mm_shuffle_ps(det, det, 0x00); +#else + det = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(0, 0, 0, 0)); +#endif + + minor0 = _mm_mul_ps(det, minor0); + minor1 = _mm_mul_ps(det, minor1); + minor2 = _mm_mul_ps(det, minor2); + minor3 = _mm_mul_ps(det, minor3); + Mat44V invTrans(minor0, minor1, minor2, minor3); + return M44Trnsps(invTrans); +} + +QT3DS_FORCE_INLINE Vec4V Vec4V_From_XYZW(QT3DSF32 x, QT3DSF32 y, QT3DSF32 z, QT3DSF32 w) +{ + return _mm_set_ps(w, z, y, x); +} + +// AP: work in progress - use proper SSE intrinsics where possible +QT3DS_FORCE_INLINE VecU16V V4U32PK(VecU32V a, VecU32V b) +{ + VecU16V result; + result.m128_u16[0] = QT3DSU16(NVClamp<QT3DSU32>((a).m128_u32[0], 0, 0xFFFF)); + result.m128_u16[1] = QT3DSU16(NVClamp<QT3DSU32>((a).m128_u32[1], 0, 0xFFFF)); + result.m128_u16[2] = QT3DSU16(NVClamp<QT3DSU32>((a).m128_u32[2], 0, 0xFFFF)); + result.m128_u16[3] = QT3DSU16(NVClamp<QT3DSU32>((a).m128_u32[3], 0, 0xFFFF)); + result.m128_u16[4] = QT3DSU16(NVClamp<QT3DSU32>((b).m128_u32[0], 0, 0xFFFF)); + result.m128_u16[5] = QT3DSU16(NVClamp<QT3DSU32>((b).m128_u32[1], 0, 0xFFFF)); + result.m128_u16[6] = QT3DSU16(NVClamp<QT3DSU32>((b).m128_u32[2], 0, 0xFFFF)); + result.m128_u16[7] = QT3DSU16(NVClamp<QT3DSU32>((b).m128_u32[3], 0, 0xFFFF)); + return result; +} + +QT3DS_FORCE_INLINE VecU32V V4U32or(VecU32V a, VecU32V b) +{ + return m128_I2F(_mm_or_si128(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecU32V V4U32and(VecU32V a, VecU32V b) +{ + return m128_I2F(_mm_and_si128(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecU32V V4U32Andc(VecU32V a, VecU32V b) +{ + return m128_I2F(_mm_andnot_si128(m128_F2I(b), m128_F2I(a))); +} + +QT3DS_FORCE_INLINE VecU16V V4U16Or(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_or_si128(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecU16V V4U16And(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_and_si128(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecU16V V4U16Andc(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_andnot_si128(m128_F2I(b), m128_F2I(a))); +} + +QT3DS_FORCE_INLINE VecI32V VecI32V_From_I32(const QT3DSI32 i) +{ + return (_mm_load1_ps((QT3DSF32 *)&i)); +} + +QT3DS_FORCE_INLINE VecI32V VecI32V_From_I32Array(const QT3DSI32 *i) +{ + return _mm_loadu_ps((QT3DSF32 *)i); +} + +QT3DS_FORCE_INLINE VecI32V VecI32V_From_I32Array_Aligned(const QT3DSI32 *i) +{ + return _mm_load_ps((QT3DSF32 *)i); +} + +QT3DS_FORCE_INLINE VecI32V VecI32V_Add(const VecI32VArg a, const VecI32VArg b) +{ + return m128_I2F(_mm_add_epi32(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecI32V VecI32V_Sub(const VecI32VArg a, const VecI32VArg b) +{ + return m128_I2F(_mm_sub_epi32(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE BoolV VecI32V_IsGrtr(const VecI32VArg a, const VecI32VArg b) +{ + return m128_I2F(_mm_cmpgt_epi32(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE BoolV VecI32V_IsEq(const VecI32VArg a, const VecI32VArg b) +{ + return m128_I2F(_mm_cmpeq_epi32(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecI32V VecI32V_Zero() +{ + return V4Zero(); +} + +QT3DS_FORCE_INLINE VecI32V VecI32V_Merge(const VecI32VArg a, const VecI32VArg b, const VecI32VArg c, + const VecI32VArg d) +{ + return V4Merge(a, b, c, d); +} + +template <int a> +QT3DS_FORCE_INLINE VecI32V V4ISplat() +{ + VecI32V result; + result.m128_i32[0] = a; + result.m128_i32[1] = a; + result.m128_i32[2] = a; + result.m128_i32[3] = a; + return result; +} + +QT3DS_FORCE_INLINE void V4U16StoreAligned(VecU16V val, VecU16V *address) +{ + *address = val; +} + +QT3DS_FORCE_INLINE void V4U32StoreAligned(VecU32V val, VecU32V *address) +{ + *address = val; +} + +QT3DS_FORCE_INLINE Vec4V V4LoadAligned(Vec4V *addr) +{ + return *addr; +} + +QT3DS_FORCE_INLINE Vec4V V4LoadUnaligned(Vec4V *addr) +{ + return Vec4V_From_F32Array((float *)addr); +} + +QT3DS_FORCE_INLINE Vec4V V4Andc(const Vec4V a, const VecU32V b) +{ + VecU32V result32(a); + result32 = V4U32Andc(result32, b); + return Vec4V(result32); +} + +QT3DS_FORCE_INLINE VecU32V V4IsGrtrV32u(const Vec4V a, const Vec4V b) +{ + return V4IsGrtr(a, b); +} + +QT3DS_FORCE_INLINE VecU16V V4U16LoadAligned(VecU16V *addr) +{ + return *addr; +} + +QT3DS_FORCE_INLINE VecU16V V4U16LoadUnaligned(VecU16V *addr) +{ + return *addr; +} + +QT3DS_FORCE_INLINE VecU16V V4U16CompareGt(VecU16V a, VecU16V b) +{ + // _mm_cmpgt_epi16 doesn't work for unsigned values unfortunately + // return m128_I2F(_mm_cmpgt_epi16(m128_F2I(a), m128_F2I(b))); + VecU16V result; + result.m128_u16[0] = (a).m128_u16[0] > (b).m128_u16[0]; + result.m128_u16[1] = (a).m128_u16[1] > (b).m128_u16[1]; + result.m128_u16[2] = (a).m128_u16[2] > (b).m128_u16[2]; + result.m128_u16[3] = (a).m128_u16[3] > (b).m128_u16[3]; + result.m128_u16[4] = (a).m128_u16[4] > (b).m128_u16[4]; + result.m128_u16[5] = (a).m128_u16[5] > (b).m128_u16[5]; + result.m128_u16[6] = (a).m128_u16[6] > (b).m128_u16[6]; + result.m128_u16[7] = (a).m128_u16[7] > (b).m128_u16[7]; + return result; +} + +QT3DS_FORCE_INLINE Vec4V Vec4V_From_VecU32V(VecU32V a) +{ + Vec4V result = Vec4V_From_XYZW(QT3DSF32(a.m128_u32[0]), QT3DSF32(a.m128_u32[1]), QT3DSF32(a.m128_u32[2]), + QT3DSF32(a.m128_u32[3])); + return result; +} + +template <int index> +QT3DS_FORCE_INLINE VecU32V V4U32SplatElement(VecU32V a) +{ + VecU32V result; + result.m128_u32[0] = result.m128_u32[1] = result.m128_u32[2] = result.m128_u32[3] = + a.m128_u32[index]; + return result; +} + +template <int index> +QT3DS_FORCE_INLINE Vec4V V4SplatElement(Vec4V a) +{ + float *data = (float *)&a; + return Vec4V_From_F32(data[index]); +} + +template <int index> +QT3DS_FORCE_INLINE VecU16V V4U16SplatElement(VecU16V a) +{ + VecU16V result; + for (int i = 0; i < 8; i++) + result.m128_u16[i] = a.m128_u16[index]; + return result; +} + +template <int imm> +QT3DS_FORCE_INLINE VecI16V V4I16SplatImmediate() +{ + VecI16V result; + result.m128_i16[0] = imm; + result.m128_i16[1] = imm; + result.m128_i16[2] = imm; + result.m128_i16[3] = imm; + result.m128_i16[4] = imm; + result.m128_i16[5] = imm; + result.m128_i16[6] = imm; + result.m128_i16[7] = imm; + return result; +} + +QT3DS_FORCE_INLINE VecU16V V4U16SubtractModulo(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_sub_epi16(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecU16V V4U16AddModulo(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_add_epi16(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecU32V V4U16GetLo16(VecU16V a) +{ + VecU32V result; + result.m128_u32[0] = a.m128_u16[0]; + result.m128_u32[1] = a.m128_u16[2]; + result.m128_u32[2] = a.m128_u16[4]; + result.m128_u32[3] = a.m128_u16[6]; + return result; +} + +QT3DS_FORCE_INLINE VecU32V V4U16GetHi16(VecU16V a) +{ + VecU32V result; + result.m128_u32[0] = a.m128_u16[1]; + result.m128_u32[1] = a.m128_u16[3]; + result.m128_u32[2] = a.m128_u16[5]; + result.m128_u32[3] = a.m128_u16[7]; + return result; +} + +QT3DS_FORCE_INLINE VecU32V VecU32V_From_XYZW(QT3DSU32 x, QT3DSU32 y, QT3DSU32 z, QT3DSU32 w) +{ + VecU32V result; + result.m128_u32[0] = x; + result.m128_u32[1] = y; + result.m128_u32[2] = z; + result.m128_u32[3] = w; + return result; +} + +QT3DS_FORCE_INLINE Vec4V V4Ceil(const Vec4V in) +{ + UnionM128 a(in); + return Vec4V_From_XYZW(NVCeil(a.m128_f32[0]), NVCeil(a.m128_f32[1]), NVCeil(a.m128_f32[2]), + NVCeil(a.m128_f32[3])); +} + +QT3DS_FORCE_INLINE Vec4V V4Floor(const Vec4V in) +{ + UnionM128 a(in); + return Vec4V_From_XYZW(NVFloor(a.m128_f32[0]), NVFloor(a.m128_f32[1]), NVFloor(a.m128_f32[2]), + NVFloor(a.m128_f32[3])); +} + +QT3DS_FORCE_INLINE VecU32V V4ConvertToU32VSaturate(const Vec4V in, QT3DSU32 power) +{ + QT3DS_ASSERT(power == 0 && "Non-zero power not supported in convertToU32VSaturate"); + QT3DS_FORCE_PARAMETER_REFERENCE(power); // prevent warning in release builds + QT3DSF32 ffffFFFFasFloat = QT3DSF32(0xFFFF0000); + UnionM128 a(in); + VecU32V result; + result.m128_u32[0] = QT3DSU32(NVClamp<QT3DSF32>((a).m128_f32[0], 0.0f, ffffFFFFasFloat)); + result.m128_u32[1] = QT3DSU32(NVClamp<QT3DSF32>((a).m128_f32[1], 0.0f, ffffFFFFasFloat)); + result.m128_u32[2] = QT3DSU32(NVClamp<QT3DSF32>((a).m128_f32[2], 0.0f, ffffFFFFasFloat)); + result.m128_u32[3] = QT3DSU32(NVClamp<QT3DSF32>((a).m128_f32[3], 0.0f, ffffFFFFasFloat)); + return result; +} + +#endif // QT3DS_LINUX_INLINE_AOS_H diff --git a/src/foundation/linux/Qt3DSLinuxIntrinsics.h b/src/foundation/linux/Qt3DSLinuxIntrinsics.h new file mode 100644 index 0000000..7097460 --- /dev/null +++ b/src/foundation/linux/Qt3DSLinuxIntrinsics.h @@ -0,0 +1,209 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_LINUX_INTRINSICS_H +#define QT3DS_FOUNDATION_QT3DS_LINUX_INTRINSICS_H + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSAssert.h" + +#if !(defined QT3DS_LINUX || defined QT3DS_ANDROID || defined QT3DS_APPLE || defined QT3DS_QNX) +#error "This file should only be included by Linux builds!!" +#endif + +#include <cmath> +#include <math.h> +#include <float.h> +#include <string.h> + +namespace qt3ds { +namespace intrinsics { + //! \brief platform-specific absolute value + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float abs(float a) { return ::fabs(a); } + + //! \brief platform-specific select float + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float fsel(float a, float b, float c) + { + return (a >= 0.0f) ? b : c; + } + + //! \brief platform-specific sign + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float sign(float a) { return (a >= 0.0f) ? 1.0f : -1.0f; } + + //! \brief platform-specific reciprocal + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float recip(float a) { return 1.0f / a; } + + //! \brief platform-specific reciprocal estimate + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float recipFast(float a) { return 1.0f / a; } + + //! \brief platform-specific square root + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float sqrt(float a) { return ::sqrtf(a); } + + //! \brief platform-specific reciprocal square root + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float recipSqrt(float a) { return 1.0f / ::sqrtf(a); } + + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float recipSqrtFast(float a) { return 1.0f / ::sqrtf(a); } + + //! \brief platform-specific sine + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float sin(float a) { return ::sinf(a); } + + //! \brief platform-specific cosine + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float cos(float a) { return ::cosf(a); } + + //! \brief platform-specific minimum + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float selectMin(float a, float b) { return a < b ? a : b; } + + //! \brief platform-specific maximum + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float selectMax(float a, float b) { return a > b ? a : b; } + + //! \brief platform-specific finiteness check (not INF or NAN) + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool isFinite(float a) { return std::isfinite(a); } + + //! \brief platform-specific finiteness check (not INF or NAN) + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool isFinite(double a) { return std::isfinite(a); } + QT3DS_FORCE_INLINE void memoryBarrier() + { +#if 0 //!defined (QT3DS_ARM) + smp_mb(); +#endif + } + + /*! + Return the index of the highest set bit. Undefined for zero arg. + */ + QT3DS_INLINE QT3DSU32 highestSetBitUnsafe(QT3DSU32 v) + { + + // http://graphics.stanford.edu/~seander/bithacks.html + static const QT3DSU32 MultiplyDeBruijnBitPosition[32] = { 0, 9, 1, 10, 13, 21, 2, 29, + 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, + 19, 27, 23, 6, 26, 5, 4, 31 }; + + v |= v >> 1; // first round up to one less than a power of 2 + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + + return MultiplyDeBruijnBitPosition[(QT3DSU32)(v * 0x07C4ACDDU) >> 27]; + } + + /*! + Return the index of the highest set bit. Undefined for zero arg. + */ + QT3DS_INLINE QT3DSI32 lowestSetBitUnsafe(QT3DSU32 v) + { + // http://graphics.stanford.edu/~seander/bithacks.html + static const QT3DSU32 MultiplyDeBruijnBitPosition[32] = { 0, 1, 28, 2, 29, 14, 24, 3, + 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, + 26, 12, 18, 6, 11, 5, 10, 9 }; + QT3DSI32 w = v; + return MultiplyDeBruijnBitPosition[(QT3DSU32)((w & -w) * 0x077CB531U) >> 27]; + } + + /*! + Returns the index of the highest set bit. Undefined for zero arg. + */ + QT3DS_INLINE QT3DSU32 countLeadingZeros(QT3DSU32 v) + { + QT3DSI32 result = 0; +#ifdef _INTEGRITYPLATFORM + QT3DSU32 testBit = (1U << 31); +#else + QT3DSU32 testBit = (1 << 31); +#endif + while ((v & testBit) == 0 && testBit != 0) + result++, testBit >>= 1; + return result; + } + + /*! + Sets \c count bytes starting at \c dst to zero. + */ + QT3DS_FORCE_INLINE void *memZero(void *QT3DS_RESTRICT dest, QT3DSU32 count) + { + return memset(dest, 0, count); + } + + /*! + Sets \c count bytes starting at \c dst to \c c. + */ + QT3DS_FORCE_INLINE void *memSet(void *QT3DS_RESTRICT dest, QT3DSI32 c, QT3DSU32 count) + { + return memset(dest, c, count); + } + + /*! + Copies \c count bytes from \c src to \c dst. User memMove if regions overlap. + */ + QT3DS_FORCE_INLINE void *memCopy(void *QT3DS_RESTRICT dest, const void *QT3DS_RESTRICT src, QT3DSU32 count) + { + return memcpy(dest, src, count); + } + + /*! + Copies \c count bytes from \c src to \c dst. Supports overlapping regions. + */ + QT3DS_FORCE_INLINE void *memMove(void *QT3DS_RESTRICT dest, const void *QT3DS_RESTRICT src, QT3DSU32 count) + { + return memmove(dest, src, count); + } + + /*! + Set 128B to zero starting at \c dst+offset. Must be aligned. + */ + QT3DS_FORCE_INLINE void memZero128(void *QT3DS_RESTRICT dest, QT3DSU32 offset = 0) + { + QT3DS_ASSERT(((size_t(dest) + offset) & 0x7f) == 0); + memSet((char *QT3DS_RESTRICT)dest + offset, 0, 128); + } + + /*! + Prefetch aligned 128B around \c ptr+offset. + */ + QT3DS_FORCE_INLINE void prefetch128(const void *, QT3DSU32 = 0) {} + + /*! + Prefetch \c count bytes starting at \c ptr. + */ + QT3DS_FORCE_INLINE void prefetch(const void *ptr, QT3DSU32 count = 0) + { + for (QT3DSU32 i = 0; i <= count; i += 128) + prefetch128(ptr, i); + } + + //! \brief platform-specific floor + QT3DS_FORCE_INLINE float floatFloor(float x) { return ::floorf(x); } +} // namespace intrinsics +} // namespace qt3ds + +#endif diff --git a/src/foundation/linux/Qt3DSLinuxMutex.cpp b/src/foundation/linux/Qt3DSLinuxMutex.cpp new file mode 100644 index 0000000..bbc5582 --- /dev/null +++ b/src/foundation/linux/Qt3DSLinuxMutex.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSMutex.h" +#include "foundation/Qt3DSAssert.h" +#include "foundation/Qt3DSAtomic.h" + +#include <pthread.h> + +namespace qt3ds { +namespace foundation { + + namespace { + pthread_mutex_t *getMutex(MutexImpl *impl) + { + return reinterpret_cast<pthread_mutex_t *>(impl); + } + } + + MutexImpl::MutexImpl() + { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(getMutex(this), &attr); + pthread_mutexattr_destroy(&attr); + } + + MutexImpl::~MutexImpl() { pthread_mutex_destroy(getMutex(this)); } + + bool MutexImpl::lock() { return !pthread_mutex_lock(getMutex(this)); } + + bool MutexImpl::trylock() { return !pthread_mutex_trylock(getMutex(this)); } + + bool MutexImpl::unlock() { return !pthread_mutex_unlock(getMutex(this)); } + + const QT3DSU32 MutexImpl::size = sizeof(pthread_mutex_t); + + class ReadWriteLockImpl + { + public: + ReadWriteLockImpl(NVAllocatorCallback &alloc) + : mAllocator(alloc) + { + } + NVAllocatorCallback &mAllocator; + pthread_mutex_t mutex; + volatile int readerCounter; + }; + + ReadWriteLock::ReadWriteLock(NVAllocatorCallback &alloc) + { + mImpl = QT3DS_NEW(alloc, ReadWriteLockImpl)(alloc); + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mImpl->mutex, &attr); + pthread_mutexattr_destroy(&attr); + + mImpl->readerCounter = 0; + } + + ReadWriteLock::~ReadWriteLock() + { + pthread_mutex_destroy(&mImpl->mutex); + QT3DS_FREE(mImpl->mAllocator, mImpl); + } + + void ReadWriteLock::lockReader() + { + pthread_mutex_lock(&mImpl->mutex); + + atomicIncrement(&mImpl->readerCounter); + + pthread_mutex_unlock(&mImpl->mutex); + } + + void ReadWriteLock::lockWriter() + { + pthread_mutex_lock(&mImpl->mutex); + + while (mImpl->readerCounter != 0) + ; + } + + void ReadWriteLock::unlockReader() { atomicDecrement(&mImpl->readerCounter); } + + void ReadWriteLock::unlockWriter() { pthread_mutex_unlock(&mImpl->mutex); } + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/linux/Qt3DSLinuxSemaphore.cpp b/src/foundation/linux/Qt3DSLinuxSemaphore.cpp new file mode 100644 index 0000000..90aab79 --- /dev/null +++ b/src/foundation/linux/Qt3DSLinuxSemaphore.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSSemaphore.h" +#include "foundation/Qt3DSAssert.h" +#include "foundation/Qt3DSAllocator.h" +#include "foundation/Qt3DSAllocatorCallback.h" + +#include <errno.h> +#include <stdio.h> +#include <pthread.h> +#include <time.h> +#include <sys/time.h> + +namespace qt3ds { +namespace foundation { + + class SemaphoreImpl + { + public: + SemaphoreImpl(NVAllocatorCallback &alloc) + : mAllocator(alloc) + { + } + NVAllocatorCallback &mAllocator; + pthread_mutex_t mutex; + pthread_cond_t cond; + QT3DSU32 count; + QT3DSU32 maxCount; + }; + + struct NVLinuxScopeLock + { + NVLinuxScopeLock(pthread_mutex_t &m) + : mMutex(m) + { + pthread_mutex_lock(&mMutex); + } + + ~NVLinuxScopeLock() { pthread_mutex_unlock(&mMutex); } + private: + pthread_mutex_t &mMutex; + }; + + Semaphore::Semaphore(NVAllocatorCallback &alloc, QT3DSU32 initialCount, QT3DSU32 maxCount) + { + mImpl = QT3DS_NEW(alloc, SemaphoreImpl)(alloc); + int status = pthread_mutex_init(&mImpl->mutex, 0); + QT3DS_ASSERT(!status); + status = pthread_cond_init(&mImpl->cond, 0); + QT3DS_ASSERT(!status); + mImpl->count = initialCount; + mImpl->maxCount = maxCount; + QT3DS_ASSERT(initialCount <= maxCount); + } + + Semaphore::~Semaphore() + { + pthread_cond_destroy(&mImpl->cond); + pthread_mutex_destroy(&mImpl->mutex); + QT3DS_FREE(mImpl->mAllocator, mImpl); + } + + bool Semaphore::wait(QT3DSU32 milliseconds) + { + NVLinuxScopeLock lock(mImpl->mutex); + + if (mImpl->count > 0) { + mImpl->count--; + return true; + } + + if (milliseconds == 0) { + return false; + } + + if (milliseconds == QT3DSU32(-1)) { + int status = pthread_cond_wait(&mImpl->cond, &mImpl->mutex); + QT3DS_ASSERT(!status); + (void)status; + } else { + timespec ts; + timeval tp; + gettimeofday(&tp, NULL); + QT3DSU32 sec = milliseconds / 1000; + QT3DSU32 usec = (milliseconds - 1000 * sec) * 1000; + + // sschirm: taking into account that us might accumulate to a second + // otherwise the pthread_cond_timedwait complains on osx. + usec = tp.tv_usec + usec; + QT3DSU32 div_sec = usec / 1000000; + QT3DSU32 rem_usec = usec - div_sec * 1000000; + + ts.tv_sec = tp.tv_sec + sec + div_sec; + ts.tv_nsec = rem_usec * 1000; + + int ierr = pthread_cond_timedwait(&mImpl->cond, &mImpl->mutex, &ts); + QT3DS_ASSERT((ierr == 0) || (ierr == ETIMEDOUT)); + (void)ierr; + return false; + } + + return true; + } + + void Semaphore::post() + { + NVLinuxScopeLock lock(mImpl->mutex); + mImpl->count++; + if (mImpl->count > mImpl->maxCount) + mImpl->count = mImpl->maxCount; + else { + pthread_cond_broadcast(&mImpl->cond); + } + } + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/linux/Qt3DSLinuxString.h b/src/foundation/linux/Qt3DSLinuxString.h new file mode 100644 index 0000000..a4645f2 --- /dev/null +++ b/src/foundation/linux/Qt3DSLinuxString.h @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_LINUX_STRING_H +#define QT3DS_FOUNDATION_QT3DS_LINUX_STRING_H + +#include "foundation/Qt3DS.h" + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> + +#pragma warning(push) +#pragma warning(disable : 4995 4996) + +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif + +QT3DS_INLINE void NVStrcpy(char *dest, size_t size, const char *src) +{ + ::strncpy(dest, src, size); +} + +QT3DS_INLINE int NVStrcat(char *dest, size_t size, const char *src) +{ + ::strcat(dest, src); + return 0; +} +QT3DS_INLINE int NVVsprintf(char *dest, size_t size, const char *src, va_list arg) +{ + int r = ::vsprintf(dest, src, arg); + + return r; +} +QT3DS_INLINE int NVStricmp(const char *str, const char *str1) +{ + return (::strcasecmp(str, str1)); +} + +namespace string { + + QT3DS_INLINE void strcpy(char *dest, size_t size, const char *src) { ::strcpy(dest, src); } + QT3DS_INLINE void strcat(char *dest, size_t size, const char *src) { ::strcat(dest, src); } + // QT3DS_INLINE int strcasecmp(const char *str, const char *str1) {return(::strcasecmp(str, + // str1));} + QT3DS_INLINE QT3DSI32 stricmp(const char *str, const char *str1) { return (::strcasecmp(str, str1)); } + QT3DS_INLINE QT3DSI32 strnicmp(const char *str, const char *str1, size_t len) + { + return (::strncasecmp(str, str1, len)); + } + + QT3DS_INLINE QT3DSI32 strncat_s(char *dstBfr, size_t dstSize, const char *srcBfr, size_t numCpy) + { + if (!dstBfr || !srcBfr || !dstSize) + return -1; + + size_t len = strlen(dstBfr); + + if (len >= dstSize) + return -1; + + size_t remain = dstSize - len - 1; + size_t transfer = remain > numCpy ? numCpy : remain; + ::memmove(dstBfr + len, srcBfr, transfer); + dstBfr[len + transfer] = '\0'; + return numCpy <= remain ? 0 : -1; + } + + QT3DS_INLINE QT3DSI32 _vsnprintf(char *dest, size_t size, const char *src, va_list arg) + { + QT3DSI32 r = ::vsnprintf(dest, size, src, arg); + + return r; + } + QT3DS_INLINE QT3DSI32 vsprintf(char *dest, size_t size, const char *src, va_list arg) + { + QT3DSI32 r = ::vsprintf(dest, src, arg); + + return r; + } + QT3DS_INLINE int vsprintf_s(char *dest, size_t size, const char *src, va_list arg) + { + int r = ::vsprintf(dest, src, arg); + + return r; + } + + QT3DS_INLINE int sprintf_s(char *_DstBuf, size_t _DstSize, const char *_Format, ...) + { + if (_DstBuf == NULL || _Format == NULL) { + return -1; + } + + va_list arg; + va_start(arg, _Format); + int r = ::vsprintf(_DstBuf, _Format, arg); + va_end(arg); + + return r; + } + + QT3DS_INLINE QT3DSI32 sprintf(char *_DstBuf, size_t _DstSize, const char *_Format, ...) + { + va_list arg; + va_start(arg, _Format); + QT3DSI32 r = ::vsprintf(_DstBuf, _Format, arg); + va_end(arg); + + return r; + } + + QT3DS_INLINE int strncpy_s(char *strDest, size_t sizeInBytes, const char *strSource, size_t count) + { + if (strDest == NULL || strSource == NULL || sizeInBytes == 0) { + return -1; + } + + if (sizeInBytes < count) { + strDest[0] = 0; + return -1; + } + + ::strncpy(strDest, strSource, count); + return 0; + } + + QT3DS_INLINE void strcpy_s(char *dest, size_t size, const char *src) + { + ::strncpy(dest, src, size); + } + + QT3DS_INLINE int strcat_s(char *dest, size_t size, const char *src) + { + ::strcat(dest, src); + return 0; + } + + QT3DS_INLINE QT3DSI32 sscanf(const char *buffer, const char *format, ...) + { + va_list arg; + va_start(arg, format); + QT3DSI32 r = ::sscanf(buffer, format, arg); + va_end(arg); + + return r; + }; + + QT3DS_INLINE void strlwr(char *str) + { + while (*str) { + if (*str >= 'A' && *str <= 'Z') + *str += 32; + str++; + } + } + + QT3DS_INLINE void strupr(char *str) + { + while (*str) { + if (*str >= 'a' && *str <= 'z') + *str -= 32; + str++; + } + } + +} // namespace string + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +#pragma warning(pop) + +#endif diff --git a/src/foundation/linux/Qt3DSLinuxSync.cpp b/src/foundation/linux/Qt3DSLinuxSync.cpp new file mode 100644 index 0000000..52f9d7a --- /dev/null +++ b/src/foundation/linux/Qt3DSLinuxSync.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSSync.h" +#include "foundation/Qt3DSAssert.h" +#include "foundation/Qt3DSAllocator.h" +#include "foundation/Qt3DSAllocatorCallback.h" + +#include <errno.h> +#include <stdio.h> +#include <pthread.h> +#include <time.h> +#include <sys/time.h> + +namespace qt3ds { +namespace foundation { + + class SyncImpl + { + public: + SyncImpl(NVAllocatorCallback &alloc) + : mAllocator(alloc) + { + } + NVAllocatorCallback &mAllocator; + pthread_mutex_t mutex; + pthread_cond_t cond; + volatile bool is_set; + }; + + struct NVLinuxScopeLock + { + NVLinuxScopeLock(pthread_mutex_t &m) + : mMutex(m) + { + pthread_mutex_lock(&mMutex); + } + + ~NVLinuxScopeLock() { pthread_mutex_unlock(&mMutex); } + private: + pthread_mutex_t &mMutex; + }; + + Sync::Sync(NVAllocatorCallback &alloc) + { + mImpl = QT3DS_NEW(alloc, SyncImpl)(alloc); + int status = pthread_mutex_init(&mImpl->mutex, 0); + QT3DS_ASSERT(!status); + status = pthread_cond_init(&mImpl->cond, 0); + QT3DS_ASSERT(!status); + mImpl->is_set = false; + } + + Sync::~Sync() + { + pthread_cond_destroy(&mImpl->cond); + pthread_mutex_destroy(&mImpl->mutex); + QT3DS_FREE(mImpl->mAllocator, mImpl); + } + + void Sync::reset() + { + NVLinuxScopeLock lock(mImpl->mutex); + mImpl->is_set = false; + } + + void Sync::set() + { + NVLinuxScopeLock lock(mImpl->mutex); + if (!mImpl->is_set) { + mImpl->is_set = true; + pthread_cond_broadcast(&mImpl->cond); + } + } + + bool Sync::wait(QT3DSU32 ms) + { + NVLinuxScopeLock lock(mImpl->mutex); + if (!mImpl->is_set) { + if (ms == QT3DSU32(-1)) { + int status = pthread_cond_wait(&mImpl->cond, &mImpl->mutex); + QT3DS_ASSERT(!status); + (void)status; + } else { + timespec ts; + timeval tp; + gettimeofday(&tp, NULL); + QT3DSU32 sec = ms / 1000; + QT3DSU32 usec = (ms - 1000 * sec) * 1000; + + // sschirm: taking into account that us might accumulate to a second + // otherwise the pthread_cond_timedwait complains on osx. + usec = tp.tv_usec + usec; + QT3DSU32 div_sec = usec / 1000000; + QT3DSU32 rem_usec = usec - div_sec * 1000000; + + ts.tv_sec = tp.tv_sec + sec + div_sec; + ts.tv_nsec = rem_usec * 1000; + + int ierr = pthread_cond_timedwait(&mImpl->cond, &mImpl->mutex, &ts); + QT3DS_ASSERT((ierr == 0) || (ierr == ETIMEDOUT)); + (void)ierr; + } + } + return mImpl->is_set; + } + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/linux/Qt3DSLinuxThread.cpp b/src/foundation/linux/Qt3DSLinuxThread.cpp new file mode 100644 index 0000000..fa6e069 --- /dev/null +++ b/src/foundation/linux/Qt3DSLinuxThread.cpp @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSThread.h" +#include "foundation/Qt3DSAssert.h" +#include "foundation/Qt3DSIntrinsics.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#if !defined(QT3DS_APPLE) && !defined(ANDROID) && !defined(__CYGWIN__) && !defined(__QNX__) && !defined(__INTEGRITY) +#include <bits/local_lim.h> // PTHREAD_STACK_MIN +#endif +#include <stdio.h> +#include <pthread.h> +#if !defined(__QNX__) && !defined(__INTEGRITY) +#include <unistd.h> +#include <sys/syscall.h> +#if !defined(QT3DS_APPLE) +#include <asm/unistd.h> +#include <sys/resource.h> +#endif +#endif + +#define NVSpinLockPause() asm("nop") + +namespace qt3ds { +namespace foundation { + using namespace intrinsics; + + typedef enum { _NVThreadNotStarted, _NVThreadStarted, _NVThreadStopped } NVThreadState; + + class ThreadImpl + { + public: + ThreadImpl(NVFoundationBase &foundation) + : mFoundation(foundation) + { + } + NVFoundationBase &mFoundation; + Thread::ExecuteFn fn; + void *arg; + volatile QT3DSI32 quitNow; + volatile QT3DSI32 threadStarted; + NVThreadState state; + + pthread_t thread; + pid_t tid; + }; + + void *NVThreadStart(void *arg); + + Thread::Id Thread::getId() { return Id(pthread_self()); } + + Thread::Thread(NVFoundationBase &foundation) + { + mImpl = QT3DS_NEW(foundation.getAllocator(), ThreadImpl)(foundation); + mImpl->thread = 0; + mImpl->tid = 0; + mImpl->state = _NVThreadNotStarted; + mImpl->quitNow = 0; + mImpl->threadStarted = 0; + mImpl->fn = NULL; + mImpl->arg = NULL; + } + + Thread::Thread(NVFoundationBase &foundation, Thread::ExecuteFn fn, void *arg) + { + mImpl = (ThreadImpl *)QT3DS_NEW(foundation.getAllocator(), ThreadImpl)(foundation); + mImpl->thread = 0; + mImpl->tid = 0; + mImpl->state = _NVThreadNotStarted; + mImpl->quitNow = 0; + mImpl->threadStarted = 0; + mImpl->fn = fn; + mImpl->arg = arg; + + start(0); + } + + Thread::~Thread() + { + if (mImpl->state == _NVThreadStarted) + kill(); + QT3DS_FREE(mImpl->mFoundation.getAllocator(), mImpl); + } + + void Thread::start(QT3DSU32 stackSize) + { + if (mImpl->state != _NVThreadNotStarted) + return; + + if (stackSize == 0) + stackSize = DEFAULT_STACK_SIZE; + +#if defined(PTHREAD_STACK_MIN) && !defined(ANDROID) + if (stackSize < PTHREAD_STACK_MIN) { + qCWarning(WARNING, "Thread::start(): stack size was set below PTHREAD_STACK_MIN"); + stackSize = PTHREAD_STACK_MIN; + } +#endif + + pthread_attr_t attr; + int status = pthread_attr_init(&attr); + QT3DS_ASSERT(!status); + + status = pthread_attr_setstacksize(&attr, stackSize); + QT3DS_ASSERT(!status); + status = pthread_create(&mImpl->thread, &attr, NVThreadStart, this); + QT3DS_ASSERT(!status); + +#ifndef __QNX__ + // wait for thread to startup and write out TID + // otherwise TID dependent calls like setAffinity will fail. + while (atomicCompareExchange(&(mImpl->threadStarted), 1, 1) == 0) + yield(); +#endif + + mImpl->state = _NVThreadStarted; + + status = pthread_attr_destroy(&attr); + QT3DS_ASSERT(!status); + } + + static void setTid(ThreadImpl &threadImpl) + { +#ifndef __QNX__ +// query TID +#ifdef QT3DS_APPLE + threadImpl.tid = syscall(SYS_gettid); +#elif defined(__INTEGRITY) + pthread_t ptid = pthread_self(); + uint64_t threadId = 0; + memcpy(&threadId, &ptid, std::min(sizeof(threadId), sizeof(ptid))); + threadImpl.tid = threadId; +#else + threadImpl.tid = syscall(__NR_gettid); +#endif + + // notify/unblock parent thread + atomicCompareExchange(&(threadImpl.threadStarted), 1, 0); +#else + QT3DS_ASSERT(false); +#endif + } + + void *NVThreadStart(void *arg) + { + // run execute from base class to run gettid in the new thread's context + ((Thread *)arg)->Thread::execute(); + return 0; + } + + void Thread::execute(void) + { + // run setTid in thread's context + setTid(*mImpl); + + // then run either the passed in function or execute from the derived class. + if (mImpl->fn) + (*mImpl->fn)(mImpl->arg); + else + this->execute(); + } + + void Thread::signalQuit() { atomicIncrement(&(mImpl->quitNow)); } + + bool Thread::waitForQuit() + { + if (mImpl->state == _NVThreadNotStarted) + return false; + + pthread_join(mImpl->thread, NULL); + return true; + } + + bool Thread::quitIsSignalled() + { +#ifndef __QNX__ + return atomicCompareExchange(&(mImpl->quitNow), 0, 0) != 0; +#else + // Hope for memory locking on the arm. + return mImpl->quitNow != 0; +#endif + } + + void Thread::quit() + { + mImpl->state = _NVThreadStopped; + pthread_exit(0); + } + + void Thread::kill() + { +#ifndef ANDROID + if (mImpl->state == _NVThreadStarted) + pthread_cancel(mImpl->thread); + mImpl->state = _NVThreadStopped; +#else + qCWarning(WARNING, "Thread::kill() called, but is not implemented"); +#endif + } + + void Thread::sleep(QT3DSU32 ms) + { + timespec sleepTime; + QT3DSU32 remainder = ms % 1000; + sleepTime.tv_sec = ms - remainder; + sleepTime.tv_nsec = remainder * 1000000L; + + while (nanosleep(&sleepTime, &sleepTime) == -1) + continue; + } + + void Thread::yield() { sched_yield(); } + + QT3DSU32 Thread::setAffinityMask(QT3DSU32 mask) + { + // Same as windows impl if mask is zero + if (!mask) + return 0; + +#ifndef __QNX__ + QT3DSU64 prevMask = 0; + +// Apple doesn't support syscall with getaffinity and setaffinity +#if !defined(QT3DS_APPLE) && !defined(__INTEGRITY) + QT3DSI32 errGet = syscall(__NR_sched_getaffinity, mImpl->tid, sizeof(prevMask), &prevMask); + if (errGet < 0) + return 0; + + QT3DSI32 errSet = syscall(__NR_sched_setaffinity, mImpl->tid, sizeof(mask), &mask); + if (errSet != 0) + return 0; +#endif + + return QT3DSU32(prevMask); +#else + QT3DS_ASSERT(false); + return 0; +#endif + } + + void Thread::setName(const char *name) + { +#if (defined(ANDROID) && (__ANDROID_API__ > 8)) + pthread_setname_np(mImpl->thread, name); +#else +// not implemented because most unix APIs expect setName() +// to be called from the thread's context. Example see next comment: + +// this works only with the current thread and can rename +// the main process if used in the wrong context: +// prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name) ,0,0,0); +#endif + } + +#if !defined(QT3DS_APPLE) && !defined(__INTEGRITY) + static ThreadPriority::Enum convertPriorityFromLinux(QT3DSU32 inPrio, int policy) + { + QT3DS_COMPILE_TIME_ASSERT(ThreadPriority::eLOW > ThreadPriority::eHIGH); + QT3DS_COMPILE_TIME_ASSERT(ThreadPriority::eHIGH == 0); + + int maxL = sched_get_priority_max(policy); + int minL = sched_get_priority_min(policy); + int rangeL = maxL - minL; + int rangeNV = ThreadPriority::eLOW - ThreadPriority::eHIGH; + + // case for default scheduler policy + if (rangeL == 0) + return ThreadPriority::eNORMAL; + + float floatPrio = (float(maxL - inPrio) * float(rangeNV)) / float(rangeL); + + return ThreadPriority::Enum(int(roundf(floatPrio))); + } + + static int convertPriorityToLinux(ThreadPriority::Enum inPrio, int policy) + { + int maxL = sched_get_priority_max(policy); + int minL = sched_get_priority_min(policy); + int rangeL = maxL - minL; + int rangeNV = ThreadPriority::eLOW - ThreadPriority::eHIGH; + + // case for default scheduler policy + if (rangeL == 0) + return 0; + + float floatPrio = (float(ThreadPriority::eLOW - inPrio) * float(rangeL)) / float(rangeNV); + + return minL + int(roundf(floatPrio)); + } +#endif + + void Thread::setPriority(ThreadPriority::Enum val) + { +#if !defined(QT3DS_APPLE) && !defined(__INTEGRITY) + int policy; + sched_param s_param; + pthread_getschedparam(mImpl->thread, &policy, &s_param); + s_param.sched_priority = convertPriorityToLinux(val, policy); + pthread_setschedparam(mImpl->thread, policy, &s_param); +#endif + } + + ThreadPriority::Enum Thread::getPriority(Id pthread) + { +#if !defined(QT3DS_APPLE) && !defined(__INTEGRITY) + int policy; + sched_param s_param; + int ret = pthread_getschedparam(pthread_t(pthread), &policy, &s_param); + if (ret == 0) + return convertPriorityFromLinux(s_param.sched_priority, policy); + else + return ThreadPriority::eNORMAL; +#else + return ThreadPriority::eNORMAL; +#endif + } + + QT3DSU32 TlsAlloc() + { +#if !defined(__INTEGRITY) + pthread_key_t key; + int status = pthread_key_create(&key, NULL); + QT3DS_ASSERT(!status); + (void)status; + return (QT3DSU32)key; +#else + QT3DS_ASSERT(false); + return 0; +#endif + } + + void TlsFree(QT3DSU32 index) + { + int status = pthread_key_delete((pthread_key_t)index); + QT3DS_ASSERT(!status); + (void)status; + } + + void *TlsGet(QT3DSU32 index) { return (void *)pthread_getspecific((pthread_key_t)index); } + + QT3DSU32 TlsSet(QT3DSU32 index, void *value) + { + int status = pthread_setspecific((pthread_key_t)index, value); + QT3DS_ASSERT(!status); + return !status; + } + + // DM: On Linux x86-32, without implementation-specific restrictions + // the default stack size for a new thread should be 2 megabytes (kernel.org). + // NOTE: take care of this value on other architecutres! + const QT3DSU32 Thread::DEFAULT_STACK_SIZE = 1 << 21; + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/linux/Qt3DSLinuxTime.cpp b/src/foundation/linux/Qt3DSLinuxTime.cpp new file mode 100644 index 0000000..6b3e93e --- /dev/null +++ b/src/foundation/linux/Qt3DSLinuxTime.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSTime.h" + +#include <time.h> +#include <sys/time.h> + +#if defined QT3DS_APPLE +#include <mach/mach_time.h> +#endif + +// Use real-time high-precision timer. +#ifndef QT3DS_APPLE +#define CLOCKID CLOCK_REALTIME +#endif + +namespace qt3ds { +namespace foundation { + + const CounterFrequencyToTensOfNanos Time::sCounterFreq = Time::getCounterFrequency(); + + static Time::Second getTimeSeconds() + { + static struct timeval _tv; + gettimeofday(&_tv, NULL); + return double(_tv.tv_sec) + double(_tv.tv_usec) * 0.000001; + } + + Time::Time() { mLastTime = getTimeSeconds(); } + + Time::Second Time::getElapsedSeconds() + { + Time::Second curTime = getTimeSeconds(); + Time::Second diff = curTime - mLastTime; + mLastTime = curTime; + return diff; + } + + Time::Second Time::peekElapsedSeconds() + { + Time::Second curTime = getTimeSeconds(); + Time::Second diff = curTime - mLastTime; + return diff; + } + + Time::Second Time::getLastTime() const { return mLastTime; } + +#ifdef QT3DS_APPLE + CounterFrequencyToTensOfNanos Time::getCounterFrequency() + { + mach_timebase_info_data_t info; + mach_timebase_info(&info); + // sschirm: some code in the PhysX samples assumes that + // CounterFrequencyToTensOfNanos::mDenominator is #ticks/second + // which is bad since it ignores the numenator. This is a temporary fix + // with the same setup as windows + // return CounterFrequencyToTensOfNanos( info.numer, info.denom*10 ); + return CounterFrequencyToTensOfNanos(sNumTensOfNanoSecondsInASecond, + info.denom * 1000000000 / info.numer); + } + + QT3DSU64 Time::getCurrentCounterValue() { return mach_absolute_time(); } + +#else + + CounterFrequencyToTensOfNanos Time::getCounterFrequency() + { + return CounterFrequencyToTensOfNanos(1, 10); + } + + QT3DSU64 Time::getCurrentCounterValue() + { + struct timespec mCurrTimeInt; + clock_gettime(CLOCKID, &mCurrTimeInt); + // Convert to nanos as this doesn't cause a large divide here + return (static_cast<QT3DSU64>(mCurrTimeInt.tv_sec) * 1000000000) + + (static_cast<QT3DSU64>(mCurrTimeInt.tv_nsec)); + } +#endif + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/linux/Qt3DSLinuxTrigConstants.h b/src/foundation/linux/Qt3DSLinuxTrigConstants.h new file mode 100644 index 0000000..120dec7 --- /dev/null +++ b/src/foundation/linux/Qt3DSLinuxTrigConstants.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_LINUX_TRIG_CONSTANTS_H +#define QT3DS_LINUX_TRIG_CONSTANTS_H + +//#define QT3DS_GLOBALCONST extern const __declspec(selectany) + +#define QT3DS_GLOBALCONST extern const __attribute__((weak)) + +QT3DS_ALIGN_PREFIX(16) +struct QT3DS_VECTORF32 +{ + float f[4]; +}; + +#define QT3DS_PI 3.141592654f +#define QT3DS_2PI 6.283185307f +#define QT3DS_1DIVPI 0.318309886f +#define QT3DS_1DIV2PI 0.159154943f +#define QT3DS_PIDIV2 1.570796327f +#define QT3DS_PIDIV4 0.785398163f + +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXSinCoefficients0 = { 1.0f, -0.166666667f, 8.333333333e-3f, + -1.984126984e-4f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXSinCoefficients1 = { 2.755731922e-6f, -2.505210839e-8f, + 1.605904384e-10f, -7.647163732e-13f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXSinCoefficients2 = { 2.811457254e-15f, -8.220635247e-18f, + 1.957294106e-20f, -3.868170171e-23f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXCosCoefficients0 = { 1.0f, -0.5f, 4.166666667e-2f, + -1.388888889e-3f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXCosCoefficients1 = { 2.480158730e-5f, -2.755731922e-7f, + 2.087675699e-9f, -1.147074560e-11f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXCosCoefficients2 = { 4.779477332e-14f, -1.561920697e-16f, + 4.110317623e-19f, -8.896791392e-22f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXTanCoefficients0 = { 1.0f, 0.333333333f, 0.133333333f, + 5.396825397e-2f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXTanCoefficients1 = { 2.186948854e-2f, 8.863235530e-3f, + 3.592128167e-3f, 1.455834485e-3f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXTanCoefficients2 = { 5.900274264e-4f, 2.391290764e-4f, + 9.691537707e-5f, 3.927832950e-5f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXASinCoefficients0 = { -0.05806367563904f, -0.41861972469416f, + 0.22480114791621f, 2.17337241360606f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXASinCoefficients1 = { 0.61657275907170f, 4.29696498283455f, + -1.18942822255452f, -6.53784832094831f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXASinCoefficients2 = { -1.36926553863413f, -4.48179294237210f, + 1.41810672941833f, 5.48179257935713f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXATanCoefficients0 = { 1.0f, 0.333333334f, 0.2f, 0.142857143f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXATanCoefficients1 = { 1.111111111e-1f, 9.090909091e-2f, + 7.692307692e-2f, 6.666666667e-2f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXATanCoefficients2 = { 5.882352941e-2f, 5.263157895e-2f, + 4.761904762e-2f, 4.347826087e-2f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXSinEstCoefficients = { 1.0f, -1.66521856991541e-1f, + 8.199913018755e-3f, -1.61475937228e-4f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXCosEstCoefficients = { 1.0f, -4.95348008918096e-1f, + 3.878259962881e-2f, -9.24587976263e-4f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXTanEstCoefficients = { 2.484f, -1.954923183e-1f, 2.467401101f, + QT3DS_1DIVPI }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXATanEstCoefficients = { 7.689891418951e-1f, 1.104742493348f, + 8.661844266006e-1f, QT3DS_PIDIV2 }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXASinEstCoefficients = { -1.36178272886711f, 2.37949493464538f, + -8.08228565650486e-1f, + 2.78440142746736e-1f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXASinEstConstants = { 1.00000011921f, QT3DS_PIDIV2, 0.0f, 0.0f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXPiConstants0 = { QT3DS_PI, QT3DS_2PI, QT3DS_1DIVPI, QT3DS_1DIV2PI }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXReciprocalTwoPi = { QT3DS_1DIV2PI, QT3DS_1DIV2PI, QT3DS_1DIV2PI, + QT3DS_1DIV2PI }; + +#endif diff --git a/src/foundation/linux/SocketImpl.h b/src/foundation/linux/SocketImpl.h new file mode 100644 index 0000000..4c3fcb7 --- /dev/null +++ b/src/foundation/linux/SocketImpl.h @@ -0,0 +1,424 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef FOUNDATION_LINUX_SOCKET_IMPL_H +#define FOUNDATION_LINUX_SOCKET_IMPL_H +#pragma once + +/*=========================================================================*\ +* BSD include files +\*=========================================================================*/ +/* error codes */ +#include <errno.h> +/* close function */ +#include <unistd.h> +/* fnctnl function and associated constants */ +#include <fcntl.h> +/* struct sockaddr */ +#include <sys/types.h> +/* socket function */ +#include <sys/socket.h> +/* struct timeval */ +#include <sys/time.h> +/* gethostbyname and gethostbyaddr functions */ +#include <netdb.h> +/* sigpipe handling */ +#include <signal.h> +/* IP stuff*/ +#include <netinet/in.h> +#include <arpa/inet.h> +/* TCP options (nagle algorithm disable) */ +#include <netinet/tcp.h> + +namespace qt3ds { +namespace foundation { + namespace socketimpl { + // Functions take from lua socket implementation. Note that it has an MIT license. + + enum { + IO_DONE = 0, /* operation completed successfully */ + IO_TIMEOUT = -1, /* operation timed out */ + IO_CLOSED = -2, /* the connection has been closed */ + IO_UNKNOWN = -3 + }; + + /*-------------------------------------------------------------------------*\ + * I/O error strings + \*-------------------------------------------------------------------------*/ + const char *io_strerror(int err) + { + switch (err) { + case IO_DONE: + return NULL; + case IO_CLOSED: + return "closed"; + case IO_TIMEOUT: + return "timeout"; + default: + return "unknown error"; + } + } + + typedef int t_socket; + typedef t_socket *p_socket; + typedef struct sockaddr SA; + +#define SOCKET_INVALID (-1) + +#define WAITFD_R 1 +#define WAITFD_W 2 +#define WAITFD_C (WAITFD_R | WAITFD_W) + + int socket_waitfd(p_socket ps, int sw, QT3DSU32 tm) + { + int ret; + fd_set rfds, wfds, *rp, *wp; + struct timeval tv, *tp = NULL; + if (tm == 0) + return IO_TIMEOUT; /* optimize timeout == 0 case */ + if (tm < QT3DS_MAX_U32) { + // shoule consider max timeout specially by setting tp = NULL + // or you get invalid argument errno = 22 + tv.tv_sec = (int)(tm / 1000); + QT3DSU32 leftover = tm % 1000; + tv.tv_usec = (int)(leftover * 100000); + tp = &tv; + } + do { + /* must set bits within loop, because select may have modifed them */ + rp = wp = NULL; + if (sw & WAITFD_R) { + FD_ZERO(&rfds); + FD_SET(*ps, &rfds); + rp = &rfds; + } + if (sw & WAITFD_W) { + FD_ZERO(&wfds); + FD_SET(*ps, &wfds); + wp = &wfds; + } + ret = select(*ps + 1, rp, wp, NULL, tp); + } while (ret == -1 && errno == EINTR); + if (ret == -1) + return errno; + if (ret == 0) + return IO_TIMEOUT; + if (sw == WAITFD_C && FD_ISSET(*ps, &rfds)) + return IO_CLOSED; + return IO_DONE; + } + + /*-------------------------------------------------------------------------*\ + * Initializes module + \*-------------------------------------------------------------------------*/ + int socket_open(void) + { + /* instals a handler to ignore sigpipe or it will crash us */ + signal(SIGPIPE, SIG_IGN); + return 1; + } + + /*-------------------------------------------------------------------------*\ + * Close module + \*-------------------------------------------------------------------------*/ + int socket_close(void) { return 1; } + + /*-------------------------------------------------------------------------*\ + * Put socket into blocking mode + \*-------------------------------------------------------------------------*/ + void socket_setblocking(p_socket ps) + { + int flags = fcntl(*ps, F_GETFL, 0); + flags &= (~(O_NONBLOCK)); + fcntl(*ps, F_SETFL, flags); + } + + /*-------------------------------------------------------------------------*\ + * Put socket into non-blocking mode + \*-------------------------------------------------------------------------*/ + void socket_setnonblocking(p_socket ps) + { + int flags = fcntl(*ps, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(*ps, F_SETFL, flags); + } + + /*-------------------------------------------------------------------------*\ + * Close and inutilize socket + \*-------------------------------------------------------------------------*/ + void socket_destroy(p_socket ps) + { + if (*ps != SOCKET_INVALID) { + socket_setblocking(ps); + close(*ps); + *ps = SOCKET_INVALID; + } + } + /*-------------------------------------------------------------------------*\ + * + \*-------------------------------------------------------------------------*/ + void socket_shutdown(p_socket ps, int how) + { + socket_setblocking(ps); + shutdown(*ps, how); + socket_setnonblocking(ps); + } + + /*-------------------------------------------------------------------------*\ + * Creates and sets up a socket + \*-------------------------------------------------------------------------*/ + int socket_create(p_socket ps, int domain, int type, int protocol) + { + *ps = socket(domain, type, protocol); + if (*ps != SOCKET_INVALID) + return IO_DONE; + else + return errno; + } + + /*-------------------------------------------------------------------------*\ + * + \*-------------------------------------------------------------------------*/ + int socket_listen(p_socket ps, int backlog) + { + int err = IO_DONE; + socket_setblocking(ps); + if (listen(*ps, backlog)) + err = errno; + socket_setnonblocking(ps); + return err; + } + + /*-------------------------------------------------------------------------*\ + * Binds or returns error message + \*-------------------------------------------------------------------------*/ + int socket_bind(p_socket ps, SA *addr, socklen_t len) + { + int err = IO_DONE; + socket_setblocking(ps); + if (bind(*ps, addr, len) < 0) + err = errno; + socket_setnonblocking(ps); + return err; + } + + /*-------------------------------------------------------------------------*\ + * Connects or returns error message + \*-------------------------------------------------------------------------*/ + int socket_connect(p_socket ps, SA *addr, socklen_t len, QT3DSU32 tm) + { + int err; + /* avoid calling on closed sockets */ + if (*ps == SOCKET_INVALID) + return IO_CLOSED; + /* call connect until done or failed without being interrupted */ + do + if (connect(*ps, addr, len) == 0) + return IO_DONE; + while ((err = errno) == EINTR); + /* if connection failed immediately, return error code */ + if (err != EINPROGRESS && err != EAGAIN) + return err; + /* zero timeout case optimization */ + if (tm == 0) + return IO_TIMEOUT; + /* wait until we have the result of the connection attempt or timeout */ + err = socket_waitfd(ps, WAITFD_C, tm); + if (err == IO_CLOSED) { + if (recv(*ps, (char *)&err, 0, 0) == 0) + return IO_DONE; + else + return errno; + } else + return err; + } + + /*-------------------------------------------------------------------------*\ + * Accept with timeout + \*-------------------------------------------------------------------------*/ + int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, QT3DSU32 tm) + { + SA daddr; + socklen_t dlen = sizeof(daddr); + if (*ps == SOCKET_INVALID) + return IO_CLOSED; + if (!addr) + addr = &daddr; + if (!len) + len = &dlen; + for (;;) { + int err; + if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) + return IO_DONE; + err = errno; + if (err == EINTR) + continue; + if (err != EAGAIN && err != ECONNABORTED) + return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) + return err; + } + /* can't reach here */ + return IO_UNKNOWN; + } + + /*-------------------------------------------------------------------------*\ + * Send with timeout + \*-------------------------------------------------------------------------*/ + int socket_send(p_socket ps, const char *data, size_t count, size_t *sent, QT3DSU32 tm) + { + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) + return IO_CLOSED; + /* loop until we send something or we give up on error */ + for (;;) { + long put = (long)send(*ps, data, count, 0); + /* if we sent anything, we are done */ + if (put > 0) { + *sent = put; + return IO_DONE; + } + err = errno; + /* send can't really return 0, but EPIPE means the connection was closed */ + if (put == 0 || err == EPIPE) + return IO_CLOSED; + /* we call was interrupted, just try again */ + if (err == EINTR) + continue; + /* if failed fatal reason, report error */ + if (err != EAGAIN) + return err; + /* wait until we can send something or we timeout */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) + return err; + } + /* can't reach here */ + return IO_UNKNOWN; + } + + /*-------------------------------------------------------------------------*\ + * Receive with timeout + \*-------------------------------------------------------------------------*/ + int socket_recv(p_socket ps, char *data, size_t count, size_t *got, QT3DSU32 tm) + { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) + return IO_CLOSED; + for (;;) { + long taken = (long)recv(*ps, data, count, 0); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + err = errno; + if (taken == 0) + return IO_CLOSED; + if (err == EINTR) + continue; + if (err != EAGAIN) + return err; + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) + return err; + } + return IO_UNKNOWN; + } + + int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) + { + *hp = gethostbyaddr(addr, len, AF_INET); + if (*hp) + return IO_DONE; + else if (h_errno) + return h_errno; + else if (errno) + return errno; + else + return IO_UNKNOWN; + } + + int socket_gethostbyname(const char *addr, struct hostent **hp) + { + *hp = gethostbyname(addr); + if (*hp) + return IO_DONE; + else if (h_errno) + return h_errno; + else if (errno) + return errno; + else + return IO_UNKNOWN; + } + + /*-------------------------------------------------------------------------*\ + * Error translation functions + * Make sure important error messages are standard + \*-------------------------------------------------------------------------*/ + const char *socket_hoststrerror(int err) + { + if (err <= 0) + return io_strerror(err); + switch (err) { + case HOST_NOT_FOUND: + return "host not found"; + default: + return hstrerror(err); + } + } + + const char *socket_strerror(int err) + { + if (err <= 0) + return io_strerror(err); + switch (err) { + case EADDRINUSE: + return "address already in use"; + case EISCONN: + return "already connected"; + case EACCES: + return "permission denied"; + case ECONNREFUSED: + return "connection refused"; + case ECONNABORTED: + return "closed"; + case ECONNRESET: + return "closed"; + case ETIMEDOUT: + return "timeout"; + default: + return strerror(errno); + } + } + } +} +} + +#endif diff --git a/src/foundation/linux/qt_attribution.json b/src/foundation/linux/qt_attribution.json new file mode 100644 index 0000000..3c3bed6 --- /dev/null +++ b/src/foundation/linux/qt_attribution.json @@ -0,0 +1,10 @@ +{ + "Id": "qt3dslinuxinlineaos", + "Name": "Qt3DSLinuxInlineAoS", + "QDocModule": "qt3dstudio", + "QtUsage": "Used by Qt3DStudio, Runtime component.", + + "License": "Other", + "LicenseFile": "LICENSE.TXT", + "Copyright": "Copyright (c) 2001 Intel Corporation." +} diff --git a/src/foundation/macos/Qt3DSUnixAtomic.cpp b/src/foundation/macos/Qt3DSUnixAtomic.cpp new file mode 100644 index 0000000..c1ac396 --- /dev/null +++ b/src/foundation/macos/Qt3DSUnixAtomic.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2001-2004 NovodeX AG. +** Copyright (C) 2004-2008 AGEIA Technologies, Inc. +** Copyright (C) 2008-2013 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSAtomic.h" + +#define PAUSE() asm("nop") + +namespace qt3ds { +namespace foundation { + + void *atomicCompareExchangePointer(volatile void **dest, void *exch, void *comp) + { + return __sync_val_compare_and_swap((void **)dest, comp, exch); + } + + QT3DSI32 atomicCompareExchange(volatile QT3DSI32 *dest, QT3DSI32 exch, QT3DSI32 comp) + { + return __sync_val_compare_and_swap(dest, comp, exch); + } + + QT3DSI32 atomicIncrement(volatile QT3DSI32 *val) { return __sync_add_and_fetch(val, 1); } + + QT3DSI32 atomicDecrement(volatile QT3DSI32 *val) { return __sync_sub_and_fetch(val, 1); } + + QT3DSI32 atomicAdd(volatile QT3DSI32 *val, QT3DSI32 delta) { return __sync_add_and_fetch(val, delta); } + + QT3DSI32 atomicMax(volatile QT3DSI32 *val, QT3DSI32 val2) + { + QT3DSI32 oldVal, newVal; + + do { + PAUSE(); + oldVal = *val; + + if (val2 > oldVal) + newVal = val2; + else + newVal = oldVal; + + } while (atomicCompareExchange(val, newVal, oldVal) != oldVal); + + return *val; + } + + QT3DSI32 atomicExchange(volatile QT3DSI32 *val, QT3DSI32 val2) + { + QT3DSI32 newVal, oldVal; + + do { + PAUSE(); + oldVal = *val; + newVal = val2; + } while (atomicCompareExchange(val, newVal, oldVal) != oldVal); + + return oldVal; + } + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/macos/Qt3DSUnixFPU.cpp b/src/foundation/macos/Qt3DSUnixFPU.cpp new file mode 100644 index 0000000..59c9850 --- /dev/null +++ b/src/foundation/macos/Qt3DSUnixFPU.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2001-2004 NovodeX AG. +** Copyright (C) 2004-2008 AGEIA Technologies, Inc. +** Copyright (C) 2008-2013 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DSFPU.h" +#include <fenv.h> + +#if !(defined(__CYGWIN__) || defined(PX_ANDROID)) +QT3DS_COMPILE_TIME_ASSERT(8 * sizeof(qt3ds::QT3DSU32) >= sizeof(fenv_t)); +#endif + +qt3ds::foundation::FPUGuard::FPUGuard() +{ +#if defined(__CYGWIN__) +#pragma message "FPUGuard::FPUGuard() is not implemented" +#elif defined(PX_ANDROID) +// not supported unless ARM_HARD_FLOAT is enabled. +#else + fegetenv(reinterpret_cast<fenv_t *>(mControlWords)); + fesetenv(FE_DFL_ENV); +#endif +} + +qt3ds::foundation::FPUGuard::~FPUGuard() +{ +#if defined(__CYGWIN__) +#pragma message "FPUGuard::~FPUGuard() is not implemented" +#elif defined(PX_ANDROID) +// not supported unless ARM_HARD_FLOAT is enabled. +#else + fesetenv(reinterpret_cast<fenv_t *>(mControlWords)); +#endif +} diff --git a/src/foundation/macos/Qt3DSUnixMutex.cpp b/src/foundation/macos/Qt3DSUnixMutex.cpp new file mode 100644 index 0000000..bbc5582 --- /dev/null +++ b/src/foundation/macos/Qt3DSUnixMutex.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSMutex.h" +#include "foundation/Qt3DSAssert.h" +#include "foundation/Qt3DSAtomic.h" + +#include <pthread.h> + +namespace qt3ds { +namespace foundation { + + namespace { + pthread_mutex_t *getMutex(MutexImpl *impl) + { + return reinterpret_cast<pthread_mutex_t *>(impl); + } + } + + MutexImpl::MutexImpl() + { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(getMutex(this), &attr); + pthread_mutexattr_destroy(&attr); + } + + MutexImpl::~MutexImpl() { pthread_mutex_destroy(getMutex(this)); } + + bool MutexImpl::lock() { return !pthread_mutex_lock(getMutex(this)); } + + bool MutexImpl::trylock() { return !pthread_mutex_trylock(getMutex(this)); } + + bool MutexImpl::unlock() { return !pthread_mutex_unlock(getMutex(this)); } + + const QT3DSU32 MutexImpl::size = sizeof(pthread_mutex_t); + + class ReadWriteLockImpl + { + public: + ReadWriteLockImpl(NVAllocatorCallback &alloc) + : mAllocator(alloc) + { + } + NVAllocatorCallback &mAllocator; + pthread_mutex_t mutex; + volatile int readerCounter; + }; + + ReadWriteLock::ReadWriteLock(NVAllocatorCallback &alloc) + { + mImpl = QT3DS_NEW(alloc, ReadWriteLockImpl)(alloc); + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mImpl->mutex, &attr); + pthread_mutexattr_destroy(&attr); + + mImpl->readerCounter = 0; + } + + ReadWriteLock::~ReadWriteLock() + { + pthread_mutex_destroy(&mImpl->mutex); + QT3DS_FREE(mImpl->mAllocator, mImpl); + } + + void ReadWriteLock::lockReader() + { + pthread_mutex_lock(&mImpl->mutex); + + atomicIncrement(&mImpl->readerCounter); + + pthread_mutex_unlock(&mImpl->mutex); + } + + void ReadWriteLock::lockWriter() + { + pthread_mutex_lock(&mImpl->mutex); + + while (mImpl->readerCounter != 0) + ; + } + + void ReadWriteLock::unlockReader() { atomicDecrement(&mImpl->readerCounter); } + + void ReadWriteLock::unlockWriter() { pthread_mutex_unlock(&mImpl->mutex); } + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/macos/Qt3DSUnixSemaphore.cpp b/src/foundation/macos/Qt3DSUnixSemaphore.cpp new file mode 100644 index 0000000..90aab79 --- /dev/null +++ b/src/foundation/macos/Qt3DSUnixSemaphore.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSSemaphore.h" +#include "foundation/Qt3DSAssert.h" +#include "foundation/Qt3DSAllocator.h" +#include "foundation/Qt3DSAllocatorCallback.h" + +#include <errno.h> +#include <stdio.h> +#include <pthread.h> +#include <time.h> +#include <sys/time.h> + +namespace qt3ds { +namespace foundation { + + class SemaphoreImpl + { + public: + SemaphoreImpl(NVAllocatorCallback &alloc) + : mAllocator(alloc) + { + } + NVAllocatorCallback &mAllocator; + pthread_mutex_t mutex; + pthread_cond_t cond; + QT3DSU32 count; + QT3DSU32 maxCount; + }; + + struct NVLinuxScopeLock + { + NVLinuxScopeLock(pthread_mutex_t &m) + : mMutex(m) + { + pthread_mutex_lock(&mMutex); + } + + ~NVLinuxScopeLock() { pthread_mutex_unlock(&mMutex); } + private: + pthread_mutex_t &mMutex; + }; + + Semaphore::Semaphore(NVAllocatorCallback &alloc, QT3DSU32 initialCount, QT3DSU32 maxCount) + { + mImpl = QT3DS_NEW(alloc, SemaphoreImpl)(alloc); + int status = pthread_mutex_init(&mImpl->mutex, 0); + QT3DS_ASSERT(!status); + status = pthread_cond_init(&mImpl->cond, 0); + QT3DS_ASSERT(!status); + mImpl->count = initialCount; + mImpl->maxCount = maxCount; + QT3DS_ASSERT(initialCount <= maxCount); + } + + Semaphore::~Semaphore() + { + pthread_cond_destroy(&mImpl->cond); + pthread_mutex_destroy(&mImpl->mutex); + QT3DS_FREE(mImpl->mAllocator, mImpl); + } + + bool Semaphore::wait(QT3DSU32 milliseconds) + { + NVLinuxScopeLock lock(mImpl->mutex); + + if (mImpl->count > 0) { + mImpl->count--; + return true; + } + + if (milliseconds == 0) { + return false; + } + + if (milliseconds == QT3DSU32(-1)) { + int status = pthread_cond_wait(&mImpl->cond, &mImpl->mutex); + QT3DS_ASSERT(!status); + (void)status; + } else { + timespec ts; + timeval tp; + gettimeofday(&tp, NULL); + QT3DSU32 sec = milliseconds / 1000; + QT3DSU32 usec = (milliseconds - 1000 * sec) * 1000; + + // sschirm: taking into account that us might accumulate to a second + // otherwise the pthread_cond_timedwait complains on osx. + usec = tp.tv_usec + usec; + QT3DSU32 div_sec = usec / 1000000; + QT3DSU32 rem_usec = usec - div_sec * 1000000; + + ts.tv_sec = tp.tv_sec + sec + div_sec; + ts.tv_nsec = rem_usec * 1000; + + int ierr = pthread_cond_timedwait(&mImpl->cond, &mImpl->mutex, &ts); + QT3DS_ASSERT((ierr == 0) || (ierr == ETIMEDOUT)); + (void)ierr; + return false; + } + + return true; + } + + void Semaphore::post() + { + NVLinuxScopeLock lock(mImpl->mutex); + mImpl->count++; + if (mImpl->count > mImpl->maxCount) + mImpl->count = mImpl->maxCount; + else { + pthread_cond_broadcast(&mImpl->cond); + } + } + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/macos/Qt3DSUnixSync.cpp b/src/foundation/macos/Qt3DSUnixSync.cpp new file mode 100644 index 0000000..52f9d7a --- /dev/null +++ b/src/foundation/macos/Qt3DSUnixSync.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSSync.h" +#include "foundation/Qt3DSAssert.h" +#include "foundation/Qt3DSAllocator.h" +#include "foundation/Qt3DSAllocatorCallback.h" + +#include <errno.h> +#include <stdio.h> +#include <pthread.h> +#include <time.h> +#include <sys/time.h> + +namespace qt3ds { +namespace foundation { + + class SyncImpl + { + public: + SyncImpl(NVAllocatorCallback &alloc) + : mAllocator(alloc) + { + } + NVAllocatorCallback &mAllocator; + pthread_mutex_t mutex; + pthread_cond_t cond; + volatile bool is_set; + }; + + struct NVLinuxScopeLock + { + NVLinuxScopeLock(pthread_mutex_t &m) + : mMutex(m) + { + pthread_mutex_lock(&mMutex); + } + + ~NVLinuxScopeLock() { pthread_mutex_unlock(&mMutex); } + private: + pthread_mutex_t &mMutex; + }; + + Sync::Sync(NVAllocatorCallback &alloc) + { + mImpl = QT3DS_NEW(alloc, SyncImpl)(alloc); + int status = pthread_mutex_init(&mImpl->mutex, 0); + QT3DS_ASSERT(!status); + status = pthread_cond_init(&mImpl->cond, 0); + QT3DS_ASSERT(!status); + mImpl->is_set = false; + } + + Sync::~Sync() + { + pthread_cond_destroy(&mImpl->cond); + pthread_mutex_destroy(&mImpl->mutex); + QT3DS_FREE(mImpl->mAllocator, mImpl); + } + + void Sync::reset() + { + NVLinuxScopeLock lock(mImpl->mutex); + mImpl->is_set = false; + } + + void Sync::set() + { + NVLinuxScopeLock lock(mImpl->mutex); + if (!mImpl->is_set) { + mImpl->is_set = true; + pthread_cond_broadcast(&mImpl->cond); + } + } + + bool Sync::wait(QT3DSU32 ms) + { + NVLinuxScopeLock lock(mImpl->mutex); + if (!mImpl->is_set) { + if (ms == QT3DSU32(-1)) { + int status = pthread_cond_wait(&mImpl->cond, &mImpl->mutex); + QT3DS_ASSERT(!status); + (void)status; + } else { + timespec ts; + timeval tp; + gettimeofday(&tp, NULL); + QT3DSU32 sec = ms / 1000; + QT3DSU32 usec = (ms - 1000 * sec) * 1000; + + // sschirm: taking into account that us might accumulate to a second + // otherwise the pthread_cond_timedwait complains on osx. + usec = tp.tv_usec + usec; + QT3DSU32 div_sec = usec / 1000000; + QT3DSU32 rem_usec = usec - div_sec * 1000000; + + ts.tv_sec = tp.tv_sec + sec + div_sec; + ts.tv_nsec = rem_usec * 1000; + + int ierr = pthread_cond_timedwait(&mImpl->cond, &mImpl->mutex, &ts); + QT3DS_ASSERT((ierr == 0) || (ierr == ETIMEDOUT)); + (void)ierr; + } + } + return mImpl->is_set; + } + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/macos/Qt3DSUnixTime.cpp b/src/foundation/macos/Qt3DSUnixTime.cpp new file mode 100644 index 0000000..58eadcb --- /dev/null +++ b/src/foundation/macos/Qt3DSUnixTime.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2001-2004 NovodeX AG. +** Copyright (C) 2004-2008 AGEIA Technologies, Inc. +** Copyright (C) 2008-2013 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DS.h" +#include "foundation/Qt3DSTime.h" + +#include <time.h> +#include <sys/time.h> + +#if defined QT3DS_APPLE +#include <mach/mach_time.h> +#endif + +// Use real-time high-precision timer. +#ifndef QT3DS_APPLE +#define CLOCKID CLOCK_REALTIME +#endif + +namespace qt3ds { +namespace foundation { + + const CounterFrequencyToTensOfNanos Time::sCounterFreq = Time::getCounterFrequency(); + + static Time::Second getTimeSeconds() + { + static struct timeval _tv; + gettimeofday(&_tv, NULL); + return double(_tv.tv_sec) + double(_tv.tv_usec) * 0.000001; + } + + Time::Time() { mLastTime = getTimeSeconds(); } + + Time::Second Time::getElapsedSeconds() + { + Time::Second curTime = getTimeSeconds(); + Time::Second diff = curTime - mLastTime; + mLastTime = curTime; + return diff; + } + + Time::Second Time::peekElapsedSeconds() + { + Time::Second curTime = getTimeSeconds(); + Time::Second diff = curTime - mLastTime; + return diff; + } + + Time::Second Time::getLastTime() const { return mLastTime; } + +#ifdef QT3DS_APPLE + CounterFrequencyToTensOfNanos Time::getCounterFrequency() + { + mach_timebase_info_data_t info; + mach_timebase_info(&info); + // mach_absolute_time * (info.numer/info.denom) is in units of nano seconds + return CounterFrequencyToTensOfNanos(info.numer, info.denom * 10); + } + + QT3DSU64 Time::getCurrentCounterValue() { return mach_absolute_time(); } + +#else + + CounterFrequencyToTensOfNanos Time::getCounterFrequency() + { + return CounterFrequencyToTensOfNanos(1, 10); + } + + PxU64 Time::getCurrentCounterValue() + { + struct timespec mCurrTimeInt; + clock_gettime(CLOCKID, &mCurrTimeInt); + // Convert to nanos as this doesn't cause a large divide here + return (static_cast<PxU64>(mCurrTimeInt.tv_sec) * 1000000000) + + (static_cast<PxU64>(mCurrTimeInt.tv_nsec)); + } +#endif + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/qt/formatdiscovery.cpp b/src/foundation/qt/formatdiscovery.cpp new file mode 100644 index 0000000..7ea762b --- /dev/null +++ b/src/foundation/qt/formatdiscovery.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/qopenglcontext.h> +#include <QtGui/qoffscreensurface.h> +#include <QtGui/qopenglfunctions.h> +#include <QtGui/qopengltexture.h> + +#include "qstudio3dglobal.h" +#include "Qt3DSFoundation.h" + +QT_BEGIN_NAMESPACE + +namespace Q3DS { + +static QSurfaceFormat findIdealGLVersion() +{ + QSurfaceFormat fmt; + fmt.setProfile(QSurfaceFormat::CoreProfile); + + // Advanced: Try 4.3 core (so we get compute shaders for instance) + fmt.setVersion(4, 3); + QOpenGLContext ctx; + ctx.setFormat(fmt); + if (ctx.create() && ctx.format().version() >= qMakePair(4, 3)) { + qDebug("Requesting OpenGL 4.3 core context succeeded"); + return ctx.format(); + } + + // Basic: Stick with 3.3 for now to keep less fortunate, Mesa-based systems happy + fmt.setVersion(3, 3); + ctx.setFormat(fmt); + if (ctx.create() && ctx.format().version() >= qMakePair(3, 3)) { + qDebug("Requesting OpenGL 3.3 core context succeeded"); + return ctx.format(); + } + + qWarning("Failed to get OpenGL 3.3 or OpenGL 4.3 context!"); + return fmt; +} + +static QSurfaceFormat findIdealGLESVersion() +{ + QSurfaceFormat fmt; + + // Advanced: Try 3.1 (so we get compute shaders for instance) + fmt.setVersion(3, 1); + QOpenGLContext ctx; + ctx.setFormat(fmt); + + // Now, it's important to check the format with the actual version (parsed + // back from GL_VERSION) since some implementations, ANGLE for instance, + // are broken and succeed the 3.1 context request even though they only + // support and return a 3.0 context. This is against the spec since 3.0 is + // obviously not backwards compatible with 3.1, but hey... + if (ctx.create() && ctx.format().version() >= qMakePair(3, 1)) { + qDebug("Requesting OpenGL ES 3.1 context succeeded"); + return ctx.format(); + } + + // Basic: OpenGL ES 3.0 is a hard requirement at the moment since we can + // only generate 300 ES shaders, uniform buffers are mandatory. + fmt.setVersion(3, 0); + ctx.setFormat(fmt); + if (ctx.create() && ctx.format().version() >= qMakePair(3, 0)) { + qDebug("Requesting OpenGL ES 3.0 context succeeded"); + return ctx.format(); + } + + fmt.setVersion(2, 0); + ctx.setFormat(fmt); + if (ctx.create()) { + qDebug("Requesting OpenGL ES 2.0 context succeeded"); + return fmt; + } + + qWarning("Failed to get OpenGL ES 2.0, 3.0 or 3.1 context"); + return fmt; +} + +QSurfaceFormat surfaceFormat() +{ + static const QSurfaceFormat f = [] { + QSurfaceFormat fmt; + // works in dynamic gl builds too because there's a qguiapp already + // this requirement is also a problem, see QT3DS-3603 + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) + fmt = findIdealGLVersion(); + else + fmt = findIdealGLESVersion(); + fmt.setDepthBufferSize(24); + fmt.setStencilBufferSize(8); + // Ignore MSAA here as that is a per-layer setting. + return fmt; + }(); + return f; +} +} // End namespace Q3DS + +QT_END_NAMESPACE diff --git a/src/foundation/qt_attribution.json b/src/foundation/qt_attribution.json new file mode 100644 index 0000000..537d8be --- /dev/null +++ b/src/foundation/qt_attribution.json @@ -0,0 +1,22 @@ +{ + "Id": "convertutf", + "Name": "ConvertUTF", + "QDocModule": "qt3dstudio", + "QtUsage": "Used by Qt3DStudio, Studio and Runtime components.", + + "License": "Other", + "LicenseFile": "LICENSE_CONVERTUTF.TXT", + "Copyright": "Copyright 2001-2004 Unicode, Inc." +} +, +{ + "Id": "socket", + "Name": "Socket", + "QDocModule": "qt3dstudio", + "QtUsage": "Used by Qt3DStudio, Runtime component.", + + "License": "MIT license", + "LicenseId": "MIT", + "LicenseFile": "LICENCE_SOCKET.TXT", + "Copyright": "Copyright (C) 2004-2013 Diego Nehab." +} diff --git a/src/foundation/windows/LICENSE.TXT b/src/foundation/windows/LICENSE.TXT new file mode 100644 index 0000000..f40caef --- /dev/null +++ b/src/foundation/windows/LICENSE.TXT @@ -0,0 +1,7 @@ + Copyright (c) 2001 Intel Corporation. + +Permition is granted to use, copy, distribute and prepare derivative works +of this library for any purpose and without fee, provided, that the above +copyright notice and this statement appear in all copies. +Intel makes no representations about the suitability of this library for +any purpose, and specifically disclaims all warranties. diff --git a/src/foundation/windows/Qt3DSWindowsAoS.h b/src/foundation/windows/Qt3DSWindowsAoS.h new file mode 100644 index 0000000..c654ec7 --- /dev/null +++ b/src/foundation/windows/Qt3DSWindowsAoS.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_WINDOWS_AOS_H +#define QT3DS_WINDOWS_AOS_H + +// no includes here! this file should be included from NVcVecMath.h only!!! + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +typedef __m128 FloatV; +typedef __m128 Vec3V; +typedef __m128 Vec4V; +typedef __m128 BoolV; +typedef __m128 VecU32V; +typedef __m128 VecI32V; +typedef __m128 VecU16V; +typedef __m128 VecI16V; +typedef __m128 VecU8V; +typedef __m128 QuatV; + +#define FloatVArg FloatV & +#define Vec3VArg Vec3V & +#define Vec4VArg Vec4V & +#define BoolVArg BoolV & +#define VecU32VArg VecU32V & +#define VecI32VArg VecI32V & +#define VecU16VArg VecU16V & +#define VecI16VArg VecI16V & +#define VecU8VArg VecU8V & +#define QuatVArg QuatV & + +QT3DS_ALIGN_PREFIX(16) +struct Mat33V +{ + Mat33V() {} + Mat33V(const Vec3V &c0, const Vec3V &c1, const Vec3V &c2) + : col0(c0) + , col1(c1) + , col2(c2) + { + } + Vec3V QT3DS_ALIGN(16, col0); + Vec3V QT3DS_ALIGN(16, col1); + Vec3V QT3DS_ALIGN(16, col2); +} QT3DS_ALIGN_SUFFIX(16); + +QT3DS_ALIGN_PREFIX(16) +struct Mat34V +{ + Mat34V() {} + Mat34V(const Vec3V &c0, const Vec3V &c1, const Vec3V &c2, const Vec3V &c3) + : col0(c0) + , col1(c1) + , col2(c2) + , col3(c3) + { + } + Vec3V QT3DS_ALIGN(16, col0); + Vec3V QT3DS_ALIGN(16, col1); + Vec3V QT3DS_ALIGN(16, col2); + Vec3V QT3DS_ALIGN(16, col3); +} QT3DS_ALIGN_SUFFIX(16); + +QT3DS_ALIGN_PREFIX(16) +struct Mat43V +{ + Mat43V() {} + Mat43V(const Vec4V &c0, const Vec4V &c1, const Vec4V &c2) + : col0(c0) + , col1(c1) + , col2(c2) + { + } + Vec4V QT3DS_ALIGN(16, col0); + Vec4V QT3DS_ALIGN(16, col1); + Vec4V QT3DS_ALIGN(16, col2); +} QT3DS_ALIGN_SUFFIX(16); + +QT3DS_ALIGN_PREFIX(16) +struct Mat44V +{ + Mat44V() {} + Mat44V(const Vec4V &c0, const Vec4V &c1, const Vec4V &c2, const Vec4V &c3) + : col0(c0) + , col1(c1) + , col2(c2) + , col3(c3) + { + } + Vec4V QT3DS_ALIGN(16, col0); + Vec4V QT3DS_ALIGN(16, col1); + Vec4V QT3DS_ALIGN(16, col2); + Vec4V QT3DS_ALIGN(16, col3); +} QT3DS_ALIGN_SUFFIX(16); + +#endif // QT3DS_WINDOWS_AOS_H diff --git a/src/foundation/windows/Qt3DSWindowsAtomic.cpp b/src/foundation/windows/Qt3DSWindowsAtomic.cpp new file mode 100644 index 0000000..4f95d3d --- /dev/null +++ b/src/foundation/windows/Qt3DSWindowsAtomic.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/windows/Qt3DSWindowsInclude.h" +#include "foundation/Qt3DSAtomic.h" + +namespace qt3ds { +namespace foundation { + + QT3DSI32 atomicExchange(volatile QT3DSI32 *val, QT3DSI32 val2) + { + return (QT3DSI32)InterlockedExchange((volatile LONG *)val, (LONG)val2); + } + + QT3DSI32 atomicCompareExchange(volatile QT3DSI32 *dest, QT3DSI32 exch, QT3DSI32 comp) + { + return (QT3DSI32)InterlockedCompareExchange((volatile LONG *)dest, exch, comp); + } + + void *atomicCompareExchangePointer(volatile void **dest, void *exch, void *comp) + { + return InterlockedCompareExchangePointer((volatile PVOID *)dest, exch, comp); + } + + QT3DSI32 atomicIncrement(volatile QT3DSI32 *val) + { + return (QT3DSI32)InterlockedIncrement((volatile LONG *)val); + } + + QT3DSI32 atomicDecrement(volatile QT3DSI32 *val) + { + return (QT3DSI32)InterlockedDecrement((volatile LONG *)val); + } + + QT3DSI32 atomicAdd(volatile QT3DSI32 *val, QT3DSI32 delta) + { + LONG newValue, oldValue; + do { + oldValue = *val; + newValue = oldValue + delta; + } while (InterlockedCompareExchange((volatile LONG *)val, newValue, oldValue) != oldValue); + + return newValue; + } + + QT3DSI32 atomicMax(volatile QT3DSI32 *val, QT3DSI32 val2) + { + // Could do this more efficiently in asm... + + LONG newValue, oldValue; + + do { + oldValue = *val; + + if (val2 > oldValue) + newValue = val2; + else + newValue = oldValue; + + } while (InterlockedCompareExchange((volatile LONG *)val, newValue, oldValue) != oldValue); + + return newValue; + } + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/windows/Qt3DSWindowsFPU.cpp b/src/foundation/windows/Qt3DSWindowsFPU.cpp new file mode 100644 index 0000000..4724280 --- /dev/null +++ b/src/foundation/windows/Qt3DSWindowsFPU.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "foundation/Qt3DSFPU.h" +#include "float.h" + +#ifdef QT3DS_X64 +#define _MCW_ALL _MCW_DN | _MCW_EM | _MCW_RC +#else +#define _MCW_ALL _MCW_DN | _MCW_EM | _MCW_IC | _MCW_RC | _MCW_PC +#endif + +qt3ds::foundation::FPUGuard::FPUGuard() +{ +// default plus FTZ and DAZ +#if defined(QT3DS_WINDOWS) && defined(QT3DS_VC) +#ifdef QT3DS_X64 + _controlfp_s(mControlWords, _CW_DEFAULT | _DN_FLUSH, _MCW_ALL); +#else + __control87_2(_CW_DEFAULT | _DN_FLUSH, _MCW_ALL, mControlWords, mControlWords + 1); +#endif +#endif +} + +qt3ds::foundation::FPUGuard::~FPUGuard() +{ +#if defined(QT3DS_WINDOWS) && defined(QT3DS_VC) +#ifdef QT3DS_X64 + _controlfp_s(mControlWords, *mControlWords, _MCW_ALL); +#else + __control87_2(mControlWords[0], _MCW_ALL, mControlWords, 0); + __control87_2(mControlWords[1], _MCW_ALL, 0, mControlWords + 1); +#endif +#endif +} diff --git a/src/foundation/windows/Qt3DSWindowsFile.h b/src/foundation/windows/Qt3DSWindowsFile.h new file mode 100644 index 0000000..6c232a5 --- /dev/null +++ b/src/foundation/windows/Qt3DSWindowsFile.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_WINDOWS_FILE_H +#define QT3DS_FOUNDATION_QT3DS_WINDOWS_FILE_H + +#include "foundation/Qt3DS.h" + +#include <stdio.h> + +namespace qt3ds { +namespace foundation { + QT3DS_INLINE errno_t fopen_s(FILE **_File, const char *_Filename, const char *_Mode) + { + return ::fopen_s(_File, _Filename, _Mode); + }; + +} // namespace foundation +} // namespace qt3ds + +#endif diff --git a/src/foundation/windows/Qt3DSWindowsInclude.h b/src/foundation/windows/Qt3DSWindowsInclude.h new file mode 100644 index 0000000..f01c9f1 --- /dev/null +++ b/src/foundation/windows/Qt3DSWindowsInclude.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WINDOWSINCLUDE_H +#define WINDOWSINCLUDE_H + +#include "foundation/Qt3DS.h" + +#ifndef _WIN32 +#error "This file should only be included by Windows builds!!" +#endif + +#ifdef _WINDOWS_ // windows already included +#error "Only include windows.h through this file!!" +#endif + +// We only support >= Windows XP, and we need this for critical section and +#define _WIN32_WINNT 0x0500 + +// turn off as much as we can for windows. All we really need is the thread functions(critical +// sections/Interlocked* etc) +#define NOGDICAPMASKS +#define NOVIRTUALKEYCODES +#define NOWINMESSAGES +#define NOWINSTYLES +#define NOSYSMETRICS +#define NOMENUS +#define NOICONS +#define NOKEYSTATES +#define NOSYSCOMMANDS +#define NORASTEROPS +#define NOSHOWWINDOW +#define NOATOM +#define NOCLIPBOARD +#define NOCOLOR +#define NOCTLMGR +#define NODRAWTEXT +#define NOGDI +#define NOUSER +#define NONLS +#define NOMB +#define NOMEMMGR +#define NOMETAFILE +#define NOMSG +#define NOOPENFILE +#define NOSCROLL +#define NOSERVICE +#define NOSOUND +#define NOTEXTMETRIC +#define NOWH +#define NOWINOFFSETS +#define NOCOMM +#define NOKANJI +#define NOHELP +#define NOPROFILER +#define NODEFERWINDOWPOS +#define NOMCX +#define WIN32_LEAN_AND_MEAN + +#include <windows.h> + +#ifdef QT3DS_SUPPORT_SSE +#include <xmmintrin.h> +#endif + +#endif diff --git a/src/foundation/windows/Qt3DSWindowsInlineAoS.h b/src/foundation/windows/Qt3DSWindowsInlineAoS.h new file mode 100644 index 0000000..60c5789 --- /dev/null +++ b/src/foundation/windows/Qt3DSWindowsInlineAoS.h @@ -0,0 +1,2715 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// Copyright (c) 2001 Intel Corporation. +// +// Permition is granted to use, copy, distribute and prepare derivative works +// of this library for any purpose and without fee, provided, that the above +// copyright notice and this statement appear in all copies. +// Intel makes no representations about the suitability of this library for +// any purpose, and specifically disclaims all warranties. +// See LEGAL.TXT for all the legal information. +// + +#ifndef QT3DS_WINDOWS_INLINE_AOS_H +#define QT3DS_WINDOWS_INLINE_AOS_H + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +// Remove this define when all platforms use simd solver. +#define QT3DS_SUPPORT_SIMD + +QT3DS_FORCE_INLINE __m128 m128_I2F(__m128i n) +{ + return _mm_castsi128_ps(n); +} +QT3DS_FORCE_INLINE __m128i m128_F2I(__m128 n) +{ + return _mm_castps_si128(n); +} + +QT3DS_FORCE_INLINE QT3DSU32 BAllTrue4_R(const BoolV a) +{ + const QT3DSI32 moveMask = _mm_movemask_ps(a); + return moveMask == (0xf); +} + +QT3DS_FORCE_INLINE QT3DSU32 BAnyTrue4_R(const BoolV a) +{ + const QT3DSI32 moveMask = _mm_movemask_ps(a); + return moveMask != (0x0); +} + +QT3DS_FORCE_INLINE QT3DSU32 BAllTrue3_R(const BoolV a) +{ + const QT3DSI32 moveMask = _mm_movemask_ps(a); + return (moveMask & 0x7) == (0x7); +} + +QT3DS_FORCE_INLINE QT3DSU32 BAnyTrue3_R(const BoolV a) +{ + const QT3DSI32 moveMask = _mm_movemask_ps(a); + return (moveMask & 0x7) != (0x0); +} + +///////////////////////////////////////////////////////////////////// +////FUNCTIONS USED ONLY FOR ASSERTS IN VECTORISED IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +QT3DS_FORCE_INLINE QT3DSU32 FiniteTestEq(const Vec4V a, const Vec4V b) +{ + // This is a bit of a bodge. + //_mm_comieq_ss returns 1 if either value is nan so we need to re-cast a and b with true encoded + //as a non-nan number. + // There must be a better way of doing this in sse. + const BoolV one = FOne(); + const BoolV zero = FZero(); + const BoolV a1 = V4Sel(a, one, zero); + const BoolV b1 = V4Sel(b, one, zero); + return (_mm_comieq_ss(a1, b1) && _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(1, 1, 1, 1)), + _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(1, 1, 1, 1))) + && _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(2, 2, 2, 2)), + _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(2, 2, 2, 2))) + && _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(3, 3, 3, 3)), + _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(3, 3, 3, 3)))); +} + +QT3DS_FORCE_INLINE bool isValidFloatV(const FloatV a) +{ + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))) + && _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))) + && _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)))); +} + +QT3DS_FORCE_INLINE bool isValidVec3V(const Vec3V a) +{ + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)), FZero()) ? true : false); +} + +QT3DS_FORCE_INLINE bool isFiniteFloatV(const FloatV a) +{ + const QT3DSU32 badNumber = (_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF); + const FloatV vBadNum = FloatV_From_F32((QT3DSF32 &)badNumber); + const BoolV vMask = BAnd(vBadNum, a); + return FiniteTestEq(vMask, BFFFF()) == 1; +} + +QT3DS_FORCE_INLINE bool isFiniteVec3V(const Vec3V a) +{ + const QT3DSU32 badNumber = (_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF); + const Vec3V vBadNum = Vec3V_From_F32((QT3DSF32 &)badNumber); + const BoolV vMask = BAnd(BAnd(vBadNum, a), BTTTF()); + return FiniteTestEq(vMask, BFFFF()) == 1; +} + +QT3DS_FORCE_INLINE bool isFiniteVec4V(const Vec4V a) +{ + /*Vec4V a; + QT3DS_ALIGN(16, QT3DSF32 f[4]); + F32Array_Aligned_From_Vec4V(a, f); + return NVIsFinite(f[0]) + && NVIsFinite(f[1]) + && NVIsFinite(f[2]) + && NVIsFinite(f[3]);*/ + + const QT3DSU32 badNumber = (_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF); + const Vec4V vBadNum = Vec4V_From_F32((QT3DSF32 &)badNumber); + const BoolV vMask = BAnd(vBadNum, a); + + return FiniteTestEq(vMask, BFFFF()) == 1; +} + +QT3DS_FORCE_INLINE bool hasZeroElementinFloatV(const FloatV a) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FZero()) ? true : false); +} + +QT3DS_FORCE_INLINE bool hasZeroElementInVec3V(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FZero()) + || _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)), FZero()) + || _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)), FZero())); +} + +QT3DS_FORCE_INLINE bool hasZeroElementInVec4V(const Vec4V a) +{ + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FZero()) + || _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)), FZero()) + || _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)), FZero()) + || _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)), FZero())); +} + +///////////////////////////////////////////////////////////////////// +////VECTORISED FUNCTION IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +QT3DS_FORCE_INLINE FloatV FloatV_From_F32(const QT3DSF32 f) +{ + return (_mm_load1_ps(&f)); +} + +QT3DS_FORCE_INLINE Vec3V Vec3V_From_F32(const QT3DSF32 f) +{ + return _mm_set_ps(0.0f, f, f, f); +} + +QT3DS_FORCE_INLINE Vec4V Vec4V_From_F32(const QT3DSF32 f) +{ + return (_mm_load1_ps(&f)); +} + +QT3DS_FORCE_INLINE BoolV BoolV_From_Bool32(const bool f) +{ + const QT3DSU32 i = -(QT3DSI32)f; + return _mm_load1_ps((float *)&i); +} + +QT3DS_FORCE_INLINE Vec3V Vec3V_From_NVVec3_Aligned(const QT3DSVec3 &f) +{ + VECMATHAOS_ASSERT(0 == ((size_t)&f & 0x0f)); + return (_mm_set_ps(0.0f, f.z, f.y, f.x)); +} + +QT3DS_FORCE_INLINE Vec3V Vec3V_From_NVVec3(const QT3DSVec3 &f) +{ + return (_mm_set_ps(0.0f, f.z, f.y, f.x)); +} + +QT3DS_FORCE_INLINE Vec3V Vec3V_From_NVVec3_WUndefined(const QT3DSVec3 &f) +{ + return (_mm_set_ps(0.0f, f.z, f.y, f.x)); +} + +QT3DS_FORCE_INLINE Vec3V Vec3V_From_Vec4V(Vec4V v) +{ + return V4SetW(v, V4Zero()); +} + +QT3DS_FORCE_INLINE Vec3V Vec3V_From_F32Array_Aligned(const QT3DSF32 *const f) +{ + VECMATHAOS_ASSERT(0 == ((QT3DSU64)f & 0x0f)); + return (_mm_load_ps(f)); +} + +QT3DS_FORCE_INLINE Vec4V Vec4V_From_Vec3V(Vec3V f) +{ + return f; // ok if it is implemented as the same type. +} + +QT3DS_FORCE_INLINE Vec4V Vec4V_From_NVVec3_WUndefined(const QT3DSVec3 &f) +{ + return (_mm_set_ps(0.0f, f.z, f.y, f.x)); +} + +QT3DS_FORCE_INLINE Vec4V Vec4V_From_F32Array_Aligned(const QT3DSF32 *const f) +{ + VECMATHAOS_ASSERT(0 == ((QT3DSU64)f & 0x0f)); + return (_mm_load_ps(f)); +} + +QT3DS_FORCE_INLINE void F32Array_Aligned_From_Vec4V(const Vec4V a, QT3DSF32 *f) +{ + VECMATHAOS_ASSERT(0 == ((QT3DSU64)f & 0x0f)); + _mm_store_ps(f, a); +} + +QT3DS_FORCE_INLINE void NVU32Array_Aligned_From_BoolV(const BoolV a, QT3DSU32 *f) +{ + VECMATHAOS_ASSERT(0 == ((QT3DSU64)f & 0x0f)); + _mm_store_ps((QT3DSF32 *)f, a); +} + +QT3DS_FORCE_INLINE Vec4V Vec4V_From_F32Array(const QT3DSF32 *const f) +{ + return (_mm_loadu_ps(f)); +} + +QT3DS_FORCE_INLINE BoolV BoolV_From_Bool32Array(const bool *const f) +{ + const QT3DS_ALIGN(16, QT3DSU32 b[4]) = { -(QT3DSI32)f[0], -(QT3DSI32)f[1], -(QT3DSI32)f[2], -(QT3DSI32)f[3] }; + return _mm_load1_ps((float *)&b); +} + +QT3DS_FORCE_INLINE QT3DSF32 NVF32_From_FloatV(const FloatV a) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + QT3DSF32 f; + _mm_store_ss(&f, a); + return f; +} + +QT3DS_FORCE_INLINE void NVF32_From_FloatV(const FloatV a, QT3DSF32 *QT3DS_RESTRICT f) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + _mm_store_ss(f, a); +} + +QT3DS_FORCE_INLINE void NVVec3Aligned_From_Vec3V(const Vec3V a, QT3DSVec3 &f) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(0 == ((int)&a & 0x0F)); + VECMATHAOS_ASSERT(0 == ((int)&f & 0x0F)); + QT3DS_ALIGN(16, QT3DSF32 f2[4]); + _mm_store_ps(f2, a); + f = QT3DSVec3(f2[0], f2[1], f2[2]); +} + +QT3DS_FORCE_INLINE void Store_From_BoolV(const BoolV b, QT3DSU32 *b2) +{ + _mm_store_ss((QT3DSF32 *)b2, b); +} + +QT3DS_FORCE_INLINE void NVVec3_From_Vec3V(const Vec3V a, QT3DSVec3 &f) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(0 == ((int)&a & 0x0F)); + QT3DS_ALIGN(16, QT3DSF32 f2[4]); + _mm_store_ps(f2, a); + f = QT3DSVec3(f2[0], f2[1], f2[2]); +} + +QT3DS_FORCE_INLINE Mat33V Mat33V_From_NVMat33(const QT3DSMat33 &m) +{ + return Mat33V(Vec3V_From_NVVec3(m.column0), Vec3V_From_NVVec3(m.column1), + Vec3V_From_NVVec3(m.column2)); +} + +QT3DS_FORCE_INLINE void NVMat33_From_Mat33V(const Mat33V &m, QT3DSMat33 &out) +{ + QT3DS_ASSERT((size_t(&out) & 15) == 0); + NVVec3_From_Vec3V(m.col0, out.column0); + NVVec3_From_Vec3V(m.col1, out.column1); + NVVec3_From_Vec3V(m.col2, out.column2); +} + +QT3DS_FORCE_INLINE bool _VecMathTests::allElementsEqualFloatV(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return (_mm_comieq_ss(a, b) != 0); +} + +QT3DS_FORCE_INLINE bool _VecMathTests::allElementsEqualVec3V(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return V3AllEq(a, b) != 0; +} + +QT3DS_FORCE_INLINE bool _VecMathTests::allElementsEqualVec4V(const Vec4V a, const Vec4V b) +{ + return V4AllEq(a, b) != 0; +} + +QT3DS_FORCE_INLINE bool _VecMathTests::allElementsEqualBoolV(const BoolV a, const BoolV b) +{ + return BAllTrue4_R(VecI32V_IsEq(a, b)) != 0; +} + +#define VECMATH_AOS_EPSILON (1e-3f) + +QT3DS_FORCE_INLINE bool _VecMathTests::allElementsNearEqualFloatV(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + const FloatV c = FSub(a, b); + static const FloatV minError = FloatV_From_F32(-VECMATH_AOS_EPSILON); + static const FloatV maxError = FloatV_From_F32(VECMATH_AOS_EPSILON); + return (_mm_comigt_ss(c, minError) && _mm_comilt_ss(c, maxError)); +} + +QT3DS_FORCE_INLINE bool _VecMathTests::allElementsNearEqualVec3V(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + const Vec3V c = V3Sub(a, b); + static const Vec3V minError = Vec3V_From_F32(-VECMATH_AOS_EPSILON); + static const Vec3V maxError = Vec3V_From_F32(VECMATH_AOS_EPSILON); + return (_mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), minError) + && _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), maxError) + && _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), minError) + && _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), maxError) + && _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), minError) + && _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), maxError)); +} + +QT3DS_FORCE_INLINE bool _VecMathTests::allElementsNearEqualVec4V(const Vec4V a, const Vec4V b) +{ + const Vec4V c = V4Sub(a, b); + static const Vec4V minError = Vec4V_From_F32(-VECMATH_AOS_EPSILON); + static const Vec4V maxError = Vec4V_From_F32(VECMATH_AOS_EPSILON); + return (_mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), minError) + && _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), maxError) + && _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), minError) + && _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), maxError) + && _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), minError) + && _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), maxError) + && _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3)), minError) + && _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3)), maxError)); +} + +////////////////////////////////// +// FLOATV +////////////////////////////////// + +QT3DS_FORCE_INLINE FloatV FZero() +{ + return FloatV_From_F32(0.0f); +} + +QT3DS_FORCE_INLINE FloatV FOne() +{ + return FloatV_From_F32(1.0f); +} + +QT3DS_FORCE_INLINE FloatV FHalf() +{ + return FloatV_From_F32(0.5f); +} + +QT3DS_FORCE_INLINE FloatV FEps() +{ + return FloatV_From_F32(QT3DS_ENV_REAL); +} + +QT3DS_FORCE_INLINE FloatV FEps6() +{ + return FloatV_From_F32(1e-6f); +} + +QT3DS_FORCE_INLINE FloatV FMax() +{ + return FloatV_From_F32(QT3DS_MAX_REAL); +} + +QT3DS_FORCE_INLINE FloatV FNegMax() +{ + return FloatV_From_F32(-QT3DS_MAX_REAL); +} + +QT3DS_FORCE_INLINE FloatV IZero() +{ + const QT3DSU32 zero = 0; + return _mm_load1_ps((QT3DSF32 *)&zero); +} + +QT3DS_FORCE_INLINE FloatV IOne() +{ + const QT3DSU32 one = 1; + return _mm_load1_ps((QT3DSF32 *)&one); +} + +QT3DS_FORCE_INLINE FloatV ITwo() +{ + const QT3DSU32 two = 2; + return _mm_load1_ps((QT3DSF32 *)&two); +} + +QT3DS_FORCE_INLINE FloatV IThree() +{ + const QT3DSU32 three = 3; + return _mm_load1_ps((QT3DSF32 *)&three); +} + +QT3DS_FORCE_INLINE FloatV IFour() +{ + QT3DSU32 four = 4; + return _mm_load1_ps((QT3DSF32 *)&four); +} + +QT3DS_FORCE_INLINE FloatV FNeg(const FloatV f) +{ + VECMATHAOS_ASSERT(isValidFloatV(f)); + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +QT3DS_FORCE_INLINE FloatV FAdd(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_add_ps(a, b); +} + +QT3DS_FORCE_INLINE FloatV FSub(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_sub_ps(a, b); +} + +QT3DS_FORCE_INLINE FloatV FMul(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_mul_ps(a, b); +} + +QT3DS_FORCE_INLINE FloatV FDiv(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_div_ps(a, b); +} + +QT3DS_FORCE_INLINE FloatV FDivFast(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +QT3DS_FORCE_INLINE FloatV FRecip(const FloatV a) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + return _mm_div_ps(FOne(), a); +} + +QT3DS_FORCE_INLINE FloatV FRecipFast(const FloatV a) +{ + return _mm_rcp_ps(a); +} + +QT3DS_FORCE_INLINE FloatV FRsqrt(const FloatV a) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + return _mm_div_ps(FOne(), _mm_sqrt_ps(a)); +} + +QT3DS_FORCE_INLINE FloatV FSqrt(const FloatV a) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + return _mm_sqrt_ps(a); +} + +QT3DS_FORCE_INLINE FloatV FRsqrtFast(const FloatV a) +{ + return _mm_rsqrt_ps(a); +} + +QT3DS_FORCE_INLINE FloatV FScaleAdd(const FloatV a, const FloatV b, const FloatV c) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + VECMATHAOS_ASSERT(isValidFloatV(c)); + return FAdd(FMul(a, b), c); +} + +QT3DS_FORCE_INLINE FloatV FNegScaleSub(const FloatV a, const FloatV b, const FloatV c) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + VECMATHAOS_ASSERT(isValidFloatV(c)); + return FSub(c, FMul(a, b)); +} + +QT3DS_FORCE_INLINE FloatV FAbs(const FloatV a) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + QT3DS_ALIGN(16, const static QT3DSU32 absMask[4]) = { 0x7fFFffFF, 0x7fFFffFF, 0x7fFFffFF, + 0x7fFFffFF }; + return _mm_and_ps(a, _mm_load_ps((QT3DSF32 *)absMask)); +} + +QT3DS_FORCE_INLINE FloatV FSel(const BoolV c, const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(_VecMathTests::allElementsEqualBoolV(c, BTTTT()) + || _VecMathTests::allElementsEqualBoolV(c, BFFFF())); + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +QT3DS_FORCE_INLINE BoolV FIsGrtr(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_cmpgt_ps(a, b); +} + +QT3DS_FORCE_INLINE BoolV FIsGrtrOrEq(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_cmpge_ps(a, b); +} + +QT3DS_FORCE_INLINE BoolV FIsEq(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_cmpeq_ps(a, b); +} + +QT3DS_FORCE_INLINE FloatV FMax(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_max_ps(a, b); +} + +QT3DS_FORCE_INLINE FloatV FMin(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_min_ps(a, b); +} + +QT3DS_FORCE_INLINE FloatV FClamp(const FloatV a, const FloatV minV, const FloatV maxV) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(minV)); + VECMATHAOS_ASSERT(isValidFloatV(maxV)); + return FMax(FMin(a, maxV), minV); +} + +QT3DS_FORCE_INLINE QT3DSU32 FAllGrtr(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return (_mm_comigt_ss(a, b)); +} + +QT3DS_FORCE_INLINE QT3DSU32 FAllGrtrOrEq(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + + return (_mm_comige_ss(a, b)); +} + +QT3DS_FORCE_INLINE QT3DSU32 FAllEq(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + + return (_mm_comieq_ss(a, b)); +} + +QT3DS_FORCE_INLINE FloatV FRound(const FloatV a) +{ + // return _mm_round_ps(a, 0x0); + const Vec3V half = Vec3V_From_F32(0.5f); + const Vec3V aPlusHalf = V3Add(a, half); + __m128i tmp = _mm_cvttps_epi32(aPlusHalf); + return _mm_cvtepi32_ps(tmp); +} + +QT3DS_FORCE_INLINE FloatV FSin(const FloatV a) +{ + // Vec4V V1, V2, V3, V5, V7, V9, V11, V13, V15, V17, V19, V21, V23; + // Vec4V S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11; + FloatV Result; + + // Modulo the range of the given angles such that -XM_PI <= Angles < XM_PI + const FloatV twoPi = Vec4V_From_F32Array_Aligned(g_PXReciprocalTwoPi.f); + const FloatV tmp = FMul(a, twoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegMulSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V3 = FMul(V2, V1); + const FloatV V5 = FMul(V3, V2); + const FloatV V7 = FMul(V5, V2); + const FloatV V9 = FMul(V7, V2); + const FloatV V11 = FMul(V9, V2); + const FloatV V13 = FMul(V11, V2); + const FloatV V15 = FMul(V13, V2); + const FloatV V17 = FMul(V15, V2); + const FloatV V19 = FMul(V17, V2); + const FloatV V21 = FMul(V19, V2); + const FloatV V23 = FMul(V21, V2); + + const Vec4V sinCoefficients0 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Result = FMulAdd(S1, V3, V1); + Result = FMulAdd(S2, V5, Result); + Result = FMulAdd(S3, V7, Result); + Result = FMulAdd(S4, V9, Result); + Result = FMulAdd(S5, V11, Result); + Result = FMulAdd(S6, V13, Result); + Result = FMulAdd(S7, V15, Result); + Result = FMulAdd(S8, V17, Result); + Result = FMulAdd(S9, V19, Result); + Result = FMulAdd(S10, V21, Result); + Result = FMulAdd(S11, V23, Result); + + return Result; +} + +QT3DS_FORCE_INLINE FloatV FCos(const FloatV a) +{ + // XMVECTOR V1, V2, V4, V6, V8, V10, V12, V14, V16, V18, V20, V22; + // XMVECTOR C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11; + FloatV Result; + + // Modulo the range of the given angles such that -XM_PI <= Angles < XM_PI + const FloatV twoPi = Vec4V_From_F32Array_Aligned(g_PXReciprocalTwoPi.f); + const FloatV tmp = FMul(a, twoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegMulSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V4 = FMul(V2, V2); + const FloatV V6 = FMul(V4, V2); + const FloatV V8 = FMul(V4, V4); + const FloatV V10 = FMul(V6, V4); + const FloatV V12 = FMul(V6, V6); + const FloatV V14 = FMul(V8, V6); + const FloatV V16 = FMul(V8, V8); + const FloatV V18 = FMul(V10, V8); + const FloatV V20 = FMul(V10, V10); + const FloatV V22 = FMul(V12, V10); + + const Vec4V cosCoefficients0 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Result = FMulAdd(C1, V2, V4One()); + Result = FMulAdd(C2, V4, Result); + Result = FMulAdd(C3, V6, Result); + Result = FMulAdd(C4, V8, Result); + Result = FMulAdd(C5, V10, Result); + Result = FMulAdd(C6, V12, Result); + Result = FMulAdd(C7, V14, Result); + Result = FMulAdd(C8, V16, Result); + Result = FMulAdd(C9, V18, Result); + Result = FMulAdd(C10, V20, Result); + Result = FMulAdd(C11, V22, Result); + + return Result; +} + +QT3DS_FORCE_INLINE QT3DSU32 FOutOfBounds(const FloatV a, const FloatV min, const FloatV max) +{ + const BoolV ffff = BFFFF(); + const BoolV c = BOr(FIsGrtr(a, max), FIsGrtr(min, a)); + return !BAllEq(c, ffff); +} + +QT3DS_FORCE_INLINE QT3DSU32 FInBounds(const FloatV a, const FloatV min, const FloatV max) +{ + const BoolV tttt = BTTTT(); + const BoolV c = BAnd(FIsGrtrOrEq(a, min), FIsGrtrOrEq(max, a)); + return BAllEq(c, tttt); +} + +QT3DS_FORCE_INLINE QT3DSU32 FOutOfBounds(const FloatV a, const FloatV bounds) +{ + return FOutOfBounds(a, FNeg(bounds), bounds); +} + +QT3DS_FORCE_INLINE QT3DSU32 FInBounds(const FloatV a, const FloatV bounds) +{ + return FInBounds(a, FNeg(bounds), bounds); +} + +////////////////////////////////// +// VEC3V +////////////////////////////////// + +QT3DS_FORCE_INLINE Vec3V V3Splat(const FloatV f) +{ + VECMATHAOS_ASSERT(isValidFloatV(f)); + const __m128 zero = V3Zero(); + const __m128 fff0 = _mm_move_ss(f, zero); + return _mm_shuffle_ps(fff0, fff0, _MM_SHUFFLE(0, 1, 2, 3)); +} + +QT3DS_FORCE_INLINE Vec3V V3Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z) +{ + VECMATHAOS_ASSERT(isValidFloatV(x)); + VECMATHAOS_ASSERT(isValidFloatV(y)); + VECMATHAOS_ASSERT(isValidFloatV(z)); + // static on zero causes compiler crash on x64 debug_opt + const __m128 zero = V3Zero(); + const __m128 xy = _mm_move_ss(x, y); + const __m128 z0 = _mm_move_ss(zero, z); + + return _mm_shuffle_ps(xy, z0, _MM_SHUFFLE(1, 0, 0, 1)); +} + +QT3DS_FORCE_INLINE Vec3V V3UnitX() +{ + const QT3DS_ALIGN(16, QT3DSF32 x[4]) = { 1.0f, 0.0f, 0.0f, 0.0f }; + const __m128 x128 = _mm_load_ps(x); + return x128; +} + +QT3DS_FORCE_INLINE Vec3V V3UnitY() +{ + const QT3DS_ALIGN(16, QT3DSF32 y[4]) = { 0.0f, 1.0f, 0.0f, 0.0f }; + const __m128 y128 = _mm_load_ps(y); + return y128; +} + +QT3DS_FORCE_INLINE Vec3V V3UnitZ() +{ + const QT3DS_ALIGN(16, QT3DSF32 z[4]) = { 0.0f, 0.0f, 1.0f, 0.0f }; + const __m128 z128 = _mm_load_ps(z); + return z128; +} + +QT3DS_FORCE_INLINE FloatV V3GetX(const Vec3V f) +{ + VECMATHAOS_ASSERT(isValidVec3V(f)); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +QT3DS_FORCE_INLINE FloatV V3GetY(const Vec3V f) +{ + VECMATHAOS_ASSERT(isValidVec3V(f)); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +QT3DS_FORCE_INLINE FloatV V3GetZ(const Vec3V f) +{ + VECMATHAOS_ASSERT(isValidVec3V(f)); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +QT3DS_FORCE_INLINE Vec3V V3SetX(const Vec3V v, const FloatV f) +{ + VECMATHAOS_ASSERT(isValidVec3V(v)); + VECMATHAOS_ASSERT(isValidFloatV(f)); + return V3Sel(BFTTT(), v, f); +} + +QT3DS_FORCE_INLINE Vec3V V3SetY(const Vec3V v, const FloatV f) +{ + VECMATHAOS_ASSERT(isValidVec3V(v)); + VECMATHAOS_ASSERT(isValidFloatV(f)); + return V3Sel(BTFTT(), v, f); +} + +QT3DS_FORCE_INLINE Vec3V V3SetZ(const Vec3V v, const FloatV f) +{ + VECMATHAOS_ASSERT(isValidVec3V(v)); + VECMATHAOS_ASSERT(isValidFloatV(f)); + return V3Sel(BTTFT(), v, f); +} + +QT3DS_FORCE_INLINE Vec3V V3ColX(const Vec3V a, const Vec3V b, const Vec3V c) +{ + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 0, 3, 0)); + return V3SetY(r, V3GetX(b)); +} + +QT3DS_FORCE_INLINE Vec3V V3ColY(const Vec3V a, const Vec3V b, const Vec3V c) +{ + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 1, 3, 1)); + return V3SetY(r, V3GetY(b)); +} + +QT3DS_FORCE_INLINE Vec3V V3ColZ(const Vec3V a, const Vec3V b, const Vec3V c) +{ + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 2, 3, 2)); + return V3SetY(r, V3GetZ(b)); +} + +QT3DS_FORCE_INLINE Vec3V V3Zero() +{ + return Vec3V_From_F32(0.0f); +} + +QT3DS_FORCE_INLINE Vec3V V3One() +{ + return Vec3V_From_F32(1.0f); +} + +QT3DS_FORCE_INLINE Vec3V V3Eps() +{ + return Vec3V_From_F32(QT3DS_ENV_REAL); +} + +QT3DS_FORCE_INLINE Vec3V V3Neg(const Vec3V f) +{ + VECMATHAOS_ASSERT(isValidVec3V(f)); + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +QT3DS_FORCE_INLINE Vec3V V3Add(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_add_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3Sub(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_sub_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3Scale(const Vec3V a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_mul_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3Mul(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_mul_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3ScaleInv(const Vec3V a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_div_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3Div(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + // why are these here? + // static const __m128 one=V3One(); + // static const __m128 tttf=BTTTF(); + // const __m128 b1=V3Sel(tttf,b,one); + return _mm_div_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3ScaleInvFast(const Vec3V a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +QT3DS_FORCE_INLINE Vec3V V3DivFast(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + const __m128 one = V3One(); + const __m128 tttf = BTTTF(); + const __m128 b1 = V3Sel(tttf, b, one); + return _mm_mul_ps(a, _mm_rcp_ps(b1)); +} + +QT3DS_FORCE_INLINE Vec3V V3Recip(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_div_ps(V3One(), a); + return V3Sel(tttf, recipA, zero); +} + +QT3DS_FORCE_INLINE Vec3V V3RecipFast(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_rcp_ps(a); + return V3Sel(tttf, recipA, zero); +} + +QT3DS_FORCE_INLINE Vec3V V3Rsqrt(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_div_ps(V3One(), _mm_sqrt_ps(a)); + return V3Sel(tttf, recipA, zero); +} + +QT3DS_FORCE_INLINE Vec3V V3RsqrtFast(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_rsqrt_ps(a); + return V3Sel(tttf, recipA, zero); +} + +QT3DS_FORCE_INLINE Vec3V V3ScaleAdd(const Vec3V a, const FloatV b, const Vec3V c) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + VECMATHAOS_ASSERT(isValidVec3V(c)); + return V3Add(V3Scale(a, b), c); +} + +QT3DS_FORCE_INLINE Vec3V V3NegScaleSub(const Vec3V a, const FloatV b, const Vec3V c) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidFloatV(b)); + VECMATHAOS_ASSERT(isValidVec3V(c)); + return V3Sub(c, V3Scale(a, b)); +} + +QT3DS_FORCE_INLINE Vec3V V3MulAdd(const Vec3V a, const Vec3V b, const Vec3V c) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + VECMATHAOS_ASSERT(isValidVec3V(c)); + return V3Add(V3Mul(a, b), c); +} + +QT3DS_FORCE_INLINE Vec3V V3NegMulSub(const Vec3V a, const Vec3V b, const Vec3V c) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + VECMATHAOS_ASSERT(isValidVec3V(c)); + return V3Sub(c, V3Mul(a, b)); +} + +QT3DS_FORCE_INLINE Vec3V V3Abs(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return V3Max(a, V3Neg(a)); +} + +QT3DS_FORCE_INLINE FloatV V3Dot(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + __m128 dot1 = _mm_mul_ps(a, b); // w,z,y,x + //__m128 shuf1 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(2,1,0,3)); //z,y,x,w + //__m128 shuf2 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(1,0,3,2)); //y,x,w,z + //__m128 shuf3 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(0,3,2,1)); //x,w,z,y + // return _mm_add_ps(_mm_add_ps(shuf2, shuf3), _mm_add_ps(dot1,shuf1)); + + __m128 shuf1 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(0, 0, 0, 0)); // z,y,x,w + __m128 shuf2 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(1, 1, 1, 1)); // y,x,w,z + __m128 shuf3 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(2, 2, 2, 2)); // x,w,z,y + return _mm_add_ps(_mm_add_ps(shuf1, shuf2), shuf3); +} + +QT3DS_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + __m128 l1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + __m128 l2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + __m128 r1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + __m128 r2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + return _mm_sub_ps(_mm_mul_ps(l1, l2), _mm_mul_ps(r1, r2)); +} + +QT3DS_FORCE_INLINE FloatV V3Length(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return _mm_sqrt_ps(V3Dot(a, a)); +} + +QT3DS_FORCE_INLINE FloatV V3LengthSq(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return V3Dot(a, a); +} + +QT3DS_FORCE_INLINE Vec3V V3Normalize(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(V3Dot(a, a) != FZero()) + return V3ScaleInv(a, _mm_sqrt_ps(V3Dot(a, a))); +} + +QT3DS_FORCE_INLINE Vec3V V3NormalizeFast(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return V3Mul(a, _mm_rsqrt_ps(V3Dot(a, a))); +} + +QT3DS_FORCE_INLINE Vec3V V3NormalizeSafe(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + const __m128 zero = V3Zero(); + const __m128 eps = V3Eps(); + const __m128 length = V3Length(a); + const __m128 isGreaterThanZero = FIsGrtr(length, eps); + return V3Sel(isGreaterThanZero, V3ScaleInv(a, length), zero); +} + +QT3DS_FORCE_INLINE Vec3V V3Sel(const BoolV c, const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +QT3DS_FORCE_INLINE BoolV V3IsGrtr(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_cmpgt_ps(a, b); +} + +QT3DS_FORCE_INLINE BoolV V3IsGrtrOrEq(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_cmpge_ps(a, b); +} + +QT3DS_FORCE_INLINE BoolV V3IsEq(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_cmpeq_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3Max(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_max_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V3Min(const Vec3V a, const Vec3V b) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(b)); + return _mm_min_ps(a, b); +} + +// Extract the maximum value from a +QT3DS_FORCE_INLINE FloatV V3ExtractMax(const Vec3V a) +{ + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); + + return _mm_max_ps(_mm_max_ps(shuf1, shuf2), shuf3); +} + +// Extract the maximum value from a +QT3DS_FORCE_INLINE FloatV V3ExtractMin(const Vec3V a) +{ + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); + + return _mm_min_ps(_mm_min_ps(shuf1, shuf2), shuf3); +} + +//// if(a > 0.0f) return 1.0f; else if a == 0.f return 0.f, else return -1.f; +// QT3DS_FORCE_INLINE Vec3V V3MathSign(const Vec3V a) +//{ +// VECMATHAOS_ASSERT(isValidVec3V(a)); +// +// const __m128i ai = _mm_cvtps_epi32(a); +// const __m128i bi = _mm_cvtps_epi32(V3Neg(a)); +// const __m128 aa = _mm_cvtepi32_ps(_mm_srai_epi32(ai, 31)); +// const __m128 bb = _mm_cvtepi32_ps(_mm_srai_epi32(bi, 31)); +// return _mm_or_ps(aa, bb); +//} + +// return (a >= 0.0f) ? 1.0f : -1.0f; +QT3DS_FORCE_INLINE Vec3V V3Sign(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + const __m128 zero = V3Zero(); + const __m128 one = V3One(); + const __m128 none = V3Neg(one); + return V3Sel(V3IsGrtrOrEq(a, zero), one, none); +} + +QT3DS_FORCE_INLINE Vec3V V3Clamp(const Vec3V a, const Vec3V minV, const Vec3V maxV) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(minV)); + VECMATHAOS_ASSERT(isValidVec3V(maxV)); + return V3Max(V3Min(a, maxV), minV); +} + +QT3DS_FORCE_INLINE QT3DSU32 V3AllGrtr(const Vec3V a, const Vec3V b) +{ + return BAllTrue3_R(V4IsGrtr(a, b)); +} + +QT3DS_FORCE_INLINE QT3DSU32 V3AllGrtrOrEq(const Vec3V a, const Vec3V b) +{ + return BAllTrue3_R(V4IsGrtrOrEq(a, b)); +} + +QT3DS_FORCE_INLINE QT3DSU32 V3AllEq(const Vec3V a, const Vec3V b) +{ + return BAllTrue3_R(V4IsEq(a, b)); +} + +QT3DS_FORCE_INLINE Vec3V V3Round(const Vec3V a) +{ + // return _mm_round_ps(a, 0x0); + const Vec3V half = Vec3V_From_F32(0.5f); + const Vec3V aPlusHalf = V3Add(a, half); + const __m128i tmp = _mm_cvttps_epi32(aPlusHalf); + return _mm_cvtepi32_ps(tmp); +} + +QT3DS_FORCE_INLINE Vec3V V3Sin(const Vec3V a) +{ + // Vec4V V1, V2, V3, V5, V7, V9, V11, V13, V15, V17, V19, V21, V23; + // Vec4V S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11; + Vec3V Result; + + // Modulo the range of the given angles such that -XM_PI <= Angles < XM_PI + const Vec3V twoPi = Vec4V_From_F32Array_Aligned(g_PXReciprocalTwoPi.f); + const Vec3V tmp = V3Mul(a, twoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V3NegMulSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V3 = V3Mul(V2, V1); + const Vec3V V5 = V3Mul(V3, V2); + const Vec3V V7 = V3Mul(V5, V2); + const Vec3V V9 = V3Mul(V7, V2); + const Vec3V V11 = V3Mul(V9, V2); + const Vec3V V13 = V3Mul(V11, V2); + const Vec3V V15 = V3Mul(V13, V2); + const Vec3V V17 = V3Mul(V15, V2); + const Vec3V V19 = V3Mul(V17, V2); + const Vec3V V21 = V3Mul(V19, V2); + const Vec3V V23 = V3Mul(V21, V2); + + const Vec4V sinCoefficients0 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Result = V3MulAdd(S1, V3, V1); + Result = V3MulAdd(S2, V5, Result); + Result = V3MulAdd(S3, V7, Result); + Result = V3MulAdd(S4, V9, Result); + Result = V3MulAdd(S5, V11, Result); + Result = V3MulAdd(S6, V13, Result); + Result = V3MulAdd(S7, V15, Result); + Result = V3MulAdd(S8, V17, Result); + Result = V3MulAdd(S9, V19, Result); + Result = V3MulAdd(S10, V21, Result); + Result = V3MulAdd(S11, V23, Result); + + return Result; +} + +QT3DS_FORCE_INLINE Vec3V V3Cos(const Vec3V a) +{ + // XMVECTOR V1, V2, V4, V6, V8, V10, V12, V14, V16, V18, V20, V22; + // XMVECTOR C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11; + Vec3V Result; + + // Modulo the range of the given angles such that -XM_PI <= Angles < XM_PI + const Vec3V twoPi = Vec4V_From_F32Array_Aligned(g_PXReciprocalTwoPi.f); + const Vec3V tmp = V3Mul(a, twoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V3NegMulSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V4 = V3Mul(V2, V2); + const Vec3V V6 = V3Mul(V4, V2); + const Vec3V V8 = V3Mul(V4, V4); + const Vec3V V10 = V3Mul(V6, V4); + const Vec3V V12 = V3Mul(V6, V6); + const Vec3V V14 = V3Mul(V8, V6); + const Vec3V V16 = V3Mul(V8, V8); + const Vec3V V18 = V3Mul(V10, V8); + const Vec3V V20 = V3Mul(V10, V10); + const Vec3V V22 = V3Mul(V12, V10); + + const Vec4V cosCoefficients0 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Result = V3MulAdd(C1, V2, V4One()); + Result = V3MulAdd(C2, V4, Result); + Result = V3MulAdd(C3, V6, Result); + Result = V3MulAdd(C4, V8, Result); + Result = V3MulAdd(C5, V10, Result); + Result = V3MulAdd(C6, V12, Result); + Result = V3MulAdd(C7, V14, Result); + Result = V3MulAdd(C8, V16, Result); + Result = V3MulAdd(C9, V18, Result); + Result = V3MulAdd(C10, V20, Result); + Result = V3MulAdd(C11, V22, Result); + + return Result; +} + +QT3DS_FORCE_INLINE Vec3V V3PermYZZ(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 2, 2, 1)); +} + +QT3DS_FORCE_INLINE Vec3V V3PermXYX(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 1, 0)); +} + +QT3DS_FORCE_INLINE Vec3V V3PermYZX(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)) + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); +} + +QT3DS_FORCE_INLINE Vec3V V3PermZXY(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); +} + +QT3DS_FORCE_INLINE Vec3V V3PermZZY(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 2, 2)); +} + +QT3DS_FORCE_INLINE Vec3V V3PermYXX(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 0, 1)); +} + +QT3DS_FORCE_INLINE Vec3V V3Perm_Zero_1Z_0Y(const Vec3V v0, const Vec3V v1) +{ + VECMATHAOS_ASSERT(isValidVec3V(v0)); + VECMATHAOS_ASSERT(isValidVec3V(v1)); + return _mm_shuffle_ps(v1, v0, _MM_SHUFFLE(3, 1, 2, 3)); +} + +QT3DS_FORCE_INLINE Vec3V V3Perm_0Z_Zero_1X(const Vec3V v0, const Vec3V v1) +{ + VECMATHAOS_ASSERT(isValidVec3V(v0)); + VECMATHAOS_ASSERT(isValidVec3V(v1)); + return _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(3, 0, 3, 2)); +} + +QT3DS_FORCE_INLINE Vec3V V3Perm_1Y_0X_Zero(const Vec3V v0, const Vec3V v1) +{ + VECMATHAOS_ASSERT(isValidVec3V(v0)); + VECMATHAOS_ASSERT(isValidVec3V(v1)); + // There must be a better way to do this. + Vec3V v2 = V3Zero(); + FloatV y1 = V3GetY(v1); + FloatV x0 = V3GetX(v0); + v2 = V3SetX(v2, y1); + return V3SetY(v2, x0); +} + +QT3DS_FORCE_INLINE FloatV V3SumElems(const Vec3V a) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + + __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); // z,y,x,w + __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); // y,x,w,z + __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); // x,w,z,y + return _mm_add_ps(_mm_add_ps(shuf1, shuf2), shuf3); +} + +QT3DS_FORCE_INLINE QT3DSU32 V3OutOfBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(min)); + VECMATHAOS_ASSERT(isValidVec3V(max)); + const BoolV ffff = BFFFF(); + const BoolV c = BOr(V3IsGrtr(a, max), V3IsGrtr(min, a)); + return !BAllEq(c, ffff); +} + +QT3DS_FORCE_INLINE QT3DSU32 V3InBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + VECMATHAOS_ASSERT(isValidVec3V(a)); + VECMATHAOS_ASSERT(isValidVec3V(min)); + VECMATHAOS_ASSERT(isValidVec3V(max)); + const BoolV tttt = BTTTT(); + const BoolV c = BAnd(V3IsGrtrOrEq(a, min), V3IsGrtrOrEq(max, a)); + return BAllEq(c, tttt); +} + +QT3DS_FORCE_INLINE QT3DSU32 V3OutOfBounds(const Vec3V a, const Vec3V bounds) +{ + return V3OutOfBounds(a, V3Neg(bounds), bounds); +} + +QT3DS_FORCE_INLINE QT3DSU32 V3InBounds(const Vec3V a, const Vec3V bounds) +{ + return V3InBounds(a, V3Neg(bounds), bounds); +} + +////////////////////////////////// +// VEC4V +////////////////////////////////// + +QT3DS_FORCE_INLINE Vec4V V4Splat(const FloatV f) +{ + VECMATHAOS_ASSERT(isValidFloatV(f)); + // return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0,0,0,0)); + return f; +} + +QT3DS_FORCE_INLINE Vec4V V4Merge(const FloatV *const floatVArray) +{ + VECMATHAOS_ASSERT(isValidFloatV(floatVArray[0])); + VECMATHAOS_ASSERT(isValidFloatV(floatVArray[1])); + VECMATHAOS_ASSERT(isValidFloatV(floatVArray[2])); + VECMATHAOS_ASSERT(isValidFloatV(floatVArray[3])); + __m128 xw = _mm_move_ss(floatVArray[1], floatVArray[0]); // y, y, y, x + __m128 yz = _mm_move_ss(floatVArray[2], floatVArray[3]); // z, z, z, w + return (_mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0))); +} + +QT3DS_FORCE_INLINE Vec4V V4Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z, + const FloatVArg w) +{ + VECMATHAOS_ASSERT(isValidFloatV(x)); + VECMATHAOS_ASSERT(isValidFloatV(y)); + VECMATHAOS_ASSERT(isValidFloatV(z)); + VECMATHAOS_ASSERT(isValidFloatV(w)); + __m128 xw = _mm_move_ss(y, x); // y, y, y, x + __m128 yz = _mm_move_ss(z, w); // z, z, z, w + return (_mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0))); +} + +QT3DS_FORCE_INLINE Vec4V V4UnitW() +{ + const QT3DS_ALIGN(16, QT3DSF32 w[4]) = { 0.0f, 0.0f, 0.0f, 1.0f }; + const __m128 w128 = _mm_load_ps(w); + return w128; +} + +QT3DS_FORCE_INLINE Vec4V V4UnitX() +{ + const QT3DS_ALIGN(16, QT3DSF32 x[4]) = { 1.0f, 0.0f, 0.0f, 0.0f }; + const __m128 x128 = _mm_load_ps(x); + return x128; +} + +QT3DS_FORCE_INLINE Vec4V V4UnitY() +{ + const QT3DS_ALIGN(16, QT3DSF32 y[4]) = { 0.0f, 1.0f, 0.0f, 0.0f }; + const __m128 y128 = _mm_load_ps(y); + return y128; +} + +QT3DS_FORCE_INLINE Vec4V V4UnitZ() +{ + const QT3DS_ALIGN(16, QT3DSF32 z[4]) = { 0.0f, 0.0f, 1.0f, 0.0f }; + const __m128 z128 = _mm_load_ps(z); + return z128; +} + +QT3DS_FORCE_INLINE FloatV V4GetW(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(3, 3, 3, 3)); +} + +QT3DS_FORCE_INLINE FloatV V4GetX(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +QT3DS_FORCE_INLINE FloatV V4GetY(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +QT3DS_FORCE_INLINE FloatV V4GetZ(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +QT3DS_FORCE_INLINE Vec4V V4SetW(const Vec4V v, const FloatV f) +{ + VECMATHAOS_ASSERT(isValidFloatV(f)); + return V4Sel(BTTTF(), v, f); +} + +QT3DS_FORCE_INLINE Vec4V V4SetX(const Vec4V v, const FloatV f) +{ + VECMATHAOS_ASSERT(isValidFloatV(f)); + return V4Sel(BFTTT(), v, f); +} + +QT3DS_FORCE_INLINE Vec4V V4SetY(const Vec4V v, const FloatV f) +{ + VECMATHAOS_ASSERT(isValidFloatV(f)); + return V4Sel(BTFTT(), v, f); +} + +QT3DS_FORCE_INLINE Vec4V V4SetZ(const Vec4V v, const FloatV f) +{ + VECMATHAOS_ASSERT(isValidVec3V(v)); + VECMATHAOS_ASSERT(isValidFloatV(f)); + return V4Sel(BTTFT(), v, f); +} + +QT3DS_FORCE_INLINE Vec4V V4Zero() +{ + return Vec4V_From_F32(0.0f); +} + +QT3DS_FORCE_INLINE Vec4V V4One() +{ + return Vec4V_From_F32(1.0f); +} + +QT3DS_FORCE_INLINE Vec4V V4Eps() +{ + return Vec4V_From_F32(QT3DS_ENV_REAL); +} + +QT3DS_FORCE_INLINE Vec4V V4Neg(const Vec4V f) +{ + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +QT3DS_FORCE_INLINE Vec4V V4Add(const Vec4V a, const Vec4V b) +{ + return _mm_add_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec4V V4Sub(const Vec4V a, const Vec4V b) +{ + return _mm_sub_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec4V V4Scale(const Vec4V a, const FloatV b) +{ + return _mm_mul_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec4V V4Mul(const Vec4V a, const Vec4V b) +{ + return _mm_mul_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec4V V4ScaleInv(const Vec4V a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_div_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec4V V4Div(const Vec4V a, const Vec4V b) +{ + return _mm_div_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec4V V4ScaleInvFast(const Vec4V a, const FloatV b) +{ + VECMATHAOS_ASSERT(isValidFloatV(b)); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +QT3DS_FORCE_INLINE Vec4V V4DivFast(const Vec4V a, const Vec4V b) +{ + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +QT3DS_FORCE_INLINE Vec4V V4Recip(const Vec4V a) +{ + return _mm_div_ps(V4One(), a); +} + +QT3DS_FORCE_INLINE Vec4V V4RecipFast(const Vec4V a) +{ + return _mm_rcp_ps(a); +} + +QT3DS_FORCE_INLINE Vec4V V4Rsqrt(const Vec4V a) +{ + return _mm_div_ps(V4One(), _mm_sqrt_ps(a)); +} + +QT3DS_FORCE_INLINE Vec4V V4RsqrtFast(const Vec4V a) +{ + return _mm_rsqrt_ps(a); +} + +QT3DS_FORCE_INLINE Vec4V V4ScaleAdd(const Vec4V a, const FloatV b, const Vec4V c) +{ + VECMATHAOS_ASSERT(isValidFloatV(b)); + return V4Add(V4Scale(a, b), c); +} + +QT3DS_FORCE_INLINE Vec4V V4NegScaleSub(const Vec4V a, const FloatV b, const Vec4V c) +{ + VECMATHAOS_ASSERT(isValidFloatV(b)); + return V4Sub(c, V4Scale(a, b)); +} + +QT3DS_FORCE_INLINE Vec4V V4MulAdd(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Add(V4Mul(a, b), c); +} + +QT3DS_FORCE_INLINE Vec4V V4NegMulSub(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Sub(c, V4Mul(a, b)); +} + +QT3DS_FORCE_INLINE Vec4V V4Abs(const Vec4V a) +{ + return V4Max(a, V4Neg(a)); +} + +QT3DS_FORCE_INLINE FloatV V4Dot(const Vec4V a, const Vec4V b) +{ + __m128 dot1 = _mm_mul_ps(a, b); // x,y,z,w + __m128 shuf1 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(2, 1, 0, 3)); // w,x,y,z + __m128 shuf2 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(1, 0, 3, 2)); // z,w,x,y + __m128 shuf3 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(0, 3, 2, 1)); // y,z,w,x + return _mm_add_ps(_mm_add_ps(shuf2, shuf3), _mm_add_ps(dot1, shuf1)); +} + +QT3DS_FORCE_INLINE FloatV V4Length(const Vec4V a) +{ + return _mm_sqrt_ps(V4Dot(a, a)); +} + +QT3DS_FORCE_INLINE FloatV V4LengthSq(const Vec4V a) +{ + return V4Dot(a, a); +} + +QT3DS_FORCE_INLINE Vec4V V4Normalize(const Vec4V a) +{ + VECMATHAOS_ASSERT(V4Dot(a, a) != FZero()) + return V4ScaleInv(a, _mm_sqrt_ps(V4Dot(a, a))); +} + +QT3DS_FORCE_INLINE Vec4V V4NormalizeFast(const Vec4V a) +{ + return V4ScaleInvFast(a, _mm_sqrt_ps(V4Dot(a, a))); +} + +QT3DS_FORCE_INLINE Vec4V V4NormalizeSafe(const Vec4V a) +{ + const __m128 zero = FZero(); + const __m128 eps = V3Eps(); + const __m128 length = V4Length(a); + const __m128 isGreaterThanZero = V4IsGrtr(length, eps); + return V4Sel(isGreaterThanZero, V4ScaleInv(a, length), zero); +} + +QT3DS_FORCE_INLINE Vec4V V4Sel(const BoolV c, const Vec4V a, const Vec4V b) +{ + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +QT3DS_FORCE_INLINE BoolV V4IsGrtr(const Vec4V a, const Vec4V b) +{ + return _mm_cmpgt_ps(a, b); +} + +QT3DS_FORCE_INLINE BoolV V4IsGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return _mm_cmpge_ps(a, b); +} + +QT3DS_FORCE_INLINE BoolV V4IsEq(const Vec4V a, const Vec4V b) +{ + return _mm_cmpeq_ps(a, b); +} + +QT3DS_FORCE_INLINE BoolV V4IsEqU32(const VecU32V a, const VecU32V b) +{ + return _mm_cmpeq_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec3V V4Max(const Vec4V a, const Vec4V b) +{ + return _mm_max_ps(a, b); +} + +QT3DS_FORCE_INLINE Vec4V V4Min(const Vec4V a, const Vec4V b) +{ + return _mm_min_ps(a, b); +} + +// Extract the maximum value from a +QT3DS_FORCE_INLINE FloatV V4ExtractMax(const Vec4V a) +{ + __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 1, 0, 3)); + __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2)); + __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 3, 2, 1)); + + return _mm_max_ps(_mm_max_ps(a, shuf1), _mm_max_ps(shuf2, shuf3)); +} + +// Extract the maximum value from a +QT3DS_FORCE_INLINE FloatV V4ExtractMin(const Vec4V a) +{ + __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 1, 0, 3)); + __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2)); + __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 3, 2, 1)); + + return _mm_min_ps(_mm_min_ps(a, shuf1), _mm_min_ps(shuf2, shuf3)); +} + +QT3DS_FORCE_INLINE Vec4V V4Clamp(const Vec4V a, const Vec4V minV, const Vec4V maxV) +{ + return V4Max(V4Min(a, maxV), minV); +} + +QT3DS_FORCE_INLINE QT3DSU32 V4AllGrtr(const Vec4V a, const Vec4V b) +{ + return BAllTrue4_R(V4IsGrtr(a, b)); +} + +QT3DS_FORCE_INLINE QT3DSU32 V4AllGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return BAllTrue4_R(V4IsGrtrOrEq(a, b)); +} + +QT3DS_FORCE_INLINE QT3DSU32 V4AllEq(const Vec4V a, const Vec4V b) +{ + return BAllTrue4_R(V4IsEq(a, b)); +} + +QT3DS_FORCE_INLINE Vec4V V4Round(const Vec4V a) +{ + // return _mm_round_ps(a, 0x0); + const Vec3V half = Vec3V_From_F32(0.5f); + const Vec3V aPlusHalf = V3Add(a, half); + __m128i tmp = _mm_cvttps_epi32(aPlusHalf); + return _mm_cvtepi32_ps(tmp); +} + +QT3DS_FORCE_INLINE Vec4V V4Sin(const Vec4V a) +{ + // Vec4V V1, V2, V3, V5, V7, V9, V11, V13, V15, V17, V19, V21, V23; + // Vec4V S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11; + Vec4V Result; + + const Vec4V twoPi = Vec4V_From_F32Array_Aligned(g_PXReciprocalTwoPi.f); + const Vec4V tmp = V4Mul(a, twoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V3 = V4Mul(V2, V1); + const Vec4V V5 = V4Mul(V3, V2); + const Vec4V V7 = V4Mul(V5, V2); + const Vec4V V9 = V4Mul(V7, V2); + const Vec4V V11 = V4Mul(V9, V2); + const Vec4V V13 = V4Mul(V11, V2); + const Vec4V V15 = V4Mul(V13, V2); + const Vec4V V17 = V4Mul(V15, V2); + const Vec4V V19 = V4Mul(V17, V2); + const Vec4V V21 = V4Mul(V19, V2); + const Vec4V V23 = V4Mul(V21, V2); + + const Vec4V sinCoefficients0 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = Vec4V_From_F32Array_Aligned(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Result = V4MulAdd(S1, V3, V1); + Result = V4MulAdd(S2, V5, Result); + Result = V4MulAdd(S3, V7, Result); + Result = V4MulAdd(S4, V9, Result); + Result = V4MulAdd(S5, V11, Result); + Result = V4MulAdd(S6, V13, Result); + Result = V4MulAdd(S7, V15, Result); + Result = V4MulAdd(S8, V17, Result); + Result = V4MulAdd(S9, V19, Result); + Result = V4MulAdd(S10, V21, Result); + Result = V4MulAdd(S11, V23, Result); + + return Result; +} + +QT3DS_FORCE_INLINE Vec4V V4Cos(const Vec4V a) +{ + // XMVECTOR V1, V2, V4, V6, V8, V10, V12, V14, V16, V18, V20, V22; + // XMVECTOR C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11; + Vec4V Result; + + const Vec4V twoPi = Vec4V_From_F32Array_Aligned(g_PXReciprocalTwoPi.f); + const Vec4V tmp = V4Mul(a, twoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V4 = V4Mul(V2, V2); + const Vec4V V6 = V4Mul(V4, V2); + const Vec4V V8 = V4Mul(V4, V4); + const Vec4V V10 = V4Mul(V6, V4); + const Vec4V V12 = V4Mul(V6, V6); + const Vec4V V14 = V4Mul(V8, V6); + const Vec4V V16 = V4Mul(V8, V8); + const Vec4V V18 = V4Mul(V10, V8); + const Vec4V V20 = V4Mul(V10, V10); + const Vec4V V22 = V4Mul(V12, V10); + + const Vec4V cosCoefficients0 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = Vec4V_From_F32Array_Aligned(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Result = V4MulAdd(C1, V2, V4One()); + Result = V4MulAdd(C2, V4, Result); + Result = V4MulAdd(C3, V6, Result); + Result = V4MulAdd(C4, V8, Result); + Result = V4MulAdd(C5, V10, Result); + Result = V4MulAdd(C6, V12, Result); + Result = V4MulAdd(C7, V14, Result); + Result = V4MulAdd(C8, V16, Result); + Result = V4MulAdd(C9, V18, Result); + Result = V4MulAdd(C10, V20, Result); + Result = V4MulAdd(C11, V22, Result); + + return Result; +} + +////////////////////////////////// +// BoolV +////////////////////////////////// + +QT3DS_FORCE_INLINE BoolV BFFFF() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0, 0, 0, 0 }; + const __m128 ffff = _mm_load_ps((float *)&f); + return ffff; +} + +QT3DS_FORCE_INLINE BoolV BFFFT() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0, 0, 0, 0xFFFFFFFF }; + const __m128 ffft = _mm_load_ps((float *)&f); + return ffft; +} + +QT3DS_FORCE_INLINE BoolV BFFTF() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0, 0, 0xFFFFFFFF, 0 }; + const __m128 fftf = _mm_load_ps((float *)&f); + return fftf; +} + +QT3DS_FORCE_INLINE BoolV BFFTT() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0, 0, 0xFFFFFFFF, 0xFFFFFFFF }; + const __m128 fftt = _mm_load_ps((float *)&f); + return fftt; +} + +QT3DS_FORCE_INLINE BoolV BFTFF() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0, 0xFFFFFFFF, 0, 0 }; + const __m128 ftff = _mm_load_ps((float *)&f); + return ftff; +} + +QT3DS_FORCE_INLINE BoolV BFTFT() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0, 0xFFFFFFFF, 0, 0xFFFFFFFF }; + const __m128 ftft = _mm_load_ps((float *)&f); + return ftft; +} + +QT3DS_FORCE_INLINE BoolV BFTTF() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0, 0xFFFFFFFF, 0xFFFFFFFF, 0 }; + const __m128 fttf = _mm_load_ps((float *)&f); + return fttf; +} + +QT3DS_FORCE_INLINE BoolV BFTTT() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + const __m128 fttt = _mm_load_ps((float *)&f); + return fttt; +} + +QT3DS_FORCE_INLINE BoolV BTFFF() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0xFFFFFFFF, 0, 0, 0 }; + const __m128 tfff = _mm_load_ps((float *)&f); + return tfff; +} + +QT3DS_FORCE_INLINE BoolV BTFFT() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0xFFFFFFFF, 0, 0, 0xFFFFFFFF }; + const __m128 tfft = _mm_load_ps((float *)&f); + return tfft; +} + +QT3DS_FORCE_INLINE BoolV BTFTF() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0xFFFFFFFF, 0, 0xFFFFFFFF, 0 }; + const __m128 tftf = _mm_load_ps((float *)&f); + return tftf; +} + +QT3DS_FORCE_INLINE BoolV BTFTT() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0xFFFFFFFF, 0, 0xFFFFFFFF, 0xFFFFFFFF }; + const __m128 tftt = _mm_load_ps((float *)&f); + return tftt; +} + +QT3DS_FORCE_INLINE BoolV BTTFF() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0xFFFFFFFF, 0xFFFFFFFF, 0, 0 }; + const __m128 ttff = _mm_load_ps((float *)&f); + return ttff; +} + +QT3DS_FORCE_INLINE BoolV BTTFT() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0xFFFFFFFF, 0xFFFFFFFF, 0, 0xFFFFFFFF }; + const __m128 ttft = _mm_load_ps((float *)&f); + return ttft; +} + +QT3DS_FORCE_INLINE BoolV BTTTF() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0 }; + const __m128 tttf = _mm_load_ps((float *)&f); + return tttf; +} + +QT3DS_FORCE_INLINE BoolV BTTTT() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + const __m128 tttt = _mm_load_ps((float *)&f); + return tttt; +} + +QT3DS_FORCE_INLINE BoolV BXMask() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0xFFFFFFFF, 0, 0, 0 }; + const __m128 tfff = _mm_load_ps((float *)&f); + return tfff; +} + +QT3DS_FORCE_INLINE BoolV BYMask() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0, 0xFFFFFFFF, 0, 0 }; + const __m128 ftff = _mm_load_ps((float *)&f); + return ftff; +} + +QT3DS_FORCE_INLINE BoolV BZMask() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0, 0, 0xFFFFFFFF, 0 }; + const __m128 fftf = _mm_load_ps((float *)&f); + return fftf; +} + +QT3DS_FORCE_INLINE BoolV BWMask() +{ + const QT3DS_ALIGN(16, QT3DSU32 f[4]) = { 0, 0, 0, 0xFFFFFFFF }; + const __m128 ffft = _mm_load_ps((float *)&f); + return ffft; +} + +QT3DS_FORCE_INLINE BoolV BGetX(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +QT3DS_FORCE_INLINE BoolV BGetY(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +QT3DS_FORCE_INLINE BoolV BGetZ(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +QT3DS_FORCE_INLINE BoolV BGetW(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(3, 3, 3, 3)); +} + +QT3DS_FORCE_INLINE BoolV BAnd(const BoolV a, const BoolV b) +{ + return (_mm_and_ps(a, b)); +} + +QT3DS_FORCE_INLINE BoolV BNot(const BoolV a) +{ + const BoolV bAllTrue(BTTTT()); + return _mm_xor_ps(a, bAllTrue); +} + +QT3DS_FORCE_INLINE BoolV BAndNot(const BoolV a, const BoolV b) +{ + return (_mm_andnot_ps(a, b)); +} + +QT3DS_FORCE_INLINE BoolV BOr(const BoolV a, const BoolV b) +{ + return (_mm_or_ps(a, b)); +} + +QT3DS_FORCE_INLINE BoolV BAllTrue4(const BoolV a) +{ + const BoolV bTmp = _mm_and_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), + _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 2, 3))); + return _mm_and_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +QT3DS_FORCE_INLINE BoolV BAnyTrue4(const BoolV a) +{ + const BoolV bTmp = _mm_or_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), + _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 2, 3))); + return _mm_or_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +QT3DS_FORCE_INLINE BoolV BAllTrue3(const BoolV a) +{ + const BoolV bTmp = _mm_and_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), + _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + return _mm_and_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +QT3DS_FORCE_INLINE BoolV BAnyTrue3(const BoolV a) +{ + const BoolV bTmp = _mm_or_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), + _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + return _mm_or_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +QT3DS_FORCE_INLINE QT3DSU32 BAllEq(const BoolV a, const BoolV b) +{ + const BoolV bTest = m128_I2F(_mm_cmpeq_epi32(m128_F2I(a), m128_F2I(b))); + return BAllTrue4_R(bTest); +} + +////////////////////////////////// +// MAT33V +////////////////////////////////// + +QT3DS_FORCE_INLINE Vec3V M33MulV3(const Mat33V &a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +QT3DS_FORCE_INLINE Vec3V M33TrnspsMulV3(const Mat33V &a, const Vec3V b) +{ + const FloatV x = V3Dot(a.col0, b); + const FloatV y = V3Dot(a.col1, b); + const FloatV z = V3Dot(a.col2, b); + return V3Merge(x, y, z); +} + +QT3DS_FORCE_INLINE Vec3V M33MulV3AddV3(const Mat33V &A, const Vec3V b, const Vec3V c) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + Vec3V result = V3MulAdd(A.col0, x, c); + result = V3MulAdd(A.col1, y, result); + return V3MulAdd(A.col2, z, result); +} + +QT3DS_FORCE_INLINE Mat33V M33MulM33(const Mat33V &a, const Mat33V &b) +{ + return Mat33V(M33MulV3(a, b.col0), M33MulV3(a, b.col1), M33MulV3(a, b.col2)); +} + +QT3DS_FORCE_INLINE Mat33V M33Add(const Mat33V &a, const Mat33V &b) +{ + return Mat33V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2)); +} + +QT3DS_FORCE_INLINE Mat33V M33Sub(const Mat33V &a, const Mat33V &b) +{ + return Mat33V(V3Sub(a.col0, b.col0), V3Sub(a.col1, b.col1), V3Sub(a.col2, b.col2)); +} + +QT3DS_FORCE_INLINE Mat33V M33Neg(const Mat33V &a) +{ + return Mat33V(V3Neg(a.col0), V3Neg(a.col1), V3Neg(a.col2)); +} + +QT3DS_FORCE_INLINE Mat33V M33Abs(const Mat33V &a) +{ + return Mat33V(V3Abs(a.col0), V3Abs(a.col1), V3Abs(a.col2)); +} + +QT3DS_FORCE_INLINE Mat33V M33Inverse(const Mat33V &a) +{ + const BoolV tfft = BTFFT(); + const BoolV tttf = BTTTF(); + const FloatV zero = V3Zero(); + const Vec3V cross01 = V3Cross(a.col0, a.col1); + const Vec3V cross12 = V3Cross(a.col1, a.col2); + const Vec3V cross20 = V3Cross(a.col2, a.col0); + const FloatV dot = V3Dot(cross01, a.col2); + const FloatV invDet = _mm_rcp_ps(dot); + const Vec3V mergeh = _mm_unpacklo_ps(cross12, cross01); + const Vec3V mergel = _mm_unpackhi_ps(cross12, cross01); + Vec3V colInv0 = _mm_unpacklo_ps(mergeh, cross20); + colInv0 = _mm_or_ps(_mm_andnot_ps(tttf, zero), _mm_and_ps(tttf, colInv0)); + const Vec3V zppd = _mm_shuffle_ps(mergeh, cross20, _MM_SHUFFLE(3, 0, 0, 2)); + const Vec3V pbwp = _mm_shuffle_ps(cross20, mergeh, _MM_SHUFFLE(3, 3, 1, 0)); + const Vec3V colInv1 = _mm_or_ps(_mm_andnot_ps(BTFFT(), pbwp), _mm_and_ps(BTFFT(), zppd)); + const Vec3V xppd = _mm_shuffle_ps(mergel, cross20, _MM_SHUFFLE(3, 0, 0, 0)); + const Vec3V pcyp = _mm_shuffle_ps(cross20, mergel, _MM_SHUFFLE(3, 1, 2, 0)); + const Vec3V colInv2 = _mm_or_ps(_mm_andnot_ps(tfft, pcyp), _mm_and_ps(tfft, xppd)); + + return Mat33V(_mm_mul_ps(colInv0, invDet), _mm_mul_ps(colInv1, invDet), + _mm_mul_ps(colInv2, invDet)); +} + +QT3DS_FORCE_INLINE Mat33V M33Trnsps(const Mat33V &a) +{ + return Mat33V(V3Merge(V3GetX(a.col0), V3GetX(a.col1), V3GetX(a.col2)), + V3Merge(V3GetY(a.col0), V3GetY(a.col1), V3GetY(a.col2)), + V3Merge(V3GetZ(a.col0), V3GetZ(a.col1), V3GetZ(a.col2))); +} + +QT3DS_FORCE_INLINE Mat33V M33Identity() +{ + return Mat33V(V3UnitX(), V3UnitY(), V3UnitZ()); +} + +QT3DS_FORCE_INLINE Mat33V M33Diagonal(const Vec3VArg d) +{ + const FloatV x = V3Mul(V3UnitX(), d); + const FloatV y = V3Mul(V3UnitY(), d); + const FloatV z = V3Mul(V3UnitZ(), d); + return Mat33V(x, y, z); +} + +////////////////////////////////// +// MAT34V +////////////////////////////////// + +QT3DS_FORCE_INLINE Vec3V M34MulV3(const Mat34V &a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + const Vec3V v0PlusV1Plusv2 = V3Add(v0PlusV1, v2); + return (V3Add(v0PlusV1Plusv2, a.col3)); +} + +QT3DS_FORCE_INLINE Vec3V M34Mul33V3(const Mat34V &a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +QT3DS_FORCE_INLINE Vec3V M34TrnspsMul33V3(const Mat34V &a, const Vec3V b) +{ + const FloatV x = V3Dot(a.col0, b); + const FloatV y = V3Dot(a.col1, b); + const FloatV z = V3Dot(a.col2, b); + return V3Merge(x, y, z); +} + +QT3DS_FORCE_INLINE Mat34V M34MulM34(const Mat34V &a, const Mat34V &b) +{ + return Mat34V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2), + M34MulV3(a, b.col3)); +} + +QT3DS_FORCE_INLINE Mat33V M34MulM33(const Mat34V &a, const Mat33V &b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +QT3DS_FORCE_INLINE Mat33V M34Mul33MM34(const Mat34V &a, const Mat34V &b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +QT3DS_FORCE_INLINE Mat34V M34Add(const Mat34V &a, const Mat34V &b) +{ + return Mat34V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2), + V3Add(a.col3, b.col3)); +} + +QT3DS_FORCE_INLINE Mat34V M34Inverse(const Mat34V &a) +{ + Mat34V aInv; + const BoolV tfft = BTFFT(); + const BoolV tttf = BTTTF(); + const FloatV zero = V3Zero(); + const Vec3V cross01 = V3Cross(a.col0, a.col1); + const Vec3V cross12 = V3Cross(a.col1, a.col2); + const Vec3V cross20 = V3Cross(a.col2, a.col0); + const FloatV dot = V3Dot(cross01, a.col2); + const FloatV invDet = _mm_rcp_ps(dot); + const Vec3V mergeh = _mm_unpacklo_ps(cross12, cross01); + const Vec3V mergel = _mm_unpackhi_ps(cross12, cross01); + Vec3V colInv0 = _mm_unpacklo_ps(mergeh, cross20); + colInv0 = _mm_or_ps(_mm_andnot_ps(tttf, zero), _mm_and_ps(tttf, colInv0)); + const Vec3V zppd = _mm_shuffle_ps(mergeh, cross20, _MM_SHUFFLE(3, 0, 0, 2)); + const Vec3V pbwp = _mm_shuffle_ps(cross20, mergeh, _MM_SHUFFLE(3, 3, 1, 0)); + const Vec3V colInv1 = _mm_or_ps(_mm_andnot_ps(BTFFT(), pbwp), _mm_and_ps(BTFFT(), zppd)); + const Vec3V xppd = _mm_shuffle_ps(mergel, cross20, _MM_SHUFFLE(3, 0, 0, 0)); + const Vec3V pcyp = _mm_shuffle_ps(cross20, mergel, _MM_SHUFFLE(3, 1, 2, 0)); + const Vec3V colInv2 = _mm_or_ps(_mm_andnot_ps(tfft, pcyp), _mm_and_ps(tfft, xppd)); + aInv.col0 = _mm_mul_ps(colInv0, invDet); + aInv.col1 = _mm_mul_ps(colInv1, invDet); + aInv.col2 = _mm_mul_ps(colInv2, invDet); + aInv.col3 = M34Mul33V3(aInv, V3Neg(a.col3)); + return aInv; +} + +QT3DS_FORCE_INLINE Mat33V M34Trnsps33(const Mat34V &a) +{ + return Mat33V(V3Merge(V3GetX(a.col0), V3GetX(a.col1), V3GetX(a.col2)), + V3Merge(V3GetY(a.col0), V3GetY(a.col1), V3GetY(a.col2)), + V3Merge(V3GetZ(a.col0), V3GetZ(a.col1), V3GetZ(a.col2))); +} + +////////////////////////////////// +// MAT44V +////////////////////////////////// + +QT3DS_FORCE_INLINE Vec4V M44MulV4(const Mat44V &a, const Vec4V b) +{ + const FloatV x = V4GetX(b); + const FloatV y = V4GetY(b); + const FloatV z = V4GetZ(b); + const FloatV w = V4GetW(b); + + const Vec4V v0 = V4Scale(a.col0, x); + const Vec4V v1 = V4Scale(a.col1, y); + const Vec4V v2 = V4Scale(a.col2, z); + const Vec4V v3 = V4Scale(a.col3, w); + const Vec4V v0PlusV1 = V4Add(v0, v1); + const Vec4V v0PlusV1Plusv2 = V4Add(v0PlusV1, v2); + return (V4Add(v0PlusV1Plusv2, v3)); +} + +QT3DS_FORCE_INLINE Vec4V M44TrnspsMulV4(const Mat44V &a, const Vec4V b) +{ + QT3DS_ALIGN(16, FloatV dotProdArray[4]) = { V4Dot(a.col0, b), V4Dot(a.col1, b), V4Dot(a.col2, b), + V4Dot(a.col3, b) }; + return V4Merge(dotProdArray); +} + +QT3DS_FORCE_INLINE Mat44V M44MulM44(const Mat44V &a, const Mat44V &b) +{ + return Mat44V(M44MulV4(a, b.col0), M44MulV4(a, b.col1), M44MulV4(a, b.col2), + M44MulV4(a, b.col3)); +} + +QT3DS_FORCE_INLINE Mat44V M44Add(const Mat44V &a, const Mat44V &b) +{ + return Mat44V(V4Add(a.col0, b.col0), V4Add(a.col1, b.col1), V4Add(a.col2, b.col2), + V4Add(a.col3, b.col3)); +} + +QT3DS_FORCE_INLINE Mat44V M44Trnsps(const Mat44V &a) +{ + const Vec4V v0 = _mm_unpacklo_ps(a.col0, a.col2); + const Vec4V v1 = _mm_unpackhi_ps(a.col0, a.col2); + const Vec4V v2 = _mm_unpacklo_ps(a.col1, a.col3); + const Vec4V v3 = _mm_unpackhi_ps(a.col1, a.col3); + return Mat44V(_mm_unpacklo_ps(v0, v2), _mm_unpackhi_ps(v0, v2), _mm_unpacklo_ps(v1, v3), + _mm_unpackhi_ps(v1, v3)); +} + +// The original code as provided by Intel in +// "Streaming SIMD Extensions - Inverse of 4x4 Matrix" +// (ftp://download.intel.com/design/pentiumiii/sml/24504301.pdf) +QT3DS_FORCE_INLINE Mat44V M44Inverse(const Mat44V &a) +{ + __m128 minor0, minor1, minor2, minor3; + __m128 row0, row1, row2, row3; + __m128 det, tmp1; + + tmp1 = V4Zero(); + row1 = V4Zero(); + row3 = V4Zero(); + + row0 = a.col0; + row1 = _mm_shuffle_ps(a.col1, a.col1, _MM_SHUFFLE(1, 0, 3, 2)); + row2 = a.col2; + row3 = _mm_shuffle_ps(a.col3, a.col3, _MM_SHUFFLE(1, 0, 3, 2)); + + tmp1 = _mm_mul_ps(row2, row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor0 = _mm_mul_ps(row1, tmp1); + minor1 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(_mm_mul_ps(row1, tmp1), minor0); + minor1 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor1); + minor1 = _mm_shuffle_ps(minor1, minor1, 0x4E); + + tmp1 = _mm_mul_ps(row1, row2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor0 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor0); + minor3 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row3, tmp1)); + minor3 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor3); + minor3 = _mm_shuffle_ps(minor3, minor3, 0x4E); + + tmp1 = _mm_mul_ps(_mm_shuffle_ps(row1, row1, 0x4E), row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + row2 = _mm_shuffle_ps(row2, row2, 0x4E); + minor0 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor0); + minor2 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row2, tmp1)); + minor2 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor2); + minor2 = _mm_shuffle_ps(minor2, minor2, 0x4E); + + tmp1 = _mm_mul_ps(row0, row1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor2 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor2); + minor3 = _mm_sub_ps(_mm_mul_ps(row2, tmp1), minor3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor2 = _mm_sub_ps(_mm_mul_ps(row3, tmp1), minor2); + minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row2, tmp1)); + + tmp1 = _mm_mul_ps(row0, row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row2, tmp1)); + minor2 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor1 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor1); + minor2 = _mm_sub_ps(minor2, _mm_mul_ps(row1, tmp1)); + + tmp1 = _mm_mul_ps(row0, row2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor1 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor1); + minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row1, tmp1)); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row3, tmp1)); + minor3 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor3); + + det = _mm_mul_ps(row0, minor0); + det = _mm_add_ps(_mm_shuffle_ps(det, det, 0x4E), det); + det = _mm_add_ss(_mm_shuffle_ps(det, det, 0xB1), det); + tmp1 = _mm_rcp_ss(det); +#if 0 + det = _mm_sub_ss(_mm_add_ss(tmp1, tmp1), _mm_mul_ss(det, _mm_mul_ss(tmp1, tmp1))); + det = _mm_shuffle_ps(det, det, 0x00); +#else + det = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(0, 0, 0, 0)); +#endif + + minor0 = _mm_mul_ps(det, minor0); + minor1 = _mm_mul_ps(det, minor1); + minor2 = _mm_mul_ps(det, minor2); + minor3 = _mm_mul_ps(det, minor3); + Mat44V invTrans(minor0, minor1, minor2, minor3); + return M44Trnsps(invTrans); +} + +QT3DS_FORCE_INLINE Vec4V Vec4V_From_XYZW(QT3DSF32 x, QT3DSF32 y, QT3DSF32 z, QT3DSF32 w) +{ + return _mm_set_ps(w, z, y, x); +} + +// AP: work in progress - use proper SSE intrinsics where possible +QT3DS_FORCE_INLINE VecU16V V4U32PK(VecU32V a, VecU32V b) +{ + VecU16V result; + result.m128_u16[0] = QT3DSU16(NVClamp<QT3DSU32>((a).m128_u32[0], 0, 0xFFFF)); + result.m128_u16[1] = QT3DSU16(NVClamp<QT3DSU32>((a).m128_u32[1], 0, 0xFFFF)); + result.m128_u16[2] = QT3DSU16(NVClamp<QT3DSU32>((a).m128_u32[2], 0, 0xFFFF)); + result.m128_u16[3] = QT3DSU16(NVClamp<QT3DSU32>((a).m128_u32[3], 0, 0xFFFF)); + result.m128_u16[4] = QT3DSU16(NVClamp<QT3DSU32>((b).m128_u32[0], 0, 0xFFFF)); + result.m128_u16[5] = QT3DSU16(NVClamp<QT3DSU32>((b).m128_u32[1], 0, 0xFFFF)); + result.m128_u16[6] = QT3DSU16(NVClamp<QT3DSU32>((b).m128_u32[2], 0, 0xFFFF)); + result.m128_u16[7] = QT3DSU16(NVClamp<QT3DSU32>((b).m128_u32[3], 0, 0xFFFF)); + return result; +} + +QT3DS_FORCE_INLINE VecU32V V4U32or(VecU32V a, VecU32V b) +{ + return m128_I2F(_mm_or_si128(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecU32V V4U32and(VecU32V a, VecU32V b) +{ + return m128_I2F(_mm_and_si128(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecU32V V4U32Andc(VecU32V a, VecU32V b) +{ + return m128_I2F(_mm_andnot_si128(m128_F2I(b), m128_F2I(a))); +} + +QT3DS_FORCE_INLINE VecU16V V4U16Or(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_or_si128(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecU16V V4U16And(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_and_si128(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecU16V V4U16Andc(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_andnot_si128(m128_F2I(b), m128_F2I(a))); +} + +QT3DS_FORCE_INLINE VecI32V VecI32V_From_I32(const QT3DSI32 i) +{ + return (_mm_load1_ps((QT3DSF32 *)&i)); +} + +QT3DS_FORCE_INLINE VecI32V VecI32V_From_I32Array(const QT3DSI32 *i) +{ + return _mm_loadu_ps((QT3DSF32 *)i); +} + +QT3DS_FORCE_INLINE VecI32V VecI32V_From_I32Array_Aligned(const QT3DSI32 *i) +{ + return _mm_load_ps((QT3DSF32 *)i); +} + +QT3DS_FORCE_INLINE VecI32V VecI32V_Add(const VecI32VArg a, const VecI32VArg b) +{ + return m128_I2F(_mm_add_epi32(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecI32V VecI32V_Sub(const VecI32VArg a, const VecI32VArg b) +{ + return m128_I2F(_mm_sub_epi32(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE BoolV VecI32V_IsGrtr(const VecI32VArg a, const VecI32VArg b) +{ + return m128_I2F(_mm_cmpgt_epi32(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE BoolV VecI32V_IsEq(const VecI32VArg a, const VecI32VArg b) +{ + return m128_I2F(_mm_cmpeq_epi32(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecI32V VecI32V_Zero() +{ + return V4Zero(); +} + +QT3DS_FORCE_INLINE VecI32V VecI32V_Merge(const VecI32VArg a, const VecI32VArg b, const VecI32VArg c, + const VecI32VArg d) +{ + return V4Merge(a, b, c, d); +} + +template <int a> +QT3DS_FORCE_INLINE VecI32V V4ISplat() +{ + VecI32V result; + result.m128_i32[0] = a; + result.m128_i32[1] = a; + result.m128_i32[2] = a; + result.m128_i32[3] = a; + return result; +} + +QT3DS_FORCE_INLINE void V4U16StoreAligned(VecU16V val, VecU16V *address) +{ + *address = val; +} + +QT3DS_FORCE_INLINE void V4U32StoreAligned(VecU32V val, VecU32V *address) +{ + *address = val; +} + +QT3DS_FORCE_INLINE Vec4V V4LoadAligned(Vec4V *addr) +{ + return *addr; +} + +QT3DS_FORCE_INLINE Vec4V V4LoadUnaligned(Vec4V *addr) +{ + return Vec4V_From_F32Array((float *)addr); +} + +QT3DS_FORCE_INLINE Vec4V V4Andc(const Vec4V a, const VecU32V b) +{ + VecU32V result32(a); + result32 = V4U32Andc(result32, b); + return Vec4V(result32); +} + +QT3DS_FORCE_INLINE VecU32V V4IsGrtrV32u(const Vec4V a, const Vec4V b) +{ + return V4IsGrtr(a, b); +} + +QT3DS_FORCE_INLINE VecU16V V4U16LoadAligned(VecU16V *addr) +{ + return *addr; +} + +QT3DS_FORCE_INLINE VecU16V V4U16LoadUnaligned(VecU16V *addr) +{ + return *addr; +} + +QT3DS_FORCE_INLINE VecU16V V4U16CompareGt(VecU16V a, VecU16V b) +{ + // _mm_cmpgt_epi16 doesn't work for unsigned values unfortunately + // return m128_I2F(_mm_cmpgt_epi16(m128_F2I(a), m128_F2I(b))); + VecU16V result; + result.m128_u16[0] = (a).m128_u16[0] > (b).m128_u16[0]; + result.m128_u16[1] = (a).m128_u16[1] > (b).m128_u16[1]; + result.m128_u16[2] = (a).m128_u16[2] > (b).m128_u16[2]; + result.m128_u16[3] = (a).m128_u16[3] > (b).m128_u16[3]; + result.m128_u16[4] = (a).m128_u16[4] > (b).m128_u16[4]; + result.m128_u16[5] = (a).m128_u16[5] > (b).m128_u16[5]; + result.m128_u16[6] = (a).m128_u16[6] > (b).m128_u16[6]; + result.m128_u16[7] = (a).m128_u16[7] > (b).m128_u16[7]; + return result; +} + +QT3DS_FORCE_INLINE Vec4V Vec4V_From_VecU32V(VecU32V a) +{ + Vec4V result = Vec4V_From_XYZW(QT3DSF32(a.m128_u32[0]), QT3DSF32(a.m128_u32[1]), QT3DSF32(a.m128_u32[2]), + QT3DSF32(a.m128_u32[3])); + return result; +} + +template <int index> +QT3DS_FORCE_INLINE VecU32V V4U32SplatElement(VecU32V a) +{ + VecU32V result; + result.m128_u32[0] = result.m128_u32[1] = result.m128_u32[2] = result.m128_u32[3] = + a.m128_u32[index]; + return result; +} + +template <int index> +QT3DS_FORCE_INLINE Vec4V V4SplatElement(Vec4V a) +{ + float *data = (float *)&a; + return Vec4V_From_F32(data[index]); +} + +template <int index> +QT3DS_FORCE_INLINE VecU16V V4U16SplatElement(VecU16V a) +{ + VecU16V result = a; // AM: initializing to avoid nonsensical warning 4701 here with VC10. + for (int i = 0; i < 8; i++) + result.m128_u16[i] = a.m128_u16[index]; + return result; +} + +template <int imm> +QT3DS_FORCE_INLINE VecI16V V4I16SplatImmediate() +{ + VecI16V result; + result.m128_i16[0] = imm; + result.m128_i16[1] = imm; + result.m128_i16[2] = imm; + result.m128_i16[3] = imm; + result.m128_i16[4] = imm; + result.m128_i16[5] = imm; + result.m128_i16[6] = imm; + result.m128_i16[7] = imm; + return result; +} + +QT3DS_FORCE_INLINE VecU16V V4U16SubtractModulo(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_sub_epi16(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecU16V V4U16AddModulo(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_add_epi16(m128_F2I(a), m128_F2I(b))); +} + +QT3DS_FORCE_INLINE VecU32V V4U16GetLo16(VecU16V a) +{ + VecU32V result; + result.m128_u32[0] = a.m128_u16[0]; + result.m128_u32[1] = a.m128_u16[2]; + result.m128_u32[2] = a.m128_u16[4]; + result.m128_u32[3] = a.m128_u16[6]; + return result; +} + +QT3DS_FORCE_INLINE VecU32V V4U16GetHi16(VecU16V a) +{ + VecU32V result; + result.m128_u32[0] = a.m128_u16[1]; + result.m128_u32[1] = a.m128_u16[3]; + result.m128_u32[2] = a.m128_u16[5]; + result.m128_u32[3] = a.m128_u16[7]; + return result; +} + +QT3DS_FORCE_INLINE VecU32V VecU32V_From_XYZW(QT3DSU32 x, QT3DSU32 y, QT3DSU32 z, QT3DSU32 w) +{ + VecU32V result; + result.m128_u32[0] = x; + result.m128_u32[1] = y; + result.m128_u32[2] = z; + result.m128_u32[3] = w; + return result; +} + +QT3DS_FORCE_INLINE Vec4V V4Ceil(const Vec4V a) +{ + return Vec4V_From_XYZW(NVCeil(a.m128_f32[0]), NVCeil(a.m128_f32[1]), NVCeil(a.m128_f32[2]), + NVCeil(a.m128_f32[3])); +} + +QT3DS_FORCE_INLINE Vec4V V4Floor(const Vec4V a) +{ + return Vec4V_From_XYZW(NVFloor(a.m128_f32[0]), NVFloor(a.m128_f32[1]), NVFloor(a.m128_f32[2]), + NVFloor(a.m128_f32[3])); +} + +QT3DS_FORCE_INLINE VecU32V V4ConvertToU32VSaturate(const Vec4V a, QT3DSU32 power) +{ + QT3DS_ASSERT(power == 0 && "Non-zero power not supported in convertToU32VSaturate"); + QT3DS_FORCE_PARAMETER_REFERENCE(power); // prevent warning in release builds + QT3DSF32 ffffFFFFasFloat = QT3DSF32(0xFFFF0000); + VecU32V result; + result.m128_u32[0] = QT3DSU32(NVClamp<QT3DSF32>((a).m128_f32[0], 0.0f, ffffFFFFasFloat)); + result.m128_u32[1] = QT3DSU32(NVClamp<QT3DSF32>((a).m128_f32[1], 0.0f, ffffFFFFasFloat)); + result.m128_u32[2] = QT3DSU32(NVClamp<QT3DSF32>((a).m128_f32[2], 0.0f, ffffFFFFasFloat)); + result.m128_u32[3] = QT3DSU32(NVClamp<QT3DSF32>((a).m128_f32[3], 0.0f, ffffFFFFasFloat)); + return result; +} + +#endif // QT3DS_WINDOWS_INLINE_AOS_H diff --git a/src/foundation/windows/Qt3DSWindowsIntrinsics.h b/src/foundation/windows/Qt3DSWindowsIntrinsics.h new file mode 100644 index 0000000..3e0b3e8 --- /dev/null +++ b/src/foundation/windows/Qt3DSWindowsIntrinsics.h @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_WINDOWS_INTRINSICS_H +#define QT3DS_FOUNDATION_QT3DS_WINDOWS_INTRINSICS_H + +#include "foundation/Qt3DS.h" + +#if !defined QT3DS_WINDOWS && !defined QT3DS_WIN8ARM +#error "This file should only be included by Windows builds!!" +#endif + +#ifdef QT3DS_GNUC +#include <cmath> +#else +#include <math.h> +#endif +#include <float.h> +#include <intrin.h> +#include <string.h> +#include "foundation/Qt3DSAssert.h" + +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +namespace intrinsics { +#endif + + //! \brief platform-specific absolute value + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float abs(float a) { return float(::fabs(a)); } + + //! \brief platform-specific select float + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float fsel(float a, float b, float c) + { + return (a >= 0.0f) ? b : c; + } + + //! \brief platform-specific sign + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float sign(float a) { return (a >= 0.0f) ? 1.0f : -1.0f; } + + //! \brief platform-specific reciprocal + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float recip(float a) { return 1.0f / a; } + + //! \brief platform-specific square root + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float sqrt(float a) { return ::sqrtf(a); } + + //! \brief platform-specific reciprocal square root + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float recipSqrt(float a) { return 1.0f / ::sqrtf(a); } + + //! \brief platform-specific sine + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float sin(float a) { return ::sinf(a); } + + //! \brief platform-specific cosine + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float cos(float a) { return ::cosf(a); } + + //! \brief platform-specific minimum + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float selectMin(float a, float b) { return a < b ? a : b; } + + //! \brief platform-specific maximum + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float selectMax(float a, float b) { return a > b ? a : b; } + + //! \brief platform-specific finiteness check (not INF or NAN) + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool isFinite(float a) + { +#if defined(__CUDACC__) + return isfinite(a)? true : false; +#elif defined(QT3DS_GNUC) + return (std::isfinite(a) && !std::isinf(a)) ? true : false; +#else + return (0 == ((_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF) & _fpclass(a))); +#endif + } + + //! \brief platform-specific finiteness check (not INF or NAN) + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE bool isFinite(double a) + { +#if defined(__CUDACC__) + return isfinite(a)? true : false; +#elif defined(QT3DS_GNUC) + return (std::isfinite(a) && !std::isinf(a)) ? true : false; +#else + return (0 == ((_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF) & _fpclass(a))); +#endif + } + /* +* Implements a memory barrier +*/ + QT3DS_FORCE_INLINE void memoryBarrier() + { + _ReadWriteBarrier(); + /* long Barrier; + __asm { + xchg Barrier, eax + }*/ + } + + /*! + Returns the index of the highest set bit. Not valid for zero arg. + */ + QT3DS_FORCE_INLINE QT3DSU32 highestSetBitUnsafe(QT3DSU32 v) + { + unsigned long retval; + _BitScanReverse(&retval, v); + return retval; + } + + /*! + Returns the index of the highest set bit. Undefined for zero arg. + */ + QT3DS_FORCE_INLINE QT3DSU32 lowestSetBitUnsafe(QT3DSU32 v) + { + unsigned long retval; + _BitScanForward(&retval, v); + return retval; + } + + /*! + Returns the number of leading zeros in v. Returns 32 for v=0. + */ + QT3DS_FORCE_INLINE QT3DSU32 countLeadingZeros(QT3DSU32 v) + { + if (v) { + unsigned long bsr = (unsigned long)-1; + _BitScanReverse(&bsr, v); + return 31 - bsr; + } else + return 32; + } + + /*! + Sets \c count bytes starting at \c dst to zero. + */ + QT3DS_FORCE_INLINE void *memZero(void *QT3DS_RESTRICT dest, QT3DSU32 count) + { + return memset(dest, 0, count); + } + + /*! + Sets \c count bytes starting at \c dst to \c c. + */ + QT3DS_FORCE_INLINE void *memSet(void *QT3DS_RESTRICT dest, QT3DSI32 c, QT3DSU32 count) + { + return memset(dest, c, count); + } + + /*! + Copies \c count bytes from \c src to \c dst. User memMove if regions overlap. + */ + QT3DS_FORCE_INLINE void *memCopy(void *QT3DS_RESTRICT dest, const void *QT3DS_RESTRICT src, QT3DSU32 count) + { + return memcpy(dest, src, count); + } + + /*! + Copies \c count bytes from \c src to \c dst. Supports overlapping regions. + */ + QT3DS_FORCE_INLINE void *memMove(void *QT3DS_RESTRICT dest, const void *QT3DS_RESTRICT src, QT3DSU32 count) + { + return memmove(dest, src, count); + } + + /*! + Set 128B to zero starting at \c dst+offset. Must be aligned. + */ + QT3DS_FORCE_INLINE void memZero128(void *QT3DS_RESTRICT dest, QT3DSU32 offset = 0) + { + QT3DS_ASSERT(((size_t(dest) + offset) & 0x7f) == 0); + memSet((char *QT3DS_RESTRICT)dest + offset, 0, 128); + } + + /*! + Prefetch aligned 128B around \c ptr+offset. + */ + QT3DS_FORCE_INLINE void prefetch128(const void *ptr, QT3DSU32 offset = 0) + { +#ifdef QT3DS_WINDOWS + _mm_prefetch(((const char *)ptr + offset), _MM_HINT_T0); +#endif + } + + /*! + Prefetch \c count bytes starting at \c ptr. + */ + QT3DS_FORCE_INLINE void prefetch(const void *ptr, QT3DSU32 count = 0) + { + for (QT3DSU32 i = 0; i <= count; i += 128) + prefetch128(ptr, i); + } + + //! \brief platform-specific reciprocal + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float recipFast(float a) { return 1.0f / a; } + + //! \brief platform-specific fast reciprocal square root + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float recipSqrtFast(float a) { return 1.0f / ::sqrtf(a); } + + //! \brief platform-specific floor + QT3DS_CUDA_CALLABLE QT3DS_FORCE_INLINE float floatFloor(float x) { return ::floorf(x); } + +#ifndef QT3DS_DOXYGEN +} // namespace intrinsics +} // namespace qt3ds +#endif + +#endif diff --git a/src/foundation/windows/Qt3DSWindowsMutex.cpp b/src/foundation/windows/Qt3DSWindowsMutex.cpp new file mode 100644 index 0000000..c5fef09 --- /dev/null +++ b/src/foundation/windows/Qt3DSWindowsMutex.cpp @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/windows/Qt3DSWindowsInclude.h" +#include "foundation/Qt3DSMutex.h" +#include "foundation/Qt3DSAssert.h" + +namespace qt3ds { +namespace foundation { + + namespace { + CRITICAL_SECTION *getMutex(MutexImpl *impl) + { + return reinterpret_cast<CRITICAL_SECTION *>(impl); + } + } + + MutexImpl::MutexImpl() { InitializeCriticalSection(getMutex(this)); } + + MutexImpl::~MutexImpl() { DeleteCriticalSection(getMutex(this)); } + + bool MutexImpl::lock() + { + EnterCriticalSection(getMutex(this)); + return true; + } + + bool MutexImpl::trylock() { return TryEnterCriticalSection(getMutex(this)) != 0; } + + bool MutexImpl::unlock() + { + LeaveCriticalSection(getMutex(this)); + return true; + } + + const QT3DSU32 MutexImpl::size = sizeof(CRITICAL_SECTION); + + class ReadWriteLockImpl + { + public: + ReadWriteLockImpl(NVAllocatorCallback &alloc) + : mAllocator(alloc) + { + } + NVAllocatorCallback &mAllocator; + HANDLE hReaderEvent; + HANDLE hMutex; + CRITICAL_SECTION writerMutex; + LONG counter; // count the number of readers in the lock. + LONG recursionCounter; // handle recursive writer locking + }; + + ReadWriteLock::ReadWriteLock(NVAllocatorCallback &alloc) + { + mImpl = QT3DS_NEW(alloc, ReadWriteLockImpl)(alloc); + + mImpl->hReaderEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + QT3DS_ASSERT(mImpl->hReaderEvent != NULL); + + mImpl->hMutex = CreateEvent(NULL, FALSE, TRUE, NULL); + QT3DS_ASSERT(mImpl->hMutex != NULL); + + InitializeCriticalSection(&mImpl->writerMutex); + mImpl->counter = -1; + mImpl->recursionCounter = 0; + } + + ReadWriteLock::~ReadWriteLock() + { + if (mImpl->hReaderEvent != NULL) { + CloseHandle(mImpl->hReaderEvent); + } + + if (mImpl->hMutex != NULL) { + CloseHandle(mImpl->hMutex); + } + + DeleteCriticalSection(&mImpl->writerMutex); + + QT3DS_FREE(mImpl->mAllocator, mImpl); + } + + void ReadWriteLock::lockReader() + { + if (InterlockedIncrement(&mImpl->counter) == 0) { + WaitForSingleObject(mImpl->hMutex, INFINITE); + SetEvent(mImpl->hReaderEvent); + } + + WaitForSingleObject(mImpl->hReaderEvent, INFINITE); + } + + void ReadWriteLock::lockWriter() + { + EnterCriticalSection(&mImpl->writerMutex); + + // we may already have the global mutex(really an event so we have to handle recursion + // ourselves) + if (++mImpl->recursionCounter == 1) { + WaitForSingleObject(mImpl->hMutex, INFINITE); + } + } + + void ReadWriteLock::unlockReader() + { + if (InterlockedDecrement(&mImpl->counter) < 0) { + ResetEvent(mImpl->hReaderEvent); + SetEvent(mImpl->hMutex); + } + } + + void ReadWriteLock::unlockWriter() + { + if (--mImpl->recursionCounter == 0) { + SetEvent(mImpl->hMutex); + } + + LeaveCriticalSection(&mImpl->writerMutex); + } + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/windows/Qt3DSWindowsSemaphore.cpp b/src/foundation/windows/Qt3DSWindowsSemaphore.cpp new file mode 100644 index 0000000..bb14c10 --- /dev/null +++ b/src/foundation/windows/Qt3DSWindowsSemaphore.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/windows/Qt3DSWindowsInclude.h" +#include "foundation/Qt3DSSemaphore.h" +#include "foundation/Qt3DSAllocator.h" + +namespace qt3ds { +namespace foundation { + + class SemaphoreImpl + { + public: + SemaphoreImpl(NVAllocatorCallback &alloc) + : mAllocator(alloc) + { + } + NVAllocatorCallback &mAllocator; + HANDLE handle; + }; + + Semaphore::Semaphore(NVAllocatorCallback &alloc, QT3DSU32 initialCount, QT3DSU32 maxCount) + { + mImpl = QT3DS_NEW(alloc, SemaphoreImpl)(alloc); + mImpl->handle = CreateSemaphore(0, initialCount, maxCount, (LPCTSTR)NULL); + } + + Semaphore::~Semaphore() + { + CloseHandle(mImpl->handle); + QT3DS_FREE(mImpl->mAllocator, mImpl); + } + + bool Semaphore::wait(QT3DSU32 milliseconds) + { + if (milliseconds == -1) + milliseconds = INFINITE; + + return WaitForSingleObject(mImpl->handle, milliseconds) == WAIT_OBJECT_0 ? true : false; + } + + void Semaphore::post() { ReleaseSemaphore(mImpl->handle, 1, (LPLONG)NULL); } + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/windows/Qt3DSWindowsString.h b/src/foundation/windows/Qt3DSWindowsString.h new file mode 100644 index 0000000..8d5fcd3 --- /dev/null +++ b/src/foundation/windows/Qt3DSWindowsString.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_FOUNDATION_QT3DS_WINDOWS_STRING_H +#define QT3DS_FOUNDATION_QT3DS_WINDOWS_STRING_H + +#include "foundation/Qt3DS.h" + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> + +#pragma warning(push) +#pragma warning(disable : 4995 4996) + +#ifndef QT3DS_DOXYGEN +namespace qt3ds { +#endif + +QT3DS_INLINE void NVStrcpy(char *dest, size_t size, const char *src) +{ + ::strcpy_s(dest, size, src); +} +QT3DS_INLINE void NVStrcat(char *dest, size_t size, const char *src) +{ + ::strcat_s(dest, size, src); +} +QT3DS_INLINE QT3DSI32 NVVsprintf(char *dest, size_t size, const char *src, va_list arg) +{ + QT3DSI32 r = ::vsprintf_s(dest, size, src, arg); + + return r; +} +QT3DS_INLINE QT3DSI32 NVStricmp(const char *str, const char *str1) +{ + return (::_stricmp(str, str1)); +} + +namespace string { + QT3DS_INLINE QT3DSI32 stricmp(const char *str, const char *str1) { return (::_stricmp(str, str1)); } + QT3DS_INLINE QT3DSI32 strnicmp(const char *str, const char *str1, size_t len) + { + return (::_strnicmp(str, str1, len)); + } + QT3DS_INLINE QT3DSI32 strncat_s(char *a, QT3DSI32 b, const char *c, size_t d) + { + return (::strncat_s(a, b, c, d)); + } + QT3DS_INLINE QT3DSI32 strncpy_s(char *strDest, size_t sizeInBytes, const char *strSource, + size_t count) + { + return (::strncpy_s(strDest, sizeInBytes, strSource, count)); + } + QT3DS_INLINE void strcpy_s(char *dest, size_t size, const char *src) + { + ::strcpy_s(dest, size, src); + } + QT3DS_INLINE void strcat_s(char *dest, size_t size, const char *src) + { + ::strcat_s(dest, size, src); + } + QT3DS_INLINE QT3DSI32 _vsnprintf(char *dest, size_t size, const char *src, va_list arg) + { + QT3DSI32 r = ::_vsnprintf(dest, size, src, arg); + + return r; + } + QT3DS_INLINE QT3DSI32 vsprintf_s(char *dest, size_t size, const char *src, va_list arg) + { + QT3DSI32 r = ::vsprintf_s(dest, size, src, arg); + + return r; + } + + QT3DS_INLINE QT3DSI32 sprintf_s(char *_DstBuf, size_t _DstSize, const char *_Format, ...) + { + va_list arg; + va_start(arg, _Format); + QT3DSI32 r = ::vsprintf_s(_DstBuf, _DstSize, _Format, arg); + va_end(arg); + + return r; + } + QT3DS_INLINE QT3DSI32 sscanf_s(const char *buffer, const char *format, ...) + { + va_list arg; + va_start(arg, format); + QT3DSI32 r = ::sscanf_s(buffer, format, arg); + va_end(arg); + + return r; + }; + + QT3DS_INLINE void strlwr(char *str) + { + while (*str) { + if (*str >= 'A' && *str <= 'Z') + *str += 32; + str++; + } + } + + QT3DS_INLINE void strupr(char *str) + { + while (*str) { + if (*str >= 'a' && *str <= 'z') + *str -= 32; + str++; + } + } + +} // namespace string + +#ifndef QT3DS_DOXYGEN +} // namespace qt3ds +#endif + +#pragma warning(pop) + +#endif diff --git a/src/foundation/windows/Qt3DSWindowsSync.cpp b/src/foundation/windows/Qt3DSWindowsSync.cpp new file mode 100644 index 0000000..49667e9 --- /dev/null +++ b/src/foundation/windows/Qt3DSWindowsSync.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/windows/Qt3DSWindowsInclude.h" +#include "foundation/Qt3DSSync.h" +#include "foundation/Qt3DSAllocator.h" + +namespace qt3ds { +namespace foundation { + + class SyncImpl + { + public: + SyncImpl(NVAllocatorCallback &alloc) + : mAllocator(alloc) + { + } + NVAllocatorCallback &mAllocator; + HANDLE handle; + }; + + Sync::Sync(NVAllocatorCallback &alloc) + { + mImpl = QT3DS_NEW(alloc, SyncImpl)(alloc); + mImpl->handle = CreateEvent(0, true, false, 0); + } + + Sync::~Sync() + { + CloseHandle(mImpl->handle); + QT3DS_FREE(mImpl->mAllocator, mImpl); + } + + void Sync::reset() { ResetEvent(mImpl->handle); } + + void Sync::set() { SetEvent(mImpl->handle); } + + bool Sync::wait(QT3DSU32 milliseconds) + { + if (milliseconds == -1) + milliseconds = INFINITE; + + return WaitForSingleObject(mImpl->handle, milliseconds) == WAIT_OBJECT_0 ? true : false; + } + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/windows/Qt3DSWindowsThread.cpp b/src/foundation/windows/Qt3DSWindowsThread.cpp new file mode 100644 index 0000000..3a30de8 --- /dev/null +++ b/src/foundation/windows/Qt3DSWindowsThread.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/windows/Qt3DSWindowsInclude.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSThread.h" +#include "foundation/Qt3DSAssert.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" + +// an exception for setting the thread name in microsoft debuggers +#define QT3DS_MS_VC_EXCEPTION 0x406D1388 + +namespace qt3ds { +namespace foundation { + +// struct for naming a thread in the debugger +#pragma pack(push, 8) + + typedef struct tagTHREADNAME_INFO + { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. + } THREADNAME_INFO; + +#pragma pack(pop) + + namespace { + + DWORD WINAPI NVThreadStart(LPVOID arg) + { + ((Thread *)arg)->execute(); + return 0; + } + } + + class ThreadImpl + { + public: + enum State { NotStarted, Started, Stopped }; + ThreadImpl(NVFoundationBase &foundation) + : mFoundation(foundation) + { + } + NVFoundationBase &mFoundation; + HANDLE thread; + LONG quitNow; // Should be 32bit aligned on SMP systems. + State state; + DWORD threadID; + + Thread::ExecuteFn fn; + void *arg; + }; + + Thread::Id Thread::getId() { return static_cast<Id>(GetCurrentThreadId()); } + + Thread::Thread(NVFoundationBase &foundation) + { + mImpl = (ThreadImpl *)QT3DS_NEW(foundation.getAllocator(), ThreadImpl)(foundation); + mImpl->thread = NULL; + mImpl->state = ThreadImpl::NotStarted; + mImpl->quitNow = 0; + } + + Thread::Thread(NVFoundationBase &foundation, ExecuteFn fn, void *arg) + { + mImpl = (ThreadImpl *)QT3DS_NEW(foundation.getAllocator(), ThreadImpl)(foundation); + mImpl->thread = NULL; + mImpl->state = ThreadImpl::NotStarted; + mImpl->quitNow = 0; + mImpl->fn = fn; + mImpl->arg = arg; + + start(0); + } + + Thread::~Thread() + { + if (mImpl->state == ThreadImpl::Started) + kill(); + CloseHandle(mImpl->thread); + QT3DS_FREE(mImpl->mFoundation.getAllocator(), mImpl); + } + + void Thread::start(QT3DSU32 stackSize) + { + if (mImpl->state != ThreadImpl::NotStarted) + return; + mImpl->state = ThreadImpl::Started; + + mImpl->thread = + CreateThread(NULL, stackSize, NVThreadStart, (LPVOID)this, 0, &mImpl->threadID); + } + + void Thread::signalQuit() { InterlockedIncrement(&(mImpl->quitNow)); } + + bool Thread::waitForQuit() + { + if (mImpl->state == ThreadImpl::NotStarted) + return false; + + WaitForSingleObject(mImpl->thread, INFINITE); + return true; + } + + bool Thread::quitIsSignalled() + { + return InterlockedCompareExchange(&(mImpl->quitNow), 0, 0) != 0; + } + + void Thread::quit() + { + mImpl->state = ThreadImpl::Stopped; + ExitThread(0); + } + + void Thread::kill() + { + if (mImpl->state == ThreadImpl::Started) + TerminateThread(mImpl->thread, 0); + mImpl->state = ThreadImpl::Stopped; + } + + void Thread::sleep(QT3DSU32 ms) { Sleep(ms); } + + void Thread::yield() { SwitchToThread(); } + + void Thread::execute(void) { (*mImpl->fn)(mImpl->arg); } + + QT3DSU32 Thread::setAffinityMask(QT3DSU32 mask) + { + return mask ? (QT3DSU32)SetThreadAffinityMask(mImpl->thread, mask) : 0; + } + + void Thread::setName(const char *name) + { + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = mImpl->threadID; + info.dwFlags = 0; + +#ifdef QT3DS_VC + // C++ Exceptions are disabled for this project, but SEH is not (and cannot be) + // http://stackoverflow.com/questions/943087/what-exactly-will-happen-if-i-disable-c-exceptions-in-a-project + __try { + RaiseException(QT3DS_MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), + (ULONG_PTR *)&info); + } __except (EXCEPTION_EXECUTE_HANDLER) { + // this runs if not attached to a debugger (thus not really naming the thread) + } +#else + RaiseException(QT3DS_MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), + (ULONG_PTR *)&info); +#endif + } + + void Thread::setPriority(ThreadPriority::Enum prio) + { + switch (prio) { + case ThreadPriority::eHIGH: + SetThreadPriority(mImpl->thread, THREAD_PRIORITY_HIGHEST); + break; + case ThreadPriority::eABOVE_NORMAL: + SetThreadPriority(mImpl->thread, THREAD_PRIORITY_ABOVE_NORMAL); + break; + case ThreadPriority::eNORMAL: + SetThreadPriority(mImpl->thread, THREAD_PRIORITY_NORMAL); + break; + case ThreadPriority::eBELOW_NORMAL: + SetThreadPriority(mImpl->thread, THREAD_PRIORITY_BELOW_NORMAL); + break; + case ThreadPriority::eLOW: + SetThreadPriority(mImpl->thread, THREAD_PRIORITY_LOWEST); + break; + default: + break; + } + } + + ThreadPriority::Enum Thread::getPriority(Id threadId) + { + ThreadPriority::Enum retval = ThreadPriority::eLOW; + int priority = GetThreadPriority((HANDLE)threadId); + StaticAssert<(THREAD_PRIORITY_HIGHEST > THREAD_PRIORITY_ABOVE_NORMAL)>::valid_expression(); + if (priority >= THREAD_PRIORITY_HIGHEST) + retval = ThreadPriority::eHIGH; + else if (priority >= THREAD_PRIORITY_ABOVE_NORMAL) + retval = ThreadPriority::eABOVE_NORMAL; + else if (priority >= THREAD_PRIORITY_NORMAL) + retval = ThreadPriority::eNORMAL; + else if (priority >= THREAD_PRIORITY_BELOW_NORMAL) + retval = ThreadPriority::eBELOW_NORMAL; + return retval; + } + + QT3DSU32 TlsAlloc() + { + DWORD rv = ::TlsAlloc(); + QT3DS_ASSERT(rv != TLS_OUT_OF_INDEXES); + return (QT3DSU32)rv; + } + + void TlsFree(QT3DSU32 index) { ::TlsFree(index); } + + void *TlsGet(QT3DSU32 index) { return ::TlsGetValue(index); } + + QT3DSU32 TlsSet(QT3DSU32 index, void *value) { return ::TlsSetValue(index, value); } + + const QT3DSU32 Thread::DEFAULT_STACK_SIZE = 1048576; + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/windows/Qt3DSWindowsTime.cpp b/src/foundation/windows/Qt3DSWindowsTime.cpp new file mode 100644 index 0000000..9215d64 --- /dev/null +++ b/src/foundation/windows/Qt3DSWindowsTime.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foundation/Qt3DSTime.h" +#include "foundation/windows/Qt3DSWindowsInclude.h" + +namespace { +::qt3ds::QT3DSI64 getTimeTicks() +{ + LARGE_INTEGER a; + QueryPerformanceCounter(&a); + return a.QuadPart; +} + +double getTickDuration() +{ + LARGE_INTEGER a; + QueryPerformanceFrequency(&a); + return 1.0f / double(a.QuadPart); +} + +double sTickDuration = getTickDuration(); +} // namespace + +namespace qt3ds { +namespace foundation { + + const CounterFrequencyToTensOfNanos Time::sCounterFreq = Time::getCounterFrequency(); + + CounterFrequencyToTensOfNanos Time::getCounterFrequency() + { + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + return CounterFrequencyToTensOfNanos(Time::sNumTensOfNanoSecondsInASecond, freq.QuadPart); + } + + QT3DSU64 Time::getCurrentCounterValue() + { + LARGE_INTEGER ticks; + QueryPerformanceCounter(&ticks); + return ticks.QuadPart; + } + + Time::Time() + : mTickCount(0) + { + getElapsedSeconds(); + } + + Time::Second Time::getElapsedSeconds() + { + QT3DSI64 lastTickCount = mTickCount; + mTickCount = getTimeTicks(); + return (mTickCount - lastTickCount) * sTickDuration; + } + + Time::Second Time::peekElapsedSeconds() + { + return (getTimeTicks() - mTickCount) * sTickDuration; + } + + Time::Second Time::getLastTime() const { return mTickCount * sTickDuration; } + +} // namespace foundation +} // namespace qt3ds diff --git a/src/foundation/windows/Qt3DSWindowsTrigConstants.h b/src/foundation/windows/Qt3DSWindowsTrigConstants.h new file mode 100644 index 0000000..e3da844 --- /dev/null +++ b/src/foundation/windows/Qt3DSWindowsTrigConstants.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DS_WINDOWS_TRIG_CONSTANTS_H +#define QT3DS_WINDOWS_TRIG_CONSTANTS_H + +#define QT3DS_GLOBALCONST extern const __declspec(selectany) + +__declspec(align(16)) struct QT3DS_VECTORF32 +{ + float f[4]; +}; + +#define QT3DS_PI 3.141592654f +#define QT3DS_2PI 6.283185307f +#define QT3DS_1DIVPI 0.318309886f +#define QT3DS_1DIV2PI 0.159154943f +#define QT3DS_PIDIV2 1.570796327f +#define QT3DS_PIDIV4 0.785398163f + +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXSinCoefficients0 = { 1.0f, -0.166666667f, 8.333333333e-3f, + -1.984126984e-4f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXSinCoefficients1 = { 2.755731922e-6f, -2.505210839e-8f, + 1.605904384e-10f, -7.647163732e-13f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXSinCoefficients2 = { 2.811457254e-15f, -8.220635247e-18f, + 1.957294106e-20f, -3.868170171e-23f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXCosCoefficients0 = { 1.0f, -0.5f, 4.166666667e-2f, + -1.388888889e-3f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXCosCoefficients1 = { 2.480158730e-5f, -2.755731922e-7f, + 2.087675699e-9f, -1.147074560e-11f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXCosCoefficients2 = { 4.779477332e-14f, -1.561920697e-16f, + 4.110317623e-19f, -8.896791392e-22f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXTanCoefficients0 = { 1.0f, 0.333333333f, 0.133333333f, + 5.396825397e-2f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXTanCoefficients1 = { 2.186948854e-2f, 8.863235530e-3f, + 3.592128167e-3f, 1.455834485e-3f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXTanCoefficients2 = { 5.900274264e-4f, 2.391290764e-4f, + 9.691537707e-5f, 3.927832950e-5f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXASinCoefficients0 = { -0.05806367563904f, -0.41861972469416f, + 0.22480114791621f, 2.17337241360606f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXASinCoefficients1 = { 0.61657275907170f, 4.29696498283455f, + -1.18942822255452f, -6.53784832094831f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXASinCoefficients2 = { -1.36926553863413f, -4.48179294237210f, + 1.41810672941833f, 5.48179257935713f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXATanCoefficients0 = { 1.0f, 0.333333334f, 0.2f, 0.142857143f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXATanCoefficients1 = { 1.111111111e-1f, 9.090909091e-2f, + 7.692307692e-2f, 6.666666667e-2f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXATanCoefficients2 = { 5.882352941e-2f, 5.263157895e-2f, + 4.761904762e-2f, 4.347826087e-2f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXSinEstCoefficients = { 1.0f, -1.66521856991541e-1f, + 8.199913018755e-3f, -1.61475937228e-4f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXCosEstCoefficients = { 1.0f, -4.95348008918096e-1f, + 3.878259962881e-2f, -9.24587976263e-4f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXTanEstCoefficients = { 2.484f, -1.954923183e-1f, 2.467401101f, + QT3DS_1DIVPI }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXATanEstCoefficients = { 7.689891418951e-1f, 1.104742493348f, + 8.661844266006e-1f, QT3DS_PIDIV2 }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXASinEstCoefficients = { -1.36178272886711f, 2.37949493464538f, + -8.08228565650486e-1f, + 2.78440142746736e-1f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXASinEstConstants = { 1.00000011921f, QT3DS_PIDIV2, 0.0f, 0.0f }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXPiConstants0 = { QT3DS_PI, QT3DS_2PI, QT3DS_1DIVPI, QT3DS_1DIV2PI }; +QT3DS_GLOBALCONST QT3DS_VECTORF32 g_PXReciprocalTwoPi = { QT3DS_1DIV2PI, QT3DS_1DIV2PI, QT3DS_1DIV2PI, + QT3DS_1DIV2PI }; + +#endif diff --git a/src/foundation/windows/SocketImpl.h b/src/foundation/windows/SocketImpl.h new file mode 100644 index 0000000..f72203b --- /dev/null +++ b/src/foundation/windows/SocketImpl.h @@ -0,0 +1,506 @@ +/**************************************************************************** +** +** Copyright (C) 1993-2009 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +LuaSocket 3.0 license +Copyright � 2004-2013 Diego Nehab + +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. +*/ + +#ifndef FOUNDATION_WINDOWS_SOCKET_IMPL_H +#define FOUNDATION_WINDOWS_SOCKET_IMPL_H +#pragma once +#include <winsock.h> +#pragma warning(disable : 4127) +#pragma warning(disable : 4702) + +namespace qt3ds { +namespace foundation { + namespace socketimpl { + + // Functions take from lua socket implementation. Note that it has an MIT license. + + enum { + IO_DONE = 0, /* operation completed successfully */ + IO_TIMEOUT = -1, /* operation timed out */ + IO_CLOSED = -2, /* the connection has been closed */ + IO_UNKNOWN = -3 + }; + + /*-------------------------------------------------------------------------*\ + * I/O error strings + \*-------------------------------------------------------------------------*/ + const char *io_strerror(int err) + { + switch (err) { + case IO_DONE: + return NULL; + case IO_CLOSED: + return "closed"; + case IO_TIMEOUT: + return "timeout"; + default: + return "unknown error"; + } + } + + typedef int socklen_t; + typedef SOCKET t_socket; + typedef t_socket *p_socket; + typedef struct sockaddr SA; + +#define SOCKET_INVALID (INVALID_SOCKET) + + static const char *wstrerror(int err) + { + switch (err) { + case WSAEINTR: + return "Interrupted function call"; + case WSAEACCES: + return "Permission denied"; + case WSAEFAULT: + return "Bad address"; + case WSAEINVAL: + return "Invalid argument"; + case WSAEMFILE: + return "Too many open files"; + case WSAEWOULDBLOCK: + return "Resource temporarily unavailable"; + case WSAEINPROGRESS: + return "Operation now in progress"; + case WSAEALREADY: + return "Operation already in progress"; + case WSAENOTSOCK: + return "Socket operation on nonsocket"; + case WSAEDESTADDRREQ: + return "Destination address required"; + case WSAEMSGSIZE: + return "Message too long"; + case WSAEPROTOTYPE: + return "Protocol wrong type for socket"; + case WSAENOPROTOOPT: + return "Bad protocol option"; + case WSAEPROTONOSUPPORT: + return "Protocol not supported"; + case WSAESOCKTNOSUPPORT: + return "Socket type not supported"; + case WSAEOPNOTSUPP: + return "Operation not supported"; + case WSAEPFNOSUPPORT: + return "Protocol family not supported"; + case WSAEAFNOSUPPORT: + return "Address family not supported by protocol family"; + case WSAEADDRINUSE: + return "Address already in use"; + case WSAEADDRNOTAVAIL: + return "Cannot assign requested address"; + case WSAENETDOWN: + return "Network is down"; + case WSAENETUNREACH: + return "Network is unreachable"; + case WSAENETRESET: + return "Network dropped connection on reset"; + case WSAECONNABORTED: + return "Software caused connection abort"; + case WSAECONNRESET: + return "Connection reset by peer"; + case WSAENOBUFS: + return "No buffer space available"; + case WSAEISCONN: + return "Socket is already connected"; + case WSAENOTCONN: + return "Socket is not connected"; + case WSAESHUTDOWN: + return "Cannot send after socket shutdown"; + case WSAETIMEDOUT: + return "Connection timed out"; + case WSAECONNREFUSED: + return "Connection refused"; + case WSAEHOSTDOWN: + return "Host is down"; + case WSAEHOSTUNREACH: + return "No route to host"; + case WSAEPROCLIM: + return "Too many processes"; + case WSASYSNOTREADY: + return "Network subsystem is unavailable"; + case WSAVERNOTSUPPORTED: + return "Winsock.dll version out of range"; + case WSANOTINITIALISED: + return "Successful WSAStartup not yet performed"; + case WSAEDISCON: + return "Graceful shutdown in progress"; + case WSAHOST_NOT_FOUND: + return "Host not found"; + case WSATRY_AGAIN: + return "Nonauthoritative host not found"; + case WSANO_RECOVERY: + return "Nonrecoverable name lookup error"; + case WSANO_DATA: + return "Valid name, no data record of requested type"; + default: + return "Unknown error"; + } + } + + const char *socket_strerror(int err) + { + switch (err) { + case WSAEADDRINUSE: + return "address already in use"; + case WSAECONNREFUSED: + return "connection refused"; + case WSAEISCONN: + return "already connected"; + case WSAEACCES: + return "permission denied"; + case WSAECONNABORTED: + return "closed"; + case WSAECONNRESET: + return "closed"; + case WSAETIMEDOUT: + return "timeout"; + default: + return wstrerror(err); + } + } + + int socket_open(void) + { + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 0); + int err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) + return 0; + if ((LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) + && (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)) { + WSACleanup(); + return 0; + } + return 1; + } + + /*-------------------------------------------------------------------------*\ + * Close module + \*-------------------------------------------------------------------------*/ + int socket_close(void) + { + WSACleanup(); + return 1; + } + +/*-------------------------------------------------------------------------*\ +* Wait for readable/writable/connected socket with timeout +\*-------------------------------------------------------------------------*/ +#define WAITFD_R 1 +#define WAITFD_W 2 +#define WAITFD_E 4 +#define WAITFD_C (WAITFD_E | WAITFD_W) + + static int socket_waitfd(p_socket ps, int sw, QT3DSU32 timeoutMilliseconds) + { + int ret; + fd_set rfds, wfds, efds, *rp = NULL, *wp = NULL, *ep = NULL; + struct timeval tv, *tp = NULL; + if (timeoutMilliseconds == 0) + return IO_TIMEOUT; /* optimize timeout == 0 case */ + if (sw & WAITFD_R) { + FD_ZERO(&rfds); + FD_SET(*ps, &rfds); + rp = &rfds; + } + if (sw & WAITFD_W) { + FD_ZERO(&wfds); + FD_SET(*ps, &wfds); + wp = &wfds; + } + if (sw & WAITFD_C) { + FD_ZERO(&efds); + FD_SET(*ps, &efds); + ep = &efds; + } + if (timeoutMilliseconds >= 0.0) { + tv.tv_sec = (int)(timeoutMilliseconds / 1000); + QT3DSU32 leftover = timeoutMilliseconds % 1000; + tv.tv_usec = (int)(leftover * 100000); + tp = &tv; + } + ret = select(0, rp, wp, ep, tp); + if (ret == -1) + return WSAGetLastError(); + if (ret == 0) + return IO_TIMEOUT; + if (sw == WAITFD_C && FD_ISSET(*ps, &efds)) + return IO_CLOSED; + return IO_DONE; + } + + static int socket_send(p_socket ps, const char *data, size_t count, size_t *sent, + QT3DSU32 timeoutMilliseconds) + { + int err; + *sent = 0; + /* avoid making system calls on closed sockets */ + if (*ps == SOCKET_INVALID) + return IO_CLOSED; + /* loop until we send something or we give up on error */ + for (;;) { + /* try to send something */ + int put = send(*ps, data, (int)count, 0); + /* if we sent something, we are done */ + if (put > 0) { + *sent = put; + return IO_DONE; + } + /* deal with failure */ + err = WSAGetLastError(); + /* we can only proceed if there was no serious error */ + if (err != WSAEWOULDBLOCK) + return err; + /* avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_W, timeoutMilliseconds)) != IO_DONE) + return err; + } + /* can't reach here */ + return IO_UNKNOWN; + } + /*-------------------------------------------------------------------------*\ + * Receive with timeout + \*-------------------------------------------------------------------------*/ + static int socket_recv(p_socket ps, char *data, size_t count, size_t *got, + QT3DSU32 timeoutMilliseconds) + { + int err; + *got = 0; + if (*ps == SOCKET_INVALID) + return IO_CLOSED; + for (;;) { + int taken = recv(*ps, data, (int)count, 0); + if (taken > 0) { + *got = taken; + return IO_DONE; + } + if (taken == 0) + return IO_CLOSED; + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK) + return err; + if ((err = socket_waitfd(ps, WAITFD_R, timeoutMilliseconds)) != IO_DONE) + return err; + } + return IO_UNKNOWN; + } + + void socket_setblocking(p_socket ps) + { + u_long argp = 0; + ioctlsocket(*ps, FIONBIO, &argp); + } + + /*-------------------------------------------------------------------------*\ + * Put socket into non-blocking mode + \*-------------------------------------------------------------------------*/ + void socket_setnonblocking(p_socket ps) + { + u_long argp = 1; + ioctlsocket(*ps, FIONBIO, &argp); + } + + int socket_listen(p_socket ps, int backlog) + { + int err = IO_DONE; + socket_setblocking(ps); + if (listen(*ps, backlog) < 0) + err = WSAGetLastError(); + socket_setnonblocking(ps); + return err; + } + + /*-------------------------------------------------------------------------*\ + * Accept with timeout + \*-------------------------------------------------------------------------*/ + static int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, QT3DSU32 tm) + { + SA daddr; + socklen_t dlen = sizeof(daddr); + if (*ps == SOCKET_INVALID) + return IO_CLOSED; + if (!addr) + addr = &daddr; + if (!len) + len = &dlen; + for (;;) { + int err; + /* try to get client socket */ + if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) + return IO_DONE; + /* find out why we failed */ + err = WSAGetLastError(); + /* if we failed because there was no connectoin, keep trying */ + if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED) + return err; + /* call select to avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) + return err; + } + /* can't reach here */ + return IO_UNKNOWN; + } + + int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) + { + *hp = gethostbyaddr(addr, len, AF_INET); + if (*hp) + return IO_DONE; + else + return WSAGetLastError(); + } + + int socket_gethostbyname(const char *addr, struct hostent **hp) + { + *hp = gethostbyname(addr); + if (*hp) + return IO_DONE; + else + return WSAGetLastError(); + } + + /*-------------------------------------------------------------------------*\ + * Error translation functions + \*-------------------------------------------------------------------------*/ + const char *socket_hoststrerror(int err) + { + if (err <= 0) + return io_strerror(err); + switch (err) { + case WSAHOST_NOT_FOUND: + return "host not found"; + default: + return wstrerror(err); + } + } + /*-------------------------------------------------------------------------*\ + * Close and inutilize socket + \*-------------------------------------------------------------------------*/ + void socket_destroy(p_socket ps) + { + if (*ps != SOCKET_INVALID) { + socket_setblocking(ps); /* close can take a long time on WIN32 */ + closesocket(*ps); + *ps = SOCKET_INVALID; + } + } + + /*-------------------------------------------------------------------------*\ + * + \*-------------------------------------------------------------------------*/ + void socket_shutdown(p_socket ps, int how) + { + socket_setblocking(ps); + shutdown(*ps, how); + } + + int socket_create(p_socket ps, int domain, int type, int protocol) + { + *ps = socket(domain, type, protocol); + if (*ps != SOCKET_INVALID) + return IO_DONE; + else + return WSAGetLastError(); + } + + /*-------------------------------------------------------------------------*\ + * Connects or returns error message + \*-------------------------------------------------------------------------*/ + int socket_connect(p_socket ps, SA *addr, socklen_t len, QT3DSU32 tm) + { + int err; + /* don't call on closed socket */ + if (*ps == SOCKET_INVALID) + return IO_CLOSED; + /* ask system to connect */ + if (connect(*ps, addr, len) == 0) + return IO_DONE; + /* make sure the system is trying to connect */ + err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) + return err; + /* zero timeout case optimization */ + if (tm == 0) + return IO_TIMEOUT; + /* we wait until something happens */ + err = socket_waitfd(ps, WAITFD_C, tm); + if (err == IO_CLOSED) { + int len = sizeof(err); + /* give windows time to set the error (yes, disgusting) */ + Sleep(10); + /* find out why we failed */ + getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &len); + /* we KNOW there was an error. if 'why' is 0, we will return + * "unknown error", but it's not really our fault */ + return err > 0 ? err : IO_UNKNOWN; + } else + return err; + } + + /*-------------------------------------------------------------------------*\ + * Binds or returns error message + \*-------------------------------------------------------------------------*/ + int socket_bind(p_socket ps, SA *addr, socklen_t len) + { + int err = IO_DONE; + socket_setblocking(ps); + if (bind(*ps, addr, len) < 0) + err = WSAGetLastError(); + socket_setnonblocking(ps); + return err; + } + } +} +} + +#endif
\ No newline at end of file diff --git a/src/foundation/windows/qt_attribution.json b/src/foundation/windows/qt_attribution.json new file mode 100644 index 0000000..e0310cf --- /dev/null +++ b/src/foundation/windows/qt_attribution.json @@ -0,0 +1,10 @@ +{ + "Id": "qt3dswindowsaos", + "Name": "Qt3DSWindowsAoS", + "QDocModule": "qt3dstudio", + "QtUsage": "Used by Qt3DStudio, Runtime component.", + + "License": "Other", + "LicenseFile": "LICENSE.TXT", + "Copyright": "Copyright (c) 2001 Intel Corporation." +} |