mirror of
https://github.com/ryujinx-mirror/ryujinx.git
synced 2024-11-28 21:13:01 +00:00
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:
parent
3bcc395253
commit
d786d8d2b9
9 changed files with 131 additions and 160 deletions
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -227,10 +227,6 @@ 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);
|
|
||||||
|
|
||||||
if (color3D == null)
|
|
||||||
{
|
|
||||||
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
||||||
{
|
{
|
||||||
var colorState = _context.State.Get<RtColorState>(MethodOffset.RtColorState, index);
|
var colorState = _context.State.Get<RtColorState>(MethodOffset.RtColorState, index);
|
||||||
|
@ -254,13 +250,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
color.Modified = true;
|
color.Modified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_textureManager.SetRenderTargetColor3D(color3D);
|
|
||||||
|
|
||||||
color3D.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.
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ViewTargetCompatible(TextureInfo info)
|
return size.Width == otherSize.Width &&
|
||||||
|
size.Height == otherSize.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,9 @@ 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,8 +131,6 @@ 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;
|
||||||
|
@ -160,21 +148,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
_context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
|
_context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_rtHostColors[0] != _rtColor3D.HostTexture)
|
|
||||||
{
|
|
||||||
_rtHostColors[0] = _rtColor3D.HostTexture;
|
|
||||||
|
|
||||||
anyChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (anyChanged)
|
|
||||||
{
|
|
||||||
_context.Renderer.Pipeline.SetRenderTargets(_rtColor3D.HostTexture, _rtHostDs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Texture FindOrCreateTexture(CopyTexture copyTexture)
|
public Texture FindOrCreateTexture(CopyTexture copyTexture)
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,8 +90,8 @@ namespace Ryujinx.Graphics.Texture
|
||||||
gobBlocksInY,
|
gobBlocksInY,
|
||||||
gobBlocksInZ);
|
gobBlocksInZ);
|
||||||
|
|
||||||
int[] allOffsets = new int[levels * layers];
|
if (!is3D)
|
||||||
|
{
|
||||||
for (int layer = 0; layer < layers; layer++)
|
for (int layer = 0; layer < layers; layer++)
|
||||||
{
|
{
|
||||||
int baseIndex = layer * levels;
|
int baseIndex = layer * levels;
|
||||||
|
@ -80,6 +102,7 @@ namespace Ryujinx.Graphics.Texture
|
||||||
allOffsets[baseIndex + level] = baseOffset + mipOffsets[level];
|
allOffsets[baseIndex + level] = baseOffset + mipOffsets[level];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int totalSize = layerSize * layers;
|
int totalSize = layerSize * layers;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue