From 3cb1fa0e853efc04cc183d3ee75ec1bbe2c845a4 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 25 Apr 2020 10:02:18 -0300 Subject: [PATCH] Implement texture buffers (#1152) * Implement texture buffers * Throw NotSupportedException where appropriate --- Ryujinx.Graphics.GAL/ITexture.cs | 1 + Ryujinx.Graphics.Gpu/Engine/MethodReport.cs | 2 - Ryujinx.Graphics.Gpu/Engine/Methods.cs | 2 +- Ryujinx.Graphics.Gpu/Image/Texture.cs | 4 +- .../Image/TextureBindingsManager.cs | 25 +++--- Ryujinx.Graphics.Gpu/Image/TextureManager.cs | 6 +- Ryujinx.Graphics.Gpu/Memory/BufferManager.cs | 50 +++++++++-- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 71 +++++++-------- Ryujinx.Graphics.OpenGL/Pipeline.cs | 12 +-- Ryujinx.Graphics.OpenGL/Renderer.cs | 7 +- Ryujinx.Graphics.OpenGL/TextureBase.cs | 39 +++++++++ Ryujinx.Graphics.OpenGL/TextureBuffer.cs | 71 +++++++++++++++ Ryujinx.Graphics.OpenGL/TextureView.cs | 86 +++++++------------ .../Decoders/OpCodeTable.cs | 2 +- .../Instructions/InstEmitTexture.cs | 28 +++++- Ryujinx.Graphics.Texture/SizeInfo.cs | 20 +++-- 16 files changed, 291 insertions(+), 135 deletions(-) create mode 100644 Ryujinx.Graphics.OpenGL/TextureBase.cs create mode 100644 Ryujinx.Graphics.OpenGL/TextureBuffer.cs diff --git a/Ryujinx.Graphics.GAL/ITexture.cs b/Ryujinx.Graphics.GAL/ITexture.cs index 5278e3b74d..a818f73aad 100644 --- a/Ryujinx.Graphics.GAL/ITexture.cs +++ b/Ryujinx.Graphics.GAL/ITexture.cs @@ -12,5 +12,6 @@ namespace Ryujinx.Graphics.GAL byte[] GetData(); void SetData(ReadOnlySpan data); + void SetStorage(BufferRange buffer); } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs b/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs index eeec3569cb..997f55ff2d 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs @@ -12,8 +12,6 @@ namespace Ryujinx.Graphics.Gpu.Engine private const int NsToTicksFractionNumerator = 384; private const int NsToTicksFractionDenominator = 625; - private ulong _runningCounter; - private readonly CounterCache _counterCache = new CounterCache(); /// diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index 7bc8501864..ddb6e052a5 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs @@ -259,7 +259,7 @@ namespace Ryujinx.Graphics.Gpu.Engine { UpdateStorageBuffers(); - BufferManager.CommitBindings(); + BufferManager.CommitGraphicsBindings(); TextureManager.CommitGraphicsBindings(); } diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index ba7dce7bf5..957c3465d7 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -295,7 +295,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// public void SynchronizeMemory() { - if (_sequenceNumber == _context.SequenceNumber && _hasData) + // Texture buffers are not handled here, instead they are invalidated (if modified) + // when the texture is bound. This is handled by the buffer manager. + if ((_sequenceNumber == _context.SequenceNumber && _hasData) || Info.Target == Target.TextureBuffer) { return; } diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index 7cc7f04688..612ec5ca8f 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -69,7 +69,6 @@ namespace Ryujinx.Graphics.Gpu.Image public void SetTextures(int stage, TextureBindingInfo[] bindings) { _textureBindings[stage] = bindings; - _textureState[stage] = new TextureStatePerStage[bindings.Length]; } @@ -81,7 +80,6 @@ namespace Ryujinx.Graphics.Gpu.Image public void SetImages(int stage, TextureBindingInfo[] bindings) { _imageBindings[stage] = bindings; - _imageState[stage] = new TextureStatePerStage[bindings.Length]; } @@ -201,7 +199,7 @@ namespace Ryujinx.Graphics.Gpu.Image } else { - packedId = ReadPackedId(stageIndex, binding.Handle); + packedId = ReadPackedId(stageIndex, binding.Handle, _textureBufferIndex); } int textureId = UnpackTextureId(packedId); @@ -227,6 +225,14 @@ namespace Ryujinx.Graphics.Gpu.Image _context.Renderer.Pipeline.SetTexture(index, stage, hostTexture); } + if (hostTexture != null && texture.Info.Target == Target.TextureBuffer) + { + // Ensure that the buffer texture is using the correct buffer as storage. + // Buffers are frequently re-created to accomodate larger data, so we need to re-bind + // to ensure we're not using a old buffer that was already deleted. + _context.Methods.BufferManager.SetBufferTextureStorage(hostTexture, texture.Address, texture.Size, _isCompute); + } + Sampler sampler = _samplerPool.Get(samplerId); ISampler hostSampler = sampler?.HostSampler; @@ -258,8 +264,7 @@ namespace Ryujinx.Graphics.Gpu.Image { TextureBindingInfo binding = _imageBindings[stageIndex][index]; - int packedId = ReadPackedId(stageIndex, binding.Handle); - + int packedId = ReadPackedId(stageIndex, binding.Handle, _textureBufferIndex); int textureId = UnpackTextureId(packedId); Texture texture = pool.Get(textureId); @@ -284,8 +289,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// The texture descriptor for the specified texture public TextureDescriptor GetTextureDescriptor(GpuState state, int stageIndex, int handle) { - int packedId = ReadPackedId(stageIndex, handle); - + int packedId = ReadPackedId(stageIndex, handle, state.Get(MethodOffset.TextureBufferIndex)); int textureId = UnpackTextureId(packedId); var poolState = state.Get(MethodOffset.TexturePoolState); @@ -303,8 +307,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// The number of the shader stage where the texture is bound /// A word offset of the handle on the buffer (the "fake" shader handle) + /// Index of the constant buffer holding the texture handles /// The packed texture and sampler ID (the real texture handle) - private int ReadPackedId(int stageIndex, int wordOffset) + private int ReadPackedId(int stageIndex, int wordOffset, int textureBufferIndex) { ulong address; @@ -312,11 +317,11 @@ namespace Ryujinx.Graphics.Gpu.Image if (_isCompute) { - address = bufferManager.GetComputeUniformBufferAddress(_textureBufferIndex); + address = bufferManager.GetComputeUniformBufferAddress(textureBufferIndex); } else { - address = bufferManager.GetGraphicsUniformBufferAddress(stageIndex, _textureBufferIndex); + address = bufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex); } address += (uint)wordOffset * 4; diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index b127690ba9..600e2f5bc7 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -489,7 +489,11 @@ namespace Ryujinx.Graphics.Gpu.Image // Calculate texture sizes, used to find all overlapping textures. SizeInfo sizeInfo; - if (info.IsLinear) + if (info.Target == Target.TextureBuffer) + { + sizeInfo = new SizeInfo(info.Width * info.FormatInfo.BytesPerPixel); + } + else if (info.IsLinear) { sizeInfo = SizeCalculator.GetLinearTextureSize( info.Stride, diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 0acbd94ad0..2fe0ecbb27 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -273,6 +273,19 @@ namespace Ryujinx.Graphics.Gpu.Memory return 0; } + CreateBuffer(address, size); + + return address; + } + + /// + /// Creates a new buffer for the specified range, if it does not yet exist. + /// This can be used to ensure the existance of a buffer. + /// + /// Address of the buffer in memory + /// Size of the buffer in bytes + public void CreateBuffer(ulong address, ulong size) + { ulong endAddress = address + size; ulong alignedAddress = address & ~BufferAlignmentMask; @@ -285,9 +298,7 @@ namespace Ryujinx.Graphics.Gpu.Memory alignedEndAddress += BufferAlignmentSize; } - CreateBuffer(alignedAddress, alignedEndAddress - alignedAddress); - - return address; + CreateBufferAligned(alignedAddress, alignedEndAddress - alignedAddress); } /// @@ -297,7 +308,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Address of the buffer in guest memory /// Size in bytes of the buffer - private void CreateBuffer(ulong address, ulong size) + private void CreateBufferAligned(ulong address, ulong size) { int overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps); @@ -339,6 +350,7 @@ namespace Ryujinx.Graphics.Gpu.Memory buffer.Dispose(); } + // Existing buffers were modified, we need to rebind everything. _rebind = true; } } @@ -441,7 +453,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Ensures that the graphics engine bindings are visible to the host GPU. /// Note: this actually performs the binding using the host graphics API. /// - public void CommitBindings() + public void CommitGraphicsBindings() { if (_indexBufferDirty || _rebind) { @@ -606,6 +618,34 @@ namespace Ryujinx.Graphics.Gpu.Memory } } + /// + /// Sets the buffer storage of a buffer texture. + /// + /// Buffer texture + /// Address of the buffer in memory + /// Size of the buffer in bytes + /// Indicates if the buffer texture belongs to the compute or graphics pipeline + public void SetBufferTextureStorage(ITexture texture, ulong address, ulong size, bool compute) + { + CreateBuffer(address, size); + + if (_rebind) + { + // We probably had to modify existing buffers to create the texture buffer, + // so rebind everything to ensure we're using the new buffers for all bound resources. + if (compute) + { + CommitComputeBindings(); + } + else + { + CommitGraphicsBindings(); + } + } + + texture.SetStorage(GetBufferRange(address, size)); + } + /// /// Copy a buffer data from a given address to another. /// diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 6bbc3b1176..d71440634b 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -280,8 +280,12 @@ namespace Ryujinx.Graphics.Gpu.Shader => localMemorySize, QueryInfoName.ComputeSharedMemorySize => sharedMemorySize, + QueryInfoName.IsTextureBuffer + => Convert.ToInt32(QueryIsTextureBuffer(state, 0, index, compute: true)), + QueryInfoName.IsTextureRectangle + => Convert.ToInt32(QueryIsTextureRectangle(state, 0, index, compute: true)), QueryInfoName.TextureFormat - => (int)QueryComputeTextureFormat(state, index), + => (int)QueryTextureFormat(state, 0, index, compute: true), _ => QueryInfoCommon(info) }; @@ -331,13 +335,13 @@ namespace Ryujinx.Graphics.Gpu.Shader return info switch { QueryInfoName.IsTextureBuffer - => Convert.ToInt32(QueryIsTextureBuffer(state, (int)stage - 1, index)), + => Convert.ToInt32(QueryIsTextureBuffer(state, (int)stage - 1, index, compute: false)), QueryInfoName.IsTextureRectangle - => Convert.ToInt32(QueryIsTextureRectangle(state, (int)stage - 1, index)), + => Convert.ToInt32(QueryIsTextureRectangle(state, (int)stage - 1, index, compute: false)), QueryInfoName.PrimitiveTopology => (int)QueryPrimitiveTopology(), QueryInfoName.TextureFormat - => (int)QueryGraphicsTextureFormat(state, (int)stage - 1, index), + => (int)QueryTextureFormat(state, (int)stage - 1, index, compute: false), _ => QueryInfoCommon(info) }; @@ -429,11 +433,12 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Current GPU state /// Index of the shader stage - /// Index of the texture (this is the shader "fake" handle) + /// Index of the texture (this is the shader "fake" handle) + /// Indicates whenever the texture descriptor is for the compute or graphics engine /// True if the texture is a buffer texture, false otherwise - private bool QueryIsTextureBuffer(GpuState state, int stageIndex, int index) + private bool QueryIsTextureBuffer(GpuState state, int stageIndex, int handle, bool compute) { - return GetGraphicsTextureDescriptor(state, stageIndex, index).UnpackTextureTarget() == TextureTarget.TextureBuffer; + return GetTextureDescriptor(state, stageIndex, handle, compute).UnpackTextureTarget() == TextureTarget.TextureBuffer; } /// @@ -443,11 +448,12 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Current GPU state /// Index of the shader stage - /// Index of the texture (this is the shader "fake" handle) + /// Index of the texture (this is the shader "fake" handle) + /// Indicates whenever the texture descriptor is for the compute or graphics engine /// True if the texture is a rectangle texture, false otherwise - private bool QueryIsTextureRectangle(GpuState state, int stageIndex, int index) + private bool QueryIsTextureRectangle(GpuState state, int stageIndex, int handle, bool compute) { - var descriptor = GetGraphicsTextureDescriptor(state, stageIndex, index); + var descriptor = GetTextureDescriptor(state, stageIndex, handle, compute); TextureTarget target = descriptor.UnpackTextureTarget(); @@ -461,23 +467,13 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Queries the format of a given texture. /// /// Current GPU state - /// Index of the texture (this is the shader "fake" handle) + /// Index of the shader stage. This is ignored if is true + /// Index of the texture (this is the shader "fake" handle) + /// Indicates whenever the texture descriptor is for the compute or graphics engine /// The texture format - private TextureFormat QueryComputeTextureFormat(GpuState state, int index) + private TextureFormat QueryTextureFormat(GpuState state, int stageIndex, int handle, bool compute) { - return QueryTextureFormat(GetComputeTextureDescriptor(state, index)); - } - - /// - /// Queries the format of a given texture. - /// - /// Current GPU state - /// Index of the shader stage - /// Index of the texture (this is the shader "fake" handle) - /// The texture format - private TextureFormat QueryGraphicsTextureFormat(GpuState state, int stageIndex, int index) - { - return QueryTextureFormat(GetGraphicsTextureDescriptor(state, stageIndex, index)); + return QueryTextureFormat(GetTextureDescriptor(state, stageIndex, handle, compute)); } /// @@ -541,23 +537,20 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Gets the texture descriptor for a given texture on the pool. /// /// Current GPU state + /// Index of the shader stage. This is ignored if is true /// Index of the texture (this is the shader "fake" handle) + /// Indicates whenever the texture descriptor is for the compute or graphics engine /// Texture descriptor - private TextureDescriptor GetComputeTextureDescriptor(GpuState state, int handle) + private TextureDescriptor GetTextureDescriptor(GpuState state, int stageIndex, int handle, bool compute) { - return _context.Methods.TextureManager.GetComputeTextureDescriptor(state, handle); - } - - /// - /// Gets the texture descriptor for a given texture on the pool. - /// - /// Current GPU state - /// Index of the shader stage - /// Index of the texture (this is the shader "fake" handle) - /// Texture descriptor - private TextureDescriptor GetGraphicsTextureDescriptor(GpuState state, int stageIndex, int handle) - { - return _context.Methods.TextureManager.GetGraphicsTextureDescriptor(state, stageIndex, handle); + if (compute) + { + return _context.Methods.TextureManager.GetComputeTextureDescriptor(state, handle); + } + else + { + return _context.Methods.TextureManager.GetGraphicsTextureDescriptor(state, stageIndex, handle); + } } /// diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index eec2b6432a..e313595d67 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Graphics.OpenGL private bool _depthTest; private bool _hasDepthBuffer; - private TextureView _unit0Texture; + private TextureBase _unit0Texture; private ClipOrigin _clipOrigin; private ClipDepthMode _clipDepthMode; @@ -616,13 +616,13 @@ namespace Ryujinx.Graphics.OpenGL if (unit != -1 && texture != null) { - TextureView view = (TextureView)texture; + TextureBase texBase = (TextureBase)texture; - FormatInfo formatInfo = FormatTable.GetFormatInfo(view.Format); + FormatInfo formatInfo = FormatTable.GetFormatInfo(texBase.Format); SizedInternalFormat format = (SizedInternalFormat)formatInfo.PixelInternalFormat; - GL.BindImageTexture(unit, view.Handle, 0, true, 0, TextureAccess.ReadWrite, format); + GL.BindImageTexture(unit, texBase.Handle, 0, true, 0, TextureAccess.ReadWrite, format); } } @@ -801,11 +801,11 @@ namespace Ryujinx.Graphics.OpenGL { if (unit == 0) { - _unit0Texture = ((TextureView)texture); + _unit0Texture = (TextureBase)texture; } else { - ((TextureView)texture).Bind(unit); + ((TextureBase)texture).Bind(unit); } } } diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs index 504a947bee..3f7183458b 100644 --- a/Ryujinx.Graphics.OpenGL/Renderer.cs +++ b/Ryujinx.Graphics.OpenGL/Renderer.cs @@ -20,19 +20,14 @@ namespace Ryujinx.Graphics.OpenGL internal TextureCopy TextureCopy { get; } public string GpuVendor { get; private set; } - public string GpuRenderer { get; private set; } - public string GpuVersion { get; private set; } public Renderer() { _pipeline = new Pipeline(); - _counters = new Counters(); - _window = new Window(this); - TextureCopy = new TextureCopy(this); } @@ -58,7 +53,7 @@ namespace Ryujinx.Graphics.OpenGL public ITexture CreateTexture(TextureCreateInfo info) { - return new TextureStorage(this, info).CreateDefaultView(); + return info.Target == Target.TextureBuffer ? new TextureBuffer(info) : new TextureStorage(this, info).CreateDefaultView(); } public Capabilities GetCapabilities() diff --git a/Ryujinx.Graphics.OpenGL/TextureBase.cs b/Ryujinx.Graphics.OpenGL/TextureBase.cs new file mode 100644 index 0000000000..f4ab0bda42 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/TextureBase.cs @@ -0,0 +1,39 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Ryujinx.Graphics.OpenGL +{ + class TextureBase + { + public int Handle { get; protected set; } + + protected TextureCreateInfo Info { get; } + + public int Width => Info.Width; + public int Height => Info.Height; + + public Target Target => Info.Target; + public Format Format => Info.Format; + + public TextureBase(TextureCreateInfo info) + { + Info = info; + + Handle = GL.GenTexture(); + } + + public void Bind(int unit) + { + Bind(Target.Convert(), unit); + } + + protected void Bind(TextureTarget target, int unit) + { + GL.ActiveTexture(TextureUnit.Texture0 + unit); + GL.BindTexture(target, Handle); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/TextureBuffer.cs b/Ryujinx.Graphics.OpenGL/TextureBuffer.cs new file mode 100644 index 0000000000..fb18c6ee75 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/TextureBuffer.cs @@ -0,0 +1,71 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + class TextureBuffer : TextureBase, ITexture + { + private int _bufferOffset; + private int _bufferSize; + + private Buffer _buffer; + + public TextureBuffer(TextureCreateInfo info) : base(info) {} + + public void CopyTo(ITexture destination, int firstLayer, int firstLevel) + { + throw new NotSupportedException(); + } + + public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) + { + throw new NotSupportedException(); + } + + public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) + { + throw new NotSupportedException(); + } + + public byte[] GetData() + { + return _buffer?.GetData(_bufferOffset, _bufferSize); + } + + public void SetData(ReadOnlySpan data) + { + _buffer?.SetData(_bufferOffset, data.Slice(0, Math.Min(data.Length, _bufferSize))); + } + + public void SetStorage(BufferRange buffer) + { + if (buffer.Buffer == _buffer && + buffer.Offset == _bufferOffset && + buffer.Size == _bufferSize) + { + return; + } + + _buffer = (Buffer)buffer.Buffer; + _bufferOffset = buffer.Offset; + _bufferSize = buffer.Size; + + Bind(0); + + SizedInternalFormat format = (SizedInternalFormat)FormatTable.GetFormatInfo(Info.Format).PixelInternalFormat; + + GL.TexBufferRange(TextureBufferTarget.TextureBuffer, format, _buffer.Handle, (IntPtr)buffer.Offset, buffer.Size); + } + + public void Dispose() + { + if (Handle != 0) + { + GL.DeleteTexture(Handle); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/TextureView.cs b/Ryujinx.Graphics.OpenGL/TextureView.cs index cb872880ac..0ab59d4257 100644 --- a/Ryujinx.Graphics.OpenGL/TextureView.cs +++ b/Ryujinx.Graphics.OpenGL/TextureView.cs @@ -4,10 +4,8 @@ using System; namespace Ryujinx.Graphics.OpenGL { - class TextureView : ITexture + class TextureView : TextureBase, ITexture { - public int Handle { get; private set; } - private readonly Renderer _renderer; private readonly TextureStorage _parent; @@ -16,33 +14,22 @@ namespace Ryujinx.Graphics.OpenGL private TextureView _incompatibleFormatView; - private readonly TextureCreateInfo _info; - public int FirstLayer { get; private set; } public int FirstLevel { get; private set; } - public int Width => _info.Width; - public int Height => _info.Height; - - public Target Target => _info.Target; - public Format Format => _info.Format; - public TextureView( Renderer renderer, TextureStorage parent, TextureCreateInfo info, int firstLayer, - int firstLevel) + int firstLevel) : base(info) { _renderer = renderer; _parent = parent; - _info = info; FirstLayer = firstLayer; FirstLevel = firstLevel; - Handle = GL.GenTexture(); - CreateView(); } @@ -50,7 +37,7 @@ namespace Ryujinx.Graphics.OpenGL { TextureTarget target = Target.Convert(); - FormatInfo format = FormatTable.GetFormatInfo(_info.Format); + FormatInfo format = FormatTable.GetFormatInfo(Info.Format); PixelInternalFormat pixelInternalFormat; @@ -69,9 +56,9 @@ namespace Ryujinx.Graphics.OpenGL _parent.Handle, pixelInternalFormat, FirstLevel, - _info.Levels, + Info.Levels, FirstLayer, - _info.GetLayers()); + Info.GetLayers()); GL.ActiveTexture(TextureUnit.Texture0); @@ -79,15 +66,15 @@ namespace Ryujinx.Graphics.OpenGL int[] swizzleRgba = new int[] { - (int)_info.SwizzleR.Convert(), - (int)_info.SwizzleG.Convert(), - (int)_info.SwizzleB.Convert(), - (int)_info.SwizzleA.Convert() + (int)Info.SwizzleR.Convert(), + (int)Info.SwizzleG.Convert(), + (int)Info.SwizzleB.Convert(), + (int)Info.SwizzleA.Convert() }; GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba); - int maxLevel = _info.Levels - 1; + int maxLevel = Info.Levels - 1; if (maxLevel < 0) { @@ -95,12 +82,12 @@ namespace Ryujinx.Graphics.OpenGL } GL.TexParameter(target, TextureParameterName.TextureMaxLevel, maxLevel); - GL.TexParameter(target, TextureParameterName.DepthStencilTextureMode, (int)_info.DepthStencilMode.Convert()); + GL.TexParameter(target, TextureParameterName.DepthStencilTextureMode, (int)Info.DepthStencilMode.Convert()); } public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) { - if (_info.IsCompressed == info.IsCompressed) + if (Info.IsCompressed == info.IsCompressed) { firstLayer += FirstLayer; firstLevel += FirstLevel; @@ -135,10 +122,10 @@ namespace Ryujinx.Graphics.OpenGL { if (_incompatibleFormatView == null) { - _incompatibleFormatView = (TextureView)_renderer.CreateTexture(_info); + _incompatibleFormatView = (TextureView)_renderer.CreateTexture(Info); } - TextureCopyUnscaled.Copy(_parent.Info, _incompatibleFormatView._info, _parent.Handle, _incompatibleFormatView.Handle, FirstLayer, 0, FirstLevel, 0); + TextureCopyUnscaled.Copy(_parent.Info, _incompatibleFormatView.Info, _parent.Handle, _incompatibleFormatView.Handle, FirstLayer, 0, FirstLevel, 0); return _incompatibleFormatView.Handle; } @@ -150,7 +137,7 @@ namespace Ryujinx.Graphics.OpenGL { if (_incompatibleFormatView != null) { - TextureCopyUnscaled.Copy(_incompatibleFormatView._info, _parent.Info, _incompatibleFormatView.Handle, _parent.Handle, 0, FirstLayer, 0, FirstLevel); + TextureCopyUnscaled.Copy(_incompatibleFormatView.Info, _parent.Info, _incompatibleFormatView.Handle, _parent.Handle, 0, FirstLayer, 0, FirstLevel); } } @@ -158,13 +145,13 @@ namespace Ryujinx.Graphics.OpenGL { TextureView destinationView = (TextureView)destination; - TextureCopyUnscaled.Copy(_info, destinationView._info, Handle, destinationView.Handle, 0, firstLayer, 0, firstLevel); + TextureCopyUnscaled.Copy(Info, destinationView.Info, Handle, destinationView.Handle, 0, firstLayer, 0, firstLevel); if (destinationView._emulatedViewParent != null) { TextureCopyUnscaled.Copy( - _info, - destinationView._emulatedViewParent._info, + Info, + destinationView._emulatedViewParent.Info, Handle, destinationView._emulatedViewParent.Handle, 0, @@ -183,9 +170,9 @@ namespace Ryujinx.Graphics.OpenGL { int size = 0; - for (int level = 0; level < _info.Levels; level++) + for (int level = 0; level < Info.Levels; level++) { - size += _info.GetMipSize(level); + size += Info.GetMipSize(level); } byte[] data = new byte[size]; @@ -207,7 +194,7 @@ namespace Ryujinx.Graphics.OpenGL Bind(target, 0); - FormatInfo format = FormatTable.GetFormatInfo(_info.Format); + FormatInfo format = FormatTable.GetFormatInfo(Info.Format); int faces = 1; @@ -218,11 +205,11 @@ namespace Ryujinx.Graphics.OpenGL faces = 6; } - for (int level = 0; level < _info.Levels; level++) + for (int level = 0; level < Info.Levels; level++) { for (int face = 0; face < faces; face++) { - int faceOffset = face * _info.GetMipSize2D(level); + int faceOffset = face * Info.GetMipSize2D(level); if (format.IsCompressed) { @@ -239,7 +226,7 @@ namespace Ryujinx.Graphics.OpenGL } } - ptr += _info.GetMipSize(level); + ptr += Info.GetMipSize(level); } } @@ -260,17 +247,17 @@ namespace Ryujinx.Graphics.OpenGL Bind(target, 0); - FormatInfo format = FormatTable.GetFormatInfo(_info.Format); + FormatInfo format = FormatTable.GetFormatInfo(Info.Format); - int width = _info.Width; - int height = _info.Height; - int depth = _info.Depth; + int width = Info.Width; + int height = Info.Height; + int depth = Info.Depth; int offset = 0; - for (int level = 0; level < _info.Levels; level++) + for (int level = 0; level < Info.Levels; level++) { - int mipSize = _info.GetMipSize(level); + int mipSize = Info.GetMipSize(level); int endOffset = offset + mipSize; @@ -279,7 +266,7 @@ namespace Ryujinx.Graphics.OpenGL return; } - switch (_info.Target) + switch (Info.Target) { case Target.Texture1D: if (format.IsCompressed) @@ -419,16 +406,9 @@ namespace Ryujinx.Graphics.OpenGL } } - public void Bind(int unit) + public void SetStorage(BufferRange buffer) { - Bind(Target.Convert(), unit); - } - - private void Bind(TextureTarget target, int unit) - { - GL.ActiveTexture(TextureUnit.Texture0 + unit); - - GL.BindTexture(target, Handle); + throw new NotSupportedException(); } public void Dispose() diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs index 02a8be0161..dcb6b1f6a9 100644 --- a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs @@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("000101xxxxxxxx", InstEmit.Iscadd, typeof(OpCodeAluImm32)); Set("0101110000011x", InstEmit.Iscadd, typeof(OpCodeAluReg)); Set("010010110101xx", InstEmit.Iset, typeof(OpCodeSetCbuf)); - Set("001101100101xx", InstEmit.Iset, typeof(OpCodeSetImm)); + Set("0011011x0101xx", InstEmit.Iset, typeof(OpCodeSetImm)); Set("010110110101xx", InstEmit.Iset, typeof(OpCodeSetReg)); Set("010010110110xx", InstEmit.Isetp, typeof(OpCodeSetCbuf)); Set("0011011x0110xx", InstEmit.Isetp, typeof(OpCodeSetImm)); diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index 4f8100cbb4..18552d0a10 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -376,6 +376,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { case TextureTarget.Texture1DLodZero: sourcesList.Add(Ra()); + sourcesList.Add(ConstF(0)); break; case TextureTarget.Texture2D: @@ -429,11 +430,21 @@ namespace Ryujinx.Graphics.Shader.Instructions flags = ConvertTextureFlags(tldsOp.Target) | TextureFlags.IntCoords; + if (tldsOp.Target == TexelLoadTarget.Texture1DLodZero && context.Config.QueryInfoBool(QueryInfoName.IsTextureBuffer, tldsOp.Immediate)) + { + type = SamplerType.TextureBuffer; + flags &= ~TextureFlags.LodLevel; + } + switch (tldsOp.Target) { case TexelLoadTarget.Texture1DLodZero: sourcesList.Add(Ra()); - sourcesList.Add(Const(0)); + + if (type != SamplerType.TextureBuffer) + { + sourcesList.Add(Const(0)); + } break; case TexelLoadTarget.Texture1DLodLevel: @@ -615,8 +626,7 @@ namespace Ryujinx.Graphics.Shader.Instructions List sourcesList = new List(); - SamplerType type = ConvertSamplerType(op.Dimensions); - + SamplerType type = ConvertSamplerType(op.Dimensions); TextureFlags flags = TextureFlags.Gather; if (op.Bindless) @@ -1008,6 +1018,16 @@ namespace Ryujinx.Graphics.Shader.Instructions type |= SamplerType.Multisample; } + if (type == SamplerType.Texture1D && flags == TextureFlags.IntCoords && !isBindless) + { + bool isTypeBuffer = context.Config.QueryInfoBool(QueryInfoName.IsTextureBuffer, op.Immediate); + + if (isTypeBuffer) + { + type = SamplerType.TextureBuffer; + } + } + Operand[] sources = sourcesList.ToArray(); int rdIndex = op.Rd.Index; @@ -1190,7 +1210,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return SamplerType.None; } - private static TextureFlags ConvertTextureFlags(Decoders.TextureTarget type) + private static TextureFlags ConvertTextureFlags(TextureTarget type) { switch (type) { diff --git a/Ryujinx.Graphics.Texture/SizeInfo.cs b/Ryujinx.Graphics.Texture/SizeInfo.cs index 37d824cc55..b6085e0cb2 100644 --- a/Ryujinx.Graphics.Texture/SizeInfo.cs +++ b/Ryujinx.Graphics.Texture/SizeInfo.cs @@ -1,19 +1,27 @@ -using Ryujinx.Common; using System; namespace Ryujinx.Graphics.Texture { public struct SizeInfo { - private int[] _mipOffsets; - private int[] _allOffsets; + private readonly int[] _mipOffsets; + private readonly int[] _allOffsets; - private int _levels; + private readonly int _levels; public int LayerSize { get; } public int TotalSize { get; } - public SizeInfo( + public SizeInfo(int size) + { + _mipOffsets = new int[] { 0 }; + _allOffsets = new int[] { 0 }; + _levels = 1; + LayerSize = size; + TotalSize = size; + } + + internal SizeInfo( int[] mipOffsets, int[] allOffsets, int levels, @@ -29,7 +37,7 @@ namespace Ryujinx.Graphics.Texture public int GetMipOffset(int level) { - if ((uint)level > _mipOffsets.Length) + if ((uint)level >= _mipOffsets.Length) { throw new ArgumentOutOfRangeException(nameof(level)); }