// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Runtime.InteropServices;
namespace FlaxEngine
{
partial class TextureBase
{
///
/// The maximum size for the texture resources (supported by engine, the target platform can be lower capabilities).
///
public const int MaxTextureSize = 8192;
///
/// The maximum amount of the mip levels for the texture resources (supported by engine, the target platform can be lower capabilities).
///
public const int MaxMipLevels = 14;
///
/// The maximum array size for the texture resources (supported by engine, the target platform can be lower capabilities).
///
public const int MaxArraySize = 512;
///
/// The texture data initialization container.
///
public struct InitData
{
///
/// The format of the pixels.
///
public PixelFormat Format;
///
/// The width (in pixels).
///
public int Width;
///
/// The height (in pixels).
///
public int Height;
///
/// The array size (slices count).
///
public int ArraySize;
///
/// If checked, the engine will generate automatic-mips based on the latest provided mip.
///
public bool GenerateMips;
///
/// If checked, the generated mips will use linear filter, otherwise it will use point filter. Linear filter is supported only for formats compatible with Color32.
///
public bool GenerateMipsLinear;
///
/// The mips levels data.
///
public MipData[] Mips;
///
/// Returns true if init data is valid.
///
public bool IsValid => Format != PixelFormat.Unknown &&
Mathf.IsInRange(Width, 1, MaxTextureSize) &&
Mathf.IsInRange(Height, 1, MaxTextureSize) &&
Mathf.IsInRange(ArraySize, 1, MaxArraySize) &&
Mips != null &&
Mathf.IsInRange(Mips.Length, 1, MaxMipLevels);
///
/// The mip data container.
///
public struct MipData
{
///
/// The texture data. Use and to define the storage format.
///
public byte[] Data;
///
/// The data container image row pitch (in bytes).
///
public int RowPitch;
///
/// The data container image slice pitch (in bytes).
///
public int SlicePitch;
}
}
///
/// Initializes the texture storage container with the given data. Valid only for virtual assets. Can be used in both Editor and at runtime in a build game.
/// It does not perform any data streaming or uploading to the GPU. Only the texture resource is being initialized and the data is copied to be streamed later.
///
///
/// Can be used only for virtual assets (see and ).
///
/// The texture init data.
public unsafe void Init(ref InitData initData)
{
// Validate input
if (!IsVirtual)
throw new InvalidOperationException("Only virtual textures can be modified at runtime.");
if (!initData.IsValid)
throw new ArgumentException("Invalid texture init data.");
for (int i = 0; i < initData.Mips.Length; i++)
{
if (initData.Mips[i].Data == null)
throw new ArgumentException($"Missing texture mip{i} init data.");
if (initData.Mips[i].Data.Length < initData.Mips[i].SlicePitch * initData.ArraySize)
throw new ArgumentException($"Invalid texture mip{i} init data. It has size {initData.Mips[i].Data.Length} bytes but should be {initData.Mips[i].SlicePitch * initData.ArraySize} bytes.");
}
// Convert data to internal storage (don't allocate memory but pin the managed arrays)
var t = new InternalInitData
{
Format = initData.Format,
Width = initData.Width,
Height = initData.Height,
ArraySize = initData.ArraySize,
MipLevels = initData.Mips.Length,
};
if (initData.GenerateMips)
{
t.GenerateMips = 1;
if (initData.GenerateMipsLinear)
t.GenerateMips |= 2;
}
var emptyArray = Utils.GetEmptyArray();
fixed (byte* data13 = (initData.Mips.Length > 13 ? initData.Mips[13].Data : emptyArray))
fixed (byte* data12 = (initData.Mips.Length > 12 ? initData.Mips[12].Data : emptyArray))
fixed (byte* data11 = (initData.Mips.Length > 11 ? initData.Mips[11].Data : emptyArray))
fixed (byte* data10 = (initData.Mips.Length > 10 ? initData.Mips[10].Data : emptyArray))
fixed (byte* data09 = (initData.Mips.Length > 09 ? initData.Mips[9].Data : emptyArray))
fixed (byte* data08 = (initData.Mips.Length > 08 ? initData.Mips[8].Data : emptyArray))
fixed (byte* data07 = (initData.Mips.Length > 07 ? initData.Mips[7].Data : emptyArray))
fixed (byte* data06 = (initData.Mips.Length > 06 ? initData.Mips[6].Data : emptyArray))
fixed (byte* data05 = (initData.Mips.Length > 05 ? initData.Mips[5].Data : emptyArray))
fixed (byte* data04 = (initData.Mips.Length > 04 ? initData.Mips[4].Data : emptyArray))
fixed (byte* data03 = (initData.Mips.Length > 03 ? initData.Mips[3].Data : emptyArray))
fixed (byte* data02 = (initData.Mips.Length > 02 ? initData.Mips[2].Data : emptyArray))
fixed (byte* data01 = (initData.Mips.Length > 01 ? initData.Mips[1].Data : emptyArray))
fixed (byte* data00 = (initData.Mips.Length > 00 ? initData.Mips[0].Data : emptyArray))
{
t.Data13 = new IntPtr(data13);
t.Data12 = new IntPtr(data12);
t.Data11 = new IntPtr(data11);
t.Data10 = new IntPtr(data10);
t.Data09 = new IntPtr(data09);
t.Data08 = new IntPtr(data08);
t.Data07 = new IntPtr(data07);
t.Data06 = new IntPtr(data06);
t.Data05 = new IntPtr(data05);
t.Data04 = new IntPtr(data04);
t.Data03 = new IntPtr(data03);
t.Data02 = new IntPtr(data02);
t.Data01 = new IntPtr(data01);
t.Data00 = new IntPtr(data00);
int* rowPitches = &t.Data00RowPitch;
for (int i = 0; i < t.MipLevels; i++)
{
rowPitches[i] = initData.Mips[i].RowPitch;
}
int* slicePitches = &t.Data00SlicePitch;
for (int i = 0; i < t.MipLevels; i++)
{
slicePitches[i] = initData.Mips[i].SlicePitch;
}
// Call backend
if (Internal_InitCSharp(__unmanagedPtr, new IntPtr(&t)))
throw new Exception("Failed to init texture data.");
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct InternalInitData
{
public PixelFormat Format;
public int Width;
public int Height;
public int ArraySize;
public int MipLevels;
public int GenerateMips;
public int Data00RowPitch;
public int Data01RowPitch;
public int Data02RowPitch;
public int Data03RowPitch;
public int Data04RowPitch;
public int Data05RowPitch;
public int Data06RowPitch;
public int Data07RowPitch;
public int Data08RowPitch;
public int Data09RowPitch;
public int Data10RowPitch;
public int Data11RowPitch;
public int Data12RowPitch;
public int Data13RowPitch;
public int Data00SlicePitch;
public int Data01SlicePitch;
public int Data02SlicePitch;
public int Data03SlicePitch;
public int Data04SlicePitch;
public int Data05SlicePitch;
public int Data06SlicePitch;
public int Data07SlicePitch;
public int Data08SlicePitch;
public int Data09SlicePitch;
public int Data10SlicePitch;
public int Data11SlicePitch;
public int Data12SlicePitch;
public int Data13SlicePitch;
public IntPtr Data00;
public IntPtr Data01;
public IntPtr Data02;
public IntPtr Data03;
public IntPtr Data04;
public IntPtr Data05;
public IntPtr Data06;
public IntPtr Data07;
public IntPtr Data08;
public IntPtr Data09;
public IntPtr Data10;
public IntPtr Data11;
public IntPtr Data12;
public IntPtr Data13;
}
}
}