Refactor Texture Data function to be reusable
This commit is contained in:
@@ -742,57 +742,8 @@ bool ProcessTextureBase(CookAssetsStep::AssetCookData& data)
|
||||
{
|
||||
auto chunk = New<FlaxChunk>();
|
||||
data.InitData.Header.Chunks[mipIndex] = chunk;
|
||||
|
||||
if (header.Format == PixelFormat::Basis)
|
||||
{
|
||||
// Store as-is
|
||||
int32 maxDataSize = 0;
|
||||
for (int32 arrayIndex = 0; arrayIndex < textureData->Items.Count(); arrayIndex++)
|
||||
{
|
||||
auto& mipData = textureData->Items[arrayIndex].Mips[mipIndex];
|
||||
maxDataSize = Math::Max(maxDataSize, mipData.Data.Length());
|
||||
}
|
||||
chunk->Data.Allocate(maxDataSize * textureData->GetArraySize());
|
||||
for (int32 arrayIndex = 0; arrayIndex < textureData->Items.Count(); arrayIndex++)
|
||||
{
|
||||
auto& mipData = textureData->Items[arrayIndex].Mips[mipIndex];
|
||||
byte* dst = chunk->Data.Get() + maxDataSize * arrayIndex;
|
||||
Platform::MemoryCopy(dst, mipData.Data.Get(), mipData.Data.Length());
|
||||
Platform::MemoryClear(dst + mipData.Data.Length(), maxDataSize - mipData.Data.Length());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TextureTool::WriteTextureData(chunk->Data, *textureData, mipIndex))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clone any custom asset chunks (eg. sprite atlas data, mips are in 0-13 chunks)
|
||||
|
||||
@@ -46,7 +46,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
var texture = window.Asset;
|
||||
|
||||
var group = layout.Group("General");
|
||||
group.Label("Format: " + texture.Format);
|
||||
var textureFormat = texture.Format;
|
||||
var gpuFormat = texture.Texture?.Format ?? textureFormat;
|
||||
group.Label(textureFormat != gpuFormat && gpuFormat != PixelFormat.Unknown ? $"Format: {textureFormat} ({gpuFormat})" : $"Format: {textureFormat}");
|
||||
group.Label(string.Format("Size: {0}x{1}", texture.Width, texture.Height)).AddCopyContextMenu();
|
||||
group.Label("Mip levels: " + texture.MipLevels);
|
||||
group.Label("Memory usage: " + Utilities.Utils.FormatBytesCount(texture.TotalMemoryUsage));
|
||||
@@ -117,7 +119,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
private readonly CustomEditorPresenter _propertiesEditor;
|
||||
|
||||
private readonly PropertiesProxy _properties;
|
||||
private bool _isWaitingForLoad;
|
||||
private bool _isWaitingForLoad, _isWaitingForTexture;
|
||||
|
||||
/// <inheritdoc />
|
||||
public CubeTextureWindow(Editor editor, AssetItem item)
|
||||
@@ -193,8 +195,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Check if need to load
|
||||
if (_isWaitingForLoad && _asset.IsLoaded)
|
||||
{
|
||||
// Clear flag
|
||||
_isWaitingForLoad = false;
|
||||
_isWaitingForTexture = true;
|
||||
|
||||
// Init properties and parameters proxy
|
||||
_properties.OnLoad(this);
|
||||
@@ -203,6 +205,12 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Setup
|
||||
ClearEditedFlag();
|
||||
}
|
||||
if (_isWaitingForTexture && _asset.Texture.IsAllocated)
|
||||
{
|
||||
// Refresh properties to display GPU texture info
|
||||
_isWaitingForTexture = false;
|
||||
_propertiesEditor.BuildLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -221,7 +221,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
private readonly SplitPanel _split;
|
||||
private readonly TexturePreview _preview;
|
||||
private readonly ToolStripButton _saveButton;
|
||||
private bool _isWaitingForLoad;
|
||||
private bool _isWaitingForLoad, _isWaitingForTexture;
|
||||
|
||||
/// <inheritdoc />
|
||||
public TextureWindow(Editor editor, AssetItem item)
|
||||
@@ -328,8 +328,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Check if need to load
|
||||
if (_isWaitingForLoad && _asset.IsLoaded)
|
||||
{
|
||||
// Clear flag
|
||||
_isWaitingForLoad = false;
|
||||
_isWaitingForTexture = true;
|
||||
|
||||
// Init properties and parameters proxy
|
||||
foreach (var child in _tabs.Children)
|
||||
@@ -344,6 +344,16 @@ namespace FlaxEditor.Windows.Assets
|
||||
// Setup
|
||||
ClearEditedFlag();
|
||||
}
|
||||
if (_isWaitingForTexture && _asset.Texture.IsAllocated)
|
||||
{
|
||||
// Refresh properties to display GPU texture info
|
||||
_isWaitingForTexture = false;
|
||||
foreach (var child in _tabs.Children)
|
||||
{
|
||||
if (child is Tab tab && tab.Proxy != null)
|
||||
tab.Presenter.BuildLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -184,48 +184,12 @@ CreateAssetResult ImportTexture::Create(CreateAssetContext& context, const Textu
|
||||
}
|
||||
|
||||
// Save mip maps
|
||||
if (!isCubeMap)
|
||||
for (int32 mipIndex = 0; mipIndex < textureHeader.MipLevels; mipIndex++)
|
||||
{
|
||||
for (int32 mipIndex = 0; mipIndex < textureHeader.MipLevels; mipIndex++)
|
||||
{
|
||||
auto mipData = textureData.GetData(0, mipIndex);
|
||||
|
||||
if (context.AllocateChunk(mipIndex))
|
||||
return CreateAssetResult::CannotAllocateChunk;
|
||||
context.Data.Header.Chunks[mipIndex]->Data.Copy(mipData->Data.Get(), static_cast<uint32>(mipData->DepthPitch));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocate memory for a temporary buffer
|
||||
const uint32 imageSize = textureData.GetData(0, 0)->DepthPitch * 6;
|
||||
MemoryWriteStream imageData(imageSize);
|
||||
|
||||
// Copy cube sides for every mip into separate chunks
|
||||
for (int32 mipLevelIndex = 0; mipLevelIndex < textureHeader.MipLevels; mipLevelIndex++)
|
||||
{
|
||||
// Write array slices to the stream
|
||||
imageData.SetPosition(0);
|
||||
for (int32 cubeFaceIndex = 0; cubeFaceIndex < 6; cubeFaceIndex++)
|
||||
{
|
||||
// Get image
|
||||
const auto image = textureData.GetData(cubeFaceIndex, mipLevelIndex);
|
||||
if (image == nullptr)
|
||||
{
|
||||
LOG(Warning, "Cannot create cube texture '{0}'. Missing image slice.", context.InputPath);
|
||||
return CreateAssetResult::Error;
|
||||
}
|
||||
ASSERT(image->DepthPitch < MAX_int32);
|
||||
|
||||
// Copy data
|
||||
imageData.WriteBytes(image->Data.Get(), image->Data.Length());
|
||||
}
|
||||
|
||||
// Copy mip
|
||||
if (context.AllocateChunk(mipLevelIndex))
|
||||
return CreateAssetResult::CannotAllocateChunk;
|
||||
context.Data.Header.Chunks[mipLevelIndex]->Data.Copy(imageData.GetHandle(), imageData.GetPosition());
|
||||
}
|
||||
if (context.AllocateChunk(mipIndex))
|
||||
return CreateAssetResult::CannotAllocateChunk;
|
||||
if (TextureTool::WriteTextureData(context.Data.Header.Chunks[mipIndex]->Data, textureData, mipIndex))
|
||||
return CreateAssetResult::Error;
|
||||
}
|
||||
|
||||
#if IMPORT_TEXTURE_CACHE_OPTIONS
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
#include "Engine/Graphics/PixelFormatSampler.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Graphics/Textures/TextureData.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
@@ -526,6 +527,66 @@ PixelFormat TextureTool::ToPixelFormat(TextureFormatType format, int32 width, in
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
bool TextureTool::WriteTextureData(BytesContainer& result, const TextureData& textureData, int32 mipIndex)
|
||||
{
|
||||
if (textureData.Format == PixelFormat::Basis)
|
||||
{
|
||||
// Store as-is, each slice is stored in a separate block with the same size
|
||||
int32 maxDataSize = 0;
|
||||
for (int32 arrayIndex = 0; arrayIndex < textureData.Items.Count(); arrayIndex++)
|
||||
{
|
||||
auto& mipData = textureData.Items[arrayIndex].Mips[mipIndex];
|
||||
maxDataSize = Math::Max(maxDataSize, mipData.Data.Length());
|
||||
}
|
||||
result.Allocate(maxDataSize * textureData.GetArraySize());
|
||||
for (int32 arrayIndex = 0; arrayIndex < textureData.Items.Count(); arrayIndex++)
|
||||
{
|
||||
auto& mipData = textureData.Items[arrayIndex].Mips[mipIndex];
|
||||
byte* dst = result.Get() + maxDataSize * arrayIndex;
|
||||
Platform::MemoryCopy(dst, mipData.Data.Get(), mipData.Data.Length());
|
||||
Platform::MemoryClear(dst + mipData.Data.Length(), maxDataSize - mipData.Data.Length());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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);
|
||||
result.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];
|
||||
const byte* src = mipData.Data.Get();
|
||||
byte* dst = result.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 uint32 copyRowSize = Math::Min(mipData.RowPitch, rowPitch);
|
||||
for (uint32 line = 0; line < mipData.Lines; line++)
|
||||
{
|
||||
Platform::MemoryCopy(dst, src, copyRowSize);
|
||||
src += mipData.RowPitch;
|
||||
dst += rowPitch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool TextureTool::GetImageType(const StringView& path, ImageType& type)
|
||||
{
|
||||
const auto extension = FileSystem::GetExtension(path).ToLower();
|
||||
|
||||
@@ -217,6 +217,9 @@ public:
|
||||
|
||||
public:
|
||||
static PixelFormat ToPixelFormat(TextureFormatType format, int32 width, int32 height, bool canCompress = true);
|
||||
#if USE_EDITOR
|
||||
static bool WriteTextureData(BytesContainer& result, const TextureData& textureData, int32 mipIndex);
|
||||
#endif
|
||||
|
||||
private:
|
||||
enum class ImageType
|
||||
|
||||
Reference in New Issue
Block a user