// Copyright (c) 2012-2024 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; } } }