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