forked from MoonsideGames/MoonWorks
Debug mode bounds checks for buffer and texture upload
parent
eaa9266521
commit
df3f38a67b
|
@ -1767,6 +1767,7 @@ namespace MoonWorks.Graphics
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
AssertRenderPassInactive("Cannot copy during render pass!");
|
AssertRenderPassInactive("Cannot copy during render pass!");
|
||||||
AssertNonEmptyCopy(dataLengthInBytes);
|
AssertNonEmptyCopy(dataLengthInBytes);
|
||||||
|
AssertBufferBoundsCheck(buffer.Size, bufferOffsetInBytes, dataLengthInBytes);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Refresh.Refresh_SetBufferData(
|
Refresh.Refresh_SetBufferData(
|
||||||
|
@ -1803,6 +1804,7 @@ namespace MoonWorks.Graphics
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
AssertRenderPassInactive("Cannot copy during render pass!");
|
AssertRenderPassInactive("Cannot copy during render pass!");
|
||||||
AssertNonEmptyCopy(dataLengthInBytes);
|
AssertNonEmptyCopy(dataLengthInBytes);
|
||||||
|
AssertBufferBoundsCheck(buffer.Size, bufferOffsetInBytes, dataLengthInBytes);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
fixed (T* ptr = data)
|
fixed (T* ptr = data)
|
||||||
|
@ -1847,17 +1849,19 @@ namespace MoonWorks.Graphics
|
||||||
{
|
{
|
||||||
var elementSize = Marshal.SizeOf<T>();
|
var elementSize = Marshal.SizeOf<T>();
|
||||||
var dataLengthInBytes = (uint) (elementSize * numElements);
|
var dataLengthInBytes = (uint) (elementSize * numElements);
|
||||||
|
var offsetLengthInBytes = (uint) elementSize * bufferOffsetInElements;
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
AssertRenderPassInactive("Cannot copy during render pass!");
|
AssertRenderPassInactive("Cannot copy during render pass!");
|
||||||
AssertNonEmptyCopy((uint) (elementSize * numElements));
|
AssertNonEmptyCopy((uint) (elementSize * numElements));
|
||||||
|
AssertBufferBoundsCheck(buffer.Size, offsetLengthInBytes, dataLengthInBytes);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Refresh.Refresh_SetBufferData(
|
Refresh.Refresh_SetBufferData(
|
||||||
Device.Handle,
|
Device.Handle,
|
||||||
Handle,
|
Handle,
|
||||||
buffer.Handle,
|
buffer.Handle,
|
||||||
(uint) elementSize * bufferOffsetInElements,
|
offsetLengthInBytes,
|
||||||
dataPtr,
|
dataPtr,
|
||||||
dataLengthInBytes
|
dataLengthInBytes
|
||||||
);
|
);
|
||||||
|
@ -1888,12 +1892,13 @@ namespace MoonWorks.Graphics
|
||||||
/// <param name="data">A span of data to copy into the texture.</param>
|
/// <param name="data">A span of data to copy into the texture.</param>
|
||||||
public unsafe void SetTextureData<T>(in TextureSlice textureSlice, Span<T> data) where T : unmanaged
|
public unsafe void SetTextureData<T>(in TextureSlice textureSlice, Span<T> data) where T : unmanaged
|
||||||
{
|
{
|
||||||
|
var dataLengthInBytes = (uint) (data.Length * Marshal.SizeOf<T>());
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
AssertRenderPassInactive("Cannot copy during render pass!");
|
AssertRenderPassInactive("Cannot copy during render pass!");
|
||||||
|
AssertTextureBoundsCheck(textureSlice.Size, dataLengthInBytes);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var size = sizeof(T);
|
|
||||||
|
|
||||||
fixed (T* ptr = data)
|
fixed (T* ptr = data)
|
||||||
{
|
{
|
||||||
Refresh.Refresh_SetTextureData(
|
Refresh.Refresh_SetTextureData(
|
||||||
|
@ -1901,7 +1906,7 @@ namespace MoonWorks.Graphics
|
||||||
Handle,
|
Handle,
|
||||||
textureSlice.ToRefreshTextureSlice(),
|
textureSlice.ToRefreshTextureSlice(),
|
||||||
(IntPtr) ptr,
|
(IntPtr) ptr,
|
||||||
(uint) (data.Length * size)
|
dataLengthInBytes
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1926,6 +1931,7 @@ namespace MoonWorks.Graphics
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
AssertRenderPassInactive("Cannot copy during render pass!");
|
AssertRenderPassInactive("Cannot copy during render pass!");
|
||||||
|
AssertTextureBoundsCheck(textureSlice.Size, dataLengthInBytes);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Refresh.Refresh_SetTextureData(
|
Refresh.Refresh_SetTextureData(
|
||||||
|
@ -2027,6 +2033,7 @@ namespace MoonWorks.Graphics
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
AssertRenderPassInactive("Cannot copy during render pass!");
|
AssertRenderPassInactive("Cannot copy during render pass!");
|
||||||
|
AssertBufferBoundsCheck(buffer.Size, 0, textureSlice.Size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var refreshTextureSlice = textureSlice.ToRefreshTextureSlice();
|
var refreshTextureSlice = textureSlice.ToRefreshTextureSlice();
|
||||||
|
@ -2209,6 +2216,22 @@ namespace MoonWorks.Graphics
|
||||||
throw new System.InvalidOperationException("SetBufferData must have a length greater than 0 bytes!");
|
throw new System.InvalidOperationException("SetBufferData must have a length greater than 0 bytes!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AssertBufferBoundsCheck(uint bufferLengthInBytes, uint offsetInBytes, uint copyLengthInBytes)
|
||||||
|
{
|
||||||
|
if (copyLengthInBytes > bufferLengthInBytes + offsetInBytes)
|
||||||
|
{
|
||||||
|
throw new System.InvalidOperationException($"SetBufferData overflow! buffer length {bufferLengthInBytes}, offset {offsetInBytes}, copy length {copyLengthInBytes}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AssertTextureBoundsCheck(uint textureSizeInBytes, uint dataLengthInBytes)
|
||||||
|
{
|
||||||
|
if (dataLengthInBytes > textureSizeInBytes)
|
||||||
|
{
|
||||||
|
throw new System.InvalidOperationException($"SetTextureData overflow! texture size {textureSizeInBytes}, data size {dataLengthInBytes}");
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,7 @@ namespace MoonWorks.Graphics
|
||||||
window.SwapchainFormat = GetSwapchainFormat(window);
|
window.SwapchainFormat = GetSwapchainFormat(window);
|
||||||
if (window.SwapchainTexture == null)
|
if (window.SwapchainTexture == null)
|
||||||
{
|
{
|
||||||
window.SwapchainTexture = new Texture(this);
|
window.SwapchainTexture = new Texture(this, window.SwapchainFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ namespace MoonWorks.Graphics
|
||||||
public uint LevelCount { get; }
|
public uint LevelCount { get; }
|
||||||
public SampleCount SampleCount { get; }
|
public SampleCount SampleCount { get; }
|
||||||
public TextureUsageFlags UsageFlags { get; }
|
public TextureUsageFlags UsageFlags { get; }
|
||||||
|
public uint Size { get; }
|
||||||
|
|
||||||
// FIXME: this allocates a delegate instance
|
// FIXME: this allocates a delegate instance
|
||||||
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyTexture;
|
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyTexture;
|
||||||
|
@ -296,6 +297,7 @@ namespace MoonWorks.Graphics
|
||||||
LevelCount = textureCreateInfo.LevelCount;
|
LevelCount = textureCreateInfo.LevelCount;
|
||||||
SampleCount = textureCreateInfo.SampleCount;
|
SampleCount = textureCreateInfo.SampleCount;
|
||||||
UsageFlags = textureCreateInfo.UsageFlags;
|
UsageFlags = textureCreateInfo.UsageFlags;
|
||||||
|
Size = Width * Height * BytesPerPixel(Format) / BlockSizeSquared(Format);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static implicit operator TextureSlice(Texture t) => new TextureSlice(t);
|
public static implicit operator TextureSlice(Texture t) => new TextureSlice(t);
|
||||||
|
@ -303,12 +305,13 @@ namespace MoonWorks.Graphics
|
||||||
// Used by AcquireSwapchainTexture.
|
// Used by AcquireSwapchainTexture.
|
||||||
// Should not be tracked, because swapchain textures are managed by Vulkan.
|
// Should not be tracked, because swapchain textures are managed by Vulkan.
|
||||||
internal Texture(
|
internal Texture(
|
||||||
GraphicsDevice device
|
GraphicsDevice device,
|
||||||
|
TextureFormat format
|
||||||
) : base(device)
|
) : base(device)
|
||||||
{
|
{
|
||||||
Handle = IntPtr.Zero;
|
Handle = IntPtr.Zero;
|
||||||
|
|
||||||
Format = TextureFormat.R8G8B8A8;
|
Format = format;
|
||||||
Width = 0;
|
Width = 0;
|
||||||
Height = 0;
|
Height = 0;
|
||||||
Depth = 1;
|
Depth = 1;
|
||||||
|
@ -316,6 +319,7 @@ namespace MoonWorks.Graphics
|
||||||
LevelCount = 1;
|
LevelCount = 1;
|
||||||
SampleCount = SampleCount.One;
|
SampleCount = SampleCount.One;
|
||||||
UsageFlags = TextureUsageFlags.ColorTarget;
|
UsageFlags = TextureUsageFlags.ColorTarget;
|
||||||
|
Size = Width * Height * BytesPerPixel(Format) / BlockSizeSquared(Format);
|
||||||
}
|
}
|
||||||
|
|
||||||
// DDS loading extension, based on MojoDDS
|
// DDS loading extension, based on MojoDDS
|
||||||
|
@ -644,5 +648,96 @@ namespace MoonWorks.Graphics
|
||||||
|
|
||||||
NativeMemory.Free(pixelsPtr);
|
NativeMemory.Free(pixelsPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static uint BytesPerPixel(TextureFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case TextureFormat.R8:
|
||||||
|
case TextureFormat.R8_UINT:
|
||||||
|
return 1;
|
||||||
|
case TextureFormat.R5G6B5:
|
||||||
|
case TextureFormat.B4G4R4A4:
|
||||||
|
case TextureFormat.A1R5G5B5:
|
||||||
|
case TextureFormat.R16_SFLOAT:
|
||||||
|
case TextureFormat.R8G8_SNORM:
|
||||||
|
case TextureFormat.R8G8_UINT:
|
||||||
|
case TextureFormat.R16_UINT:
|
||||||
|
case TextureFormat.D16:
|
||||||
|
return 2;
|
||||||
|
case TextureFormat.D16S8:
|
||||||
|
return 3;
|
||||||
|
case TextureFormat.R8G8B8A8:
|
||||||
|
case TextureFormat.B8G8R8A8:
|
||||||
|
case TextureFormat.R32_SFLOAT:
|
||||||
|
case TextureFormat.R16G16:
|
||||||
|
case TextureFormat.R16G16_SFLOAT:
|
||||||
|
case TextureFormat.R8G8B8A8_SNORM:
|
||||||
|
case TextureFormat.A2R10G10B10:
|
||||||
|
case TextureFormat.R8G8B8A8_UINT:
|
||||||
|
case TextureFormat.R16G16_UINT:
|
||||||
|
case TextureFormat.D32:
|
||||||
|
return 4;
|
||||||
|
case TextureFormat.D32S8:
|
||||||
|
return 5;
|
||||||
|
case TextureFormat.R16G16B16A16_SFLOAT:
|
||||||
|
case TextureFormat.R16G16B16A16:
|
||||||
|
case TextureFormat.R32G32_SFLOAT:
|
||||||
|
case TextureFormat.R16G16B16A16_UINT:
|
||||||
|
case TextureFormat.BC1:
|
||||||
|
return 8;
|
||||||
|
case TextureFormat.R32G32B32A32_SFLOAT:
|
||||||
|
case TextureFormat.BC2:
|
||||||
|
case TextureFormat.BC3:
|
||||||
|
case TextureFormat.BC7:
|
||||||
|
return 16;
|
||||||
|
default:
|
||||||
|
Logger.LogError("Texture format not recognized!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static uint BlockSizeSquared(TextureFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case TextureFormat.BC1:
|
||||||
|
case TextureFormat.BC2:
|
||||||
|
case TextureFormat.BC3:
|
||||||
|
case TextureFormat.BC7:
|
||||||
|
return 16;
|
||||||
|
case TextureFormat.R8G8B8A8:
|
||||||
|
case TextureFormat.B8G8R8A8:
|
||||||
|
case TextureFormat.R5G6B5:
|
||||||
|
case TextureFormat.A1R5G5B5:
|
||||||
|
case TextureFormat.B4G4R4A4:
|
||||||
|
case TextureFormat.A2R10G10B10:
|
||||||
|
case TextureFormat.R16G16:
|
||||||
|
case TextureFormat.R16G16B16A16:
|
||||||
|
case TextureFormat.R8:
|
||||||
|
case TextureFormat.R8G8_SNORM:
|
||||||
|
case TextureFormat.R8G8B8A8_SNORM:
|
||||||
|
case TextureFormat.R16_SFLOAT:
|
||||||
|
case TextureFormat.R16G16_SFLOAT:
|
||||||
|
case TextureFormat.R16G16B16A16_SFLOAT:
|
||||||
|
case TextureFormat.R32_SFLOAT:
|
||||||
|
case TextureFormat.R32G32_SFLOAT:
|
||||||
|
case TextureFormat.R32G32B32A32_SFLOAT:
|
||||||
|
case TextureFormat.R8_UINT:
|
||||||
|
case TextureFormat.R8G8_UINT:
|
||||||
|
case TextureFormat.R8G8B8A8_UINT:
|
||||||
|
case TextureFormat.R16_UINT:
|
||||||
|
case TextureFormat.R16G16_UINT:
|
||||||
|
case TextureFormat.R16G16B16A16_UINT:
|
||||||
|
case TextureFormat.D16:
|
||||||
|
case TextureFormat.D32:
|
||||||
|
case TextureFormat.D16S8:
|
||||||
|
case TextureFormat.D32S8:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
Logger.LogError("Texture format not recognized!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@ namespace MoonWorks.Graphics
|
||||||
public uint Layer { get; }
|
public uint Layer { get; }
|
||||||
public uint Level { get; }
|
public uint Level { get; }
|
||||||
|
|
||||||
|
public uint Size => (uint) (Rectangle.W * Rectangle.H * Texture.BytesPerPixel(Texture.Format) / Texture.BlockSizeSquared(Texture.Format));
|
||||||
|
|
||||||
public TextureSlice(Texture texture)
|
public TextureSlice(Texture texture)
|
||||||
{
|
{
|
||||||
Texture = texture;
|
Texture = texture;
|
||||||
|
|
Loading…
Reference in New Issue