Support copy of slices to 3D textures, remove old 3D render target layered render support, do not delete textures with existing views created from them

This commit is contained in:
gdk 2019-10-30 20:45:01 -03:00 committed by Thog
parent 3bcc395253
commit d786d8d2b9
9 changed files with 131 additions and 160 deletions

View file

@ -61,7 +61,6 @@ namespace Ryujinx.Graphics.GAL
void SetRenderTargetColorMasks(uint[] componentMask); void SetRenderTargetColorMasks(uint[] componentMask);
void SetRenderTargets(ITexture color3D, ITexture depthStencil);
void SetRenderTargets(ITexture[] colors, ITexture depthStencil); void SetRenderTargets(ITexture[] colors, ITexture depthStencil);
void SetStencilTest(StencilTestDescriptor stencilTest); void SetStencilTest(StencilTestDescriptor stencilTest);

View file

@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.GAL
{ {
int Handle { get; } int Handle { get; }
void CopyTo(ITexture destination); void CopyTo(ITexture destination, int firstLayer, int firstLevel);
void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter); void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter);
ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel); ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel);

View file

@ -227,39 +227,28 @@ namespace Ryujinx.Graphics.Gpu.Engine
int samplesInX = msaaMode.SamplesInX(); int samplesInX = msaaMode.SamplesInX();
int samplesInY = msaaMode.SamplesInY(); int samplesInY = msaaMode.SamplesInY();
Image.Texture color3D = Get3DRenderTarget(samplesInX, samplesInY); for (int index = 0; index < Constants.TotalRenderTargets; index++)
if (color3D == null)
{ {
for (int index = 0; index < Constants.TotalRenderTargets; index++) var colorState = _context.State.Get<RtColorState>(MethodOffset.RtColorState, index);
if (!IsRtEnabled(colorState))
{ {
var colorState = _context.State.Get<RtColorState>(MethodOffset.RtColorState, index); _textureManager.SetRenderTargetColor(index, null);
if (!IsRtEnabled(colorState)) continue;
{
_textureManager.SetRenderTargetColor(index, null);
continue;
}
Image.Texture color = _textureManager.FindOrCreateTexture(
colorState,
samplesInX,
samplesInY);
_textureManager.SetRenderTargetColor(index, color);
if (color != null)
{
color.Modified = true;
}
} }
}
else
{
_textureManager.SetRenderTargetColor3D(color3D);
color3D.Modified = true; Image.Texture color = _textureManager.FindOrCreateTexture(
colorState,
samplesInX,
samplesInY);
_textureManager.SetRenderTargetColor(index, color);
if (color != null)
{
color.Modified = true;
}
} }
bool dsEnable = _context.State.Get<Boolean32>(MethodOffset.RtDepthStencilEnable); bool dsEnable = _context.State.Get<Boolean32>(MethodOffset.RtDepthStencilEnable);
@ -286,45 +275,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
} }
} }
private Image.Texture Get3DRenderTarget(int samplesInX, int samplesInY)
{
var colorState0 = _context.State.Get<RtColorState>(MethodOffset.RtColorState, 0);
if (!IsRtEnabled(colorState0) || !colorState0.MemoryLayout.UnpackIsTarget3D() || colorState0.Depth != 1)
{
return null;
}
int slices = 1;
int unused = 0;
for (int index = 1; index < Constants.TotalRenderTargets; index++)
{
var colorState = _context.State.Get<RtColorState>(MethodOffset.RtColorState, index);
if (!IsRtEnabled(colorState))
{
unused++;
continue;
}
if (colorState.MemoryLayout.UnpackIsTarget3D() && colorState.Depth == 1)
{
slices++;
}
}
if (slices + unused == Constants.TotalRenderTargets)
{
colorState0.Depth = slices;
return _textureManager.FindOrCreateTexture(colorState0, samplesInX, samplesInY);
}
return null;
}
private static bool IsRtEnabled(RtColorState colorState) private static bool IsRtEnabled(RtColorState colorState)
{ {
// Colors are disabled by writing 0 to the format. // Colors are disabled by writing 0 to the format.

View file

@ -6,6 +6,7 @@ using Ryujinx.Graphics.Texture;
using Ryujinx.Graphics.Texture.Astc; using Ryujinx.Graphics.Texture.Astc;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
{ {
@ -116,6 +117,8 @@ namespace Ryujinx.Graphics.Gpu.Image
_views.Remove(texture); _views.Remove(texture);
texture._viewStorage = null; texture._viewStorage = null;
DeleteIfNotUsed();
} }
public void ChangeSize(int width, int height, int depthOrLayers) public void ChangeSize(int width, int height, int depthOrLayers)
@ -187,7 +190,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
ITexture newStorage = _context.Renderer.CreateTexture(createInfo); ITexture newStorage = _context.Renderer.CreateTexture(createInfo);
HostTexture.CopyTo(newStorage); HostTexture.CopyTo(newStorage, 0, 0);
ReplaceStorage(newStorage); ReplaceStorage(newStorage);
} }
@ -413,7 +416,21 @@ namespace Ryujinx.Graphics.Gpu.Image
_info.SamplesInY == info.SamplesInY; _info.SamplesInY == info.SamplesInY;
} }
public bool IsViewCompatible(TextureInfo info, ulong size, out int firstLayer, out int firstLevel) public bool IsViewCompatible(
TextureInfo info,
ulong size,
out int firstLayer,
out int firstLevel)
{
return IsViewCompatible(info, size, isCopy: false, out firstLayer, out firstLevel);
}
public bool IsViewCompatible(
TextureInfo info,
ulong size,
bool isCopy,
out int firstLayer,
out int firstLevel)
{ {
// Out of range. // Out of range.
if (info.Address < Address || info.Address + size > EndAddress) if (info.Address < Address || info.Address + size > EndAddress)
@ -441,12 +458,12 @@ namespace Ryujinx.Graphics.Gpu.Image
return false; return false;
} }
if (!ViewSizeMatches(info, firstLevel)) if (!ViewSizeMatches(info, firstLevel, isCopy))
{ {
return false; return false;
} }
if (!ViewTargetCompatible(info)) if (!ViewTargetCompatible(info, isCopy))
{ {
return false; return false;
} }
@ -496,18 +513,24 @@ namespace Ryujinx.Graphics.Gpu.Image
return TextureCompatibility.FormatCompatible(_info.FormatInfo, info.FormatInfo); return TextureCompatibility.FormatCompatible(_info.FormatInfo, info.FormatInfo);
} }
private bool ViewSizeMatches(TextureInfo info, int level) private bool ViewSizeMatches(TextureInfo info, int level, bool isCopy)
{ {
Size size = GetAlignedSize(_info, level); Size size = GetAlignedSize(_info, level);
Size otherSize = GetAlignedSize(info); Size otherSize = GetAlignedSize(info);
return size.Width == otherSize.Width && // For copies, we can copy a subset of the 3D texture slices,
size.Height == otherSize.Height && // so the depth may be different in this case.
size.Depth == otherSize.Depth; if (!isCopy && info.Target == Target.Texture3D && size.Depth != otherSize.Depth)
{
return false;
}
return size.Width == otherSize.Width &&
size.Height == otherSize.Height;
} }
private bool ViewTargetCompatible(TextureInfo info) private bool ViewTargetCompatible(TextureInfo info, bool isCopy)
{ {
switch (_info.Target) switch (_info.Target)
{ {
@ -534,7 +557,8 @@ namespace Ryujinx.Graphics.Gpu.Image
info.Target == Target.Texture2DMultisampleArray; info.Target == Target.Texture2DMultisampleArray;
case Target.Texture3D: case Target.Texture3D:
return info.Target == Target.Texture3D; return info.Target == Target.Texture3D ||
(info.Target == Target.Texture2D && isCopy);
} }
return false; return false;
@ -686,7 +710,9 @@ namespace Ryujinx.Graphics.Gpu.Image
public void DecrementReferenceCount() public void DecrementReferenceCount()
{ {
if (--_referenceCount == 0) int newRefCount = --_referenceCount;
if (newRefCount == 0)
{ {
if (_viewStorage != this) if (_viewStorage != this)
{ {
@ -694,7 +720,21 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
_context.Methods.TextureManager.RemoveTextureFromCache(this); _context.Methods.TextureManager.RemoveTextureFromCache(this);
}
Debug.Assert(newRefCount >= 0);
DeleteIfNotUsed();
}
private void DeleteIfNotUsed()
{
// We can delete the texture as long it is not being used
// in any cache (the reference count is 0 in this case), and
// also all views that may be created from this texture were
// already deleted (views count is 0).
if (_referenceCount == 0 && _views.Count == 0)
{
DisposeTextures(); DisposeTextures();
} }
} }

View file

@ -17,13 +17,10 @@ namespace Ryujinx.Graphics.Gpu.Image
private TextureBindingsManager _gpBindingsManager; private TextureBindingsManager _gpBindingsManager;
private Texture[] _rtColors; private Texture[] _rtColors;
private Texture _rtColor3D; private Texture _rtDepthStencil;
private Texture _rtDepthStencil;
private ITexture[] _rtHostColors; private ITexture[] _rtHostColors;
private ITexture _rtHostDs;
private ITexture _rtHostDs;
private RangeList<Texture> _textures; private RangeList<Texture> _textures;
@ -98,13 +95,6 @@ namespace Ryujinx.Graphics.Gpu.Image
public void SetRenderTargetColor(int index, Texture color) public void SetRenderTargetColor(int index, Texture color)
{ {
_rtColors[index] = color; _rtColors[index] = color;
_rtColor3D = null;
}
public void SetRenderTargetColor3D(Texture color)
{
_rtColor3D = color;
} }
public void SetRenderTargetDepthStencil(Texture depthStencil) public void SetRenderTargetDepthStencil(Texture depthStencil)
@ -141,38 +131,21 @@ namespace Ryujinx.Graphics.Gpu.Image
anyChanged = true; anyChanged = true;
} }
if (_rtColor3D == null) for (int index = 0; index < _rtColors.Length; index++)
{ {
for (int index = 0; index < _rtColors.Length; index++) ITexture hostTexture = _rtColors[index]?.HostTexture;
{
ITexture hostTexture = _rtColors[index]?.HostTexture;
if (_rtHostColors[index] != hostTexture) if (_rtHostColors[index] != hostTexture)
{
_rtHostColors[index] = hostTexture;
anyChanged = true;
}
}
if (anyChanged)
{ {
_context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs); _rtHostColors[index] = hostTexture;
}
}
else
{
if (_rtHostColors[0] != _rtColor3D.HostTexture)
{
_rtHostColors[0] = _rtColor3D.HostTexture;
anyChanged = true; anyChanged = true;
} }
}
if (anyChanged) if (anyChanged)
{ {
_context.Renderer.Pipeline.SetRenderTargets(_rtColor3D.HostTexture, _rtHostDs); _context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
}
} }
} }
@ -447,11 +420,29 @@ namespace Ryujinx.Graphics.Gpu.Image
ITexture newView = texture.HostTexture.CreateView(createInfo, firstLayer, firstLevel); ITexture newView = texture.HostTexture.CreateView(createInfo, firstLayer, firstLevel);
overlap.HostTexture.CopyTo(newView); overlap.HostTexture.CopyTo(newView, 0, 0);
overlap.ReplaceView(texture, overlapInfo, newView); overlap.ReplaceView(texture, overlapInfo, newView);
} }
} }
// If the texture is a 3D texture, we need to additionally copy any slice
// of the 3D texture to the newly created 3D texture.
if (info.Target == Target.Texture3D)
{
foreach (Texture overlap in overlaps)
{
if (texture.IsViewCompatible(
overlap.Info,
overlap.Size,
isCopy: true,
out int firstLayer,
out int firstLevel))
{
overlap.HostTexture.CopyTo(texture.HostTexture, firstLayer, firstLevel);
}
}
}
} }
// Sampler textures are managed by the texture pool, all other textures // Sampler textures are managed by the texture pool, all other textures

View file

@ -29,16 +29,6 @@ namespace Ryujinx.Graphics.OpenGL
0); 0);
} }
public void AttachColor(int index, TextureView color, int layer)
{
GL.FramebufferTextureLayer(
FramebufferTarget.Framebuffer,
FramebufferAttachment.ColorAttachment0 + index,
color?.Handle ?? 0,
0,
layer);
}
public void AttachDepthStencil(TextureView depthStencil) public void AttachDepthStencil(TextureView depthStencil)
{ {
// Detach the last depth/stencil buffer if there is any. // Detach the last depth/stencil buffer if there is any.

View file

@ -697,28 +697,6 @@ namespace Ryujinx.Graphics.OpenGL
} }
} }
public void SetRenderTargets(ITexture color3D, ITexture depthStencil)
{
EnsureFramebuffer();
TextureView color = (TextureView)color3D;
for (int index = 0; index < color.DepthOrLayers; index++)
{
_framebuffer.AttachColor(index, color, index);
}
TextureView depthStencilView = (TextureView)depthStencil;
_framebuffer.AttachDepthStencil(depthStencilView);
_framebuffer.SetDrawBuffers(color.DepthOrLayers);
_hasDepthBuffer = depthStencil != null && depthStencilView.Format != Format.S8Uint;
UpdateDepthTest();
}
public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
{ {
EnsureFramebuffer(); EnsureFramebuffer();

View file

@ -137,11 +137,11 @@ namespace Ryujinx.Graphics.OpenGL
return _parent.GetHashCode(); return _parent.GetHashCode();
} }
public void CopyTo(ITexture destination) public void CopyTo(ITexture destination, int firstLayer, int firstLevel)
{ {
TextureView destinationView = (TextureView)destination; TextureView destinationView = (TextureView)destination;
TextureCopyUnscaled.Copy(this, destinationView, 0, 0); TextureCopyUnscaled.Copy(this, destinationView, firstLayer, firstLevel);
int width = Math.Min(Width, destinationView.Width); int width = Math.Min(Width, destinationView.Width);
int height = Math.Min(Height, destinationView.Height); int height = Math.Min(Height, destinationView.Height);

View file

@ -22,8 +22,11 @@ namespace Ryujinx.Graphics.Texture
int gobBlocksInZ, int gobBlocksInZ,
int gobBlocksInTileX) int gobBlocksInTileX)
{ {
bool is3D = depth > 1;
int layerSize = 0; int layerSize = 0;
int[] allOffsets = new int[levels * layers * depth];
int[] mipOffsets = new int[levels]; int[] mipOffsets = new int[levels];
int mipGobBlocksInY = gobBlocksInY; int mipGobBlocksInY = gobBlocksInY;
@ -55,6 +58,25 @@ namespace Ryujinx.Graphics.Texture
int robSize = widthInGobs * mipGobBlocksInY * mipGobBlocksInZ * GobSize; int robSize = widthInGobs * mipGobBlocksInY * mipGobBlocksInZ * GobSize;
if (is3D)
{
int gobSize = mipGobBlocksInY * GobSize;
int sliceSize = totalBlocksOfGobsInY * widthInGobs * gobSize;
int baseOffset = layerSize;
int mask = gobBlocksInZ - 1;
for (int z = 0; z < d; z++)
{
int zLow = z & mask;
int zHigh = z & ~mask;
allOffsets[z * levels + level] = baseOffset + zLow * gobSize + zHigh * sliceSize;
}
}
mipOffsets[level] = layerSize; mipOffsets[level] = layerSize;
layerSize += totalBlocksOfGobsInZ * totalBlocksOfGobsInY * robSize; layerSize += totalBlocksOfGobsInZ * totalBlocksOfGobsInY * robSize;
@ -68,16 +90,17 @@ namespace Ryujinx.Graphics.Texture
gobBlocksInY, gobBlocksInY,
gobBlocksInZ); gobBlocksInZ);
int[] allOffsets = new int[levels * layers]; if (!is3D)
for (int layer = 0; layer < layers; layer++)
{ {
int baseIndex = layer * levels; for (int layer = 0; layer < layers; layer++)
int baseOffset = layer * layerSize;
for (int level = 0; level < levels; level++)
{ {
allOffsets[baseIndex + level] = baseOffset + mipOffsets[level]; int baseIndex = layer * levels;
int baseOffset = layer * layerSize;
for (int level = 0; level < levels; level++)
{
allOffsets[baseIndex + level] = baseOffset + mipOffsets[level];
}
} }
} }