From 32764f95602611e9daa50362330d760e8ed83fda Mon Sep 17 00:00:00 2001 From: gdkchan <gab.dark.100@gmail.com> Date: Sun, 29 Dec 2019 20:26:37 -0300 Subject: [PATCH] Add XML documentation to Ryujinx.Graphics.Gpu.Image --- Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs | 22 +- Ryujinx.Graphics.Gpu/Image/FormatInfo.cs | 38 ++- Ryujinx.Graphics.Gpu/Image/FormatTable.cs | 16 ++ Ryujinx.Graphics.Gpu/Image/Pool.cs | 40 ++- Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs | 4 + Ryujinx.Graphics.Gpu/Image/Sampler.cs | 14 + .../Image/SamplerDescriptor.cs | 74 +++++ .../Image/SamplerMinFilter.cs | 3 + .../Image/SamplerMipFilter.cs | 3 + Ryujinx.Graphics.Gpu/Image/SamplerPool.cs | 24 ++ Ryujinx.Graphics.Gpu/Image/Texture.cs | 255 +++++++++++++++++- .../Image/TextureBindingInfo.cs | 35 ++- .../Image/TextureBindingsManager.cs | 89 +++++- .../Image/TextureCompatibility.cs | 19 ++ .../Image/TextureComponent.cs | 8 + .../Image/TextureDescriptor.cs | 105 ++++++++ .../Image/TextureDescriptorType.cs | 5 + Ryujinx.Graphics.Gpu/Image/TextureInfo.cs | 128 ++++++++- Ryujinx.Graphics.Gpu/Image/TextureManager.cs | 159 ++++++++++- Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs | 63 +++-- Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 58 +++- .../Image/TexturePoolCache.cs | 20 ++ .../Image/TextureSearchFlags.cs | 3 + Ryujinx.Graphics.Gpu/Image/TextureTarget.cs | 9 + 24 files changed, 1133 insertions(+), 61 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs index 33ed78818a..fc30d03c77 100644 --- a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs @@ -3,17 +3,31 @@ using System.Collections.Generic; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// A texture cache that automatically removes older textures that are not used for some time. + /// The cache works with a rotated list with a fixed size. When new textures are added, the + /// old ones at the bottom of the list are deleted. + /// </summary> class AutoDeleteCache : IEnumerable<Texture> { private const int MaxCapacity = 2048; - private LinkedList<Texture> _textures; + private readonly LinkedList<Texture> _textures; + /// <summary> + /// Creates a new instance of the automatic deletion cache. + /// </summary> public AutoDeleteCache() { _textures = new LinkedList<Texture>(); } + /// <summary> + /// Adds a new texture to the cache, even if the texture added is already on the cache. + /// Using this method is only recommended if you know that the texture is not yet on the cache, + /// otherwise it would store the same texture more than once. + /// </summary> + /// <param name="texture">The texture to be added to the cache</param> public void Add(Texture texture) { texture.IncrementReferenceCount(); @@ -32,6 +46,12 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Adds a new texture to the cache, or just moves it to the top of the list if the + /// texture is already on the cache. Moving the texture to the top of the list prevents + /// it from being deleted, as the textures on the bottom of the list are deleted when new ones are added. + /// </summary> + /// <param name="texture">The texture to be added, or moved to the top</param> public void Lift(Texture texture) { if (texture.CacheNode != null) diff --git a/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs b/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs index a728c66e1f..4f73bfa867 100644 --- a/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs +++ b/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs @@ -2,20 +2,48 @@ using Ryujinx.Graphics.GAL; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Represents texture format information. + /// </summary> struct FormatInfo { - private static FormatInfo _rgba8 = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4); - - public static FormatInfo Default => _rgba8; + /// <summary> + /// A default, generic RGBA8 texture format. + /// </summary> + public static FormatInfo Default { get; } = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4); + /// <summary> + /// The format of the texture data. + /// </summary> public Format Format { get; } - public int BlockWidth { get; } - public int BlockHeight { get; } + /// <summary> + /// The block width for compressed formats. Must be 1 for non-compressed formats. + /// </summary> + public int BlockWidth { get; } + + /// <summary> + /// The block height for compressed formats. Must be 1 for non-compressed formats. + /// </summary> + public int BlockHeight { get; } + + /// <summary> + /// The number of bytes occupied by a single pixel in memory of the texture data. + /// </summary> public int BytesPerPixel { get; } + /// <summary> + /// Whenever or not the texture format is a compressed format. Determined from block size. + /// </summary> public bool IsCompressed => (BlockWidth | BlockHeight) != 1; + /// <summary> + /// Constructs the texture format info structure. + /// </summary> + /// <param name="format">The format of the texture data</param> + /// <param name="blockWidth">The block width for compressed formats. Must be 1 for non-compressed formats</param> + /// <param name="blockHeight">The block height for compressed formats. Must be 1 for non-compressed formats</param> + /// <param name="bytesPerPixel">The number of bytes occupied by a single pixel in memory of the texture data</param> public FormatInfo( Format format, int blockWidth, diff --git a/Ryujinx.Graphics.Gpu/Image/FormatTable.cs b/Ryujinx.Graphics.Gpu/Image/FormatTable.cs index baff2e4fcb..468a1ed5c5 100644 --- a/Ryujinx.Graphics.Gpu/Image/FormatTable.cs +++ b/Ryujinx.Graphics.Gpu/Image/FormatTable.cs @@ -3,6 +3,9 @@ using System.Collections.Generic; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Contains format tables, for texture and vertex attribute formats. + /// </summary> static class FormatTable { private static Dictionary<uint, FormatInfo> _textureFormats = new Dictionary<uint, FormatInfo>() @@ -186,6 +189,13 @@ namespace Ryujinx.Graphics.Gpu.Image { 0x36000000, Format.R10G10B10A2Sscaled } }; + /// <summary> + /// Try getting the texture format from a 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); @@ -193,6 +203,12 @@ namespace Ryujinx.Graphics.Gpu.Image return _textureFormats.TryGetValue(encoded, out format); } + /// <summary> + /// Try getting the vertex attribute format from a encoded format integer from Maxwell attribute registers. + /// </summary> + /// <param name="encoded">The encoded format integer from the attribute registers</param> + /// <param name="format">The output vertex attribute format</param> + /// <returns>True if the format is valid, false otherwise</returns> public static bool TryGetAttribFormat(uint encoded, out Format format) { return _attribFormats.TryGetValue(encoded, out format); diff --git a/Ryujinx.Graphics.Gpu/Image/Pool.cs b/Ryujinx.Graphics.Gpu/Image/Pool.cs index 5ce8d7f689..bb55d40e1d 100644 --- a/Ryujinx.Graphics.Gpu/Image/Pool.cs +++ b/Ryujinx.Graphics.Gpu/Image/Pool.cs @@ -3,6 +3,10 @@ using System; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Represents a pool of GPU resources, such as samplers or textures. + /// </summary> + /// <typeparam name="T">GPU resource type</typeparam> abstract class Pool<T> : IDisposable { protected const int DescriptorSize = 0x20; @@ -11,10 +15,21 @@ namespace Ryujinx.Graphics.Gpu.Image protected T[] Items; + /// <summary> + /// The maximum ID value of resources on the pool (inclusive). + /// The maximum amount of resources on the pool is equal to this value plus one. + /// </summary> public int MaximumId { get; } + /// <summary> + /// The address of the pool in guest memory. + /// </summary> public ulong Address { get; } - public ulong Size { get; } + + /// <summary> + /// The size of the pool in bytes. + /// </summary> + public ulong Size { get; } public Pool(GpuContext context, ulong address, int maximumId) { @@ -31,8 +46,18 @@ namespace Ryujinx.Graphics.Gpu.Image Size = size; } + /// <summary> + /// Gets the GPU resource with the given ID. + /// </summary> + /// <param name="id">ID of the resource. This is effectively a zero-based index</param> + /// <returns>The GPU resource with the given ID</returns> public abstract T Get(int id); + /// <summary> + /// Synchronizes host memory with guest memory. + /// This causes a invalidation of pool entries, + /// if a modification of entries by the CPU is detected. + /// </summary> public void SynchronizeMemory() { (ulong, ulong)[] modifiedRanges = Context.PhysicalMemory.GetModifiedRanges(Address, Size, ResourceName.TexturePool); @@ -57,6 +82,13 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Invalidates a range of memory of the GPU resource pool. + /// Entries that falls inside the speicified range will be invalidated, + /// causing all the data to be reloaded from guest memory. + /// </summary> + /// <param name="address">The start address of the range to invalidate</param> + /// <param name="size">The size of the range to invalidate</param> public void InvalidateRange(ulong address, ulong size) { ulong endAddress = address + size; @@ -80,6 +112,8 @@ namespace Ryujinx.Graphics.Gpu.Image endAddress = texturePoolEndAddress; } + size = endAddress - address; + InvalidateRangeImpl(address, size); } @@ -87,6 +121,10 @@ namespace Ryujinx.Graphics.Gpu.Image protected abstract void Delete(T item); + /// <summary> + /// Performs the disposal of all resources stored on the pool. + /// It's an error to try using the pool after disposal. + /// </summary> public void Dispose() { if (Items != null) diff --git a/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs b/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs index f14a17f2ea..94b3f54255 100644 --- a/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs +++ b/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs @@ -1,5 +1,9 @@ namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Represents a filter used with texture minification linear filtering. + /// This feature is only supported on NVIDIA GPUs. + /// </summary> enum ReductionFilter { Average, diff --git a/Ryujinx.Graphics.Gpu/Image/Sampler.cs b/Ryujinx.Graphics.Gpu/Image/Sampler.cs index 80790bd510..23c6160e39 100644 --- a/Ryujinx.Graphics.Gpu/Image/Sampler.cs +++ b/Ryujinx.Graphics.Gpu/Image/Sampler.cs @@ -3,10 +3,21 @@ using System; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Cached sampler entry for sampler pools. + /// </summary> class Sampler : IDisposable { + /// <summary> + /// Host sampler object. + /// </summary> public ISampler HostSampler { get; } + /// <summary> + /// Creates a new instance of the cached sampler. + /// </summary> + /// <param name="context">The GPU context the sampler belongs to</param> + /// <param name="descriptor">The Maxwell sampler descriptor</param> public Sampler(GpuContext context, SamplerDescriptor descriptor) { MinFilter minFilter = descriptor.UnpackMinFilter(); @@ -42,6 +53,9 @@ namespace Ryujinx.Graphics.Gpu.Image maxAnisotropy)); } + /// <summary> + /// Disposes the host sampler object. + /// </summary> public void Dispose() { HostSampler.Dispose(); diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs b/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs index 00b4ecb464..80b1b70fd5 100644 --- a/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs +++ b/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs @@ -2,6 +2,10 @@ using Ryujinx.Graphics.GAL; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Maxwell sampler descriptor structure. + /// This structure defines the sampler descriptor as it is packed on the GPU sampler pool region. + /// </summary> struct SamplerDescriptor { private static readonly float[] _f5ToF32ConversionLut = new float[] @@ -56,41 +60,81 @@ namespace Ryujinx.Graphics.Gpu.Image public uint BorderColorB; public uint BorderColorA; + /// <summary> + /// Unpacks the texture wrap mode along the X axis. + /// </summary> + /// <returns>The texture wrap mode enum</returns> public AddressMode UnpackAddressU() { return (AddressMode)(Word0 & 7); } + // <summary> + /// Unpacks the texture wrap mode along the Y axis. + /// </summary> + /// <returns>The texture wrap mode enum</returns> public AddressMode UnpackAddressV() { return (AddressMode)((Word0 >> 3) & 7); } + // <summary> + /// Unpacks the texture wrap mode along the Z axis. + /// </summary> + /// <returns>The texture wrap mode enum</returns> public AddressMode UnpackAddressP() { return (AddressMode)((Word0 >> 6) & 7); } + /// <summary> + /// Unpacks the compare mode used for depth comparison on the shader, for + /// depth buffer texture. + /// This is only relevant for shaders with shadow samplers. + /// </summary> + /// <returns>The depth comparison mode enum</returns> public CompareMode UnpackCompareMode() { return (CompareMode)((Word0 >> 9) & 1); } + /// <summary> + /// Unpacks the compare operation used for depth comparison on the shader, for + /// depth buffer texture. + /// This is only relevant for shaders with shadow samplers. + /// </summary> + /// <returns>The depth comparison operation enum</returns> public CompareOp UnpackCompareOp() { return (CompareOp)(((Word0 >> 10) & 7) + 1); } + /// <summary> + /// Unpacks and converts the maximum anisotropy value used for texture anisotropic filtering. + /// </summary> + /// <returns>The maximum anisotropy</returns> public float UnpackMaxAnisotropy() { return _maxAnisotropyLut[(Word0 >> 20) & 7]; } + /// <summary> + /// Unpacks the texture magnification filter. + /// This defines the filtering used when the texture covers an area on the screen + /// that is larger than the texture size. + /// </summary> + /// <returns>The magnification filter</returns> public MagFilter UnpackMagFilter() { return (MagFilter)(Word1 & 3); } + /// <summary> + /// Unpacks the texture minification filter. + /// This defines the filtering used when the texture covers an area on the screen + /// that is smaller than the texture size. + /// </summary> + /// <returns>The minification filter</returns> public MinFilter UnpackMinFilter() { SamplerMinFilter minFilter = (SamplerMinFilter)((Word1 >> 4) & 3); @@ -99,6 +143,13 @@ namespace Ryujinx.Graphics.Gpu.Image return ConvertFilter(minFilter, mipFilter); } + /// <summary> + /// Converts two minification and filter enum, to a single minification enum, + /// including mipmap filtering information, as expected from the host API. + /// </summary> + /// <param name="minFilter">The minification filter</param> + /// <param name="mipFilter">The mipmap level filter</param> + /// <returns>The combined, host API compatible filter enum</returns> private static MinFilter ConvertFilter(SamplerMinFilter minFilter, SamplerMipFilter mipFilter) { switch (mipFilter) @@ -131,11 +182,22 @@ namespace Ryujinx.Graphics.Gpu.Image return MinFilter.Nearest; } + /// <summary> + /// Unpacks the reduction filter, used with texture minification linear filtering. + /// This describes how the final value will be computed from neighbouring pixels. + /// </summary> + /// <returns>The reduction filter</returns> public ReductionFilter UnpackReductionFilter() { return (ReductionFilter)((Word1 >> 10) & 3); } + /// <summary> + /// Unpacks the level-of-detail bias value. + /// This is a bias added to the level-of-detail value as computed by the GPU, used to select + /// which mipmap level to use from a given texture. + /// </summary> + /// <returns>The level-of-detail bias value</returns> public float UnpackMipLodBias() { int fixedValue = (int)(Word1 >> 12) & 0x1fff; @@ -145,16 +207,28 @@ namespace Ryujinx.Graphics.Gpu.Image return fixedValue * Frac8ToF32; } + /// <summary> + /// Unpacks the level-of-detail snap value. + /// </summary> + /// <returns>The level-of-detail snap value</returns> public float UnpackLodSnap() { return _f5ToF32ConversionLut[(Word1 >> 26) & 0x1f]; } + /// <summary> + /// Unpacks the minimum level-of-detail value. + /// </summary> + /// <returns>The minimum level-of-detail value</returns> public float UnpackMinLod() { return (Word2 & 0xfff) * Frac8ToF32; } + /// <summary> + /// Unpacks the maximum level-of-detail value. + /// </summary> + /// <returns>The maximum level-of-detail value</returns> public float UnpackMaxLod() { return ((Word2 >> 12) & 0xfff) * Frac8ToF32; diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs b/Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs index b3274b6492..17beb12933 100644 --- a/Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs +++ b/Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs @@ -1,5 +1,8 @@ namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Sampler texture minification filter. + /// </summary> enum SamplerMinFilter { Nearest = 1, diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs b/Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs index 0bc9eb92c7..319d419609 100644 --- a/Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs +++ b/Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs @@ -1,5 +1,8 @@ namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Sampler texture mipmap level filter. + /// </summary> enum SamplerMipFilter { None = 1, diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs index 5555510952..f10f800cd7 100644 --- a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs +++ b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs @@ -3,12 +3,26 @@ using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Sampler pool. + /// </summary> class SamplerPool : Pool<Sampler> { private int _sequenceNumber; + /// <summary> + /// Constructs a new instance of the sampler pool. + /// </summary> + /// <param name="context">GPU context that the sampler pool belongs to</param> + /// <param name="address">Address of the sampler pool in guest memory</param> + /// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param> public SamplerPool(GpuContext context, ulong address, int maximumId) : base(context, address, maximumId) { } + /// <summary> + /// Gets the sampler with the given ID. + /// </summary> + /// <param name="id">ID of the sampler. This is effectively a zero-based index</param> + /// <returns>The sampler with the given ID</returns> public override Sampler Get(int id) { if ((uint)id >= Items.Length) @@ -41,6 +55,11 @@ namespace Ryujinx.Graphics.Gpu.Image return sampler; } + /// <summary> + /// Implementation of the sampler pool range invalidation. + /// </summary> + /// <param name="address">Start address of the range of the sampler pool</param> + /// <param name="size">Size of the range being invalidated</param> protected override void InvalidateRangeImpl(ulong address, ulong size) { ulong endAddress = address + size; @@ -60,6 +79,11 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Deletes a given sampler pool entry. + /// The host memory used by the sampler is released by the driver. + /// </summary> + /// <param name="item">The entry to be deleted</param> protected override void Delete(Sampler item) { item?.Dispose(); diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 544c49af5d..9e5bea9085 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -10,14 +10,23 @@ using System.Diagnostics; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Represents a cached GPU texture. + /// </summary> class Texture : IRange<Texture> { private GpuContext _context; private SizeInfo _sizeInfo; + /// <summary> + /// Texture format. + /// </summary> public Format Format => Info.FormatInfo.Format; + /// <summary> + /// Texture information. + /// </summary> public TextureInfo Info { get; private set; } private int _depth; @@ -34,21 +43,48 @@ namespace Ryujinx.Graphics.Gpu.Image private List<Texture> _views; + /// <summary> + /// Host texture. + /// </summary> public ITexture HostTexture { get; private set; } + /// <summary> + /// Intrusive linked list node used on the auto deletion texture cache. + /// </summary> public LinkedListNode<Texture> CacheNode { get; set; } + /// <summary> + /// Texture data modified by the GPU. + /// </summary> public bool Modified { get; set; } - public ulong Address => Info.Address; + /// <summary> + /// Start address of the texture in guest memory. + /// </summary> + public ulong Address => Info.Address; + + /// <summary> + /// End address of the texture in guest memory. + /// </summary> public ulong EndAddress => Info.Address + Size; + /// <summary> + /// Texture size in bytes. + /// </summary> public ulong Size => (ulong)_sizeInfo.TotalSize; private int _referenceCount; private int _sequenceNumber; + /// <summary> + /// Constructs a new instance of the cached GPU texture. + /// </summary> + /// <param name="context">GPU context that the texture belongs to</param> + /// <param name="info">Texture information</param> + /// <param name="sizeInfo">Size information of the texture</param> + /// <param name="firstLayer">The first layer of the texture, or 0 if the texture has no parent</param> + /// <param name="firstLevel">The first mipmap level of the texture, or 0 if the texture has no parent</param> private Texture( GpuContext context, TextureInfo info, @@ -64,6 +100,12 @@ namespace Ryujinx.Graphics.Gpu.Image _hasData = true; } + /// <summary> + /// Constructs a new instance of the cached GPU texture. + /// </summary> + /// <param name="context">GPU context that the texture belongs to</param> + /// <param name="info">Texture information</param> + /// <param name="sizeInfo">Size information of the texture</param> public Texture(GpuContext context, TextureInfo info, SizeInfo sizeInfo) { InitializeTexture(context, info, sizeInfo); @@ -73,6 +115,14 @@ namespace Ryujinx.Graphics.Gpu.Image HostTexture = _context.Renderer.CreateTexture(createInfo); } + /// <summary> + /// Common texture initialization method. + /// This sets the context, info and sizeInfo fields. + /// Other fields are initialized with their default values. + /// </summary> + /// <param name="context">GPU context that the texture belongs to</param> + /// <param name="info">Texture information</param> + /// <param name="sizeInfo">Size information of the texture</param> private void InitializeTexture(GpuContext context, TextureInfo info, SizeInfo sizeInfo) { _context = context; @@ -85,6 +135,17 @@ namespace Ryujinx.Graphics.Gpu.Image _views = new List<Texture>(); } + /// <summary> + /// Create a texture view from this texture. + /// A texture view is defined as a child texture, from a sub-range of their parent texture. + /// For example, the initial layer and mipmap level of the view can be defined, so the texture + /// will start at the given layer/level of the parent texture. + /// </summary> + /// <param name="info">Child texture information</param> + /// <param name="sizeInfo">Child texture size information</param> + /// <param name="firstLayer">Start layer of the child texture on the parent texture</param> + /// <param name="firstLevel">Start mipmap level of the child texture on the parent texture</param> + /// <returns>The child texture</returns> public Texture CreateView(TextureInfo info, SizeInfo sizeInfo, int firstLayer, int firstLevel) { Texture texture = new Texture( @@ -103,6 +164,10 @@ namespace Ryujinx.Graphics.Gpu.Image return texture; } + /// <summary> + /// Adds a child texture to this texture. + /// </summary> + /// <param name="texture">The child texture</param> private void AddView(Texture texture) { _views.Add(texture); @@ -110,6 +175,10 @@ namespace Ryujinx.Graphics.Gpu.Image texture._viewStorage = this; } + /// <summary> + /// Removes a child texture from this texture. + /// </summary> + /// <param name="texture">The child texture</param> private void RemoveView(Texture texture) { _views.Remove(texture); @@ -119,6 +188,14 @@ namespace Ryujinx.Graphics.Gpu.Image DeleteIfNotUsed(); } + /// <summary> + /// Changes the texture size. + /// This operation may also change the size of all mipmap levels, including from the parent + /// and other possible child textures, to ensure that all sizes are consistent. + /// </summary> + /// <param name="width">The new texture width</param> + /// <param name="height">The new texture height</param> + /// <param name="depthOrLayers">The new texture depth (for 3D textures) or layers (for layered textures)</param> public void ChangeSize(int width, int height, int depthOrLayers) { width <<= _firstLevel; @@ -155,6 +232,14 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Recreates the texture storage (or view, in the case of child textures) of this texture. + /// This allows recreating the texture with a new size. + /// A copy is automatically performed from the old to the new texture. + /// </summary> + /// <param name="width">The new texture width</param> + /// <param name="height">The new texture height</param> + /// <param name="depthOrLayers">The new texture depth (for 3D textures) or layers (for layered textures)</param> private void RecreateStorageOrView(int width, int height, int depthOrLayers) { SetInfo(new TextureInfo( @@ -194,6 +279,13 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Synchronizes guest and host memory. + /// This will overwrite the texture data with the texture data on the guest memory, if a CPU + /// modification is detected. + /// Be aware that this can cause texture data written by the GPU to be lost, this is just a + /// one way copy (from CPU owned to GPU owned memory). + /// </summary> public void SynchronizeMemory() { if (_sequenceNumber == _context.SequenceNumber && _hasData) @@ -266,6 +358,14 @@ namespace Ryujinx.Graphics.Gpu.Image _hasData = true; } + /// <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 a 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> public void Flush() { Span<byte> data = HostTexture.GetData(); @@ -302,6 +402,13 @@ namespace Ryujinx.Graphics.Gpu.Image _context.PhysicalMemory.Write(Address, data); } + /// <summary> + /// Performs a comparison of this texture information, with the specified texture information. + /// This performs a strict comparison, used to check if two textures are equal. + /// </summary> + /// <param name="info">Texture information to compare with</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 (!FormatMatches(info, (flags & TextureSearchFlags.Strict) != 0)) @@ -344,6 +451,12 @@ namespace Ryujinx.Graphics.Gpu.Image return Info.Address == info.Address && Info.Levels == info.Levels; } + /// <summary> + /// Checks if the texture format matches with the specified texture information. + /// </summary> + /// <param name="info">Texture information to compare with</param> + /// <param name="strict">True to perform a strict comparison (formats must be exactly equal)</param> + /// <returns>True if the format matches, with the given comparison rules</returns> private bool FormatMatches(TextureInfo info, bool strict) { // D32F and R32F texture have the same representation internally, @@ -356,6 +469,13 @@ namespace Ryujinx.Graphics.Gpu.Image return Info.FormatInfo.Format == info.FormatInfo.Format; } + /// <summary> + /// Checks if the texture layout specified matches with this texture layout. + /// The layout information is composed of the Stride for linear textures, or GOB block size + /// for block linear textures. + /// </summary> + /// <param name="info">Texture information to compare with</param> + /// <returns>True if the layout matches, false otherwise</returns> private bool LayoutMatches(TextureInfo info) { if (Info.IsLinear != info.IsLinear) @@ -376,11 +496,23 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Checks if the texture sizes of the supplied texture information matches this texture. + /// </summary> + /// <param name="info">Texture information to compare with</param> + /// <returns>True if the size matches, false otherwise</returns> public bool SizeMatches(TextureInfo info) { return SizeMatches(info, alignSizes: false); } + /// <summary> + /// Checks if the texture sizes of the supplied texture information matches the given level of + /// this texture. + /// </summary> + /// <param name="info">Texture information to compare with</param> + /// <param name="level">Mipmap level of this texture to compare with</param> + /// <returns>True if the size matches with the level, false otherwise</returns> public bool SizeMatches(TextureInfo info, int level) { return Math.Max(1, Info.Width >> level) == info.Width && @@ -388,6 +520,12 @@ namespace Ryujinx.Graphics.Gpu.Image Math.Max(1, Info.GetDepth() >> level) == info.GetDepth(); } + /// <summary> + /// Checks if the texture sizes of the supplied texture information matches this texture. + /// </summary> + /// <param name="info">Texture information to compare with</param> + /// <param name="alignSizes">True to align the sizes according to the texture layout for comparison</param> + /// <returns>True if the sizes matches, false otherwise</returns> private bool SizeMatches(TextureInfo info, bool alignSizes) { if (Info.GetLayers() != info.GetLayers()) @@ -412,6 +550,11 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Checks if the texture shader sampling parameters matches. + /// </summary> + /// <param name="info">Texture information to compare with</param> + /// <returns>True if the texture shader sampling parameters matches, false otherwise</returns> private bool SamplerParamsMatches(TextureInfo info) { return Info.DepthStencilMode == info.DepthStencilMode && @@ -421,6 +564,11 @@ namespace Ryujinx.Graphics.Gpu.Image Info.SwizzleA == info.SwizzleA; } + /// <summary> + /// Check if the texture target and samples count (for multisampled textures) matches. + /// </summary> + /// <param name="info">Texture information to compare with</param> + /// <returns>True if the texture target and samples count matches, false otherwise</returns> private bool TargetAndSamplesCompatible(TextureInfo info) { return Info.Target == info.Target && @@ -428,6 +576,14 @@ namespace Ryujinx.Graphics.Gpu.Image Info.SamplesInY == info.SamplesInY; } + /// <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>True if a view with the given parameters can be created from this texture, false otherwise</returns> public bool IsViewCompatible( TextureInfo info, ulong size, @@ -437,6 +593,15 @@ namespace Ryujinx.Graphics.Gpu.Image return IsViewCompatible(info, size, isCopy: false, out firstLayer, out firstLevel); } + /// <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="isCopy">True to check for copy compability, instead of view compatibility</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>True if a view with the given parameters can be created from this texture, false otherwise</returns> public bool IsViewCompatible( TextureInfo info, ulong size, @@ -484,6 +649,14 @@ namespace Ryujinx.Graphics.Gpu.Image Info.SamplesInY == info.SamplesInY; } + /// <summary> + /// Check it's possible to create a view with the specified layout. + /// The layout information is composed of the Stride for linear textures, or GOB block size + /// for block linear textures. + /// </summary> + /// <param name="info">Texture information of the texture view</param> + /// <param name="level">Start level of the texture view, in relation with this texture</param> + /// <returns>True if the layout is compatible, false otherwise</returns> private bool ViewLayoutCompatible(TextureInfo info, int level) { if (Info.IsLinear != info.IsLinear) @@ -520,11 +693,26 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Checks if the view format is compatible with this texture format. + /// In general, the formats are considered compatible if the bytes per pixel value is 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> + /// Checks if the size of a given texture view is compatible with this texture. + /// </summary> + /// <param name="info">Texture information of the texture view</param> + /// <param name="level">Mipmap level of the texture view in relation to this texture</param> + /// <param name="isCopy">True to check for copy compatibility rather than view compatibility</param> + /// <returns>True if the sizes are compatible, false otherwise</returns> private bool ViewSizeMatches(TextureInfo info, int level, bool isCopy) { Size size = GetAlignedSize(Info, level); @@ -542,6 +730,14 @@ namespace Ryujinx.Graphics.Gpu.Image size.Height == otherSize.Height; } + /// <summary> + /// Check if the target of the specified texture view information is compatible with this + /// texture. + /// This follows the host API target compatibility rules. + /// </summary> + /// <param name="info">Texture information of the texture view</param> + /// <param name="isCopy">True to check for copy rather than view compatibility</param> + /// <returns>True if the targets are compatible, false otherwise</returns> private bool ViewTargetCompatible(TextureInfo info, bool isCopy) { switch (Info.Target) @@ -576,6 +772,13 @@ namespace Ryujinx.Graphics.Gpu.Image return false; } + /// <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); @@ -614,6 +817,13 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Gets a texture of the specified target type from this texture. + /// This can be used to get an array texture from a non-array texture and vice-versa. + /// If this texture and the requested targets are equal, then this texture Host texture is returned directly. + /// </summary> + /// <param name="target">The desired target type</param> + /// <returns>A view of this texture with the requested target, or null if the target is invalid for this texture</returns> public ITexture GetTargetTexture(Target target) { if (target == Info.Target) @@ -655,6 +865,13 @@ namespace Ryujinx.Graphics.Gpu.Image return null; } + /// <summary> + /// Check if this texture and the specified target have the same number of dimensions. + /// For the purposes of this comparison, 2D and 2D Multisample textures are not considered to have + /// the same number of dimensions. Same for Cubemap and 3D textures. + /// </summary> + /// <param name="target">The target to compare with</param> + /// <returns>True if both targets have the same number of dimensions, false otherwise</returns> private bool IsSameDimensionsTarget(Target target) { switch (Info.Target) @@ -686,6 +903,13 @@ namespace Ryujinx.Graphics.Gpu.Image return false; } + /// <summary> + /// Replaces view texture information. + /// This should only be used for child textures with a parent. + /// </summary> + /// <param name="parent">The parent texture</param> + /// <param name="info">The new view texture information</param> + /// <param name="hostTexture">The new host texture</param> public void ReplaceView(Texture parent, TextureInfo info, ITexture hostTexture) { ReplaceStorage(hostTexture); @@ -695,6 +919,10 @@ namespace Ryujinx.Graphics.Gpu.Image SetInfo(info); } + /// <summary> + /// Sets the internal texture information structure. + /// </summary> + /// <param name="info">The new texture information</param> private void SetInfo(TextureInfo info) { Info = info; @@ -703,6 +931,10 @@ namespace Ryujinx.Graphics.Gpu.Image _layers = info.GetLayers(); } + /// <summary> + /// Replaces the host texture, while disposing of the old one if needed. + /// </summary> + /// <param name="hostTexture">The new host texture</param> private void ReplaceStorage(ITexture hostTexture) { DisposeTextures(); @@ -710,16 +942,29 @@ namespace Ryujinx.Graphics.Gpu.Image HostTexture = hostTexture; } + /// <summary> + /// Checks if the texture overlaps with a memory range. + /// </summary> + /// <param name="address">Start address of the range</param> + /// <param name="size">Size of the range</param> + /// <returns>True if the texture overlaps with the range, false otherwise</returns> public bool OverlapsWith(ulong address, ulong size) { return Address < address + size && address < EndAddress; } + /// <summary> + /// Increments the texture reference count. + /// </summary> public void IncrementReferenceCount() { _referenceCount++; } + /// <summary> + /// Decrements the texture reference count. + /// When the reference count hits zero, the texture may be deleted and can't be used anymore. + /// </summary> public void DecrementReferenceCount() { int newRefCount = --_referenceCount; @@ -739,6 +984,11 @@ namespace Ryujinx.Graphics.Gpu.Image DeleteIfNotUsed(); } + /// <summary> + /// Delete the texture if it is not used anymore. + /// The texture is considered unused when the reference count is zero, + /// and it has no child views. + /// </summary> private void DeleteIfNotUsed() { // We can delete the texture as long it is not being used @@ -751,6 +1001,9 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Performs texture disposal, deleting the texture. + /// </summary> private void DisposeTextures() { HostTexture.Dispose(); diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs index cf922ac5ab..94225406ef 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs @@ -2,17 +2,44 @@ using Ryujinx.Graphics.GAL; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Texture binding information. + /// This is used for textures that needs to be accessed from shaders. + /// </summary> struct TextureBindingInfo { + /// <summary> + /// Shader sampler target type. + /// </summary> public Target Target { get; } + /// <summary> + /// Shader texture handle. + /// This is a index into the texture constant buffer. + /// </summary> public int Handle { get; } + /// <summary> + /// Indicates if the texture is a bindless texture. + /// For those textures, Handle is ignored. + /// </summary> public bool IsBindless { get; } - public int CbufSlot { get; } + /// <summary> + /// Constant buffer slot with the bindless texture handle, for bindless texture. + /// </summary> + public int CbufSlot { get; } + + /// <summary> + /// Constant buffer offset of the bindless texture handle, for bindless texture. + /// </summary> public int CbufOffset { get; } + /// <summary> + /// Constructs the texture binding information structure. + /// </summary> + /// <param name="target">The shader sampler target type</param> + /// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param> public TextureBindingInfo(Target target, int handle) { Target = target; @@ -24,6 +51,12 @@ namespace Ryujinx.Graphics.Gpu.Image CbufOffset = 0; } + /// <summary> + /// Constructs the bindless texture binding information structure. + /// </summary> + /// <param name="target">The shader sampler target type</param> + /// <param name="cbufSlot">Constant buffer slot where the bindless texture handle is located</param> + /// <param name="cbufOffset">Constant buffer offset of the bindless texture handle</param> public TextureBindingInfo(Target target, int cbufSlot, int cbufOffset) { Target = target; diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index 0ea7aec274..4d50c46e30 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -6,6 +6,9 @@ using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Texture bindings manager. + /// </summary> class TextureBindingsManager { private GpuContext _context; @@ -37,6 +40,12 @@ namespace Ryujinx.Graphics.Gpu.Image private bool _rebind; + /// <summary> + /// Constructs a new instance of the texture bindings manager. + /// </summary> + /// <param name="context">The GPU context that the texture bindings manager belongs to</param> + /// <param name="texturePoolCache">Texture pools cache used to get texture pools from</param> + /// <param name="isCompute">True if the bindings manager is used for the compute engine</param> public TextureBindingsManager(GpuContext context, TexturePoolCache texturePoolCache, bool isCompute) { _context = context; @@ -52,6 +61,11 @@ namespace Ryujinx.Graphics.Gpu.Image _imageState = new TextureStatePerStage[stages][]; } + /// <summary> + /// Binds textures for a given shader stage. + /// </summary> + /// <param name="stage">Shader stage number, or 0 for compute shaders</param> + /// <param name="bindings">Texture bindings</param> public void SetTextures(int stage, TextureBindingInfo[] bindings) { _textureBindings[stage] = bindings; @@ -59,6 +73,11 @@ namespace Ryujinx.Graphics.Gpu.Image _textureState[stage] = new TextureStatePerStage[bindings.Length]; } + /// <summary> + /// Binds images for a given shader stage. + /// </summary> + /// <param name="stage">Shader stage number, or 0 for compute shaders</param> + /// <param name="bindings">Image bindings</param> public void SetImages(int stage, TextureBindingInfo[] bindings) { _imageBindings[stage] = bindings; @@ -66,11 +85,22 @@ namespace Ryujinx.Graphics.Gpu.Image _imageState[stage] = new TextureStatePerStage[bindings.Length]; } + /// <summary> + /// Sets the textures constant buffer index. + /// The constant buffer specified holds the texture handles. + /// </summary> + /// <param name="index">Constant buffer index</param> public void SetTextureBufferIndex(int index) { _textureBufferIndex = index; } + /// <summary> + /// Sets the current texture sampler pool to be used. + /// </summary> + /// <param name="gpuVa">Start GPU virtual address of the pool</param> + /// <param name="maximumId">Maximum ID of the pool (total count minus one)</param> + /// <param name="samplerIndex">Type of the sampler pool indexing used for bound samplers</param> public void SetSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex) { ulong address = _context.MemoryManager.Translate(gpuVa); @@ -90,6 +120,11 @@ namespace Ryujinx.Graphics.Gpu.Image _samplerIndex = samplerIndex; } + /// <summary> + /// Sets the current texture pool to be used. + /// </summary> + /// <param name="gpuVa">Start GPU virtual address of the pool</param> + /// <param name="maximumId">Maximum ID of the pool (total count minus one)</param> public void SetTexturePool(ulong gpuVa, int maximumId) { ulong address = _context.MemoryManager.Translate(gpuVa); @@ -98,6 +133,10 @@ namespace Ryujinx.Graphics.Gpu.Image _texturePoolMaximumId = maximumId; } + /// <summary> + /// Ensures that the bindings are visible to the host GPU. + /// This actually performs the binding using the host graphics API. + /// </summary> public void CommitBindings() { TexturePool texturePool = _texturePoolCache.FindOrCreate( @@ -123,6 +162,13 @@ namespace Ryujinx.Graphics.Gpu.Image _rebind = false; } + /// <summary> + /// Ensures that the texture bindings are visible to the host GPU. + /// This actually performs the binding using the host graphics API. + /// </summary> + /// <param name="pool">The current texture pool</param> + /// <param name="stage">The shader stage using the textures to be bound</param> + /// <param name="stageIndex">The stage number of the specified shader stage</param> private void CommitTextureBindings(TexturePool pool, ShaderStage stage, int stageIndex) { if (_textureBindings[stageIndex] == null) @@ -194,6 +240,13 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Ensures that the image bindings are visible to the host GPU. + /// This actually performs the binding using the host graphics API. + /// </summary> + /// <param name="pool">The current texture pool</param> + /// <param name="stage">The shader stage using the textures to be bound</param> + /// <param name="stageIndex">The stage number of the specified shader stage</param> private void CommitImageBindings(TexturePool pool, ShaderStage stage, int stageIndex) { if (_imageBindings[stageIndex] == null) @@ -222,6 +275,13 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Gets the texture descriptor for a given texture handle. + /// </summary> + /// <param name="state">The current GPU state</param> + /// <param name="stageIndex">The stage number where the texture is bound</param> + /// <param name="handle">The texture handle</param> + /// <returns>The texture descriptor for the specified texture</returns> public TextureDescriptor GetTextureDescriptor(GpuState state, int stageIndex, int handle) { int packedId = ReadPackedId(stageIndex, handle); @@ -237,7 +297,14 @@ namespace Ryujinx.Graphics.Gpu.Image return texturePool.GetDescriptor(textureId); } - private int ReadPackedId(int stage, int wordOffset) + /// <summary> + /// Reads a packed texture and sampler ID (basically, the real texture handle) + /// from the texture constant buffer. + /// </summary> + /// <param name="stageIndex">The number of the shader stage where the texture is bound</param> + /// <param name="wordOffset">A word offset of the handle on the buffer (the "fake" shader handle)</param> + /// <returns>The packed texture and sampler ID (the real texture handle)</returns> + private int ReadPackedId(int stageIndex, int wordOffset) { ulong address; @@ -249,7 +316,7 @@ namespace Ryujinx.Graphics.Gpu.Image } else { - address = bufferManager.GetGraphicsUniformBufferAddress(stage, _textureBufferIndex); + address = bufferManager.GetGraphicsUniformBufferAddress(stageIndex, _textureBufferIndex); } address += (uint)wordOffset * 4; @@ -257,16 +324,31 @@ namespace Ryujinx.Graphics.Gpu.Image return BitConverter.ToInt32(_context.PhysicalMemory.Read(address, 4)); } + /// <summary> + /// Unpacks the texture ID from the real texture handle. + /// </summary> + /// <param name="packedId">The real texture handle</param> + /// <returns>The texture ID</returns> private static int UnpackTextureId(int packedId) { return (packedId >> 0) & 0xfffff; } + /// <summary> + /// Unpacks the sampler ID from the real texture handle. + /// </summary> + /// <param name="packedId">The real texture handle</param> + /// <returns>The sampler ID</returns> private static int UnpackSamplerId(int packedId) { return (packedId >> 20) & 0xfff; } + /// <summary> + /// Invalidates a range of memory on all GPU resource pools (both texture and sampler pools). + /// </summary> + /// <param name="address">Start address of the range to invalidate</param> + /// <param name="size">Size of the range to invalidate</param> public void InvalidatePoolRange(ulong address, ulong size) { _samplerPool?.InvalidateRange(address, size); @@ -274,6 +356,9 @@ namespace Ryujinx.Graphics.Gpu.Image _texturePoolCache.InvalidateRange(address, size); } + /// <summary> + /// Force all bound textures and images to be rebound the next time CommitBindings is called. + /// </summary> public void Rebind() { _rebind = true; diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index 524721647e..5b86962093 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -2,6 +2,9 @@ using Ryujinx.Graphics.GAL; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Texture format compatibility checks. + /// </summary> static class TextureCompatibility { private enum FormatClass @@ -19,6 +22,12 @@ namespace Ryujinx.Graphics.Gpu.Image Bc7 } + /// <summary> + /// Checks if two formats are compatible, according to the host API copy format compatibility rules. + /// </summary> + /// <param name="lhs">First comparand</param> + /// <param name="rhs">Second comparand</param> + /// <returns>True if the formats are compatible, false otherwise</returns> public static bool FormatCompatible(FormatInfo lhs, FormatInfo rhs) { if (IsDsFormat(lhs.Format) || IsDsFormat(rhs.Format)) @@ -44,6 +53,11 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Gets the texture format class, for compressed textures, or Unclassified otherwise. + /// </summary> + /// <param name="format">The format</param> + /// <returns>Format class</returns> private static FormatClass GetFormatClass(Format format) { switch (format) @@ -77,6 +91,11 @@ namespace Ryujinx.Graphics.Gpu.Image return FormatClass.Unclassified; } + /// <summary> + /// Checks if the format is a depth-stencil texture format. + /// </summary> + /// <param name="format">Format to check</param> + /// <returns>True if the format is a depth-stencil format (including depth only), false otherwise</returns> private static bool IsDsFormat(Format format) { switch (format) diff --git a/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs b/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs index db9b9dae69..359069bcfa 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs @@ -2,6 +2,9 @@ using Ryujinx.Graphics.GAL; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Texture swizzle color component. + /// </summary> enum TextureComponent { Zero = 0, @@ -15,6 +18,11 @@ namespace Ryujinx.Graphics.Gpu.Image static class TextureComponentConverter { + /// <summary> + /// Converts the texture swizzle color component enum to the respective Graphics Abstraction Layer enum. + /// </summary> + /// <param name="component">Texture swizzle color component</param> + /// <returns>Converted enum</returns> public static SwizzleComponent Convert(this TextureComponent component) { switch (component) diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs index 6d1f0fb18d..5dbd1a082d 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs @@ -1,5 +1,8 @@ namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Maxwell texture descriptor, as stored on the GPU texture pool memory region. + /// </summary> struct TextureDescriptor { public uint Word0; @@ -11,111 +14,213 @@ namespace Ryujinx.Graphics.Gpu.Image public uint Word6; public uint Word7; + /// <summary> + /// Unpacks Maxwell texture format integer. + /// </summary> + /// <returns>The texture format integer</returns> public uint UnpackFormat() { return Word0 & 0x8007ffff; } + /// <summary> + /// Unpacks the swizzle component for the texture red color channel. + /// </summary> + /// <returns>The swizzle component</returns> public TextureComponent UnpackSwizzleR() { return(TextureComponent)((Word0 >> 19) & 7); } + /// <summary> + /// Unpacks the swizzle component for the texture green color channel. + /// </summary> + /// <returns>The swizzle component</returns> public TextureComponent UnpackSwizzleG() { return(TextureComponent)((Word0 >> 22) & 7); } + /// <summary> + /// Unpacks the swizzle component for the texture blue color channel. + /// </summary> + /// <returns>The swizzle component</returns> public TextureComponent UnpackSwizzleB() { return(TextureComponent)((Word0 >> 25) & 7); } + /// <summary> + /// Unpacks the swizzle component for the texture alpha color channel. + /// </summary> + /// <returns>The swizzle component</returns> public TextureComponent UnpackSwizzleA() { return(TextureComponent)((Word0 >> 28) & 7); } + /// <summary> + /// Unpacks the 40-bits texture GPU virtual address. + /// </summary> + /// <returns>The GPU virtual address</returns> public ulong UnpackAddress() { return Word1 | ((ulong)(Word2 & 0xffff) << 32); } + /// <summary> + /// Unpacks texture descriptor type for this texture descriptor. + /// This defines the texture layout, among other things. + /// </summary> + /// <returns>The texture descriptor type</returns> public TextureDescriptorType UnpackTextureDescriptorType() { return (TextureDescriptorType)((Word2 >> 21) & 7); } + /// <summary> + /// Unpacks the texture stride (bytes per line) for linear textures only. + /// Always 32-bytes aligned. + /// </summary> + /// <returns>The linear texture stride</returns> public int UnpackStride() { return (int)(Word3 & 0xffff) << 5; } + /// <summary> + /// Unpacks the GOB block size in X (width) for block linear textures. + /// Must be always 1, ignored by the GPU. + /// </summary> + /// <returns>THe GOB block X size</returns> public int UnpackGobBlocksInX() { return 1 << (int)(Word3 & 7); } + /// <summary> + /// Unpacks the GOB block size in Y (height) for block linear textures. + /// Must be always a power of 2, with a maximum value of 32. + /// </summary> + /// <returns>THe GOB block Y size</returns> public int UnpackGobBlocksInY() { return 1 << (int)((Word3 >> 3) & 7); } + /// <summary> + /// Unpacks the GOB block size in Z (depth) for block linear textures. + /// Must be always a power of 2, with a maximum value of 32. + /// Must be 1 for any texture target other than 3D textures. + /// </summary> + /// <returns>The GOB block Z size</returns> public int UnpackGobBlocksInZ() { return 1 << (int)((Word3 >> 6) & 7); } + /// <summary> + /// Number of GOB blocks per tile in the X direction. + /// This is only used for sparse textures, should be 1 otherwise. + /// </summary> + /// <returns>The number of GOB blocks per tile</returns> public int UnpackGobBlocksInTileX() { return 1 << (int)((Word3 >> 10) & 7); } + /// <summary> + /// Unpacks the number of mipmap levels of the texture. + /// </summary> + /// <returns>The number of mipmap levels</returns> public int UnpackLevels() { return (int)(Word3 >> 28) + 1; } + /// <summary> + /// Unpack the base level texture width size. + /// </summary> + /// <returns>The texture width</returns> public int UnpackWidth() { return (int)(Word4 & 0xffff) + 1; } + /// <summary> + /// Unpacks the texture sRGB format flag. + /// </summary> + /// <returns>True if the texture is sRGB, false otherwise</returns> public bool UnpackSrgb() { return (Word4 & (1 << 22)) != 0; } + /// <summary> + /// Unpacks the texture target. + /// </summary> + /// <returns>The texture target</returns> public TextureTarget UnpackTextureTarget() { return (TextureTarget)((Word4 >> 23) & 0xf); } + /// <summary> + /// Unpack the base level texture height size, or array layers for 1D array textures. + /// Should be ignored for 1D or buffer textures. + /// </summary> + /// <returns>The texture height or layers count</returns> public int UnpackHeight() { return (int)(Word5 & 0xffff) + 1; } + /// <summary> + /// Unpack the base level texture depth size, number of array layers or cubemap faces. + /// The meaning of this value depends on the texture target. + /// </summary> + /// <returns>The texture depth, layer or faces count</returns> public int UnpackDepth() { return (int)((Word5 >> 16) & 0x3fff) + 1; } + /// <summary> + /// Unpacks the texture coordinates normalized flag. + /// When this is true, texture coordinates are expected to be in the [0, 1] range on the shader. + /// WHen this is false, texture coordinates are expected to be in the [0, W], [0, H] and [0, D] range. + /// It must be set to false (by the guest driver) for rectangle textures. + /// </summary> + /// <returns>The texture coordinates normalized flag</returns> public bool UnpackTextureCoordNormalized() { return (Word5 & (1 << 31)) != 0; } + /// <summary> + /// Unpacks the base mipmap level of the texture. + /// </summary> + /// <returns>The base mipmap level of the texture</returns> public int UnpackBaseLevel() { return (int)(Word7 & 0xf); } + /// <summary> + /// Unpacks the maximum mipmap level (inclusive) of the texture. + /// Usually equal to Levels minus 1. + /// </summary> + /// <returns>The maximum mipmap level (inclusive) of the texture</returns> public int UnpackMaxLevelInclusive() { return (int)((Word7 >> 4) & 0xf); } + /// <summary> + /// Unpacks the multisampled texture samples count in each direction. + /// Must be ignored for non-multisample textures. + /// </summary> + /// <returns>The multisample counts enum</returns> public TextureMsaaMode UnpackTextureMsaaMode() { return (TextureMsaaMode)((Word7 >> 8) & 0xf); diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs b/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs index 6f6048a619..8e7d40bbe1 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs @@ -1,5 +1,10 @@ namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// The texture descriptor type. + /// This specifies the texture memory layout. + /// The texture descriptor structure depends on the type. + /// </summary> enum TextureDescriptorType { Buffer, diff --git a/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs b/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs index 19110dcf6c..784ff0e971 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs @@ -2,35 +2,131 @@ using Ryujinx.Graphics.GAL; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Texture information. + /// </summary> struct TextureInfo { + /// <summary> + /// Address of the texture in guest memory. + /// </summary> public ulong Address { get; } - public int Width { get; } - public int Height { get; } - public int DepthOrLayers { get; } - public int Levels { get; } - public int SamplesInX { get; } - public int SamplesInY { get; } - public int Stride { get; } - public bool IsLinear { get; } - public int GobBlocksInY { get; } - public int GobBlocksInZ { get; } - public int GobBlocksInTileX { get; } + /// <summary> + /// The width of the texture. + /// </summary> + public int Width { get; } + /// <summary> + /// The height of the texture, or layers count for 1D array textures. + /// </summary> + public int Height { get; } + + /// <summary> + /// The depth of the texture (for 3D textures), or layers count for array textures. + /// </summary> + public int DepthOrLayers { get; } + + /// <summary> + /// The number of mipmap levels of the texture. + /// </summary> + public int Levels { get; } + + /// <summary> + /// The number of samples in the X direction for multisampled textures. + /// </summary> + public int SamplesInX { get; } + + /// <summary> + /// The number of samples in the Y direction for multisampled textures. + /// </summary> + public int SamplesInY { get; } + + /// <summary> + /// The number of bytes per line for linear textures. + /// </summary> + public int Stride { get; } + + /// <summary> + /// Indicates whenever or not the texture is a linear texture. + /// </summary> + public bool IsLinear { get; } + + /// <summary> + /// GOB blocks in the Y direction, for block linear textures. + /// </summary> + public int GobBlocksInY { get; } + + /// <summary> + /// GOB blocks in the Z direction, for block linear textures. + /// </summary> + public int GobBlocksInZ { get; } + + /// <summary> + /// Number of GOB blocks per tile in the X direction, for block linear textures. + /// </summary> + public int GobBlocksInTileX { get; } + + /// <summary> + /// Total number of samples for multisampled textures. + /// </summary> public int Samples => SamplesInX * SamplesInY; + /// <summary> + /// Texture target type. + /// </summary> public Target Target { get; } + /// <summary> + /// Texture format information. + /// </summary> public FormatInfo FormatInfo { get; } + /// <summary> + /// Depth-stencil mode of the texture. This defines whenever the depth or stencil value is read from shaders, + /// for depth-stencil texture formats. + /// </summary> public DepthStencilMode DepthStencilMode { get; } + /// <summary> + /// Texture swizzle for the red color channel. + /// </summary> public SwizzleComponent SwizzleR { get; } + /// <summary> + /// Texture swizzle for the green color channel. + /// </summary> public SwizzleComponent SwizzleG { get; } + /// <summary> + /// Texture swizzle for the blue color channel. + /// </summary> public SwizzleComponent SwizzleB { get; } + /// <summary> + /// Texture swizzle for the alpha color channel. + /// </summary> public SwizzleComponent SwizzleA { get; } + /// <summary> + /// Constructs the texture information structure. + /// </summary> + /// <param name="address">The address of the texture</param> + /// <param name="width">The width of the texture</param> + /// <param name="height">The height or the texture</param> + /// <param name="depthOrLayers">The depth or layers count of the texture</param> + /// <param name="levels">The amount if mipmap levels of the texture</param> + /// <param name="samplesInX">The number of samples in the X direction for multisample textures, should be 1 otherwise</param> + /// <param name="samplesInY">The number of samples in the Y direction for multisample textures, should be 1 otherwise</param> + /// <param name="stride">The stride for linear textures</param> + /// <param name="isLinear">Whenever the texture is linear or block linear</param> + /// <param name="gobBlocksInY">Number of GOB blocks in the Y direction</param> + /// <param name="gobBlocksInZ">Number of GOB blocks in the Z direction</param> + /// <param name="gobBlocksInTileX">Number of GOB blocks per tile in the X direction</param> + /// <param name="target">Texture target type</param> + /// <param name="formatInfo">Texture format information</param> + /// <param name="depthStencilMode">Depth-stencil mode</param> + /// <param name="swizzleR">Swizzle for the red color channel</param> + /// <param name="swizzleG">Swizzle for the green color channel</param> + /// <param name="swizzleB">Swizzle for the blue color channel</param> + /// <param name="swizzleA">Swizzle for the alpha color channel</param> public TextureInfo( ulong address, int width, @@ -73,11 +169,21 @@ namespace Ryujinx.Graphics.Gpu.Image SwizzleA = swizzleA; } + /// <summary> + /// Gets the real texture depth. + /// Returns 1 for any target other than 3D textures. + /// </summary> + /// <returns>Texture depth</returns> public int GetDepth() { return Target == Target.Texture3D ? DepthOrLayers : 1; } + /// <summary> + /// Gets the number of layers of the texture. + /// Returns 1 for non-array textures, 6 for cubemap textures, and layer faces for cubemap array textures. + /// </summary> + /// <returns>The number of texture layers</returns> public int GetLayers() { if (Target == Target.Texture2DArray || Target == Target.Texture2DMultisampleArray) diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index 6e1b8c60a6..4f6d5e588e 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -8,28 +8,37 @@ using System; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Texture manager. + /// </summary> class TextureManager { private const int OverlapsBufferInitialCapacity = 10; private const int OverlapsBufferMaxCapacity = 10000; - private GpuContext _context; + private readonly GpuContext _context; - private TextureBindingsManager _cpBindingsManager; - private TextureBindingsManager _gpBindingsManager; + private readonly TextureBindingsManager _cpBindingsManager; + private readonly TextureBindingsManager _gpBindingsManager; - private Texture[] _rtColors; - private Texture _rtDepthStencil; + private readonly Texture[] _rtColors; - private ITexture[] _rtHostColors; - private ITexture _rtHostDs; + private Texture _rtDepthStencil; - private RangeList<Texture> _textures; + private readonly ITexture[] _rtHostColors; + + private ITexture _rtHostDs; + + private readonly RangeList<Texture> _textures; private Texture[] _textureOverlaps; - private AutoDeleteCache _cache; + private readonly AutoDeleteCache _cache; + /// <summary> + /// Constructs a new instance of the texture manager. + /// </summary> + /// <param name="context">The GPU context that the texture manager belongs to</param> public TextureManager(GpuContext context) { _context = context; @@ -50,66 +59,126 @@ namespace Ryujinx.Graphics.Gpu.Image _cache = new AutoDeleteCache(); } + /// <summary> + /// Sets texture bindings on the compute pipeline. + /// </summary> + /// <param name="bindings">The texture bindings</param> public void SetComputeTextures(TextureBindingInfo[] bindings) { _cpBindingsManager.SetTextures(0, bindings); } + /// <summary> + /// Sets texture bindings on the graphics pipeline. + /// </summary> + /// <param name="stage">The index of the shader stage to bind the textures</param> + /// <param name="bindings">The texture bindings</param> public void SetGraphicsTextures(int stage, TextureBindingInfo[] bindings) { _gpBindingsManager.SetTextures(stage, bindings); } + /// <summary> + /// Sets image bindings on the compute pipeline. + /// </summary> + /// <param name="bindings">The image bindings</param> public void SetComputeImages(TextureBindingInfo[] bindings) { _cpBindingsManager.SetImages(0, bindings); } + /// <summary> + /// Sets image bindings on the graphics pipeline. + /// </summary> + /// <param name="stage">The index of the shader stage to bind the images</param> + /// <param name="bindings">The image bindings</param> public void SetGraphicsImages(int stage, TextureBindingInfo[] bindings) { _gpBindingsManager.SetImages(stage, bindings); } + /// <summary> + /// Sets the texture constant buffer index on the compute pipeline. + /// </summary> + /// <param name="index">The texture constant buffer index</param> public void SetComputeTextureBufferIndex(int index) { _cpBindingsManager.SetTextureBufferIndex(index); } + /// <summary> + /// Sets the texture constant buffer index on the graphics pipeline. + /// </summary> + /// <param name="index">The texture constant buffer index</param> public void SetGraphicsTextureBufferIndex(int index) { _gpBindingsManager.SetTextureBufferIndex(index); } + /// <summary> + /// Sets the current sampler pool on the compute pipeline. + /// </summary> + /// <param name="gpuVa">The start GPU virtual address of the sampler pool</param> + /// <param name="maximumId">The maximum ID of the sampler pool</param> + /// <param name="samplerIndex">The indexing type of the sampler</param> public void SetComputeSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex) { _cpBindingsManager.SetSamplerPool(gpuVa, maximumId, samplerIndex); } + /// <summary> + /// Sets the current sampler pool on the graphics pipeline. + /// </summary> + /// <param name="gpuVa">The start GPU virtual address of the sampler pool</param> + /// <param name="maximumId">The maximum ID of the sampler pool</param> + /// <param name="samplerIndex">The indexing type of the sampler</param> public void SetGraphicsSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex) { _gpBindingsManager.SetSamplerPool(gpuVa, maximumId, samplerIndex); } + /// <summary> + /// Sets the current texture pool on the compute pipeline. + /// </summary> + /// <param name="gpuVa">The start GPU virtual address of the texture pool</param> + /// <param name="maximumId">The maximum ID of the texture pool</param> public void SetComputeTexturePool(ulong gpuVa, int maximumId) { _cpBindingsManager.SetTexturePool(gpuVa, maximumId); } + /// <summary> + /// Sets the current texture pool on the graphics pipeline. + /// </summary> + /// <param name="gpuVa">The start GPU virtual address of the texture pool</param> + /// <param name="maximumId">The maximum ID of the texture pool</param> public void SetGraphicsTexturePool(ulong gpuVa, int maximumId) { _gpBindingsManager.SetTexturePool(gpuVa, maximumId); } + /// <summary> + /// Sets the render target color buffer. + /// </summary> + /// <param name="index">The index of the color buffer to set (up to 8)</param> + /// <param name="color">The color buffer texture</param> public void SetRenderTargetColor(int index, Texture color) { _rtColors[index] = color; } + /// <summary> + /// Sets the render target depth-stencil buffer. + /// </summary> + /// <param name="depthStencil">The depth-stencil buffer texture</param> public void SetRenderTargetDepthStencil(Texture depthStencil) { _rtDepthStencil = depthStencil; } + /// <summary> + /// Commits bindings on the compute pipeline. + /// </summary> public void CommitComputeBindings() { // Every time we switch between graphics and compute work, @@ -121,6 +190,9 @@ namespace Ryujinx.Graphics.Gpu.Image _gpBindingsManager.Rebind(); } + /// <summary> + /// Commits bindings on the graphics pipeline. + /// </summary> public void CommitGraphicsBindings() { _gpBindingsManager.CommitBindings(); @@ -128,11 +200,21 @@ namespace Ryujinx.Graphics.Gpu.Image UpdateRenderTargets(); } + /// <summary> + /// Gets a texture descriptor used on the graphics pipeline. + /// </summary> + /// <param name="state">Current GPU state</param> + /// <param name="stageIndex">Index of the shader stage where the texture is bound</param> + /// <param name="handle">Shader "fake" handle of the texture</param> + /// <returns>The texture descriptor</returns> public TextureDescriptor GetGraphicsTextureDescriptor(GpuState state, int stageIndex, int handle) { return _gpBindingsManager.GetTextureDescriptor(state, stageIndex, handle); } + /// <summary> + /// Update host framebuffer attachments based on currently bound render target buffers. + /// </summary> private void UpdateRenderTargets() { bool anyChanged = false; @@ -162,6 +244,11 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Tries to find a existing texture, or create a new one if not found. + /// </summary> + /// <param name="copyTexture">Copy texture to find or create</param> + /// <returns>The texture</returns> public Texture FindOrCreateTexture(CopyTexture copyTexture) { ulong address = _context.MemoryManager.Translate(copyTexture.Address.Pack()); @@ -210,6 +297,13 @@ namespace Ryujinx.Graphics.Gpu.Image return texture; } + /// <summary> + /// Tries to find a 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()); @@ -286,6 +380,14 @@ namespace Ryujinx.Graphics.Gpu.Image return texture; } + /// <summary> + /// Tries to find a existing texture, or create a new one if not found. + /// </summary> + /// <param name="dsState">Depth-stencil buffer texture to find or create</param> + /// <param name="size">Size of the depth-stencil texture</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(RtDepthStencilState dsState, Size3D size, int samplesInX, int samplesInY) { ulong address = _context.MemoryManager.Translate(dsState.Address.Pack()); @@ -327,6 +429,12 @@ namespace Ryujinx.Graphics.Gpu.Image return texture; } + /// <summary> + /// Tries to find a 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; @@ -480,6 +588,9 @@ namespace Ryujinx.Graphics.Gpu.Image return texture; } + /// <summary> + /// Resizes the temporary buffer used for range list intersection results, if it has grown too much. + /// </summary> private void ShrinkOverlapsBufferIfNeeded() { if (_textureOverlaps.Length > OverlapsBufferMaxCapacity) @@ -488,6 +599,14 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Adjusts the size of the texture information for a given mipmap level, + /// based on the size of a parent texture. + /// </summary> + /// <param name="parent">The parent texture</param> + /// <param name="info">The texture information to be adjusted</param> + /// <param name="firstLevel">The first level of the texture view</param> + /// <returns>The adjusted texture information with the new size</returns> private static TextureInfo AdjustSizes(Texture parent, TextureInfo info, int firstLevel) { // When the texture is used as view of another texture, we must @@ -551,6 +670,14 @@ namespace Ryujinx.Graphics.Gpu.Image info.SwizzleA); } + + /// <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> + /// <returns>The texture creation information</returns> public static TextureCreateInfo GetCreateInfo(TextureInfo info, Capabilities caps) { FormatInfo formatInfo = info.FormatInfo; @@ -590,6 +717,9 @@ namespace Ryujinx.Graphics.Gpu.Image info.SwizzleA); } + /// <summary> + /// Flushes all the textures in the cache that have been modified since the last call. + /// </summary> public void Flush() { foreach (Texture texture in _cache) @@ -603,6 +733,11 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Flushes textures in the cache inside a given range that have been modified since the last call. + /// </summary> + /// <param name="address">The range start address</param> + /// <param name="size">The range size</param> public void Flush(ulong address, ulong size) { foreach (Texture texture in _cache) @@ -616,6 +751,12 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Removes a texture from the cache. + /// This only removes the texture from the internal list, not from the auto-deletion cache. + /// It may still have live references after the removal. + /// </summary> + /// <param name="texture">The texture to be removed</param> public void RemoveTextureFromCache(Texture texture) { _textures.Remove(texture); diff --git a/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs b/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs index 134210671a..0461888f11 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs @@ -1,5 +1,8 @@ namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Multisampled texture samples count. + /// </summary> enum TextureMsaaMode { Ms1x1 = 0, @@ -11,43 +14,55 @@ namespace Ryujinx.Graphics.Gpu.Image static class TextureMsaaModeConverter { + /// <summary> + /// Returns the total number of samples from the MSAA mode. + /// </summary> + /// <param name="msaaMode">The MSAA mode</param> + /// <returns>The total number of samples</returns> public static int SamplesCount(this TextureMsaaMode msaaMode) { - switch (msaaMode) + return msaaMode switch { - case TextureMsaaMode.Ms2x1: return 2; - case TextureMsaaMode.Ms2x2: return 4; - case TextureMsaaMode.Ms4x2: return 8; - case TextureMsaaMode.Ms4x4: return 16; - } - - return 1; + TextureMsaaMode.Ms2x1 => 2, + TextureMsaaMode.Ms2x2 => 4, + TextureMsaaMode.Ms4x2 => 8, + TextureMsaaMode.Ms4x4 => 16, + _ => 1 + }; } + /// <summary> + /// Returns the number of samples in the X direction from the MSAA mode. + /// </summary> + /// <param name="msaaMode">The MSAA mode</param> + /// <returns>The number of samples in the X direction</returns> public static int SamplesInX(this TextureMsaaMode msaaMode) { - switch (msaaMode) + return msaaMode switch { - case TextureMsaaMode.Ms2x1: return 2; - case TextureMsaaMode.Ms2x2: return 2; - case TextureMsaaMode.Ms4x2: return 4; - case TextureMsaaMode.Ms4x4: return 4; - } - - return 1; + TextureMsaaMode.Ms2x1 => 2, + TextureMsaaMode.Ms2x2 => 2, + TextureMsaaMode.Ms4x2 => 4, + TextureMsaaMode.Ms4x4 => 4, + _ => 1 + }; } + /// <summary> + /// Returns the number of samples in the Y direction from the MSAA mode. + /// </summary> + /// <param name="msaaMode">The MSAA mode</param> + /// <returns>The number of samples in the Y direction</returns> public static int SamplesInY(this TextureMsaaMode msaaMode) { - switch (msaaMode) + return msaaMode switch { - case TextureMsaaMode.Ms2x1: return 1; - case TextureMsaaMode.Ms2x2: return 2; - case TextureMsaaMode.Ms4x2: return 2; - case TextureMsaaMode.Ms4x4: return 4; - } - - return 1; + TextureMsaaMode.Ms2x1 => 1, + TextureMsaaMode.Ms2x2 => 2, + TextureMsaaMode.Ms4x2 => 2, + TextureMsaaMode.Ms4x4 => 4, + _ => 1 + }; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index f6fa50692c..91f6338f33 100644 --- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -7,17 +7,31 @@ using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Texture pool. + /// </summary> class TexturePool : Pool<Texture> { - public LinkedListNode<TexturePool> CacheNode { get; set; } - private int _sequenceNumber; - public TexturePool( - GpuContext context, - ulong address, - int maximumId) : base(context, address, maximumId) { } + /// <summary> + /// Intrusive linked list node used on the texture pool cache. + /// </summary> + public LinkedListNode<TexturePool> CacheNode { get; set; } + /// <summary> + /// Constructs a new instance of the texture pool. + /// </summary> + /// <param name="context">GPU context that the texture pool belongs to</param> + /// <param name="address">Address of the texture pool in guest memory</param> + /// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param> + public TexturePool(GpuContext context, ulong address, int maximumId) : base(context, address, maximumId) { } + + /// <summary> + /// Gets the texture with the given ID. + /// </summary> + /// <param name="id">ID of the texture. This is effectively a zero-based index</param> + /// <returns>The texture with the given ID</returns> public override Texture Get(int id) { if ((uint)id >= Items.Length) @@ -62,6 +76,11 @@ namespace Ryujinx.Graphics.Gpu.Image return texture; } + /// <summary> + /// Gets the texture descriptor from a given texture ID. + /// </summary> + /// <param name="id">ID of the texture. This is effectively a zero-based index</param> + /// <returns>The texture descriptor</returns> public TextureDescriptor GetDescriptor(int id) { ulong address = Address + (ulong)(uint)id * DescriptorSize; @@ -71,6 +90,11 @@ namespace Ryujinx.Graphics.Gpu.Image return MemoryMarshal.Cast<byte, TextureDescriptor>(data)[0]; } + /// <summary> + /// Implementation of the texture pool range invalidation. + /// </summary> + /// <param name="address">Start address of the range of the texture pool</param> + /// <param name="size">Size of the range being invalidated</param> protected override void InvalidateRangeImpl(ulong address, ulong size) { ulong endAddress = address + size; @@ -101,6 +125,11 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Gets texture information from a texture descriptor. + /// </summary> + /// <param name="descriptor">The texture descriptor</param> + /// <returns>The texture information</returns> private TextureInfo GetInfo(TextureDescriptor descriptor) { ulong address = Context.MemoryManager.Translate(descriptor.UnpackAddress()); @@ -172,6 +201,13 @@ namespace Ryujinx.Graphics.Gpu.Image swizzleA); } + /// <summary> + /// Gets the texture depth-stencil mode, based on the swizzle components of each color channel. + /// The depth-stencil mode is determined based on how the driver sets those parameters. + /// </summary> + /// <param name="format">The format of the texture</param> + /// <param name="components">The texture swizzle components</param> + /// <returns>The depth-stencil mode</returns> private static DepthStencilMode GetDepthStencilMode(Format format, params SwizzleComponent[] components) { // R = Depth, G = Stencil. @@ -205,12 +241,22 @@ namespace Ryujinx.Graphics.Gpu.Image } } + /// <summary> + /// Checks if the swizzle component is equal to the red or green channels. + /// </summary> + /// <param name="component">The swizzle component to check</param> + /// <returns>True if the swizzle component is equal to the red or blue, false otherwise</returns> private static bool IsRG(SwizzleComponent component) { return component == SwizzleComponent.Red || component == SwizzleComponent.Green; } + /// <summary> + /// Decrements the reference count of the texture. + /// This indicates that the texture pool is not using it anymore. + /// </summary> + /// <param name="item">The texture to be deleted</param> protected override void Delete(Texture item) { item?.DecrementReferenceCount(); diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs b/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs index 9ab7e292d9..6c249ca65b 100644 --- a/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs @@ -2,6 +2,11 @@ using System.Collections.Generic; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Texture pool cache. + /// This can keep multiple texture pools, and return the current one as needed. + /// It is useful for applications that uses multiple texture pools. + /// </summary> class TexturePoolCache { private const int MaxCapacity = 4; @@ -10,6 +15,10 @@ namespace Ryujinx.Graphics.Gpu.Image private LinkedList<TexturePool> _pools; + /// <summary> + /// Constructs a new instance of the texture pool. + /// </summary> + /// <param name="context"></param> public TexturePoolCache(GpuContext context) { _context = context; @@ -17,6 +26,12 @@ namespace Ryujinx.Graphics.Gpu.Image _pools = new LinkedList<TexturePool>(); } + /// <summary> + /// Finds a cache texture pool, or creates a new one if not found. + /// </summary> + /// <param name="address">Start address of the texture pool</param> + /// <param name="maximumId">Maximum ID of the texture pool</param> + /// <returns>The found or newly created texture pool</returns> public TexturePool FindOrCreate(ulong address, int maximumId) { TexturePool pool; @@ -58,6 +73,11 @@ namespace Ryujinx.Graphics.Gpu.Image return pool; } + /// <summary> + /// Invalidates a memory range of all intersecting texture pools on the cache. + /// </summary> + /// <param name="address">Start address of the range to invalidate</param> + /// <param name="size">Size of the range to invalidate</param> public void InvalidateRange(ulong address, ulong size) { for (LinkedListNode<TexturePool> node = _pools.First; node != null; node = node.Next) diff --git a/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs b/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs index a5c951b539..daf726f1d2 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs @@ -2,6 +2,9 @@ using System; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Texture search flags, defines texture information comparison rules. + /// </summary> [Flags] enum TextureSearchFlags { diff --git a/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs b/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs index 96a814c62b..301fc87b2b 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs @@ -2,6 +2,9 @@ using Ryujinx.Graphics.GAL; namespace Ryujinx.Graphics.Gpu.Image { + /// <summary> + /// Texture target. + /// </summary> enum TextureTarget { Texture1D, @@ -17,6 +20,12 @@ namespace Ryujinx.Graphics.Gpu.Image static class TextureTargetConverter { + /// <summary> + /// Converts the texture target enum to a host compatible, Graphics Abstraction Layer enum. + /// </summary> + /// <param name="target">The target enum to convert</param> + /// <param name="isMultisample">True if the texture is a multisampled texture</param> + /// <returns>The host compatible texture target</returns> public static Target Convert(this TextureTarget target, bool isMultisample) { if (isMultisample)