Fix code style
This commit is contained in:
@@ -36,17 +36,13 @@ struct GeometryTriangle
|
|||||||
|
|
||||||
void GetRandomPoint(Vector3& result) const
|
void GetRandomPoint(Vector3& result) const
|
||||||
{
|
{
|
||||||
// Sample parallelogram
|
|
||||||
float x = Random::Rand();
|
float x = Random::Rand();
|
||||||
float y = Random::Rand();
|
float y = Random::Rand();
|
||||||
|
|
||||||
// Flip if we're outside the triangle
|
|
||||||
if (x + y > 1.0f)
|
if (x + y > 1.0f)
|
||||||
{
|
{
|
||||||
x = 1.0f - x;
|
x = 1.0f - x;
|
||||||
y = 1.0f - y;
|
y = 1.0f - y;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = Vertex + x * Vector1 + y * Vector2;
|
result = Vertex + x * Vector1 + y * Vector2;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -234,15 +230,13 @@ void FoliageTools::Paint(Foliage* foliage, Span<int32> foliageTypesIndices, cons
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the total set of instances disregarding parameters like slope, height or layer
|
// Calculate amount of foliage instances to place
|
||||||
const float desiredInstanceCountFloat = triangle.Area * foliageType.PaintDensity * densityScale / (1000.0f * 1000.0f);
|
const float targetInstanceCountEst = triangle.Area * foliageType.PaintDensity * densityScale / (1000.0f * 1000.0f);
|
||||||
|
const int32 targetInstanceCount = targetInstanceCountEst > 1.0f ? Math::RoundToInt(targetInstanceCountEst) : Random::Rand() < targetInstanceCountEst ? 1 : 0;
|
||||||
|
|
||||||
// Allow a single instance with a random chance, if the brush is smaller than the density
|
// Try to add new instances
|
||||||
const int32 desiredInstanceCount = desiredInstanceCountFloat > 1.0f ? Math::RoundToInt(desiredInstanceCountFloat) : Random::Rand() < desiredInstanceCountFloat ? 1 : 0;
|
|
||||||
|
|
||||||
// Try to new instances
|
|
||||||
FoliagePlacement placement;
|
FoliagePlacement placement;
|
||||||
for (int32 j = 0; j < desiredInstanceCount; j++)
|
for (int32 j = 0; j < targetInstanceCount; j++)
|
||||||
{
|
{
|
||||||
triangle.GetRandomPoint(placement.Location);
|
triangle.GetRandomPoint(placement.Location);
|
||||||
|
|
||||||
|
|||||||
@@ -152,20 +152,16 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
var clipX = clipWidth * clipIndex;
|
var clipX = clipWidth * clipIndex;
|
||||||
var clipRight = Mathf.Min(width, clipX + clipWidth);
|
var clipRight = Mathf.Min(width, clipX + clipWidth);
|
||||||
|
|
||||||
// Render each channel separately so outer loop is the sound wave channel index
|
// Render every audio channel separately
|
||||||
for (uint channelIndex = 0; channelIndex < info.NumChannels; channelIndex++)
|
for (uint channelIndex = 0; channelIndex < info.NumChannels; channelIndex++)
|
||||||
{
|
{
|
||||||
uint currentSample = channelIndex;
|
uint currentSample = channelIndex;
|
||||||
float yCenter = Y + ((2 * channelIndex) + 1) * height / (2.0f * info.NumChannels);
|
float yCenter = Y + ((2 * channelIndex) + 1) * height / (2.0f * info.NumChannels);
|
||||||
|
|
||||||
// Loop through each pixel (in x direction)
|
|
||||||
for (float pixelX = clipX; pixelX < clipRight; pixelX++)
|
for (float pixelX = clipX; pixelX < clipRight; pixelX++)
|
||||||
{
|
{
|
||||||
// Reset the sample sum and num samples in pixel for each pixel
|
|
||||||
float samplesSum = 0;
|
float samplesSum = 0;
|
||||||
int samplesInPixel = 0;
|
int samplesInPixel = 0;
|
||||||
|
|
||||||
// Loop through all pixels in this x-frame and sum all audio data
|
|
||||||
uint samplesEnd = Math.Min(currentSample + samplesPerIndex, info.NumSamples);
|
uint samplesEnd = Math.Min(currentSample + samplesPerIndex, info.NumSamples);
|
||||||
for (uint sampleIndex = currentSample; sampleIndex < samplesEnd; sampleIndex += samplesPerIndexDiff)
|
for (uint sampleIndex = currentSample; sampleIndex < samplesEnd; sampleIndex += samplesPerIndexDiff)
|
||||||
{
|
{
|
||||||
@@ -173,16 +169,14 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
samplesInPixel++;
|
samplesInPixel++;
|
||||||
}
|
}
|
||||||
currentSample = samplesEnd;
|
currentSample = samplesEnd;
|
||||||
|
|
||||||
if (samplesInPixel > 0)
|
if (samplesInPixel > 0)
|
||||||
{
|
{
|
||||||
float averageSampleValue = samplesSum / samplesInPixel;
|
float sampleValueAvg = samplesSum / samplesInPixel;
|
||||||
float averageSampleValueScaled = averageSampleValue * sampleYScale;
|
float sampleValueAvgScaled = sampleValueAvg * sampleYScale;
|
||||||
|
if (sampleValueAvgScaled > 0.1f)
|
||||||
// Don't try to draw anything if the audio data was too quiet
|
|
||||||
if (averageSampleValueScaled > 0.1f)
|
|
||||||
{
|
{
|
||||||
// Draw vertical line mirrored around x-axis for channel equal to average sample value height
|
Render2D.DrawLine(new Vector2(pixelX, yCenter - sampleValueAvgScaled), new Vector2(pixelX, yCenter + sampleValueAvgScaled), color);
|
||||||
Render2D.DrawLine(new Vector2(pixelX, yCenter - averageSampleValueScaled), new Vector2(pixelX, yCenter + averageSampleValueScaled), color);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootNode, Transform& jointNode
|
|||||||
Vector3 jointPos = jointNode.Translation;
|
Vector3 jointPos = jointNode.Translation;
|
||||||
Vector3 desiredDelta = target - rootNode.Translation;
|
Vector3 desiredDelta = target - rootNode.Translation;
|
||||||
float desiredLength = desiredDelta.Length();
|
float desiredLength = desiredDelta.Length();
|
||||||
float maxLimbLength = lowerLimbLength + upperLimbLength;
|
float limbLengthLimit = lowerLimbLength + upperLimbLength;
|
||||||
|
|
||||||
Vector3 desiredDir;
|
Vector3 desiredDir;
|
||||||
if (desiredLength < ZeroTolerance)
|
if (desiredLength < ZeroTolerance)
|
||||||
@@ -56,17 +56,17 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootNode, Transform& jointNode
|
|||||||
|
|
||||||
if (allowStretching)
|
if (allowStretching)
|
||||||
{
|
{
|
||||||
const float startStretchRatio = 1.0f;
|
const float initialStretchRatio = 1.0f;
|
||||||
const float scaleRange = maxStretchScale - startStretchRatio;
|
const float range = maxStretchScale - initialStretchRatio;
|
||||||
if (scaleRange > ZeroTolerance && maxLimbLength > ZeroTolerance)
|
if (range > ZeroTolerance && limbLengthLimit > ZeroTolerance)
|
||||||
{
|
{
|
||||||
const float reachRatio = desiredLength / maxLimbLength;
|
const float reachRatio = desiredLength / limbLengthLimit;
|
||||||
const float scalingFactor = (maxStretchScale - 1.0f) * Math::Saturate((reachRatio - startStretchRatio) / scaleRange);
|
const float scalingFactor = (maxStretchScale - 1.0f) * Math::Saturate((reachRatio - initialStretchRatio) / range);
|
||||||
if (scalingFactor > ZeroTolerance)
|
if (scalingFactor > ZeroTolerance)
|
||||||
{
|
{
|
||||||
lowerLimbLength *= 1.0f + scalingFactor;
|
lowerLimbLength *= 1.0f + scalingFactor;
|
||||||
upperLimbLength *= 1.0f + scalingFactor;
|
upperLimbLength *= 1.0f + scalingFactor;
|
||||||
maxLimbLength *= 1.0f + scalingFactor;
|
limbLengthLimit *= 1.0f + scalingFactor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,9 +74,9 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootNode, Transform& jointNode
|
|||||||
Vector3 resultEndPos = target;
|
Vector3 resultEndPos = target;
|
||||||
Vector3 resultJointPos = jointPos;
|
Vector3 resultJointPos = jointPos;
|
||||||
|
|
||||||
if (desiredLength >= maxLimbLength)
|
if (desiredLength >= limbLengthLimit)
|
||||||
{
|
{
|
||||||
resultEndPos = rootNode.Translation + maxLimbLength * desiredDir;
|
resultEndPos = rootNode.Translation + limbLengthLimit * desiredDir;
|
||||||
resultJointPos = rootNode.Translation + upperLimbLength * desiredDir;
|
resultJointPos = rootNode.Translation + upperLimbLength * desiredDir;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -2,19 +2,13 @@
|
|||||||
|
|
||||||
#include "Engine/Core/Log.h"
|
#include "Engine/Core/Log.h"
|
||||||
#include "Engine/Core/RandomStream.h"
|
#include "Engine/Core/RandomStream.h"
|
||||||
#include "Engine/Core/Enums.h"
|
|
||||||
#include "Engine/Core/Math/Packed.h"
|
#include "Engine/Core/Math/Packed.h"
|
||||||
#include "IESLoader.h"
|
#include "IESLoader.h"
|
||||||
|
|
||||||
/// <summary>
|
#define MAX_LINE 200
|
||||||
/// IES profile format version
|
|
||||||
/// </summary>
|
|
||||||
DECLARE_ENUM_4(IESVersion, EIESV_1986, EIESV_1991, EIESV_1995, EIESV_2002);
|
|
||||||
|
|
||||||
static void JumpOverWhiteSpace(const uint8*& bufferPos)
|
static void SkipWhiteSpace(const uint8*& bufferPos)
|
||||||
{
|
{
|
||||||
// Space and return
|
|
||||||
|
|
||||||
while (*bufferPos)
|
while (*bufferPos)
|
||||||
{
|
{
|
||||||
if (*bufferPos == 13 && *(bufferPos + 1) == 10)
|
if (*bufferPos == 13 && *(bufferPos + 1) == 10)
|
||||||
@@ -22,15 +16,13 @@ static void JumpOverWhiteSpace(const uint8*& bufferPos)
|
|||||||
bufferPos += 2;
|
bufferPos += 2;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*bufferPos == 10)
|
if (*bufferPos == 10)
|
||||||
{
|
{
|
||||||
// No valid MSDOS return file
|
bufferPos++;
|
||||||
CRASH;
|
continue;
|
||||||
}
|
}
|
||||||
else if (*bufferPos <= ' ')
|
if (*bufferPos <= ' ')
|
||||||
{
|
{
|
||||||
// Tab, space, invisible characters
|
|
||||||
bufferPos++;
|
bufferPos++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -39,14 +31,12 @@ static void JumpOverWhiteSpace(const uint8*& bufferPos)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GetLineContent(const uint8*& bufferPos, char Line[256], bool bStopOnWhitespace)
|
static void ReadLine(const uint8*& bufferPos, char line[MAX_LINE], bool skipUntilWhitespace)
|
||||||
{
|
{
|
||||||
JumpOverWhiteSpace(bufferPos);
|
SkipWhiteSpace(bufferPos);
|
||||||
|
|
||||||
char* linePtr = Line;
|
|
||||||
|
|
||||||
|
char* linePtr = line;
|
||||||
uint32 i;
|
uint32 i;
|
||||||
|
|
||||||
for (i = 0; i < 255; i++)
|
for (i = 0; i < 255; i++)
|
||||||
{
|
{
|
||||||
if (*bufferPos == 0)
|
if (*bufferPos == 0)
|
||||||
@@ -60,12 +50,11 @@ static void GetLineContent(const uint8*& bufferPos, char Line[256], bool bStopOn
|
|||||||
}
|
}
|
||||||
if (*bufferPos == 10)
|
if (*bufferPos == 10)
|
||||||
{
|
{
|
||||||
// No valid MSDOS return file
|
bufferPos++;
|
||||||
CRASH;
|
continue;
|
||||||
}
|
}
|
||||||
else if (bStopOnWhitespace && (*bufferPos <= ' '))
|
if (skipUntilWhitespace && *bufferPos <= ' ')
|
||||||
{
|
{
|
||||||
// Tab, space, invisible characters
|
|
||||||
bufferPos++;
|
bufferPos++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -73,387 +62,231 @@ static void GetLineContent(const uint8*& bufferPos, char Line[256], bool bStopOn
|
|||||||
*linePtr++ = *bufferPos++;
|
*linePtr++ = *bufferPos++;
|
||||||
}
|
}
|
||||||
|
|
||||||
Line[i] = 0;
|
line[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool GetFloat(const uint8*& bufferPos, float& ret)
|
static bool ReadFloat(const uint8*& bufferPos, float& ret)
|
||||||
{
|
{
|
||||||
char line[256];
|
char line[MAX_LINE];
|
||||||
|
ReadLine(bufferPos, line, true);
|
||||||
GetLineContent(bufferPos, line, true);
|
|
||||||
|
|
||||||
ret = static_cast<float>(atof(line));
|
ret = static_cast<float>(atof(line));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool GetInt(const uint8*& bufferPos, int32& ret)
|
static bool ReadLine(const uint8*& bufferPos, int32& ret)
|
||||||
{
|
{
|
||||||
char line[256];
|
char line[MAX_LINE];
|
||||||
|
ReadLine(bufferPos, line, true);
|
||||||
GetLineContent(bufferPos, line, true);
|
|
||||||
|
|
||||||
ret = atoi(line);
|
ret = atoi(line);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
IESLoader::IESLoader()
|
#define PARSE_FLOAT(x) float x; if (!ReadFloat(bufferPos, x)) { return true; }
|
||||||
: _brightness(0)
|
#define PARSE_INT(x) int32 x; if (!ReadLine(bufferPos, x)) { return true; }
|
||||||
, _cachedIntegral(-1)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LOG_IES_IMPORT_ERROR(error) LOG(Warning, "Cannot import IES profile. {0}", error)
|
|
||||||
#define PARSE_FLOAT(x) float x; if (!GetFloat(bufferPos, x)) { LOG_IES_IMPORT_ERROR(error); return true; }
|
|
||||||
#define PARSE_INT(x) int32 x; if (!GetInt(bufferPos, x)) { LOG_IES_IMPORT_ERROR(error); return true; }
|
|
||||||
|
|
||||||
bool IESLoader::Load(const byte* buffer)
|
bool IESLoader::Load(const byte* buffer)
|
||||||
{
|
{
|
||||||
// File format as described here:
|
// Referenced IES file format:
|
||||||
// http://www.ltblight.com/English.lproj/LTBLhelp/pages/iesformat.html
|
// http://www.ltblight.com/English.lproj/LTBLhelp/pages/iesformat.html
|
||||||
|
|
||||||
const uint8* bufferPos = buffer;
|
const uint8* bufferPos = buffer;
|
||||||
const Char* error = nullptr;
|
const char* version;
|
||||||
|
|
||||||
IESVersion version = IESVersion::EIESV_1986;
|
|
||||||
{
|
{
|
||||||
error = TEXT("VersionError");
|
char line[MAX_LINE];
|
||||||
|
ReadLine(bufferPos, line, false);
|
||||||
char line1[256];
|
if (StringUtils::CompareIgnoreCase(line, "IESNA:LM-63-1995") == 0)
|
||||||
|
|
||||||
GetLineContent(bufferPos, line1, false);
|
|
||||||
|
|
||||||
if (StringUtils::CompareIgnoreCase(line1, "IESNA:LM-63-1995") == 0)
|
|
||||||
{
|
{
|
||||||
version = IESVersion::EIESV_1995;
|
version = "EIESV_1995";
|
||||||
}
|
}
|
||||||
else if (StringUtils::CompareIgnoreCase(line1, "IESNA91") == 0)
|
else if (StringUtils::CompareIgnoreCase(line, "IESNA91") == 0)
|
||||||
{
|
{
|
||||||
version = IESVersion::EIESV_1991;
|
version = "EIESV_1991";
|
||||||
}
|
}
|
||||||
else if (StringUtils::CompareIgnoreCase(line1, "IESNA:LM-63-2002") == 0)
|
else if (StringUtils::CompareIgnoreCase(line, "IESNA:LM-63-2002") == 0)
|
||||||
{
|
{
|
||||||
version = IESVersion::EIESV_2002;
|
version = "EIESV_2002";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// EIESV_1986
|
version = "EIESV_1986";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
error = TEXT("HeaderError");
|
|
||||||
|
|
||||||
while (*bufferPos)
|
while (*bufferPos)
|
||||||
{
|
{
|
||||||
char line[256];
|
char line[MAX_LINE];
|
||||||
|
ReadLine(bufferPos, line, false);
|
||||||
GetLineContent(bufferPos, line, false);
|
if (StringUtils::Compare(line, "TILT=NONE") == 0)
|
||||||
|
|
||||||
if (strcmp(line, "TILT=NONE") == 0)
|
|
||||||
{
|
{
|
||||||
// At the moment we don't support only profiles with TILT=NONE
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (strncmp(line, "TILT=", 5) == 0)
|
if (StringUtils::Compare(line, "TILT=", 5) == 0)
|
||||||
{
|
{
|
||||||
// "TILT=NONE", "TILT=INCLUDE", and "TILT={filename}"
|
|
||||||
// not supported yet, seems rare
|
|
||||||
LOG(Warning, "Cannot import IES profile. {0}", String(error));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
error = TEXT("HeaderParameterError");
|
PARSE_INT(lights);
|
||||||
|
PARSE_FLOAT(lumensPerLight);
|
||||||
|
PARSE_FLOAT(candalaScale);
|
||||||
|
PARSE_INT(vAnglesCount);
|
||||||
|
PARSE_INT(hAnglesCount);
|
||||||
|
PARSE_INT(photometricType);
|
||||||
|
PARSE_INT(unitType);
|
||||||
|
PARSE_FLOAT(width);
|
||||||
|
PARSE_FLOAT(length);
|
||||||
|
PARSE_FLOAT(height);
|
||||||
|
PARSE_FLOAT(ballastWeight);
|
||||||
|
PARSE_FLOAT(dummy);
|
||||||
|
PARSE_FLOAT(watts);
|
||||||
|
|
||||||
PARSE_INT(LightCount);
|
if (lights < 1)
|
||||||
|
|
||||||
if (LightCount < 1)
|
|
||||||
{
|
{
|
||||||
error = TEXT("Light count needs to be positive.");
|
|
||||||
LOG(Warning, "Cannot import IES profile. {0}", error);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is any file with that - do we need to parse it differently?
|
if (candalaScale < 0 || vAnglesCount < 0 || hAnglesCount < 0)
|
||||||
ASSERT(LightCount >= 1);
|
|
||||||
|
|
||||||
PARSE_FLOAT(LumensPerLamp);
|
|
||||||
|
|
||||||
_brightness = LumensPerLamp / LightCount;
|
|
||||||
|
|
||||||
PARSE_FLOAT(CandalaMult);
|
|
||||||
|
|
||||||
if (CandalaMult < 0)
|
|
||||||
{
|
{
|
||||||
error = TEXT("CandalaMult is negative");
|
|
||||||
LOG_IES_IMPORT_ERROR(error);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PARSE_INT(VAnglesNum);
|
_brightness = lumensPerLight / lights;
|
||||||
PARSE_INT(HAnglesNum);
|
|
||||||
|
|
||||||
if (VAnglesNum < 0)
|
|
||||||
{
|
|
||||||
error = TEXT("VAnglesNum is not valid");
|
|
||||||
LOG_IES_IMPORT_ERROR(error);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HAnglesNum < 0)
|
|
||||||
{
|
|
||||||
error = TEXT("HAnglesNum is not valid");
|
|
||||||
LOG_IES_IMPORT_ERROR(error);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
PARSE_INT(PhotometricType);
|
|
||||||
|
|
||||||
// 1:feet, 2:meter
|
|
||||||
PARSE_INT(UnitType);
|
|
||||||
|
|
||||||
PARSE_FLOAT(Width);
|
|
||||||
PARSE_FLOAT(Length);
|
|
||||||
PARSE_FLOAT(Height);
|
|
||||||
|
|
||||||
PARSE_FLOAT(BallastFactor);
|
|
||||||
PARSE_FLOAT(FutureUse);
|
|
||||||
|
|
||||||
PARSE_FLOAT(InputWatts);
|
|
||||||
|
|
||||||
LOG(Info, "IES profile version: {0}, VAngles: {1}, HAngles: {2}", ::ToString(version), VAnglesNum, HAnglesNum);
|
|
||||||
|
|
||||||
error = TEXT("ContentError");
|
|
||||||
|
|
||||||
{
|
{
|
||||||
float minSoFar = MIN_float;
|
float minValue = MIN_float;
|
||||||
|
_vAngles.SetCapacity(vAnglesCount, false);
|
||||||
_vAngles.SetCapacity(VAnglesNum, false);
|
for (int32 y = 0; y < vAnglesCount; y++)
|
||||||
for (int32 y = 0; y < VAnglesNum; y++)
|
|
||||||
{
|
{
|
||||||
PARSE_FLOAT(Value);
|
PARSE_FLOAT(value);
|
||||||
|
if (value < minValue)
|
||||||
if (Value < minSoFar)
|
|
||||||
{
|
|
||||||
// binary search later relies on that
|
|
||||||
error = TEXT("V Values are not in increasing order");
|
|
||||||
LOG_IES_IMPORT_ERROR(error);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
minValue = value;
|
||||||
|
_vAngles.Add(value);
|
||||||
minSoFar = Value;
|
|
||||||
_vAngles.Add(Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
float minSoFar = MIN_float;
|
float minValue = MIN_float;
|
||||||
|
_hAngles.SetCapacity(hAnglesCount, false);
|
||||||
_hAngles.SetCapacity(HAnglesNum, false);
|
for (int32 x = 0; x < hAnglesCount; x++)
|
||||||
for (int32 x = 0; x < HAnglesNum; x++)
|
|
||||||
{
|
{
|
||||||
PARSE_FLOAT(Value);
|
PARSE_FLOAT(value);
|
||||||
|
if (value < minValue)
|
||||||
if (Value < minSoFar)
|
|
||||||
{
|
|
||||||
// binary search later relies on that
|
|
||||||
error = TEXT("H Values are not in increasing order");
|
|
||||||
LOG_IES_IMPORT_ERROR(error);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
minValue = value;
|
||||||
|
_hAngles.Add(value);
|
||||||
minSoFar = Value;
|
|
||||||
_hAngles.Add(Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_candalaValues.SetCapacity(HAnglesNum * VAnglesNum, false);
|
_candalaValues.SetCapacity(hAnglesCount * vAnglesCount, false);
|
||||||
for (int32 y = 0; y < HAnglesNum; y++)
|
for (int32 y = 0; y < hAnglesCount; y++)
|
||||||
{
|
{
|
||||||
for (int32 x = 0; x < VAnglesNum; x++)
|
for (int32 x = 0; x < vAnglesCount; x++)
|
||||||
{
|
{
|
||||||
PARSE_FLOAT(Value);
|
PARSE_FLOAT(value);
|
||||||
|
_candalaValues.Add(value * candalaScale);
|
||||||
_candalaValues.Add(Value * CandalaMult);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
error = TEXT("Unexpected content after candala values.");
|
SkipWhiteSpace(bufferPos);
|
||||||
|
|
||||||
JumpOverWhiteSpace(bufferPos);
|
|
||||||
|
|
||||||
if (*bufferPos)
|
if (*bufferPos)
|
||||||
{
|
{
|
||||||
// some files are terminated with "END"
|
char line[MAX_LINE];
|
||||||
char Line[256];
|
ReadLine(bufferPos, line, true);
|
||||||
|
if (StringUtils::Compare(line, "END") == 0)
|
||||||
GetLineContent(bufferPos, Line, true);
|
|
||||||
|
|
||||||
if (strcmp(Line, "END") == 0)
|
|
||||||
{
|
{
|
||||||
JumpOverWhiteSpace(bufferPos);
|
SkipWhiteSpace(bufferPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*bufferPos)
|
if (*bufferPos)
|
||||||
{
|
{
|
||||||
error = TEXT("Unexpected content after END.");
|
|
||||||
LOG_IES_IMPORT_ERROR(error);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_brightness <= 0)
|
if (_brightness <= 0)
|
||||||
{
|
{
|
||||||
// Some samples have -1, then the brightness comes from the samples
|
|
||||||
// Brightness = ComputeFullIntegral();
|
|
||||||
|
|
||||||
// Use some reasonable value
|
|
||||||
_brightness = 1000;
|
_brightness = 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef PARSE_FLOAT
|
#undef PARSE_FLOAT
|
||||||
#undef PARSE_INT
|
#undef PARSE_INT
|
||||||
|
|
||||||
float IESLoader::ExtractInR16F(Array<byte>& output)
|
float IESLoader::ExtractInR16(Array<byte>& output)
|
||||||
{
|
{
|
||||||
uint32 width = GetWidth();
|
const uint32 width = GetWidth();
|
||||||
uint32 height = GetHeight();
|
const uint32 height = GetHeight();
|
||||||
|
|
||||||
ASSERT(output.IsEmpty());
|
output.Clear();
|
||||||
output.Resize(width * height * sizeof(Half), false);
|
output.Resize(width * height * sizeof(Half), false);
|
||||||
|
|
||||||
Half* out = reinterpret_cast<Half*>(output.Get());
|
Half* out = reinterpret_cast<Half*>(output.Get());
|
||||||
|
|
||||||
float invWidth = 1.0f / width;
|
const float invWidth = 1.0f / (float)width;
|
||||||
float maxValue = ComputeMax();
|
float maxValue = _candalaValues[0];
|
||||||
float invMaxValue = 1.0f / maxValue;
|
for (int32 i = 1; i < _candalaValues.Count(); i++)
|
||||||
|
maxValue = Math::Max(maxValue, _candalaValues[i]);
|
||||||
|
const float invMaxValue = 1.0f / maxValue;
|
||||||
|
const uint32 hAnglesCount = static_cast<uint32>(_hAngles.Count());
|
||||||
|
|
||||||
for (uint32 y = 0; y < height; y++)
|
for (uint32 y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
for (uint32 x = 0; x < width; x++)
|
for (uint32 x = 0; x < width; x++)
|
||||||
{
|
{
|
||||||
// 0..1
|
const float vAngle = (float)x * invWidth * 180.0f;
|
||||||
float fraction = x * invWidth;
|
const float v = ComputeFilterPos(vAngle, _vAngles);
|
||||||
|
float result = 0.0f;
|
||||||
// TODO: distort for better quality? eg. Fraction = Square(Fraction);
|
for (uint32 i = 0; i < hAnglesCount; i++)
|
||||||
|
result += InterpolateBilinear(static_cast<float>(i), v);
|
||||||
float value = invMaxValue * Interpolate1D(fraction * 180.0f);
|
*out++ = ConvertFloatToHalf(invMaxValue * result / (float)hAnglesCount);
|
||||||
|
|
||||||
*out++ = ConvertFloatToHalf(value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float integral = ComputeFullIntegral();
|
float integral;
|
||||||
|
{
|
||||||
|
// Calculate integral using Monte Carlo
|
||||||
|
const int32 count = 500000;
|
||||||
|
const RandomStream randomStream(0x1234);
|
||||||
|
double sum = 0;
|
||||||
|
for (uint32 i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
const Vector3 v = randomStream.GetUnitVector();
|
||||||
|
const float hAngle = Math::Acos(v.Z) / PI * 180;
|
||||||
|
const float vAngle = Math::Atan2(v.Y, v.X) / PI * 180 + 180;
|
||||||
|
sum += InterpolateBilinear(ComputeFilterPos(hAngle, _hAngles), ComputeFilterPos(vAngle, _vAngles));
|
||||||
|
}
|
||||||
|
integral = static_cast<float>(sum / count);
|
||||||
|
}
|
||||||
|
|
||||||
return maxValue / integral;
|
return maxValue / integral;
|
||||||
}
|
}
|
||||||
|
|
||||||
float IESLoader::InterpolatePoint(int32 X, int32 Y) const
|
float IESLoader::InterpolatePoint(int32 x, int32 y) const
|
||||||
{
|
{
|
||||||
int32 HAnglesNum = _hAngles.Count();
|
x %= _hAngles.Count();
|
||||||
int32 VAnglesNum = _vAngles.Count();
|
y %= _vAngles.Count();
|
||||||
|
return _candalaValues[y + _vAngles.Count() * x];
|
||||||
ASSERT(X >= 0);
|
|
||||||
ASSERT(Y >= 0);
|
|
||||||
|
|
||||||
X %= HAnglesNum;
|
|
||||||
Y %= VAnglesNum;
|
|
||||||
|
|
||||||
ASSERT(X < HAnglesNum);
|
|
||||||
ASSERT(Y < VAnglesNum);
|
|
||||||
|
|
||||||
return _candalaValues[Y + VAnglesNum * X];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float IESLoader::InterpolateBilinear(float fX, float fY) const
|
float IESLoader::InterpolateBilinear(float x, float y) const
|
||||||
{
|
{
|
||||||
int32 X = static_cast<int32>(fX);
|
const int32 xInt = static_cast<int32>(x);
|
||||||
int32 Y = static_cast<int32>(fY);
|
const int32 yInt = static_cast<int32>(y);
|
||||||
|
|
||||||
float fracX = fX - X;
|
const float xFrac = x - xInt;
|
||||||
float fracY = fY - Y;
|
const float yFrac = y - yInt;
|
||||||
|
|
||||||
float p00 = InterpolatePoint(X + 0, Y + 0);
|
const float p00 = InterpolatePoint(xInt + 0, yInt + 0);
|
||||||
float p10 = InterpolatePoint(X + 1, Y + 0);
|
const float p10 = InterpolatePoint(xInt + 1, yInt + 0);
|
||||||
float p01 = InterpolatePoint(X + 0, Y + 1);
|
const float p01 = InterpolatePoint(xInt + 0, yInt + 1);
|
||||||
float p11 = InterpolatePoint(X + 1, Y + 1);
|
const float p11 = InterpolatePoint(xInt + 1, yInt + 1);
|
||||||
|
|
||||||
float p0 = Math::Lerp(p00, p01, fracY);
|
const float p0 = Math::Lerp(p00, p01, yFrac);
|
||||||
float p1 = Math::Lerp(p10, p11, fracY);
|
const float p1 = Math::Lerp(p10, p11, yFrac);
|
||||||
|
|
||||||
return Math::Lerp(p0, p1, fracX);
|
return Math::Lerp(p0, p1, xFrac);
|
||||||
}
|
|
||||||
|
|
||||||
float IESLoader::Interpolate2D(float HAngle, float VAngle) const
|
|
||||||
{
|
|
||||||
float u = ComputeFilterPos(HAngle, _hAngles);
|
|
||||||
float v = ComputeFilterPos(VAngle, _vAngles);
|
|
||||||
|
|
||||||
return InterpolateBilinear(u, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
float IESLoader::Interpolate1D(float VAngle) const
|
|
||||||
{
|
|
||||||
float v = ComputeFilterPos(VAngle, _vAngles);
|
|
||||||
|
|
||||||
float ret = 0.0f;
|
|
||||||
|
|
||||||
uint32 HAnglesNum = static_cast<uint32>(_hAngles.Count());
|
|
||||||
|
|
||||||
for (uint32 i = 0; i < HAnglesNum; i++)
|
|
||||||
{
|
|
||||||
ret += InterpolateBilinear(static_cast<float>(i), v);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret / HAnglesNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
float IESLoader::ComputeMax() const
|
|
||||||
{
|
|
||||||
float result = 0.0f;
|
|
||||||
|
|
||||||
for (int32 i = 0; i < _candalaValues.Count(); i++)
|
|
||||||
result = Math::Max(result, _candalaValues[i]);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
float IESLoader::ComputeFullIntegral()
|
|
||||||
{
|
|
||||||
// Compute only if needed
|
|
||||||
if (_cachedIntegral < 0)
|
|
||||||
{
|
|
||||||
// Monte carlo integration
|
|
||||||
// If quality is a problem we can improve on this algorithm or increase SampleCount
|
|
||||||
|
|
||||||
// Larger number costs more time but improves quality
|
|
||||||
const int32 SamplesCount = 1000000;
|
|
||||||
|
|
||||||
RandomStream randomStream(0x1234);
|
|
||||||
|
|
||||||
double sum = 0;
|
|
||||||
for (uint32 i = 0; i < SamplesCount; i++)
|
|
||||||
{
|
|
||||||
Vector3 v = randomStream.GetUnitVector();
|
|
||||||
|
|
||||||
// http://en.wikipedia.org/wiki/Spherical_coordinate_system
|
|
||||||
|
|
||||||
// 0..180
|
|
||||||
float HAngle = Math::Acos(v.Z) / PI * 180;
|
|
||||||
// 0..360
|
|
||||||
float VAngle = Math::Atan2(v.Y, v.X) / PI * 180 + 180;
|
|
||||||
|
|
||||||
ASSERT(HAngle >= 0 && HAngle <= 180);
|
|
||||||
ASSERT(VAngle >= 0 && VAngle <= 360);
|
|
||||||
|
|
||||||
sum += Interpolate2D(HAngle, VAngle);
|
|
||||||
}
|
|
||||||
|
|
||||||
_cachedIntegral = static_cast<float>(sum / SamplesCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _cachedIntegral;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float IESLoader::ComputeFilterPos(float value, const Array<float>& sortedValues)
|
float IESLoader::ComputeFilterPos(float value, const Array<float>& sortedValues)
|
||||||
@@ -476,9 +309,9 @@ float IESLoader::ComputeFilterPos(float value, const Array<float>& sortedValues)
|
|||||||
// Binary search
|
// Binary search
|
||||||
while (startPos < endPos)
|
while (startPos < endPos)
|
||||||
{
|
{
|
||||||
uint32 testPos = (startPos + endPos + 1) / 2;
|
const uint32 testPos = (startPos + endPos + 1) / 2;
|
||||||
|
|
||||||
float testValue = sortedValues[testPos];
|
const float testValue = sortedValues[testPos];
|
||||||
|
|
||||||
if (value >= testValue)
|
if (value >= testValue)
|
||||||
{
|
{
|
||||||
@@ -496,15 +329,15 @@ float IESLoader::ComputeFilterPos(float value, const Array<float>& sortedValues)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float leftValue = sortedValues[startPos];
|
const float leftValue = sortedValues[startPos];
|
||||||
|
|
||||||
float fraction = 0.0f;
|
float fraction = 0.0f;
|
||||||
|
|
||||||
if (startPos + 1 < static_cast<uint32>(sortedValues.Count()))
|
if (startPos + 1 < static_cast<uint32>(sortedValues.Count()))
|
||||||
{
|
{
|
||||||
// If not at right border
|
// If not at right border
|
||||||
float rightValue = sortedValues[startPos + 1];
|
const float rightValue = sortedValues[startPos + 1];
|
||||||
float deltaValue = rightValue - leftValue;
|
const float deltaValue = rightValue - leftValue;
|
||||||
|
|
||||||
if (deltaValue > 0.0001f)
|
if (deltaValue > 0.0001f)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,16 +6,13 @@
|
|||||||
#include "Engine/Core/Collections/Array.h"
|
#include "Engine/Core/Collections/Array.h"
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// To load the IES file image format. IES files exist for many real world lights. The file stores how much light is emitted in a specific direction.
|
/// Utility for loading IES files and extract light emission information.
|
||||||
/// The data is usually measured but tools to paint IES files exist.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class IESLoader
|
class IESLoader
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
float _brightness;
|
float _brightness = 0;
|
||||||
float _cachedIntegral;
|
|
||||||
|
|
||||||
Array<float> _hAngles;
|
Array<float> _hAngles;
|
||||||
Array<float> _vAngles;
|
Array<float> _vAngles;
|
||||||
Array<float> _candalaValues;
|
Array<float> _candalaValues;
|
||||||
@@ -23,108 +20,39 @@ private:
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Init
|
/// Loads the IES file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IESLoader();
|
/// <param name="buffer">The buffer with data.</param>
|
||||||
|
/// <returns>True if cannot load, otherwise false.</returns>
|
||||||
public:
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Load IES file
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer">Buffer with data</param>
|
|
||||||
/// <returns>True if cannot load, otherwise false</returns>
|
|
||||||
FORCE_INLINE bool Load(const Array<byte>& buffer)
|
|
||||||
{
|
|
||||||
return Load(buffer.Get());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Load IES file
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer">Buffer with data</param>
|
|
||||||
/// <returns>True if cannot load, otherwise false</returns>
|
|
||||||
bool Load(const byte* buffer);
|
bool Load(const byte* buffer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracts IES profile data to R16 format (float).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="output">The result data container.</param>
|
||||||
|
/// <returns>Thr multiplier as the texture is normalized.</returns>
|
||||||
|
float ExtractInR16(Array<byte>& output);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
uint32 GetWidth() const
|
||||||
/// Gets width
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Width in pixels</returns>
|
|
||||||
FORCE_INLINE uint32 GetWidth() const
|
|
||||||
{
|
{
|
||||||
// We use constant size
|
|
||||||
return 256;
|
return 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
uint32 GetHeight() const
|
||||||
/// Gets height
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Height in pixels</returns>
|
|
||||||
FORCE_INLINE uint32 GetHeight() const
|
|
||||||
{
|
{
|
||||||
// We use constant size
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
float GetBrightness() const
|
||||||
/// Gets IES profile brightness value in lumens (always > 0)
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Brightness (in lumens)</returns>
|
|
||||||
FORCE_INLINE float GetBrightness() const
|
|
||||||
{
|
{
|
||||||
return _brightness;
|
return _brightness;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
private:
|
||||||
|
|
||||||
/// <summary>
|
float InterpolatePoint(int32 x, int32 y) const;
|
||||||
/// Extracts IES profile data to R16F format
|
float InterpolateBilinear(float x, float y) const;
|
||||||
/// </summary>
|
|
||||||
/// <param name="output">Result data container</param>
|
|
||||||
/// <returns>Multiplier as the texture is normalized</returns>
|
|
||||||
float ExtractInR16F(Array<byte>& output);
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
float InterpolatePoint(int32 X, int32 Y) const;
|
|
||||||
float InterpolateBilinear(float fX, float fY) const;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Compute the Candala value for a given direction
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="HAngle">HAngle (in degrees e.g. 0..180)</param>
|
|
||||||
/// <param name="VAngle">VAngle (in degrees e.g. 0..180)</param>
|
|
||||||
/// <returns>Sampled value</returns>
|
|
||||||
float Interpolate2D(float HAngle, float VAngle) const;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Compute the Candala value for a given direction (integrates over HAngle)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="VAngle">VAngle (in degrees e.g. 0..180)</param>
|
|
||||||
/// <returns>Sampled value</returns>
|
|
||||||
float Interpolate1D(float VAngle) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates maximum value
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Maximum value</returns>
|
|
||||||
float ComputeMax() const;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Integrate over the unit sphere
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Integral value</returns>
|
|
||||||
float ComputeFullIntegral();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Computes filtering position for given value in sorted set of values
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">Value to find</param>
|
|
||||||
/// <param name="sortedValues">Value to use</param>
|
|
||||||
/// <returns>Filter position</returns>
|
|
||||||
static float ComputeFilterPos(float value, const Array<float>& sortedValues);
|
static float ComputeFilterPos(float value, const Array<float>& sortedValues);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -528,14 +528,14 @@ CreateAssetResult ImportTexture::ImportIES(class CreateAssetContext& context)
|
|||||||
|
|
||||||
// Load IES profile data
|
// Load IES profile data
|
||||||
IESLoader loader;
|
IESLoader loader;
|
||||||
if (loader.Load(fileData))
|
if (loader.Load(fileData.Get()))
|
||||||
{
|
{
|
||||||
return CreateAssetResult::Error;
|
return CreateAssetResult::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract texture data
|
// Extract texture data
|
||||||
Array<byte> rawData;
|
Array<byte> rawData;
|
||||||
const float multiplier = loader.ExtractInR16F(rawData);
|
const float multiplier = loader.ExtractInR16(rawData);
|
||||||
|
|
||||||
// Fill texture header
|
// Fill texture header
|
||||||
TextureHeader textureHeader;
|
TextureHeader textureHeader;
|
||||||
|
|||||||
@@ -70,23 +70,21 @@ Color Color::FromHex(const String& hexString, bool& isValid)
|
|||||||
return FromBytes(r, g, b, a);
|
return FromBytes(r, g, b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
Color Color::FromHSV(const Vector3& hsv)
|
Color Color::FromHSV(float hue, float saturation, float value, float alpha)
|
||||||
{
|
{
|
||||||
const float Hue = hsv.X;
|
const float hDiv60 = hue / 60.0f;
|
||||||
const float Saturation = hsv.Y;
|
const float hDiv60Floor = Math::Floor(hDiv60);
|
||||||
const float Value = hsv.Z;
|
const float hDiv60Fraction = hDiv60 - hDiv60Floor;
|
||||||
|
|
||||||
const float HDiv60 = Hue / 60.0f;
|
const float rgbValues[4] =
|
||||||
const float HDiv60_Floor = floorf(HDiv60);
|
{
|
||||||
const float HDiv60_Fraction = HDiv60 - HDiv60_Floor;
|
value,
|
||||||
|
value * (1.0f - saturation),
|
||||||
const float RGBValues[4] = {
|
value * (1.0f - hDiv60Fraction * saturation),
|
||||||
Value,
|
value * (1.0f - (1.0f - hDiv60Fraction) * saturation),
|
||||||
Value * (1.0f - Saturation),
|
|
||||||
Value * (1.0f - HDiv60_Fraction * Saturation),
|
|
||||||
Value * (1.0f - (1.0f - HDiv60_Fraction) * Saturation),
|
|
||||||
};
|
};
|
||||||
const uint32 RGBSwizzle[6][3] = {
|
const int32 rgbSwizzle[6][3]
|
||||||
|
{
|
||||||
{ 0, 3, 1 },
|
{ 0, 3, 1 },
|
||||||
{ 2, 0, 1 },
|
{ 2, 0, 1 },
|
||||||
{ 1, 0, 3 },
|
{ 1, 0, 3 },
|
||||||
@@ -94,12 +92,17 @@ Color Color::FromHSV(const Vector3& hsv)
|
|||||||
{ 3, 1, 0 },
|
{ 3, 1, 0 },
|
||||||
{ 0, 1, 2 },
|
{ 0, 1, 2 },
|
||||||
};
|
};
|
||||||
const uint32 SwizzleIndex = (uint32)HDiv60_Floor % 6;
|
const int32 swizzleIndex = (int32)hDiv60Floor % 6;
|
||||||
|
|
||||||
return Color(RGBValues[RGBSwizzle[SwizzleIndex][0]],
|
return Color(rgbValues[rgbSwizzle[swizzleIndex][0]],
|
||||||
RGBValues[RGBSwizzle[SwizzleIndex][1]],
|
rgbValues[rgbSwizzle[swizzleIndex][1]],
|
||||||
RGBValues[RGBSwizzle[SwizzleIndex][2]],
|
rgbValues[rgbSwizzle[swizzleIndex][2]],
|
||||||
1.0f);
|
alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
Color Color::FromHSV(const Vector3& hsv, float alpha)
|
||||||
|
{
|
||||||
|
return FromHSV(hsv.X, hsv.Y, hsv.Z, alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
Color Color::Random()
|
Color Color::Random()
|
||||||
|
|||||||
@@ -516,7 +516,7 @@ namespace FlaxEngine
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates RGB color from Hue[0-360], Saturation[0-1] and Value[0-1] paked to XYZ vector.
|
/// Creates RGB color from Hue[0-360], Saturation[0-1] and Value[0-1] packed to XYZ vector.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hsv">The HSV color.</param>
|
/// <param name="hsv">The HSV color.</param>
|
||||||
/// <param name="alpha">The alpha value. Default is 1.</param>
|
/// <param name="alpha">The alpha value. Default is 1.</param>
|
||||||
|
|||||||
@@ -152,12 +152,23 @@ public:
|
|||||||
|
|
||||||
static Color FromHex(const String& hexString, bool& isValid);
|
static Color FromHex(const String& hexString, bool& isValid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates RGB color from Hue[0-360], Saturation[0-1] and Value[0-1].
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hue">The hue angle in degrees [0-360].</param>
|
||||||
|
/// <param name="saturation">The saturation normalized [0-1].</param>
|
||||||
|
/// <param name="value">The value normalized [0-1].</param>
|
||||||
|
/// <param name="alpha">The alpha value. Default is 1.</param>
|
||||||
|
/// <returns>The RGB color.</returns>
|
||||||
|
static Color FromHSV(float hue, float saturation, float value, float alpha = 1.0f);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates RGB color from Hue[0-360], Saturation[0-1] and Value[0-1] packed to XYZ vector.
|
/// Creates RGB color from Hue[0-360], Saturation[0-1] and Value[0-1] packed to XYZ vector.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hsv">The HSV color.</param>
|
/// <param name="hsv">The HSV color.</param>
|
||||||
|
/// <param name="alpha">The alpha value. Default is 1.</param>
|
||||||
/// <returns>The RGB color.</returns>
|
/// <returns>The RGB color.</returns>
|
||||||
static Color FromHSV(const Vector3& hsv);
|
static Color FromHSV(const Vector3& hsv, float alpha = 1.0f);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets random color with opaque alpha.
|
/// Gets random color with opaque alpha.
|
||||||
|
|||||||
@@ -3,6 +3,46 @@
|
|||||||
#include "Math.h"
|
#include "Math.h"
|
||||||
#include "Vector3.h"
|
#include "Vector3.h"
|
||||||
|
|
||||||
|
void Math::SinCos(float angle, float& sine, float& cosine)
|
||||||
|
{
|
||||||
|
sine = sin(angle);
|
||||||
|
cosine = cos(angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 Math::FloorLog2(uint32 value)
|
||||||
|
{
|
||||||
|
// References:
|
||||||
|
// http://codinggorilla.domemtech.com/?p=81
|
||||||
|
// http://en.wikipedia.org/wiki/Binary_logarithm
|
||||||
|
|
||||||
|
uint32 pos = 0;
|
||||||
|
if (value >= 1 << 16)
|
||||||
|
{
|
||||||
|
value >>= 16;
|
||||||
|
pos += 16;
|
||||||
|
}
|
||||||
|
if (value >= 1 << 8)
|
||||||
|
{
|
||||||
|
value >>= 8;
|
||||||
|
pos += 8;
|
||||||
|
}
|
||||||
|
if (value >= 1 << 4)
|
||||||
|
{
|
||||||
|
value >>= 4;
|
||||||
|
pos += 4;
|
||||||
|
}
|
||||||
|
if (value >= 1 << 2)
|
||||||
|
{
|
||||||
|
value >>= 2;
|
||||||
|
pos += 2;
|
||||||
|
}
|
||||||
|
if (value >= 1 << 1)
|
||||||
|
{
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
return value == 0 ? 0 : pos;
|
||||||
|
}
|
||||||
|
|
||||||
Vector3 Math::RotateAboutAxis(const Vector3& normalizedRotationAxis, float angle, const Vector3& positionOnAxis, const Vector3& position)
|
Vector3 Math::RotateAboutAxis(const Vector3& normalizedRotationAxis, float angle, const Vector3& positionOnAxis, const Vector3& position)
|
||||||
{
|
{
|
||||||
// Project position onto the rotation axis and find the closest point on the axis to Position
|
// Project position onto the rotation axis and find the closest point on the axis to Position
|
||||||
|
|||||||
@@ -18,10 +18,10 @@
|
|||||||
#define ZeroTolerance 1e-6f
|
#define ZeroTolerance 1e-6f
|
||||||
|
|
||||||
// Converts radians to degrees.
|
// Converts radians to degrees.
|
||||||
#define RadiansToDegrees (180.f / PI)
|
#define RadiansToDegrees (180.0f / PI)
|
||||||
|
|
||||||
// Converts degrees to radians.
|
// Converts degrees to radians.
|
||||||
#define DegreesToRadians (PI / 180.f)
|
#define DegreesToRadians (PI / 180.0f)
|
||||||
|
|
||||||
namespace Math
|
namespace Math
|
||||||
{
|
{
|
||||||
@@ -31,97 +31,14 @@ namespace Math
|
|||||||
/// <param name="angle">The input angle (in radians).</param>
|
/// <param name="angle">The input angle (in radians).</param>
|
||||||
/// <param name="sine">The output sine value.</param>
|
/// <param name="sine">The output sine value.</param>
|
||||||
/// <param name="cosine">The output cosine value.</param>
|
/// <param name="cosine">The output cosine value.</param>
|
||||||
static void SinCos(float angle, float& sine, float& cosine)
|
FLAXENGINE_API void SinCos(float angle, float& sine, float& cosine);
|
||||||
{
|
|
||||||
#if 1
|
|
||||||
// Map value to y in [-pi,pi], x = 2*pi*quotient + remainder
|
|
||||||
float quotient = PI_INV * 0.5f * angle;
|
|
||||||
if (angle >= 0.0f)
|
|
||||||
{
|
|
||||||
quotient = (float)(int)(quotient + 0.5f);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
quotient = (float)(int)(quotient - 0.5f);
|
|
||||||
}
|
|
||||||
float y = angle - 2.0f * PI * quotient;
|
|
||||||
|
|
||||||
// Map y to [-pi/2,pi/2] with sin(y) = sin(value)
|
|
||||||
float sign;
|
|
||||||
if (y > PI_OVER_2)
|
|
||||||
{
|
|
||||||
y = PI - y;
|
|
||||||
sign = -1.0f;
|
|
||||||
}
|
|
||||||
else if (y < -PI_OVER_2)
|
|
||||||
{
|
|
||||||
y = -PI - y;
|
|
||||||
sign = -1.0f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sign = +1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float y2 = y * y;
|
|
||||||
|
|
||||||
// 11-degree minimax approximation
|
|
||||||
sine = (((((-2.3889859e-08f * y2 + 2.7525562e-06f) * y2 - 0.00019840874f) * y2 + 0.0083333310f) * y2 - 0.16666667f) * y2 + 1.0f) * y;
|
|
||||||
|
|
||||||
// 10-degree minimax approximation
|
|
||||||
const float p = ((((-2.6051615e-07f * y2 + 2.4760495e-05f) * y2 - 0.0013888378f) * y2 + 0.041666638f) * y2 - 0.5f) * y2 + 1.0f;
|
|
||||||
cosine = sign * p;
|
|
||||||
#else
|
|
||||||
sine = sin(angle);
|
|
||||||
cosine = cos(angle);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Computes the base 2 logarithm for an integer value that is greater than 0. The result is rounded down to the nearest integer.
|
/// Computes the base 2 logarithm for an integer value that is greater than 0. The result is rounded down to the nearest integer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The value to compute the log of.</param>
|
/// <param name="value">The value to compute the log of.</param>
|
||||||
/// <returns>The Log2 of value. 0 if value is 0.</returns>
|
/// <returns>The Log2 of value. 0 if value is 0.</returns>
|
||||||
static uint32 FloorLog2(uint32 value)
|
FLAXENGINE_API uint32 FloorLog2(uint32 value);
|
||||||
{
|
|
||||||
// Reference implementation
|
|
||||||
// uint32 bit = 32;
|
|
||||||
// for (; bit > 0;)
|
|
||||||
// {
|
|
||||||
// bit--;
|
|
||||||
// if (value & (1 << bit))
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// return bit;
|
|
||||||
|
|
||||||
// Optimized version http://codinggorilla.domemtech.com/?p=81 or http://en.wikipedia.org/wiki/Binary_logarithm but modified to return 0 for a input value of 0
|
|
||||||
uint32 pos = 0;
|
|
||||||
if (value >= 1 << 16)
|
|
||||||
{
|
|
||||||
value >>= 16;
|
|
||||||
pos += 16;
|
|
||||||
}
|
|
||||||
if (value >= 1 << 8)
|
|
||||||
{
|
|
||||||
value >>= 8;
|
|
||||||
pos += 8;
|
|
||||||
}
|
|
||||||
if (value >= 1 << 4)
|
|
||||||
{
|
|
||||||
value >>= 4;
|
|
||||||
pos += 4;
|
|
||||||
}
|
|
||||||
if (value >= 1 << 2)
|
|
||||||
{
|
|
||||||
value >>= 2;
|
|
||||||
pos += 2;
|
|
||||||
}
|
|
||||||
if (value >= 1 << 1)
|
|
||||||
{
|
|
||||||
pos += 1;
|
|
||||||
}
|
|
||||||
return value == 0 ? 0 : pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
static FORCE_INLINE float Trunc(float value)
|
static FORCE_INLINE float Trunc(float value)
|
||||||
{
|
{
|
||||||
@@ -712,68 +629,38 @@ namespace Math
|
|||||||
|
|
||||||
static float ClampAxis(float angle)
|
static float ClampAxis(float angle)
|
||||||
{
|
{
|
||||||
// returns angle in the range (-360,360)
|
|
||||||
angle = Mod(angle, 360.f);
|
angle = Mod(angle, 360.f);
|
||||||
|
if (angle < 0.0f)
|
||||||
if (angle < 0.f)
|
angle += 360.0f;
|
||||||
{
|
|
||||||
// shift to [0,360) range
|
|
||||||
angle += 360.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
return angle;
|
return angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
static float NormalizeAxis(float angle)
|
static float NormalizeAxis(float angle)
|
||||||
{
|
{
|
||||||
// returns angle in the range [0,360)
|
|
||||||
angle = ClampAxis(angle);
|
angle = ClampAxis(angle);
|
||||||
|
|
||||||
if (angle > 180.f)
|
if (angle > 180.f)
|
||||||
{
|
|
||||||
// shift to (-180,180]
|
|
||||||
angle -= 360.f;
|
angle -= 360.f;
|
||||||
}
|
|
||||||
|
|
||||||
return angle;
|
return angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the smallest angle between two headings (in radians).
|
// Find the smallest angle between two headings (in radians).
|
||||||
static float FindDeltaAngle(float a1, float a2)
|
static float FindDeltaAngle(float a1, float a2)
|
||||||
{
|
{
|
||||||
// Find the difference
|
float delta = a2 - a1;
|
||||||
float Delta = a2 - a1;
|
if (delta > PI)
|
||||||
|
delta = delta - PI * 2.0f;
|
||||||
// If change is larger than PI
|
else if (delta < -PI)
|
||||||
if (Delta > PI)
|
delta = delta + PI * 2.0f;
|
||||||
{
|
return delta;
|
||||||
// Flip to negative equivalent
|
|
||||||
Delta = Delta - PI * 2.0f;
|
|
||||||
}
|
|
||||||
else if (Delta < -PI)
|
|
||||||
{
|
|
||||||
// Otherwise, if change is smaller than -PI
|
|
||||||
// Flip to positive equivalent
|
|
||||||
Delta = Delta + PI * 2.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return delta in [-PI,PI] range
|
|
||||||
return Delta;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given a heading which may be outside the +/- PI range, 'unwind' it back into that range
|
// Given a heading which may be outside the +/- PI range, 'unwind' it back into that range
|
||||||
static float UnwindRadians(float a)
|
static float UnwindRadians(float a)
|
||||||
{
|
{
|
||||||
while (a > PI)
|
while (a > PI)
|
||||||
{
|
|
||||||
a -= (float)PI * 2.0f;
|
a -= (float)PI * 2.0f;
|
||||||
}
|
|
||||||
|
|
||||||
while (a < -PI)
|
while (a < -PI)
|
||||||
{
|
|
||||||
a += (float)PI * 2.0f;
|
a += (float)PI * 2.0f;
|
||||||
}
|
|
||||||
|
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -781,15 +668,9 @@ namespace Math
|
|||||||
static float UnwindDegrees(float a)
|
static float UnwindDegrees(float a)
|
||||||
{
|
{
|
||||||
while (a > 180.f)
|
while (a > 180.f)
|
||||||
{
|
|
||||||
a -= 360.f;
|
a -= 360.f;
|
||||||
}
|
|
||||||
|
|
||||||
while (a < -180.f)
|
while (a < -180.f)
|
||||||
{
|
|
||||||
a += 360.f;
|
a += 360.f;
|
||||||
}
|
|
||||||
|
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -818,13 +699,9 @@ namespace Math
|
|||||||
static float SmoothStep(float a, float b, float x)
|
static float SmoothStep(float a, float b, float x)
|
||||||
{
|
{
|
||||||
if (x < a)
|
if (x < a)
|
||||||
{
|
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
if (x >= b)
|
if (x >= b)
|
||||||
{
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
|
||||||
const float fraction = (x - a) / (b - a);
|
const float fraction = (x - a) / (b - a);
|
||||||
return fraction * fraction * (3.0f - 2.0f * fraction);
|
return fraction * fraction * (3.0f - 2.0f * fraction);
|
||||||
}
|
}
|
||||||
@@ -852,8 +729,8 @@ namespace Math
|
|||||||
template<class T>
|
template<class T>
|
||||||
static FORCE_INLINE T InterpEaseIn(const T& a, const T& b, float alpha, float exponent)
|
static FORCE_INLINE T InterpEaseIn(const T& a, const T& b, float alpha, float exponent)
|
||||||
{
|
{
|
||||||
float const modifiedAlpha = Pow(alpha, exponent);
|
const float blend = Pow(alpha, exponent);
|
||||||
return Lerp<T>(a, b, modifiedAlpha);
|
return Lerp<T>(a, b, blend);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -862,8 +739,8 @@ namespace Math
|
|||||||
template<class T>
|
template<class T>
|
||||||
static FORCE_INLINE T InterpEaseOut(const T& a, const T& b, float alpha, float exponent)
|
static FORCE_INLINE T InterpEaseOut(const T& a, const T& b, float alpha, float exponent)
|
||||||
{
|
{
|
||||||
float const modifiedAlpha = 1.f - Pow(1.f - alpha, exponent);
|
const float blend = 1.f - Pow(1.f - alpha, exponent);
|
||||||
return Lerp<T>(a, b, modifiedAlpha);
|
return Lerp<T>(a, b, blend);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -881,8 +758,8 @@ namespace Math
|
|||||||
template<class T>
|
template<class T>
|
||||||
static FORCE_INLINE T InterpSinIn(const T& a, const T& b, float alpha)
|
static FORCE_INLINE T InterpSinIn(const T& a, const T& b, float alpha)
|
||||||
{
|
{
|
||||||
float const modifiedAlpha = -1.f * Cos(alpha * PI_HALF) + 1.f;
|
const float blend = -1.f * Cos(alpha * PI_HALF) + 1.f;
|
||||||
return Lerp<T>(a, b, modifiedAlpha);
|
return Lerp<T>(a, b, blend);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -891,8 +768,8 @@ namespace Math
|
|||||||
template<class T>
|
template<class T>
|
||||||
static FORCE_INLINE T InterpSinOut(const T& a, const T& b, float alpha)
|
static FORCE_INLINE T InterpSinOut(const T& a, const T& b, float alpha)
|
||||||
{
|
{
|
||||||
float const modifiedAlpha = Sin(alpha * PI_HALF);
|
const float blend = Sin(alpha * PI_HALF);
|
||||||
return Lerp<T>(a, b, modifiedAlpha);
|
return Lerp<T>(a, b, blend);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -910,8 +787,8 @@ namespace Math
|
|||||||
template<class T>
|
template<class T>
|
||||||
static FORCE_INLINE T InterpExpoIn(const T& a, const T& b, float alpha)
|
static FORCE_INLINE T InterpExpoIn(const T& a, const T& b, float alpha)
|
||||||
{
|
{
|
||||||
float const modifiedAlpha = alpha == 0.f ? 0.f : Pow(2.f, 10.f * (alpha - 1.f));
|
const float blend = alpha == 0.f ? 0.f : Pow(2.f, 10.f * (alpha - 1.f));
|
||||||
return Lerp<T>(a, b, modifiedAlpha);
|
return Lerp<T>(a, b, blend);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -920,8 +797,8 @@ namespace Math
|
|||||||
template<class T>
|
template<class T>
|
||||||
static FORCE_INLINE T InterpExpoOut(const T& a, const T& b, float alpha)
|
static FORCE_INLINE T InterpExpoOut(const T& a, const T& b, float alpha)
|
||||||
{
|
{
|
||||||
float const modifiedAlpha = alpha == 1.f ? 1.f : -Pow(2.f, -10.f * alpha) + 1.f;
|
const float blend = alpha == 1.f ? 1.f : -Pow(2.f, -10.f * alpha) + 1.f;
|
||||||
return Lerp<T>(a, b, modifiedAlpha);
|
return Lerp<T>(a, b, blend);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -939,8 +816,8 @@ namespace Math
|
|||||||
template<class T>
|
template<class T>
|
||||||
static FORCE_INLINE T InterpCircularIn(const T& a, const T& b, float alpha)
|
static FORCE_INLINE T InterpCircularIn(const T& a, const T& b, float alpha)
|
||||||
{
|
{
|
||||||
float const modifiedAlpha = -1.f * (Sqrt(1.f - alpha * alpha) - 1.f);
|
const float blend = -1.f * (Sqrt(1.f - alpha * alpha) - 1.f);
|
||||||
return Lerp<T>(a, b, modifiedAlpha);
|
return Lerp<T>(a, b, blend);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -950,8 +827,8 @@ namespace Math
|
|||||||
static FORCE_INLINE T InterpCircularOut(const T& a, const T& b, float alpha)
|
static FORCE_INLINE T InterpCircularOut(const T& a, const T& b, float alpha)
|
||||||
{
|
{
|
||||||
alpha -= 1.f;
|
alpha -= 1.f;
|
||||||
float const modifiedAlpha = Sqrt(1.f - alpha * alpha);
|
const float blend = Sqrt(1.f - alpha * alpha);
|
||||||
return Lerp<T>(a, b, modifiedAlpha);
|
return Lerp<T>(a, b, blend);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ Vector3 Float1010102::ToVector3() const
|
|||||||
|
|
||||||
FloatR11G11B10::FloatR11G11B10(float x, float y, float z)
|
FloatR11G11B10::FloatR11G11B10(float x, float y, float z)
|
||||||
{
|
{
|
||||||
|
// Reference: https://github.com/microsoft/DirectXMath
|
||||||
|
|
||||||
uint32 iValue[4];
|
uint32 iValue[4];
|
||||||
iValue[0] = *(uint32*)&x;
|
iValue[0] = *(uint32*)&x;
|
||||||
iValue[1] = *(uint32*)&y;
|
iValue[1] = *(uint32*)&y;
|
||||||
@@ -197,6 +199,8 @@ FloatR11G11B10::operator Vector3() const
|
|||||||
|
|
||||||
Vector3 FloatR11G11B10::ToVector3() const
|
Vector3 FloatR11G11B10::ToVector3() const
|
||||||
{
|
{
|
||||||
|
// Reference: https://github.com/microsoft/DirectXMath
|
||||||
|
|
||||||
uint32 result[4];
|
uint32 result[4];
|
||||||
uint32 mantissa;
|
uint32 mantissa;
|
||||||
uint32 exponent;
|
uint32 exponent;
|
||||||
|
|||||||
@@ -360,8 +360,8 @@ void Quaternion::FindBetween(const Vector3& from, const Vector3& to, Quaternion&
|
|||||||
if (w < 1.e-6f * normFromNormTo)
|
if (w < 1.e-6f * normFromNormTo)
|
||||||
{
|
{
|
||||||
result = Math::Abs(from.X) > Math::Abs(from.Z)
|
result = Math::Abs(from.X) > Math::Abs(from.Z)
|
||||||
? Quaternion(-from.Y, from.X, 0.f, 0.f)
|
? Quaternion(-from.Y, from.X, 0.0f, 0.0f)
|
||||||
: Quaternion(0.f, -from.Z, from.Y, 0.f);
|
: Quaternion(0.0f, -from.Z, from.Y, 0.0f);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -311,39 +311,32 @@ void Vector3::Unproject(const Vector3& vector, float x, float y, float width, fl
|
|||||||
|
|
||||||
void Vector3::CreateOrthonormalBasis(Vector3& xAxis, Vector3& yAxis, Vector3& zAxis)
|
void Vector3::CreateOrthonormalBasis(Vector3& xAxis, Vector3& yAxis, Vector3& zAxis)
|
||||||
{
|
{
|
||||||
// Project the X and Y axes onto the plane perpendicular to the Z axis.
|
|
||||||
xAxis -= (xAxis | zAxis) / (zAxis | zAxis) * zAxis;
|
xAxis -= (xAxis | zAxis) / (zAxis | zAxis) * zAxis;
|
||||||
yAxis -= (yAxis | zAxis) / (zAxis | zAxis) * zAxis;
|
yAxis -= (yAxis | zAxis) / (zAxis | zAxis) * zAxis;
|
||||||
|
|
||||||
// If the X axis was parallel to the Z axis, choose a vector which is orthogonal to the Y and Z axes.
|
|
||||||
if (xAxis.LengthSquared() < ZeroTolerance)
|
if (xAxis.LengthSquared() < ZeroTolerance)
|
||||||
{
|
|
||||||
xAxis = yAxis ^ zAxis;
|
xAxis = yAxis ^ zAxis;
|
||||||
}
|
|
||||||
|
|
||||||
// If the Y axis was parallel to the Z axis, choose a vector which is orthogonal to the X and Z axes.
|
|
||||||
if (yAxis.LengthSquared() < ZeroTolerance)
|
if (yAxis.LengthSquared() < ZeroTolerance)
|
||||||
{
|
|
||||||
yAxis = xAxis ^ zAxis;
|
yAxis = xAxis ^ zAxis;
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize the basis vectors.
|
|
||||||
xAxis.Normalize();
|
xAxis.Normalize();
|
||||||
yAxis.Normalize();
|
yAxis.Normalize();
|
||||||
zAxis.Normalize();
|
zAxis.Normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vector3::FindBestAxisVectors(Vector3& Axis1, Vector3& Axis2) const
|
void Vector3::FindBestAxisVectors(Vector3& firstAxis, Vector3& secondAxis) const
|
||||||
{
|
{
|
||||||
const float absX = Math::Abs(X);
|
const float absX = Math::Abs(X);
|
||||||
const float absY = Math::Abs(Y);
|
const float absY = Math::Abs(Y);
|
||||||
const float absZ = Math::Abs(Z);
|
const float absZ = Math::Abs(Z);
|
||||||
|
|
||||||
if (absZ > absX && absZ > absY)
|
if (absZ > absX && absZ > absY)
|
||||||
Axis1 = Vector3(1, 0, 0);
|
firstAxis = Vector3(1, 0, 0);
|
||||||
else
|
else
|
||||||
Axis1 = Vector3(0, 0, 1);
|
firstAxis = Vector3(0, 0, 1);
|
||||||
Axis1 = (Axis1 - *this * (Axis1 | *this)).GetNormalized();
|
|
||||||
Axis2 = Axis1 ^ *this;
|
firstAxis = (firstAxis - *this * (firstAxis | *this)).GetNormalized();
|
||||||
|
secondAxis = firstAxis ^ *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Vector3::TriangleArea(const Vector3& v0, const Vector3& v1, const Vector3& v2)
|
float Vector3::TriangleArea(const Vector3& v0, const Vector3& v1, const Vector3& v2)
|
||||||
|
|||||||
@@ -866,9 +866,9 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the best arbitrary axis vectors to represent U and V axes of a plane, by using this vector as the normal of the plane.
|
/// Finds the best arbitrary axis vectors to represent U and V axes of a plane, by using this vector as the normal of the plane.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Axis1">The reference to first axis.</param>
|
/// <param name="firstAxis">The reference to first axis.</param>
|
||||||
/// <param name="Axis2">The reference to second axis.</param>
|
/// <param name="secondAxis">The reference to second axis.</param>
|
||||||
void FindBestAxisVectors(Vector3& Axis1, Vector3& Axis2) const;
|
void FindBestAxisVectors(Vector3& firstAxis, Vector3& secondAxis) const;
|
||||||
|
|
||||||
static Vector3 Round(const Vector3& v)
|
static Vector3 Round(const Vector3& v)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -138,9 +138,9 @@ public:
|
|||||||
float l;
|
float l;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
result.X = GetFraction() * 2.f - 1.f;
|
result.X = GetFraction() * 2.0f - 1.0f;
|
||||||
result.Y = GetFraction() * 2.f - 1.f;
|
result.Y = GetFraction() * 2.0f - 1.0f;
|
||||||
result.Z = GetFraction() * 2.f - 1.f;
|
result.Z = GetFraction() * 2.0f - 1.0f;
|
||||||
l = result.LengthSquared();
|
l = result.LengthSquared();
|
||||||
} while (l > 1.0f || l < ZeroTolerance);
|
} while (l > 1.0f || l < ZeroTolerance);
|
||||||
return Vector3::Normalize(result);
|
return Vector3::Normalize(result);
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
#include "Engine/Platform/Platform.h"
|
#include "Engine/Platform/Platform.h"
|
||||||
#include "Engine/Core/Math/Math.h"
|
#include "Engine/Core/Math/Math.h"
|
||||||
|
|
||||||
const int32 DateTime::DaysPerMonth[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
const int32 DateTime::CachedDaysPerMonth[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||||
const int32 DateTime::DaysToMonth[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
|
const int32 DateTime::CachedDaysToMonth[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
|
||||||
|
|
||||||
DateTime::DateTime(int32 year, int32 month, int32 day, int32 hour, int32 minute, int32 second, int32 millisecond)
|
DateTime::DateTime(int32 year, int32 month, int32 day, int32 hour, int32 minute, int32 second, int32 millisecond)
|
||||||
{
|
{
|
||||||
@@ -17,7 +17,7 @@ DateTime::DateTime(int32 year, int32 month, int32 day, int32 hour, int32 minute,
|
|||||||
totalDays++;
|
totalDays++;
|
||||||
year--;
|
year--;
|
||||||
month--;
|
month--;
|
||||||
totalDays += year * 365 + year / 4 - year / 100 + year / 400 + DaysToMonth[month] + day - 1;
|
totalDays += year * 365 + year / 4 - year / 100 + year / 400 + CachedDaysToMonth[month] + day - 1;
|
||||||
Ticks = totalDays * Constants::TicksPerDay
|
Ticks = totalDays * Constants::TicksPerDay
|
||||||
+ hour * Constants::TicksPerHour
|
+ hour * Constants::TicksPerHour
|
||||||
+ minute * Constants::TicksPerMinute
|
+ minute * Constants::TicksPerMinute
|
||||||
@@ -27,7 +27,7 @@ DateTime::DateTime(int32 year, int32 month, int32 day, int32 hour, int32 minute,
|
|||||||
|
|
||||||
void DateTime::GetDate(int32& year, int32& month, int32& day) const
|
void DateTime::GetDate(int32& year, int32& month, int32& day) const
|
||||||
{
|
{
|
||||||
// Based on FORTRAN code in:
|
// Based on:
|
||||||
// Fliegel, H. F. and van Flandern, T. C.,
|
// Fliegel, H. F. and van Flandern, T. C.,
|
||||||
// Communications of the ACM, Vol. 11, No. 10 (October 1968).
|
// Communications of the ACM, Vol. 11, No. 10 (October 1968).
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ int32 DateTime::DaysInMonth(int32 year, int32 month)
|
|||||||
ASSERT_LOW_LAYER((month >= 1) && (month <= 12));
|
ASSERT_LOW_LAYER((month >= 1) && (month <= 12));
|
||||||
if (month == 2 && IsLeapYear(year))
|
if (month == 2 && IsLeapYear(year))
|
||||||
return 29;
|
return 29;
|
||||||
return DaysPerMonth[month];
|
return CachedDaysPerMonth[month];
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 DateTime::DaysInYear(int32 year)
|
int32 DateTime::DaysInYear(int32 year)
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ API_STRUCT(InBuild, Namespace="System") struct FLAXENGINE_API DateTime
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
static const int32 DaysPerMonth[];
|
static const int32 CachedDaysPerMonth[];
|
||||||
static const int32 DaysToMonth[];
|
static const int32 CachedDaysToMonth[];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|||||||
@@ -417,27 +417,23 @@ public:
|
|||||||
|
|
||||||
int32 Replace(const T* searchText, const T* replacementText, StringSearchCase searchCase = StringSearchCase::CaseSensitive)
|
int32 Replace(const T* searchText, const T* replacementText, StringSearchCase searchCase = StringSearchCase::CaseSensitive)
|
||||||
{
|
{
|
||||||
int32 replacementCount = 0;
|
int32 replacedCount = 0;
|
||||||
|
if (HasChars() && searchText && *searchText && replacementText && (searchCase == StringSearchCase::IgnoreCase || StringUtils::Compare(searchText, replacementText) != 0))
|
||||||
if (HasChars()
|
|
||||||
&& searchText != nullptr && *searchText != 0
|
|
||||||
&& replacementText != nullptr && (searchCase == StringSearchCase::IgnoreCase || StringUtils::Compare(searchText, replacementText) != 0))
|
|
||||||
{
|
{
|
||||||
const int32 numCharsToReplace = StringUtils::Length(searchText);
|
const int32 searchTextLength = StringUtils::Length(searchText);
|
||||||
const int32 numCharsToInsert = StringUtils::Length(replacementText);
|
const int32 replacementTextLength = StringUtils::Length(replacementText);
|
||||||
|
if (searchTextLength == replacementTextLength)
|
||||||
if (numCharsToInsert == numCharsToReplace)
|
|
||||||
{
|
{
|
||||||
T* pos = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(_data, searchText) : StringUtils::Find(_data, searchText));
|
T* pos = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(_data, searchText) : StringUtils::Find(_data, searchText));
|
||||||
while (pos != nullptr)
|
while (pos != nullptr)
|
||||||
{
|
{
|
||||||
replacementCount++;
|
replacedCount++;
|
||||||
|
|
||||||
for (int32 i = 0; i < numCharsToInsert; i++)
|
for (int32 i = 0; i < replacementTextLength; i++)
|
||||||
pos[i] = replacementText[i];
|
pos[i] = replacementText[i];
|
||||||
|
|
||||||
if (pos + numCharsToReplace - **this < Length())
|
if (pos + searchTextLength - **this < Length())
|
||||||
pos = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(pos + numCharsToReplace, searchText) : StringUtils::Find(pos + numCharsToReplace, searchText));
|
pos = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(pos + searchTextLength, searchText) : StringUtils::Find(pos + searchTextLength, searchText));
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -448,14 +444,14 @@ public:
|
|||||||
T* searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText));
|
T* searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText));
|
||||||
while (searchPosition != nullptr)
|
while (searchPosition != nullptr)
|
||||||
{
|
{
|
||||||
replacementCount++;
|
replacedCount++;
|
||||||
readPosition = searchPosition + numCharsToReplace;
|
readPosition = searchPosition + searchTextLength;
|
||||||
searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText));
|
searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto oldLength = _length;
|
const auto oldLength = _length;
|
||||||
const auto oldData = _data;
|
const auto oldData = _data;
|
||||||
_length += replacementCount * (numCharsToInsert - numCharsToReplace);
|
_length += replacedCount * (replacementTextLength - searchTextLength);
|
||||||
_data = (T*)Platform::Allocate((_length + 1) * sizeof(T), 16);
|
_data = (T*)Platform::Allocate((_length + 1) * sizeof(T), 16);
|
||||||
|
|
||||||
T* writePosition = _data;
|
T* writePosition = _data;
|
||||||
@@ -467,10 +463,10 @@ public:
|
|||||||
Platform::MemoryCopy(writePosition, readPosition, writeOffset * sizeof(T));
|
Platform::MemoryCopy(writePosition, readPosition, writeOffset * sizeof(T));
|
||||||
writePosition += writeOffset;
|
writePosition += writeOffset;
|
||||||
|
|
||||||
Platform::MemoryCopy(writePosition, replacementText, numCharsToInsert * sizeof(T));
|
Platform::MemoryCopy(writePosition, replacementText, replacementTextLength * sizeof(T));
|
||||||
writePosition += numCharsToInsert;
|
writePosition += replacementTextLength;
|
||||||
|
|
||||||
readPosition = searchPosition + numCharsToReplace;
|
readPosition = searchPosition + searchTextLength;
|
||||||
searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText));
|
searchPosition = (T*)(searchCase == StringSearchCase::IgnoreCase ? StringUtils::FindIgnoreCase(readPosition, searchText) : StringUtils::Find(readPosition, searchText));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,7 +478,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return replacementCount;
|
return replacedCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ public:
|
|||||||
ASSERT(_subresourceState.IsEmpty() && subresourceCount > 0);
|
ASSERT(_subresourceState.IsEmpty() && subresourceCount > 0);
|
||||||
|
|
||||||
// Allocate space for per-subresource tracking structures
|
// Allocate space for per-subresource tracking structures
|
||||||
// Note: for resources with single subresource we don't use this table
|
|
||||||
if (usePerSubresourceTracking && subresourceCount > 1)
|
if (usePerSubresourceTracking && subresourceCount > 1)
|
||||||
_subresourceState.Resize(subresourceCount, false);
|
_subresourceState.Resize(subresourceCount, false);
|
||||||
|
|
||||||
@@ -87,9 +86,8 @@ public:
|
|||||||
return state == _resourceState;
|
return state == _resourceState;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All subresources must be individually checked
|
// Check all subresources
|
||||||
const uint32 numSubresourceStates = _subresourceState.Count();
|
for (int32 i = 0; i < _subresourceState.Count(); i++)
|
||||||
for (uint32 i = 0; i < numSubresourceStates; i++)
|
|
||||||
{
|
{
|
||||||
if (_subresourceState[i] != state)
|
if (_subresourceState[i] != state)
|
||||||
{
|
{
|
||||||
@@ -116,10 +114,8 @@ public:
|
|||||||
_allSubresourcesSame = 1;
|
_allSubresourcesSame = 1;
|
||||||
_resourceState = state;
|
_resourceState = state;
|
||||||
|
|
||||||
// State is now tracked per-resource, so _subresourceState should not be read
|
|
||||||
#if BUILD_DEBUG
|
#if BUILD_DEBUG
|
||||||
const uint32 numSubresourceStates = _subresourceState.Count();
|
for (int32 i = 0; i < _subresourceState.Count(); i++)
|
||||||
for (uint32 i = 0; i < numSubresourceStates; i++)
|
|
||||||
{
|
{
|
||||||
_subresourceState[i] = InvalidState;
|
_subresourceState[i] = InvalidState;
|
||||||
}
|
}
|
||||||
@@ -140,15 +136,13 @@ public:
|
|||||||
// If state was previously tracked on a per-resource level, then transition to per-subresource tracking
|
// If state was previously tracked on a per-resource level, then transition to per-subresource tracking
|
||||||
if (_allSubresourcesSame)
|
if (_allSubresourcesSame)
|
||||||
{
|
{
|
||||||
const int32 numSubresourceStates = _subresourceState.Count();
|
for (int32 i = 0; i < _subresourceState.Count(); i++)
|
||||||
for (int32 i = 0; i < numSubresourceStates; i++)
|
|
||||||
{
|
{
|
||||||
_subresourceState[i] = _resourceState;
|
_subresourceState[i] = _resourceState;
|
||||||
}
|
}
|
||||||
|
|
||||||
_allSubresourcesSame = 0;
|
_allSubresourcesSame = 0;
|
||||||
|
|
||||||
// State is now tracked per-subresource, so _resourceState should not be read
|
|
||||||
#if BUILD_DEBUG
|
#if BUILD_DEBUG
|
||||||
_resourceState = InvalidState;
|
_resourceState = InvalidState;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ bool MeshData::GenerateLightmapUVs()
|
|||||||
// Generate UVs
|
// Generate UVs
|
||||||
std::vector<DirectX::UVAtlasVertex> vb;
|
std::vector<DirectX::UVAtlasVertex> vb;
|
||||||
std::vector<uint8_t> ib;
|
std::vector<uint8_t> ib;
|
||||||
float outStretch = 0.f;
|
float outStretch = 0.0f;
|
||||||
size_t outCharts = 0;
|
size_t outCharts = 0;
|
||||||
std::vector<uint32_t> facePartitioning;
|
std::vector<uint32_t> facePartitioning;
|
||||||
std::vector<uint32_t> vertexRemapArray;
|
std::vector<uint32_t> vertexRemapArray;
|
||||||
|
|||||||
@@ -144,23 +144,17 @@ void GPUContextDX12::SetResourceState(ResourceOwnerDX12* resource, D3D12_RESOURC
|
|||||||
auto& state = resource->State;
|
auto& state = resource->State;
|
||||||
if (subresourceIndex == D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES && !state.AreAllSubresourcesSame())
|
if (subresourceIndex == D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES && !state.AreAllSubresourcesSame())
|
||||||
{
|
{
|
||||||
// Slow path. Want to transition the entire resource (with multiple subresources). But they aren't in the same state.
|
// Slow path because we have to transition the entire resource with multiple subresources that aren't in the same state
|
||||||
|
|
||||||
const uint32 subresourceCount = resource->GetSubresourcesCount();
|
const uint32 subresourceCount = resource->GetSubresourcesCount();
|
||||||
for (uint32 i = 0; i < subresourceCount; i++)
|
for (uint32 i = 0; i < subresourceCount; i++)
|
||||||
{
|
{
|
||||||
const D3D12_RESOURCE_STATES before = state.GetSubresourceState(i);
|
const D3D12_RESOURCE_STATES before = state.GetSubresourceState(i);
|
||||||
|
|
||||||
// We're not using IsTransitionNeeded() because we do want to transition even if 'after' is a subset of 'before'
|
|
||||||
// This is so that we can ensure all subresources are in the same state, simplifying future barriers
|
|
||||||
if (before != after)
|
if (before != after)
|
||||||
{
|
{
|
||||||
AddTransitionBarrier(resource, before, after, i);
|
AddTransitionBarrier(resource, before, after, i);
|
||||||
state.SetSubresourceState(i, after);
|
state.SetSubresourceState(i, after);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The entire resource should now be in the after state on this command list
|
|
||||||
ASSERT(state.CheckResourceState(after));
|
ASSERT(state.CheckResourceState(after));
|
||||||
state.SetResourceState(after);
|
state.SetResourceState(after);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,12 +172,8 @@ public:
|
|||||||
{
|
{
|
||||||
if (sampleCount <= 8)
|
if (sampleCount <= 8)
|
||||||
{
|
{
|
||||||
// 0 has better quality (a more even distribution)
|
|
||||||
// Higher quality levels might be useful for non box filtered AA or when using weighted samples.
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not supported.
|
|
||||||
return 0xffffffff;
|
return 0xffffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +183,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
#if PLATFORM_XBOX_SCARLETT
|
#if PLATFORM_XBOX_SCARLETT
|
||||||
void updateFrameEvents();
|
void updateFrameEvents();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -33,14 +33,11 @@ public:
|
|||||||
/// Returns true if resource state transition is needed in order to use resource in given state.
|
/// Returns true if resource state transition is needed in order to use resource in given state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="currentState">The current resource state.</param>
|
/// <param name="currentState">The current resource state.</param>
|
||||||
/// <param name="targeState">the destination resource state.</param>
|
/// <param name="targetState">the destination resource state.</param>
|
||||||
/// <returns>True if need to perform a transition, otherwise false.</returns>
|
/// <returns>True if need to perform a transition, otherwise false.</returns>
|
||||||
static bool IsTransitionNeeded(D3D12_RESOURCE_STATES currentState, D3D12_RESOURCE_STATES targeState)
|
static bool IsTransitionNeeded(D3D12_RESOURCE_STATES currentState, D3D12_RESOURCE_STATES targetState)
|
||||||
{
|
{
|
||||||
// If 'targeState' is a subset of 'before', then there's no need for a transition
|
return currentState != targetState && ((currentState | targetState) != currentState || targetState == D3D12_RESOURCE_STATE_COMMON);
|
||||||
// Note: COMMON is an oddball state that doesn't follow the RESOURE_STATE pattern of
|
|
||||||
// having exactly one bit set so we need to special case these
|
|
||||||
return currentState != targeState && ((currentState | targeState) != currentState || targeState == D3D12_RESOURCE_STATE_COMMON);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -267,43 +267,33 @@ protected:
|
|||||||
// [GPUDevice]
|
// [GPUDevice]
|
||||||
bool Init() override
|
bool Init() override
|
||||||
{
|
{
|
||||||
// Cache adapter description
|
|
||||||
const DXGI_ADAPTER_DESC& adapterDesc = _adapter->Description;
|
const DXGI_ADAPTER_DESC& adapterDesc = _adapter->Description;
|
||||||
const uint64 dedicatedVideoMemory = static_cast<uint64>(adapterDesc.DedicatedVideoMemory);
|
const uint64 dedicatedVideoMemory = static_cast<uint64>(adapterDesc.DedicatedVideoMemory);
|
||||||
const uint64 dedicatedSystemMemory = static_cast<uint64>(adapterDesc.DedicatedSystemMemory);
|
const uint64 dedicatedSystemMemory = static_cast<uint64>(adapterDesc.DedicatedSystemMemory);
|
||||||
const uint64 sharedSystemMemory = static_cast<uint64>(adapterDesc.SharedSystemMemory);
|
const uint64 sharedSystemMemory = static_cast<uint64>(adapterDesc.SharedSystemMemory);
|
||||||
|
|
||||||
// Total amount of system memory clamped to 8 GB
|
|
||||||
const uint64 totalPhysicalMemory = Math::Min(Platform::GetMemoryStats().TotalPhysicalMemory, 8Ull * 1024Ull * 1024Ull * 1024Ull);
|
|
||||||
|
|
||||||
// Consider 50% of the shared memory but max 25% of total system memory
|
|
||||||
const uint64 consideredSharedSystemMemory = Math::Min(sharedSystemMemory / 2Ull, totalPhysicalMemory / 4Ull);
|
|
||||||
|
|
||||||
// Calculate total GPU memory
|
// Calculate total GPU memory
|
||||||
|
const uint64 totalPhysicalMemory = Math::Min(Platform::GetMemoryStats().TotalPhysicalMemory, 16Ull * 1024Ull * 1024Ull * 1024Ull);
|
||||||
|
const uint64 totalSystemMemory = Math::Min(sharedSystemMemory / 2Ull, totalPhysicalMemory / 4Ull);
|
||||||
TotalGraphicsMemory = 0;
|
TotalGraphicsMemory = 0;
|
||||||
if (_adapter->IsIntel())
|
if (_adapter->IsIntel())
|
||||||
{
|
{
|
||||||
// Use total memory for integrated cards
|
TotalGraphicsMemory = dedicatedVideoMemory + dedicatedSystemMemory + totalSystemMemory;
|
||||||
TotalGraphicsMemory = dedicatedVideoMemory + dedicatedSystemMemory + consideredSharedSystemMemory;
|
|
||||||
}
|
}
|
||||||
else if (dedicatedVideoMemory >= 200 * 1024 * 1024)
|
else if (dedicatedVideoMemory >= 200 * 1024 * 1024)
|
||||||
{
|
{
|
||||||
// Use dedicated video memory, if it's more than 200 MB
|
|
||||||
TotalGraphicsMemory = dedicatedVideoMemory;
|
TotalGraphicsMemory = dedicatedVideoMemory;
|
||||||
}
|
}
|
||||||
else if (dedicatedSystemMemory >= 200 * 1024 * 1024)
|
else if (dedicatedSystemMemory >= 200 * 1024 * 1024)
|
||||||
{
|
{
|
||||||
// Use dedicated system memory, if it's more than 200 MB
|
|
||||||
TotalGraphicsMemory = dedicatedSystemMemory;
|
TotalGraphicsMemory = dedicatedSystemMemory;
|
||||||
}
|
}
|
||||||
else if (sharedSystemMemory >= 400 * 1024 * 1024)
|
else if (sharedSystemMemory >= 400 * 1024 * 1024)
|
||||||
{
|
{
|
||||||
// Use some shared system memory, if it's more than 400 MB
|
TotalGraphicsMemory = totalSystemMemory;
|
||||||
TotalGraphicsMemory = consideredSharedSystemMemory;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Otherwise consider 25% of total system memory for graphics
|
|
||||||
TotalGraphicsMemory = totalPhysicalMemory / 4Ull;
|
TotalGraphicsMemory = totalPhysicalMemory / 4Ull;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,25 +5,25 @@
|
|||||||
#include "AndroidVulkanPlatform.h"
|
#include "AndroidVulkanPlatform.h"
|
||||||
#include "../RenderToolsVulkan.h"
|
#include "../RenderToolsVulkan.h"
|
||||||
|
|
||||||
void AndroidVulkanPlatform::GetInstanceExtensions(Array<const char*>& outExtensions)
|
void AndroidVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions)
|
||||||
{
|
{
|
||||||
outExtensions.Add(VK_KHR_SURFACE_EXTENSION_NAME);
|
extensions.Add(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||||
outExtensions.Add(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
extensions.Add(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidVulkanPlatform::GetDeviceExtensions(Array<const char*>& outExtensions)
|
void AndroidVulkanPlatform::GetDeviceExtensions(Array<const char*>& extensions)
|
||||||
{
|
{
|
||||||
outExtensions.Add(VK_KHR_SURFACE_EXTENSION_NAME);
|
extensions.Add(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||||
outExtensions.Add(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
extensions.Add(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface)
|
void AndroidVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||||
{
|
{
|
||||||
ASSERT(windowHandle);
|
ASSERT(windowHandle);
|
||||||
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
|
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR);
|
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR);
|
||||||
surfaceCreateInfo.window = (struct ANativeWindow*)windowHandle;
|
surfaceCreateInfo.window = (struct ANativeWindow*)windowHandle;
|
||||||
VALIDATE_VULKAN_RESULT(vkCreateAndroidSurfaceKHR(instance, &surfaceCreateInfo, nullptr, outSurface));
|
VALIDATE_VULKAN_RESULT(vkCreateAndroidSurfaceKHR(instance, &surfaceCreateInfo, nullptr, surface));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ class AndroidVulkanPlatform : public VulkanPlatformBase
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static void GetInstanceExtensions(Array<const char*>& outExtensions);
|
static void GetInstanceExtensions(Array<const char*>& extensions);
|
||||||
static void GetDeviceExtensions(Array<const char*>& outExtensions);
|
static void GetDeviceExtensions(Array<const char*>& extensions);
|
||||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface);
|
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef AndroidVulkanPlatform VulkanPlatform;
|
typedef AndroidVulkanPlatform VulkanPlatform;
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ void CmdBufferVulkan::Begin()
|
|||||||
VkCommandBufferBeginInfo beginInfo;
|
VkCommandBufferBeginInfo beginInfo;
|
||||||
RenderToolsVulkan::ZeroStruct(beginInfo, VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO);
|
RenderToolsVulkan::ZeroStruct(beginInfo, VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO);
|
||||||
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||||
VALIDATE_VULKAN_RESULT(vkBeginCommandBuffer(_commandBufferHandle, &beginInfo));
|
VALIDATE_VULKAN_RESULT(vkBeginCommandBuffer(_commandBuffer, &beginInfo));
|
||||||
|
|
||||||
// Acquire a descriptor pool set on
|
// Acquire a descriptor pool set on
|
||||||
if (_descriptorPoolSetContainer == nullptr)
|
if (_descriptorPoolSetContainer == nullptr)
|
||||||
{
|
{
|
||||||
@@ -68,14 +68,14 @@ void CmdBufferVulkan::BeginRenderPass(RenderPassVulkan* renderPass, FramebufferV
|
|||||||
info.clearValueCount = clearValueCount;
|
info.clearValueCount = clearValueCount;
|
||||||
info.pClearValues = clearValues;
|
info.pClearValues = clearValues;
|
||||||
|
|
||||||
vkCmdBeginRenderPass(_commandBufferHandle, &info, VK_SUBPASS_CONTENTS_INLINE);
|
vkCmdBeginRenderPass(_commandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
_state = State::IsInsideRenderPass;
|
_state = State::IsInsideRenderPass;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CmdBufferVulkan::EndRenderPass()
|
void CmdBufferVulkan::EndRenderPass()
|
||||||
{
|
{
|
||||||
ASSERT(IsInsideRenderPass());
|
ASSERT(IsInsideRenderPass());
|
||||||
vkCmdEndRenderPass(_commandBufferHandle);
|
vkCmdEndRenderPass(_commandBuffer);
|
||||||
_state = State::IsInsideBegin;
|
_state = State::IsInsideBegin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ void CmdBufferVulkan::RefreshFenceStatus()
|
|||||||
|
|
||||||
_submittedWaitSemaphores.Clear();
|
_submittedWaitSemaphores.Clear();
|
||||||
|
|
||||||
vkResetCommandBuffer(_commandBufferHandle, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
|
vkResetCommandBuffer(_commandBuffer, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
|
||||||
_fence->GetOwner()->ResetFence(_fence);
|
_fence->GetOwner()->ResetFence(_fence);
|
||||||
_fenceSignaledCounter++;
|
_fenceSignaledCounter++;
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ void CmdBufferVulkan::RefreshFenceStatus()
|
|||||||
|
|
||||||
CmdBufferVulkan::CmdBufferVulkan(GPUDeviceVulkan* device, CmdBufferPoolVulkan* pool)
|
CmdBufferVulkan::CmdBufferVulkan(GPUDeviceVulkan* device, CmdBufferPoolVulkan* pool)
|
||||||
: _device(device)
|
: _device(device)
|
||||||
, _commandBufferHandle(VK_NULL_HANDLE)
|
, _commandBuffer(VK_NULL_HANDLE)
|
||||||
, _state(State::ReadyForBegin)
|
, _state(State::ReadyForBegin)
|
||||||
, _fence(nullptr)
|
, _fence(nullptr)
|
||||||
, _fenceSignaledCounter(0)
|
, _fenceSignaledCounter(0)
|
||||||
@@ -164,7 +164,7 @@ CmdBufferVulkan::CmdBufferVulkan(GPUDeviceVulkan* device, CmdBufferPoolVulkan* p
|
|||||||
createCmdBufInfo.commandBufferCount = 1;
|
createCmdBufInfo.commandBufferCount = 1;
|
||||||
createCmdBufInfo.commandPool = _commandBufferPool->GetHandle();
|
createCmdBufInfo.commandPool = _commandBufferPool->GetHandle();
|
||||||
|
|
||||||
VALIDATE_VULKAN_RESULT(vkAllocateCommandBuffers(_device->Device, &createCmdBufInfo, &_commandBufferHandle));
|
VALIDATE_VULKAN_RESULT(vkAllocateCommandBuffers(_device->Device, &createCmdBufInfo, &_commandBuffer));
|
||||||
_fence = _device->FenceManager.AllocateFence();
|
_fence = _device->FenceManager.AllocateFence();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,13 +183,13 @@ CmdBufferVulkan::~CmdBufferVulkan()
|
|||||||
fenceManager.ReleaseFence(_fence);
|
fenceManager.ReleaseFence(_fence);
|
||||||
}
|
}
|
||||||
|
|
||||||
vkFreeCommandBuffers(_device->Device, _commandBufferPool->GetHandle(), 1, &_commandBufferHandle);
|
vkFreeCommandBuffers(_device->Device, _commandBufferPool->GetHandle(), 1, &_commandBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
CmdBufferVulkan* CmdBufferPoolVulkan::Create()
|
CmdBufferVulkan* CmdBufferPoolVulkan::Create()
|
||||||
{
|
{
|
||||||
const auto cmdBuffer = New<CmdBufferVulkan>(Device, this);
|
const auto cmdBuffer = New<CmdBufferVulkan>(_device, this);
|
||||||
CmdBuffers.Add(cmdBuffer);
|
_cmdBuffers.Add(cmdBuffer);
|
||||||
return cmdBuffer;
|
return cmdBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,30 +200,30 @@ void CmdBufferPoolVulkan::Create(uint32 queueFamilyIndex)
|
|||||||
poolInfo.queueFamilyIndex = queueFamilyIndex;
|
poolInfo.queueFamilyIndex = queueFamilyIndex;
|
||||||
// TODO: use VK_COMMAND_POOL_CREATE_TRANSIENT_BIT?
|
// TODO: use VK_COMMAND_POOL_CREATE_TRANSIENT_BIT?
|
||||||
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||||||
VALIDATE_VULKAN_RESULT(vkCreateCommandPool(Device->Device, &poolInfo, nullptr, &Handle));
|
VALIDATE_VULKAN_RESULT(vkCreateCommandPool(_device->Device, &poolInfo, nullptr, &_handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
CmdBufferPoolVulkan::CmdBufferPoolVulkan(GPUDeviceVulkan* device)
|
CmdBufferPoolVulkan::CmdBufferPoolVulkan(GPUDeviceVulkan* device)
|
||||||
: Device(device)
|
: _device(device)
|
||||||
, Handle(VK_NULL_HANDLE)
|
, _handle(VK_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CmdBufferPoolVulkan::~CmdBufferPoolVulkan()
|
CmdBufferPoolVulkan::~CmdBufferPoolVulkan()
|
||||||
{
|
{
|
||||||
for (int32 i = 0; i < CmdBuffers.Count(); i++)
|
for (int32 i = 0; i < _cmdBuffers.Count(); i++)
|
||||||
{
|
{
|
||||||
Delete(CmdBuffers[i]);
|
Delete(_cmdBuffers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
vkDestroyCommandPool(Device->Device, Handle, nullptr);
|
vkDestroyCommandPool(_device->Device, _handle, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CmdBufferPoolVulkan::RefreshFenceStatus(CmdBufferVulkan* skipCmdBuffer)
|
void CmdBufferPoolVulkan::RefreshFenceStatus(CmdBufferVulkan* skipCmdBuffer)
|
||||||
{
|
{
|
||||||
for (int32 i = 0; i < CmdBuffers.Count(); i++)
|
for (int32 i = 0; i < _cmdBuffers.Count(); i++)
|
||||||
{
|
{
|
||||||
auto cmdBuffer = CmdBuffers[i];
|
auto cmdBuffer = _cmdBuffers[i];
|
||||||
if (cmdBuffer != skipCmdBuffer)
|
if (cmdBuffer != skipCmdBuffer)
|
||||||
{
|
{
|
||||||
cmdBuffer->RefreshFenceStatus();
|
cmdBuffer->RefreshFenceStatus();
|
||||||
@@ -232,63 +232,63 @@ void CmdBufferPoolVulkan::RefreshFenceStatus(CmdBufferVulkan* skipCmdBuffer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
CmdBufferManagerVulkan::CmdBufferManagerVulkan(GPUDeviceVulkan* device, GPUContextVulkan* context)
|
CmdBufferManagerVulkan::CmdBufferManagerVulkan(GPUDeviceVulkan* device, GPUContextVulkan* context)
|
||||||
: Device(device)
|
: _device(device)
|
||||||
, Pool(device)
|
, _pool(device)
|
||||||
, Queue(context->GetQueue())
|
, _queue(context->GetQueue())
|
||||||
, ActiveCmdBuffer(nullptr)
|
, _activeCmdBuffer(nullptr)
|
||||||
{
|
{
|
||||||
Pool.Create(Queue->GetFamilyIndex());
|
_pool.Create(_queue->GetFamilyIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CmdBufferManagerVulkan::SubmitActiveCmdBuffer(SemaphoreVulkan* signalSemaphore)
|
void CmdBufferManagerVulkan::SubmitActiveCmdBuffer(SemaphoreVulkan* signalSemaphore)
|
||||||
{
|
{
|
||||||
ASSERT(ActiveCmdBuffer);
|
ASSERT(_activeCmdBuffer);
|
||||||
if (!ActiveCmdBuffer->IsSubmitted() && ActiveCmdBuffer->HasBegun())
|
if (!_activeCmdBuffer->IsSubmitted() && _activeCmdBuffer->HasBegun())
|
||||||
{
|
{
|
||||||
if (ActiveCmdBuffer->IsInsideRenderPass())
|
if (_activeCmdBuffer->IsInsideRenderPass())
|
||||||
{
|
{
|
||||||
ActiveCmdBuffer->EndRenderPass();
|
_activeCmdBuffer->EndRenderPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pause all active queries
|
// Pause all active queries
|
||||||
for (int32 i = 0; i < QueriesInProgress.Count(); i++)
|
for (int32 i = 0; i < _queriesInProgress.Count(); i++)
|
||||||
{
|
{
|
||||||
QueriesInProgress[i]->Interrupt(ActiveCmdBuffer);
|
_queriesInProgress[i]->Interrupt(_activeCmdBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
ActiveCmdBuffer->End();
|
_activeCmdBuffer->End();
|
||||||
|
|
||||||
if (signalSemaphore)
|
if (signalSemaphore)
|
||||||
{
|
{
|
||||||
Queue->Submit(ActiveCmdBuffer, signalSemaphore->GetHandle());
|
_queue->Submit(_activeCmdBuffer, signalSemaphore->GetHandle());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Queue->Submit(ActiveCmdBuffer);
|
_queue->Submit(_activeCmdBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ActiveCmdBuffer = nullptr;
|
_activeCmdBuffer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CmdBufferManagerVulkan::WaitForCmdBuffer(CmdBufferVulkan* cmdBuffer, float timeInSecondsToWait)
|
void CmdBufferManagerVulkan::WaitForCmdBuffer(CmdBufferVulkan* cmdBuffer, float timeInSecondsToWait)
|
||||||
{
|
{
|
||||||
ASSERT(cmdBuffer->IsSubmitted());
|
ASSERT(cmdBuffer->IsSubmitted());
|
||||||
bool success = Device->FenceManager.WaitForFence(cmdBuffer->GetFence(), (uint64)(timeInSecondsToWait * 1e9));
|
const bool failed = _device->FenceManager.WaitForFence(cmdBuffer->GetFence(), (uint64)(timeInSecondsToWait * 1e9));
|
||||||
ASSERT(success);
|
ASSERT(!failed);
|
||||||
cmdBuffer->RefreshFenceStatus();
|
cmdBuffer->RefreshFenceStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CmdBufferManagerVulkan::PrepareForNewActiveCommandBuffer()
|
void CmdBufferManagerVulkan::PrepareForNewActiveCommandBuffer()
|
||||||
{
|
{
|
||||||
for (int32 i = 0; i < Pool.CmdBuffers.Count(); i++)
|
for (int32 i = 0; i < _pool._cmdBuffers.Count(); i++)
|
||||||
{
|
{
|
||||||
auto cmdBuffer = Pool.CmdBuffers[i];
|
auto cmdBuffer = _pool._cmdBuffers[i];
|
||||||
cmdBuffer->RefreshFenceStatus();
|
cmdBuffer->RefreshFenceStatus();
|
||||||
if (cmdBuffer->GetState() == CmdBufferVulkan::State::ReadyForBegin)
|
if (cmdBuffer->GetState() == CmdBufferVulkan::State::ReadyForBegin)
|
||||||
{
|
{
|
||||||
ActiveCmdBuffer = cmdBuffer;
|
_activeCmdBuffer = cmdBuffer;
|
||||||
ActiveCmdBuffer->Begin();
|
_activeCmdBuffer->Begin();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -298,24 +298,24 @@ void CmdBufferManagerVulkan::PrepareForNewActiveCommandBuffer()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// All cmd buffers are being executed still
|
// All cmd buffers are being executed still
|
||||||
ActiveCmdBuffer = Pool.Create();
|
_activeCmdBuffer = _pool.Create();
|
||||||
ActiveCmdBuffer->Begin();
|
_activeCmdBuffer->Begin();
|
||||||
|
|
||||||
// Resume any paused queries with the new command buffer
|
// Resume any paused queries with the new command buffer
|
||||||
for (int32 i = 0; i < QueriesInProgress.Count(); i++)
|
for (int32 i = 0; i < _queriesInProgress.Count(); i++)
|
||||||
{
|
{
|
||||||
QueriesInProgress[i]->Resume(ActiveCmdBuffer);
|
_queriesInProgress[i]->Resume(_activeCmdBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CmdBufferManagerVulkan::OnQueryBegin(GPUTimerQueryVulkan* query)
|
void CmdBufferManagerVulkan::OnQueryBegin(GPUTimerQueryVulkan* query)
|
||||||
{
|
{
|
||||||
QueriesInProgress.Add(query);
|
_queriesInProgress.Add(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CmdBufferManagerVulkan::OnQueryEnd(GPUTimerQueryVulkan* query)
|
void CmdBufferManagerVulkan::OnQueryEnd(GPUTimerQueryVulkan* query)
|
||||||
{
|
{
|
||||||
QueriesInProgress.Remove(query);
|
_queriesInProgress.Remove(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
GPUDeviceVulkan* _device;
|
GPUDeviceVulkan* _device;
|
||||||
VkCommandBuffer _commandBufferHandle;
|
VkCommandBuffer _commandBuffer;
|
||||||
State _state;
|
State _state;
|
||||||
|
|
||||||
Array<VkPipelineStageFlags> _waitFlags;
|
Array<VkPipelineStageFlags> _waitFlags;
|
||||||
@@ -44,8 +44,6 @@ private:
|
|||||||
void MarkSemaphoresAsSubmitted()
|
void MarkSemaphoresAsSubmitted()
|
||||||
{
|
{
|
||||||
_waitFlags.Clear();
|
_waitFlags.Clear();
|
||||||
|
|
||||||
// Move to pending delete list
|
|
||||||
_submittedWaitSemaphores = _waitSemaphores;
|
_submittedWaitSemaphores = _waitSemaphores;
|
||||||
_waitSemaphores.Clear();
|
_waitSemaphores.Clear();
|
||||||
}
|
}
|
||||||
@@ -55,12 +53,12 @@ private:
|
|||||||
int32 _eventsBegin = 0;
|
int32 _eventsBegin = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Last value passed after the fence got signaled
|
// The latest value when command buffer was submitted.
|
||||||
volatile uint64 _fenceSignaledCounter;
|
|
||||||
|
|
||||||
// Last value when we submitted the cmd buffer; useful to track down if something waiting for the fence has actually been submitted
|
|
||||||
volatile uint64 _submittedFenceCounter;
|
volatile uint64 _submittedFenceCounter;
|
||||||
|
|
||||||
|
// The latest value passed after the fence was signaled.
|
||||||
|
volatile uint64 _fenceSignaledCounter;
|
||||||
|
|
||||||
CmdBufferPoolVulkan* _commandBufferPool;
|
CmdBufferPoolVulkan* _commandBufferPool;
|
||||||
|
|
||||||
DescriptorPoolSetContainerVulkan* _descriptorPoolSetContainer = nullptr;
|
DescriptorPoolSetContainerVulkan* _descriptorPoolSetContainer = nullptr;
|
||||||
@@ -73,17 +71,17 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
CmdBufferPoolVulkan* GetOwner()
|
CmdBufferPoolVulkan* GetOwner() const
|
||||||
{
|
{
|
||||||
return _commandBufferPool;
|
return _commandBufferPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
State GetState()
|
State GetState() const
|
||||||
{
|
{
|
||||||
return _state;
|
return _state;
|
||||||
}
|
}
|
||||||
|
|
||||||
FenceVulkan* GetFence()
|
FenceVulkan* GetFence() const
|
||||||
{
|
{
|
||||||
return _fence;
|
return _fence;
|
||||||
}
|
}
|
||||||
@@ -115,7 +113,7 @@ public:
|
|||||||
|
|
||||||
inline VkCommandBuffer GetHandle() const
|
inline VkCommandBuffer GetHandle() const
|
||||||
{
|
{
|
||||||
return _commandBufferHandle;
|
return _commandBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline volatile uint64 GetFenceSignaledCounter() const
|
inline volatile uint64 GetFenceSignaledCounter() const
|
||||||
@@ -155,30 +153,28 @@ public:
|
|||||||
|
|
||||||
class CmdBufferPoolVulkan
|
class CmdBufferPoolVulkan
|
||||||
{
|
{
|
||||||
|
friend class CmdBufferManagerVulkan;
|
||||||
private:
|
private:
|
||||||
|
|
||||||
GPUDeviceVulkan* Device;
|
GPUDeviceVulkan* _device;
|
||||||
VkCommandPool Handle;
|
VkCommandPool _handle;
|
||||||
|
Array<CmdBufferVulkan*> _cmdBuffers;
|
||||||
|
|
||||||
CmdBufferVulkan* Create();
|
CmdBufferVulkan* Create();
|
||||||
|
|
||||||
Array<CmdBufferVulkan*> CmdBuffers;
|
|
||||||
|
|
||||||
void Create(uint32 queueFamilyIndex);
|
void Create(uint32 queueFamilyIndex);
|
||||||
friend class CmdBufferManagerVulkan;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
CmdBufferPoolVulkan(GPUDeviceVulkan* device);
|
CmdBufferPoolVulkan(GPUDeviceVulkan* device);
|
||||||
|
|
||||||
~CmdBufferPoolVulkan();
|
~CmdBufferPoolVulkan();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
inline VkCommandPool GetHandle() const
|
inline VkCommandPool GetHandle() const
|
||||||
{
|
{
|
||||||
ASSERT(Handle != VK_NULL_HANDLE);
|
ASSERT(_handle != VK_NULL_HANDLE);
|
||||||
return Handle;
|
return _handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RefreshFenceStatus(CmdBufferVulkan* skipCmdBuffer = nullptr);
|
void RefreshFenceStatus(CmdBufferVulkan* skipCmdBuffer = nullptr);
|
||||||
@@ -188,11 +184,11 @@ class CmdBufferManagerVulkan
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
GPUDeviceVulkan* Device;
|
GPUDeviceVulkan* _device;
|
||||||
CmdBufferPoolVulkan Pool;
|
CmdBufferPoolVulkan _pool;
|
||||||
QueueVulkan* Queue;
|
QueueVulkan* _queue;
|
||||||
CmdBufferVulkan* ActiveCmdBuffer;
|
CmdBufferVulkan* _activeCmdBuffer;
|
||||||
Array<GPUTimerQueryVulkan*> QueriesInProgress;
|
Array<GPUTimerQueryVulkan*> _queriesInProgress;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -200,46 +196,46 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
inline VkCommandPool GetHandle() const
|
FORCE_INLINE VkCommandPool GetHandle() const
|
||||||
{
|
{
|
||||||
return Pool.GetHandle();
|
return _pool.GetHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline CmdBufferVulkan* GetActiveCmdBuffer() const
|
FORCE_INLINE CmdBufferVulkan* GetActiveCmdBuffer() const
|
||||||
{
|
{
|
||||||
return ActiveCmdBuffer;
|
return _activeCmdBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool HasPendingActiveCmdBuffer() const
|
FORCE_INLINE bool HasPendingActiveCmdBuffer() const
|
||||||
{
|
{
|
||||||
return ActiveCmdBuffer != nullptr;
|
return _activeCmdBuffer != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool HasQueriesInProgress() const
|
FORCE_INLINE bool HasQueriesInProgress() const
|
||||||
{
|
{
|
||||||
return QueriesInProgress.Count() != 0;
|
return _queriesInProgress.Count() != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CmdBufferVulkan* GetCmdBuffer()
|
||||||
|
{
|
||||||
|
if (!_activeCmdBuffer)
|
||||||
|
PrepareForNewActiveCommandBuffer();
|
||||||
|
return _activeCmdBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
void SubmitActiveCmdBuffer(SemaphoreVulkan* signalSemaphore = nullptr);
|
void SubmitActiveCmdBuffer(SemaphoreVulkan* signalSemaphore = nullptr);
|
||||||
|
|
||||||
void WaitForCmdBuffer(CmdBufferVulkan* cmdBuffer, float timeInSecondsToWait = 1.0f);
|
void WaitForCmdBuffer(CmdBufferVulkan* cmdBuffer, float timeInSecondsToWait = 1.0f);
|
||||||
|
|
||||||
// Update the fences of all cmd buffers except SkipCmdBuffer
|
|
||||||
void RefreshFenceStatus(CmdBufferVulkan* skipCmdBuffer = nullptr)
|
void RefreshFenceStatus(CmdBufferVulkan* skipCmdBuffer = nullptr)
|
||||||
{
|
{
|
||||||
Pool.RefreshFenceStatus(skipCmdBuffer);
|
_pool.RefreshFenceStatus(skipCmdBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrepareForNewActiveCommandBuffer();
|
void PrepareForNewActiveCommandBuffer();
|
||||||
|
|
||||||
inline CmdBufferVulkan* GetCmdBuffer()
|
|
||||||
{
|
|
||||||
if (!ActiveCmdBuffer)
|
|
||||||
PrepareForNewActiveCommandBuffer();
|
|
||||||
|
|
||||||
return ActiveCmdBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnQueryBegin(GPUTimerQueryVulkan* query);
|
void OnQueryBegin(GPUTimerQueryVulkan* query);
|
||||||
void OnQueryEnd(GPUTimerQueryVulkan* query);
|
void OnQueryEnd(GPUTimerQueryVulkan* query);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -82,61 +82,24 @@ void DescriptorSetLayoutVulkan::Compile()
|
|||||||
{
|
{
|
||||||
ASSERT(_handles.IsEmpty());
|
ASSERT(_handles.IsEmpty());
|
||||||
|
|
||||||
// Check if we obey limits
|
// Validate device limits for the engine
|
||||||
const VkPhysicalDeviceLimits& limits = _device->PhysicalDeviceLimits;
|
const VkPhysicalDeviceLimits& limits = _device->PhysicalDeviceLimits;
|
||||||
|
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_SAMPLER] + _layoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER] < limits.maxDescriptorSetSamplers);
|
||||||
// Check for maxDescriptorSetSamplers
|
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER]+ _layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] < limits.maxDescriptorSetUniformBuffers);
|
||||||
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_SAMPLER]
|
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] < limits.maxDescriptorSetUniformBuffersDynamic);
|
||||||
+ _layoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER]
|
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER] + _layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC] < limits.maxDescriptorSetStorageBuffers);
|
||||||
< limits.maxDescriptorSetSamplers);
|
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC] < limits.maxDescriptorSetStorageBuffersDynamic);
|
||||||
|
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER] + _layoutTypes[VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE] + _layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER] < limits.maxDescriptorSetSampledImages);
|
||||||
// Check for maxDescriptorSetUniformBuffers
|
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_IMAGE] + _layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER]< limits.maxDescriptorSetStorageImages);
|
||||||
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER]
|
|
||||||
+ _layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC]
|
|
||||||
< limits.maxDescriptorSetUniformBuffers);
|
|
||||||
|
|
||||||
// Check for maxDescriptorSetUniformBuffersDynamic
|
|
||||||
if (!_device->Adapter->IsAMD())
|
|
||||||
{
|
|
||||||
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC]
|
|
||||||
< limits.maxDescriptorSetUniformBuffersDynamic);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for maxDescriptorSetStorageBuffers
|
|
||||||
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER]
|
|
||||||
+ _layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC]
|
|
||||||
< limits.maxDescriptorSetStorageBuffers);
|
|
||||||
|
|
||||||
// Check for maxDescriptorSetStorageBuffersDynamic
|
|
||||||
if (_layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] > limits.maxDescriptorSetUniformBuffersDynamic)
|
|
||||||
{
|
|
||||||
// TODO: Downgrade to non-dynamic?
|
|
||||||
}
|
|
||||||
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC]
|
|
||||||
< limits.maxDescriptorSetStorageBuffersDynamic);
|
|
||||||
|
|
||||||
// Check for maxDescriptorSetSampledImages
|
|
||||||
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER]
|
|
||||||
+ _layoutTypes[VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE]
|
|
||||||
+ _layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER]
|
|
||||||
< limits.maxDescriptorSetSampledImages);
|
|
||||||
|
|
||||||
// Check for maxDescriptorSetStorageImages
|
|
||||||
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_IMAGE]
|
|
||||||
+ _layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER]
|
|
||||||
< limits.maxDescriptorSetStorageImages);
|
|
||||||
|
|
||||||
_handles.Resize(_setLayouts.Count());
|
_handles.Resize(_setLayouts.Count());
|
||||||
|
|
||||||
for (int32 i = 0; i < _setLayouts.Count(); i++)
|
for (int32 i = 0; i < _setLayouts.Count(); i++)
|
||||||
{
|
{
|
||||||
auto& layout = _setLayouts[i];
|
auto& layout = _setLayouts[i];
|
||||||
|
|
||||||
VkDescriptorSetLayoutCreateInfo layoutInfo;
|
VkDescriptorSetLayoutCreateInfo layoutInfo;
|
||||||
RenderToolsVulkan::ZeroStruct(layoutInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO);
|
RenderToolsVulkan::ZeroStruct(layoutInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO);
|
||||||
layoutInfo.bindingCount = layout.LayoutBindings.Count();
|
layoutInfo.bindingCount = layout.LayoutBindings.Count();
|
||||||
layoutInfo.pBindings = layout.LayoutBindings.Get();
|
layoutInfo.pBindings = layout.LayoutBindings.Get();
|
||||||
|
|
||||||
VALIDATE_VULKAN_RESULT(vkCreateDescriptorSetLayout(_device->Device, &layoutInfo, nullptr, &_handles[i]));
|
VALIDATE_VULKAN_RESULT(vkCreateDescriptorSetLayout(_device->Device, &layoutInfo, nullptr, &_handles[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,11 +125,6 @@ DescriptorPoolVulkan::DescriptorPoolVulkan(GPUDeviceVulkan* device, const Descri
|
|||||||
|
|
||||||
// The maximum amount of descriptor sets layout allocations to hold
|
// The maximum amount of descriptor sets layout allocations to hold
|
||||||
const uint32 MaxSetsAllocations = 256;
|
const uint32 MaxSetsAllocations = 256;
|
||||||
|
|
||||||
// Descriptor sets number required to allocate the max number of descriptor sets layout.
|
|
||||||
// When we're hashing pools with types usage ID the descriptor pool can be used for different layouts so the initial layout does not make much sense.
|
|
||||||
// In the latter case we'll be probably over-allocating the descriptor types but given the relatively small number of max allocations this should not have
|
|
||||||
// a serious impact.
|
|
||||||
DescriptorSetsMax = MaxSetsAllocations * (VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? 1 : Layout.GetLayouts().Count());
|
DescriptorSetsMax = MaxSetsAllocations * (VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? 1 : Layout.GetLayouts().Count());
|
||||||
for (uint32 typeIndex = VULKAN_DESCRIPTOR_TYPE_BEGIN; typeIndex <= VULKAN_DESCRIPTOR_TYPE_END; typeIndex++)
|
for (uint32 typeIndex = VULKAN_DESCRIPTOR_TYPE_BEGIN; typeIndex <= VULKAN_DESCRIPTOR_TYPE_END; typeIndex++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -72,18 +72,6 @@ static VKAPI_ATTR VkBool32 VKAPI_PTR DebugReportFunction(VkDebugReportFlagsEXT m
|
|||||||
return VK_FALSE;
|
return VK_FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!StringUtils::Compare(layerPrefix, "DS"))
|
|
||||||
{
|
|
||||||
if (msgCode == 6)
|
|
||||||
{
|
|
||||||
auto* Found = StringUtils::Find(msg, " array layer ");
|
|
||||||
if (Found && Found[13] >= '1' && Found[13] <= '9')
|
|
||||||
{
|
|
||||||
// Potential bug in the validation layers for slice > 1 on 3d textures
|
|
||||||
return VK_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
|
else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
|
||||||
{
|
{
|
||||||
@@ -1634,7 +1622,7 @@ bool GPUDeviceVulkan::Init()
|
|||||||
|
|
||||||
if ((curProps.queueFlags & VK_QUEUE_TRANSFER_BIT) == VK_QUEUE_TRANSFER_BIT)
|
if ((curProps.queueFlags & VK_QUEUE_TRANSFER_BIT) == VK_QUEUE_TRANSFER_BIT)
|
||||||
{
|
{
|
||||||
// Prefer a non-gfx transfer queue
|
// Favor a non-gfx transfer queue
|
||||||
if (transferQueueFamilyIndex == -1 && (curProps.queueFlags & VK_QUEUE_GRAPHICS_BIT) != VK_QUEUE_GRAPHICS_BIT && (curProps.queueFlags & VK_QUEUE_COMPUTE_BIT) != VK_QUEUE_COMPUTE_BIT)
|
if (transferQueueFamilyIndex == -1 && (curProps.queueFlags & VK_QUEUE_GRAPHICS_BIT) != VK_QUEUE_GRAPHICS_BIT && (curProps.queueFlags & VK_QUEUE_COMPUTE_BIT) != VK_QUEUE_COMPUTE_BIT)
|
||||||
{
|
{
|
||||||
transferQueueFamilyIndex = familyIndex;
|
transferQueueFamilyIndex = familyIndex;
|
||||||
@@ -2084,10 +2072,10 @@ bool FenceManagerVulkan::WaitForFence(FenceVulkan* fence, uint64 timeInNanosecon
|
|||||||
if (result == VK_SUCCESS)
|
if (result == VK_SUCCESS)
|
||||||
{
|
{
|
||||||
fence->_signaled = true;
|
fence->_signaled = true;
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FenceManagerVulkan::ResetFence(FenceVulkan* fence)
|
void FenceManagerVulkan::ResetFence(FenceVulkan* fence)
|
||||||
|
|||||||
@@ -134,20 +134,20 @@ public:
|
|||||||
return CheckFenceState(fence);
|
return CheckFenceState(fence);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns false if it timed out
|
// Returns true if waiting timed out or failed, false otherwise.
|
||||||
bool WaitForFence(FenceVulkan* fence, uint64 timeInNanoseconds);
|
bool WaitForFence(FenceVulkan* fence, uint64 timeInNanoseconds);
|
||||||
|
|
||||||
void ResetFence(FenceVulkan* fence);
|
void ResetFence(FenceVulkan* fence);
|
||||||
|
|
||||||
// Sets it to nullptr
|
// Sets the fence handle to null
|
||||||
void ReleaseFence(FenceVulkan*& fence);
|
void ReleaseFence(FenceVulkan*& fence);
|
||||||
|
|
||||||
// Sets it to nullptr
|
// Sets the fence handle to null
|
||||||
void WaitAndReleaseFence(FenceVulkan*& fence, uint64 timeInNanoseconds);
|
void WaitAndReleaseFence(FenceVulkan*& fence, uint64 timeInNanoseconds);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Returns true if signaled
|
// Returns true if fence was signaled, otherwise false.
|
||||||
bool CheckFenceState(FenceVulkan* fence);
|
bool CheckFenceState(FenceVulkan* fence);
|
||||||
|
|
||||||
void DestroyFence(FenceVulkan* fence);
|
void DestroyFence(FenceVulkan* fence);
|
||||||
@@ -209,14 +209,14 @@ public:
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
inline void EnqueueResource(Type type, T handle)
|
inline void EnqueueResource(Type type, T handle)
|
||||||
{
|
{
|
||||||
static_assert(sizeof(T) <= sizeof(uint64), "Vulkan resource handle type size too large.");
|
static_assert(sizeof(T) <= sizeof(uint64), "Invalid handle size.");
|
||||||
EnqueueGenericResource(type, (uint64)handle, VK_NULL_HANDLE);
|
EnqueueGenericResource(type, (uint64)handle, VK_NULL_HANDLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline void EnqueueResource(Type type, T handle, VmaAllocation allocation)
|
inline void EnqueueResource(Type type, T handle, VmaAllocation allocation)
|
||||||
{
|
{
|
||||||
static_assert(sizeof(T) <= sizeof(uint64), "Vulkan resource handle type size too large.");
|
static_assert(sizeof(T) <= sizeof(uint64), "Invalid handle size.");
|
||||||
EnqueueGenericResource(type, (uint64)handle, allocation);
|
EnqueueGenericResource(type, (uint64)handle, allocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,8 +49,6 @@ private:
|
|||||||
uint64 _offset;
|
uint64 _offset;
|
||||||
uint32 _minAlignment;
|
uint32 _minAlignment;
|
||||||
byte* _mapped;
|
byte* _mapped;
|
||||||
|
|
||||||
// Fence for wrapping around
|
|
||||||
CmdBufferVulkan* _fenceCmdBuffer;
|
CmdBufferVulkan* _fenceCmdBuffer;
|
||||||
uint64 _fenceCounter;
|
uint64 _fenceCounter;
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ GPUTextureView* GPUSwapChainVulkan::GetBackBufferView()
|
|||||||
{
|
{
|
||||||
if (_acquiredImageIndex == -1)
|
if (_acquiredImageIndex == -1)
|
||||||
{
|
{
|
||||||
if (DoCheckedSwapChainJob(DoAcquireImageIndex) < 0)
|
if (TryPresent(DoAcquireImageIndex) < 0)
|
||||||
{
|
{
|
||||||
LOG(Fatal, "Swapchain acquire image index failed!");
|
LOG(Fatal, "Swapchain acquire image index failed!");
|
||||||
}
|
}
|
||||||
@@ -415,8 +415,7 @@ GPUSwapChainVulkan::Status GPUSwapChainVulkan::Present(QueueVulkan* presentQueue
|
|||||||
{
|
{
|
||||||
if (_currentImageIndex == -1)
|
if (_currentImageIndex == -1)
|
||||||
{
|
{
|
||||||
// Skip present silently if image has not been acquired
|
return Status::Ok;
|
||||||
return Status::Healthy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VkPresentInfoKHR presentInfo;
|
VkPresentInfoKHR presentInfo;
|
||||||
@@ -436,23 +435,23 @@ GPUSwapChainVulkan::Status GPUSwapChainVulkan::Present(QueueVulkan* presentQueue
|
|||||||
|
|
||||||
if (presentResult == VK_ERROR_OUT_OF_DATE_KHR)
|
if (presentResult == VK_ERROR_OUT_OF_DATE_KHR)
|
||||||
{
|
{
|
||||||
return Status::OutOfDate;
|
return Status::Outdated;
|
||||||
}
|
}
|
||||||
if (presentResult == VK_ERROR_SURFACE_LOST_KHR)
|
if (presentResult == VK_ERROR_SURFACE_LOST_KHR)
|
||||||
{
|
{
|
||||||
return Status::SurfaceLost;
|
return Status::LostSurface;
|
||||||
}
|
}
|
||||||
if (presentResult != VK_SUCCESS && presentResult != VK_SUBOPTIMAL_KHR)
|
if (presentResult != VK_SUCCESS && presentResult != VK_SUBOPTIMAL_KHR)
|
||||||
{
|
{
|
||||||
VALIDATE_VULKAN_RESULT(presentResult);
|
VALIDATE_VULKAN_RESULT(presentResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Status::Healthy;
|
return Status::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 GPUSwapChainVulkan::DoAcquireImageIndex(GPUSwapChainVulkan* viewport, void* customData)
|
int32 GPUSwapChainVulkan::DoAcquireImageIndex(GPUSwapChainVulkan* viewport, void* customData)
|
||||||
{
|
{
|
||||||
return viewport->_acquiredImageIndex = viewport->AcquireImageIndex(&viewport->_acquiredSemaphore);
|
return viewport->_acquiredImageIndex = viewport->AcquireNextImage(&viewport->_acquiredSemaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 GPUSwapChainVulkan::DoPresent(GPUSwapChainVulkan* viewport, void* customData)
|
int32 GPUSwapChainVulkan::DoPresent(GPUSwapChainVulkan* viewport, void* customData)
|
||||||
@@ -460,20 +459,20 @@ int32 GPUSwapChainVulkan::DoPresent(GPUSwapChainVulkan* viewport, void* customDa
|
|||||||
return (int32)viewport->Present((QueueVulkan*)customData, viewport->_backBuffers[viewport->_acquiredImageIndex].RenderingDoneSemaphore);
|
return (int32)viewport->Present((QueueVulkan*)customData, viewport->_backBuffers[viewport->_acquiredImageIndex].RenderingDoneSemaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 GPUSwapChainVulkan::DoCheckedSwapChainJob(Function<int32(GPUSwapChainVulkan*, void*)> job, void* customData, bool skipOnOutOfDate)
|
int32 GPUSwapChainVulkan::TryPresent(Function<int32(GPUSwapChainVulkan*, void*)> job, void* customData, bool skipOnOutOfDate)
|
||||||
{
|
{
|
||||||
int32 attemptsPending = 4;
|
int32 attemptsPending = 4;
|
||||||
int32 status = job(this, customData);
|
int32 status = job(this, customData);
|
||||||
|
|
||||||
while (status < 0 && attemptsPending > 0)
|
while (status < 0 && attemptsPending > 0)
|
||||||
{
|
{
|
||||||
if (status == (int32)Status::OutOfDate)
|
if (status == (int32)Status::Outdated)
|
||||||
{
|
{
|
||||||
//LOG(Warning, "Swapchain is out of date");
|
//LOG(Warning, "Swapchain is out of date");
|
||||||
if (skipOnOutOfDate)
|
if (skipOnOutOfDate)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
else if (status == (int32)Status::SurfaceLost)
|
else if (status == (int32)Status::LostSurface)
|
||||||
{
|
{
|
||||||
LOG(Warning, "Swapchain surface lost");
|
LOG(Warning, "Swapchain surface lost");
|
||||||
}
|
}
|
||||||
@@ -487,7 +486,7 @@ int32 GPUSwapChainVulkan::DoCheckedSwapChainJob(Function<int32(GPUSwapChainVulka
|
|||||||
ReleaseGPU();
|
ReleaseGPU();
|
||||||
CreateSwapChain(_width, _height);
|
CreateSwapChain(_width, _height);
|
||||||
|
|
||||||
// Swapchain creation pushes some commands - flush the command buffers now to begin with a fresh state
|
// Flush commands
|
||||||
_device->GetMainContext()->Flush();
|
_device->GetMainContext()->Flush();
|
||||||
_device->WaitForGPU();
|
_device->WaitForGPU();
|
||||||
|
|
||||||
@@ -499,7 +498,7 @@ int32 GPUSwapChainVulkan::DoCheckedSwapChainJob(Function<int32(GPUSwapChainVulka
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 GPUSwapChainVulkan::AcquireImageIndex(SemaphoreVulkan** outSemaphore)
|
int32 GPUSwapChainVulkan::AcquireNextImage(SemaphoreVulkan** outSemaphore)
|
||||||
{
|
{
|
||||||
ASSERT(_swapChain && _backBuffers.HasItems());
|
ASSERT(_swapChain && _backBuffers.HasItems());
|
||||||
|
|
||||||
@@ -519,13 +518,13 @@ int32 GPUSwapChainVulkan::AcquireImageIndex(SemaphoreVulkan** outSemaphore)
|
|||||||
if (result == VK_ERROR_OUT_OF_DATE_KHR)
|
if (result == VK_ERROR_OUT_OF_DATE_KHR)
|
||||||
{
|
{
|
||||||
_semaphoreIndex = prevSemaphoreIndex;
|
_semaphoreIndex = prevSemaphoreIndex;
|
||||||
return (int32)Status::OutOfDate;
|
return (int32)Status::Outdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == VK_ERROR_SURFACE_LOST_KHR)
|
if (result == VK_ERROR_SURFACE_LOST_KHR)
|
||||||
{
|
{
|
||||||
_semaphoreIndex = prevSemaphoreIndex;
|
_semaphoreIndex = prevSemaphoreIndex;
|
||||||
return (int32)Status::SurfaceLost;
|
return (int32)Status::LostSurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
*outSemaphore = semaphore;
|
*outSemaphore = semaphore;
|
||||||
@@ -562,8 +561,8 @@ void GPUSwapChainVulkan::Present(bool vsync)
|
|||||||
context->GetCmdBufferManager()->SubmitActiveCmdBuffer(_backBuffers[_acquiredImageIndex].RenderingDoneSemaphore);
|
context->GetCmdBufferManager()->SubmitActiveCmdBuffer(_backBuffers[_acquiredImageIndex].RenderingDoneSemaphore);
|
||||||
|
|
||||||
// Present the back buffer to the viewport window
|
// Present the back buffer to the viewport window
|
||||||
const auto result = DoCheckedSwapChainJob(DoPresent, _device->PresentQueue, true);
|
const auto result = TryPresent(DoPresent, _device->PresentQueue, true);
|
||||||
if (result == (int32)Status::OutOfDate)
|
if (result == (int32)Status::Outdated)
|
||||||
{
|
{
|
||||||
// Failed to present, window can be minimized or doesn't want to swap the buffers so just ignore the present
|
// Failed to present, window can be minimized or doesn't want to swap the buffers so just ignore the present
|
||||||
if (_window->IsMinimized())
|
if (_window->IsMinimized())
|
||||||
|
|||||||
@@ -93,20 +93,19 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Has to be negative as we use this also on other callbacks as the acquired image index
|
|
||||||
enum class Status
|
enum class Status
|
||||||
{
|
{
|
||||||
Healthy = 0,
|
Ok = 0,
|
||||||
OutOfDate = -1,
|
Outdated = -1,
|
||||||
SurfaceLost = -2,
|
LostSurface = -2,
|
||||||
};
|
};
|
||||||
|
|
||||||
Status Present(QueueVulkan* presentQueue, SemaphoreVulkan* backBufferRenderingDoneSemaphore);
|
Status Present(QueueVulkan* presentQueue, SemaphoreVulkan* backBufferRenderingDoneSemaphore);
|
||||||
|
|
||||||
static int32 DoAcquireImageIndex(GPUSwapChainVulkan* viewport, void* customData);
|
static int32 DoAcquireImageIndex(GPUSwapChainVulkan* viewport, void* customData);
|
||||||
static int32 DoPresent(GPUSwapChainVulkan* viewport, void* customData);
|
static int32 DoPresent(GPUSwapChainVulkan* viewport, void* customData);
|
||||||
int32 DoCheckedSwapChainJob(Function<int32(GPUSwapChainVulkan*, void*)> job, void* customData = nullptr, bool skipOnOutOfDate = false);
|
int32 TryPresent(Function<int32(GPUSwapChainVulkan*, void*)> job, void* customData = nullptr, bool skipOnOutOfDate = false);
|
||||||
int32 AcquireImageIndex(SemaphoreVulkan** outSemaphore);
|
int32 AcquireNextImage(SemaphoreVulkan** outSemaphore);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|||||||
@@ -47,20 +47,20 @@ VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceXlibPresentationSupportKHR(
|
|||||||
extern PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
|
extern PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
|
||||||
extern PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR;
|
extern PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR;
|
||||||
|
|
||||||
void LinuxVulkanPlatform::GetInstanceExtensions(Array<const char*>& outExtensions)
|
void LinuxVulkanPlatform::GetInstanceExtensions(Array<const char*>& outExextensionstensions)
|
||||||
{
|
{
|
||||||
// Include X11 surface extension
|
// Include X11 surface extension
|
||||||
outExtensions.Add(VK_KHR_SURFACE_EXTENSION_NAME);
|
extensions.Add(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||||
outExtensions.Add(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
|
extensions.Add(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LinuxVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface)
|
void LinuxVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||||
{
|
{
|
||||||
VkXlibSurfaceCreateInfoKHR surfaceCreateInfo;
|
VkXlibSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR);
|
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR);
|
||||||
surfaceCreateInfo.dpy = (X11::Display*)LinuxPlatform::GetXDisplay();
|
surfaceCreateInfo.dpy = (X11::Display*)LinuxPlatform::GetXDisplay();
|
||||||
surfaceCreateInfo.window = (X11::Window)windowHandle;
|
surfaceCreateInfo.window = (X11::Window)windowHandle;
|
||||||
VALIDATE_VULKAN_RESULT(vkCreateXlibSurfaceKHR(instance, &surfaceCreateInfo, nullptr, outSurface));
|
VALIDATE_VULKAN_RESULT(vkCreateXlibSurfaceKHR(instance, &surfaceCreateInfo, nullptr, surface));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class LinuxVulkanPlatform : public VulkanPlatformBase
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static void GetInstanceExtensions(Array<const char*>& outExtensions);
|
static void GetInstanceExtensions(Array<const char*>& extensions);
|
||||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface);
|
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,20 +7,20 @@
|
|||||||
#include "../RenderToolsVulkan.h"
|
#include "../RenderToolsVulkan.h"
|
||||||
#include "Engine/Graphics/GPUDevice.h"
|
#include "Engine/Graphics/GPUDevice.h"
|
||||||
|
|
||||||
void Win32VulkanPlatform::GetInstanceExtensions(Array<const char*>& outExtensions)
|
void Win32VulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions)
|
||||||
{
|
{
|
||||||
// Include Windows surface extension
|
// Include Windows surface extension
|
||||||
outExtensions.Add(VK_KHR_SURFACE_EXTENSION_NAME);
|
extensions.Add(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||||
outExtensions.Add(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
|
extensions.Add(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Win32VulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface)
|
void Win32VulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||||
{
|
{
|
||||||
VkWin32SurfaceCreateInfoKHR surfaceCreateInfo;
|
VkWin32SurfaceCreateInfoKHR surfaceCreateInfo;
|
||||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR);
|
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR);
|
||||||
surfaceCreateInfo.hinstance = GetModuleHandle(nullptr);
|
surfaceCreateInfo.hinstance = GetModuleHandle(nullptr);
|
||||||
surfaceCreateInfo.hwnd = static_cast<HWND>(windowHandle);
|
surfaceCreateInfo.hwnd = static_cast<HWND>(windowHandle);
|
||||||
VALIDATE_VULKAN_RESULT(vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, nullptr, outSurface));
|
VALIDATE_VULKAN_RESULT(vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, nullptr, surface));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ class Win32VulkanPlatform : public VulkanPlatformBase
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static void GetInstanceExtensions(Array<const char*>& outExtensions);
|
static void GetInstanceExtensions(Array<const char*>& extensions);
|
||||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface);
|
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef Win32VulkanPlatform VulkanPlatform;
|
typedef Win32VulkanPlatform VulkanPlatform;
|
||||||
|
|||||||
@@ -142,10 +142,9 @@ void SimulationEventCallback::onSleep(PxActor** actors, PxU32 count)
|
|||||||
|
|
||||||
void SimulationEventCallback::onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 nbPairs)
|
void SimulationEventCallback::onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 nbPairs)
|
||||||
{
|
{
|
||||||
// Check actors are not destroyed
|
// Skip sending events to removed actors
|
||||||
if (pairHeader.flags & (PxContactPairHeaderFlag::eREMOVED_ACTOR_0 | PxContactPairHeaderFlag::eREMOVED_ACTOR_1))
|
if (pairHeader.flags & (PxContactPairHeaderFlag::eREMOVED_ACTOR_0 | PxContactPairHeaderFlag::eREMOVED_ACTOR_1))
|
||||||
{
|
{
|
||||||
LOG(Warning, "SimulationEventCallback::onContact(): Actors have been deleted!");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -746,15 +746,15 @@ uint64 AndroidPlatform::GetTimeCycles()
|
|||||||
|
|
||||||
void AndroidPlatform::GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond)
|
void AndroidPlatform::GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond)
|
||||||
{
|
{
|
||||||
// Query for calendar time
|
// Get the calendar time
|
||||||
struct timeval time;
|
struct timeval time;
|
||||||
gettimeofday(&time, nullptr);
|
gettimeofday(&time, nullptr);
|
||||||
|
|
||||||
// Convert to local time
|
// Convert calendar time to local time
|
||||||
struct tm localTime;
|
struct tm localTime;
|
||||||
localtime_r(&time.tv_sec, &localTime);
|
localtime_r(&time.tv_sec, &localTime);
|
||||||
|
|
||||||
// Extract time
|
// Extract time from Unix date
|
||||||
year = localTime.tm_year + 1900;
|
year = localTime.tm_year + 1900;
|
||||||
month = localTime.tm_mon + 1;
|
month = localTime.tm_mon + 1;
|
||||||
dayOfWeek = localTime.tm_wday;
|
dayOfWeek = localTime.tm_wday;
|
||||||
|
|||||||
@@ -18,77 +18,49 @@ const char VolumeSeparatorChar = ':';
|
|||||||
|
|
||||||
const Char* StringUtils::FindIgnoreCase(const Char* str, const Char* toFind)
|
const Char* StringUtils::FindIgnoreCase(const Char* str, const Char* toFind)
|
||||||
{
|
{
|
||||||
// Validate input
|
|
||||||
if (toFind == nullptr || str == nullptr)
|
if (toFind == nullptr || str == nullptr)
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get upper-case first letter of the find string (to reduce the number of full strnicmps)
|
const Char findInitial = ToUpper(*toFind);
|
||||||
Char findInitial = ToUpper(*toFind);
|
const int32 length = Length(toFind++) - 1;
|
||||||
|
Char c = *str++;
|
||||||
// Get length of find string, and increment past first letter
|
while (c)
|
||||||
int32 length = Length(toFind++) - 1;
|
|
||||||
|
|
||||||
// Get the first letter of the search string, and increment past it
|
|
||||||
Char strChar = *str++;
|
|
||||||
|
|
||||||
// While we aren't at end of string
|
|
||||||
while (strChar)
|
|
||||||
{
|
{
|
||||||
// Make sure it's upper-case
|
c = ToUpper(c);
|
||||||
strChar = ToUpper(strChar);
|
if (c == findInitial && !CompareIgnoreCase(str, toFind, length))
|
||||||
|
|
||||||
// If it matches the first letter of the find string, do a case-insensitive string compare for the length of the find string
|
|
||||||
if (strChar == findInitial && !CompareIgnoreCase(str, toFind, length))
|
|
||||||
{
|
{
|
||||||
// If we found the string, then return a pointer to the beginning of it in the search string
|
|
||||||
return str - 1;
|
return str - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go to next letter
|
c = *str++;
|
||||||
strChar = *str++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing found
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* StringUtils::FindIgnoreCase(const char* str, const char* toFind)
|
const char* StringUtils::FindIgnoreCase(const char* str, const char* toFind)
|
||||||
{
|
{
|
||||||
// Validate input
|
|
||||||
if (toFind == nullptr || str == nullptr)
|
if (toFind == nullptr || str == nullptr)
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get upper-case first letter of the find string (to reduce the number of full strnicmps)
|
const char findInitial = (char)ToUpper(*toFind);
|
||||||
char findInitial = (char)ToUpper(*toFind);
|
const int32 length = Length(toFind++) - 1;
|
||||||
|
char c = *str++;
|
||||||
// Get length of find string, and increment past first letter
|
while (c)
|
||||||
int32 length = Length(toFind++) - 1;
|
|
||||||
|
|
||||||
// Get the first letter of the search string, and increment past it
|
|
||||||
char strChar = *str++;
|
|
||||||
|
|
||||||
// While we aren't at end of string
|
|
||||||
while (strChar)
|
|
||||||
{
|
{
|
||||||
// Make sure it's upper-case
|
c = (char)ToUpper(c);
|
||||||
strChar = (char)ToUpper(strChar);
|
if (c == findInitial && !CompareIgnoreCase(str, toFind, length))
|
||||||
|
|
||||||
// If it matches the first letter of the find string, do a case-insensitive string compare for the length of the find string
|
|
||||||
if (strChar == findInitial && !CompareIgnoreCase(str, toFind, length))
|
|
||||||
{
|
{
|
||||||
// If we found the string, then return a pointer to the beginning of it in the search string
|
|
||||||
return str - 1;
|
return str - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go to next letter
|
c = *str++;
|
||||||
strChar = *str++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing found
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ uint64 LinuxFileSystem::GetFileSize(const StringView& path)
|
|||||||
const StringAsANSI<> pathANSI(*path, path.Length());
|
const StringAsANSI<> pathANSI(*path, path.Length());
|
||||||
if (stat(pathANSI.Get(), &fileInfo) != -1)
|
if (stat(pathANSI.Get(), &fileInfo) != -1)
|
||||||
{
|
{
|
||||||
// make sure to return -1 for directories
|
// Check for directories
|
||||||
if (S_ISDIR(fileInfo.st_mode))
|
if (S_ISDIR(fileInfo.st_mode))
|
||||||
{
|
{
|
||||||
fileInfo.st_size = -1;
|
fileInfo.st_size = -1;
|
||||||
|
|||||||
@@ -1329,13 +1329,13 @@ bool LinuxPlatform::Init()
|
|||||||
Platform::MemoryClear(cpuInfos, sizeof(cpuInfos));
|
Platform::MemoryClear(cpuInfos, sizeof(cpuInfos));
|
||||||
int maxCoreId = 0;
|
int maxCoreId = 0;
|
||||||
int maxPackageId = 0;
|
int maxPackageId = 0;
|
||||||
int numCpusAvailable = 0;
|
int cpuCountAvailable = 0;
|
||||||
|
|
||||||
for (int32 cpuIdx = 0; cpuIdx < CPU_SETSIZE; cpuIdx++)
|
for (int32 cpuIdx = 0; cpuIdx < CPU_SETSIZE; cpuIdx++)
|
||||||
{
|
{
|
||||||
if (CPU_ISSET(cpuIdx, &availableCpusMask))
|
if (CPU_ISSET(cpuIdx, &availableCpusMask))
|
||||||
{
|
{
|
||||||
numCpusAvailable++;
|
cpuCountAvailable++;
|
||||||
|
|
||||||
sprintf(fileNameBuffer, "/sys/devices/system/cpu/cpu%d/topology/core_id", cpuIdx);
|
sprintf(fileNameBuffer, "/sys/devices/system/cpu/cpu%d/topology/core_id", cpuIdx);
|
||||||
if (FILE* coreIdFile = fopen(fileNameBuffer, "r"))
|
if (FILE* coreIdFile = fopen(fileNameBuffer, "r"))
|
||||||
@@ -1363,31 +1363,28 @@ bool LinuxPlatform::Init()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int numCores = maxCoreId + 1;
|
int coresCount = maxCoreId + 1;
|
||||||
int numPackages = maxPackageId + 1;
|
int packagesCount = maxPackageId + 1;
|
||||||
int numPairs = numPackages * numCores;
|
int pairsCount = packagesCount * coresCount;
|
||||||
|
|
||||||
// AArch64 topology seems to be incompatible with the above assumptions, particularly, core_id can be all 0 while the cores themselves are obviously independent.
|
if (coresCount * 2 < cpuCountAvailable)
|
||||||
// Check if num CPUs available to us is more than 2 per core (i.e. more than reasonable when hyperthreading is involved), and if so, don't trust the topology.
|
|
||||||
if (numCores * 2 < numCpusAvailable)
|
|
||||||
{
|
{
|
||||||
// Consider all CPUs to be separate
|
numberOfCores = cpuCountAvailable;
|
||||||
numberOfCores = numCpusAvailable;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
byte* pairs = (byte*)Allocator::Allocate(numPairs);
|
byte* pairs = (byte*)Allocator::Allocate(pairsCount);
|
||||||
Platform::MemoryClear(pairs, numPairs * sizeof(unsigned char));
|
Platform::MemoryClear(pairs, pairsCount * sizeof(unsigned char));
|
||||||
|
|
||||||
for (int32 cpuIdx = 0; cpuIdx < CPU_SETSIZE; cpuIdx++)
|
for (int32 cpuIdx = 0; cpuIdx < CPU_SETSIZE; cpuIdx++)
|
||||||
{
|
{
|
||||||
if (CPU_ISSET(cpuIdx, &availableCpusMask))
|
if (CPU_ISSET(cpuIdx, &availableCpusMask))
|
||||||
{
|
{
|
||||||
pairs[cpuInfos[cpuIdx].Package * numCores + cpuInfos[cpuIdx].Core] = 1;
|
pairs[cpuInfos[cpuIdx].Package * coresCount + cpuInfos[cpuIdx].Core] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int32 i = 0; i < numPairs; i++)
|
for (int32 i = 0; i < pairsCount; i++)
|
||||||
{
|
{
|
||||||
numberOfCores += pairs[i];
|
numberOfCores += pairs[i];
|
||||||
}
|
}
|
||||||
@@ -1395,7 +1392,7 @@ bool LinuxPlatform::Init()
|
|||||||
Allocator::Free(pairs);
|
Allocator::Free(pairs);
|
||||||
}
|
}
|
||||||
|
|
||||||
UnixCpu.ProcessorPackageCount = numPackages;
|
UnixCpu.ProcessorPackageCount = packagesCount;
|
||||||
UnixCpu.ProcessorCoreCount = Math::Max(numberOfCores, 1);
|
UnixCpu.ProcessorCoreCount = Math::Max(numberOfCores, 1);
|
||||||
UnixCpu.LogicalProcessorCount = CPU_COUNT(&availableCpusMask);
|
UnixCpu.LogicalProcessorCount = CPU_COUNT(&availableCpusMask);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ bool WindowsMouse::WndProc(Window* window, const UINT msg, WPARAM wParam, LPARAM
|
|||||||
float NormalizeXInputAxis(const int16 axisVal)
|
float NormalizeXInputAxis(const int16 axisVal)
|
||||||
{
|
{
|
||||||
// Normalize [-32768..32767] -> [-1..1]
|
// Normalize [-32768..32767] -> [-1..1]
|
||||||
const float norm = axisVal <= 0 ? 32768.f : 32767.f;
|
const float norm = axisVal <= 0 ? 32768.0f : 32767.0f;
|
||||||
return float(axisVal) / norm;
|
return float(axisVal) / norm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ FontTextureAtlas::FontTextureAtlas(const SpawnParams& params, const AssetInfo* i
|
|||||||
|
|
||||||
uint32 FontTextureAtlas::GetPaddingAmount() const
|
uint32 FontTextureAtlas::GetPaddingAmount() const
|
||||||
{
|
{
|
||||||
return (_paddingStyle == NoPadding ? 0 : 1);
|
return _paddingStyle == NoPadding ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FontTextureAtlas::Setup(PixelFormat format, PaddingStyle paddingStyle)
|
void FontTextureAtlas::Setup(PixelFormat format, PaddingStyle paddingStyle)
|
||||||
@@ -97,107 +97,95 @@ bool FontTextureAtlas::Invalidate(uint32 x, uint32 y, uint32 width, uint32 heigh
|
|||||||
|
|
||||||
void FontTextureAtlas::CopyDataIntoSlot(const Slot* slot, const Array<byte>& data)
|
void FontTextureAtlas::CopyDataIntoSlot(const Slot* slot, const Array<byte>& data)
|
||||||
{
|
{
|
||||||
// Copy pixel data to the texture
|
|
||||||
uint8* start = &_data[slot->Y * _width * _bytesPerPixel + slot->X * _bytesPerPixel];
|
uint8* start = &_data[slot->Y * _width * _bytesPerPixel + slot->X * _bytesPerPixel];
|
||||||
|
|
||||||
// Account for same padding on each sides
|
|
||||||
const uint32 padding = GetPaddingAmount();
|
const uint32 padding = GetPaddingAmount();
|
||||||
const uint32 allPadding = padding * 2;
|
const uint32 allPadding = padding * 2;
|
||||||
|
const uint32 srcWidth = slot->Width - allPadding;
|
||||||
|
const uint32 srcHeight = slot->Height - allPadding;
|
||||||
|
|
||||||
// The width of the source texture without padding (actual width)
|
RowData rowData;
|
||||||
const uint32 sourceWidth = slot->Width - allPadding;
|
rowData.DstData = start;
|
||||||
const uint32 sourceHeight = slot->Height - allPadding;
|
rowData.SrcData = data.Get();
|
||||||
|
rowData.DstTextureWidth = _width;
|
||||||
|
rowData.SrcTextureWidth = srcWidth;
|
||||||
|
rowData.RowWidth = slot->Width;
|
||||||
|
|
||||||
CopyRowData copyRowData;
|
// Start with padding
|
||||||
copyRowData.DestData = start;
|
|
||||||
copyRowData.SrcData = data.Get();
|
|
||||||
copyRowData.DestTextureWidth = _width;
|
|
||||||
copyRowData.SrcTextureWidth = sourceWidth;
|
|
||||||
copyRowData.RowWidth = slot->Width;
|
|
||||||
|
|
||||||
// Apply the padding for bilinear filtering
|
|
||||||
// Not used if no padding (assumes sampling outside boundaries of the sub texture is not possible)
|
|
||||||
if (padding > 0)
|
if (padding > 0)
|
||||||
{
|
{
|
||||||
// Copy first color row into padding.
|
rowData.SrcRow = 0;
|
||||||
copyRowData.SrcRow = 0;
|
rowData.DstRow = 0;
|
||||||
copyRowData.DestRow = 0;
|
|
||||||
|
|
||||||
if (_paddingStyle == DilateBorder)
|
if (_paddingStyle == DilateBorder)
|
||||||
{
|
{
|
||||||
copyRow(copyRowData);
|
copyRow(rowData);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
zeroRow(copyRowData);
|
zeroRow(rowData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy each row of the texture
|
// Copy each row of the texture
|
||||||
for (uint32 row = padding; row < slot->Height - padding; row++)
|
for (uint32 row = padding; row < slot->Height - padding; row++)
|
||||||
{
|
{
|
||||||
copyRowData.SrcRow = row - padding;
|
rowData.SrcRow = row - padding;
|
||||||
copyRowData.DestRow = row;
|
rowData.DstRow = row;
|
||||||
|
|
||||||
copyRow(copyRowData);
|
copyRow(rowData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Finish with padding
|
||||||
if (padding > 0)
|
if (padding > 0)
|
||||||
{
|
{
|
||||||
// Copy last color row into padding row for bilinear filtering
|
rowData.SrcRow = srcHeight - 1;
|
||||||
copyRowData.SrcRow = sourceHeight - 1;
|
rowData.DstRow = slot->Height - padding;
|
||||||
copyRowData.DestRow = slot->Height - padding;
|
|
||||||
|
|
||||||
if (_paddingStyle == DilateBorder)
|
if (_paddingStyle == DilateBorder)
|
||||||
{
|
copyRow(rowData);
|
||||||
copyRow(copyRowData);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
zeroRow(rowData);
|
||||||
zeroRow(copyRowData);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FontTextureAtlas::copyRow(const CopyRowData& copyRowData) const
|
void FontTextureAtlas::copyRow(const RowData& copyRowData) const
|
||||||
{
|
{
|
||||||
const byte* data = copyRowData.SrcData;
|
const byte* data = copyRowData.SrcData;
|
||||||
byte* start = copyRowData.DestData;
|
byte* start = copyRowData.DstData;
|
||||||
const uint32 sourceWidth = copyRowData.SrcTextureWidth;
|
const uint32 srdWidth = copyRowData.SrcTextureWidth;
|
||||||
const uint32 destWidth = copyRowData.DestTextureWidth;
|
const uint32 dstWidth = copyRowData.DstTextureWidth;
|
||||||
const uint32 srcRow = copyRowData.SrcRow;
|
const uint32 srcRow = copyRowData.SrcRow;
|
||||||
const uint32 destRow = copyRowData.DestRow;
|
const uint32 dstRow = copyRowData.DstRow;
|
||||||
const uint32 padding = GetPaddingAmount();
|
const uint32 padding = GetPaddingAmount();
|
||||||
|
|
||||||
const byte* sourceDataAddr = &data[(srcRow * sourceWidth) * _bytesPerPixel];
|
const byte* srcData = &data[srcRow * srdWidth * _bytesPerPixel];
|
||||||
byte* destDataAddr = &start[(destRow * destWidth + padding) * _bytesPerPixel];
|
byte* dstData = &start[(dstRow * dstWidth + padding) * _bytesPerPixel];
|
||||||
Platform::MemoryCopy(destDataAddr, sourceDataAddr, sourceWidth * _bytesPerPixel);
|
Platform::MemoryCopy(dstData, srcData, srdWidth * _bytesPerPixel);
|
||||||
|
|
||||||
if (padding > 0)
|
if (padding > 0)
|
||||||
{
|
{
|
||||||
byte* destPaddingPixelLeft = &start[(destRow * destWidth) * _bytesPerPixel];
|
byte* dstPaddingPixelLeft = &start[dstRow * dstWidth * _bytesPerPixel];
|
||||||
byte* destPaddingPixelRight = destPaddingPixelLeft + ((copyRowData.RowWidth - 1) * _bytesPerPixel);
|
byte* dstPaddingPixelRight = dstPaddingPixelLeft + (copyRowData.RowWidth - 1) * _bytesPerPixel;
|
||||||
if (_paddingStyle == DilateBorder)
|
if (_paddingStyle == DilateBorder)
|
||||||
{
|
{
|
||||||
const byte* firstPixel = sourceDataAddr;
|
const byte* firstPixel = srcData;
|
||||||
const byte* lastPixel = sourceDataAddr + ((sourceWidth - 1) * _bytesPerPixel);
|
const byte* lastPixel = srcData + (srdWidth - 1) * _bytesPerPixel;
|
||||||
Platform::MemoryCopy(destPaddingPixelLeft, firstPixel, _bytesPerPixel);
|
Platform::MemoryCopy(dstPaddingPixelLeft, firstPixel, _bytesPerPixel);
|
||||||
Platform::MemoryCopy(destPaddingPixelRight, lastPixel, _bytesPerPixel);
|
Platform::MemoryCopy(dstPaddingPixelRight, lastPixel, _bytesPerPixel);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Platform::MemoryClear(destPaddingPixelLeft, _bytesPerPixel);
|
Platform::MemoryClear(dstPaddingPixelLeft, _bytesPerPixel);
|
||||||
Platform::MemoryClear(destPaddingPixelRight, _bytesPerPixel);
|
Platform::MemoryClear(dstPaddingPixelRight, _bytesPerPixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FontTextureAtlas::zeroRow(const CopyRowData& copyRowData) const
|
void FontTextureAtlas::zeroRow(const RowData& copyRowData) const
|
||||||
{
|
{
|
||||||
const uint32 destWidth = copyRowData.DestTextureWidth;
|
const uint32 dstWidth = copyRowData.DstTextureWidth;
|
||||||
const uint32 destRow = copyRowData.DestRow;
|
const uint32 dstRow = copyRowData.DstRow;
|
||||||
byte* destData = ©RowData.DestData[destRow * destWidth * _bytesPerPixel];
|
byte* dstData = ©RowData.DstData[dstRow * dstWidth * _bytesPerPixel];
|
||||||
Platform::MemoryClear(destData, copyRowData.RowWidth * _bytesPerPixel);
|
Platform::MemoryClear(dstData, copyRowData.RowWidth * _bytesPerPixel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FontTextureAtlas::unload(bool isReloading)
|
void FontTextureAtlas::unload(bool isReloading)
|
||||||
|
|||||||
@@ -15,28 +15,15 @@ API_CLASS(NoSpawn) class FLAXENGINE_API FontTextureAtlas : public Texture
|
|||||||
DECLARE_BINARY_ASSET_HEADER(FontTextureAtlas, TexturesSerializedVersion);
|
DECLARE_BINARY_ASSET_HEADER(FontTextureAtlas, TexturesSerializedVersion);
|
||||||
private:
|
private:
|
||||||
|
|
||||||
struct CopyRowData
|
struct RowData
|
||||||
{
|
{
|
||||||
// Source data to copy
|
|
||||||
const byte* SrcData;
|
const byte* SrcData;
|
||||||
|
uint8* DstData;
|
||||||
// Place to copy data to
|
|
||||||
uint8* DestData;
|
|
||||||
|
|
||||||
// The row number to copy
|
|
||||||
uint32 SrcRow;
|
uint32 SrcRow;
|
||||||
|
uint32 DstRow;
|
||||||
// The row number to copy to
|
|
||||||
uint32 DestRow;
|
|
||||||
|
|
||||||
// The width of a source row
|
|
||||||
uint32 RowWidth;
|
uint32 RowWidth;
|
||||||
|
|
||||||
// The width of the source texture
|
|
||||||
uint32 SrcTextureWidth;
|
uint32 SrcTextureWidth;
|
||||||
|
uint32 DstTextureWidth;
|
||||||
// The width of the dest texture
|
|
||||||
uint32 DestTextureWidth;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -226,8 +213,8 @@ private:
|
|||||||
|
|
||||||
Slot* invalidate(Slot* parent, uint32 x, uint32 y, uint32 width, uint32 height);
|
Slot* invalidate(Slot* parent, uint32 x, uint32 y, uint32 width, uint32 height);
|
||||||
void markAsDirty();
|
void markAsDirty();
|
||||||
void copyRow(const CopyRowData& copyRowData) const;
|
void copyRow(const RowData& copyRowData) const;
|
||||||
void zeroRow(const CopyRowData& copyRowData) const;
|
void zeroRow(const RowData& copyRowData) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|||||||
@@ -769,26 +769,22 @@ void Render2D::PopClip()
|
|||||||
OnClipScissors();
|
OnClipScissors();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComputeEffectiveKernelSize(float strength, int32& outKernelSize, int32& outDownSampleAmount)
|
void CalculateKernelSize(float strength, int32& kernelSize, int32& downSample)
|
||||||
{
|
{
|
||||||
// Auto-compute radius based on the strength
|
kernelSize = Math::RoundToInt(strength * 3.0f);
|
||||||
outKernelSize = Math::RoundToInt(strength * 3.f);
|
|
||||||
|
|
||||||
// Down sample if needed
|
if (DownsampleForBlur && kernelSize > 9)
|
||||||
if (DownsampleForBlur && outKernelSize > 9)
|
|
||||||
{
|
{
|
||||||
outDownSampleAmount = outKernelSize >= 64 ? 4 : 2;
|
downSample = kernelSize >= 64 ? 4 : 2;
|
||||||
outKernelSize /= outDownSampleAmount;
|
kernelSize /= downSample;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kernel sizes must be odd
|
if (kernelSize % 2 == 0)
|
||||||
if (outKernelSize % 2 == 0)
|
|
||||||
{
|
{
|
||||||
outKernelSize++;
|
kernelSize++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clamp kernel to valid bounds
|
kernelSize = Math::Clamp(kernelSize, 3, 255);
|
||||||
outKernelSize = Math::Clamp(outKernelSize, 3, 255);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static float GetWeight(float dist, float strength)
|
static float GetWeight(float dist, float strength)
|
||||||
@@ -940,16 +936,13 @@ void DrawBatch(int32 startIndex, int32 count)
|
|||||||
int32 renderTargetWidth = Math::Min(Math::RoundToInt(d.AsBlur.Width), limits.MaximumTexture2DSize);
|
int32 renderTargetWidth = Math::Min(Math::RoundToInt(d.AsBlur.Width), limits.MaximumTexture2DSize);
|
||||||
int32 renderTargetHeight = Math::Min(Math::RoundToInt(d.AsBlur.Height), limits.MaximumTexture2DSize);
|
int32 renderTargetHeight = Math::Min(Math::RoundToInt(d.AsBlur.Height), limits.MaximumTexture2DSize);
|
||||||
|
|
||||||
int32 kernelSize = 0;
|
int32 kernelSize = 0, downSample = 0;
|
||||||
int32 downSampleAmount = 0;
|
CalculateKernelSize(blurStrength, kernelSize, downSample);
|
||||||
ComputeEffectiveKernelSize(blurStrength, kernelSize, downSampleAmount);
|
if (downSample > 0)
|
||||||
const bool needDownscale = downSampleAmount > 0;
|
|
||||||
|
|
||||||
if (needDownscale)
|
|
||||||
{
|
{
|
||||||
renderTargetWidth = Math::DivideAndRoundUp(renderTargetWidth, downSampleAmount);
|
renderTargetWidth = Math::DivideAndRoundUp(renderTargetWidth, downSample);
|
||||||
renderTargetHeight = Math::DivideAndRoundUp(renderTargetHeight, downSampleAmount);
|
renderTargetHeight = Math::DivideAndRoundUp(renderTargetHeight, downSample);
|
||||||
blurStrength /= downSampleAmount;
|
blurStrength /= downSample;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip if no chance to render anything
|
// Skip if no chance to render anything
|
||||||
@@ -1304,10 +1297,8 @@ void Render2D::DrawRectangle(const Rectangle& rect, const Color& color1, const C
|
|||||||
drawCall.StartIB = IBIndex;
|
drawCall.StartIB = IBIndex;
|
||||||
drawCall.CountIB = 4 * (6 + 3);
|
drawCall.CountIB = 4 * (6 + 3);
|
||||||
|
|
||||||
// Half of the width of the filter size to use for anti-aliasing. Increasing this value will increase the fuzziness of line edges.
|
// The has to match HLSL code
|
||||||
const float filterScale = 1.0f; // Must match HLSL code
|
const float filterScale = 1.0f;
|
||||||
|
|
||||||
// The amount we increase each side of the line to generate enough pixels
|
|
||||||
const float thicknessHalf = (2.82842712f + thickness) * 0.5f + filterScale;
|
const float thicknessHalf = (2.82842712f + thickness) * 0.5f + filterScale;
|
||||||
|
|
||||||
for (int32 i = 1; i < 5; i++)
|
for (int32 i = 1; i < 5; i++)
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext)
|
|||||||
{
|
{
|
||||||
context->SetRenderTarget(lut->ViewVolume());
|
context->SetRenderTarget(lut->ViewVolume());
|
||||||
|
|
||||||
// Render a quad per slice affected by the given bounds
|
// Render one fullscreen-triangle per slice intersecting the bounds
|
||||||
const int32 numInstances = lutDesc.Depth;
|
const int32 numInstances = lutDesc.Depth;
|
||||||
context->DrawFullscreenTriangle(numInstances);
|
context->DrawFullscreenTriangle(numInstances);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ void VolumetricFogPass::RenderRadialLight(RenderContext& renderContext, GPUConte
|
|||||||
|
|
||||||
// Ensure to have valid buffers created
|
// Ensure to have valid buffers created
|
||||||
if (_vbCircleRasterize == nullptr || _ibCircleRasterize == nullptr)
|
if (_vbCircleRasterize == nullptr || _ibCircleRasterize == nullptr)
|
||||||
InitCircleRasterizeBuffers();
|
InitCircleBuffer();
|
||||||
|
|
||||||
// Call rendering to the volume
|
// Call rendering to the volume
|
||||||
const int32 psIndex = (_cache.TemporalReprojection ? 1 : 0) + 2;
|
const int32 psIndex = (_cache.TemporalReprojection ? 1 : 0) + 2;
|
||||||
@@ -378,7 +378,7 @@ void VolumetricFogPass::RenderRadialLight(RenderContext& renderContext, GPUConte
|
|||||||
|
|
||||||
// Ensure to have valid buffers created
|
// Ensure to have valid buffers created
|
||||||
if (_vbCircleRasterize == nullptr || _ibCircleRasterize == nullptr)
|
if (_vbCircleRasterize == nullptr || _ibCircleRasterize == nullptr)
|
||||||
InitCircleRasterizeBuffers();
|
InitCircleBuffer();
|
||||||
|
|
||||||
// Call rendering to the volume
|
// Call rendering to the volume
|
||||||
const int32 psIndex = (cache.TemporalReprojection ? 1 : 0) + (withShadow ? 2 : 0);
|
const int32 psIndex = (cache.TemporalReprojection ? 1 : 0) + (withShadow ? 2 : 0);
|
||||||
@@ -644,19 +644,16 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
|||||||
context->FlushState();
|
context->FlushState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolumetricFogPass::InitCircleRasterizeBuffers()
|
void VolumetricFogPass::InitCircleBuffer()
|
||||||
{
|
{
|
||||||
const int32 vertices = 8;
|
const int32 vertices = 8;
|
||||||
const int32 triangles = vertices - 2;
|
const int32 triangles = vertices - 2;
|
||||||
const int32 rings = vertices;
|
const int32 rings = vertices;
|
||||||
const float radiansPerRingSegment = PI / (float)rings;
|
const float radiansPerRingSegment = PI / (float)rings;
|
||||||
|
|
||||||
Vector2 vbData[vertices];
|
Vector2 vbData[vertices];
|
||||||
uint16 ibData[triangles * 3];
|
uint16 ibData[triangles * 3];
|
||||||
|
|
||||||
// Boost the effective radius so that the edges of the circle approximation lie on the circle, instead of the vertices
|
|
||||||
const float radiusScale = 1.0f / Math::Cos(radiansPerRingSegment);
|
const float radiusScale = 1.0f / Math::Cos(radiansPerRingSegment);
|
||||||
|
|
||||||
for (int32 vertexIndex = 0; vertexIndex < vertices; vertexIndex++)
|
for (int32 vertexIndex = 0; vertexIndex < vertices; vertexIndex++)
|
||||||
{
|
{
|
||||||
const float angle = vertexIndex / static_cast<float>(vertices - 1) * 2 * PI;
|
const float angle = vertexIndex / static_cast<float>(vertices - 1) * 2 * PI;
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ private:
|
|||||||
|
|
||||||
bool Init(RenderContext& renderContext, GPUContext* context, VolumetricFogOptions& options);
|
bool Init(RenderContext& renderContext, GPUContext* context, VolumetricFogOptions& options);
|
||||||
GPUTextureView* GetLocalShadowedLightScattering(RenderContext& renderContext, GPUContext* context, VolumetricFogOptions& options) const;
|
GPUTextureView* GetLocalShadowedLightScattering(RenderContext& renderContext, GPUContext* context, VolumetricFogOptions& options) const;
|
||||||
void InitCircleRasterizeBuffers();
|
void InitCircleBuffer();
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RenderRadialLight(RenderContext& renderContext, GPUContext* context, T& light, LightShadowData& shadow);
|
void RenderRadialLight(RenderContext& renderContext, GPUContext* context, T& light, LightShadowData& shadow);
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
#include "Engine/Core/Math/Math.h"
|
#include "Engine/Core/Math/Math.h"
|
||||||
#include "Engine/Platform/Platform.h"
|
#include "Engine/Platform/Platform.h"
|
||||||
|
|
||||||
// CRC 32 polynomial
|
// Use CRC 32 polynomial
|
||||||
enum { Crc32Poly = 0x04c11db7 };
|
enum { Crc32Poly = 0x04c11db7 };
|
||||||
|
|
||||||
uint32 Crc::CRCTablesSB8[8][256] =
|
uint32 Crc::CachedCRCTablesSB8[8][256] =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
0x00000000,
|
0x00000000,
|
||||||
@@ -2088,16 +2088,16 @@ void Crc::Init()
|
|||||||
CRC = (CRC & 1) ? (CRC >> 1) ^ rCrc32Poly : (CRC >> 1);
|
CRC = (CRC & 1) ? (CRC >> 1) ^ rCrc32Poly : (CRC >> 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(CRCTablesSB8[0][i] == CRC);
|
ASSERT(CachedCRCTablesSB8[0][i] == CRC);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32 i = 0; i != 256; ++i)
|
for (uint32 i = 0; i != 256; ++i)
|
||||||
{
|
{
|
||||||
uint32 CRC = CRCTablesSB8[0][i];
|
uint32 CRC = CachedCRCTablesSB8[0][i];
|
||||||
for (uint32 j = 1; j != 8; ++j)
|
for (uint32 j = 1; j != 8; ++j)
|
||||||
{
|
{
|
||||||
CRC = CRCTablesSB8[0][CRC & 0xFF] ^ (CRC >> 8);
|
CRC = CachedCRCTablesSB8[0][CRC & 0xFF] ^ (CRC >> 8);
|
||||||
ASSERT(CRCTablesSB8[j][i] == CRC);
|
ASSERT(CachedCRCTablesSB8[j][i] == CRC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -2121,7 +2121,7 @@ uint32 Crc::MemCrc32(const void* data, int32 length, uint32 crc)
|
|||||||
|
|
||||||
for (; initBytes; --initBytes)
|
for (; initBytes; --initBytes)
|
||||||
{
|
{
|
||||||
crc = (crc >> 8) ^ CRCTablesSB8[0][(crc & 0xFF) ^ *ptr++];
|
crc = (crc >> 8) ^ CachedCRCTablesSB8[0][(crc & 0xFF) ^ *ptr++];
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ptr4 = (const uint32*)ptr;
|
auto ptr4 = (const uint32*)ptr;
|
||||||
@@ -2130,14 +2130,14 @@ uint32 Crc::MemCrc32(const void* data, int32 length, uint32 crc)
|
|||||||
uint32 v1 = *ptr4++ ^ crc;
|
uint32 v1 = *ptr4++ ^ crc;
|
||||||
uint32 v2 = *ptr4++;
|
uint32 v2 = *ptr4++;
|
||||||
crc =
|
crc =
|
||||||
CRCTablesSB8[7][v1 & 0xFF] ^
|
CachedCRCTablesSB8[7][v1 & 0xFF] ^
|
||||||
CRCTablesSB8[6][(v1 >> 8) & 0xFF] ^
|
CachedCRCTablesSB8[6][(v1 >> 8) & 0xFF] ^
|
||||||
CRCTablesSB8[5][(v1 >> 16) & 0xFF] ^
|
CachedCRCTablesSB8[5][(v1 >> 16) & 0xFF] ^
|
||||||
CRCTablesSB8[4][v1 >> 24] ^
|
CachedCRCTablesSB8[4][v1 >> 24] ^
|
||||||
CRCTablesSB8[3][v2 & 0xFF] ^
|
CachedCRCTablesSB8[3][v2 & 0xFF] ^
|
||||||
CRCTablesSB8[2][(v2 >> 8) & 0xFF] ^
|
CachedCRCTablesSB8[2][(v2 >> 8) & 0xFF] ^
|
||||||
CRCTablesSB8[1][(v2 >> 16) & 0xFF] ^
|
CachedCRCTablesSB8[1][(v2 >> 16) & 0xFF] ^
|
||||||
CRCTablesSB8[0][v2 >> 24];
|
CachedCRCTablesSB8[0][v2 >> 24];
|
||||||
}
|
}
|
||||||
ptr = (const uint8*)ptr4;
|
ptr = (const uint8*)ptr4;
|
||||||
|
|
||||||
@@ -2146,7 +2146,7 @@ uint32 Crc::MemCrc32(const void* data, int32 length, uint32 crc)
|
|||||||
|
|
||||||
for (; length; --length)
|
for (; length; --length)
|
||||||
{
|
{
|
||||||
crc = (crc >> 8) ^ CRCTablesSB8[0][(crc & 0xFF) ^ *ptr++];
|
crc = (crc >> 8) ^ CachedCRCTablesSB8[0][(crc & 0xFF) ^ *ptr++];
|
||||||
}
|
}
|
||||||
|
|
||||||
return ~crc;
|
return ~crc;
|
||||||
|
|||||||
@@ -5,55 +5,17 @@
|
|||||||
#include "Engine/Core/Types/BaseTypes.h"
|
#include "Engine/Core/Types/BaseTypes.h"
|
||||||
#include "Engine/Core/Templates.h"
|
#include "Engine/Core/Templates.h"
|
||||||
|
|
||||||
// The CRC hash generation for different types of input data.
|
// The utilities for CRC hash generation.
|
||||||
class Crc
|
class Crc
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Lookup table with pre-calculated CRC values - slicing by 8 implementation.
|
// Helper lookup table with cached CRC values.
|
||||||
static uint32 CRCTablesSB8[8][256];
|
static uint32 CachedCRCTablesSB8[8][256];
|
||||||
|
|
||||||
// Initializes the CRC lookup table. Must be called before any of the CRC functions are used.
|
// Initializes the CRC lookup table. Must be called before any of the CRC functions are used.
|
||||||
static void Init();
|
static void Init();
|
||||||
|
|
||||||
// Generates CRC hash of the memory area
|
// Generates CRC hash of the memory area
|
||||||
static uint32 MemCrc32(const void* data, int32 length, uint32 crc = 0);
|
static uint32 MemCrc32(const void* data, int32 length, uint32 crc = 0);
|
||||||
|
|
||||||
// String CRC.
|
|
||||||
template<typename CharType>
|
|
||||||
static typename TEnableIf<sizeof(CharType) != 1, uint32>::Type StrCrc32(const CharType* data, uint32 crc = 0)
|
|
||||||
{
|
|
||||||
// We ensure that we never try to do a StrCrc32 with a CharType of more than 4 bytes. This is because
|
|
||||||
// we always want to treat every CRC as if it was based on 4 byte chars, even if it's less, because we
|
|
||||||
// want consistency between equivalent strings with different character types.
|
|
||||||
static_assert(sizeof(CharType) <= 4, "StrCrc32 only works with CharType up to 32 bits.");
|
|
||||||
|
|
||||||
crc = ~crc;
|
|
||||||
while (CharType c = *data++)
|
|
||||||
{
|
|
||||||
crc = (crc >> 8) ^ CRCTablesSB8[0][(crc ^ c) & 0xFF];
|
|
||||||
c >>= 8;
|
|
||||||
crc = (crc >> 8) ^ CRCTablesSB8[0][(crc ^ c) & 0xFF];
|
|
||||||
c >>= 8;
|
|
||||||
crc = (crc >> 8) ^ CRCTablesSB8[0][(crc ^ c) & 0xFF];
|
|
||||||
c >>= 8;
|
|
||||||
crc = (crc >> 8) ^ CRCTablesSB8[0][(crc ^ c) & 0xFF];
|
|
||||||
}
|
|
||||||
return ~crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename CharType>
|
|
||||||
static typename TEnableIf<sizeof(CharType) == 1, uint32>::Type StrCrc32(const CharType* data, uint32 crc = 0)
|
|
||||||
{
|
|
||||||
// Overload for when CharType is a byte, which causes warnings when right-shifting by 8
|
|
||||||
crc = ~crc;
|
|
||||||
while (CharType Ch = *data++)
|
|
||||||
{
|
|
||||||
crc = (crc >> 8) ^ CRCTablesSB8[0][(crc ^ Ch) & 0xFF];
|
|
||||||
crc = (crc >> 8) ^ CRCTablesSB8[0][(crc) & 0xFF];
|
|
||||||
crc = (crc >> 8) ^ CRCTablesSB8[0][(crc) & 0xFF];
|
|
||||||
crc = (crc >> 8) ^ CRCTablesSB8[0][(crc) & 0xFF];
|
|
||||||
}
|
|
||||||
return ~crc;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ namespace Flax.Deploy
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Function to query the registry under HKCU/HKLM Win32/Wow64 software registry keys for a certain install directory.
|
/// Queries the registry entries for a certain install directory of the MsBuild.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True if found MsBuild tool, otherwise false.</returns>
|
/// <returns>True if found MsBuild tool, otherwise false.</returns>
|
||||||
private static bool TryReadInstallPath(string keyRelativePath, string keyName, string msBuildRelativePath, out string outMsBuildPath)
|
private static bool TryReadInstallPath(string keyRelativePath, string keyName, string msBuildRelativePath, out string outMsBuildPath)
|
||||||
|
|||||||
@@ -406,14 +406,13 @@ namespace Flax.Build.Platforms
|
|||||||
case WindowsPlatformToolset.v142:
|
case WindowsPlatformToolset.v142:
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
// Use the x86-on-x64 compiler
|
|
||||||
string crossCompilerPath = Path.Combine(vcToolChainDir, "bin", "HostX64", "x86", "cl.exe");
|
string crossCompilerPath = Path.Combine(vcToolChainDir, "bin", "HostX64", "x86", "cl.exe");
|
||||||
if (File.Exists(crossCompilerPath))
|
if (File.Exists(crossCompilerPath))
|
||||||
{
|
{
|
||||||
return Path.GetDirectoryName(crossCompilerPath);
|
return Path.GetDirectoryName(crossCompilerPath);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
// Otherwise the native 32-bit compiler if present
|
|
||||||
string nativeCompilerPath = Path.Combine(vcToolChainDir, "bin", "HostX86", "x86", "cl.exe");
|
string nativeCompilerPath = Path.Combine(vcToolChainDir, "bin", "HostX86", "x86", "cl.exe");
|
||||||
if (File.Exists(nativeCompilerPath))
|
if (File.Exists(nativeCompilerPath))
|
||||||
{
|
{
|
||||||
@@ -442,14 +441,12 @@ namespace Flax.Build.Platforms
|
|||||||
{
|
{
|
||||||
case WindowsPlatformToolset.v140:
|
case WindowsPlatformToolset.v140:
|
||||||
{
|
{
|
||||||
// Use the native 64-bit compiler if present
|
|
||||||
string nativeCompilerPath = Path.Combine(vcToolChainDir, "bin", "amd64", "cl.exe");
|
string nativeCompilerPath = Path.Combine(vcToolChainDir, "bin", "amd64", "cl.exe");
|
||||||
if (File.Exists(nativeCompilerPath))
|
if (File.Exists(nativeCompilerPath))
|
||||||
{
|
{
|
||||||
return Path.GetDirectoryName(nativeCompilerPath);
|
return Path.GetDirectoryName(nativeCompilerPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise use the x64-on-x86 compiler
|
|
||||||
string crossCompilerPath = Path.Combine(vcToolChainDir, "bin", "x86_amd64", "cl.exe");
|
string crossCompilerPath = Path.Combine(vcToolChainDir, "bin", "x86_amd64", "cl.exe");
|
||||||
if (File.Exists(crossCompilerPath))
|
if (File.Exists(crossCompilerPath))
|
||||||
{
|
{
|
||||||
@@ -462,14 +459,12 @@ namespace Flax.Build.Platforms
|
|||||||
case WindowsPlatformToolset.v141:
|
case WindowsPlatformToolset.v141:
|
||||||
case WindowsPlatformToolset.v142:
|
case WindowsPlatformToolset.v142:
|
||||||
{
|
{
|
||||||
// Use the native 64-bit compiler if present
|
|
||||||
string nativeCompilerPath = Path.Combine(vcToolChainDir, "bin", "HostX64", "x64", "cl.exe");
|
string nativeCompilerPath = Path.Combine(vcToolChainDir, "bin", "HostX64", "x64", "cl.exe");
|
||||||
if (File.Exists(nativeCompilerPath))
|
if (File.Exists(nativeCompilerPath))
|
||||||
{
|
{
|
||||||
return Path.GetDirectoryName(nativeCompilerPath);
|
return Path.GetDirectoryName(nativeCompilerPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise try the x64-on-x86 compiler
|
|
||||||
string crossCompilerPath = Path.Combine(vcToolChainDir, "bin", "HostX86", "x64", "cl.exe");
|
string crossCompilerPath = Path.Combine(vcToolChainDir, "bin", "HostX86", "x64", "cl.exe");
|
||||||
if (File.Exists(crossCompilerPath))
|
if (File.Exists(crossCompilerPath))
|
||||||
{
|
{
|
||||||
@@ -488,6 +483,7 @@ namespace Flax.Build.Platforms
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
void IProjectCustomizer.GetProjectArchitectureName(Project project, Platform platform, TargetArchitecture architecture, ref string name)
|
void IProjectCustomizer.GetProjectArchitectureName(Project project, Platform platform, TargetArchitecture architecture, ref string name)
|
||||||
{
|
{
|
||||||
if (architecture == TargetArchitecture.x86)
|
if (architecture == TargetArchitecture.x86)
|
||||||
|
|||||||
@@ -355,22 +355,21 @@ namespace Flax.Build
|
|||||||
/// <returns>Thew relative path from the given directory.</returns>
|
/// <returns>Thew relative path from the given directory.</returns>
|
||||||
public static string MakePathRelativeTo(string path, string directory)
|
public static string MakePathRelativeTo(string path, string directory)
|
||||||
{
|
{
|
||||||
// Find how much of the path is common between the two paths. This length does not include a trailing directory separator character
|
int sharedDirectoryLength = -1;
|
||||||
int commonDirectoryLength = -1;
|
|
||||||
for (int i = 0;; i++)
|
for (int i = 0;; i++)
|
||||||
{
|
{
|
||||||
if (i == path.Length)
|
if (i == path.Length)
|
||||||
{
|
{
|
||||||
// Check if two paths are the same
|
// Paths are the same
|
||||||
if (i == directory.Length)
|
if (i == directory.Length)
|
||||||
{
|
{
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we're finishing on a complete directory name
|
// Finished on a complete directory
|
||||||
if (directory[i] == Path.DirectorySeparatorChar)
|
if (directory[i] == Path.DirectorySeparatorChar)
|
||||||
{
|
{
|
||||||
commonDirectoryLength = i;
|
sharedDirectoryLength = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -378,16 +377,15 @@ namespace Flax.Build
|
|||||||
|
|
||||||
if (i == directory.Length)
|
if (i == directory.Length)
|
||||||
{
|
{
|
||||||
// Check whether the end of the directory name coincides with a boundary for the current name
|
// End of the directory name starts with a boundary for the current name
|
||||||
if (path[i] == Path.DirectorySeparatorChar)
|
if (path[i] == Path.DirectorySeparatorChar)
|
||||||
{
|
{
|
||||||
commonDirectoryLength = i;
|
sharedDirectoryLength = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the two paths match, and bail if they don't. Increase the common directory length if we've reached a separator
|
|
||||||
if (string.Compare(path, i, directory, i, 1, StringComparison.OrdinalIgnoreCase) != 0)
|
if (string.Compare(path, i, directory, i, 1, StringComparison.OrdinalIgnoreCase) != 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
@@ -395,19 +393,19 @@ namespace Flax.Build
|
|||||||
|
|
||||||
if (path[i] == Path.DirectorySeparatorChar)
|
if (path[i] == Path.DirectorySeparatorChar)
|
||||||
{
|
{
|
||||||
commonDirectoryLength = i;
|
sharedDirectoryLength = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there's no relative path, just return the absolute path
|
// No shared path found
|
||||||
if (commonDirectoryLength == -1)
|
if (sharedDirectoryLength == -1)
|
||||||
{
|
{
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append all the '..' separators to get back to the common directory, then the rest of the string to reach the target item
|
// Add all the '..' separators to get back to the shared directory,
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
for (int i = commonDirectoryLength + 1; i < directory.Length; i++)
|
for (int i = sharedDirectoryLength + 1; i < directory.Length; i++)
|
||||||
{
|
{
|
||||||
// Move up a directory
|
// Move up a directory
|
||||||
result.Append("..");
|
result.Append("..");
|
||||||
@@ -420,9 +418,9 @@ namespace Flax.Build
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commonDirectoryLength + 1 < path.Length)
|
if (sharedDirectoryLength + 1 < path.Length)
|
||||||
{
|
{
|
||||||
result.Append(path, commonDirectoryLength + 1, path.Length - commonDirectoryLength - 1);
|
result.Append(path, sharedDirectoryLength + 1, path.Length - sharedDirectoryLength - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.ToString();
|
return result.ToString();
|
||||||
|
|||||||
Reference in New Issue
Block a user