/// <summary> /// Flushes the texture data, to be called from an external thread. /// The host backend must ensure that we have shared access to the resource from this thread. /// This is used when flushing from memory access handlers. /// </summary> public void ExternalFlush(ulong address, ulong size) { if (!IsModified || _memoryTracking == null) { return; } _context.Renderer.BackgroundContextAction(() => { IsModified = false; if (TextureCompatibility.IsFormatHostIncompatible(Info, _context.Capabilities)) { return; // Flushing this format is not supported, as it may have been converted to another host format. } ITexture texture = HostTexture; if (ScaleFactor != 1f) { // If needed, create a texture to flush back to host at 1x scale. texture = _flushHostTexture = GetScaledHostTexture(1f, _flushHostTexture); } _context.MemoryManager.WriteUntracked(Info.GpuAddress, GetTextureDataFromGpu(false, texture)); }); }
/// <summary> /// Changes a texture's size to match the desired size for samplers, /// or increases a texture's size to fit the region indicated by a size hint. /// </summary> /// <param name="info">The desired texture info</param> /// <param name="texture">The texture to resize</param> /// <param name="isSamplerTexture">True if the texture will be used for a sampler, false otherwise</param> /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param> private void ChangeSizeIfNeeded(TextureInfo info, Texture texture, bool isSamplerTexture, Size?sizeHint) { if (isSamplerTexture) { // 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. if (!TextureCompatibility.SizeMatches(texture.Info, info)) { texture.ChangeSize(info.Width, info.Height, info.DepthOrLayers); } } else if (sizeHint != null) { // A size hint indicates that data will be used within that range, at least. // If the texture is smaller than the size hint, it must be enlarged to meet it. // The maximum size is provided by the requested info, which generally has an aligned size. int width = Math.Max(texture.Info.Width, Math.Min(sizeHint.Value.Width, info.Width)); int height = Math.Max(texture.Info.Height, Math.Min(sizeHint.Value.Height, info.Height)); if (texture.Info.Width != width || texture.Info.Height != height) { texture.ChangeSize(width, height, info.DepthOrLayers); } } }
/// <summary> /// Flushes the texture data, to be called from an external thread. /// The host backend must ensure that we have shared access to the resource from this thread. /// This is used when flushing from memory access handlers. /// </summary> public void ExternalFlush(ulong address, ulong size) { if (!IsModified || _memoryTracking == null) { return; } _context.Renderer.BackgroundContextAction(() => { IsModified = false; if (TextureCompatibility.IsFormatHostIncompatible(Info, _context.Capabilities)) { return; // Flushing this format is not supported, as it may have been converted to another host format. } if (Info.Target == Target.Texture2DMultisample || Info.Target == Target.Texture2DMultisampleArray) { return; // Flushing multisample textures is not supported, the host does not allow getting their data. } ITexture texture = HostTexture; if (ScaleFactor != 1f) { // If needed, create a texture to flush back to host at 1x scale. texture = _flushHostTexture = GetScaledHostTexture(1f, _flushHostTexture); } _context.PhysicalMemory.WriteUntracked(Range, GetTextureDataFromGpu(false, texture)); }); }
/// <summary> /// Flushes the texture data. /// This causes the texture data to be written back to guest memory. /// If the texture was written by the GPU, this includes all modification made by the GPU /// up to this point. /// Be aware that this is an expensive operation, avoid calling it unless strictly needed. /// This may cause data corruption if the memory is already being used for something else on the CPU side. /// </summary> /// <param name="tracked">Whether or not the flush triggers write tracking. If it doesn't, the texture will not be blacklisted for scaling either.</param> public void Flush(bool tracked = true) { IsModified = false; if (TextureCompatibility.IsFormatHostIncompatible(Info, _context.Capabilities)) { return; // Flushing this format is not supported, as it may have been converted to another host format. } if (tracked) { _context.MemoryManager.Write(Info.GpuAddress, GetTextureDataFromGpu(tracked)); } else { _context.MemoryManager.WriteUntracked(Info.GpuAddress, GetTextureDataFromGpu(tracked)); } }
/// <summary> /// This performs a strict comparison, used to check if this texture is equal to the one supplied. /// </summary> /// <param name="info">Texture information to compare against</param> /// <param name="flags">Comparison flags</param> /// <returns>A value indicating how well this texture matches the given info</returns> public TextureMatchQuality IsExactMatch(TextureInfo info, TextureSearchFlags flags) { TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, (flags & TextureSearchFlags.ForSampler) != 0, (flags & TextureSearchFlags.ForCopy) != 0); if (matchQuality == TextureMatchQuality.NoMatch) { return(matchQuality); } if (!TextureCompatibility.LayoutMatches(Info, info)) { return(TextureMatchQuality.NoMatch); } if (!TextureCompatibility.SizeMatches(Info, info, (flags & TextureSearchFlags.Strict) == 0)) { return(TextureMatchQuality.NoMatch); } if ((flags & TextureSearchFlags.ForSampler) != 0 || (flags & TextureSearchFlags.Strict) != 0) { if (!TextureCompatibility.SamplerParamsMatches(Info, info)) { return(TextureMatchQuality.NoMatch); } } if ((flags & TextureSearchFlags.ForCopy) != 0) { bool msTargetCompatible = Info.Target == Target.Texture2DMultisample && info.Target == Target.Texture2D; if (!msTargetCompatible && !TextureCompatibility.TargetAndSamplesCompatible(Info, info)) { return(TextureMatchQuality.NoMatch); } } else if (!TextureCompatibility.TargetAndSamplesCompatible(Info, info)) { return(TextureMatchQuality.NoMatch); } return(Info.Address == info.Address && Info.Levels == info.Levels ? matchQuality : TextureMatchQuality.NoMatch); }
/// <summary> /// This performs a strict comparison, used to check if this texture is equal to the one supplied. /// </summary> /// <param name="info">Texture information to compare against</param> /// <param name="flags">Comparison flags</param> /// <returns>True if the textures are strictly equal or similar, false otherwise</returns> public bool IsPerfectMatch(TextureInfo info, TextureSearchFlags flags) { if (!TextureCompatibility.FormatMatches(Info, info, (flags & TextureSearchFlags.ForSampler) != 0, (flags & TextureSearchFlags.ForCopy) != 0)) { return(false); } if (!TextureCompatibility.LayoutMatches(Info, info)) { return(false); } if (!TextureCompatibility.SizeMatches(Info, info, (flags & TextureSearchFlags.Strict) == 0)) { return(false); } if ((flags & TextureSearchFlags.ForSampler) != 0 || (flags & TextureSearchFlags.Strict) != 0) { if (!TextureCompatibility.SamplerParamsMatches(Info, info)) { return(false); } } if ((flags & TextureSearchFlags.ForCopy) != 0) { bool msTargetCompatible = Info.Target == Target.Texture2DMultisample && info.Target == Target.Texture2D; if (!msTargetCompatible && !TextureCompatibility.TargetAndSamplesCompatible(Info, info)) { return(false); } } else if (!TextureCompatibility.TargetAndSamplesCompatible(Info, info)) { return(false); } return(Info.Address == info.Address && Info.Levels == info.Levels); }
/// <summary> /// Check if it's possible to create a view, with the given parameters, from this texture. /// </summary> /// <param name="info">Texture view information</param> /// <param name="size">Texture view size</param> /// <param name="firstLayer">Texture view initial layer on this texture</param> /// <param name="firstLevel">Texture view first mipmap level on this texture</param> /// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns> public TextureViewCompatibility IsViewCompatible( TextureInfo info, ulong size, out int firstLayer, out int firstLevel) { // Out of range. if (info.Address < Address || info.Address + size > EndAddress) { firstLayer = 0; firstLevel = 0; return(TextureViewCompatibility.Incompatible); } int offset = (int)(info.Address - Address); if (!_sizeInfo.FindView(offset, (int)size, out firstLayer, out firstLevel)) { return(TextureViewCompatibility.Incompatible); } if (!TextureCompatibility.ViewLayoutCompatible(Info, info, firstLevel)) { return(TextureViewCompatibility.Incompatible); } if (!TextureCompatibility.ViewFormatCompatible(Info, info)) { return(TextureViewCompatibility.Incompatible); } TextureViewCompatibility result = TextureViewCompatibility.Full; result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, firstLevel)); result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info)); return((Info.SamplesInX == info.SamplesInX && Info.SamplesInY == info.SamplesInY) ? result : TextureViewCompatibility.Incompatible); }
/// <summary> /// Check if it's possible to create a view, with the given parameters, from this texture. /// </summary> /// <param name="info">Texture view information</param> /// <param name="range">Texture view physical memory ranges</param> /// <param name="firstLayer">Texture view initial layer on this texture</param> /// <param name="firstLevel">Texture view first mipmap level on this texture</param> /// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns> public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, out int firstLayer, out int firstLevel) { int offset = Range.FindOffset(range); // Out of range. if (offset < 0) { firstLayer = 0; firstLevel = 0; return(TextureViewCompatibility.Incompatible); } if (!_sizeInfo.FindView(offset, out firstLayer, out firstLevel)) { return(TextureViewCompatibility.Incompatible); } if (!TextureCompatibility.ViewLayoutCompatible(Info, info, firstLevel)) { return(TextureViewCompatibility.Incompatible); } if (info.GetSlices() > 1 && LayerSize != layerSize) { return(TextureViewCompatibility.Incompatible); } TextureViewCompatibility result = TextureViewCompatibility.Full; result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info)); result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, firstLevel)); result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info)); result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel)); return((Info.SamplesInX == info.SamplesInX && Info.SamplesInY == info.SamplesInY) ? result : TextureViewCompatibility.Incompatible); }
/// <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 a texture creation information from texture information. /// This can be used to create new host textures. /// </summary> /// <param name="info">Texture information</param> /// <param name="caps">GPU capabilities</param> /// <param name="scale">Texture scale factor, to be applied to the texture size</param> /// <returns>The texture creation information</returns> public static TextureCreateInfo GetCreateInfo(TextureInfo info, Capabilities caps, float scale) { FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(info, caps); if (info.Target == Target.TextureBuffer) { // We assume that the host does not support signed normalized format // (as is the case with OpenGL), so we just use a unsigned format. // The shader will need the appropriate conversion code to compensate. switch (formatInfo.Format) { case Format.R8Snorm: formatInfo = new FormatInfo(Format.R8Sint, 1, 1, 1, 1); break; case Format.R16Snorm: formatInfo = new FormatInfo(Format.R16Sint, 1, 1, 2, 1); break; case Format.R8G8Snorm: formatInfo = new FormatInfo(Format.R8G8Sint, 1, 1, 2, 2); break; case Format.R16G16Snorm: formatInfo = new FormatInfo(Format.R16G16Sint, 1, 1, 4, 2); break; case Format.R8G8B8A8Snorm: formatInfo = new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4, 4); break; case Format.R16G16B16A16Snorm: formatInfo = new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8, 4); break; } } int width = info.Width / info.SamplesInX; int height = info.Height / info.SamplesInY; int depth = info.GetDepth() * info.GetLayers(); if (scale != 1f) { width = (int)MathF.Ceiling(width * scale); height = (int)MathF.Ceiling(height * scale); } return(new TextureCreateInfo( width, height, depth, info.Levels, info.Samples, formatInfo.BlockWidth, formatInfo.BlockHeight, formatInfo.BytesPerPixel, formatInfo.Format, info.DepthStencilMode, info.Target, info.SwizzleR, info.SwizzleG, info.SwizzleB, info.SwizzleA)); }
/// <summary> /// Checks if the view format is compatible with this texture format. /// In general, the formats are considered compatible if the bytes per pixel values are equal, /// but there are more complex rules for some formats, like compressed or depth-stencil formats. /// This follows the host API copy compatibility rules. /// </summary> /// <param name="info">Texture information of the texture view</param> /// <returns>True if the formats are compatible, false otherwise</returns> private bool ViewFormatCompatible(TextureInfo info) { return(TextureCompatibility.FormatCompatible(Info.FormatInfo, info.FormatInfo)); }
/// <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; } // 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 (!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 = _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 (!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); // 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); 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, 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, firstLayer, 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 < overlapsCount; index++) { Texture overlap = _textureOverlaps[index]; if (texture.IsViewCompatible( overlap.Info, overlap.Size, isCopy: true, out int firstLayer, out int firstLevel)) { overlap.BlacklistScale(); 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); }