/// <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); } }
/// <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> /// 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> private 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); (int gobBlocksInY, int gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes( height, depth, info.FormatInfo.BlockHeight, info.GobBlocksInY, info.GobBlocksInZ); return(SizeCalculator.GetBlockLinearAlignedSize( width, height, depth, info.FormatInfo.BlockWidth, info.FormatInfo.BlockHeight, info.FormatInfo.BytesPerPixel, gobBlocksInY, gobBlocksInZ, info.GobBlocksInTileX)); } }
/// <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)); }