forked from Mirror/Ryujinx
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