/// <summary> /// Gets the aligned sizes for the given dimensions, using the specified texture information. /// The alignment depends on the texture layout and format bytes per pixel. /// </summary> /// <param name="info">Texture information to calculate the aligned size from</param> /// <param name="width">The width to be aligned</param> /// <param name="height">The height to be aligned</param> /// <param name="depth">The depth to be aligned</param> /// <returns>The aligned texture size</returns> private static Size GetAlignedSize(TextureInfo info, int width, int height, int depth) { if (info.IsLinear) { return(SizeCalculator.GetLinearAlignedSize( width, height, info.FormatInfo.BlockWidth, info.FormatInfo.BlockHeight, info.FormatInfo.BytesPerPixel)); } else { return(SizeCalculator.GetBlockLinearAlignedSize( width, height, depth, info.FormatInfo.BlockWidth, info.FormatInfo.BlockHeight, info.FormatInfo.BytesPerPixel, info.GobBlocksInY, info.GobBlocksInZ, info.GobBlocksInTileX)); } }
public static PatternFabricOptionsReportViewModel Build(IEnumerable <V2.PatternModel> sourceItems) { var model = new PatternFabricOptionsReportViewModel { Lines = new List <FabricPatterns>() }; var items = sourceItems .Where(x => x.FabricOptions != null && x.FabricOptions.Any()) .ToArray(); var options = items .SelectMany(x => x.FabricOptions) .Distinct(_comparer) .OrderBy(x => x.Name) .ToArray(); foreach (var option in options) { var patterns = items.Where(x => x.FabricOptions.Contains(option, _comparer)); model.Lines.Add(new FabricPatterns { Name = $"{option.Name}, {option.Color} ({option.ColorName})", Patterns = patterns.Select(x => $"{x.Title}, {SizeCalculator.SizeInSm(x.Size.Width, x.Size.Height, option.Name)}") }); } return(model); }
/// <summary> /// Calculates the size information from the texture information. /// </summary> /// <param name="layerSize">Optional size of each texture layer in bytes</param> /// <returns>Texture size information</returns> public SizeInfo CalculateSizeInfo(int layerSize = 0) { if (Target == Target.TextureBuffer) { return(new SizeInfo(Width * FormatInfo.BytesPerPixel)); } else if (IsLinear) { return(SizeCalculator.GetLinearTextureSize( Stride, Height, FormatInfo.BlockHeight)); } else { return(SizeCalculator.GetBlockLinearTextureSize( Width, Height, GetDepth(), Levels, GetLayers(), FormatInfo.BlockWidth, FormatInfo.BlockHeight, FormatInfo.BytesPerPixel, GobBlocksInY, GobBlocksInZ, GobBlocksInTileX, layerSize)); } }
/// <summary> /// Gets the aligned sizes of the specified texture information. /// The alignment depends on the texture layout and format bytes per pixel. /// </summary> /// <param name="info">Texture information to calculate the aligned size from</param> /// <param name="level">Mipmap level for texture views</param> /// <returns>The aligned texture size</returns> public static Size GetAlignedSize(TextureInfo info, int level = 0) { int width = Math.Max(1, info.Width >> level); int height = Math.Max(1, info.Height >> level); if (info.IsLinear) { return(SizeCalculator.GetLinearAlignedSize( width, height, info.FormatInfo.BlockWidth, info.FormatInfo.BlockHeight, info.FormatInfo.BytesPerPixel)); } else { int depth = Math.Max(1, info.GetDepth() >> level); return(SizeCalculator.GetBlockLinearAlignedSize( width, height, depth, info.FormatInfo.BlockWidth, info.FormatInfo.BlockHeight, info.FormatInfo.BytesPerPixel, info.GobBlocksInY, info.GobBlocksInZ, info.GobBlocksInTileX)); } }
/// <summary> /// Check if it's possible to create a view with the layout of the second texture information from the first. /// The layout information is composed of the Stride for linear textures, or GOB block size /// for block linear textures. /// </summary> /// <param name="lhs">Texture information of the texture view</param> /// <param name="rhs">Texture information of the texture view to compare against</param> /// <param name="level">Start level of the texture view, in relation with the first texture</param> /// <returns>True if the layout is compatible, false otherwise</returns> public static bool ViewLayoutCompatible(TextureInfo lhs, TextureInfo rhs, int level) { if (lhs.IsLinear != rhs.IsLinear) { return(false); } // For linear textures, gob block sizes are ignored. // For block linear textures, the stride is ignored. if (rhs.IsLinear) { int width = Math.Max(1, lhs.Width >> level); int stride = width * lhs.FormatInfo.BytesPerPixel; stride = BitUtils.AlignUp(stride, 32); return(stride == rhs.Stride); } else { int height = Math.Max(1, lhs.Height >> level); int depth = Math.Max(1, lhs.GetDepth() >> level); (int gobBlocksInY, int gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes( height, depth, lhs.FormatInfo.BlockHeight, lhs.GobBlocksInY, lhs.GobBlocksInZ); return(gobBlocksInY == rhs.GobBlocksInY && gobBlocksInZ == rhs.GobBlocksInZ); } }
public void TestCalculationOfAbsoluteFromPercentageWorks(int percent, int currentWidth, int currentHeight, int expectedWidth, int expectedHeight) { var newSize = SizeCalculator.CalcAbsoluteFromPercentage(percent, new System.Drawing.Size(currentWidth, currentHeight)); Assert.Equal(expectedWidth, newSize.Width); Assert.Equal(expectedHeight, newSize.Height); }
static void Main(string[] args) { var routeDirectory = args.Length == 1 ? args[0] : Directory.GetCurrentDirectory(); var calculator = new SizeCalculator(routeDirectory); calculator.PrintOutput(); }
public CodeGen(ASTNodeBase astTree, GlobalSymbolTable globalSymbolTable, CodeWriter codeStream) { _astTree = astTree; _globalSymbolTable = globalSymbolTable; _writer = codeStream; _sizeCalculator = new SizeCalculator(_astTree, _globalSymbolTable); }
/// <summary> /// Enqueues a frame for presentation. /// This method is thread safe and can be called from any thread. /// When the texture is presented and not needed anymore, the release callback is called. /// It's an error to modify the texture after calling this method, before the release callback is called. /// </summary> /// <param name="address">CPU virtual address of the texture data</param> /// <param name="width">Texture width</param> /// <param name="height">Texture height</param> /// <param name="stride">Texture stride for linear texture, should be zero otherwise</param> /// <param name="isLinear">Indicates if the texture is linear, normally false</param> /// <param name="gobBlocksInY">GOB blocks in the Y direction, for block linear textures</param> /// <param name="format">Texture format</param> /// <param name="bytesPerPixel">Texture format bytes per pixel (must match the format)</param> /// <param name="crop">Texture crop region</param> /// <param name="acquireCallback">Texture acquire callback</param> /// <param name="releaseCallback">Texture release callback</param> /// <param name="userObj">User defined object passed to the release callback</param> public void EnqueueFrameThreadSafe( ulong address, int width, int height, int stride, bool isLinear, int gobBlocksInY, Format format, int bytesPerPixel, ImageCrop crop, Action <GpuContext, object> acquireCallback, Action <object> releaseCallback, object userObj) { FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel, 4); TextureInfo info = new TextureInfo( 0UL, width, height, 1, 1, 1, 1, stride, isLinear, gobBlocksInY, 1, 1, Target.Texture2D, formatInfo); int size = SizeCalculator.GetBlockLinearTextureSize( width, height, 1, 1, 1, 1, 1, bytesPerPixel, gobBlocksInY, 1, 1).TotalSize; MultiRange range = new MultiRange(address, (ulong)size); _frameQueue.Enqueue(new PresentationTexture(info, range, crop, acquireCallback, releaseCallback, userObj)); }
public void Start() { var spriteRenderer = GetSpriteRenderer(); if (spriteRenderer != null) { spriteRenderer.sprite = BaseUnitData.unitSprite; var scale = SizeCalculator.SpriteScale(this.SizeCategory); spriteRenderer.transform.localScale = new Vector3(scale, scale); AlignSpriteRenderer(); } Initialize(); }
/// <summary> /// Check if it's possible to create a view with the layout of the second texture information from the first. /// The layout information is composed of the Stride for linear textures, or GOB block size /// for block linear textures. /// </summary> /// <param name="lhs">Texture information of the texture view</param> /// <param name="rhs">Texture information of the texture view to compare against</param> /// <param name="lhsLevel">Start level of the texture view, in relation with the first texture</param> /// <param name="rhsLevel">Start level of the texture view, in relation with the second texture</param> /// <returns>True if the layout is compatible, false otherwise</returns> public static bool ViewLayoutCompatible(TextureInfo lhs, TextureInfo rhs, int lhsLevel, int rhsLevel) { if (lhs.IsLinear != rhs.IsLinear) { return(false); } // For linear textures, gob block sizes are ignored. // For block linear textures, the stride is ignored. if (rhs.IsLinear) { int lhsStride = Math.Max(1, lhs.Stride >> lhsLevel); lhsStride = BitUtils.AlignUp(lhsStride, Constants.StrideAlignment); int rhsStride = Math.Max(1, rhs.Stride >> rhsLevel); rhsStride = BitUtils.AlignUp(rhsStride, Constants.StrideAlignment); return(lhsStride == rhsStride); } else { int lhsHeight = Math.Max(1, lhs.Height >> lhsLevel); int lhsDepth = Math.Max(1, lhs.GetDepth() >> lhsLevel); (int lhsGobBlocksInY, int lhsGobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes( lhsHeight, lhsDepth, lhs.FormatInfo.BlockHeight, lhs.GobBlocksInY, lhs.GobBlocksInZ); int rhsHeight = Math.Max(1, rhs.Height >> rhsLevel); int rhsDepth = Math.Max(1, rhs.GetDepth() >> rhsLevel); (int rhsGobBlocksInY, int rhsGobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes( rhsHeight, rhsDepth, rhs.FormatInfo.BlockHeight, rhs.GobBlocksInY, rhs.GobBlocksInZ); return(lhsGobBlocksInY == rhsGobBlocksInY && lhsGobBlocksInZ == rhsGobBlocksInZ); } }
/// <summary> /// Creates a new instance of the state update tracker. /// </summary> /// <param name="entries">Update tracker callback entries</param> public StateUpdateTracker(StateUpdateCallbackEntry[] entries) { _registerToGroupMapping = new byte[BlockSize]; _callbacks = new Action[entries.Length]; var fieldToDelegate = new Dictionary <string, int>(); for (int entryIndex = 0; entryIndex < entries.Length; entryIndex++) { var entry = entries[entryIndex]; foreach (var fieldName in entry.FieldNames) { fieldToDelegate.Add(fieldName, entryIndex); } _callbacks[entryIndex] = entry.Callback; } var fields = typeof(TState).GetFields(); int offset = 0; for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++) { var field = fields[fieldIndex]; int sizeOfField = SizeCalculator.SizeOf(field.FieldType); if (fieldToDelegate.TryGetValue(field.Name, out int entryIndex)) { for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4) { _registerToGroupMapping[(offset + i) / RegisterSize] = (byte)(entryIndex + 1); } } offset += sizeOfField; } Debug.Assert(offset == Unsafe.SizeOf <TState>()); }
/// <summary> /// Processes the <paramref name="inputImage"/>. /// </summary> /// <param name="inputImage">Image to process.</param> /// <returns>Processed image.</returns> public Image Process(Image inputImage) { if (inputImage == null) { throw new ArgumentNullException(nameof(inputImage)); } var newSize = SizeCalculator.GetCalculatedSize(inputImage); if (Mode == ScaleMode.NearestNeighbor) { return(ProcessNearestNeighbor(inputImage, newSize)); } else if (Mode == ScaleMode.Bilinear) { return(ProcessBiliniar(inputImage, newSize)); } else { throw new ArgumentException("Unknown scale mode"); } }
/// <summary> /// Enqueues a frame for presentation. /// This method is thread safe and can be called from any thread. /// When the texture is presented and not needed anymore, the release callback is called. /// It's an error to modify the texture after calling this method, before the release callback is called. /// </summary> /// <param name="pid">Process ID of the process that owns the texture pointed to by <paramref name="address"/></param> /// <param name="address">CPU virtual address of the texture data</param> /// <param name="width">Texture width</param> /// <param name="height">Texture height</param> /// <param name="stride">Texture stride for linear texture, should be zero otherwise</param> /// <param name="isLinear">Indicates if the texture is linear, normally false</param> /// <param name="gobBlocksInY">GOB blocks in the Y direction, for block linear textures</param> /// <param name="format">Texture format</param> /// <param name="bytesPerPixel">Texture format bytes per pixel (must match the format)</param> /// <param name="crop">Texture crop region</param> /// <param name="acquireCallback">Texture acquire callback</param> /// <param name="releaseCallback">Texture release callback</param> /// <param name="userObj">User defined object passed to the release callback</param> /// <exception cref="ArgumentException">Thrown when <paramref name="pid"/> is invalid</exception> public void EnqueueFrameThreadSafe( long pid, ulong address, int width, int height, int stride, bool isLinear, int gobBlocksInY, Format format, int bytesPerPixel, ImageCrop crop, Action <GpuContext, object> acquireCallback, Action <object> releaseCallback, object userObj) { if (!_context.PhysicalMemoryRegistry.TryGetValue(pid, out var physicalMemory)) { throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid)); } FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel, 4); TextureInfo info = new TextureInfo( 0UL, width, height, 1, 1, 1, 1, stride, isLinear, gobBlocksInY, 1, 1, Target.Texture2D, formatInfo); int size = SizeCalculator.GetBlockLinearTextureSize( width, height, 1, 1, 1, 1, 1, bytesPerPixel, gobBlocksInY, 1, 1).TotalSize; MultiRange range = new MultiRange(address, (ulong)size); _frameQueue.Enqueue(new PresentationTexture( physicalMemory.TextureCache, info, range, crop, acquireCallback, releaseCallback, userObj)); }
public void TestCalculationOfPercentageFromAbsoluteWorks(int currentSize, int initSize, int expectedPerc) { var perc = SizeCalculator.CalcPercentageFromAbsolute(initSize, currentSize); Assert.Equal(perc, expectedPerc); }
public void CreateDamageModifiers(AbilityExecuteParameters parameters, AbilityResultContainer abilityResultContainer) { var user = parameters.UnitExecuting; if (!(parameters.Target is IGameUnit)) { return; } var target = parameters.Target as IGameUnit; var sizeDifference = SizeCalculator.GetSizeDifference(user.SizeCategory, target.SizeCategory); var absSizeDifference = Math.Abs(sizeDifference); // Static size difference modifiers if (sizeDifference > 0) { var multiplier = Mathf.Pow(1.2f, absSizeDifference); abilityResultContainer.DamageModifiers.Add(new DamageModifierContainer { DamageMultiplier = multiplier, Name = "Bigger Size Bonus" }); } else if (sizeDifference < 0) { var multiplier = Mathf.Pow(0.8f, absSizeDifference); abilityResultContainer.DamageModifiers.Add(new DamageModifierContainer { DamageMultiplier = multiplier, Name = "Smaller Size Bonus" }); } // Ability-based Size Difference if (_damageParameters.SizeBiggerDamageMultiplier > 1 && sizeDifference > 0) { var multiplier = Mathf.Pow(_damageParameters.SizeBiggerDamageMultiplier, absSizeDifference); abilityResultContainer.DamageModifiers.Add(new DamageModifierContainer { DamageMultiplier = multiplier, Name = "Ability Bigger Size Bonus" }); } else if (_damageParameters.SizeBiggerDamageMultiplier < 1 && sizeDifference > 0) { var multiplier = Mathf.Pow(_damageParameters.SizeBiggerDamageMultiplier, absSizeDifference); abilityResultContainer.DamageModifiers.Add(new DamageModifierContainer { DamageMultiplier = multiplier, Name = "Ability Bigger Size Penalty" }); } if (_damageParameters.SizeSmallerDamageMultiplier > 1 && sizeDifference < 0) { var multiplier = Mathf.Pow(_damageParameters.SizeSmallerDamageMultiplier, absSizeDifference); abilityResultContainer.DamageModifiers.Add(new DamageModifierContainer { DamageMultiplier = multiplier, Name = "Ability Smaller Size Bonus" }); } else if (_damageParameters.SizeSmallerDamageMultiplier < 1 && sizeDifference < 0) { var multiplier = Mathf.Pow(_damageParameters.SizeSmallerDamageMultiplier, absSizeDifference); abilityResultContainer.DamageModifiers.Add(new DamageModifierContainer { DamageMultiplier = multiplier, Name = "Ability Smaller Size Penalty" }); } }
/// <summary> /// Tries to find an existing texture, or create a new one if not found. /// </summary> /// <param name="info">Texture information of the texture to be found or created</param> /// <param name="flags">The texture search flags, defines texture comparison rules</param> /// <returns>The texture</returns> public Texture FindOrCreateTexture(TextureInfo info, TextureSearchFlags flags = TextureSearchFlags.None) { bool isSamplerTexture = (flags & TextureSearchFlags.Sampler) != 0; // Try to find a perfect texture match, with the same address and parameters. int sameAddressOverlapsCount = _textures.FindOverlaps(info.Address, ref _textureOverlaps); for (int index = 0; index < sameAddressOverlapsCount; index++) { Texture overlap = _textureOverlaps[index]; if (overlap.IsPerfectMatch(info, flags)) { if (!isSamplerTexture) { // If not a sampler texture, it is managed by the auto delete // cache, ensure that it is on the "top" of the list to avoid // deletion. _cache.Lift(overlap); } else if (!overlap.SizeMatches(info)) { // If this is used for sampling, the size must match, // otherwise the shader would sample garbage data. // To fix that, we create a new texture with the correct // size, and copy the data from the old one to the new one. overlap.ChangeSize(info.Width, info.Height, info.DepthOrLayers); } return(overlap); } } // Calculate texture sizes, used to find all overlapping textures. SizeInfo sizeInfo; if (info.IsLinear) { sizeInfo = SizeCalculator.GetLinearTextureSize( info.Stride, info.Height, info.FormatInfo.BlockHeight); } else { sizeInfo = SizeCalculator.GetBlockLinearTextureSize( info.Width, info.Height, info.GetDepth(), info.Levels, info.GetLayers(), info.FormatInfo.BlockWidth, info.FormatInfo.BlockHeight, info.FormatInfo.BytesPerPixel, info.GobBlocksInY, info.GobBlocksInZ, info.GobBlocksInTileX); } // Find view compatible matches. ulong size = (ulong)sizeInfo.TotalSize; int overlapsCount = _textures.FindOverlaps(info.Address, size, ref _textureOverlaps); Texture texture = null; for (int index = 0; index < overlapsCount; index++) { Texture overlap = _textureOverlaps[index]; if (overlap.IsViewCompatible(info, size, out int firstLayer, out int firstLevel)) { if (!isSamplerTexture) { info = AdjustSizes(overlap, info, firstLevel); } texture = overlap.CreateView(info, sizeInfo, firstLayer, firstLevel); if (IsTextureModified(overlap)) { CacheTextureModified(texture); } // The size only matters (and is only really reliable) when the // texture is used on a sampler, because otherwise the size will be // aligned. if (!overlap.SizeMatches(info, firstLevel) && isSamplerTexture) { texture.ChangeSize(info.Width, info.Height, info.DepthOrLayers); } break; } } // No match, create a new texture. if (texture == null) { texture = new Texture(_context, info, sizeInfo); // We need to synchronize before copying the old view data to the texture, // otherwise the copied data would be overwritten by a future synchronization. texture.SynchronizeMemory(); for (int index = 0; index < overlapsCount; index++) { Texture overlap = _textureOverlaps[index]; if (texture.IsViewCompatible(overlap.Info, overlap.Size, out int firstLayer, out int firstLevel)) { TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, firstLevel); TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities); ITexture newView = texture.HostTexture.CreateView(createInfo, firstLayer, firstLevel); overlap.HostTexture.CopyTo(newView, 0, 0); // Inherit modification from overlapping texture, do that before replacing // the view since the replacement operation removes it from the list. if (IsTextureModified(overlap)) { CacheTextureModified(texture); } overlap.ReplaceView(texture, overlapInfo, newView); } } // If the texture is a 3D texture, we need to additionally copy any slice // of the 3D texture to the newly created 3D texture. if (info.Target == Target.Texture3D) { for (int index = 0; index < overlapsCount; index++) { Texture overlap = _textureOverlaps[index]; if (texture.IsViewCompatible( overlap.Info, overlap.Size, isCopy: true, out int firstLayer, out int firstLevel)) { overlap.HostTexture.CopyTo(texture.HostTexture, firstLayer, firstLevel); if (IsTextureModified(overlap)) { CacheTextureModified(texture); } } } } } // Sampler textures are managed by the texture pool, all other textures // are managed by the auto delete cache. if (!isSamplerTexture) { _cache.Add(texture); texture.Modified += CacheTextureModified; texture.Disposed += CacheTextureDisposed; } _textures.Add(texture); ShrinkOverlapsBufferIfNeeded(); return(texture); }
public static int GetBlockLinearSize(int width, int height, int bytesPerPixel) { return(SizeCalculator.GetBlockLinearTextureSize(width, height, 1, 1, 1, 1, 1, bytesPerPixel, 2, 1, 1).TotalSize); }
/// <summary> /// Tries to find an existing texture, or create a new one if not found. /// </summary> /// <param name="info">Texture information of the texture to be found or created</param> /// <param name="flags">The texture search flags, defines texture comparison rules</param> /// <returns>The texture</returns> public Texture FindOrCreateTexture(TextureInfo info, TextureSearchFlags flags = TextureSearchFlags.None) { bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0; bool isScalable = IsUpscaleCompatible(info); TextureScaleMode scaleMode = TextureScaleMode.Blacklisted; if (isScalable) { scaleMode = (flags & TextureSearchFlags.WithUpscale) != 0 ? TextureScaleMode.Scaled : TextureScaleMode.Eligible; } int sameAddressOverlapsCount; lock (_textures) { // Try to find a perfect texture match, with the same address and parameters. sameAddressOverlapsCount = _textures.FindOverlaps(info.Address, ref _textureOverlaps); } for (int index = 0; index < sameAddressOverlapsCount; index++) { Texture overlap = _textureOverlaps[index]; if (overlap.IsPerfectMatch(info, flags)) { if (!isSamplerTexture) { // If not a sampler texture, it is managed by the auto delete // cache, ensure that it is on the "top" of the list to avoid // deletion. _cache.Lift(overlap); } else if (!TextureCompatibility.SizeMatches(overlap.Info, info)) { // If this is used for sampling, the size must match, // otherwise the shader would sample garbage data. // To fix that, we create a new texture with the correct // size, and copy the data from the old one to the new one. overlap.ChangeSize(info.Width, info.Height, info.DepthOrLayers); } overlap.SynchronizeMemory(); return(overlap); } } // Calculate texture sizes, used to find all overlapping textures. SizeInfo sizeInfo; if (info.Target == Target.TextureBuffer) { sizeInfo = new SizeInfo(info.Width * info.FormatInfo.BytesPerPixel); } else if (info.IsLinear) { sizeInfo = SizeCalculator.GetLinearTextureSize( info.Stride, info.Height, info.FormatInfo.BlockHeight); } else { sizeInfo = SizeCalculator.GetBlockLinearTextureSize( info.Width, info.Height, info.GetDepth(), info.Levels, info.GetLayers(), info.FormatInfo.BlockWidth, info.FormatInfo.BlockHeight, info.FormatInfo.BytesPerPixel, info.GobBlocksInY, info.GobBlocksInZ, info.GobBlocksInTileX); } // Find view compatible matches. ulong size = (ulong)sizeInfo.TotalSize; int overlapsCount; lock (_textures) { overlapsCount = _textures.FindOverlaps(info.Address, size, ref _textureOverlaps); } Texture texture = null; for (int index = 0; index < overlapsCount; index++) { Texture overlap = _textureOverlaps[index]; if (overlap.IsViewCompatible(info, size, out int firstLayer, out int firstLevel) == TextureViewCompatibility.Full) { if (!isSamplerTexture) { info = AdjustSizes(overlap, info, firstLevel); } texture = overlap.CreateView(info, sizeInfo, firstLayer, firstLevel); if (IsTextureModified(overlap)) { texture.SignalModified(); } // The size only matters (and is only really reliable) when the // texture is used on a sampler, because otherwise the size will be // aligned. if (!TextureCompatibility.SizeMatches(overlap.Info, info, firstLevel) && isSamplerTexture) { texture.ChangeSize(info.Width, info.Height, info.DepthOrLayers); } break; } } // No match, create a new texture. if (texture == null) { texture = new Texture(_context, info, sizeInfo, scaleMode); // Step 1: Find textures that are view compatible with the new texture. // Any textures that are incompatible will contain garbage data, so they should be removed where possible. int viewCompatible = 0; bool setData = isSamplerTexture || overlapsCount == 0; for (int index = 0; index < overlapsCount; index++) { Texture overlap = _textureOverlaps[index]; bool overlapInCache = overlap.CacheNode != null; TextureViewCompatibility compatibility = texture.IsViewCompatible(overlap.Info, overlap.Size, out int firstLayer, out int firstLevel); if (compatibility != TextureViewCompatibility.Incompatible) { if (_overlapInfo.Length != _textureOverlaps.Length) { Array.Resize(ref _overlapInfo, _textureOverlaps.Length); } _overlapInfo[viewCompatible] = new OverlapInfo(compatibility, firstLayer, firstLevel); _textureOverlaps[viewCompatible++] = overlap; } else if (overlapInCache || !setData) { if (info.GobBlocksInZ > 1 && info.GobBlocksInZ == overlap.Info.GobBlocksInZ) { // Allow overlapping slices of 3D textures. Could be improved in future by making sure the textures don't overlap. continue; } // The overlap texture is going to contain garbage data after we draw, or is generally incompatible. // If the texture cannot be entirely contained in the new address space, and one of its view children is compatible with us, // it must be flushed before removal, so that the data is not lost. // If the texture was modified since its last use, then that data is probably meant to go into this texture. // If the data has been modified by the CPU, then it also shouldn't be flushed. bool modified = overlap.ConsumeModified(); bool flush = overlapInCache && !modified && (overlap.Address <texture.Address || overlap.EndAddress> texture.EndAddress) && overlap.HasViewCompatibleChild(texture); setData |= modified || flush; if (overlapInCache) { _cache.Remove(overlap, flush); } } } // We need to synchronize before copying the old view data to the texture, // otherwise the copied data would be overwritten by a future synchronization. texture.InitializeData(false, setData); for (int index = 0; index < viewCompatible; index++) { Texture overlap = _textureOverlaps[index]; OverlapInfo oInfo = _overlapInfo[index]; if (oInfo.Compatibility != TextureViewCompatibility.Full) { continue; // Copy only compatibilty. } TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, oInfo.FirstLevel); TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities); if (texture.ScaleFactor != overlap.ScaleFactor) { // A bit tricky, our new texture may need to contain an existing texture that is upscaled, but isn't itself. // In that case, we prefer the higher scale only if our format is render-target-like, otherwise we scale the view down before copy. texture.PropagateScale(overlap); } ITexture newView = texture.HostTexture.CreateView(createInfo, oInfo.FirstLayer, oInfo.FirstLevel); overlap.HostTexture.CopyTo(newView, 0, 0); // Inherit modification from overlapping texture, do that before replacing // the view since the replacement operation removes it from the list. if (IsTextureModified(overlap)) { texture.SignalModified(); } overlap.ReplaceView(texture, overlapInfo, newView, oInfo.FirstLayer, oInfo.FirstLevel); } // If the texture is a 3D texture, we need to additionally copy any slice // of the 3D texture to the newly created 3D texture. if (info.Target == Target.Texture3D) { for (int index = 0; index < viewCompatible; index++) { Texture overlap = _textureOverlaps[index]; OverlapInfo oInfo = _overlapInfo[index]; if (oInfo.Compatibility != TextureViewCompatibility.Incompatible) { overlap.BlacklistScale(); overlap.HostTexture.CopyTo(texture.HostTexture, oInfo.FirstLayer, oInfo.FirstLevel); if (IsTextureModified(overlap)) { texture.SignalModified(); } } } } } // Sampler textures are managed by the texture pool, all other textures // are managed by the auto delete cache. if (!isSamplerTexture) { _cache.Add(texture); texture.Modified += CacheTextureModified; texture.Disposed += CacheTextureDisposed; } lock (_textures) { _textures.Add(texture); } ShrinkOverlapsBufferIfNeeded(); return(texture); }
/// <summary> /// Gets texture information from a texture descriptor. /// </summary> /// <param name="descriptor">The texture descriptor</param> /// <param name="layerSize">Layer size for textures using a sub-range of mipmap levels, otherwise 0</param> /// <returns>The texture information</returns> private TextureInfo GetInfo(TextureDescriptor descriptor, out int layerSize) { int width = descriptor.UnpackWidth(); int height = descriptor.UnpackHeight(); int depthOrLayers = descriptor.UnpackDepth(); int levels = descriptor.UnpackLevels(); TextureMsaaMode msaaMode = descriptor.UnpackTextureMsaaMode(); int samplesInX = msaaMode.SamplesInX(); int samplesInY = msaaMode.SamplesInY(); int stride = descriptor.UnpackStride(); TextureDescriptorType descriptorType = descriptor.UnpackTextureDescriptorType(); bool isLinear = descriptorType == TextureDescriptorType.Linear; Target target = descriptor.UnpackTextureTarget().Convert((samplesInX | samplesInY) != 1); // We use 2D targets for 1D textures as that makes texture cache // management easier. We don't know the target for render target // and copies, so those would normally use 2D targets, which are // not compatible with 1D targets. By doing that we also allow those // to match when looking for compatible textures on the cache. if (target == Target.Texture1D) { target = Target.Texture2D; height = 1; } else if (target == Target.Texture1DArray) { target = Target.Texture2DArray; height = 1; } uint format = descriptor.UnpackFormat(); bool srgb = descriptor.UnpackSrgb(); ulong gpuVa = descriptor.UnpackAddress(); if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo)) { if (Context.MemoryManager.IsMapped(gpuVa) && (int)format > 0) { Logger.Error?.Print(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb})."); } formatInfo = FormatInfo.Default; } int gobBlocksInY = descriptor.UnpackGobBlocksInY(); int gobBlocksInZ = descriptor.UnpackGobBlocksInZ(); int gobBlocksInTileX = descriptor.UnpackGobBlocksInTileX(); layerSize = 0; int minLod = descriptor.UnpackBaseLevel(); int maxLod = descriptor.UnpackMaxLevelInclusive(); // Linear textures don't support mipmaps, so we don't handle this case here. if ((minLod != 0 || maxLod + 1 != levels) && target != Target.TextureBuffer && !isLinear) { int depth = TextureInfo.GetDepth(target, depthOrLayers); int layers = TextureInfo.GetLayers(target, depthOrLayers); SizeInfo sizeInfo = SizeCalculator.GetBlockLinearTextureSize( width, height, depth, levels, layers, formatInfo.BlockWidth, formatInfo.BlockHeight, formatInfo.BytesPerPixel, gobBlocksInY, gobBlocksInZ, gobBlocksInTileX); layerSize = sizeInfo.LayerSize; if (minLod != 0 && minLod < levels) { // If the base level is not zero, we additionally add the mip level offset // to the address, this allows the texture manager to find the base level from the // address if there is a overlapping texture on the cache that can contain the new texture. gpuVa += (ulong)sizeInfo.GetMipOffset(minLod); width = Math.Max(1, width >> minLod); height = Math.Max(1, height >> minLod); if (target == Target.Texture3D) { depthOrLayers = Math.Max(1, depthOrLayers >> minLod); } (gobBlocksInY, gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(height, depth, formatInfo.BlockHeight, gobBlocksInY, gobBlocksInZ); } levels = (maxLod - minLod) + 1; } SwizzleComponent swizzleR = descriptor.UnpackSwizzleR().Convert(); SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert(); SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert(); SwizzleComponent swizzleA = descriptor.UnpackSwizzleA().Convert(); DepthStencilMode depthStencilMode = GetDepthStencilMode( formatInfo.Format, swizzleR, swizzleG, swizzleB, swizzleA); if (formatInfo.Format.IsDepthOrStencil()) { swizzleR = SwizzleComponent.Red; swizzleG = SwizzleComponent.Red; swizzleB = SwizzleComponent.Red; if (depthStencilMode == DepthStencilMode.Depth) { swizzleA = SwizzleComponent.One; } else { swizzleA = SwizzleComponent.Red; } } return(new TextureInfo( gpuVa, width, height, depthOrLayers, levels, samplesInX, samplesInY, stride, isLinear, gobBlocksInY, gobBlocksInZ, gobBlocksInTileX, target, formatInfo, depthStencilMode, swizzleR, swizzleG, swizzleB, swizzleA)); }