Add support for stripping texture resolution for target platform when cooking game
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
"Version": {
|
||||
"Major": 1,
|
||||
"Minor": 1,
|
||||
"Build": 6220
|
||||
"Build": 6221
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "Engine/Core/Config/PlatformSettings.h"
|
||||
#include "Engine/Core/Config/GameSettings.h"
|
||||
#include "Engine/Core/Config/BuildSettings.h"
|
||||
#include "Engine/Streaming/StreamingSettings.h"
|
||||
#include "Engine/ShadersCompilation/ShadersCompilation.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Graphics/Textures/TextureData.h"
|
||||
@@ -44,6 +45,35 @@
|
||||
|
||||
Dictionary<String, CookAssetsStep::ProcessAssetFunc> CookAssetsStep::AssetProcessors;
|
||||
|
||||
bool CookAssetsStep::CacheEntry::IsValid(bool withDependencies)
|
||||
{
|
||||
AssetInfo assetInfo;
|
||||
if (Content::GetAssetInfo(ID, assetInfo))
|
||||
{
|
||||
if (TypeName == assetInfo.TypeName)
|
||||
{
|
||||
if (FileSystem::GetFileLastEditTime(assetInfo.Path) <= FileModified)
|
||||
{
|
||||
bool isValid = true;
|
||||
if (withDependencies)
|
||||
{
|
||||
for (auto& f : FileDependencies)
|
||||
{
|
||||
if (FileSystem::GetFileLastEditTime(f.First) > f.Second)
|
||||
{
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isValid)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CookAssetsStep::CacheEntry& CookAssetsStep::CacheData::CreateEntry(const JsonAssetBase* asset, String& cachedFilePath)
|
||||
{
|
||||
ASSERT(asset->DataTypeName.HasChars());
|
||||
@@ -68,7 +98,6 @@ CookAssetsStep::CacheEntry& CookAssetsStep::CacheData::CreateEntry(const Asset*
|
||||
void CookAssetsStep::CacheData::InvalidateShaders()
|
||||
{
|
||||
LOG(Info, "Invalidating cached shader assets.");
|
||||
|
||||
for (auto e = Entries.Begin(); e.IsNotEnd(); ++e)
|
||||
{
|
||||
auto& typeName = e->Value.TypeName;
|
||||
@@ -83,6 +112,23 @@ void CookAssetsStep::CacheData::InvalidateShaders()
|
||||
}
|
||||
}
|
||||
|
||||
void CookAssetsStep::CacheData::InvalidateTextures()
|
||||
{
|
||||
LOG(Info, "Invalidating cached texture assets.");
|
||||
for (auto e = Entries.Begin(); e.IsNotEnd(); ++e)
|
||||
{
|
||||
auto& typeName = e->Value.TypeName;
|
||||
if (
|
||||
typeName == Texture::TypeName ||
|
||||
typeName == CubeTexture::TypeName ||
|
||||
typeName == SpriteAtlas::TypeName
|
||||
)
|
||||
{
|
||||
Entries.Remove(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CookAssetsStep::CacheData::Load(CookingData& data)
|
||||
{
|
||||
HeaderFilePath = data.CacheDirectory / String::Format(TEXT("CookedHeader_{0}.bin"), FLAXENGINE_VERSION_BUILD);
|
||||
@@ -159,17 +205,17 @@ void CookAssetsStep::CacheData::Load(CookingData& data)
|
||||
Entries.Clear();
|
||||
}
|
||||
|
||||
const auto buildSettings = BuildSettings::Get();
|
||||
const auto gameSettings = GameSettings::Get();
|
||||
|
||||
// Invalidate shaders and assets with shaders if need to rebuild them
|
||||
bool invalidateShaders = false;
|
||||
const auto buildSettings = BuildSettings::Get();
|
||||
const bool shadersNoOptimize = buildSettings->ShadersNoOptimize;
|
||||
const bool shadersGenerateDebugData = buildSettings->ShadersGenerateDebugData;
|
||||
if (shadersNoOptimize != Settings.Global.ShadersNoOptimize)
|
||||
if (buildSettings->ShadersNoOptimize != Settings.Global.ShadersNoOptimize)
|
||||
{
|
||||
LOG(Info, "ShadersNoOptimize option has been modified.");
|
||||
invalidateShaders = true;
|
||||
}
|
||||
if (shadersGenerateDebugData != Settings.Global.ShadersGenerateDebugData)
|
||||
if (buildSettings->ShadersGenerateDebugData != Settings.Global.ShadersGenerateDebugData)
|
||||
{
|
||||
LOG(Info, "ShadersGenerateDebugData option has been modified.");
|
||||
invalidateShaders = true;
|
||||
@@ -218,6 +264,12 @@ void CookAssetsStep::CacheData::Load(CookingData& data)
|
||||
#endif
|
||||
if (invalidateShaders)
|
||||
InvalidateShaders();
|
||||
|
||||
// Invalidate textures if streaming settings gets modified
|
||||
if (Settings.Global.StreamingSettingsAssetId != gameSettings->Streaming || (Entries.ContainsKey(gameSettings->Streaming) && !Entries[gameSettings->Streaming].IsValid()))
|
||||
{
|
||||
InvalidateTextures();
|
||||
}
|
||||
}
|
||||
|
||||
void CookAssetsStep::CacheData::Save()
|
||||
@@ -541,91 +593,127 @@ bool ProcessParticleEmitter(CookAssetsStep::AssetCookData& data)
|
||||
bool ProcessTextureBase(CookAssetsStep::AssetCookData& data)
|
||||
{
|
||||
const auto asset = static_cast<TextureBase*>(data.Asset);
|
||||
|
||||
// Check if target platform doesn't support the texture format
|
||||
const auto& assetHeader = asset->StreamingTexture()->GetHeader();
|
||||
const auto format = asset->Format();
|
||||
const auto targetFormat = data.Data.Tools->GetTextureFormat(data.Data, asset, format);
|
||||
const auto streamingSettings = StreamingSettings::Get();
|
||||
int32 mipLevelsMax = GPU_MAX_TEXTURE_MIP_LEVELS;
|
||||
if (assetHeader->TextureGroup >= 0 && assetHeader->TextureGroup < streamingSettings->TextureGroups.Count())
|
||||
{
|
||||
auto& group = streamingSettings->TextureGroups[assetHeader->TextureGroup];
|
||||
mipLevelsMax = group.MipLevelsMax;
|
||||
group.MipLevelsMaxPerPlatform.TryGet(data.Data.Tools->GetPlatform(), mipLevelsMax);
|
||||
}
|
||||
|
||||
// Faster path if don't need to modify texture for the target platform
|
||||
if (format == targetFormat && assetHeader->MipLevels <= mipLevelsMax)
|
||||
{
|
||||
return CookAssetsStep::ProcessDefaultAsset(data);
|
||||
}
|
||||
|
||||
// Extract texture data from the asset
|
||||
TextureData textureDataSrc;
|
||||
auto assetLock = asset->LockData();
|
||||
if (asset->GetTextureData(textureDataSrc, false))
|
||||
{
|
||||
LOG(Error, "Failed to load data from texture {0}", asset->ToString());
|
||||
return true;
|
||||
}
|
||||
|
||||
TextureData* textureData = &textureDataSrc;
|
||||
TextureData textureDataTmp1;
|
||||
|
||||
if (format != targetFormat)
|
||||
{
|
||||
// Extract texture data from the asset
|
||||
TextureData textureData;
|
||||
auto assetLock = asset->LockData();
|
||||
if (asset->GetTextureData(textureData, false))
|
||||
{
|
||||
LOG(Error, "Failed to load data from texture {0}", asset->ToString());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convert texture data to the target format
|
||||
TextureData targetTextureData;
|
||||
if (TextureTool::Convert(targetTextureData, textureData, targetFormat))
|
||||
if (TextureTool::Convert(textureDataTmp1, *textureData, targetFormat))
|
||||
{
|
||||
LOG(Error, "Failed to convert texture {0} from format {1} to {2}", asset->ToString(), (int32)format, (int32)targetFormat);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Adjust texture header
|
||||
auto& header = *(TextureHeader*)data.InitData.CustomData.Get();
|
||||
header.Width = targetTextureData.Width;
|
||||
header.Height = targetTextureData.Height;
|
||||
header.Format = targetTextureData.Format;
|
||||
header.MipLevels = targetTextureData.GetMipLevels();
|
||||
|
||||
// Serialize texture data into the asset chunks
|
||||
for (int32 mipIndex = 0; mipIndex < targetTextureData.GetMipLevels(); mipIndex++)
|
||||
{
|
||||
auto chunk = New<FlaxChunk>();
|
||||
data.InitData.Header.Chunks[mipIndex] = chunk;
|
||||
|
||||
// Calculate the texture data storage layout
|
||||
uint32 rowPitch, slicePitch;
|
||||
const int32 mipWidth = Math::Max(1, targetTextureData.Width >> mipIndex);
|
||||
const int32 mipHeight = Math::Max(1, targetTextureData.Height >> mipIndex);
|
||||
RenderTools::ComputePitch(targetTextureData.Format, mipWidth, mipHeight, rowPitch, slicePitch);
|
||||
chunk->Data.Allocate(slicePitch * targetTextureData.GetArraySize());
|
||||
|
||||
// Copy array slices into mip data (sequential)
|
||||
for (int32 arrayIndex = 0; arrayIndex < targetTextureData.Items.Count(); arrayIndex++)
|
||||
{
|
||||
auto& mipData = targetTextureData.Items[arrayIndex].Mips[mipIndex];
|
||||
byte* src = mipData.Data.Get();
|
||||
byte* dst = chunk->Data.Get() + (slicePitch * arrayIndex);
|
||||
|
||||
// Faster path if source and destination data layout matches
|
||||
if (rowPitch == mipData.RowPitch && slicePitch == mipData.DepthPitch)
|
||||
{
|
||||
Platform::MemoryCopy(dst, src, slicePitch);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto copyRowSize = Math::Min(mipData.RowPitch, rowPitch);
|
||||
for (uint32 line = 0; line < mipData.Lines; line++)
|
||||
{
|
||||
Platform::MemoryCopy(dst, src, copyRowSize);
|
||||
src += mipData.RowPitch;
|
||||
dst += rowPitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone any custom asset chunks (eg. sprite atlas data, mips are in 0-13 chunks)
|
||||
for (int32 i = 14; i < ASSET_FILE_DATA_CHUNKS; i++)
|
||||
{
|
||||
const auto chunk = asset->GetChunk(i);
|
||||
if (chunk != nullptr && chunk->IsMissing() && chunk->ExistsInFile())
|
||||
{
|
||||
if (asset->Storage->LoadAssetChunk(chunk))
|
||||
return true;
|
||||
data.InitData.Header.Chunks[i] = chunk->Clone();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
textureData = &textureDataSrc;
|
||||
}
|
||||
|
||||
// Fallback to the default asset processing
|
||||
return CookAssetsStep::ProcessDefaultAsset(data);
|
||||
if (assetHeader->MipLevels > mipLevelsMax)
|
||||
{
|
||||
// Reduce texture quality
|
||||
const int32 mipLevelsToStrip = assetHeader->MipLevels - mipLevelsMax;
|
||||
textureData->Width = Math::Max(1, textureData->Width >> mipLevelsToStrip);
|
||||
textureData->Height = Math::Max(1, textureData->Height >> mipLevelsToStrip);
|
||||
textureData->Depth = Math::Max(1, textureData->Depth >> mipLevelsToStrip);
|
||||
for (int32 arrayIndex = 0; arrayIndex < textureData->Items.Count(); arrayIndex++)
|
||||
{
|
||||
auto& item = textureData->Items[arrayIndex];
|
||||
Array<TextureMipData, FixedAllocation<GPU_MAX_TEXTURE_MIP_LEVELS>> oldMips(MoveTemp(item.Mips));
|
||||
item.Mips.Resize(mipLevelsMax);
|
||||
for (int32 mipIndex = 0; mipIndex < mipLevelsMax; mipIndex++)
|
||||
{
|
||||
auto& dstMip = item.Mips[mipIndex];
|
||||
auto& srcMip = oldMips[mipIndex + mipLevelsToStrip];
|
||||
dstMip = MoveTemp(srcMip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust texture header
|
||||
auto& header = *(TextureHeader*)data.InitData.CustomData.Get();
|
||||
header.Width = textureData->Width;
|
||||
header.Height = textureData->Height;
|
||||
header.Depth = textureData->Depth;
|
||||
header.Format = textureData->Format;
|
||||
header.MipLevels = textureData->GetMipLevels();
|
||||
|
||||
// Serialize texture data into the asset chunks
|
||||
for (int32 mipIndex = 0; mipIndex < textureData->GetMipLevels(); mipIndex++)
|
||||
{
|
||||
auto chunk = New<FlaxChunk>();
|
||||
data.InitData.Header.Chunks[mipIndex] = chunk;
|
||||
|
||||
// Calculate the texture data storage layout
|
||||
uint32 rowPitch, slicePitch;
|
||||
const int32 mipWidth = Math::Max(1, textureData->Width >> mipIndex);
|
||||
const int32 mipHeight = Math::Max(1, textureData->Height >> mipIndex);
|
||||
RenderTools::ComputePitch(textureData->Format, mipWidth, mipHeight, rowPitch, slicePitch);
|
||||
chunk->Data.Allocate(slicePitch * textureData->GetArraySize());
|
||||
|
||||
// Copy array slices into mip data (sequential)
|
||||
for (int32 arrayIndex = 0; arrayIndex < textureData->Items.Count(); arrayIndex++)
|
||||
{
|
||||
auto& mipData = textureData->Items[arrayIndex].Mips[mipIndex];
|
||||
byte* src = mipData.Data.Get();
|
||||
byte* dst = chunk->Data.Get() + (slicePitch * arrayIndex);
|
||||
|
||||
// Faster path if source and destination data layout matches
|
||||
if (rowPitch == mipData.RowPitch && slicePitch == mipData.DepthPitch)
|
||||
{
|
||||
Platform::MemoryCopy(dst, src, slicePitch);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto copyRowSize = Math::Min(mipData.RowPitch, rowPitch);
|
||||
for (uint32 line = 0; line < mipData.Lines; line++)
|
||||
{
|
||||
Platform::MemoryCopy(dst, src, copyRowSize);
|
||||
src += mipData.RowPitch;
|
||||
dst += rowPitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone any custom asset chunks (eg. sprite atlas data, mips are in 0-13 chunks)
|
||||
for (int32 i = 14; i < ASSET_FILE_DATA_CHUNKS; i++)
|
||||
{
|
||||
const auto chunk = asset->GetChunk(i);
|
||||
if (chunk != nullptr && chunk->IsMissing() && chunk->ExistsInFile())
|
||||
{
|
||||
if (asset->Storage->LoadAssetChunk(chunk))
|
||||
return true;
|
||||
data.InitData.Header.Chunks[i] = chunk->Clone();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CookAssetsStep::CookAssetsStep()
|
||||
@@ -938,6 +1026,7 @@ bool CookAssetsStep::Perform(CookingData& data)
|
||||
{
|
||||
cache.Settings.Global.ShadersNoOptimize = buildSettings->ShadersNoOptimize;
|
||||
cache.Settings.Global.ShadersGenerateDebugData = buildSettings->ShadersGenerateDebugData;
|
||||
cache.Settings.Global.StreamingSettingsAssetId = gameSettings->Streaming;
|
||||
}
|
||||
|
||||
// Note: this step converts all the assets (even the json) into the binary files (FlaxStorage format).
|
||||
|
||||
@@ -49,6 +49,8 @@ public:
|
||||
/// The list of files on which this entry depends on. Cached date is the last edit time used to discard cache result on modification.
|
||||
/// </summary>
|
||||
FileDependenciesList FileDependencies;
|
||||
|
||||
bool IsValid(bool withDependencies = false);
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -93,6 +95,7 @@ public:
|
||||
{
|
||||
bool ShadersNoOptimize;
|
||||
bool ShadersGenerateDebugData;
|
||||
Guid StreamingSettingsAssetId;
|
||||
} Global;
|
||||
} Settings;
|
||||
|
||||
@@ -134,6 +137,11 @@ public:
|
||||
/// </summary>
|
||||
void InvalidateShaders();
|
||||
|
||||
/// <summary>
|
||||
/// Removes all cached entries for assets that contain a texture. This forces rebuild for them.
|
||||
/// </summary>
|
||||
void InvalidateTextures();
|
||||
|
||||
/// <summary>
|
||||
/// Loads the cache for the given cooking data.
|
||||
/// </summary>
|
||||
|
||||
@@ -12,6 +12,51 @@
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Content/Factories/BinaryAssetFactory.h"
|
||||
|
||||
TextureMipData::TextureMipData()
|
||||
: RowPitch(0)
|
||||
, DepthPitch(0)
|
||||
, Lines(0)
|
||||
{
|
||||
}
|
||||
|
||||
TextureMipData::TextureMipData(const TextureMipData& other)
|
||||
: RowPitch(other.RowPitch)
|
||||
, DepthPitch(other.DepthPitch)
|
||||
, Lines(other.Lines)
|
||||
, Data(other.Data)
|
||||
{
|
||||
}
|
||||
|
||||
TextureMipData::TextureMipData(TextureMipData&& other) noexcept
|
||||
: RowPitch(other.RowPitch)
|
||||
, DepthPitch(other.DepthPitch)
|
||||
, Lines(other.Lines)
|
||||
, Data(std::move(other.Data))
|
||||
{
|
||||
}
|
||||
|
||||
TextureMipData& TextureMipData::operator=(const TextureMipData& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
RowPitch = other.RowPitch;
|
||||
DepthPitch = other.DepthPitch;
|
||||
Lines = other.Lines;
|
||||
Data = other.Data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
TextureMipData& TextureMipData::operator=(TextureMipData&& other) noexcept
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
RowPitch = other.RowPitch;
|
||||
DepthPitch = other.DepthPitch;
|
||||
Lines = other.Lines;
|
||||
Data = MoveTemp(other.Data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
REGISTER_BINARY_ASSET_ABSTRACT(TextureBase, "FlaxEngine.TextureBase");
|
||||
|
||||
TextureBase::TextureBase(const SpawnParams& params, const AssetInfo* info)
|
||||
|
||||
@@ -19,6 +19,12 @@ public:
|
||||
uint32 Lines;
|
||||
BytesContainer Data;
|
||||
|
||||
TextureMipData();
|
||||
TextureMipData(const TextureMipData& other);
|
||||
TextureMipData(TextureMipData&& other) noexcept;
|
||||
TextureMipData& operator=(const TextureMipData& other);
|
||||
TextureMipData& operator=(TextureMipData&& other) noexcept;
|
||||
|
||||
template<typename T>
|
||||
T& Get(int32 x, int32 y)
|
||||
{
|
||||
@@ -120,7 +126,6 @@ public:
|
||||
/// <summary>
|
||||
/// Gets amount of textures in the array
|
||||
/// </summary>
|
||||
/// <returns>Array size</returns>
|
||||
int32 GetArraySize() const
|
||||
{
|
||||
return Items.Count();
|
||||
@@ -129,7 +134,6 @@ public:
|
||||
/// <summary>
|
||||
/// Gets amount of mip maps in the textures
|
||||
/// </summary>
|
||||
/// <returns>Amount of mip levels</returns>
|
||||
int32 GetMipLevels() const
|
||||
{
|
||||
return Items.HasItems() ? Items[0].Mips.Count() : 0;
|
||||
|
||||
@@ -13,5 +13,5 @@ using System.Runtime.InteropServices;
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: Guid("b8442186-4a70-7c85-704a-857c262d00f6")]
|
||||
[assembly: AssemblyVersion("1.1.6220")]
|
||||
[assembly: AssemblyFileVersion("1.1.6220")]
|
||||
[assembly: AssemblyVersion("1.1.6221")]
|
||||
[assembly: AssemblyFileVersion("1.1.6221")]
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
#pragma once
|
||||
|
||||
#define FLAXENGINE_NAME "FlaxEngine"
|
||||
#define FLAXENGINE_VERSION Version(1, 1, 6220)
|
||||
#define FLAXENGINE_VERSION_TEXT "1.1.6220"
|
||||
#define FLAXENGINE_VERSION Version(1, 1, 6221)
|
||||
#define FLAXENGINE_VERSION_TEXT "1.1.6221"
|
||||
#define FLAXENGINE_VERSION_MAJOR 1
|
||||
#define FLAXENGINE_VERSION_MINOR 1
|
||||
#define FLAXENGINE_VERSION_BUILD 6220
|
||||
#define FLAXENGINE_VERSION_BUILD 6221
|
||||
#define FLAXENGINE_COMPANY "Flax"
|
||||
#define FLAXENGINE_COPYRIGHT "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved."
|
||||
|
||||
|
||||
Reference in New Issue
Block a user