diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute.cs b/Ryujinx.Graphics.Gpu/Engine/Compute.cs index c7e059ba..bcff5953 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Compute.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Compute.cs @@ -141,8 +141,8 @@ namespace Ryujinx.Graphics.Gpu.Engine TextureManager.SetComputeImages(imageBindings); - BufferManager.CommitComputeBindings(); TextureManager.CommitComputeBindings(); + BufferManager.CommitComputeBindings(); _context.Renderer.Pipeline.DispatchCompute( qmd.CtaRasterWidth, diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index 0731f1c2..033311b2 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs @@ -304,8 +304,8 @@ namespace Ryujinx.Graphics.Gpu.Engine { UpdateStorageBuffers(); - BufferManager.CommitGraphicsBindings(); TextureManager.CommitGraphicsBindings(); + BufferManager.CommitGraphicsBindings(); } /// diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index 173340b3..7fea7ebe 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -303,7 +303,7 @@ namespace Ryujinx.Graphics.Gpu.Image // 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.Range.GetSubRange(0).Address, texture.Size, _isCompute); + _context.Methods.BufferManager.SetBufferTextureStorage(hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, bindingInfo.Format, false); } Sampler sampler = _samplerPool.Get(samplerId); @@ -349,12 +349,26 @@ namespace Ryujinx.Graphics.Gpu.Image ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target); + bool isStore = bindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore); + if (hostTexture != null && texture.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.Range.GetSubRange(0).Address, texture.Size, _isCompute); + + Format format = bindingInfo.Format; + + if (format == 0 && texture != null) + { + format = texture.Format; + } + + _context.Methods.BufferManager.SetBufferTextureStorage(hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, format, true); + } + else if (isStore) + { + texture?.SignalModified(); } if (_imageState[stageIndex][index].Texture != hostTexture || _rebind) diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs index e85df136..62862e74 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs @@ -153,6 +153,15 @@ namespace Ryujinx.Graphics.Gpu.Image return (int)(Word4 & 0xffff) + 1; } + /// + /// Unpack the width of a buffer texture. + /// + /// The texture width + public int UnpackBufferTextureWidth() + { + return (int)((Word4 & 0xffff) | (Word3 << 16)) + 1; + } + /// /// Unpacks the texture sRGB format flag. /// diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index eece2a79..8f225f16 100644 --- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -172,8 +172,6 @@ namespace Ryujinx.Graphics.Gpu.Image /// The texture information private TextureInfo GetInfo(TextureDescriptor descriptor, out int layerSize) { - int width = descriptor.UnpackWidth(); - int height = descriptor.UnpackHeight(); int depthOrLayers = descriptor.UnpackDepth(); int levels = descriptor.UnpackLevels(); @@ -190,6 +188,9 @@ namespace Ryujinx.Graphics.Gpu.Image Target target = descriptor.UnpackTextureTarget().Convert((samplesInX | samplesInY) != 1); + int width = target == Target.TextureBuffer ? descriptor.UnpackBufferTextureWidth() : descriptor.UnpackWidth(); + int height = descriptor.UnpackHeight(); + // We use 2D targets for 1D textures as that makes texture cache // management easier. We don't know the target for render target // and copies, so those would normally use 2D targets, which are diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index 08d52faa..b2cd1ced 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -1,9 +1,11 @@ using Ryujinx.Common; using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.State; using Ryujinx.Graphics.Shader; using Ryujinx.Memory.Range; using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; @@ -31,6 +33,7 @@ namespace Ryujinx.Graphics.Gpu.Memory private IndexBuffer _indexBuffer; private VertexBuffer[] _vertexBuffers; private BufferBounds[] _transformFeedbackBuffers; + private List _bufferTextures; /// /// Holds shader stage buffer state and binding information. @@ -138,6 +141,8 @@ namespace Ryujinx.Graphics.Gpu.Memory _gpStorageBuffers[index] = new BuffersPerStage(Constants.TotalGpStorageBuffers); _gpUniformBuffers[index] = new BuffersPerStage(Constants.TotalGpUniformBuffers); } + + _bufferTextures = new List(); } /// @@ -620,10 +625,39 @@ namespace Ryujinx.Graphics.Gpu.Memory _context.Renderer.Pipeline.SetUniformBuffers(uRanges); + CommitBufferTextureBindings(); + // Force rebind after doing compute work. _rebind = true; } + /// + /// Commit any queued buffer texture bindings. + /// + private void CommitBufferTextureBindings() + { + if (_bufferTextures.Count > 0) + { + foreach (var binding in _bufferTextures) + { + binding.Texture.SetStorage(GetBufferRange(binding.Address, binding.Size, binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore))); + + // The texture must be rebound to use the new storage if it was updated. + + if (binding.IsImage) + { + _context.Renderer.Pipeline.SetImage(binding.BindingInfo.Binding, binding.Texture, binding.Format); + } + else + { + _context.Renderer.Pipeline.SetTexture(binding.BindingInfo.Binding, binding.Texture); + } + } + + _bufferTextures.Clear(); + } + } + /// /// Ensures that the graphics engine bindings are visible to the host GPU. /// Note: this actually performs the binding using the host graphics API. @@ -743,6 +777,8 @@ namespace Ryujinx.Graphics.Gpu.Memory UpdateBuffers(_gpUniformBuffers); } + CommitBufferTextureBindings(); + _rebind = false; } @@ -813,31 +849,19 @@ namespace Ryujinx.Graphics.Gpu.Memory } /// - /// Sets the buffer storage of a buffer texture. + /// Sets the buffer storage of a buffer texture. This will be bound when the buffer manager commits bindings. /// /// 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) + /// Binding info for the buffer texture + /// Format of the buffer texture + /// Whether the binding is for an image or a sampler + public void SetBufferTextureStorage(ITexture texture, ulong address, ulong size, TextureBindingInfo bindingInfo, Format format, bool isImage) { 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)); + _bufferTextures.Add(new BufferTextureBinding(texture, address, size, bindingInfo, format, isImage)); } /// diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs b/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs new file mode 100644 index 00000000..cf0d225e --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs @@ -0,0 +1,60 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Image; + +namespace Ryujinx.Graphics.Gpu.Memory +{ + /// + /// A buffer binding to apply to a buffer texture. + /// + struct BufferTextureBinding + { + /// + /// The buffer texture. + /// + public ITexture Texture { get; } + + /// + /// The base address of the buffer binding. + /// + public ulong Address { get; } + + /// + /// The size of the buffer binding in bytes. + /// + public ulong Size { get; } + + /// + /// The image or sampler binding info for the buffer texture. + /// + public TextureBindingInfo BindingInfo { get; } + + /// + /// The image format for the binding. + /// + public Format Format { get; } + + /// + /// Whether the binding is for an image or a sampler. + /// + public bool IsImage { get; } + + /// + /// Create a new buffer texture binding. + /// + /// Buffer texture + /// Base address + /// Size in bytes + /// Binding info + /// Binding format + /// Whether the binding is for an image or a sampler + public BufferTextureBinding(ITexture texture, ulong address, ulong size, TextureBindingInfo bindingInfo, Format format, bool isImage) + { + Texture = texture; + Address = address; + Size = size; + BindingInfo = bindingInfo; + Format = format; + IsImage = isImage; + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index bf89f29d..768a58e7 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Version of the codegen (to be changed when codegen or guest format change). /// - private const ulong ShaderCodeGenVersion = 1961; + private const ulong ShaderCodeGenVersion = 2088; // Progress reporting helpers private int _shaderCount; diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs b/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs index 5607fb40..f49a0647 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs @@ -6,12 +6,17 @@ namespace Ryujinx.Graphics.OpenGL.Image { class TextureBuffer : TextureBase, ITexture { + private Renderer _renderer; private int _bufferOffset; private int _bufferSize; + private int _bufferCount; private BufferHandle _buffer; - public TextureBuffer(TextureCreateInfo info) : base(info) {} + public TextureBuffer(Renderer renderer, TextureCreateInfo info) : base(info) + { + _renderer = renderer; + } public void CopyTo(ITexture destination, int firstLayer, int firstLevel) { @@ -50,16 +55,19 @@ namespace Ryujinx.Graphics.OpenGL.Image public void SetStorage(BufferRange buffer) { - if (buffer.Handle == _buffer && + if (_buffer != BufferHandle.Null && buffer.Offset == _bufferOffset && - buffer.Size == _bufferSize) + buffer.Size == _bufferSize && + _renderer.BufferCount == _bufferCount) { + // Only rebind the buffer when more have been created. return; } _buffer = buffer.Handle; _bufferOffset = buffer.Offset; _bufferSize = buffer.Size; + _bufferCount = _renderer.BufferCount; Bind(0); diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs index 4a3f51bf..c3d08309 100644 --- a/Ryujinx.Graphics.OpenGL/Renderer.cs +++ b/Ryujinx.Graphics.OpenGL/Renderer.cs @@ -30,6 +30,8 @@ namespace Ryujinx.Graphics.OpenGL internal ResourcePool ResourcePool { get; } + internal int BufferCount { get; private set; } + public string GpuVendor { get; private set; } public string GpuRenderer { get; private set; } public string GpuVersion { get; private set; } @@ -52,6 +54,8 @@ namespace Ryujinx.Graphics.OpenGL public BufferHandle CreateBuffer(int size) { + BufferCount++; + return Buffer.Create(size); } @@ -69,7 +73,7 @@ namespace Ryujinx.Graphics.OpenGL { if (info.Target == Target.TextureBuffer) { - return new TextureBuffer(info); + return new TextureBuffer(this, info); } else { diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 3bfc0647..fc5b06b1 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -111,6 +111,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if (texOp.Inst == Instruction.ImageStore) { + int texIndex = context.FindImageDescriptorIndex(texOp); + context.ImageDescriptors[texIndex] = context.ImageDescriptors[texIndex].SetFlag(TextureUsageFlags.ImageStore); + VariableType type = texOp.Format.GetComponentType(); string[] cElems = new string[4]; diff --git a/Ryujinx.Graphics.Shader/TextureUsageFlags.cs b/Ryujinx.Graphics.Shader/TextureUsageFlags.cs index 1f024309..87be0d01 100644 --- a/Ryujinx.Graphics.Shader/TextureUsageFlags.cs +++ b/Ryujinx.Graphics.Shader/TextureUsageFlags.cs @@ -12,6 +12,7 @@ namespace Ryujinx.Graphics.Shader // Integer sampled textures must be noted for resolution scaling. ResScaleUnsupported = 1 << 0, - NeedsScaleValue = 1 << 1 + NeedsScaleValue = 1 << 1, + ImageStore = 1 << 2 } }