Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#include "Engine/Core/Collections/BitArray.h"
|
||||
#include "Engine/Tools/ModelTool/ModelTool.h"
|
||||
#include "Engine/Tools/ModelTool/VertexTriangleAdjacency.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#define USE_MIKKTSPACE 1
|
||||
#include "ThirdParty/MikkTSpace/mikktspace.h"
|
||||
@@ -78,6 +79,7 @@ void RemapArrayHelper(Array<T>& target, const std::vector<uint32_t>& remap)
|
||||
|
||||
bool MeshData::GenerateLightmapUVs()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
#if PLATFORM_WINDOWS
|
||||
// Prepare
|
||||
HRESULT hr;
|
||||
@@ -235,6 +237,7 @@ void RemapBuffer(Array<T>& src, Array<T>& dst, const Array<int32>& mapping, int3
|
||||
|
||||
void MeshData::BuildIndexBuffer()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
const auto startTime = Platform::GetTimeSeconds();
|
||||
|
||||
const int32 vertexCount = Positions.Count();
|
||||
@@ -341,6 +344,7 @@ bool MeshData::GenerateNormals(float smoothingAngle)
|
||||
LOG(Warning, "Missing vertex or index data to generate normals.");
|
||||
return true;
|
||||
}
|
||||
PROFILE_CPU();
|
||||
|
||||
const auto startTime = Platform::GetTimeSeconds();
|
||||
|
||||
@@ -520,6 +524,7 @@ bool MeshData::GenerateTangents(float smoothingAngle)
|
||||
LOG(Warning, "Missing normals or texcoors data to generate tangents.");
|
||||
return true;
|
||||
}
|
||||
PROFILE_CPU();
|
||||
|
||||
const auto startTime = Platform::GetTimeSeconds();
|
||||
const int32 vertexCount = Positions.Count();
|
||||
@@ -706,6 +711,7 @@ void MeshData::ImproveCacheLocality()
|
||||
|
||||
if (Positions.IsEmpty() || Indices.IsEmpty() || Positions.Count() <= VertexCacheSize)
|
||||
return;
|
||||
PROFILE_CPU();
|
||||
|
||||
const auto startTime = Platform::GetTimeSeconds();
|
||||
|
||||
@@ -886,6 +892,7 @@ void MeshData::ImproveCacheLocality()
|
||||
|
||||
float MeshData::CalculateTrianglesArea() const
|
||||
{
|
||||
PROFILE_CPU();
|
||||
float sum = 0;
|
||||
// TODO: use SIMD
|
||||
for (int32 i = 0; i + 2 < Indices.Count(); i += 3)
|
||||
|
||||
@@ -625,6 +625,11 @@ bool MaterialSlotEntry::UsesProperties() const
|
||||
Normals.TextureIndex != -1;
|
||||
}
|
||||
|
||||
ModelLodData::~ModelLodData()
|
||||
{
|
||||
Meshes.ClearDelete();
|
||||
}
|
||||
|
||||
BoundingBox ModelLodData::GetBox() const
|
||||
{
|
||||
if (Meshes.IsEmpty())
|
||||
@@ -644,11 +649,9 @@ void ModelData::CalculateLODsScreenSizes()
|
||||
{
|
||||
const float autoComputeLodPowerBase = 0.5f;
|
||||
const int32 lodCount = LODs.Count();
|
||||
|
||||
for (int32 lodIndex = 0; lodIndex < lodCount; lodIndex++)
|
||||
{
|
||||
auto& lod = LODs[lodIndex];
|
||||
|
||||
if (lodIndex == 0)
|
||||
{
|
||||
lod.ScreenSize = 1.0f;
|
||||
@@ -675,6 +678,8 @@ void ModelData::TransformBuffer(const Matrix& matrix)
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
bool ModelData::Pack2ModelHeader(WriteStream* stream) const
|
||||
{
|
||||
// Validate input
|
||||
@@ -724,7 +729,12 @@ bool ModelData::Pack2ModelHeader(WriteStream* stream) const
|
||||
|
||||
// Amount of meshes
|
||||
const int32 meshes = lod.Meshes.Count();
|
||||
if (meshes == 0 || meshes > MODEL_MAX_MESHES)
|
||||
if (meshes == 0)
|
||||
{
|
||||
LOG(Warning, "Empty LOD.");
|
||||
return true;
|
||||
}
|
||||
if (meshes > MODEL_MAX_MESHES)
|
||||
{
|
||||
LOG(Warning, "Too many meshes per LOD.");
|
||||
return true;
|
||||
@@ -880,20 +890,21 @@ bool ModelData::Pack2SkinnedModelHeader(WriteStream* stream) const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModelData::Pack2AnimationHeader(WriteStream* stream) const
|
||||
bool ModelData::Pack2AnimationHeader(WriteStream* stream, int32 animIndex) const
|
||||
{
|
||||
// Validate input
|
||||
if (stream == nullptr)
|
||||
if (stream == nullptr || animIndex < 0 || animIndex >= Animations.Count())
|
||||
{
|
||||
Log::ArgumentNullException();
|
||||
return true;
|
||||
}
|
||||
if (Animation.Duration <= ZeroTolerance || Animation.FramesPerSecond <= ZeroTolerance)
|
||||
auto& anim = Animations.Get()[animIndex];
|
||||
if (anim.Duration <= ZeroTolerance || anim.FramesPerSecond <= ZeroTolerance)
|
||||
{
|
||||
Log::InvalidOperationException(TEXT("Invalid animation duration."));
|
||||
return true;
|
||||
}
|
||||
if (Animation.Channels.IsEmpty())
|
||||
if (anim.Channels.IsEmpty())
|
||||
{
|
||||
Log::ArgumentOutOfRangeException(TEXT("Channels"), TEXT("Animation channels collection cannot be empty."));
|
||||
return true;
|
||||
@@ -901,22 +912,23 @@ bool ModelData::Pack2AnimationHeader(WriteStream* stream) const
|
||||
|
||||
// Info
|
||||
stream->WriteInt32(100); // Header version (for fast version upgrades without serialization format change)
|
||||
stream->WriteDouble(Animation.Duration);
|
||||
stream->WriteDouble(Animation.FramesPerSecond);
|
||||
stream->WriteBool(Animation.EnableRootMotion);
|
||||
stream->WriteString(Animation.RootNodeName, 13);
|
||||
stream->WriteDouble(anim.Duration);
|
||||
stream->WriteDouble(anim.FramesPerSecond);
|
||||
stream->WriteBool(anim.EnableRootMotion);
|
||||
stream->WriteString(anim.RootNodeName, 13);
|
||||
|
||||
// Animation channels
|
||||
stream->WriteInt32(Animation.Channels.Count());
|
||||
for (int32 i = 0; i < Animation.Channels.Count(); i++)
|
||||
stream->WriteInt32(anim.Channels.Count());
|
||||
for (int32 i = 0; i < anim.Channels.Count(); i++)
|
||||
{
|
||||
auto& anim = Animation.Channels[i];
|
||||
|
||||
stream->WriteString(anim.NodeName, 172);
|
||||
Serialization::Serialize(*stream, anim.Position);
|
||||
Serialization::Serialize(*stream, anim.Rotation);
|
||||
Serialization::Serialize(*stream, anim.Scale);
|
||||
auto& channel = anim.Channels[i];
|
||||
stream->WriteString(channel.NodeName, 172);
|
||||
Serialization::Serialize(*stream, channel.Position);
|
||||
Serialization::Serialize(*stream, channel.Rotation);
|
||||
Serialization::Serialize(*stream, channel.Scale);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -366,12 +366,32 @@ struct FLAXENGINE_API MaterialSlotEntry
|
||||
bool UsesProperties() const;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Data container for model hierarchy node.
|
||||
/// </summary>
|
||||
struct FLAXENGINE_API ModelDataNode
|
||||
{
|
||||
/// <summary>
|
||||
/// The parent node index. The root node uses value -1.
|
||||
/// </summary>
|
||||
int32 ParentIndex;
|
||||
|
||||
/// <summary>
|
||||
/// The local transformation of the node, relative to the parent node.
|
||||
/// </summary>
|
||||
Transform LocalTransform;
|
||||
|
||||
/// <summary>
|
||||
/// The name of this node.
|
||||
/// </summary>
|
||||
String Name;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Data container for LOD metadata and sub meshes.
|
||||
/// </summary>
|
||||
class FLAXENGINE_API ModelLodData
|
||||
struct FLAXENGINE_API ModelLodData
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// The screen size to switch LODs. Bottom limit of the model screen size to render this LOD.
|
||||
/// </summary>
|
||||
@@ -382,21 +402,10 @@ public:
|
||||
/// </summary>
|
||||
Array<MeshData*> Meshes;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModelLodData"/> class.
|
||||
/// </summary>
|
||||
ModelLodData()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="ModelLodData"/> class.
|
||||
/// </summary>
|
||||
~ModelLodData()
|
||||
{
|
||||
Meshes.ClearDelete();
|
||||
}
|
||||
~ModelLodData();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounding box combined for all meshes in this model LOD.
|
||||
@@ -426,7 +435,7 @@ public:
|
||||
Array<MaterialSlotEntry> Materials;
|
||||
|
||||
/// <summary>
|
||||
/// Array with all LODs. The first element is the top most LOD0 followed by the LOD1, LOD2, etc.
|
||||
/// Array with all Level Of Details that contain meshes. The first element is the top most LOD0 followed by the LOD1, LOD2, etc.
|
||||
/// </summary>
|
||||
Array<ModelLodData> LODs;
|
||||
|
||||
@@ -435,24 +444,20 @@ public:
|
||||
/// </summary>
|
||||
SkeletonData Skeleton;
|
||||
|
||||
/// <summary>
|
||||
/// The scene nodes (in hierarchy).
|
||||
/// </summary>
|
||||
Array<ModelDataNode> Nodes;
|
||||
|
||||
/// <summary>
|
||||
/// The node animations.
|
||||
/// </summary>
|
||||
AnimationData Animation;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModelData"/> class.
|
||||
/// </summary>
|
||||
ModelData()
|
||||
{
|
||||
}
|
||||
Array<AnimationData> Animations;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the valid level of details count.
|
||||
/// </summary>
|
||||
/// <returns>The LOD count.</returns>
|
||||
FORCE_INLINE int32 GetLODsCount() const
|
||||
{
|
||||
return LODs.Count();
|
||||
@@ -461,7 +466,6 @@ public:
|
||||
/// <summary>
|
||||
/// Determines whether this instance has valid skeleton structure.
|
||||
/// </summary>
|
||||
/// <returns>True if has skeleton, otherwise false.</returns>
|
||||
FORCE_INLINE bool HasSkeleton() const
|
||||
{
|
||||
return Skeleton.Bones.HasItems();
|
||||
@@ -479,6 +483,7 @@ public:
|
||||
/// <param name="matrix">The matrix to use for the transformation.</param>
|
||||
void TransformBuffer(const Matrix& matrix);
|
||||
|
||||
#if USE_EDITOR
|
||||
public:
|
||||
/// <summary>
|
||||
/// Pack mesh data to the header stream
|
||||
@@ -498,6 +503,8 @@ public:
|
||||
/// Pack animation data to the header stream
|
||||
/// </summary>
|
||||
/// <param name="stream">Output stream</param>
|
||||
/// <param name="animIndex">Index of animation.</param>
|
||||
/// <returns>True if cannot save data, otherwise false</returns>
|
||||
bool Pack2AnimationHeader(WriteStream* stream) const;
|
||||
bool Pack2AnimationHeader(WriteStream* stream, int32 animIndex = 0) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="sourceSkeleton">The source model skeleton.</param>
|
||||
/// <param name="targetSkeleton">The target skeleton. May be null to disable nodes mapping.</param>
|
||||
SkeletonMapping(Items& sourceSkeleton, Items* targetSkeleton)
|
||||
SkeletonMapping(const Items& sourceSkeleton, const Items* targetSkeleton)
|
||||
{
|
||||
Size = sourceSkeleton.Count();
|
||||
SourceToTarget.Resize(Size); // model => skeleton mapping
|
||||
|
||||
@@ -660,6 +660,37 @@ int PixelFormatExtensions::ComputeComponentsCount(const PixelFormat format)
|
||||
}
|
||||
}
|
||||
|
||||
int32 PixelFormatExtensions::ComputeBlockSize(PixelFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case PixelFormat::BC1_Typeless:
|
||||
case PixelFormat::BC1_UNorm:
|
||||
case PixelFormat::BC1_UNorm_sRGB:
|
||||
case PixelFormat::BC2_Typeless:
|
||||
case PixelFormat::BC2_UNorm:
|
||||
case PixelFormat::BC2_UNorm_sRGB:
|
||||
case PixelFormat::BC3_Typeless:
|
||||
case PixelFormat::BC3_UNorm:
|
||||
case PixelFormat::BC3_UNorm_sRGB:
|
||||
case PixelFormat::BC4_Typeless:
|
||||
case PixelFormat::BC4_UNorm:
|
||||
case PixelFormat::BC4_SNorm:
|
||||
case PixelFormat::BC5_Typeless:
|
||||
case PixelFormat::BC5_UNorm:
|
||||
case PixelFormat::BC5_SNorm:
|
||||
case PixelFormat::BC6H_Typeless:
|
||||
case PixelFormat::BC6H_Uf16:
|
||||
case PixelFormat::BC6H_Sf16:
|
||||
case PixelFormat::BC7_Typeless:
|
||||
case PixelFormat::BC7_UNorm:
|
||||
case PixelFormat::BC7_UNorm_sRGB:
|
||||
return 4;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
PixelFormat PixelFormatExtensions::TosRGB(const PixelFormat format)
|
||||
{
|
||||
switch (format)
|
||||
|
||||
@@ -173,6 +173,13 @@ public:
|
||||
/// <returns>The components count.</returns>
|
||||
API_FUNCTION() static int ComputeComponentsCount(PixelFormat format);
|
||||
|
||||
/// <summary>
|
||||
/// Computes the amount of pixels per-axis stored in the a single block of the format (eg. 4 for BC-family). Returns 1 for uncompressed formats.
|
||||
/// </summary>
|
||||
/// <param name="format">The <see cref="PixelFormat"/>.</param>
|
||||
/// <returns>The block pixels count.</returns>
|
||||
API_FUNCTION() static int32 ComputeBlockSize(PixelFormat format);
|
||||
|
||||
/// <summary>
|
||||
/// Finds the equivalent sRGB format to the provided format.
|
||||
/// </summary>
|
||||
|
||||
@@ -13,7 +13,7 @@ struct RenderContext;
|
||||
/// Custom PostFx which can modify final image by processing it with material based filters. The base class for all post process effects used by the graphics pipeline. Allows to extend frame rendering logic and apply custom effects such as outline, night vision, contrast etc.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Override this class and implement custom post fx logic. Use <b>MainRenderTask.Instance.CustomPostFx.Add(myPostFx)</b> to attach your script to rendering or add script to camera actor.
|
||||
/// Override this class and implement custom post fx logic. Use <b>MainRenderTask.Instance.AddCustomPostFx(myPostFx)</b> to attach your script to rendering or add script to camera actor.
|
||||
/// </remarks>
|
||||
API_CLASS(Abstract) class FLAXENGINE_API PostProcessEffect : public Script
|
||||
{
|
||||
|
||||
@@ -192,6 +192,9 @@ bool RenderBuffers::Init(int32 width, int32 height)
|
||||
_viewport = Viewport(0, 0, static_cast<float>(width), static_cast<float>(height));
|
||||
LastEyeAdaptationTime = 0;
|
||||
|
||||
// Flush any pool render targets to prevent over-allocating GPU memory when resizing game viewport
|
||||
RenderTargetPool::Flush(false, 4);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,35 +7,31 @@
|
||||
|
||||
struct Entry
|
||||
{
|
||||
bool IsOccupied;
|
||||
GPUTexture* RT;
|
||||
uint64 LastFrameTaken;
|
||||
uint64 LastFrameReleased;
|
||||
uint32 DescriptionHash;
|
||||
bool IsOccupied;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
Array<Entry> TemporaryRTs(64);
|
||||
Array<Entry> TemporaryRTs;
|
||||
}
|
||||
|
||||
void RenderTargetPool::Flush(bool force)
|
||||
void RenderTargetPool::Flush(bool force, int32 framesOffset)
|
||||
{
|
||||
const uint64 framesOffset = 3 * 60;
|
||||
if (framesOffset < 0)
|
||||
framesOffset = 3 * 60; // For how many frames RTs should be cached (by default)
|
||||
const uint64 maxReleaseFrame = Engine::FrameCount - framesOffset;
|
||||
force |= Engine::ShouldExit();
|
||||
|
||||
for (int32 i = 0; i < TemporaryRTs.Count(); i++)
|
||||
{
|
||||
auto& tmp = TemporaryRTs[i];
|
||||
|
||||
if (!tmp.IsOccupied && (force || (tmp.LastFrameReleased < maxReleaseFrame)))
|
||||
const auto& e = TemporaryRTs[i];
|
||||
if (!e.IsOccupied && (force || e.LastFrameReleased < maxReleaseFrame))
|
||||
{
|
||||
// Release
|
||||
tmp.RT->DeleteObjectNow();
|
||||
TemporaryRTs.RemoveAt(i);
|
||||
i--;
|
||||
|
||||
e.RT->DeleteObjectNow();
|
||||
TemporaryRTs.RemoveAt(i--);
|
||||
if (TemporaryRTs.IsEmpty())
|
||||
break;
|
||||
}
|
||||
@@ -48,19 +44,14 @@ GPUTexture* RenderTargetPool::Get(const GPUTextureDescription& desc)
|
||||
const uint32 descHash = GetHash(desc);
|
||||
for (int32 i = 0; i < TemporaryRTs.Count(); i++)
|
||||
{
|
||||
auto& tmp = TemporaryRTs[i];
|
||||
|
||||
if (!tmp.IsOccupied && tmp.DescriptionHash == descHash)
|
||||
auto& e = TemporaryRTs[i];
|
||||
if (!e.IsOccupied && e.DescriptionHash == descHash)
|
||||
{
|
||||
ASSERT(tmp.RT);
|
||||
|
||||
// Mark as used
|
||||
tmp.IsOccupied = true;
|
||||
tmp.LastFrameTaken = Engine::FrameCount;
|
||||
return tmp.RT;
|
||||
e.IsOccupied = true;
|
||||
return e.RT;
|
||||
}
|
||||
}
|
||||
|
||||
#if !BUILD_RELEASE
|
||||
if (TemporaryRTs.Count() > 2000)
|
||||
{
|
||||
@@ -71,24 +62,23 @@ GPUTexture* RenderTargetPool::Get(const GPUTextureDescription& desc)
|
||||
|
||||
// Create new rt
|
||||
const String name = TEXT("TemporaryRT_") + StringUtils::ToString(TemporaryRTs.Count());
|
||||
auto newRenderTarget = GPUDevice::Instance->CreateTexture(name);
|
||||
if (newRenderTarget->Init(desc))
|
||||
GPUTexture* rt = GPUDevice::Instance->CreateTexture(name);
|
||||
if (rt->Init(desc))
|
||||
{
|
||||
Delete(newRenderTarget);
|
||||
Delete(rt);
|
||||
LOG(Error, "Cannot create temporary render target. Description: {0}", desc.ToString());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create temporary rt entry
|
||||
Entry entry;
|
||||
entry.IsOccupied = true;
|
||||
entry.LastFrameReleased = 0;
|
||||
entry.LastFrameTaken = Engine::FrameCount;
|
||||
entry.RT = newRenderTarget;
|
||||
entry.DescriptionHash = descHash;
|
||||
TemporaryRTs.Add(entry);
|
||||
Entry e;
|
||||
e.IsOccupied = true;
|
||||
e.LastFrameReleased = 0;
|
||||
e.RT = rt;
|
||||
e.DescriptionHash = descHash;
|
||||
TemporaryRTs.Add(e);
|
||||
|
||||
return newRenderTarget;
|
||||
return rt;
|
||||
}
|
||||
|
||||
void RenderTargetPool::Release(GPUTexture* rt)
|
||||
@@ -98,14 +88,13 @@ void RenderTargetPool::Release(GPUTexture* rt)
|
||||
|
||||
for (int32 i = 0; i < TemporaryRTs.Count(); i++)
|
||||
{
|
||||
auto& tmp = TemporaryRTs[i];
|
||||
|
||||
if (tmp.RT == rt)
|
||||
auto& e = TemporaryRTs[i];
|
||||
if (e.RT == rt)
|
||||
{
|
||||
// Mark as free
|
||||
ASSERT(tmp.IsOccupied);
|
||||
tmp.IsOccupied = false;
|
||||
tmp.LastFrameReleased = Engine::FrameCount;
|
||||
ASSERT(e.IsOccupied);
|
||||
e.IsOccupied = false;
|
||||
e.LastFrameReleased = Engine::FrameCount;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ public:
|
||||
/// Flushes the temporary render targets.
|
||||
/// </summary>
|
||||
/// <param name="force">True if release unused render targets by force, otherwise will use a few frames of delay.</param>
|
||||
static void Flush(bool force = false);
|
||||
/// <param name="framesOffset">Amount of previous frames that should persist in the pool after flush. Resources used more than given value wil be freed. Use value of -1 to auto pick default duration.</param>
|
||||
static void Flush(bool force = false, int32 framesOffset = -1);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a temporary render target.
|
||||
|
||||
@@ -115,27 +115,20 @@ bool ShaderAssetBase::Save()
|
||||
bool IsValidShaderCache(DataContainer<byte>& shaderCache, Array<String>& includes)
|
||||
{
|
||||
if (shaderCache.Length() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MemoryReadStream stream(shaderCache.Get(), shaderCache.Length());
|
||||
|
||||
// Read cache format version
|
||||
int32 version;
|
||||
stream.ReadInt32(&version);
|
||||
if (version != GPU_SHADER_CACHE_VERSION)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the location of additional data that contains list of included source files
|
||||
int32 additionalDataStart;
|
||||
stream.ReadInt32(&additionalDataStart);
|
||||
stream.SetPosition(additionalDataStart);
|
||||
|
||||
bool result = true;
|
||||
|
||||
// Read all includes
|
||||
int32 includesCount;
|
||||
stream.ReadInt32(&includesCount);
|
||||
@@ -144,28 +137,16 @@ bool IsValidShaderCache(DataContainer<byte>& shaderCache, Array<String>& include
|
||||
{
|
||||
String& include = includes.AddOne();
|
||||
stream.ReadString(&include, 11);
|
||||
include = ShadersCompilation::ResolveShaderPath(include);
|
||||
DateTime lastEditTime;
|
||||
stream.Read(lastEditTime);
|
||||
|
||||
// Check if included file exists locally and has been modified since last compilation
|
||||
if (FileSystem::FileExists(include) && FileSystem::GetFileLastEditTime(include) > lastEditTime)
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Check duplicates
|
||||
for (int32 i = 0; i < includes.Count(); i++)
|
||||
{
|
||||
if (includes.FindLast(includes[i]) != i)
|
||||
{
|
||||
CRASH;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -480,6 +480,16 @@ bool GPUTexture::Init(const GPUTextureDescription& desc)
|
||||
break;
|
||||
}
|
||||
}
|
||||
const bool isCompressed = PixelFormatExtensions::IsCompressed(desc.Format);
|
||||
if (isCompressed)
|
||||
{
|
||||
const int32 blockSize = PixelFormatExtensions::ComputeBlockSize(desc.Format);
|
||||
if (desc.Width < blockSize || desc.Height < blockSize)
|
||||
{
|
||||
LOG(Warning, "Cannot create texture. Invalid dimensions. Description: {0}", desc.ToString());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Release previous data
|
||||
ReleaseGPU();
|
||||
@@ -487,7 +497,7 @@ bool GPUTexture::Init(const GPUTextureDescription& desc)
|
||||
// Initialize
|
||||
_desc = desc;
|
||||
_sRGB = PixelFormatExtensions::IsSRGB(desc.Format);
|
||||
_isBlockCompressed = PixelFormatExtensions::IsCompressed(desc.Format);
|
||||
_isBlockCompressed = isCompressed;
|
||||
if (OnInit())
|
||||
{
|
||||
ReleaseGPU();
|
||||
|
||||
@@ -114,9 +114,9 @@ bool StreamingTexture::Create(const TextureHeader& header)
|
||||
{
|
||||
// Ensure that streaming doesn't go too low because the hardware expects the texture to be min in size of compressed texture block
|
||||
int32 lastMip = header.MipLevels - 1;
|
||||
while ((header.Width >> lastMip) < 4 && (header.Height >> lastMip) < 4)
|
||||
while ((header.Width >> lastMip) < 4 && (header.Height >> lastMip) < 4 && lastMip > 0)
|
||||
lastMip--;
|
||||
_minMipCountBlockCompressed = header.MipLevels - lastMip + 1;
|
||||
_minMipCountBlockCompressed = Math::Min(header.MipLevels - lastMip + 1, header.MipLevels);
|
||||
}
|
||||
|
||||
// Request resource streaming
|
||||
@@ -296,6 +296,7 @@ Task* StreamingTexture::UpdateAllocation(int32 residency)
|
||||
// Setup texture
|
||||
if (texture->Init(desc))
|
||||
{
|
||||
Streaming.Error = true;
|
||||
LOG(Error, "Cannot allocate texture {0}.", ToString());
|
||||
}
|
||||
if (allocatedResidency != 0)
|
||||
|
||||
@@ -223,6 +223,11 @@ void TextureBase::SetTextureGroup(int32 textureGroup)
|
||||
}
|
||||
}
|
||||
|
||||
bool TextureBase::HasStreamingError() const
|
||||
{
|
||||
return _texture.Streaming.Error;
|
||||
}
|
||||
|
||||
BytesContainer TextureBase::GetMipData(int32 mipIndex, int32& rowPitch, int32& slicePitch)
|
||||
{
|
||||
BytesContainer result;
|
||||
|
||||
@@ -148,6 +148,11 @@ public:
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetTextureGroup(int32 textureGroup);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if texture streaming failed (eg. pixel format is unsupported or texture data cannot be uploaded to GPU due to memory limit).
|
||||
/// </summary>
|
||||
API_PROPERTY() bool HasStreamingError() const;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the mip data.
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Types/BaseTypes.h"
|
||||
#include "Types.h"
|
||||
|
||||
/// <summary>
|
||||
/// Texture utilities class
|
||||
/// </summary>
|
||||
class TextureUtils
|
||||
{
|
||||
public:
|
||||
static PixelFormat ToPixelFormat(const TextureFormatType format, int32 width, int32 height, bool canCompress)
|
||||
{
|
||||
const bool canUseBlockCompression = width % 4 == 0 && height % 4 == 0;
|
||||
if (canCompress && canUseBlockCompression)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case TextureFormatType::ColorRGB:
|
||||
return PixelFormat::BC1_UNorm;
|
||||
case TextureFormatType::ColorRGBA:
|
||||
return PixelFormat::BC3_UNorm;
|
||||
case TextureFormatType::NormalMap:
|
||||
return PixelFormat::BC5_UNorm;
|
||||
case TextureFormatType::GrayScale:
|
||||
return PixelFormat::BC4_UNorm;
|
||||
case TextureFormatType::HdrRGBA:
|
||||
return PixelFormat::BC7_UNorm;
|
||||
case TextureFormatType::HdrRGB:
|
||||
#if PLATFORM_LINUX
|
||||
// TODO: support BC6H compression for Linux Editor
|
||||
return PixelFormat::BC7_UNorm;
|
||||
#else
|
||||
return PixelFormat::BC6H_Uf16;
|
||||
#endif
|
||||
default:
|
||||
return PixelFormat::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case TextureFormatType::ColorRGB:
|
||||
return PixelFormat::R8G8B8A8_UNorm;
|
||||
case TextureFormatType::ColorRGBA:
|
||||
return PixelFormat::R8G8B8A8_UNorm;
|
||||
case TextureFormatType::NormalMap:
|
||||
return PixelFormat::R16G16_UNorm;
|
||||
case TextureFormatType::GrayScale:
|
||||
return PixelFormat::R8_UNorm;
|
||||
case TextureFormatType::HdrRGBA:
|
||||
return PixelFormat::R16G16B16A16_Float;
|
||||
case TextureFormatType::HdrRGB:
|
||||
return PixelFormat::R11G11B10_Float;
|
||||
default:
|
||||
return PixelFormat::Unknown;
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user