Fix code style

This commit is contained in:
Wojtek Figat
2020-12-30 11:20:37 +01:00
parent 4ec3e6aed9
commit 4c205be617
56 changed files with 608 additions and 1139 deletions

View File

@@ -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);

View File

@@ -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);
} }
} }
} }

View File

@@ -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

View File

@@ -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)
{ {

View File

@@ -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);
}; };

View File

@@ -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;

View File

@@ -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()

View File

@@ -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>

View File

@@ -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.

View File

@@ -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

View File

@@ -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>

View File

@@ -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;

View File

@@ -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
{ {

View File

@@ -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)

View File

@@ -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)
{ {

View File

@@ -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);

View File

@@ -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)

View File

@@ -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:

View File

@@ -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>

View File

@@ -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

View File

@@ -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;

View File

@@ -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);
} }

View File

@@ -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

View File

@@ -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);
} }
}; };

View File

@@ -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;
} }

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}; };

View File

@@ -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++)
{ {

View File

@@ -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)

View File

@@ -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);
} }

View File

@@ -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;

View File

@@ -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())

View File

@@ -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:

View File

@@ -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

View File

@@ -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);
}; };

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
} }

View File

@@ -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;

View File

@@ -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;
} }

View File

@@ -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;

View File

@@ -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);
} }

View File

@@ -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;
} }

View File

@@ -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 = &copyRowData.DestData[destRow * destWidth * _bytesPerPixel]; byte* dstData = &copyRowData.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)

View File

@@ -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:

View File

@@ -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++)

View File

@@ -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);
} }

View File

@@ -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;

View File

@@ -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>

View File

@@ -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;

View File

@@ -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;
}
}; };

View File

@@ -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)

View File

@@ -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)

View File

@@ -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();