/// <summary> /// Tries to find an existing texture, or create a new one if not found. /// </summary> /// <param name="colorState">Color buffer texture to find or create</param> /// <param name="samplesInX">Number of samples in the X direction, for MSAA</param> /// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param> /// <returns>The texture</returns> public Texture FindOrCreateTexture(RtColorState colorState, int samplesInX, int samplesInY) { ulong address = _context.MemoryManager.Translate(colorState.Address.Pack()); if (address == MemoryManager.BadAddress) { return(null); } bool isLinear = colorState.MemoryLayout.UnpackIsLinear(); int gobBlocksInY = colorState.MemoryLayout.UnpackGobBlocksInY(); int gobBlocksInZ = colorState.MemoryLayout.UnpackGobBlocksInZ(); Target target; if (colorState.MemoryLayout.UnpackIsTarget3D()) { target = Target.Texture3D; } else if ((samplesInX | samplesInY) != 1) { target = colorState.Depth > 1 ? Target.Texture2DMultisampleArray : Target.Texture2DMultisample; } else { target = colorState.Depth > 1 ? Target.Texture2DArray : Target.Texture2D; } FormatInfo formatInfo = colorState.Format.Convert(); int width, stride; // For linear textures, the width value is actually the stride. // We can easily get the width by dividing the stride by the bpp, // since the stride is the total number of bytes occupied by a // line. The stride should also meet alignment constraints however, // so the width we get here is the aligned width. if (isLinear) { width = colorState.WidthOrStride / formatInfo.BytesPerPixel; stride = colorState.WidthOrStride; } else { width = colorState.WidthOrStride; stride = 0; } TextureInfo info = new TextureInfo( address, width, colorState.Height, colorState.Depth, 1, samplesInX, samplesInY, stride, isLinear, gobBlocksInY, gobBlocksInZ, 1, target, formatInfo); Texture texture = FindOrCreateTexture(info, TextureSearchFlags.WithUpscale); texture.SynchronizeMemory(); 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> /// Try getting the texture format from an encoded format integer from the Maxwell texture descriptor. /// </summary> /// <param name="encoded">The encoded format integer from the texture descriptor</param> /// <param name="isSrgb">Indicates if the format is a sRGB format</param> /// <param name="format">The output texture format</param> /// <returns>True if the format is valid, false otherwise</returns> public static bool TryGetTextureFormat(uint encoded, bool isSrgb, out FormatInfo format) { encoded |= (isSrgb ? 1u << 19 : 0u); return(_textureFormats.TryGetValue(encoded, out format)); }
/// <summary> /// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null. /// </summary> /// <param name="memoryManager">GPU memory manager where the texture is mapped</param> /// <param name="tex">The texture information</param> /// <param name="gpuVa">GPU virtual address of the texture</param> /// <param name="bpp">Bytes per pixel</param> /// <param name="stride">If <paramref name="linear"/> is true, should have the texture stride, otherwise ignored</param> /// <param name="xCount">Number of pixels to be copied per line</param> /// <param name="yCount">Number of lines to be copied</param> /// <param name="linear">True if the texture has a linear layout, false otherwise</param> /// <returns>A matching texture, or null if there is no match</returns> public Texture FindTexture( MemoryManager memoryManager, DmaTexture tex, ulong gpuVa, int bpp, int stride, int xCount, int yCount, bool linear) { ulong address = memoryManager.Translate(gpuVa); if (address == MemoryManager.PteUnmapped) { return(null); } int addressMatches = _textures.FindOverlaps(address, ref _textureOverlaps); Texture textureMatch = null; for (int i = 0; i < addressMatches; i++) { Texture texture = _textureOverlaps[i]; FormatInfo format = texture.Info.FormatInfo; if (texture.Info.DepthOrLayers > 1 || texture.Info.Levels > 1 || texture.Info.FormatInfo.IsCompressed) { // Don't support direct buffer copies to anything that isn't a single 2D image, uncompressed. continue; } bool match; if (linear) { // Size is not available for linear textures. Use the stride and end of the copy region instead. match = texture.Info.IsLinear && texture.Info.Stride == stride && tex.RegionY + yCount <= texture.Info.Height; } else { // Bpp may be a mismatch between the target texture and the param. // Due to the way linear strided and block layouts work, widths can be multiplied by Bpp for comparison. // Note: tex.Width is the aligned texture size. Prefer param.XCount, as the destination should be a texture with that exact size. bool sizeMatch = xCount * bpp == texture.Info.Width * format.BytesPerPixel && tex.Height == texture.Info.Height; bool formatMatch = !texture.Info.IsLinear && texture.Info.GobBlocksInY == tex.MemoryLayout.UnpackGobBlocksInY() && texture.Info.GobBlocksInZ == tex.MemoryLayout.UnpackGobBlocksInZ(); match = sizeMatch && formatMatch; } if (match) { if (textureMatch == null) { textureMatch = texture; } else if (texture.Group != textureMatch.Group) { return(null); // It's ambiguous which texture should match between multiple choices, so leave it up to the slow path. } } } return(textureMatch); }