mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-09-20 17:51:13 +02:00
Fix Vulkan validation errors
This PR fixes several validation errors caused by invalid Vulkan usage. These validation errors often end up invoking Undefined Behavior on the driver side, which can lead to artifacts or crashes which are driver specific and otherwise incredibly hard to track. I don't think it should have any impact on performance, but it would be good to test it with as many games as possible (maybe a bug in a game was fixed?). Each commit fixes an error. I added to each a description with the validation error that was fixed and a small explanation on what was causing it and how I fixed it. Co-authored-by: AsperTheDog <guillerman0000@gmail.com>
This commit is contained in:
committed by
KeatonTheBot
co-authored by
AsperTheDog
parent
79f55b12bf
commit
e88fd3273d
@@ -46,7 +46,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public static (AccessFlags Access, PipelineStageFlags Stages) GetSubpassAccessSuperset(VulkanRenderer gd)
|
public static (AccessFlags Access, PipelineStageFlags Stages) GetSubpassAccessSuperset(VulkanRenderer gd)
|
||||||
{
|
{
|
||||||
AccessFlags access = BufferAccess;
|
AccessFlags access = BufferAccess |
|
||||||
|
AccessFlags.ShaderReadBit |
|
||||||
|
AccessFlags.ShaderWriteBit |
|
||||||
|
AccessFlags.ColorAttachmentReadBit |
|
||||||
|
AccessFlags.ColorAttachmentWriteBit |
|
||||||
|
AccessFlags.DepthStencilAttachmentReadBit |
|
||||||
|
AccessFlags.DepthStencilAttachmentWriteBit;
|
||||||
|
|
||||||
PipelineStageFlags stages = PipelineStageFlags.AllGraphicsBit;
|
PipelineStageFlags stages = PipelineStageFlags.AllGraphicsBit;
|
||||||
|
|
||||||
if (gd.TransformFeedbackApi != null)
|
if (gd.TransformFeedbackApi != null)
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Image dstImage,
|
Image dstImage,
|
||||||
TextureCreateInfo srcInfo,
|
TextureCreateInfo srcInfo,
|
||||||
TextureCreateInfo dstInfo,
|
TextureCreateInfo dstInfo,
|
||||||
|
TextureCreateInfo srcStorageInfo,
|
||||||
|
TextureCreateInfo dstStorageInfo,
|
||||||
Extents2D srcRegion,
|
Extents2D srcRegion,
|
||||||
Extents2D dstRegion,
|
Extents2D dstRegion,
|
||||||
int srcLayer,
|
int srcLayer,
|
||||||
@@ -40,6 +42,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return (xy1, xy2);
|
return (xy1, xy2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static (Offset3D, Offset3D) ClampOffsetsToMip(Offset3D xy1, Offset3D xy2, int mipW, int mipH)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
new Offset3D(Math.Min(xy1.X, mipW), Math.Min(xy1.Y, mipH), xy1.Z),
|
||||||
|
new Offset3D(Math.Min(xy2.X, mipW), Math.Min(xy2.Y, mipH), xy2.Z));
|
||||||
|
}
|
||||||
|
|
||||||
if (srcAspectFlags == 0)
|
if (srcAspectFlags == 0)
|
||||||
{
|
{
|
||||||
srcAspectFlags = srcInfo.Format.ConvertAspectFlags();
|
srcAspectFlags = srcInfo.Format.ConvertAspectFlags();
|
||||||
@@ -80,6 +89,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
(srcOffsets.Element0, srcOffsets.Element1) = ExtentsToOffset3D(srcRegion, srcInfo.Width, srcInfo.Height, level);
|
(srcOffsets.Element0, srcOffsets.Element1) = ExtentsToOffset3D(srcRegion, srcInfo.Width, srcInfo.Height, level);
|
||||||
(dstOffsets.Element0, dstOffsets.Element1) = ExtentsToOffset3D(dstRegion, dstInfo.Width, dstInfo.Height, level);
|
(dstOffsets.Element0, dstOffsets.Element1) = ExtentsToOffset3D(dstRegion, dstInfo.Width, dstInfo.Height, level);
|
||||||
|
|
||||||
|
int srcMipW = Math.Max(1, srcStorageInfo.Width >> (int)copySrcLevel);
|
||||||
|
int srcMipH = Math.Max(1, srcStorageInfo.Height >> (int)copySrcLevel);
|
||||||
|
int dstMipW = Math.Max(1, dstStorageInfo.Width >> (int)copyDstLevel);
|
||||||
|
int dstMipH = Math.Max(1, dstStorageInfo.Height >> (int)copyDstLevel);
|
||||||
|
|
||||||
|
(srcOffsets.Element0, srcOffsets.Element1) = ClampOffsetsToMip(srcOffsets.Element0, srcOffsets.Element1, srcMipW, srcMipH);
|
||||||
|
(dstOffsets.Element0, dstOffsets.Element1) = ClampOffsetsToMip(dstOffsets.Element0, dstOffsets.Element1, dstMipW, dstMipH);
|
||||||
|
|
||||||
var region = new ImageBlit
|
var region = new ImageBlit
|
||||||
{
|
{
|
||||||
SrcSubresource = srcSl,
|
SrcSubresource = srcSl,
|
||||||
@@ -121,6 +138,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Image dstImage,
|
Image dstImage,
|
||||||
TextureCreateInfo srcInfo,
|
TextureCreateInfo srcInfo,
|
||||||
TextureCreateInfo dstInfo,
|
TextureCreateInfo dstInfo,
|
||||||
|
TextureCreateInfo srcStorageInfo,
|
||||||
|
TextureCreateInfo dstStorageInfo,
|
||||||
int srcViewLayer,
|
int srcViewLayer,
|
||||||
int dstViewLayer,
|
int dstViewLayer,
|
||||||
int srcViewLevel,
|
int srcViewLevel,
|
||||||
@@ -151,6 +170,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
dstImage,
|
dstImage,
|
||||||
srcInfo,
|
srcInfo,
|
||||||
dstInfo,
|
dstInfo,
|
||||||
|
srcStorageInfo,
|
||||||
|
dstStorageInfo,
|
||||||
srcViewLayer,
|
srcViewLayer,
|
||||||
dstViewLayer,
|
dstViewLayer,
|
||||||
srcViewLevel,
|
srcViewLevel,
|
||||||
@@ -186,6 +207,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Image dstImage,
|
Image dstImage,
|
||||||
TextureCreateInfo srcInfo,
|
TextureCreateInfo srcInfo,
|
||||||
TextureCreateInfo dstInfo,
|
TextureCreateInfo dstInfo,
|
||||||
|
TextureCreateInfo srcStorageInfo,
|
||||||
|
TextureCreateInfo dstStorageInfo,
|
||||||
int srcViewLayer,
|
int srcViewLayer,
|
||||||
int dstViewLayer,
|
int dstViewLayer,
|
||||||
int srcViewLevel,
|
int srcViewLevel,
|
||||||
@@ -314,6 +337,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width;
|
int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width;
|
||||||
int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height;
|
int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height;
|
||||||
|
|
||||||
|
int srcMipW = Math.Max(1, srcStorageInfo.Width >> (srcViewLevel + srcLevel + level));
|
||||||
|
int srcMipH = Math.Max(1, srcStorageInfo.Height >> (srcViewLevel + srcLevel + level));
|
||||||
|
int dstMipW = Math.Max(1, dstStorageInfo.Width >> (dstViewLevel + dstLevel + level));
|
||||||
|
int dstMipH = Math.Max(1, dstStorageInfo.Height >> (dstViewLevel + dstLevel + level));
|
||||||
|
|
||||||
|
copyWidth = Math.Min(copyWidth, Math.Min(srcMipW, dstMipW));
|
||||||
|
copyHeight = Math.Min(copyHeight, Math.Min(srcMipH, dstMipH));
|
||||||
|
|
||||||
var extent = new Extent3D((uint)copyWidth, (uint)copyHeight, (uint)srcDepth);
|
var extent = new Extent3D((uint)copyWidth, (uint)copyHeight, (uint)srcDepth);
|
||||||
|
|
||||||
if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples)
|
if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples)
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public VkFormat VkFormat { get; }
|
public VkFormat VkFormat { get; }
|
||||||
|
|
||||||
|
public ImageUsageFlags UsageFlags { get; }
|
||||||
|
|
||||||
public unsafe TextureStorage(
|
public unsafe TextureStorage(
|
||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
Device device,
|
Device device,
|
||||||
@@ -93,7 +95,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
|
var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
|
||||||
|
|
||||||
var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities);
|
ImageUsageFlags usage = GetImageUsage(info.Format, info.Target, gd.Capabilities, isMsImageStorageSupported);
|
||||||
|
UsageFlags = usage;
|
||||||
|
|
||||||
var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit;
|
var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit;
|
||||||
|
|
||||||
@@ -159,7 +162,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_imageAuto = new Auto<DisposableImage>(new DisposableImage(_gd.Api, device, _image));
|
_imageAuto = new Auto<DisposableImage>(new DisposableImage(_gd.Api, device, _image));
|
||||||
|
|
||||||
InitialTransition(ImageLayout.Preinitialized, ImageLayout.General);
|
InitialTransition(ImageLayout.Undefined, ImageLayout.General);
|
||||||
}
|
}
|
||||||
|
|
||||||
_slices = new TextureSliceInfo[levels * _depthOrLayers];
|
_slices = new TextureSliceInfo[levels * _depthOrLayers];
|
||||||
@@ -307,7 +310,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageUsageFlags GetImageUsage(Format format, Target target, in HardwareCapabilities capabilities)
|
public static ImageUsageFlags GetImageUsage(Format format, Target target, in HardwareCapabilities capabilities, bool isMsImageStorageSupported)
|
||||||
{
|
{
|
||||||
var usage = DefaultUsageFlags;
|
var usage = DefaultUsageFlags;
|
||||||
|
|
||||||
@@ -320,8 +323,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
usage |= ImageUsageFlags.ColorAttachmentBit;
|
usage |= ImageUsageFlags.ColorAttachmentBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isMsImageStorageSupported = capabilities.SupportsShaderStorageImageMultisample;
|
|
||||||
|
|
||||||
if (format.IsImageCompatible() && (isMsImageStorageSupported || !target.IsMultisample()))
|
if (format.IsImageCompatible() && (isMsImageStorageSupported || !target.IsMultisample()))
|
||||||
{
|
{
|
||||||
usage |= ImageUsageFlags.StorageBit;
|
usage |= ImageUsageFlags.StorageBit;
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample();
|
bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample();
|
||||||
|
|
||||||
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported);
|
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported);
|
||||||
var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities);
|
var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities, isMsImageStorageSupported) & storage.UsageFlags;
|
||||||
|
|
||||||
var levels = (uint)info.Levels;
|
var levels = (uint)info.Levels;
|
||||||
var layers = (uint)info.GetLayers();
|
var layers = (uint)info.GetLayers();
|
||||||
|
|
||||||
@@ -130,6 +131,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
shaderUsage |= ImageUsageFlags.StorageBit;
|
shaderUsage |= ImageUsageFlags.StorageBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shaderUsage &= storage.UsageFlags;
|
||||||
|
|
||||||
_imageView = CreateImageView(componentMapping, subresourceRange, type, shaderUsage);
|
_imageView = CreateImageView(componentMapping, subresourceRange, type, shaderUsage);
|
||||||
|
|
||||||
// Framebuffer attachments and storage images requires a identity component mapping.
|
// Framebuffer attachments and storage images requires a identity component mapping.
|
||||||
@@ -254,6 +257,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
dstImage,
|
dstImage,
|
||||||
src.Info,
|
src.Info,
|
||||||
dst.Info,
|
dst.Info,
|
||||||
|
src.Storage.Info,
|
||||||
|
dst.Storage.Info,
|
||||||
src.FirstLayer,
|
src.FirstLayer,
|
||||||
dst.FirstLayer,
|
dst.FirstLayer,
|
||||||
src.FirstLevel,
|
src.FirstLevel,
|
||||||
@@ -307,6 +312,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
dstImage,
|
dstImage,
|
||||||
src.Info,
|
src.Info,
|
||||||
dst.Info,
|
dst.Info,
|
||||||
|
src.Storage.Info,
|
||||||
|
dst.Storage.Info,
|
||||||
src.FirstLayer,
|
src.FirstLayer,
|
||||||
dst.FirstLayer,
|
dst.FirstLayer,
|
||||||
src.FirstLevel,
|
src.FirstLevel,
|
||||||
@@ -382,6 +389,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
dst.GetImage().Get(cbs).Value,
|
dst.GetImage().Get(cbs).Value,
|
||||||
src.Info,
|
src.Info,
|
||||||
dst.Info,
|
dst.Info,
|
||||||
|
src.Storage.Info,
|
||||||
|
dst.Storage.Info,
|
||||||
src.FirstLayer,
|
src.FirstLayer,
|
||||||
dst.FirstLayer,
|
dst.FirstLayer,
|
||||||
src.FirstLevel,
|
src.FirstLevel,
|
||||||
@@ -407,6 +416,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
dst.GetImage().Get(cbs).Value,
|
dst.GetImage().Get(cbs).Value,
|
||||||
src.Info,
|
src.Info,
|
||||||
dst.Info,
|
dst.Info,
|
||||||
|
src.Storage.Info,
|
||||||
|
dst.Storage.Info,
|
||||||
srcRegion,
|
srcRegion,
|
||||||
dstRegion,
|
dstRegion,
|
||||||
src.FirstLayer,
|
src.FirstLayer,
|
||||||
@@ -460,6 +471,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
dstImage.Get(cbs).Value,
|
dstImage.Get(cbs).Value,
|
||||||
src.Info,
|
src.Info,
|
||||||
dst.Info,
|
dst.Info,
|
||||||
|
src.Storage.Info,
|
||||||
|
dst.Storage.Info,
|
||||||
srcRegion,
|
srcRegion,
|
||||||
dstRegion,
|
dstRegion,
|
||||||
src.FirstLayer,
|
src.FirstLayer,
|
||||||
|
|||||||
@@ -67,9 +67,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
int stride = (_stride + (alignment - 1)) & -alignment;
|
int stride = (_stride + (alignment - 1)) & -alignment;
|
||||||
int newSize = (_size / _stride) * stride;
|
int newSize = (_size / _stride) * stride;
|
||||||
|
|
||||||
var buffer = autoBuffer.Get(cbs, 0, newSize).Value;
|
updater.BindVertexBuffer(cbs, binding, autoBuffer, 0, newSize, (ulong)stride);
|
||||||
|
|
||||||
updater.BindVertexBuffer(cbs, binding, buffer, 0, (ulong)newSize, (ulong)stride);
|
|
||||||
|
|
||||||
_buffer = autoBuffer;
|
_buffer = autoBuffer;
|
||||||
|
|
||||||
@@ -92,11 +90,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (autoBuffer != null)
|
if (autoBuffer != null)
|
||||||
{
|
{
|
||||||
int offset = _offset;
|
updater.BindVertexBuffer(cbs, binding, autoBuffer, _offset, _size, (ulong)_stride);
|
||||||
bool mirrorable = _size <= VertexBufferMaxMirrorable;
|
|
||||||
var buffer = mirrorable ? autoBuffer.GetMirrorable(cbs, ref offset, _size, out _).Value : autoBuffer.Get(cbs, offset, _size).Value;
|
|
||||||
|
|
||||||
updater.BindVertexBuffer(cbs, binding, buffer, (ulong)offset, (ulong)_size, (ulong)_stride);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly NativeArray<ulong> _sizes;
|
private readonly NativeArray<ulong> _sizes;
|
||||||
private readonly NativeArray<ulong> _strides;
|
private readonly NativeArray<ulong> _strides;
|
||||||
|
|
||||||
|
private readonly Auto<DisposableBuffer>[] _bufferAutos;
|
||||||
|
private readonly int[] _bufferOffsetsForGet;
|
||||||
|
private readonly int[] _bufferSizesForGet;
|
||||||
|
|
||||||
public VertexBufferUpdater(VulkanRenderer gd)
|
public VertexBufferUpdater(VulkanRenderer gd)
|
||||||
{
|
{
|
||||||
_gd = gd;
|
_gd = gd;
|
||||||
@@ -23,9 +27,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_offsets = new NativeArray<ulong>(Constants.MaxVertexBuffers);
|
_offsets = new NativeArray<ulong>(Constants.MaxVertexBuffers);
|
||||||
_sizes = new NativeArray<ulong>(Constants.MaxVertexBuffers);
|
_sizes = new NativeArray<ulong>(Constants.MaxVertexBuffers);
|
||||||
_strides = new NativeArray<ulong>(Constants.MaxVertexBuffers);
|
_strides = new NativeArray<ulong>(Constants.MaxVertexBuffers);
|
||||||
|
|
||||||
|
_bufferAutos = new Auto<DisposableBuffer>[Constants.MaxVertexBuffers];
|
||||||
|
_bufferOffsetsForGet = new int[Constants.MaxVertexBuffers];
|
||||||
|
_bufferSizesForGet = new int[Constants.MaxVertexBuffers];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BindVertexBuffer(CommandBufferScoped cbs, uint binding, VkBuffer buffer, ulong offset, ulong size, ulong stride)
|
public void BindVertexBuffer(CommandBufferScoped cbs, uint binding, Auto<DisposableBuffer> autoBuffer, int offset, int size, ulong stride)
|
||||||
{
|
{
|
||||||
if (_count == 0)
|
if (_count == 0)
|
||||||
{
|
{
|
||||||
@@ -39,9 +47,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
int index = (int)_count;
|
int index = (int)_count;
|
||||||
|
|
||||||
_buffers[index] = buffer;
|
_bufferAutos[index] = autoBuffer;
|
||||||
_offsets[index] = offset;
|
_bufferOffsetsForGet[index] = offset;
|
||||||
_sizes[index] = size;
|
_bufferSizesForGet[index] = size;
|
||||||
|
_offsets[index] = (ulong)offset;
|
||||||
|
_sizes[index] = (ulong)size;
|
||||||
_strides[index] = stride;
|
_strides[index] = stride;
|
||||||
|
|
||||||
_count++;
|
_count++;
|
||||||
@@ -51,6 +61,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
if (_count != 0)
|
if (_count != 0)
|
||||||
{
|
{
|
||||||
|
for (int i = 0; i < _count; i++)
|
||||||
|
{
|
||||||
|
_buffers[i] = _bufferAutos[i].Get(cbs, _bufferOffsetsForGet[i], _bufferSizesForGet[i]).Value;
|
||||||
|
_bufferAutos[i] = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (_gd.Capabilities.SupportsExtendedDynamicState)
|
if (_gd.Capabilities.SupportsExtendedDynamicState)
|
||||||
{
|
{
|
||||||
_gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
|
_gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
|
||||||
|
|||||||
@@ -390,12 +390,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
if (_effect != null)
|
if (_effect != null)
|
||||||
{
|
{
|
||||||
|
_gd.FlushAllCommands();
|
||||||
_gd.CommandBufferPool.Return(
|
_gd.CommandBufferPool.Return(
|
||||||
cbs,
|
cbs,
|
||||||
null,
|
null,
|
||||||
[PipelineStageFlags.ColorAttachmentOutputBit],
|
[PipelineStageFlags.ColorAttachmentOutputBit],
|
||||||
null);
|
null);
|
||||||
_gd.FlushAllCommands();
|
|
||||||
cbs.GetFence().Wait();
|
cbs.GetFence().Wait();
|
||||||
cbs = _gd.CommandBufferPool.Rent();
|
cbs = _gd.CommandBufferPool.Rent();
|
||||||
}
|
}
|
||||||
@@ -454,6 +454,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
ImageLayout.General,
|
ImageLayout.General,
|
||||||
ImageLayout.PresentSrcKhr);
|
ImageLayout.PresentSrcKhr);
|
||||||
|
|
||||||
|
_gd.FlushAllCommands();
|
||||||
|
|
||||||
_gd.CommandBufferPool.Return(
|
_gd.CommandBufferPool.Return(
|
||||||
cbs,
|
cbs,
|
||||||
[_imageAvailableSemaphores[semaphoreIndex]],
|
[_imageAvailableSemaphores[semaphoreIndex]],
|
||||||
|
|||||||
Reference in New Issue
Block a user