Compare commits
	
		
			60 Commits 
		
	
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 4180ecb129 | |
|  | 535abec45a | |
|  | 60d2642119 | |
|  | 8d31a398ab | |
|  | baa4df5e18 | |
|  | 6d95203816 | |
|  | ad025e1777 | |
|  | 7386952974 | |
|  | 35684df005 | |
|  | baf51d2ee9 | |
|  | 3cfb43438c | |
|  | 97dee2a170 | |
|  | fe6734d6db | |
|  | eab282cdca | |
|  | 4e8653fb1f | |
|  | 8b35b6b0b7 | |
|  | cc7cae9d6b | |
|  | 39dc2fb7f4 | |
|  | b4021156cf | |
|  | 871b32d742 | |
|  | 0fbe241848 | |
|  | 1b40b5e7d3 | |
|  | ee8331a96b | |
|  | 8813a0139d | |
|  | 217ae96888 | |
|  | 23b6499479 | |
|  | 929d8f5cc9 | |
|  | 69d2c9cc31 | |
|  | 9195e445b2 | |
|  | f30d8f0197 | |
|  | bde31fbe07 | |
|  | a762a80c4f | |
|  | 099c07aa39 | |
|  | cba6ca59d3 | |
|  | a004488f81 | |
|  | 33ed8b2364 | |
|  | 3c832550d0 | |
|  | c84752f38c | |
|  | 019afa91f5 | |
|  | 00adec189c | |
|  | 0e723514df | |
|  | e0f4c19dc2 | |
|  | 178a5ea3cf | |
|  | 50b8cb11c9 | |
|  | d83501437d | |
|  | fe520dc9cc | |
|  | b29341eca3 | |
|  | 22bcd2e471 | |
|  | fe31e23ccc | |
|  | 848b1c706c | |
|  | a207f404b9 | |
|  | 31c79d3179 | |
|  | 8229e5dd33 | |
|  | 1eae01c95c | |
|  | b80527d793 | |
|  | ecfcb666a8 | |
|  | ad97aed60f | |
|  | 0a5ec9e82d | |
|  | 8648eef5d1 | |
|  | 39496c37ea | 
|  | @ -4,12 +4,12 @@ | ||||||
| [submodule "lib/FAudio"] | [submodule "lib/FAudio"] | ||||||
| 	path = lib/FAudio | 	path = lib/FAudio | ||||||
| 	url = https://github.com/FNA-XNA/FAudio.git | 	url = https://github.com/FNA-XNA/FAudio.git | ||||||
| [submodule "lib/RefreshCS"] |  | ||||||
| 	path = lib/RefreshCS |  | ||||||
| 	url = https://gitea.moonside.games/MoonsideGames/RefreshCS.git |  | ||||||
| [submodule "lib/WellspringCS"] |  | ||||||
| 	path = lib/WellspringCS |  | ||||||
| 	url = https://gitea.moonside.games/MoonsideGames/WellspringCS.git |  | ||||||
| [submodule "lib/dav1dfile"] | [submodule "lib/dav1dfile"] | ||||||
| 	path = lib/dav1dfile | 	path = lib/dav1dfile | ||||||
| 	url = https://github.com/MoonsideGames/dav1dfile.git | 	url = https://github.com/MoonsideGames/dav1dfile.git | ||||||
|  | [submodule "lib/RefreshCS"] | ||||||
|  | 	path = lib/RefreshCS | ||||||
|  | 	url = https://github.com/MoonsideGames/RefreshCS.git | ||||||
|  | [submodule "lib/WellspringCS"] | ||||||
|  | 	path = lib/WellspringCS | ||||||
|  | 	url = https://github.com/MoonsideGames/WellspringCS.git | ||||||
|  |  | ||||||
|  | @ -3,7 +3,6 @@ | ||||||
| 	<PropertyGroup> | 	<PropertyGroup> | ||||||
| 		<TargetFramework>net8.0</TargetFramework> | 		<TargetFramework>net8.0</TargetFramework> | ||||||
| 		<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | 		<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||||
| 		<LangVersion>11</LangVersion> |  | ||||||
| 	</PropertyGroup> | 	</PropertyGroup> | ||||||
| 
 | 
 | ||||||
| 	<PropertyGroup> | 	<PropertyGroup> | ||||||
|  | @ -12,7 +11,7 @@ | ||||||
| 
 | 
 | ||||||
| 	<ItemGroup> | 	<ItemGroup> | ||||||
| 		<Compile Include="lib\FAudio\csharp\FAudio.cs" /> | 		<Compile Include="lib\FAudio\csharp\FAudio.cs" /> | ||||||
| 		<Compile Include="lib\RefreshCS\src\Refresh.cs" /> | 		<Compile Include="lib\RefreshCS\RefreshCS.cs" /> | ||||||
| 		<Compile Include="lib\SDL2-CS\src\SDL2.cs" /> | 		<Compile Include="lib\SDL2-CS\src\SDL2.cs" /> | ||||||
| 		<Compile Include="lib\WellspringCS\WellspringCS.cs" /> | 		<Compile Include="lib\WellspringCS\WellspringCS.cs" /> | ||||||
| 		<Compile Include="lib\dav1dfile\csharp\dav1dfile.cs" /> | 		<Compile Include="lib\dav1dfile\csharp\dav1dfile.cs" /> | ||||||
|  | @ -26,17 +25,17 @@ | ||||||
| 	</ItemGroup> | 	</ItemGroup> | ||||||
| 
 | 
 | ||||||
| 	<ItemGroup> | 	<ItemGroup> | ||||||
| 		<EmbeddedResource Include="src\Graphics\StockShaders\Binary\video_fullscreen.vert.refresh"> | 		<EmbeddedResource Include="src\Graphics\StockShaders\Binary\fullscreen.vert.spv"> | ||||||
| 			<LogicalName>MoonWorks.Graphics.StockShaders.VideoFullscreen.vert.refresh</LogicalName> | 			<LogicalName>MoonWorks.Graphics.StockShaders.Fullscreen.vert.spv</LogicalName> | ||||||
| 		</EmbeddedResource> | 		</EmbeddedResource> | ||||||
| 		<EmbeddedResource Include="src\Graphics\StockShaders\Binary\video_yuv2rgba.frag.refresh"> | 		<EmbeddedResource Include="src\Graphics\StockShaders\Binary\video_yuv2rgba.frag.spv"> | ||||||
| 			<LogicalName>MoonWorks.Graphics.StockShaders.VideoYUV2RGBA.frag.refresh</LogicalName> | 			<LogicalName>MoonWorks.Graphics.StockShaders.VideoYUV2RGBA.frag.spv</LogicalName> | ||||||
| 		</EmbeddedResource> | 		</EmbeddedResource> | ||||||
| 		<EmbeddedResource Include="src\Graphics\StockShaders\Binary\text_transform.vert.refresh"> | 		<EmbeddedResource Include="src\Graphics\StockShaders\Binary\text_transform.vert.spv"> | ||||||
| 			<LogicalName>MoonWorks.Graphics.StockShaders.TextTransform.vert.refresh</LogicalName> | 			<LogicalName>MoonWorks.Graphics.StockShaders.TextTransform.vert.spv</LogicalName> | ||||||
| 		</EmbeddedResource> | 		</EmbeddedResource> | ||||||
| 		<EmbeddedResource Include="src\Graphics\StockShaders\Binary\text_msdf.frag.refresh"> | 		<EmbeddedResource Include="src\Graphics\StockShaders\Binary\text_msdf.frag.spv"> | ||||||
| 			<LogicalName>MoonWorks.Graphics.StockShaders.TextMSDF.frag.refresh</LogicalName> | 			<LogicalName>MoonWorks.Graphics.StockShaders.TextMSDF.frag.spv</LogicalName> | ||||||
| 		</EmbeddedResource> | 		</EmbeddedResource> | ||||||
| 	</ItemGroup> | 	</ItemGroup> | ||||||
| </Project> | </Project> | ||||||
|  |  | ||||||
|  | @ -5,8 +5,8 @@ | ||||||
| 	<dllmap dll="SDL2" os="linux,freebsd,netbsd" target="libSDL2-2.0.so.0"/> | 	<dllmap dll="SDL2" os="linux,freebsd,netbsd" target="libSDL2-2.0.so.0"/> | ||||||
| 
 | 
 | ||||||
| 	<dllmap dll="Refresh" os="windows" target="Refresh.dll"/> | 	<dllmap dll="Refresh" os="windows" target="Refresh.dll"/> | ||||||
| 	<dllmap dll="Refresh" os="osx" target="libRefresh.1.dylib"/> | 	<dllmap dll="Refresh" os="osx" target="libRefresh.2.dylib"/> | ||||||
| 	<dllmap dll="Refresh" os="linux,freebsd,netbsd" target="libRefresh.so.1"/> | 	<dllmap dll="Refresh" os="linux,freebsd,netbsd" target="libRefresh.so.2"/> | ||||||
| 
 | 
 | ||||||
| 	<dllmap dll="FAudio" os="windows" target="FAudio.dll"/> | 	<dllmap dll="FAudio" os="windows" target="FAudio.dll"/> | ||||||
| 	<dllmap dll="FAudio" os="osx" target="libFAudio.0.dylib"/> | 	<dllmap dll="FAudio" os="osx" target="libFAudio.0.dylib"/> | ||||||
|  |  | ||||||
|  | @ -1 +1 @@ | ||||||
| Subproject commit b5325e6d0329eeb35b074091a569a5f679852d28 | Subproject commit 5d09b036cbd0a1402857776d8f39be87234b450a | ||||||
|  | @ -21,7 +21,7 @@ namespace MoonWorks | ||||||
| 			{ VertexElementFormat.NormalizedShort4, (uint) Marshal.SizeOf<NormalizedShort4>() }, | 			{ VertexElementFormat.NormalizedShort4, (uint) Marshal.SizeOf<NormalizedShort4>() }, | ||||||
| 			{ VertexElementFormat.Short2, (uint) Marshal.SizeOf<Short2>() }, | 			{ VertexElementFormat.Short2, (uint) Marshal.SizeOf<Short2>() }, | ||||||
| 			{ VertexElementFormat.Short4, (uint) Marshal.SizeOf<Short4>() }, | 			{ VertexElementFormat.Short4, (uint) Marshal.SizeOf<Short4>() }, | ||||||
| 			{ VertexElementFormat.UInt, (uint) Marshal.SizeOf<uint>() }, | 			{ VertexElementFormat.Uint, (uint) Marshal.SizeOf<uint>() }, | ||||||
| 			{ VertexElementFormat.Vector2, (uint) Marshal.SizeOf<Math.Float.Vector2>() }, | 			{ VertexElementFormat.Vector2, (uint) Marshal.SizeOf<Math.Float.Vector2>() }, | ||||||
| 			{ VertexElementFormat.Vector3, (uint) Marshal.SizeOf<Math.Float.Vector3>() }, | 			{ VertexElementFormat.Vector3, (uint) Marshal.SizeOf<Math.Float.Vector3>() }, | ||||||
| 			{ VertexElementFormat.Vector4, (uint) Marshal.SizeOf<Math.Float.Vector4>() } | 			{ VertexElementFormat.Vector4, (uint) Marshal.SizeOf<Math.Float.Vector4>() } | ||||||
|  | @ -37,6 +37,16 @@ namespace MoonWorks | ||||||
| 			return b != 0; | 			return b != 0; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		public static int BoolToInt(bool b) | ||||||
|  | 		{ | ||||||
|  | 			return b ? 1 : 0; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public static bool IntToBool(int b) | ||||||
|  | 		{ | ||||||
|  | 			return b != 0; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		public static uint VertexElementFormatSize(VertexElementFormat format) | 		public static uint VertexElementFormatSize(VertexElementFormat format) | ||||||
| 		{ | 		{ | ||||||
| 			return Sizes[format]; | 			return Sizes[format]; | ||||||
|  |  | ||||||
							
								
								
									
										21
									
								
								src/Game.cs
								
								
								
								
							
							
						
						
									
										21
									
								
								src/Game.cs
								
								
								
								
							|  | @ -48,15 +48,16 @@ namespace MoonWorks | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		/// <param name="windowCreateInfo">The parameters that will be used to create the MainWindow.</param> | 		/// <param name="windowCreateInfo">The parameters that will be used to create the MainWindow.</param> | ||||||
| 		/// <param name="frameLimiterSettings">The frame limiter settings.</param> | 		/// <param name="frameLimiterSettings">The frame limiter settings.</param> | ||||||
|  | 		/// <param name="preferredBackends">Bitflags of which GPU backends to attempt to initialize.</param> | ||||||
| 		/// <param name="targetTimestep">How often Game.Update will run in terms of ticks per second.</param> | 		/// <param name="targetTimestep">How often Game.Update will run in terms of ticks per second.</param> | ||||||
| 		/// <param name="debugMode">If true, enables extra debug checks. Should be turned off for release builds.</param> | 		/// <param name="debugMode">If true, enables extra debug checks. Should be turned off for release builds.</param> | ||||||
| 		public Game( | 		public Game( | ||||||
| 			WindowCreateInfo windowCreateInfo, | 			WindowCreateInfo windowCreateInfo, | ||||||
| 			FrameLimiterSettings frameLimiterSettings, | 			FrameLimiterSettings frameLimiterSettings, | ||||||
|  | 			BackendFlags preferredBackends, | ||||||
| 			int targetTimestep = 60, | 			int targetTimestep = 60, | ||||||
| 			bool debugMode = false | 			bool debugMode = false | ||||||
| 		) | 		) { | ||||||
| 		{ |  | ||||||
| 			Logger.LogInfo("Initializing frame limiter..."); | 			Logger.LogInfo("Initializing frame limiter..."); | ||||||
| 			Timestep = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / targetTimestep); | 			Timestep = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / targetTimestep); | ||||||
| 			gameTimer = Stopwatch.StartNew(); | 			gameTimer = Stopwatch.StartNew(); | ||||||
|  | @ -75,21 +76,25 @@ namespace MoonWorks | ||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			Logger.Initialize(); |  | ||||||
| 
 |  | ||||||
| 			Logger.LogInfo("Initializing input..."); | 			Logger.LogInfo("Initializing input..."); | ||||||
| 			Inputs = new Inputs(); | 			Inputs = new Inputs(); | ||||||
| 
 | 
 | ||||||
| 			Logger.LogInfo("Initializing graphics device..."); | 			Logger.LogInfo("Initializing graphics device..."); | ||||||
| 			GraphicsDevice = new GraphicsDevice( | 			GraphicsDevice = new GraphicsDevice( | ||||||
| 				Backend.Vulkan, | 				preferredBackends, | ||||||
| 				debugMode | 				debugMode | ||||||
| 			); | 			); | ||||||
| 
 | 
 | ||||||
| 			Logger.LogInfo("Initializing main window..."); | 			SDL.SDL_WindowFlags windowFlags = 0; | ||||||
| 			MainWindow = new Window(windowCreateInfo, GraphicsDevice.WindowFlags | SDL.SDL_WindowFlags.SDL_WINDOW_HIDDEN); | 			if ((preferredBackends & BackendFlags.Vulkan) != 0) | ||||||
|  | 			{ | ||||||
|  | 				windowFlags |= SDL.SDL_WindowFlags.SDL_WINDOW_VULKAN; | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 			if (!GraphicsDevice.ClaimWindow(MainWindow, windowCreateInfo.PresentMode)) | 			Logger.LogInfo("Initializing main window..."); | ||||||
|  | 			MainWindow = new Window(windowCreateInfo, windowFlags | SDL.SDL_WindowFlags.SDL_WINDOW_HIDDEN); | ||||||
|  | 
 | ||||||
|  | 			if (!GraphicsDevice.ClaimWindow(MainWindow, windowCreateInfo.SwapchainComposition, windowCreateInfo.PresentMode)) | ||||||
| 			{ | 			{ | ||||||
| 				throw new System.SystemException("Could not claim window!"); | 				throw new System.SystemException("Could not claim window!"); | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | @ -1,17 +0,0 @@ | ||||||
| namespace MoonWorks.Graphics |  | ||||||
| { |  | ||||||
| 	/// <summary> |  | ||||||
| 	/// A buffer-offset pair to be used when binding vertex buffers. |  | ||||||
| 	/// </summary> |  | ||||||
| 	public struct BufferBinding |  | ||||||
| 	{ |  | ||||||
| 		public Buffer Buffer; |  | ||||||
| 		public ulong Offset; |  | ||||||
| 
 |  | ||||||
| 		public BufferBinding(Buffer buffer, ulong offset) |  | ||||||
| 		{ |  | ||||||
| 			Buffer = buffer; |  | ||||||
| 			Offset = offset; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -1,17 +0,0 @@ | ||||||
| namespace MoonWorks.Graphics |  | ||||||
| { |  | ||||||
| 	/// <summary> |  | ||||||
| 	/// A texture-sampler pair to be used when binding samplers. |  | ||||||
| 	/// </summary> |  | ||||||
| 	public struct TextureSamplerBinding |  | ||||||
| 	{ |  | ||||||
| 		public Texture Texture; |  | ||||||
| 		public Sampler Sampler; |  | ||||||
| 
 |  | ||||||
| 		public TextureSamplerBinding(Texture texture, Sampler sampler) |  | ||||||
| 		{ |  | ||||||
| 			Texture = texture; |  | ||||||
| 			Sampler = sampler; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,212 @@ | ||||||
|  | using System.Runtime.InteropServices; | ||||||
|  | using RefreshCS; | ||||||
|  | 
 | ||||||
|  | namespace MoonWorks.Graphics; | ||||||
|  | 
 | ||||||
|  | public class ComputePass | ||||||
|  | { | ||||||
|  | 	public nint Handle { get; private set; } | ||||||
|  | 
 | ||||||
|  | 	internal void SetHandle(nint handle) | ||||||
|  | 	{ | ||||||
|  | 		Handle = handle; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #if DEBUG | ||||||
|  | 	internal bool active; | ||||||
|  | 
 | ||||||
|  | 	ComputePipeline currentComputePipeline; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Binds a compute pipeline so that compute work may be dispatched. | ||||||
|  | 	/// </summary> | ||||||
|  | 	/// <param name="computePipeline">The compute pipeline to bind.</param> | ||||||
|  | 	public void BindComputePipeline( | ||||||
|  | 		ComputePipeline computePipeline | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertComputePassActive(); | ||||||
|  | 
 | ||||||
|  | 		// TODO: validate formats? | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_BindComputePipeline( | ||||||
|  | 			Handle, | ||||||
|  | 			computePipeline.Handle | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | #if DEBUG | ||||||
|  | 		currentComputePipeline = computePipeline; | ||||||
|  | #endif | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Binds a texture to be used in the compute shader. | ||||||
|  | 	/// This texture must have been created with the ComputeShaderRead usage flag. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public unsafe void BindStorageTexture( | ||||||
|  | 		in TextureSlice textureSlice, | ||||||
|  | 		uint slot = 0 | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertComputePassActive(); | ||||||
|  | 		AssertComputePipelineBound(); | ||||||
|  | 		AssertTextureNonNull(textureSlice.Texture); | ||||||
|  | 		AssertTextureHasComputeStorageReadFlag(textureSlice.Texture); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		var refreshTextureSlice = textureSlice.ToRefresh(); | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_BindComputeStorageTextures( | ||||||
|  | 			Handle, | ||||||
|  | 			slot, | ||||||
|  | 			&refreshTextureSlice, | ||||||
|  | 			1 | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Binds a buffer to be used in the compute shader. | ||||||
|  | 	/// This buffer must have been created with the ComputeShaderRead usage flag. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public unsafe void BindStorageBuffer( | ||||||
|  | 		GpuBuffer buffer, | ||||||
|  | 		uint slot = 0 | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertComputePassActive(); | ||||||
|  | 		AssertComputePipelineBound(); | ||||||
|  | 		AssertBufferNonNull(buffer); | ||||||
|  | 		AssertBufferHasComputeStorageReadFlag(buffer); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		var bufferHandle = buffer.Handle; | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_BindComputeStorageBuffers( | ||||||
|  | 			Handle, | ||||||
|  | 			slot, | ||||||
|  | 			&bufferHandle, | ||||||
|  | 			1 | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Pushes compute shader uniform data. | ||||||
|  | 	/// Subsequent draw calls will use this uniform data. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public unsafe void PushUniformData( | ||||||
|  | 		void* uniformsPtr, | ||||||
|  | 		uint size, | ||||||
|  | 		uint slot = 0 | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertComputePassActive(); | ||||||
|  | 		AssertComputePipelineBound(); | ||||||
|  | 
 | ||||||
|  | 		if (slot >= currentComputePipeline.ResourceInfo.UniformBufferCount) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.ArgumentException($"Slot {slot} given, but {currentComputePipeline.ResourceInfo.UniformBufferCount} uniform buffers are used on the shader!"); | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_PushComputeUniformData( | ||||||
|  | 			Handle, | ||||||
|  | 			slot, | ||||||
|  | 			(nint) uniformsPtr, | ||||||
|  | 			size | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Pushes compute shader uniform data. | ||||||
|  | 	/// Subsequent draw calls will use this uniform data. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public unsafe void PushUniformData<T>( | ||||||
|  | 		in T uniforms, | ||||||
|  | 		uint slot = 0 | ||||||
|  | 	) where T : unmanaged | ||||||
|  | 	{ | ||||||
|  | 		fixed (T* uniformsPtr = &uniforms) | ||||||
|  | 		{ | ||||||
|  | 			PushUniformData(uniformsPtr, (uint) Marshal.SizeOf<T>(), slot); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Dispatches compute work. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public void Dispatch( | ||||||
|  | 		uint groupCountX, | ||||||
|  | 		uint groupCountY, | ||||||
|  | 		uint groupCountZ | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertComputePassActive(); | ||||||
|  | 		AssertComputePipelineBound(); | ||||||
|  | 
 | ||||||
|  | 		if (groupCountX < 1 || groupCountY < 1 || groupCountZ < 1) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.ArgumentException("All dimensions for the compute work groups must be >= 1!"); | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_DispatchCompute( | ||||||
|  | 			Handle, | ||||||
|  | 			groupCountX, | ||||||
|  | 			groupCountY, | ||||||
|  | 			groupCountZ | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #if DEBUG | ||||||
|  | 	private void AssertComputePassActive(string message = "Render pass is not active!") | ||||||
|  | 	{ | ||||||
|  | 		if (!active) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.InvalidOperationException(message); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void AssertComputePipelineBound(string message = "No compute pipeline is bound!") | ||||||
|  | 	{ | ||||||
|  | 		if (currentComputePipeline == null) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.InvalidOperationException(message); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void AssertTextureNonNull(in TextureSlice textureSlice) | ||||||
|  | 	{ | ||||||
|  | 		if (textureSlice.Texture == null || textureSlice.Texture.Handle == nint.Zero) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.NullReferenceException("Texture must not be null!"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void AssertTextureHasComputeStorageReadFlag(Texture texture) | ||||||
|  | 	{ | ||||||
|  | 		if ((texture.UsageFlags & TextureUsageFlags.ComputeStorageRead) == 0) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.ArgumentException("The bound Texture's UsageFlags must include TextureUsageFlags.ComputeStorageRead!"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void AssertBufferNonNull(GpuBuffer buffer) | ||||||
|  | 	{ | ||||||
|  | 		if (buffer == null || buffer.Handle == nint.Zero) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.NullReferenceException("Buffer must not be null!"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void AssertBufferHasComputeStorageReadFlag(GpuBuffer buffer) | ||||||
|  | 	{ | ||||||
|  | 		if ((buffer.UsageFlags & BufferUsageFlags.ComputeStorageRead) == 0) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.ArgumentException("The bound Buffer's UsageFlags must include BufferUsageFlag.ComputeStorageRead!"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | @ -0,0 +1,25 @@ | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | 
 | ||||||
|  | namespace MoonWorks.Graphics; | ||||||
|  | 
 | ||||||
|  | internal class ComputePassPool | ||||||
|  | { | ||||||
|  | 	private ConcurrentQueue<ComputePass> ComputePasses = new ConcurrentQueue<ComputePass>(); | ||||||
|  | 
 | ||||||
|  | 	public ComputePass Obtain() | ||||||
|  | 	{ | ||||||
|  | 		if (ComputePasses.TryDequeue(out var computePass)) | ||||||
|  | 		{ | ||||||
|  | 			return computePass; | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 		{ | ||||||
|  | 			return new ComputePass(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public void Return(ComputePass computePass) | ||||||
|  | 	{ | ||||||
|  | 		ComputePasses.Enqueue(computePass); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,238 @@ | ||||||
|  | using System.Runtime.InteropServices; | ||||||
|  | using RefreshCS; | ||||||
|  | 
 | ||||||
|  | namespace MoonWorks.Graphics; | ||||||
|  | 
 | ||||||
|  | public class CopyPass | ||||||
|  | { | ||||||
|  | 	public nint Handle { get; private set; } | ||||||
|  | 
 | ||||||
|  | 	internal void SetHandle(nint handle) | ||||||
|  | 	{ | ||||||
|  | 		Handle = handle; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Uploads data from a TransferBuffer to a TextureSlice. | ||||||
|  | 	/// This copy occurs on the GPU timeline. | ||||||
|  | 	/// | ||||||
|  | 	/// Overwriting the contents of the TransferBuffer before the command buffer | ||||||
|  | 	/// has finished execution will cause undefined behavior. | ||||||
|  | 	/// | ||||||
|  | 	/// You MAY assume that the copy has finished for subsequent commands. | ||||||
|  | 	/// </summary> | ||||||
|  | 	/// <param name="cycle">If true, cycles the texture if the given slice is bound.</param> | ||||||
|  | 	public void UploadToTexture( | ||||||
|  | 		TransferBuffer transferBuffer, | ||||||
|  | 		in TextureRegion textureRegion, | ||||||
|  | 		in BufferImageCopy copyParams, | ||||||
|  | 		bool cycle | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertBufferBoundsCheck(transferBuffer.Size, copyParams.BufferOffset, textureRegion.Size); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_UploadToTexture( | ||||||
|  | 			Handle, | ||||||
|  | 			transferBuffer.Handle, | ||||||
|  | 			textureRegion.ToRefresh(), | ||||||
|  | 			copyParams.ToRefresh(), | ||||||
|  | 			Conversions.BoolToInt(cycle) | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Uploads the contents of an entire buffer to a 2D texture with no mips. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public void UploadToTexture( | ||||||
|  | 		TransferBuffer transferBuffer, | ||||||
|  | 		Texture texture, | ||||||
|  | 		bool cycle | ||||||
|  | 	) { | ||||||
|  | 		UploadToTexture( | ||||||
|  | 			transferBuffer, | ||||||
|  | 			new TextureRegion(texture), | ||||||
|  | 			new BufferImageCopy(0, 0, 0), | ||||||
|  | 			cycle | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Uploads data from a TransferBuffer to a GpuBuffer. | ||||||
|  | 	/// This copy occurs on the GPU timeline. | ||||||
|  | 	/// | ||||||
|  | 	/// Overwriting the contents of the TransferBuffer before the command buffer | ||||||
|  | 	/// has finished execution will cause undefined behavior. | ||||||
|  | 	/// | ||||||
|  | 	/// You MAY assume that the copy has finished for subsequent commands. | ||||||
|  | 	/// </summary> | ||||||
|  | 	/// <param name="cycle">If true, cycles the buffer if it is bound.</param> | ||||||
|  | 	public void UploadToBuffer( | ||||||
|  | 		TransferBuffer transferBuffer, | ||||||
|  | 		GpuBuffer buffer, | ||||||
|  | 		in BufferCopy copyParams, | ||||||
|  | 		bool cycle | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertBufferBoundsCheck(transferBuffer.Size, copyParams.SrcOffset, copyParams.Size); | ||||||
|  | 		AssertBufferBoundsCheck(buffer.Size, copyParams.DstOffset, copyParams.Size); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_UploadToBuffer( | ||||||
|  | 			Handle, | ||||||
|  | 			transferBuffer.Handle, | ||||||
|  | 			buffer.Handle, | ||||||
|  | 			copyParams.ToRefresh(), | ||||||
|  | 			Conversions.BoolToInt(cycle) | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Copies the entire contents of a TransferBuffer to a GpuBuffer. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public void UploadToBuffer( | ||||||
|  | 		TransferBuffer transferBuffer, | ||||||
|  | 		GpuBuffer buffer, | ||||||
|  | 		bool cycle | ||||||
|  | 	) { | ||||||
|  | 		UploadToBuffer( | ||||||
|  | 			transferBuffer, | ||||||
|  | 			buffer, | ||||||
|  | 			new BufferCopy(0, 0, transferBuffer.Size), | ||||||
|  | 			cycle | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Copies data element-wise into from a TransferBuffer to a GpuBuffer. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public void UploadToBuffer<T>( | ||||||
|  | 		TransferBuffer transferBuffer, | ||||||
|  | 		GpuBuffer buffer, | ||||||
|  | 		uint sourceStartElement, | ||||||
|  | 		uint destinationStartElement, | ||||||
|  | 		uint numElements, | ||||||
|  | 		bool cycle | ||||||
|  | 	) where T : unmanaged | ||||||
|  | 	{ | ||||||
|  | 		var elementSize = Marshal.SizeOf<T>(); | ||||||
|  | 		var dataLengthInBytes = (uint) (elementSize * numElements); | ||||||
|  | 		var srcOffsetInBytes = (uint) (elementSize * sourceStartElement); | ||||||
|  | 		var dstOffsetInBytes = (uint) (elementSize * destinationStartElement); | ||||||
|  | 
 | ||||||
|  | 		UploadToBuffer( | ||||||
|  | 			transferBuffer, | ||||||
|  | 			buffer, | ||||||
|  | 			new BufferCopy(srcOffsetInBytes, dstOffsetInBytes, dataLengthInBytes), | ||||||
|  | 			cycle | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Copies the contents of a TextureRegion to another TextureRegion. | ||||||
|  | 	/// The regions must have the same dimensions. | ||||||
|  | 	/// This copy occurs on the GPU timeline. | ||||||
|  | 	/// | ||||||
|  | 	/// You MAY assume that the copy has finished in subsequent commands. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public void CopyTextureToTexture( | ||||||
|  | 		in TextureRegion source, | ||||||
|  | 		in TextureRegion destination, | ||||||
|  | 		bool cycle | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertTextureBoundsCheck(destination.Size, source.Size); | ||||||
|  | 
 | ||||||
|  | 		if (source.Width != destination.Width || source.Height != destination.Height || source.Depth != destination.Depth) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.InvalidOperationException("Texture copy must have the same dimensions!"); | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_CopyTextureToTexture( | ||||||
|  | 			Handle, | ||||||
|  | 			source.ToRefresh(), | ||||||
|  | 			destination.ToRefresh(), | ||||||
|  | 			Conversions.BoolToInt(cycle) | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Copies data from a GpuBuffer to another GpuBuffer. | ||||||
|  | 	/// This copy occurs on the GPU timeline. | ||||||
|  | 	/// | ||||||
|  | 	/// You MAY assume that the copy has finished in subsequent commands. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public void CopyBufferToBuffer( | ||||||
|  | 		GpuBuffer source, | ||||||
|  | 		GpuBuffer destination, | ||||||
|  | 		in BufferCopy copyParams, | ||||||
|  | 		bool cycle | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertBufferBoundsCheck(source.Size, copyParams.SrcOffset, copyParams.Size); | ||||||
|  | 		AssertBufferBoundsCheck(destination.Size, copyParams.DstOffset, copyParams.Size); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_CopyBufferToBuffer( | ||||||
|  | 			Handle, | ||||||
|  | 			source.Handle, | ||||||
|  | 			destination.Handle, | ||||||
|  | 			copyParams.ToRefresh(), | ||||||
|  | 			Conversions.BoolToInt(cycle) | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public void DownloadFromBuffer( | ||||||
|  | 		GpuBuffer buffer, | ||||||
|  | 		TransferBuffer transferBuffer, | ||||||
|  | 		in BufferCopy copyParams | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertBufferBoundsCheck(buffer.Size, copyParams.SrcOffset, copyParams.Size); | ||||||
|  | 		AssertBufferBoundsCheck(transferBuffer.Size, copyParams.DstOffset, copyParams.Size); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_DownloadFromBuffer( | ||||||
|  | 			Handle, | ||||||
|  | 			buffer.Handle, | ||||||
|  | 			transferBuffer.Handle, | ||||||
|  | 			copyParams.ToRefresh() | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public void DownloadFromTexture( | ||||||
|  | 		in TextureRegion textureRegion, | ||||||
|  | 		TransferBuffer transferBuffer, | ||||||
|  | 		in BufferImageCopy copyParams | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertBufferBoundsCheck(transferBuffer.Size, copyParams.BufferOffset, textureRegion.Size); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_DownloadFromTexture( | ||||||
|  | 			Handle, | ||||||
|  | 			textureRegion.ToRefresh(), | ||||||
|  | 			transferBuffer.Handle, | ||||||
|  | 			copyParams.ToRefresh() | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #if DEBUG | ||||||
|  | 	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 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,25 @@ | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | 
 | ||||||
|  | namespace MoonWorks.Graphics; | ||||||
|  | 
 | ||||||
|  | internal class CopyPassPool | ||||||
|  | { | ||||||
|  | 	private ConcurrentQueue<CopyPass> CopyPasses = new ConcurrentQueue<CopyPass>(); | ||||||
|  | 
 | ||||||
|  | 	public CopyPass Obtain() | ||||||
|  | 	{ | ||||||
|  | 		if (CopyPasses.TryDequeue(out var copyPass)) | ||||||
|  | 		{ | ||||||
|  | 			return copyPass; | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 		{ | ||||||
|  | 			return new CopyPass(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public void Return(CopyPass copyPass) | ||||||
|  | 	{ | ||||||
|  | 		CopyPasses.Enqueue(copyPass); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -47,7 +47,13 @@ namespace MoonWorks.Graphics.Font | ||||||
| 				out float distanceRange | 				out float distanceRange | ||||||
| 			); | 			); | ||||||
| 
 | 
 | ||||||
| 			var texture = Texture.FromImageFile(graphicsDevice, commandBuffer, Path.ChangeExtension(fontPath, ".png")); | 			var imagePath = Path.ChangeExtension(fontPath, ".png"); | ||||||
|  | 			ImageUtils.ImageInfoFromFile(imagePath, out var width, out var height, out var sizeInBytes); | ||||||
|  | 
 | ||||||
|  | 			var uploader = new ResourceUploader(graphicsDevice); | ||||||
|  | 			var texture = uploader.CreateTexture2DFromCompressed(imagePath); | ||||||
|  | 			uploader.Upload(); | ||||||
|  | 			uploader.Dispose(); | ||||||
| 
 | 
 | ||||||
| 			NativeMemory.Free(fontFileByteBuffer); | 			NativeMemory.Free(fontFileByteBuffer); | ||||||
| 			NativeMemory.Free(atlasFileByteBuffer); | 			NativeMemory.Free(atlasFileByteBuffer); | ||||||
|  |  | ||||||
|  | @ -13,10 +13,12 @@ namespace MoonWorks.Graphics.Font | ||||||
| 		private GraphicsDevice GraphicsDevice { get; } | 		private GraphicsDevice GraphicsDevice { get; } | ||||||
| 		public IntPtr Handle { get; } | 		public IntPtr Handle { get; } | ||||||
| 
 | 
 | ||||||
| 		public Buffer VertexBuffer { get; protected set; } = null; | 		public GpuBuffer VertexBuffer { get; protected set; } = null; | ||||||
| 		public Buffer IndexBuffer { get; protected set; } = null; | 		public GpuBuffer IndexBuffer { get; protected set; } = null; | ||||||
| 		public uint PrimitiveCount { get; protected set; } | 		public uint PrimitiveCount { get; protected set; } | ||||||
| 
 | 
 | ||||||
|  | 		private TransferBuffer TransferBuffer; | ||||||
|  | 
 | ||||||
| 		public Font CurrentFont { get; private set; } | 		public Font CurrentFont { get; private set; } | ||||||
| 
 | 
 | ||||||
| 		private byte* StringBytes; | 		private byte* StringBytes; | ||||||
|  | @ -30,8 +32,10 @@ namespace MoonWorks.Graphics.Font | ||||||
| 			StringBytesLength = 128; | 			StringBytesLength = 128; | ||||||
| 			StringBytes = (byte*) NativeMemory.Alloc((nuint) StringBytesLength); | 			StringBytes = (byte*) NativeMemory.Alloc((nuint) StringBytesLength); | ||||||
| 
 | 
 | ||||||
| 			VertexBuffer = Buffer.Create<Vertex>(GraphicsDevice, BufferUsageFlags.Vertex, INITIAL_VERTEX_COUNT); | 			VertexBuffer = GpuBuffer.Create<Vertex>(GraphicsDevice, BufferUsageFlags.Vertex, INITIAL_VERTEX_COUNT); | ||||||
| 			IndexBuffer = Buffer.Create<uint>(GraphicsDevice, BufferUsageFlags.Index, INITIAL_INDEX_COUNT); | 			IndexBuffer = GpuBuffer.Create<uint>(GraphicsDevice, BufferUsageFlags.Index, INITIAL_INDEX_COUNT); | ||||||
|  | 
 | ||||||
|  | 			TransferBuffer = TransferBuffer.Create<byte>(GraphicsDevice, TransferUsage.Buffer, TransferBufferMapFlags.Write, VertexBuffer.Size + IndexBuffer.Size); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Call this to initialize or reset the batch. | 		// Call this to initialize or reset the batch. | ||||||
|  | @ -93,42 +97,60 @@ namespace MoonWorks.Graphics.Font | ||||||
| 				out uint indexDataLengthInBytes | 				out uint indexDataLengthInBytes | ||||||
| 			); | 			); | ||||||
| 
 | 
 | ||||||
|  | 			var vertexSpan = new Span<byte>((void*) vertexDataPointer, (int) vertexDataLengthInBytes); | ||||||
|  | 			var indexSpan = new Span<byte>((void*) indexDataPointer, (int) indexDataLengthInBytes); | ||||||
|  | 
 | ||||||
|  | 			var newTransferBufferNeeded = false; | ||||||
|  | 
 | ||||||
| 			if (VertexBuffer.Size < vertexDataLengthInBytes) | 			if (VertexBuffer.Size < vertexDataLengthInBytes) | ||||||
| 			{ | 			{ | ||||||
| 				VertexBuffer.Dispose(); | 				VertexBuffer.Dispose(); | ||||||
| 				VertexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Vertex, vertexDataLengthInBytes); | 				VertexBuffer = new GpuBuffer(GraphicsDevice, BufferUsageFlags.Vertex, vertexDataLengthInBytes); | ||||||
|  | 				newTransferBufferNeeded = true; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (IndexBuffer.Size < indexDataLengthInBytes) | 			if (IndexBuffer.Size < indexDataLengthInBytes) | ||||||
| 			{ | 			{ | ||||||
| 				IndexBuffer.Dispose(); | 				IndexBuffer.Dispose(); | ||||||
| 				IndexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Index, vertexDataLengthInBytes); | 				IndexBuffer = new GpuBuffer(GraphicsDevice, BufferUsageFlags.Index, vertexDataLengthInBytes); | ||||||
|  | 				newTransferBufferNeeded = true; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (newTransferBufferNeeded) | ||||||
|  | 			{ | ||||||
|  | 				TransferBuffer.Dispose(); | ||||||
|  | 				TransferBuffer = new TransferBuffer(GraphicsDevice, TransferUsage.Buffer, TransferBufferMapFlags.Write, VertexBuffer.Size + IndexBuffer.Size); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (vertexDataLengthInBytes > 0 && indexDataLengthInBytes > 0) | 			if (vertexDataLengthInBytes > 0 && indexDataLengthInBytes > 0) | ||||||
| 			{ | 			{ | ||||||
| 				commandBuffer.SetBufferData(VertexBuffer, vertexDataPointer, 0, vertexDataLengthInBytes); | 				TransferBuffer.SetData(vertexSpan, true); | ||||||
| 				commandBuffer.SetBufferData(IndexBuffer, indexDataPointer, 0, indexDataLengthInBytes); | 				TransferBuffer.SetData(indexSpan, (uint) vertexSpan.Length, false); | ||||||
|  | 
 | ||||||
|  | 				var copyPass = commandBuffer.BeginCopyPass(); | ||||||
|  | 				copyPass.UploadToBuffer(TransferBuffer, VertexBuffer, new BufferCopy(0, 0, (uint) vertexSpan.Length), true); | ||||||
|  | 				copyPass.UploadToBuffer(TransferBuffer, IndexBuffer, new BufferCopy((uint) vertexSpan.Length, 0, (uint) indexSpan.Length), true); | ||||||
|  | 				commandBuffer.EndCopyPass(copyPass); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			PrimitiveCount = vertexCount / 2; | 			PrimitiveCount = vertexCount / 2; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Call this AFTER binding your text pipeline! | 		// Call this AFTER binding your text pipeline! | ||||||
| 		public void Render(CommandBuffer commandBuffer, Math.Float.Matrix4x4 transformMatrix) | 		public void Render(RenderPass renderPass, Math.Float.Matrix4x4 transformMatrix) | ||||||
| 		{ | 		{ | ||||||
| 			commandBuffer.BindFragmentSamplers(new TextureSamplerBinding( | 			renderPass.BindFragmentSampler(new TextureSamplerBinding( | ||||||
| 				CurrentFont.Texture, | 				CurrentFont.Texture, | ||||||
| 				GraphicsDevice.LinearSampler | 				GraphicsDevice.LinearSampler | ||||||
| 			)); | 			)); | ||||||
| 			commandBuffer.BindVertexBuffers(VertexBuffer); | 			renderPass.BindVertexBuffer(VertexBuffer); | ||||||
| 			commandBuffer.BindIndexBuffer(IndexBuffer, IndexElementSize.ThirtyTwo); | 			renderPass.BindIndexBuffer(IndexBuffer, IndexElementSize.ThirtyTwo); | ||||||
| 			commandBuffer.DrawIndexedPrimitives( | 			renderPass.PushVertexUniformData(transformMatrix); | ||||||
|  | 			renderPass.PushFragmentUniformData(CurrentFont.DistanceRange); | ||||||
|  | 			renderPass.DrawIndexedPrimitives( | ||||||
| 				0, | 				0, | ||||||
| 				0, | 				0, | ||||||
| 				PrimitiveCount, | 				PrimitiveCount | ||||||
| 				commandBuffer.PushVertexShaderUniforms(transformMatrix), |  | ||||||
| 				commandBuffer.PushFragmentShaderUniforms(CurrentFont.DistanceRange) |  | ||||||
| 			); | 			); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ using System.IO; | ||||||
| using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||||
| using MoonWorks.Video; | using MoonWorks.Video; | ||||||
| using RefreshCS; | using RefreshCS; | ||||||
| using WellspringCS; |  | ||||||
| 
 | 
 | ||||||
| namespace MoonWorks.Graphics | namespace MoonWorks.Graphics | ||||||
| { | { | ||||||
|  | @ -14,17 +13,17 @@ namespace MoonWorks.Graphics | ||||||
| 	public class GraphicsDevice : IDisposable | 	public class GraphicsDevice : IDisposable | ||||||
| 	{ | 	{ | ||||||
| 		public IntPtr Handle { get; } | 		public IntPtr Handle { get; } | ||||||
| 		public Backend Backend { get; } | 		public BackendFlags Backend { get; } | ||||||
| 
 | 		public bool DebugMode { get; } | ||||||
| 		private uint windowFlags; |  | ||||||
| 		public SDL2.SDL.SDL_WindowFlags WindowFlags => (SDL2.SDL.SDL_WindowFlags) windowFlags; |  | ||||||
| 
 | 
 | ||||||
| 		// Built-in video pipeline | 		// Built-in video pipeline | ||||||
| 		internal GraphicsPipeline VideoPipeline { get; } | 		internal GraphicsPipeline VideoPipeline { get; } | ||||||
| 
 | 
 | ||||||
| 		// Built-in text shader info | 		// Built-in text shader info | ||||||
| 		public GraphicsShaderInfo TextVertexShaderInfo { get; } | 		public Shader TextVertexShader; | ||||||
| 		public GraphicsShaderInfo TextFragmentShaderInfo { get; } | 		public Shader TextFragmentShader; | ||||||
|  | 		public GraphicsPipelineResourceInfo TextVertexShaderInfo { get; } | ||||||
|  | 		public GraphicsPipelineResourceInfo TextFragmentShaderInfo { get; } | ||||||
| 		public VertexInputState TextVertexInputState { get; } | 		public VertexInputState TextVertexInputState { get; } | ||||||
| 
 | 
 | ||||||
| 		// Built-in samplers | 		// Built-in samplers | ||||||
|  | @ -34,73 +33,137 @@ namespace MoonWorks.Graphics | ||||||
| 		public bool IsDisposed { get; private set; } | 		public bool IsDisposed { get; private set; } | ||||||
| 
 | 
 | ||||||
| 		private readonly HashSet<GCHandle> resources = new HashSet<GCHandle>(); | 		private readonly HashSet<GCHandle> resources = new HashSet<GCHandle>(); | ||||||
| 		private FencePool FencePool; |  | ||||||
| 		private CommandBufferPool CommandBufferPool; | 		private CommandBufferPool CommandBufferPool; | ||||||
|  | 		private FencePool FencePool; | ||||||
|  | 		internal RenderPassPool RenderPassPool = new RenderPassPool(); | ||||||
|  | 		internal ComputePassPool ComputePassPool = new ComputePassPool(); | ||||||
|  | 		internal CopyPassPool CopyPassPool = new CopyPassPool(); | ||||||
| 
 | 
 | ||||||
| 		internal GraphicsDevice( | 		internal unsafe GraphicsDevice( | ||||||
| 			Backend preferredBackend, | 			BackendFlags preferredBackends, | ||||||
| 			bool debugMode | 			bool debugMode | ||||||
| 		) { | 		) { | ||||||
| 			Backend = (Backend) Refresh.Refresh_SelectBackend((Refresh.Backend) preferredBackend, out windowFlags); | 			if (preferredBackends == BackendFlags.Invalid) | ||||||
| 
 |  | ||||||
| 			if (Backend == Backend.Invalid) |  | ||||||
| 			{ | 			{ | ||||||
| 				throw new System.Exception("Could not set graphics backend!"); | 				throw new System.Exception("Could not set graphics backend!"); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			Handle = Refresh.Refresh_CreateDevice( | 			Handle = Refresh.Refresh_CreateDevice( | ||||||
|  | 				(Refresh.BackendFlags) preferredBackends, | ||||||
| 				Conversions.BoolToByte(debugMode) | 				Conversions.BoolToByte(debugMode) | ||||||
| 			); | 			); | ||||||
| 
 | 
 | ||||||
|  | 			DebugMode = debugMode; | ||||||
| 			// TODO: check for CreateDevice fail | 			// TODO: check for CreateDevice fail | ||||||
| 
 | 
 | ||||||
|  | 			Backend = (BackendFlags) Refresh.Refresh_GetBackend(Handle); | ||||||
|  | 
 | ||||||
| 			// Check for replacement stock shaders | 			// Check for replacement stock shaders | ||||||
| 			string basePath = System.AppContext.BaseDirectory; | 			string basePath = System.AppContext.BaseDirectory; | ||||||
| 
 | 
 | ||||||
| 			string videoVertPath = Path.Combine(basePath, "video_fullscreen.vert.refresh"); | 			string fullscreenVertPath = Path.Combine(basePath, "fullscreen.vert.refresh"); | ||||||
| 			string videoFragPath = Path.Combine(basePath, "video_yuv2rgba.frag.refresh"); |  | ||||||
| 
 | 
 | ||||||
| 			string textVertPath = Path.Combine(basePath, "text_transform.vert.refresh"); | 			string textVertPath = Path.Combine(basePath, "text_transform.vert.refresh"); | ||||||
| 			string textFragPath = Path.Combine(basePath, "text_msdf.frag.refresh"); | 			string textFragPath = Path.Combine(basePath, "text_msdf.frag.refresh"); | ||||||
| 
 | 
 | ||||||
| 			ShaderModule videoVertShader; | 			string videoFragPath = Path.Combine(basePath, "video_yuv2rgba.frag.refresh"); | ||||||
| 			ShaderModule videoFragShader; |  | ||||||
| 
 | 
 | ||||||
| 			ShaderModule textVertShader; | 			Shader fullscreenVertShader; | ||||||
| 			ShaderModule textFragShader; |  | ||||||
| 
 | 
 | ||||||
| 			if (File.Exists(videoVertPath) && File.Exists(videoFragPath)) | 			Shader textVertShader; | ||||||
|  | 			Shader textFragShader; | ||||||
|  | 
 | ||||||
|  | 			Shader videoFragShader; | ||||||
|  | 
 | ||||||
|  | 			if (File.Exists(fullscreenVertPath)) | ||||||
| 			{ | 			{ | ||||||
| 				videoVertShader = new ShaderModule(this, videoVertPath); | 				fullscreenVertShader = new Shader( | ||||||
| 				videoFragShader = new ShaderModule(this, videoFragPath); | 					this, | ||||||
|  | 					fullscreenVertPath, | ||||||
|  | 					"main", | ||||||
|  | 					ShaderStage.Vertex, | ||||||
|  | 					ShaderFormat.SECRET | ||||||
|  | 				); | ||||||
| 			} | 			} | ||||||
| 			else | 			else | ||||||
| 			{ | 			{ | ||||||
| 				// use defaults | 				// use defaults | ||||||
| 				var assembly = typeof(GraphicsDevice).Assembly; | 				var assembly = typeof(GraphicsDevice).Assembly; | ||||||
|  | 				using var vertStream = assembly.GetManifestResourceStream("MoonWorks.Graphics.StockShaders.Fullscreen.vert.spv"); | ||||||
|  | 				fullscreenVertShader = new Shader( | ||||||
|  | 					this, | ||||||
|  | 					vertStream, | ||||||
|  | 					"main", | ||||||
|  | 					ShaderStage.Vertex, | ||||||
|  | 					ShaderFormat.SPIRV | ||||||
|  | 				); | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 				using var vertStream = assembly.GetManifestResourceStream("MoonWorks.Graphics.StockShaders.VideoFullscreen.vert.refresh"); | 			if (File.Exists(videoFragPath)) | ||||||
| 				using var fragStream = assembly.GetManifestResourceStream("MoonWorks.Graphics.StockShaders.VideoYUV2RGBA.frag.refresh"); | 			{ | ||||||
| 
 | 				videoFragShader = new Shader( | ||||||
| 				videoVertShader = new ShaderModule(this, vertStream); | 					this, | ||||||
| 				videoFragShader = new ShaderModule(this, fragStream); | 					videoFragPath, | ||||||
|  | 					"main", | ||||||
|  | 					ShaderStage.Fragment, | ||||||
|  | 					ShaderFormat.SECRET | ||||||
|  | 				); | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				// use defaults | ||||||
|  | 				var assembly = typeof(GraphicsDevice).Assembly; | ||||||
|  | 				using var fragStream = assembly.GetManifestResourceStream("MoonWorks.Graphics.StockShaders.VideoYUV2RGBA.frag.spv"); | ||||||
|  | 				videoFragShader = new Shader( | ||||||
|  | 					this, | ||||||
|  | 					fragStream, | ||||||
|  | 					"main", | ||||||
|  | 					ShaderStage.Fragment, | ||||||
|  | 					ShaderFormat.SPIRV | ||||||
|  | 				); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (File.Exists(textVertPath) && File.Exists(textFragPath)) | 			if (File.Exists(textVertPath) && File.Exists(textFragPath)) | ||||||
| 			{ | 			{ | ||||||
| 				textVertShader = new ShaderModule(this, textVertPath); | 				textVertShader = new Shader( | ||||||
| 				textFragShader = new ShaderModule(this, textFragPath); | 					this, | ||||||
|  | 					textVertPath, | ||||||
|  | 					"main", | ||||||
|  | 					ShaderStage.Vertex, | ||||||
|  | 					ShaderFormat.SECRET | ||||||
|  | 				); | ||||||
|  | 
 | ||||||
|  | 				textFragShader = new Shader( | ||||||
|  | 					this, | ||||||
|  | 					textFragPath, | ||||||
|  | 					"main", | ||||||
|  | 					ShaderStage.Fragment, | ||||||
|  | 					ShaderFormat.SECRET | ||||||
|  | 				); | ||||||
| 			} | 			} | ||||||
| 			else | 			else | ||||||
| 			{ | 			{ | ||||||
| 				// use defaults | 				// use defaults | ||||||
| 				var assembly = typeof(GraphicsDevice).Assembly; | 				var assembly = typeof(GraphicsDevice).Assembly; | ||||||
| 
 | 
 | ||||||
| 				using var vertStream = assembly.GetManifestResourceStream("MoonWorks.Graphics.StockShaders.TextTransform.vert.refresh"); | 				using var vertStream = assembly.GetManifestResourceStream("MoonWorks.Graphics.StockShaders.TextTransform.vert.spv"); | ||||||
| 				using var fragStream = assembly.GetManifestResourceStream("MoonWorks.Graphics.StockShaders.TextMSDF.frag.refresh"); | 				using var fragStream = assembly.GetManifestResourceStream("MoonWorks.Graphics.StockShaders.TextMSDF.frag.spv"); | ||||||
| 
 | 
 | ||||||
| 				textVertShader = new ShaderModule(this, vertStream); | 				textVertShader = new Shader( | ||||||
| 				textFragShader = new ShaderModule(this, fragStream); | 					this, | ||||||
|  | 					vertStream, | ||||||
|  | 					"main", | ||||||
|  | 					ShaderStage.Fragment, | ||||||
|  | 					ShaderFormat.SPIRV | ||||||
|  | 				); | ||||||
|  | 
 | ||||||
|  | 				textFragShader = new Shader( | ||||||
|  | 					this, | ||||||
|  | 					fragStream, | ||||||
|  | 					"main", | ||||||
|  | 					ShaderStage.Fragment, | ||||||
|  | 					ShaderFormat.SPIRV | ||||||
|  | 				); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			VideoPipeline = new GraphicsPipeline( | 			VideoPipeline = new GraphicsPipeline( | ||||||
|  | @ -114,16 +177,12 @@ namespace MoonWorks.Graphics | ||||||
| 						) | 						) | ||||||
| 					), | 					), | ||||||
| 					DepthStencilState = DepthStencilState.Disable, | 					DepthStencilState = DepthStencilState.Disable, | ||||||
| 					VertexShaderInfo = GraphicsShaderInfo.Create( | 					VertexShader = fullscreenVertShader, | ||||||
| 						videoVertShader, | 					FragmentShader = videoFragShader, | ||||||
| 						"main", | 					FragmentShaderResourceInfo = new GraphicsPipelineResourceInfo | ||||||
| 						0 | 					{ | ||||||
| 					), | 						SamplerCount = 3 | ||||||
| 					FragmentShaderInfo = GraphicsShaderInfo.Create( | 					}, | ||||||
| 						videoFragShader, |  | ||||||
| 						"main", |  | ||||||
| 						3 |  | ||||||
| 					), |  | ||||||
| 					VertexInputState = VertexInputState.Empty, | 					VertexInputState = VertexInputState.Empty, | ||||||
| 					RasterizerState = RasterizerState.CCW_CullNone, | 					RasterizerState = RasterizerState.CCW_CullNone, | ||||||
| 					PrimitiveType = PrimitiveType.TriangleList, | 					PrimitiveType = PrimitiveType.TriangleList, | ||||||
|  | @ -131,8 +190,15 @@ namespace MoonWorks.Graphics | ||||||
| 				} | 				} | ||||||
| 			); | 			); | ||||||
| 
 | 
 | ||||||
| 			TextVertexShaderInfo = GraphicsShaderInfo.Create<Math.Float.Matrix4x4>(textVertShader, "main", 0); | 			TextVertexShader = textVertShader; | ||||||
| 			TextFragmentShaderInfo = GraphicsShaderInfo.Create<float>(textFragShader, "main", 1); | 			TextVertexShaderInfo = new GraphicsPipelineResourceInfo(); | ||||||
|  | 
 | ||||||
|  | 			TextFragmentShader = textFragShader; | ||||||
|  | 			TextFragmentShaderInfo = new GraphicsPipelineResourceInfo | ||||||
|  | 			{ | ||||||
|  | 				SamplerCount = 1 | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
| 			TextVertexInputState = VertexInputState.CreateSingleBinding<Font.Vertex>(); | 			TextVertexInputState = VertexInputState.CreateSingleBinding<Font.Vertex>(); | ||||||
| 
 | 
 | ||||||
| 			PointSampler = new Sampler(this, SamplerCreateInfo.PointClamp); | 			PointSampler = new Sampler(this, SamplerCreateInfo.PointClamp); | ||||||
|  | @ -145,20 +211,25 @@ namespace MoonWorks.Graphics | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Prepares a window so that frames can be presented to it. | 		/// Prepares a window so that frames can be presented to it. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
|  | 		/// <param name="swapchainComposition">The desired composition of the swapchain. Ignore this unless you are using HDR or tonemapping.</param> | ||||||
| 		/// <param name="presentMode">The desired presentation mode for the window. Roughly equivalent to V-Sync.</param> | 		/// <param name="presentMode">The desired presentation mode for the window. Roughly equivalent to V-Sync.</param> | ||||||
| 		/// <returns>True if successfully claimed.</returns> | 		/// <returns>True if successfully claimed.</returns> | ||||||
| 		public bool ClaimWindow(Window window, PresentMode presentMode) | 		public bool ClaimWindow( | ||||||
| 		{ | 			Window window, | ||||||
|  | 			SwapchainComposition swapchainComposition, | ||||||
|  | 			PresentMode presentMode | ||||||
|  | 		) { | ||||||
| 			if (window.Claimed) | 			if (window.Claimed) | ||||||
| 			{ | 			{ | ||||||
| 				Logger.LogError("Window already claimed!"); | 				Logger.LogError("Window already claimed!"); | ||||||
| 				return false; | 				return false; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			var success = Conversions.ByteToBool( | 			var success = Conversions.IntToBool( | ||||||
| 				Refresh.Refresh_ClaimWindow( | 				Refresh.Refresh_ClaimWindow( | ||||||
| 					Handle, | 					Handle, | ||||||
| 					window.Handle, | 					window.Handle, | ||||||
|  | 					(Refresh.SwapchainComposition) swapchainComposition, | ||||||
| 					(Refresh.PresentMode) presentMode | 					(Refresh.PresentMode) presentMode | ||||||
| 				) | 				) | ||||||
| 			); | 			); | ||||||
|  | @ -166,7 +237,9 @@ namespace MoonWorks.Graphics | ||||||
| 			if (success) | 			if (success) | ||||||
| 			{ | 			{ | ||||||
| 				window.Claimed = true; | 				window.Claimed = true; | ||||||
|  | 				window.SwapchainComposition = swapchainComposition; | ||||||
| 				window.SwapchainFormat = GetSwapchainFormat(window); | 				window.SwapchainFormat = GetSwapchainFormat(window); | ||||||
|  | 
 | ||||||
| 				if (window.SwapchainTexture == null) | 				if (window.SwapchainTexture == null) | ||||||
| 				{ | 				{ | ||||||
| 					window.SwapchainTexture = new Texture(this, window.SwapchainFormat); | 					window.SwapchainTexture = new Texture(this, window.SwapchainFormat); | ||||||
|  | @ -201,17 +274,21 @@ namespace MoonWorks.Graphics | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		/// <param name="window"></param> | 		/// <param name="window"></param> | ||||||
| 		/// <param name="presentMode"></param> | 		/// <param name="presentMode"></param> | ||||||
| 		public void SetPresentMode(Window window, PresentMode presentMode) | 		public void SetSwapchainParameters( | ||||||
| 		{ | 			Window window, | ||||||
|  | 			SwapchainComposition swapchainComposition, | ||||||
|  | 			PresentMode presentMode | ||||||
|  | 		) { | ||||||
| 			if (!window.Claimed) | 			if (!window.Claimed) | ||||||
| 			{ | 			{ | ||||||
| 				Logger.LogError("Cannot set present mode on unclaimed window!"); | 				Logger.LogError("Cannot set present mode on unclaimed window!"); | ||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			Refresh.Refresh_SetSwapchainPresentMode( | 			Refresh.Refresh_SetSwapchainParameters( | ||||||
| 				Handle, | 				Handle, | ||||||
| 				window.Handle, | 				window.Handle, | ||||||
|  | 				(Refresh.SwapchainComposition) swapchainComposition, | ||||||
| 				(Refresh.PresentMode) presentMode | 				(Refresh.PresentMode) presentMode | ||||||
| 			); | 			); | ||||||
| 		} | 		} | ||||||
|  | @ -244,7 +321,6 @@ namespace MoonWorks.Graphics | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| 			Refresh.Refresh_Submit( | 			Refresh.Refresh_Submit( | ||||||
| 				Handle, |  | ||||||
| 				commandBuffer.Handle | 				commandBuffer.Handle | ||||||
| 			); | 			); | ||||||
| 
 | 
 | ||||||
|  | @ -262,7 +338,6 @@ namespace MoonWorks.Graphics | ||||||
| 		public Fence SubmitAndAcquireFence(CommandBuffer commandBuffer) | 		public Fence SubmitAndAcquireFence(CommandBuffer commandBuffer) | ||||||
| 		{ | 		{ | ||||||
| 			var fenceHandle = Refresh.Refresh_SubmitAndAcquireFence( | 			var fenceHandle = Refresh.Refresh_SubmitAndAcquireFence( | ||||||
| 				Handle, |  | ||||||
| 				commandBuffer.Handle | 				commandBuffer.Handle | ||||||
| 			); | 			); | ||||||
| 
 | 
 | ||||||
|  | @ -283,16 +358,15 @@ namespace MoonWorks.Graphics | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Waits for the given fence to become signaled. | 		/// Waits for the given fence to become signaled. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		public unsafe void WaitForFences(Fence fence) | 		public unsafe void WaitForFence(Fence fence) | ||||||
| 		{ | 		{ | ||||||
| 			var handlePtr = stackalloc nint[1]; | 			var fenceHandle = fence.Handle; | ||||||
| 			handlePtr[0] = fence.Handle; |  | ||||||
| 
 | 
 | ||||||
| 			Refresh.Refresh_WaitForFences( | 			Refresh.Refresh_WaitForFences( | ||||||
| 				Handle, | 				Handle, | ||||||
| 				1, | 				1, | ||||||
| 				1, | 				&fenceHandle, | ||||||
| 				(nint) handlePtr | 				1 | ||||||
| 			); | 			); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -300,76 +374,7 @@ namespace MoonWorks.Graphics | ||||||
| 		/// Wait for one or more fences to become signaled. | 		/// Wait for one or more fences to become signaled. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		/// <param name="waitAll">If true, will wait for all given fences to be signaled.</param> | 		/// <param name="waitAll">If true, will wait for all given fences to be signaled.</param> | ||||||
| 		public unsafe void WaitForFences( | 		public unsafe void WaitForFences(Span<Fence> fences, bool waitAll) | ||||||
| 			Fence fenceOne, |  | ||||||
| 			Fence fenceTwo, |  | ||||||
| 			bool waitAll |  | ||||||
| 		) { |  | ||||||
| 			var handlePtr = stackalloc nint[2]; |  | ||||||
| 			handlePtr[0] = fenceOne.Handle; |  | ||||||
| 			handlePtr[1] = fenceTwo.Handle; |  | ||||||
| 
 |  | ||||||
| 			Refresh.Refresh_WaitForFences( |  | ||||||
| 				Handle, |  | ||||||
| 				Conversions.BoolToByte(waitAll), |  | ||||||
| 				2, |  | ||||||
| 				(nint) handlePtr |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Wait for one or more fences to become signaled. |  | ||||||
| 		/// </summary> |  | ||||||
| 		/// <param name="waitAll">If true, will wait for all given fences to be signaled.</param> |  | ||||||
| 		public unsafe void WaitForFences( |  | ||||||
| 			Fence fenceOne, |  | ||||||
| 			Fence fenceTwo, |  | ||||||
| 			Fence fenceThree, |  | ||||||
| 			bool waitAll |  | ||||||
| 		) { |  | ||||||
| 			var handlePtr = stackalloc nint[3]; |  | ||||||
| 			handlePtr[0] = fenceOne.Handle; |  | ||||||
| 			handlePtr[1] = fenceTwo.Handle; |  | ||||||
| 			handlePtr[2] = fenceThree.Handle; |  | ||||||
| 
 |  | ||||||
| 			Refresh.Refresh_WaitForFences( |  | ||||||
| 				Handle, |  | ||||||
| 				Conversions.BoolToByte(waitAll), |  | ||||||
| 				3, |  | ||||||
| 				(nint) handlePtr |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Wait for one or more fences to become signaled. |  | ||||||
| 		/// </summary> |  | ||||||
| 		/// <param name="waitAll">If true, will wait for all given fences to be signaled.</param> |  | ||||||
| 		public unsafe void WaitForFences( |  | ||||||
| 			Fence fenceOne, |  | ||||||
| 			Fence fenceTwo, |  | ||||||
| 			Fence fenceThree, |  | ||||||
| 			Fence fenceFour, |  | ||||||
| 			bool waitAll |  | ||||||
| 		) { |  | ||||||
| 			var handlePtr = stackalloc nint[4]; |  | ||||||
| 			handlePtr[0] = fenceOne.Handle; |  | ||||||
| 			handlePtr[1] = fenceTwo.Handle; |  | ||||||
| 			handlePtr[2] = fenceThree.Handle; |  | ||||||
| 			handlePtr[3] = fenceFour.Handle; |  | ||||||
| 
 |  | ||||||
| 			Refresh.Refresh_WaitForFences( |  | ||||||
| 				Handle, |  | ||||||
| 				Conversions.BoolToByte(waitAll), |  | ||||||
| 				4, |  | ||||||
| 				(nint) handlePtr |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Wait for one or more fences to become signaled. |  | ||||||
| 		/// </summary> |  | ||||||
| 		/// <param name="waitAll">If true, will wait for all given fences to be signaled.</param> |  | ||||||
| 		public unsafe void WaitForFences(Fence[] fences, bool waitAll) |  | ||||||
| 		{ | 		{ | ||||||
| 			var handlePtr = stackalloc nint[fences.Length]; | 			var handlePtr = stackalloc nint[fences.Length]; | ||||||
| 
 | 
 | ||||||
|  | @ -380,9 +385,9 @@ namespace MoonWorks.Graphics | ||||||
| 
 | 
 | ||||||
| 			Refresh.Refresh_WaitForFences( | 			Refresh.Refresh_WaitForFences( | ||||||
| 				Handle, | 				Handle, | ||||||
| 				Conversions.BoolToByte(waitAll), | 				Conversions.BoolToInt(waitAll), | ||||||
| 				4, | 				handlePtr, | ||||||
| 				(nint) handlePtr | 				(uint) fences.Length | ||||||
| 			); | 			); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -414,7 +419,12 @@ namespace MoonWorks.Graphics | ||||||
| 
 | 
 | ||||||
| 		private TextureFormat GetSwapchainFormat(Window window) | 		private TextureFormat GetSwapchainFormat(Window window) | ||||||
| 		{ | 		{ | ||||||
| 			return (TextureFormat) Refresh.Refresh_GetSwapchainFormat(Handle, window.Handle); | 			if (!window.Claimed) | ||||||
|  | 			{ | ||||||
|  | 				throw new System.ArgumentException("Cannot get swapchain format of unclaimed window!"); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return (TextureFormat) Refresh.Refresh_GetSwapchainTextureFormat(Handle, window.Handle); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		internal void AddResourceReference(GCHandle resourceReference) | 		internal void AddResourceReference(GCHandle resourceReference) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,456 @@ | ||||||
|  | using System; | ||||||
|  | using System.IO; | ||||||
|  | using System.Runtime.InteropServices; | ||||||
|  | using RefreshCS; | ||||||
|  | 
 | ||||||
|  | namespace MoonWorks.Graphics | ||||||
|  | { | ||||||
|  | 	public static class ImageUtils | ||||||
|  | 	{ | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Gets pointer to pixel data from compressed image byte data. | ||||||
|  | 		/// | ||||||
|  | 		/// The returned pointer must be freed by calling FreePixelData. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public static unsafe byte* GetPixelDataFromBytes( | ||||||
|  | 			Span<byte> data, | ||||||
|  | 			out uint width, | ||||||
|  | 			out uint height, | ||||||
|  | 			out uint sizeInBytes | ||||||
|  | 		) { | ||||||
|  | 			fixed (byte* ptr = data) | ||||||
|  | 			{ | ||||||
|  | 				var pixelData = | ||||||
|  | 					Refresh.Refresh_Image_Load( | ||||||
|  | 					ptr, | ||||||
|  | 					data.Length, | ||||||
|  | 					out var w, | ||||||
|  | 					out var h, | ||||||
|  | 					out var len | ||||||
|  | 				); | ||||||
|  | 
 | ||||||
|  | 				width = (uint) w; | ||||||
|  | 				height = (uint) h; | ||||||
|  | 				sizeInBytes = (uint) len; | ||||||
|  | 
 | ||||||
|  | 				return pixelData; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Gets pointer to pixel data from a compressed image stream. | ||||||
|  | 		/// | ||||||
|  | 		/// The returned pointer must be freed by calling FreePixelData. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public static unsafe byte* GetPixelDataFromStream( | ||||||
|  | 			Stream stream, | ||||||
|  | 			out uint width, | ||||||
|  | 			out uint height, | ||||||
|  | 			out uint sizeInBytes | ||||||
|  | 		) { | ||||||
|  | 			var length = stream.Length; | ||||||
|  | 			var buffer = NativeMemory.Alloc((nuint) length); | ||||||
|  | 			var span = new Span<byte>(buffer, (int) length); | ||||||
|  | 			stream.ReadExactly(span); | ||||||
|  | 
 | ||||||
|  | 			var pixelData = GetPixelDataFromBytes(span, out width, out height, out sizeInBytes); | ||||||
|  | 
 | ||||||
|  | 			NativeMemory.Free(buffer); | ||||||
|  | 
 | ||||||
|  | 			return pixelData; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Gets pointer to pixel data from a compressed image file. | ||||||
|  | 		/// | ||||||
|  | 		/// The returned pointer must be freed by calling FreePixelData. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public static unsafe byte* GetPixelDataFromFile( | ||||||
|  | 			string path, | ||||||
|  | 			out uint width, | ||||||
|  | 			out uint height, | ||||||
|  | 			out uint sizeInBytes | ||||||
|  | 		) { | ||||||
|  | 			var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); | ||||||
|  | 			return GetPixelDataFromStream(fileStream, out width, out height, out sizeInBytes); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Get metadata from compressed image bytes. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public static unsafe bool ImageInfoFromBytes( | ||||||
|  | 			Span<byte> data, | ||||||
|  | 			out uint width, | ||||||
|  | 			out uint height, | ||||||
|  | 			out uint sizeInBytes | ||||||
|  | 		) { | ||||||
|  | 			fixed (byte* ptr = data) | ||||||
|  | 			{ | ||||||
|  | 				var result = | ||||||
|  | 					Refresh.Refresh_Image_Info( | ||||||
|  | 					ptr, | ||||||
|  | 					data.Length, | ||||||
|  | 					out var w, | ||||||
|  | 					out var h, | ||||||
|  | 					out var len | ||||||
|  | 				); | ||||||
|  | 
 | ||||||
|  | 				width = (uint) w; | ||||||
|  | 				height = (uint) h; | ||||||
|  | 				sizeInBytes = (uint) len; | ||||||
|  | 
 | ||||||
|  | 				return Conversions.IntToBool(result); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Get metadata from a compressed image stream. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public static unsafe bool ImageInfoFromStream( | ||||||
|  | 			Stream stream, | ||||||
|  | 			out uint width, | ||||||
|  | 			out uint height, | ||||||
|  | 			out uint sizeInBytes | ||||||
|  | 		) { | ||||||
|  | 			var length = stream.Length; | ||||||
|  | 			var buffer = NativeMemory.Alloc((nuint) length); | ||||||
|  | 			var span = new Span<byte>(buffer, (int) length); | ||||||
|  | 			stream.ReadExactly(span); | ||||||
|  | 
 | ||||||
|  | 			var result = ImageInfoFromBytes(span, out width, out height, out sizeInBytes); | ||||||
|  | 
 | ||||||
|  | 			NativeMemory.Free(buffer); | ||||||
|  | 
 | ||||||
|  | 			return result; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Get metadata from a compressed image file. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public static bool ImageInfoFromFile( | ||||||
|  | 			string path, | ||||||
|  | 			out uint width, | ||||||
|  | 			out uint height, | ||||||
|  | 			out uint sizeInBytes | ||||||
|  | 		) { | ||||||
|  | 			var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); | ||||||
|  | 			return ImageInfoFromStream(fileStream, out width, out height, out sizeInBytes); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Frees pixel data obtained from GetPixelData methods. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public unsafe static void FreePixelData(byte* pixels) | ||||||
|  | 		{ | ||||||
|  | 			Refresh.Refresh_Image_Free(pixels); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Saves pixel data contained in a TransferBuffer to a PNG file. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public static unsafe void SavePNG( | ||||||
|  | 			string path, | ||||||
|  | 			TransferBuffer transferBuffer, | ||||||
|  | 			uint bufferOffsetInBytes, | ||||||
|  | 			int width, | ||||||
|  | 			int height, | ||||||
|  | 			bool bgra | ||||||
|  | 		) { | ||||||
|  | 			var sizeInBytes = width * height * 4; | ||||||
|  | 
 | ||||||
|  | 			var pixelsPtr = (byte*) NativeMemory.Alloc((nuint) sizeInBytes); | ||||||
|  | 			var pixelsSpan = new Span<byte>(pixelsPtr, sizeInBytes); | ||||||
|  | 
 | ||||||
|  | 			transferBuffer.GetData(pixelsSpan, bufferOffsetInBytes); | ||||||
|  | 
 | ||||||
|  | 			if (bgra) | ||||||
|  | 			{ | ||||||
|  | 				// if data is bgra, we have to swap the R and B channels | ||||||
|  | 				var rgbaPtr = (byte*) NativeMemory.Alloc((nuint) sizeInBytes); | ||||||
|  | 				var rgbaSpan = new Span<byte>(rgbaPtr, sizeInBytes); | ||||||
|  | 
 | ||||||
|  | 				for (var i = 0; i < sizeInBytes; i += 4) | ||||||
|  | 				{ | ||||||
|  | 					rgbaSpan[i] = pixelsSpan[i + 2]; | ||||||
|  | 					rgbaSpan[i + 1] = pixelsSpan[i + 1]; | ||||||
|  | 					rgbaSpan[i + 2] = pixelsSpan[i]; | ||||||
|  | 					rgbaSpan[i + 3] = pixelsSpan[i + 3]; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				NativeMemory.Free(pixelsPtr); | ||||||
|  | 				pixelsPtr = rgbaPtr; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			Refresh.Refresh_Image_SavePNG(path, pixelsPtr, width, height); | ||||||
|  | 			NativeMemory.Free(pixelsPtr); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// DDS loading extension, based on MojoDDS | ||||||
|  | 		// Taken from https://github.com/FNA-XNA/FNA/blob/1e49f868f595f62bc6385db45949a03186a7cd7f/src/Graphics/Texture.cs#L194 | ||||||
|  | 		public static void ParseDDS( | ||||||
|  | 			BinaryReader reader, | ||||||
|  | 			out TextureFormat format, | ||||||
|  | 			out int width, | ||||||
|  | 			out int height, | ||||||
|  | 			out int levels, | ||||||
|  | 			out bool isCube | ||||||
|  | 		) { | ||||||
|  | 			// A whole bunch of magic numbers, yay DDS! | ||||||
|  | 			const uint DDS_MAGIC = 0x20534444; | ||||||
|  | 			const uint DDS_HEADERSIZE = 124; | ||||||
|  | 			const uint DDS_PIXFMTSIZE = 32; | ||||||
|  | 			const uint DDSD_HEIGHT = 0x2; | ||||||
|  | 			const uint DDSD_WIDTH = 0x4; | ||||||
|  | 			const uint DDSD_PITCH = 0x8; | ||||||
|  | 			const uint DDSD_LINEARSIZE = 0x80000; | ||||||
|  | 			const uint DDSD_REQ = ( | ||||||
|  | 				DDSD_HEIGHT | DDSD_WIDTH | ||||||
|  | 			); | ||||||
|  | 			const uint DDSCAPS_MIPMAP = 0x400000; | ||||||
|  | 			const uint DDSCAPS_TEXTURE = 0x1000; | ||||||
|  | 			const uint DDSCAPS2_CUBEMAP = 0x200; | ||||||
|  | 			const uint DDPF_FOURCC = 0x4; | ||||||
|  | 			const uint DDPF_RGB = 0x40; | ||||||
|  | 			const uint FOURCC_DXT1 = 0x31545844; | ||||||
|  | 			const uint FOURCC_DXT3 = 0x33545844; | ||||||
|  | 			const uint FOURCC_DXT5 = 0x35545844; | ||||||
|  | 			const uint FOURCC_DX10 = 0x30315844; | ||||||
|  | 			const uint pitchAndLinear = ( | ||||||
|  | 				DDSD_PITCH | DDSD_LINEARSIZE | ||||||
|  | 			); | ||||||
|  | 
 | ||||||
|  | 			// File should start with 'DDS ' | ||||||
|  | 			if (reader.ReadUInt32() != DDS_MAGIC) | ||||||
|  | 			{ | ||||||
|  | 				throw new NotSupportedException("Not a DDS!"); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// Texture info | ||||||
|  | 			uint size = reader.ReadUInt32(); | ||||||
|  | 			if (size != DDS_HEADERSIZE) | ||||||
|  | 			{ | ||||||
|  | 				throw new NotSupportedException("Invalid DDS header!"); | ||||||
|  | 			} | ||||||
|  | 			uint flags = reader.ReadUInt32(); | ||||||
|  | 			if ((flags & DDSD_REQ) != DDSD_REQ) | ||||||
|  | 			{ | ||||||
|  | 				throw new NotSupportedException("Invalid DDS flags!"); | ||||||
|  | 			} | ||||||
|  | 			if ((flags & pitchAndLinear) == pitchAndLinear) | ||||||
|  | 			{ | ||||||
|  | 				throw new NotSupportedException("Invalid DDS flags!"); | ||||||
|  | 			} | ||||||
|  | 			height = reader.ReadInt32(); | ||||||
|  | 			width = reader.ReadInt32(); | ||||||
|  | 			reader.ReadUInt32(); // dwPitchOrLinearSize, unused | ||||||
|  | 			reader.ReadUInt32(); // dwDepth, unused | ||||||
|  | 			levels = reader.ReadInt32(); | ||||||
|  | 
 | ||||||
|  | 			// "Reserved" | ||||||
|  | 			reader.ReadBytes(4 * 11); | ||||||
|  | 
 | ||||||
|  | 			// Format info | ||||||
|  | 			uint formatSize = reader.ReadUInt32(); | ||||||
|  | 			if (formatSize != DDS_PIXFMTSIZE) | ||||||
|  | 			{ | ||||||
|  | 				throw new NotSupportedException("Bogus PIXFMTSIZE!"); | ||||||
|  | 			} | ||||||
|  | 			uint formatFlags = reader.ReadUInt32(); | ||||||
|  | 			uint formatFourCC = reader.ReadUInt32(); | ||||||
|  | 			uint formatRGBBitCount = reader.ReadUInt32(); | ||||||
|  | 			uint formatRBitMask = reader.ReadUInt32(); | ||||||
|  | 			uint formatGBitMask = reader.ReadUInt32(); | ||||||
|  | 			uint formatBBitMask = reader.ReadUInt32(); | ||||||
|  | 			uint formatABitMask = reader.ReadUInt32(); | ||||||
|  | 
 | ||||||
|  | 			// dwCaps "stuff" | ||||||
|  | 			uint caps = reader.ReadUInt32(); | ||||||
|  | 			if ((caps & DDSCAPS_TEXTURE) == 0) | ||||||
|  | 			{ | ||||||
|  | 				throw new NotSupportedException("Not a texture!"); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			isCube = false; | ||||||
|  | 
 | ||||||
|  | 			uint caps2 = reader.ReadUInt32(); | ||||||
|  | 			if (caps2 != 0) | ||||||
|  | 			{ | ||||||
|  | 				if ((caps2 & DDSCAPS2_CUBEMAP) == DDSCAPS2_CUBEMAP) | ||||||
|  | 				{ | ||||||
|  | 					isCube = true; | ||||||
|  | 				} | ||||||
|  | 				else | ||||||
|  | 				{ | ||||||
|  | 					throw new NotSupportedException("Invalid caps2!"); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			reader.ReadUInt32(); // dwCaps3, unused | ||||||
|  | 			reader.ReadUInt32(); // dwCaps4, unused | ||||||
|  | 
 | ||||||
|  | 			// "Reserved" | ||||||
|  | 			reader.ReadUInt32(); | ||||||
|  | 
 | ||||||
|  | 			// Mipmap sanity check | ||||||
|  | 			if ((caps & DDSCAPS_MIPMAP) != DDSCAPS_MIPMAP) | ||||||
|  | 			{ | ||||||
|  | 				levels = 1; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// Determine texture format | ||||||
|  | 			if ((formatFlags & DDPF_FOURCC) == DDPF_FOURCC) | ||||||
|  | 			{ | ||||||
|  | 				switch (formatFourCC) | ||||||
|  | 				{ | ||||||
|  | 					case 0x71: // D3DFMT_A16B16G16R16F | ||||||
|  | 						format = TextureFormat.R16G16B16A16_SFLOAT; | ||||||
|  | 						break; | ||||||
|  | 					case 0x74: // D3DFMT_A32B32G32R32F | ||||||
|  | 						format = TextureFormat.R32G32B32A32_SFLOAT; | ||||||
|  | 						break; | ||||||
|  | 					case FOURCC_DXT1: | ||||||
|  | 						format = TextureFormat.BC1; | ||||||
|  | 						break; | ||||||
|  | 					case FOURCC_DXT3: | ||||||
|  | 						format = TextureFormat.BC2; | ||||||
|  | 						break; | ||||||
|  | 					case FOURCC_DXT5: | ||||||
|  | 						format = TextureFormat.BC3; | ||||||
|  | 						break; | ||||||
|  | 					case FOURCC_DX10: | ||||||
|  | 						// If the fourCC is DX10, there is an extra header with additional format information. | ||||||
|  | 						uint dxgiFormat = reader.ReadUInt32(); | ||||||
|  | 
 | ||||||
|  | 						// These values are taken from the DXGI_FORMAT enum. | ||||||
|  | 						switch (dxgiFormat) | ||||||
|  | 						{ | ||||||
|  | 							case 2: | ||||||
|  | 								format = TextureFormat.R32G32B32A32_SFLOAT; | ||||||
|  | 								break; | ||||||
|  | 
 | ||||||
|  | 							case 10: | ||||||
|  | 								format = TextureFormat.R16G16B16A16_SFLOAT; | ||||||
|  | 								break; | ||||||
|  | 
 | ||||||
|  | 							case 71: | ||||||
|  | 								format = TextureFormat.BC1; | ||||||
|  | 								break; | ||||||
|  | 
 | ||||||
|  | 							case 74: | ||||||
|  | 								format = TextureFormat.BC2; | ||||||
|  | 								break; | ||||||
|  | 
 | ||||||
|  | 							case 77: | ||||||
|  | 								format = TextureFormat.BC3; | ||||||
|  | 								break; | ||||||
|  | 
 | ||||||
|  | 							case 98: | ||||||
|  | 								format = TextureFormat.BC7; | ||||||
|  | 								break; | ||||||
|  | 
 | ||||||
|  | 							default: | ||||||
|  | 								throw new NotSupportedException( | ||||||
|  | 									"Unsupported DDS texture format" | ||||||
|  | 								); | ||||||
|  | 						} | ||||||
|  | 
 | ||||||
|  | 						uint resourceDimension = reader.ReadUInt32(); | ||||||
|  | 
 | ||||||
|  | 						// These values are taken from the D3D10_RESOURCE_DIMENSION enum. | ||||||
|  | 						switch (resourceDimension) | ||||||
|  | 						{ | ||||||
|  | 							case 0: // Unknown | ||||||
|  | 							case 1: // Buffer | ||||||
|  | 								throw new NotSupportedException( | ||||||
|  | 									"Unsupported DDS texture format" | ||||||
|  | 								); | ||||||
|  | 							default: | ||||||
|  | 								break; | ||||||
|  | 						} | ||||||
|  | 
 | ||||||
|  | 						/* | ||||||
|  | 						 * This flag seemingly only indicates if the texture is a cube map. | ||||||
|  | 						 * This is already determined above. Cool! | ||||||
|  | 						 */ | ||||||
|  | 						uint miscFlag = reader.ReadUInt32(); | ||||||
|  | 
 | ||||||
|  | 						/* | ||||||
|  | 						 * Indicates the number of elements in the texture array. | ||||||
|  | 						 * We don't support texture arrays so just throw if it's greater than 1. | ||||||
|  | 						 */ | ||||||
|  | 						uint arraySize = reader.ReadUInt32(); | ||||||
|  | 
 | ||||||
|  | 						if (arraySize > 1) | ||||||
|  | 						{ | ||||||
|  | 							throw new NotSupportedException( | ||||||
|  | 								"Unsupported DDS texture format" | ||||||
|  | 							); | ||||||
|  | 						} | ||||||
|  | 
 | ||||||
|  | 						reader.ReadUInt32(); // reserved | ||||||
|  | 
 | ||||||
|  | 						break; | ||||||
|  | 					default: | ||||||
|  | 						throw new NotSupportedException( | ||||||
|  | 							"Unsupported DDS texture format" | ||||||
|  | 						); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			else if ((formatFlags & DDPF_RGB) == DDPF_RGB) | ||||||
|  | 			{ | ||||||
|  | 				if (	formatRGBBitCount != 32 || | ||||||
|  | 					formatRBitMask != 0x00FF0000 || | ||||||
|  | 					formatGBitMask != 0x0000FF00 || | ||||||
|  | 					formatBBitMask != 0x000000FF || | ||||||
|  | 					formatABitMask != 0xFF000000	) | ||||||
|  | 				{ | ||||||
|  | 					throw new NotSupportedException( | ||||||
|  | 						"Unsupported DDS texture format" | ||||||
|  | 					); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				format = TextureFormat.B8G8R8A8; | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				throw new NotSupportedException( | ||||||
|  | 					"Unsupported DDS texture format" | ||||||
|  | 				); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public static int CalculateDDSLevelSize( | ||||||
|  | 			int width, | ||||||
|  | 			int height, | ||||||
|  | 			TextureFormat format | ||||||
|  | 		) { | ||||||
|  | 			if (format == TextureFormat.R8G8B8A8) | ||||||
|  | 			{ | ||||||
|  | 				return (((width * 32) + 7) / 8) * height; | ||||||
|  | 			} | ||||||
|  | 			else if (format == TextureFormat.R16G16B16A16_SFLOAT) | ||||||
|  | 			{ | ||||||
|  | 				return (((width * 64) + 7) / 8) * height; | ||||||
|  | 			} | ||||||
|  | 			else if (format == TextureFormat.R32G32B32A32_SFLOAT) | ||||||
|  | 			{ | ||||||
|  | 				return (((width * 128) + 7) / 8) * height; | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				int blockSize = 16; | ||||||
|  | 				if (format == TextureFormat.BC1) | ||||||
|  | 				{ | ||||||
|  | 					blockSize = 8; | ||||||
|  | 				} | ||||||
|  | 				width = System.Math.Max(width, 1); | ||||||
|  | 				height = System.Math.Max(height, 1); | ||||||
|  | 				return ( | ||||||
|  | 					((width + 3) / 4) * | ||||||
|  | 					((height + 3) / 4) * | ||||||
|  | 					blockSize | ||||||
|  | 				); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -1,307 +0,0 @@ | ||||||
| using System; |  | ||||||
| 
 |  | ||||||
| namespace MoonWorks |  | ||||||
| { |  | ||||||
| 	/// <summary> |  | ||||||
| 	/// Presentation mode for a window. |  | ||||||
| 	/// </summary> |  | ||||||
| 	public enum PresentMode |  | ||||||
| 	{ |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Does not wait for v-blank to update the window. Can cause visible tearing. |  | ||||||
| 		/// </summary> |  | ||||||
| 		Immediate, |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Waits for v-blank and uses a queue to hold present requests. |  | ||||||
| 		/// Allows for low latency while preventing tearing. |  | ||||||
| 		/// May not be supported on non-Vulkan non-Linux systems or older hardware. |  | ||||||
| 		/// </summary> |  | ||||||
| 		Mailbox, |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Waits for v-blank and adds present requests to a queue. |  | ||||||
| 		/// Will probably cause latency. |  | ||||||
| 		/// Required to be supported by all compliant hardware. |  | ||||||
| 		/// </summary> |  | ||||||
| 		FIFO, |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Usually waits for v-blank, but if v-blank has passed since last update will update immediately. |  | ||||||
| 		/// May cause visible tearing. |  | ||||||
| 		/// </summary> |  | ||||||
| 		FIFORelaxed |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* Recreate all the enums in here so we don't need to explicitly |  | ||||||
|  * reference the RefreshCS namespace when using MoonWorks.Graphics |  | ||||||
|  */ |  | ||||||
| namespace MoonWorks.Graphics |  | ||||||
| { |  | ||||||
| 	public enum PrimitiveType |  | ||||||
| 	{ |  | ||||||
| 		PointList, |  | ||||||
| 		LineList, |  | ||||||
| 		LineStrip, |  | ||||||
| 		TriangleList, |  | ||||||
| 		TriangleStrip |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/// <summary> |  | ||||||
| 	/// Describes the operation that a render pass will use when loading a render target. |  | ||||||
| 	/// </summary> |  | ||||||
| 	public enum LoadOp |  | ||||||
| 	{ |  | ||||||
| 		Load, |  | ||||||
| 		Clear, |  | ||||||
| 		DontCare |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/// <summary> |  | ||||||
| 	/// Describes the operation that a render pass will use when storing a render target. |  | ||||||
| 	/// </summary> |  | ||||||
| 	public enum StoreOp |  | ||||||
| 	{ |  | ||||||
| 		Store, |  | ||||||
| 		DontCare |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[Flags] |  | ||||||
| 	public enum ClearOptionsFlags : uint |  | ||||||
| 	{ |  | ||||||
| 		Color = 1, |  | ||||||
| 		Depth = 2, |  | ||||||
| 		Stencil = 4, |  | ||||||
| 		DepthStencil = Depth | Stencil, |  | ||||||
| 		All = Color | Depth | Stencil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum IndexElementSize |  | ||||||
| 	{ |  | ||||||
| 		Sixteen, |  | ||||||
| 		ThirtyTwo |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum TextureFormat |  | ||||||
| 	{ |  | ||||||
| 		R8G8B8A8, |  | ||||||
| 		B8G8R8A8, |  | ||||||
| 		R5G6B5, |  | ||||||
| 		A1R5G5B5, |  | ||||||
| 		B4G4R4A4, |  | ||||||
| 		A2R10G10B10, |  | ||||||
| 		R16G16, |  | ||||||
| 		R16G16B16A16, |  | ||||||
| 		R8, |  | ||||||
| 		BC1, |  | ||||||
| 		BC2, |  | ||||||
| 		BC3, |  | ||||||
| 		BC7, |  | ||||||
| 		R8G8_SNORM, |  | ||||||
| 		R8G8B8A8_SNORM, |  | ||||||
| 		R16_SFLOAT, |  | ||||||
| 		R16G16_SFLOAT, |  | ||||||
| 		R16G16B16A16_SFLOAT, |  | ||||||
| 		R32_SFLOAT, |  | ||||||
| 		R32G32_SFLOAT, |  | ||||||
| 		R32G32B32A32_SFLOAT, |  | ||||||
| 
 |  | ||||||
| 		R8_UINT, |  | ||||||
| 		R8G8_UINT, |  | ||||||
| 		R8G8B8A8_UINT, |  | ||||||
| 		R16_UINT, |  | ||||||
| 		R16G16_UINT, |  | ||||||
| 		R16G16B16A16_UINT, |  | ||||||
| 		D16, |  | ||||||
| 		D32, |  | ||||||
| 		D16S8, |  | ||||||
| 		D32S8 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[Flags] |  | ||||||
| 	public enum TextureUsageFlags : uint |  | ||||||
| 	{ |  | ||||||
| 		Sampler = 1, |  | ||||||
| 		ColorTarget = 2, |  | ||||||
| 		DepthStencilTarget = 4, |  | ||||||
| 		Compute = 8 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum SampleCount |  | ||||||
| 	{ |  | ||||||
| 		One, |  | ||||||
| 		Two, |  | ||||||
| 		Four, |  | ||||||
| 		Eight |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum CubeMapFace : uint |  | ||||||
| 	{ |  | ||||||
| 		PositiveX, |  | ||||||
| 		NegativeX, |  | ||||||
| 		PositiveY, |  | ||||||
| 		NegativeY, |  | ||||||
| 		PositiveZ, |  | ||||||
| 		NegativeZ |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[Flags] |  | ||||||
| 	public enum BufferUsageFlags : uint |  | ||||||
| 	{ |  | ||||||
| 		Vertex = 1, |  | ||||||
| 		Index = 2, |  | ||||||
| 		Compute = 4, |  | ||||||
| 		Indirect = 8 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum VertexElementFormat |  | ||||||
| 	{ |  | ||||||
| 		UInt, |  | ||||||
| 		Float, |  | ||||||
| 		Vector2, |  | ||||||
| 		Vector3, |  | ||||||
| 		Vector4, |  | ||||||
| 		Color, |  | ||||||
| 		Byte4, |  | ||||||
| 		Short2, |  | ||||||
| 		Short4, |  | ||||||
| 		NormalizedShort2, |  | ||||||
| 		NormalizedShort4, |  | ||||||
| 		HalfVector2, |  | ||||||
| 		HalfVector4 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum VertexInputRate |  | ||||||
| 	{ |  | ||||||
| 		Vertex, |  | ||||||
| 		Instance |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum FillMode |  | ||||||
| 	{ |  | ||||||
| 		Fill, |  | ||||||
| 		Line |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum CullMode |  | ||||||
| 	{ |  | ||||||
| 		None, |  | ||||||
| 		Front, |  | ||||||
| 		Back |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum FrontFace |  | ||||||
| 	{ |  | ||||||
| 		CounterClockwise, |  | ||||||
| 		Clockwise |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum CompareOp |  | ||||||
| 	{ |  | ||||||
| 		Never, |  | ||||||
| 		Less, |  | ||||||
| 		Equal, |  | ||||||
| 		LessOrEqual, |  | ||||||
| 		Greater, |  | ||||||
| 		NotEqual, |  | ||||||
| 		GreaterOrEqual, |  | ||||||
| 		Always |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum StencilOp |  | ||||||
| 	{ |  | ||||||
| 		Keep, |  | ||||||
| 		Zero, |  | ||||||
| 		Replace, |  | ||||||
| 		IncrementAndClamp, |  | ||||||
| 		DecrementAndClamp, |  | ||||||
| 		Invert, |  | ||||||
| 		IncrementAndWrap, |  | ||||||
| 		DecrementAndWrap |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum BlendOp |  | ||||||
| 	{ |  | ||||||
| 		Add, |  | ||||||
| 		Subtract, |  | ||||||
| 		ReverseSubtract, |  | ||||||
| 		Min, |  | ||||||
| 		Max |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum BlendFactor |  | ||||||
| 	{ |  | ||||||
| 		Zero, |  | ||||||
| 		One, |  | ||||||
| 		SourceColor, |  | ||||||
| 		OneMinusSourceColor, |  | ||||||
| 		DestinationColor, |  | ||||||
| 		OneMinusDestinationColor, |  | ||||||
| 		SourceAlpha, |  | ||||||
| 		OneMinusSourceAlpha, |  | ||||||
| 		DestinationAlpha, |  | ||||||
| 		OneMinusDestinationAlpha, |  | ||||||
| 		ConstantColor, |  | ||||||
| 		OneMinusConstantColor, |  | ||||||
| 		SourceAlphaSaturate |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[Flags] |  | ||||||
| 	public enum ColorComponentFlags : uint |  | ||||||
| 	{ |  | ||||||
| 		R = 1, |  | ||||||
| 		G = 2, |  | ||||||
| 		B = 4, |  | ||||||
| 		A = 8, |  | ||||||
| 
 |  | ||||||
| 		RG = R | G, |  | ||||||
| 		RB = R | B, |  | ||||||
| 		RA = R | A, |  | ||||||
| 		GB = G | B, |  | ||||||
| 		GA = G | A, |  | ||||||
| 		BA = B | A, |  | ||||||
| 
 |  | ||||||
| 		RGB = R | G | B, |  | ||||||
| 		RGA = R | G | A, |  | ||||||
| 		GBA = G | B | A, |  | ||||||
| 
 |  | ||||||
| 		RGBA = R | G | B | A, |  | ||||||
| 		None = 0 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum Filter |  | ||||||
| 	{ |  | ||||||
| 		Nearest, |  | ||||||
| 		Linear |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum SamplerMipmapMode |  | ||||||
| 	{ |  | ||||||
| 		Nearest, |  | ||||||
| 		Linear |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum SamplerAddressMode |  | ||||||
| 	{ |  | ||||||
| 		Repeat, |  | ||||||
| 		MirroredRepeat, |  | ||||||
| 		ClampToEdge, |  | ||||||
| 		ClampToBorder |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum BorderColor |  | ||||||
| 	{ |  | ||||||
| 		FloatTransparentBlack, |  | ||||||
| 		IntTransparentBlack, |  | ||||||
| 		FloatOpaqueBlack, |  | ||||||
| 		IntOpaqueBlack, |  | ||||||
| 		FloatOpaqueWhite, |  | ||||||
| 		IntOpaqueWhite |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public enum Backend |  | ||||||
| 	{ |  | ||||||
| 		DontCare, |  | ||||||
| 		Vulkan, |  | ||||||
| 		PS5, |  | ||||||
| 		Invalid |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -1,5 +1,4 @@ | ||||||
| using System; | using System; | ||||||
| using System.Runtime.InteropServices; |  | ||||||
| using System.Threading; | using System.Threading; | ||||||
| 
 | 
 | ||||||
| namespace MoonWorks.Graphics; | namespace MoonWorks.Graphics; | ||||||
|  | @ -9,7 +8,7 @@ public abstract class RefreshResource : GraphicsResource | ||||||
| 	public IntPtr Handle { get => handle; internal set => handle = value; } | 	public IntPtr Handle { get => handle; internal set => handle = value; } | ||||||
| 	private IntPtr handle; | 	private IntPtr handle; | ||||||
| 
 | 
 | ||||||
| 	protected abstract Action<IntPtr, IntPtr> QueueDestroyFunction { get; } | 	protected abstract Action<IntPtr, IntPtr> ReleaseFunction { get; } | ||||||
| 
 | 
 | ||||||
| 	protected RefreshResource(GraphicsDevice device) : base(device) | 	protected RefreshResource(GraphicsDevice device) : base(device) | ||||||
| 	{ | 	{ | ||||||
|  | @ -19,11 +18,11 @@ public abstract class RefreshResource : GraphicsResource | ||||||
| 	{ | 	{ | ||||||
| 		if (!IsDisposed) | 		if (!IsDisposed) | ||||||
| 		{ | 		{ | ||||||
| 			// Atomically call destroy function in case this is called from the finalizer thread | 			// Atomically call release function in case this is called from the finalizer thread | ||||||
| 			var toDispose = Interlocked.Exchange(ref handle, IntPtr.Zero); | 			var toDispose = Interlocked.Exchange(ref handle, IntPtr.Zero); | ||||||
| 			if (toDispose != IntPtr.Zero) | 			if (toDispose != IntPtr.Zero) | ||||||
| 			{ | 			{ | ||||||
| 				QueueDestroyFunction(Device.Handle, toDispose); | 				ReleaseFunction(Device.Handle, toDispose); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		base.Dispose(disposing); | 		base.Dispose(disposing); | ||||||
|  |  | ||||||
|  | @ -1,357 +0,0 @@ | ||||||
| using RefreshCS; |  | ||||||
| using System.Runtime.InteropServices; |  | ||||||
| 
 |  | ||||||
| /* Recreate some structs in here so we don't need to explicitly |  | ||||||
|  * reference the RefreshCS namespace when using MoonWorks.Graphics |  | ||||||
|  */ |  | ||||||
| namespace MoonWorks.Graphics |  | ||||||
| { |  | ||||||
| 	[StructLayout(LayoutKind.Sequential)] |  | ||||||
| 	public struct DepthStencilValue |  | ||||||
| 	{ |  | ||||||
| 		public float Depth; |  | ||||||
| 		public uint Stencil; |  | ||||||
| 
 |  | ||||||
| 		public DepthStencilValue(float depth, uint stencil) |  | ||||||
| 		{ |  | ||||||
| 			Depth = depth; |  | ||||||
| 			Stencil = stencil; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// FIXME: can we do an unsafe cast somehow? |  | ||||||
| 		public Refresh.DepthStencilValue ToRefresh() |  | ||||||
| 		{ |  | ||||||
| 			return new Refresh.DepthStencilValue |  | ||||||
| 			{ |  | ||||||
| 				depth = Depth, |  | ||||||
| 				stencil = Stencil |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[StructLayout(LayoutKind.Sequential)] |  | ||||||
| 	public struct Rect |  | ||||||
| 	{ |  | ||||||
| 		public int X; |  | ||||||
| 		public int Y; |  | ||||||
| 		public int W; |  | ||||||
| 		public int H; |  | ||||||
| 
 |  | ||||||
| 		public Rect(int x, int y, int w, int h) |  | ||||||
| 		{ |  | ||||||
| 			X = x; |  | ||||||
| 			Y = y; |  | ||||||
| 			W = w; |  | ||||||
| 			H = h; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public Rect(int w, int h) |  | ||||||
| 		{ |  | ||||||
| 			X = 0; |  | ||||||
| 			Y = 0; |  | ||||||
| 			W = w; |  | ||||||
| 			H = h; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// FIXME: can we do an unsafe cast somehow? |  | ||||||
| 		public Refresh.Rect ToRefresh() |  | ||||||
| 		{ |  | ||||||
| 			return new Refresh.Rect |  | ||||||
| 			{ |  | ||||||
| 				x = X, |  | ||||||
| 				y = Y, |  | ||||||
| 				w = W, |  | ||||||
| 				h = H |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[StructLayout(LayoutKind.Sequential)] |  | ||||||
| 	public struct Viewport |  | ||||||
| 	{ |  | ||||||
| 		public float X; |  | ||||||
| 		public float Y; |  | ||||||
| 		public float W; |  | ||||||
| 		public float H; |  | ||||||
| 		public float MinDepth; |  | ||||||
| 		public float MaxDepth; |  | ||||||
| 
 |  | ||||||
| 		public Viewport(float w, float h) |  | ||||||
| 		{ |  | ||||||
| 			X = 0; |  | ||||||
| 			Y = 0; |  | ||||||
| 			W = w; |  | ||||||
| 			H = h; |  | ||||||
| 			MinDepth = 0; |  | ||||||
| 			MaxDepth = 1; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public Viewport(float x, float y, float w, float h) |  | ||||||
| 		{ |  | ||||||
| 			X = x; |  | ||||||
| 			Y = y; |  | ||||||
| 			W = w; |  | ||||||
| 			H = h; |  | ||||||
| 			MinDepth = 0; |  | ||||||
| 			MaxDepth = 1; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public Viewport(float x, float y, float w, float h, float minDepth, float maxDepth) |  | ||||||
| 		{ |  | ||||||
| 			X = x; |  | ||||||
| 			Y = y; |  | ||||||
| 			W = w; |  | ||||||
| 			H = h; |  | ||||||
| 			MinDepth = minDepth; |  | ||||||
| 			MaxDepth = maxDepth; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public Refresh.Viewport ToRefresh() |  | ||||||
| 		{ |  | ||||||
| 			return new Refresh.Viewport |  | ||||||
| 			{ |  | ||||||
| 				x = X, |  | ||||||
| 				y = Y, |  | ||||||
| 				w = W, |  | ||||||
| 				h = H, |  | ||||||
| 				minDepth = MinDepth, |  | ||||||
| 				maxDepth = MaxDepth |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[StructLayout(LayoutKind.Sequential)] |  | ||||||
| 	public struct VertexBinding |  | ||||||
| 	{ |  | ||||||
| 		public uint Binding; |  | ||||||
| 		public uint Stride; |  | ||||||
| 		public VertexInputRate InputRate; |  | ||||||
| 
 |  | ||||||
| 		public static VertexBinding Create<T>(uint binding = 0, VertexInputRate inputRate = VertexInputRate.Vertex) where T : unmanaged |  | ||||||
| 		{ |  | ||||||
| 			return new VertexBinding |  | ||||||
| 			{ |  | ||||||
| 				Binding = binding, |  | ||||||
| 				InputRate = inputRate, |  | ||||||
| 				Stride = (uint) Marshal.SizeOf<T>() |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[StructLayout(LayoutKind.Sequential)] |  | ||||||
| 	public struct VertexAttribute |  | ||||||
| 	{ |  | ||||||
| 		public uint Location; |  | ||||||
| 		public uint Binding; |  | ||||||
| 		public VertexElementFormat Format; |  | ||||||
| 		public uint Offset; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[StructLayout(LayoutKind.Sequential)] |  | ||||||
| 	public struct StencilOpState |  | ||||||
| 	{ |  | ||||||
| 		public StencilOp FailOp; |  | ||||||
| 		public StencilOp PassOp; |  | ||||||
| 		public StencilOp DepthFailOp; |  | ||||||
| 		public CompareOp CompareOp; |  | ||||||
| 		public uint CompareMask; |  | ||||||
| 		public uint WriteMask; |  | ||||||
| 		public uint Reference; |  | ||||||
| 
 |  | ||||||
| 		// FIXME: can we do an explicit cast here? |  | ||||||
| 		public Refresh.StencilOpState ToRefresh() |  | ||||||
| 		{ |  | ||||||
| 			return new Refresh.StencilOpState |  | ||||||
| 			{ |  | ||||||
| 				failOp = (Refresh.StencilOp) FailOp, |  | ||||||
| 				passOp = (Refresh.StencilOp) PassOp, |  | ||||||
| 				depthFailOp = (Refresh.StencilOp) DepthFailOp, |  | ||||||
| 				compareOp = (Refresh.CompareOp) CompareOp, |  | ||||||
| 				compareMask = CompareMask, |  | ||||||
| 				writeMask = WriteMask, |  | ||||||
| 				reference = Reference |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[StructLayout(LayoutKind.Sequential)] |  | ||||||
| 	public struct ColorAttachmentInfo |  | ||||||
| 	{ |  | ||||||
| 		public Texture Texture; |  | ||||||
| 		public uint Depth; |  | ||||||
| 		public uint Layer; |  | ||||||
| 		public uint Level; |  | ||||||
| 		public Color ClearColor; |  | ||||||
| 		public LoadOp LoadOp; |  | ||||||
| 		public StoreOp StoreOp; |  | ||||||
| 
 |  | ||||||
| 		public ColorAttachmentInfo( |  | ||||||
| 			Texture texture, |  | ||||||
| 			Color clearColor, |  | ||||||
| 			StoreOp storeOp = StoreOp.Store |  | ||||||
| 		) { |  | ||||||
| 			Texture = texture; |  | ||||||
| 			Depth = 0; |  | ||||||
| 			Layer = 0; |  | ||||||
| 			Level = 0; |  | ||||||
| 			ClearColor = clearColor; |  | ||||||
| 			LoadOp = LoadOp.Clear; |  | ||||||
| 			StoreOp = storeOp; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public ColorAttachmentInfo( |  | ||||||
| 			Texture texture, |  | ||||||
| 			LoadOp loadOp = LoadOp.DontCare, |  | ||||||
| 			StoreOp storeOp = StoreOp.Store |  | ||||||
| 		) { |  | ||||||
| 			Texture = texture; |  | ||||||
| 			Depth = 0; |  | ||||||
| 			Layer = 0; |  | ||||||
| 			Level = 0; |  | ||||||
| 			ClearColor = Color.White; |  | ||||||
| 			LoadOp = loadOp; |  | ||||||
| 			StoreOp = storeOp; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public Refresh.ColorAttachmentInfo ToRefresh() |  | ||||||
| 		{ |  | ||||||
| 			return new Refresh.ColorAttachmentInfo |  | ||||||
| 			{ |  | ||||||
| 				texture = Texture.Handle, |  | ||||||
| 				depth = Depth, |  | ||||||
| 				layer = Layer, |  | ||||||
| 				level = Level, |  | ||||||
| 				clearColor = new Refresh.Vec4 |  | ||||||
| 				{ |  | ||||||
| 					x = ClearColor.R / 255f, |  | ||||||
| 					y = ClearColor.G / 255f, |  | ||||||
| 					z = ClearColor.B / 255f, |  | ||||||
| 					w = ClearColor.A / 255f |  | ||||||
| 				}, |  | ||||||
| 				loadOp = (Refresh.LoadOp) LoadOp, |  | ||||||
| 				storeOp = (Refresh.StoreOp) StoreOp |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[StructLayout(LayoutKind.Sequential)] |  | ||||||
| 	public struct DepthStencilAttachmentInfo |  | ||||||
| 	{ |  | ||||||
| 		public Texture Texture; |  | ||||||
| 		public uint Depth; |  | ||||||
| 		public uint Layer; |  | ||||||
| 		public uint Level; |  | ||||||
| 		public DepthStencilValue DepthStencilClearValue; |  | ||||||
| 		public LoadOp LoadOp; |  | ||||||
| 		public StoreOp StoreOp; |  | ||||||
| 		public LoadOp StencilLoadOp; |  | ||||||
| 		public StoreOp StencilStoreOp; |  | ||||||
| 
 |  | ||||||
| 		public DepthStencilAttachmentInfo( |  | ||||||
| 			Texture texture, |  | ||||||
| 			DepthStencilValue clearValue, |  | ||||||
| 			StoreOp depthStoreOp = StoreOp.Store, |  | ||||||
| 			StoreOp stencilStoreOp = StoreOp.Store |  | ||||||
| 		) |  | ||||||
| 		{ |  | ||||||
| 			Texture = texture; |  | ||||||
| 			Depth = 0; |  | ||||||
| 			Layer = 0; |  | ||||||
| 			Level = 0; |  | ||||||
| 			DepthStencilClearValue = clearValue; |  | ||||||
| 			LoadOp = LoadOp.Clear; |  | ||||||
| 			StoreOp = depthStoreOp; |  | ||||||
| 			StencilLoadOp = LoadOp.Clear; |  | ||||||
| 			StencilStoreOp = stencilStoreOp; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public DepthStencilAttachmentInfo( |  | ||||||
| 			Texture texture, |  | ||||||
| 			LoadOp loadOp = LoadOp.DontCare, |  | ||||||
| 			StoreOp storeOp = StoreOp.Store, |  | ||||||
| 			LoadOp stencilLoadOp = LoadOp.DontCare, |  | ||||||
| 			StoreOp stencilStoreOp = StoreOp.Store |  | ||||||
| 		) { |  | ||||||
| 			Texture = texture; |  | ||||||
| 			Depth = 0; |  | ||||||
| 			Layer = 0; |  | ||||||
| 			Level = 0; |  | ||||||
| 			DepthStencilClearValue = new DepthStencilValue(); |  | ||||||
| 			LoadOp = loadOp; |  | ||||||
| 			StoreOp = storeOp; |  | ||||||
| 			StencilLoadOp = stencilLoadOp; |  | ||||||
| 			StencilStoreOp = stencilStoreOp; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public DepthStencilAttachmentInfo( |  | ||||||
| 			Texture texture, |  | ||||||
| 			DepthStencilValue depthStencilValue, |  | ||||||
| 			LoadOp loadOp, |  | ||||||
| 			StoreOp storeOp, |  | ||||||
| 			LoadOp stencilLoadOp, |  | ||||||
| 			StoreOp stencilStoreOp |  | ||||||
| 		) { |  | ||||||
| 			Texture = texture; |  | ||||||
| 			Depth = 0; |  | ||||||
| 			Layer = 0; |  | ||||||
| 			Level = 0; |  | ||||||
| 			DepthStencilClearValue = depthStencilValue; |  | ||||||
| 			LoadOp = loadOp; |  | ||||||
| 			StoreOp = storeOp; |  | ||||||
| 			StencilLoadOp = stencilLoadOp; |  | ||||||
| 			StencilStoreOp = stencilStoreOp; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public Refresh.DepthStencilAttachmentInfo ToRefresh() |  | ||||||
| 		{ |  | ||||||
| 			return new Refresh.DepthStencilAttachmentInfo |  | ||||||
| 			{ |  | ||||||
| 				texture = Texture.Handle, |  | ||||||
| 				depth = Depth, |  | ||||||
| 				layer = Layer, |  | ||||||
| 				level = Level, |  | ||||||
| 				depthStencilClearValue = DepthStencilClearValue.ToRefresh(), |  | ||||||
| 				loadOp = (Refresh.LoadOp) LoadOp, |  | ||||||
| 				storeOp = (Refresh.StoreOp) StoreOp, |  | ||||||
| 				stencilLoadOp = (Refresh.LoadOp) StencilLoadOp, |  | ||||||
| 				stencilStoreOp = (Refresh.StoreOp) StencilStoreOp |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[StructLayout(LayoutKind.Sequential)] |  | ||||||
| 	public struct ColorAttachmentDescription |  | ||||||
| 	{ |  | ||||||
| 		public TextureFormat Format; |  | ||||||
| 		public ColorAttachmentBlendState BlendState; |  | ||||||
| 
 |  | ||||||
| 		public ColorAttachmentDescription( |  | ||||||
| 			TextureFormat format, |  | ||||||
| 			ColorAttachmentBlendState blendState |  | ||||||
| 		) { |  | ||||||
| 			Format = format; |  | ||||||
| 			BlendState = blendState; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	[StructLayout(LayoutKind.Sequential)] |  | ||||||
| 	public struct IndirectDrawCommand |  | ||||||
| 	{ |  | ||||||
| 		public uint VertexCount; |  | ||||||
| 		public uint InstanceCount; |  | ||||||
| 		public uint FirstVertex; |  | ||||||
| 		public uint FirstInstance; |  | ||||||
| 
 |  | ||||||
| 		public IndirectDrawCommand( |  | ||||||
| 			uint vertexCount, |  | ||||||
| 			uint instanceCount, |  | ||||||
| 			uint firstVertex, |  | ||||||
| 			uint firstInstance |  | ||||||
| 		) { |  | ||||||
| 			VertexCount = vertexCount; |  | ||||||
| 			InstanceCount = instanceCount; |  | ||||||
| 			FirstVertex = firstVertex; |  | ||||||
| 			FirstInstance = firstInstance; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,941 @@ | ||||||
|  | using System; | ||||||
|  | using System.Runtime.InteropServices; | ||||||
|  | using RefreshCS; | ||||||
|  | 
 | ||||||
|  | namespace MoonWorks.Graphics; | ||||||
|  | 
 | ||||||
|  | // Recreate certain types in here so we can hide the Refresh namespace | ||||||
|  | 
 | ||||||
|  | public enum PrimitiveType | ||||||
|  | { | ||||||
|  | 	PointList, | ||||||
|  | 	LineList, | ||||||
|  | 	LineStrip, | ||||||
|  | 	TriangleList, | ||||||
|  | 	TriangleStrip | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum LoadOp | ||||||
|  | { | ||||||
|  | 	Load, | ||||||
|  | 	Clear, | ||||||
|  | 	DontCare | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum StoreOp | ||||||
|  | { | ||||||
|  | 	Store, | ||||||
|  | 	DontCare | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum IndexElementSize | ||||||
|  | { | ||||||
|  | 	Sixteen, | ||||||
|  | 	ThirtyTwo | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum TextureFormat | ||||||
|  | { | ||||||
|  | 	/* Unsigned Normalized Float Color Formats */ | ||||||
|  | 	R8G8B8A8, | ||||||
|  | 	B8G8R8A8, | ||||||
|  | 	R5G6B5, | ||||||
|  | 	A1R5G5B5, | ||||||
|  | 	B4G4R4A4, | ||||||
|  | 	A2R10G10B10, | ||||||
|  | 	A2B10G10R10, | ||||||
|  | 	R16G16, | ||||||
|  | 	R16G16B16A16, | ||||||
|  | 	R8, | ||||||
|  | 	A8, | ||||||
|  | 	/* Compressed Unsigned Normalized Float Color Formats */ | ||||||
|  | 	BC1, | ||||||
|  | 	BC2, | ||||||
|  | 	BC3, | ||||||
|  | 	BC7, | ||||||
|  | 	/* Signed Normalized Float Color Formats  */ | ||||||
|  | 	R8G8_SNORM, | ||||||
|  | 	R8G8B8A8_SNORM, | ||||||
|  | 	/* Signed Float Color Formats */ | ||||||
|  | 	R16_SFLOAT, | ||||||
|  | 	R16G16_SFLOAT, | ||||||
|  | 	R16G16B16A16_SFLOAT, | ||||||
|  | 	R32_SFLOAT, | ||||||
|  | 	R32G32_SFLOAT, | ||||||
|  | 	R32G32B32A32_SFLOAT, | ||||||
|  | 	/* Unsigned Integer Color Formats */ | ||||||
|  | 	R8_UINT, | ||||||
|  | 	R8G8_UINT, | ||||||
|  | 	R8G8B8A8_UINT, | ||||||
|  | 	R16_UINT, | ||||||
|  | 	R16G16_UINT, | ||||||
|  | 	R16G16B16A16_UINT, | ||||||
|  | 	/* SRGB Color Formats */ | ||||||
|  | 	R8G8B8A8_SRGB, | ||||||
|  | 	B8G8R8A8_SRGB, | ||||||
|  | 	/* Compressed SRGB Color Formats */ | ||||||
|  | 	BC3_SRGB, | ||||||
|  | 	BC7_SRGB, | ||||||
|  | 	/* Depth Formats */ | ||||||
|  | 	D16_UNORM, | ||||||
|  | 	D24_UNORM, | ||||||
|  | 	D32_SFLOAT, | ||||||
|  | 	D24_UNORM_S8_UINT, | ||||||
|  | 	D32_SFLOAT_S8_UINT | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [Flags] | ||||||
|  | public enum TextureUsageFlags | ||||||
|  | { | ||||||
|  | 	Sampler = 0x1, | ||||||
|  | 	ColorTarget = 0x2, | ||||||
|  | 	DepthStencil = 0x4, | ||||||
|  | 	GraphicsStorage = 0x8, | ||||||
|  | 	ComputeStorageRead = 0x20, | ||||||
|  | 	ComputeStorageWrite = 0x40 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum TextureType | ||||||
|  | { | ||||||
|  | 	TwoD, | ||||||
|  | 	ThreeD, | ||||||
|  | 	Cube | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum SampleCount | ||||||
|  | { | ||||||
|  | 	One, | ||||||
|  | 	Two, | ||||||
|  | 	Four, | ||||||
|  | 	Eight | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum CubeMapFace | ||||||
|  | { | ||||||
|  | 	PositiveX, | ||||||
|  | 	NegativeX, | ||||||
|  | 	PositiveY, | ||||||
|  | 	NegativeY, | ||||||
|  | 	PositiveZ, | ||||||
|  | 	NegativeZ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [Flags] | ||||||
|  | public enum BufferUsageFlags | ||||||
|  | { | ||||||
|  | 	Vertex = 0x1, | ||||||
|  | 	Index = 0x2, | ||||||
|  | 	Indirect = 0x4, | ||||||
|  | 	GraphicsStorage = 0x8, | ||||||
|  | 	ComputeStorageRead = 0x20, | ||||||
|  | 	ComputeStorageWrite = 0x40 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [Flags] | ||||||
|  | public enum TransferBufferMapFlags | ||||||
|  | { | ||||||
|  | 	Read = 0x1, | ||||||
|  | 	Write = 0x2 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum ShaderStage | ||||||
|  | { | ||||||
|  | 	Vertex, | ||||||
|  | 	Fragment, | ||||||
|  | 	Compute | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum ShaderFormat | ||||||
|  | { | ||||||
|  | 	Invalid, | ||||||
|  | 	SPIRV, | ||||||
|  | 	DXBC, | ||||||
|  | 	DXIL, | ||||||
|  | 	MSL, | ||||||
|  | 	METALLIB, | ||||||
|  | 	SECRET | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum VertexElementFormat | ||||||
|  | { | ||||||
|  | 	Uint, | ||||||
|  | 	Float, | ||||||
|  | 	Vector2, | ||||||
|  | 	Vector3, | ||||||
|  | 	Vector4, | ||||||
|  | 	Color, | ||||||
|  | 	Byte4, | ||||||
|  | 	Short2, | ||||||
|  | 	Short4, | ||||||
|  | 	NormalizedShort2, | ||||||
|  | 	NormalizedShort4, | ||||||
|  | 	HalfVector2, | ||||||
|  | 	HalfVector4 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum VertexInputRate | ||||||
|  | { | ||||||
|  | 	Vertex, | ||||||
|  | 	Instance | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum FillMode | ||||||
|  | { | ||||||
|  | 	Fill, | ||||||
|  | 	Line | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum CullMode | ||||||
|  | { | ||||||
|  | 	None, | ||||||
|  | 	Front, | ||||||
|  | 	Back | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum FrontFace | ||||||
|  | { | ||||||
|  | 	CounterClockwise, | ||||||
|  | 	Clockwise | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum CompareOp | ||||||
|  | { | ||||||
|  | 	Never, | ||||||
|  | 	Less, | ||||||
|  | 	Equal, | ||||||
|  | 	LessOrEqual, | ||||||
|  | 	Greater, | ||||||
|  | 	NotEqual, | ||||||
|  | 	GreaterOrEqual, | ||||||
|  | 	Always | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum StencilOp | ||||||
|  | { | ||||||
|  | 	Keep, | ||||||
|  | 	Zero, | ||||||
|  | 	Replace, | ||||||
|  | 	IncrementAndClamp, | ||||||
|  | 	DecrementAndClamp, | ||||||
|  | 	Invert, | ||||||
|  | 	IncrementAndWrap, | ||||||
|  | 	DecrementAndWrap | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum BlendOp | ||||||
|  | { | ||||||
|  | 	Add, | ||||||
|  | 	Subtract, | ||||||
|  | 	ReverseSubtract, | ||||||
|  | 	Min, | ||||||
|  | 	Max | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum BlendFactor | ||||||
|  | { | ||||||
|  | 	Zero, | ||||||
|  | 	One, | ||||||
|  | 	SourceColor, | ||||||
|  | 	OneMinusSourceColor, | ||||||
|  | 	DestinationColor, | ||||||
|  | 	OneMinusDestinationColor, | ||||||
|  | 	SourceAlpha, | ||||||
|  | 	OneMinusSourceAlpha, | ||||||
|  | 	DestinationAlpha, | ||||||
|  | 	OneMinusDestinationAlpha, | ||||||
|  | 	ConstantColor, | ||||||
|  | 	OneMinusConstantColor, | ||||||
|  | 	SourceAlphaSaturate | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [Flags] | ||||||
|  | public enum ColorComponentFlags | ||||||
|  | { | ||||||
|  | 	None = 0x0, | ||||||
|  | 	R = 0x1, | ||||||
|  | 	G = 0x2, | ||||||
|  | 	B = 0x4, | ||||||
|  | 	A = 0x8, | ||||||
|  | 	RGB = R | G | B, | ||||||
|  | 	RGBA = R | G| B | A | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum Filter | ||||||
|  | { | ||||||
|  | 	Nearest, | ||||||
|  | 	Linear | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum SamplerMipmapMode | ||||||
|  | { | ||||||
|  | 	Nearest, | ||||||
|  | 	Linear | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum SamplerAddressMode | ||||||
|  | { | ||||||
|  | 	Repeat, | ||||||
|  | 	MirroredRepeat, | ||||||
|  | 	ClampToEdge, | ||||||
|  | 	ClampToBorder | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum BorderColor | ||||||
|  | { | ||||||
|  | 	FloatTransparentBlack, | ||||||
|  | 	IntTransparentBlack, | ||||||
|  | 	FloatOpaqueBlack, | ||||||
|  | 	IntOpaqueBlack, | ||||||
|  | 	FloatOpaqueWhite, | ||||||
|  | 	IntOpaqueWhite | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum TransferUsage | ||||||
|  | { | ||||||
|  | 	Buffer, | ||||||
|  | 	Texture | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum PresentMode | ||||||
|  | { | ||||||
|  | 	VSync, | ||||||
|  | 	Immediate, | ||||||
|  | 	Mailbox | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public enum SwapchainComposition | ||||||
|  | { | ||||||
|  | 	SDR, | ||||||
|  | 	SDRLinear, | ||||||
|  | 	HDRExtendedLinear, | ||||||
|  | 	HDR10_ST2084 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [Flags] | ||||||
|  | public enum BackendFlags | ||||||
|  | { | ||||||
|  | 	Invalid = 0x0, | ||||||
|  | 	Vulkan = 0x1, | ||||||
|  | 	D3D11 = 0x2, | ||||||
|  | 	Metal = 0x4, | ||||||
|  | 	All = Vulkan | D3D11 | Metal | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [StructLayout(LayoutKind.Sequential)] | ||||||
|  | public struct DepthStencilValue | ||||||
|  | { | ||||||
|  | 	public float Depth; | ||||||
|  | 	public uint Stencil; | ||||||
|  | 
 | ||||||
|  | 	public DepthStencilValue(float depth, uint stencil) | ||||||
|  | 	{ | ||||||
|  | 		Depth = depth; | ||||||
|  | 		Stencil = stencil; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// FIXME: can we do an unsafe cast somehow? | ||||||
|  | 	public Refresh.DepthStencilValue ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.DepthStencilValue | ||||||
|  | 		{ | ||||||
|  | 			Depth = Depth, | ||||||
|  | 			Stencil = Stencil | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [StructLayout(LayoutKind.Sequential)] | ||||||
|  | public struct Rect | ||||||
|  | { | ||||||
|  | 	public int X; | ||||||
|  | 	public int Y; | ||||||
|  | 	public int W; | ||||||
|  | 	public int H; | ||||||
|  | 
 | ||||||
|  | 	public Rect(int x, int y, int w, int h) | ||||||
|  | 	{ | ||||||
|  | 		X = x; | ||||||
|  | 		Y = y; | ||||||
|  | 		W = w; | ||||||
|  | 		H = h; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public Rect(int w, int h) | ||||||
|  | 	{ | ||||||
|  | 		X = 0; | ||||||
|  | 		Y = 0; | ||||||
|  | 		W = w; | ||||||
|  | 		H = h; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// FIXME: can we do an unsafe cast somehow? | ||||||
|  | 	public Refresh.Rect ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.Rect | ||||||
|  | 		{ | ||||||
|  | 			X = X, | ||||||
|  | 			Y = Y, | ||||||
|  | 			W = W, | ||||||
|  | 			H = H | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [StructLayout(LayoutKind.Sequential)] | ||||||
|  | public struct Viewport | ||||||
|  | { | ||||||
|  | 	public float X; | ||||||
|  | 	public float Y; | ||||||
|  | 	public float W; | ||||||
|  | 	public float H; | ||||||
|  | 	public float MinDepth; | ||||||
|  | 	public float MaxDepth; | ||||||
|  | 
 | ||||||
|  | 	public Viewport(float w, float h) | ||||||
|  | 	{ | ||||||
|  | 		X = 0; | ||||||
|  | 		Y = 0; | ||||||
|  | 		W = w; | ||||||
|  | 		H = h; | ||||||
|  | 		MinDepth = 0; | ||||||
|  | 		MaxDepth = 1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public Viewport(float x, float y, float w, float h) | ||||||
|  | 	{ | ||||||
|  | 		X = x; | ||||||
|  | 		Y = y; | ||||||
|  | 		W = w; | ||||||
|  | 		H = h; | ||||||
|  | 		MinDepth = 0; | ||||||
|  | 		MaxDepth = 1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public Viewport(float x, float y, float w, float h, float minDepth, float maxDepth) | ||||||
|  | 	{ | ||||||
|  | 		X = x; | ||||||
|  | 		Y = y; | ||||||
|  | 		W = w; | ||||||
|  | 		H = h; | ||||||
|  | 		MinDepth = minDepth; | ||||||
|  | 		MaxDepth = maxDepth; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public Refresh.Viewport ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.Viewport | ||||||
|  | 		{ | ||||||
|  | 			X = X, | ||||||
|  | 			Y = Y, | ||||||
|  | 			W = W, | ||||||
|  | 			H = H, | ||||||
|  | 			MinDepth = MinDepth, | ||||||
|  | 			MaxDepth = MaxDepth | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [StructLayout(LayoutKind.Sequential)] | ||||||
|  | public struct VertexBinding | ||||||
|  | { | ||||||
|  | 	public uint Binding; | ||||||
|  | 	public uint Stride; | ||||||
|  | 	public VertexInputRate InputRate; | ||||||
|  | 
 | ||||||
|  | 	public static VertexBinding Create<T>(uint binding = 0, VertexInputRate inputRate = VertexInputRate.Vertex) where T : unmanaged | ||||||
|  | 	{ | ||||||
|  | 		return new VertexBinding | ||||||
|  | 		{ | ||||||
|  | 			Binding = binding, | ||||||
|  | 			InputRate = inputRate, | ||||||
|  | 			Stride = (uint) Marshal.SizeOf<T>() | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public Refresh.VertexBinding ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.VertexBinding | ||||||
|  | 		{ | ||||||
|  | 			Binding = Binding, | ||||||
|  | 			Stride = Stride, | ||||||
|  | 			InputRate = (Refresh.VertexInputRate) InputRate | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [StructLayout(LayoutKind.Sequential)] | ||||||
|  | public struct VertexAttribute | ||||||
|  | { | ||||||
|  | 	public uint Location; | ||||||
|  | 	public uint Binding; | ||||||
|  | 	public VertexElementFormat Format; | ||||||
|  | 	public uint Offset; | ||||||
|  | 
 | ||||||
|  | 	public Refresh.VertexAttribute ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.VertexAttribute | ||||||
|  | 		{ | ||||||
|  | 			Location = Location, | ||||||
|  | 			Binding = Binding, | ||||||
|  | 			Format = (Refresh.VertexElementFormat) Format, | ||||||
|  | 			Offset = Offset | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [StructLayout(LayoutKind.Sequential)] | ||||||
|  | public struct StencilOpState | ||||||
|  | { | ||||||
|  | 	public StencilOp FailOp; | ||||||
|  | 	public StencilOp PassOp; | ||||||
|  | 	public StencilOp DepthFailOp; | ||||||
|  | 	public CompareOp CompareOp; | ||||||
|  | 
 | ||||||
|  | 	public Refresh.StencilOpState ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.StencilOpState | ||||||
|  | 		{ | ||||||
|  | 			FailOp = (Refresh.StencilOp) FailOp, | ||||||
|  | 			PassOp = (Refresh.StencilOp) PassOp, | ||||||
|  | 			DepthFailOp = (Refresh.StencilOp) DepthFailOp, | ||||||
|  | 			CompareOp = (Refresh.CompareOp) CompareOp | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// <summary> | ||||||
|  | /// Determines how a color texture will be read/written in a render pass. | ||||||
|  | /// </summary> | ||||||
|  | public struct ColorAttachmentInfo | ||||||
|  | { | ||||||
|  | 	public TextureSlice TextureSlice; | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// If LoadOp is set to Clear, the texture slice will be cleared to this color. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public Color ClearColor; | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Determines what is done with the texture slice memory | ||||||
|  | 	/// at the beginning of the render pass. <br/> | ||||||
|  | 	/// | ||||||
|  | 	///   Load: | ||||||
|  | 	///     Loads the data currently in the texture slice. <br/> | ||||||
|  | 	/// | ||||||
|  | 	///   Clear: | ||||||
|  | 	///     Clears the texture slice to a single color. <br/> | ||||||
|  | 	/// | ||||||
|  | 	///   DontCare: | ||||||
|  | 	///     The driver will do whatever it wants with the texture slice data. | ||||||
|  | 	///     This is a good option if you know that every single pixel will be written in the render pass. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public LoadOp LoadOp; | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Determines what is done with the texture slice memory | ||||||
|  | 	/// at the end of the render pass. <br/> | ||||||
|  | 	/// | ||||||
|  | 	///   Store: | ||||||
|  | 	///     Stores the results of the render pass in the texture slice memory. <br/> | ||||||
|  | 	/// | ||||||
|  | 	///   DontCare: | ||||||
|  | 	///     The driver will do whatever it wants with the texture slice memory. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public StoreOp StoreOp; | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// If true, cycles the texture if it is bound. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public bool Cycle; | ||||||
|  | 
 | ||||||
|  | 	public ColorAttachmentInfo( | ||||||
|  | 		TextureSlice textureSlice, | ||||||
|  | 		bool cycle, | ||||||
|  | 		Color clearColor, | ||||||
|  | 		StoreOp storeOp = StoreOp.Store | ||||||
|  | 	) { | ||||||
|  | 		TextureSlice = textureSlice; | ||||||
|  | 		ClearColor = clearColor; | ||||||
|  | 		LoadOp = LoadOp.Clear; | ||||||
|  | 		StoreOp = storeOp; | ||||||
|  | 		Cycle = cycle; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public ColorAttachmentInfo( | ||||||
|  | 		TextureSlice textureSlice, | ||||||
|  | 		bool cycle, | ||||||
|  | 		LoadOp loadOp = LoadOp.DontCare, | ||||||
|  | 		StoreOp storeOp = StoreOp.Store | ||||||
|  | 	) { | ||||||
|  | 		TextureSlice = textureSlice; | ||||||
|  | 		ClearColor = Color.White; | ||||||
|  | 		LoadOp = loadOp; | ||||||
|  | 		StoreOp = storeOp; | ||||||
|  | 		Cycle = cycle; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public Refresh.ColorAttachmentInfo ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.ColorAttachmentInfo | ||||||
|  | 		{ | ||||||
|  | 			TextureSlice = TextureSlice.ToRefresh(), | ||||||
|  | 			ClearColor = new Refresh.Color | ||||||
|  | 			{ | ||||||
|  | 				R = ClearColor.R / 255f, | ||||||
|  | 				G = ClearColor.G / 255f, | ||||||
|  | 				B = ClearColor.B / 255f, | ||||||
|  | 				A = ClearColor.A / 255f | ||||||
|  | 			}, | ||||||
|  | 			LoadOp = (Refresh.LoadOp) LoadOp, | ||||||
|  | 			StoreOp = (Refresh.StoreOp) StoreOp, | ||||||
|  | 			Cycle = Conversions.BoolToInt(Cycle) | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// <summary> | ||||||
|  | /// Determines how a depth/stencil texture will be read/written in a render pass. | ||||||
|  | /// </summary> | ||||||
|  | public struct DepthStencilAttachmentInfo | ||||||
|  | { | ||||||
|  | 	public TextureSlice TextureSlice; | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// If LoadOp is set to Clear, the texture slice depth will be cleared to this depth value. <br/> | ||||||
|  | 	/// If StencilLoadOp is set to Clear, the texture slice stencil value will be cleared to this stencil value. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public DepthStencilValue DepthStencilClearValue; | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Determines what is done with the texture slice depth values | ||||||
|  | 	/// at the beginning of the render pass. <br/> | ||||||
|  | 	/// | ||||||
|  | 	///   Load: | ||||||
|  | 	///     Loads the data currently in the texture slice. <br/> | ||||||
|  | 	/// | ||||||
|  | 	///   Clear: | ||||||
|  | 	///     Clears the texture slice to a single depth value. <br/> | ||||||
|  | 	/// | ||||||
|  | 	///   DontCare: | ||||||
|  | 	///     The driver will do whatever it wants with the texture slice data. | ||||||
|  | 	///     This is a good option if you know that every single pixel will be written in the render pass. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public LoadOp LoadOp; | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Determines what is done with the texture slice depth values | ||||||
|  | 	/// at the end of the render pass. <br/> | ||||||
|  | 	/// | ||||||
|  | 	///   Store: | ||||||
|  | 	///     Stores the results of the render pass in the texture slice memory. <br/> | ||||||
|  | 	/// | ||||||
|  | 	///   DontCare: | ||||||
|  | 	///     The driver will do whatever it wants with the texture slice memory. | ||||||
|  | 	///     This is usually a good option for depth textures that don't need to be reused. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public StoreOp StoreOp; | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Determines what is done with the texture slice stencil values | ||||||
|  | 	/// at the beginning of the render pass. <br/> | ||||||
|  | 	/// | ||||||
|  | 	///   Load: | ||||||
|  | 	///     Loads the data currently in the texture slice. <br/> | ||||||
|  | 	/// | ||||||
|  | 	///   Clear: | ||||||
|  | 	///     Clears the texture slice to a single stencil value. <br/> | ||||||
|  | 	/// | ||||||
|  | 	///   DontCare: | ||||||
|  | 	///     The driver will do whatever it wants with the texture slice data. | ||||||
|  | 	///     This is a good option if you know that every single pixel will be written in the render pass. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public LoadOp StencilLoadOp; | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Determines what is done with the texture slice stencil values | ||||||
|  | 	/// at the end of the render pass. <br/> | ||||||
|  | 	/// | ||||||
|  | 	///   Store: | ||||||
|  | 	///     Stores the results of the render pass in the texture slice memory. <br/> | ||||||
|  | 	/// | ||||||
|  | 	///   DontCare: | ||||||
|  | 	///     The driver will do whatever it wants with the texture slice memory. | ||||||
|  | 	///     This is usually a good option for stencil textures that don't need to be reused. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public StoreOp StencilStoreOp; | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// If true, cycles the texture if it is bound. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public bool Cycle; | ||||||
|  | 
 | ||||||
|  | 	public DepthStencilAttachmentInfo( | ||||||
|  | 		TextureSlice textureSlice, | ||||||
|  | 		bool cycle, | ||||||
|  | 		DepthStencilValue clearValue, | ||||||
|  | 		StoreOp depthStoreOp = StoreOp.DontCare, | ||||||
|  | 		StoreOp stencilStoreOp = StoreOp.DontCare | ||||||
|  | 	){ | ||||||
|  | 		TextureSlice = textureSlice; | ||||||
|  | 		DepthStencilClearValue = clearValue; | ||||||
|  | 		LoadOp = LoadOp.Clear; | ||||||
|  | 		StoreOp = depthStoreOp; | ||||||
|  | 		StencilLoadOp = LoadOp.Clear; | ||||||
|  | 		StencilStoreOp = stencilStoreOp; | ||||||
|  | 		Cycle = cycle; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public DepthStencilAttachmentInfo( | ||||||
|  | 		TextureSlice textureSlice, | ||||||
|  | 		bool cycle, | ||||||
|  | 		LoadOp loadOp = LoadOp.DontCare, | ||||||
|  | 		StoreOp storeOp = StoreOp.DontCare, | ||||||
|  | 		LoadOp stencilLoadOp = LoadOp.DontCare, | ||||||
|  | 		StoreOp stencilStoreOp = StoreOp.DontCare | ||||||
|  | 	) { | ||||||
|  | 		TextureSlice = textureSlice; | ||||||
|  | 		DepthStencilClearValue = new DepthStencilValue(); | ||||||
|  | 		LoadOp = loadOp; | ||||||
|  | 		StoreOp = storeOp; | ||||||
|  | 		StencilLoadOp = stencilLoadOp; | ||||||
|  | 		StencilStoreOp = stencilStoreOp; | ||||||
|  | 		Cycle = cycle; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public DepthStencilAttachmentInfo( | ||||||
|  | 		TextureSlice textureSlice, | ||||||
|  | 		bool cycle, | ||||||
|  | 		DepthStencilValue clearValue, | ||||||
|  | 		LoadOp loadOp, | ||||||
|  | 		StoreOp storeOp, | ||||||
|  | 		LoadOp stencilLoadOp, | ||||||
|  | 		StoreOp stencilStoreOp | ||||||
|  | 	) { | ||||||
|  | 		TextureSlice = textureSlice; | ||||||
|  | 		DepthStencilClearValue = clearValue; | ||||||
|  | 		LoadOp = loadOp; | ||||||
|  | 		StoreOp = storeOp; | ||||||
|  | 		StencilLoadOp = stencilLoadOp; | ||||||
|  | 		StencilStoreOp = stencilStoreOp; | ||||||
|  | 		Cycle = cycle; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public Refresh.DepthStencilAttachmentInfo ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.DepthStencilAttachmentInfo | ||||||
|  | 		{ | ||||||
|  | 			TextureSlice = TextureSlice.ToRefresh(), | ||||||
|  | 			DepthStencilClearValue = DepthStencilClearValue.ToRefresh(), | ||||||
|  | 			LoadOp = (Refresh.LoadOp) LoadOp, | ||||||
|  | 			StoreOp = (Refresh.StoreOp) StoreOp, | ||||||
|  | 			StencilLoadOp = (Refresh.LoadOp) StencilLoadOp, | ||||||
|  | 			StencilStoreOp = (Refresh.StoreOp) StencilStoreOp, | ||||||
|  | 			Cycle = Conversions.BoolToInt(Cycle) | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [StructLayout(LayoutKind.Sequential)] | ||||||
|  | public struct ColorAttachmentDescription | ||||||
|  | { | ||||||
|  | 	public TextureFormat Format; | ||||||
|  | 	public ColorAttachmentBlendState BlendState; | ||||||
|  | 
 | ||||||
|  | 	public ColorAttachmentDescription( | ||||||
|  | 		TextureFormat format, | ||||||
|  | 		ColorAttachmentBlendState blendState | ||||||
|  | 	) { | ||||||
|  | 		Format = format; | ||||||
|  | 		BlendState = blendState; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [StructLayout(LayoutKind.Sequential)] | ||||||
|  | public struct IndirectDrawCommand | ||||||
|  | { | ||||||
|  | 	public uint VertexCount; | ||||||
|  | 	public uint InstanceCount; | ||||||
|  | 	public uint FirstVertex; | ||||||
|  | 	public uint FirstInstance; | ||||||
|  | 
 | ||||||
|  | 	public IndirectDrawCommand( | ||||||
|  | 		uint vertexCount, | ||||||
|  | 		uint instanceCount, | ||||||
|  | 		uint firstVertex, | ||||||
|  | 		uint firstInstance | ||||||
|  | 	) { | ||||||
|  | 		VertexCount = vertexCount; | ||||||
|  | 		InstanceCount = instanceCount; | ||||||
|  | 		FirstVertex = firstVertex; | ||||||
|  | 		FirstInstance = firstInstance; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [StructLayout(LayoutKind.Sequential)] | ||||||
|  | public struct BufferCopy | ||||||
|  | { | ||||||
|  | 	public uint SrcOffset; | ||||||
|  | 	public uint DstOffset; | ||||||
|  | 	public uint Size; | ||||||
|  | 
 | ||||||
|  | 	public BufferCopy( | ||||||
|  | 		uint srcOffset, | ||||||
|  | 		uint dstOffset, | ||||||
|  | 		uint size | ||||||
|  | 	) { | ||||||
|  | 		SrcOffset = srcOffset; | ||||||
|  | 		DstOffset = dstOffset; | ||||||
|  | 		Size = size; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public Refresh.BufferCopy ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.BufferCopy | ||||||
|  | 		{ | ||||||
|  | 			SourceOffset = SrcOffset, | ||||||
|  | 			DestinationOffset = DstOffset, | ||||||
|  | 			Size = Size | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// <summary> | ||||||
|  | /// Parameters for a copy between buffer and image. | ||||||
|  | /// </summary> | ||||||
|  | /// <param name="BufferOffset">The offset into the buffer.</param> | ||||||
|  | /// <param name="BufferStride">If 0, image data is assumed tightly packed.</param> | ||||||
|  | /// <param name="BufferImageHeight">If 0, image data is assumed tightly packed.</param> | ||||||
|  | [StructLayout(LayoutKind.Sequential)] | ||||||
|  | public readonly record struct BufferImageCopy( | ||||||
|  | 	uint BufferOffset, | ||||||
|  | 	uint BufferStride, | ||||||
|  | 	uint BufferImageHeight | ||||||
|  | ) { | ||||||
|  | 	public Refresh.BufferImageCopy ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.BufferImageCopy | ||||||
|  | 		{ | ||||||
|  | 			BufferOffset = BufferOffset, | ||||||
|  | 			BufferStride = BufferStride, | ||||||
|  | 			BufferImageHeight = BufferImageHeight | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public readonly record struct GraphicsPipelineResourceInfo( | ||||||
|  | 	uint SamplerCount, | ||||||
|  | 	uint StorageBufferCount, | ||||||
|  | 	uint StorageTextureCount, | ||||||
|  | 	uint UniformBufferCount | ||||||
|  | ) { | ||||||
|  | 	public Refresh.GraphicsPipelineResourceInfo ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.GraphicsPipelineResourceInfo | ||||||
|  | 		{ | ||||||
|  | 			SamplerCount = SamplerCount, | ||||||
|  | 			StorageBufferCount = StorageBufferCount, | ||||||
|  | 			StorageTextureCount = StorageTextureCount, | ||||||
|  | 			UniformBufferCount = UniformBufferCount | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public readonly record struct ComputePipelineResourceInfo( | ||||||
|  | 	uint ReadOnlyStorageTextureCount, | ||||||
|  | 	uint ReadOnlyStorageBufferCount, | ||||||
|  | 	uint ReadWriteStorageTextureCount, | ||||||
|  | 	uint ReadWriteStorageBufferCount, | ||||||
|  | 	uint UniformBufferCount | ||||||
|  | ) { | ||||||
|  | 	public Refresh.ComputePipelineResourceInfo ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.ComputePipelineResourceInfo | ||||||
|  | 		{ | ||||||
|  | 			ReadOnlyStorageTextureCount = ReadOnlyStorageTextureCount, | ||||||
|  | 			ReadOnlyStorageBufferCount = ReadOnlyStorageBufferCount, | ||||||
|  | 			ReadWriteStorageTextureCount = ReadWriteStorageTextureCount, | ||||||
|  | 			ReadWriteStorageBufferCount = ReadWriteStorageBufferCount, | ||||||
|  | 			UniformBufferCount = UniformBufferCount | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// <summary> | ||||||
|  | /// A buffer-offset pair to be used when binding buffers. | ||||||
|  | /// </summary> | ||||||
|  | public readonly record struct BufferBinding( | ||||||
|  | 	GpuBuffer Buffer, | ||||||
|  | 	uint Offset | ||||||
|  | ) { | ||||||
|  | 	public Refresh.BufferBinding ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.BufferBinding | ||||||
|  | 		{ | ||||||
|  | 			Buffer = Buffer.Handle, | ||||||
|  | 			Offset = Offset | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// <summary> | ||||||
|  | /// A texture-sampler pair to be used when binding samplers. | ||||||
|  | /// </summary> | ||||||
|  | public readonly record struct TextureSamplerBinding( | ||||||
|  | 	Texture Texture, | ||||||
|  | 	Sampler Sampler | ||||||
|  | ) { | ||||||
|  | 	public Refresh.TextureSamplerBinding ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.TextureSamplerBinding | ||||||
|  | 		{ | ||||||
|  | 			Texture = Texture.Handle, | ||||||
|  | 			Sampler = Sampler.Handle | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public readonly record struct StorageBufferReadWriteBinding( | ||||||
|  | 	GpuBuffer Buffer, | ||||||
|  | 	bool Cycle | ||||||
|  | ) { | ||||||
|  | 	public Refresh.StorageBufferReadWriteBinding ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.StorageBufferReadWriteBinding | ||||||
|  | 		{ | ||||||
|  | 			Buffer = Buffer.Handle, | ||||||
|  | 			Cycle = Conversions.BoolToInt(Cycle) | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public readonly record struct StorageTextureReadWriteBinding( | ||||||
|  | 	in TextureSlice TextureSlice, | ||||||
|  | 	bool Cycle | ||||||
|  | ) { | ||||||
|  | 	public Refresh.StorageTextureReadWriteBinding ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.StorageTextureReadWriteBinding | ||||||
|  | 		{ | ||||||
|  | 			TextureSlice = TextureSlice.ToRefresh(), | ||||||
|  | 			Cycle = Conversions.BoolToInt(Cycle) | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// <summary> | ||||||
|  | /// All of the information that is used to create a GraphicsPipeline. | ||||||
|  | /// </summary> | ||||||
|  | public struct GraphicsPipelineCreateInfo | ||||||
|  | { | ||||||
|  | 	public Shader VertexShader; | ||||||
|  | 	public Shader FragmentShader; | ||||||
|  | 	public VertexInputState VertexInputState; | ||||||
|  | 	public PrimitiveType PrimitiveType; | ||||||
|  | 	public RasterizerState RasterizerState; | ||||||
|  | 	public MultisampleState MultisampleState; | ||||||
|  | 	public DepthStencilState DepthStencilState; | ||||||
|  | 	public GraphicsPipelineAttachmentInfo AttachmentInfo; | ||||||
|  | 	public GraphicsPipelineResourceInfo VertexShaderResourceInfo; | ||||||
|  | 	public GraphicsPipelineResourceInfo FragmentShaderResourceInfo; | ||||||
|  | 	public BlendConstants BlendConstants; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,543 @@ | ||||||
|  | using System; | ||||||
|  | using System.Runtime.InteropServices; | ||||||
|  | using RefreshCS; | ||||||
|  | namespace MoonWorks.Graphics; | ||||||
|  | 
 | ||||||
|  | /// <summary> | ||||||
|  | /// Render passes are begun in command buffers and are used to apply render state and issue draw calls. | ||||||
|  | /// Render passes are pooled and should not be referenced after calling EndRenderPass. | ||||||
|  | /// </summary> | ||||||
|  | public class RenderPass | ||||||
|  | { | ||||||
|  | 	public nint Handle { get; private set; } | ||||||
|  | 
 | ||||||
|  | #if DEBUG | ||||||
|  | 	internal uint colorAttachmentCount; | ||||||
|  | 	internal SampleCount colorAttachmentSampleCount; | ||||||
|  | 	internal TextureFormat colorFormatOne; | ||||||
|  | 	internal TextureFormat colorFormatTwo; | ||||||
|  | 	internal TextureFormat colorFormatThree; | ||||||
|  | 	internal TextureFormat colorFormatFour; | ||||||
|  | 	internal bool hasDepthStencilAttachment; | ||||||
|  | 	internal SampleCount depthStencilAttachmentSampleCount; | ||||||
|  | 	internal TextureFormat depthStencilFormat; | ||||||
|  | 
 | ||||||
|  | 	GraphicsPipeline currentGraphicsPipeline; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	internal void SetHandle(nint handle) | ||||||
|  | 	{ | ||||||
|  | 		Handle = handle; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Binds a graphics pipeline so that rendering work may be performed. | ||||||
|  | 	/// </summary> | ||||||
|  | 	/// <param name="graphicsPipeline">The graphics pipeline to bind.</param> | ||||||
|  | 	public void BindGraphicsPipeline( | ||||||
|  | 		GraphicsPipeline graphicsPipeline | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertRenderPassPipelineFormatMatch(graphicsPipeline); | ||||||
|  | 
 | ||||||
|  | 		if (colorAttachmentCount > 0) | ||||||
|  | 		{ | ||||||
|  | 			if (graphicsPipeline.SampleCount != colorAttachmentSampleCount) | ||||||
|  | 			{ | ||||||
|  | 				throw new System.InvalidOperationException($"Sample count mismatch! Graphics pipeline sample count: {graphicsPipeline.SampleCount}, Color attachment sample count: {colorAttachmentSampleCount}"); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (hasDepthStencilAttachment) | ||||||
|  | 		{ | ||||||
|  | 			if (graphicsPipeline.SampleCount != depthStencilAttachmentSampleCount) | ||||||
|  | 			{ | ||||||
|  | 				throw new System.InvalidOperationException($"Sample count mismatch! Graphics pipeline sample count: {graphicsPipeline.SampleCount}, Depth stencil attachment sample count: {depthStencilAttachmentSampleCount}"); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_BindGraphicsPipeline( | ||||||
|  | 			Handle, | ||||||
|  | 			graphicsPipeline.Handle | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | #if DEBUG | ||||||
|  | 		currentGraphicsPipeline = graphicsPipeline; | ||||||
|  | #endif | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Sets the viewport. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public void SetViewport(in Viewport viewport) | ||||||
|  | 	{ | ||||||
|  | 		Refresh.Refresh_SetViewport( | ||||||
|  | 			Handle, | ||||||
|  | 			viewport.ToRefresh() | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Sets the scissor area. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public void SetScissor(in Rect scissor) | ||||||
|  | 	{ | ||||||
|  | #if DEBUG | ||||||
|  | 		if (scissor.X < 0 || scissor.Y < 0 || scissor.W <= 0 || scissor.H <= 0) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.ArgumentOutOfRangeException("Scissor position cannot be negative and dimensions must be positive!"); | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_SetScissor( | ||||||
|  | 			Handle, | ||||||
|  | 			scissor.ToRefresh() | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Binds vertex buffers to be used by subsequent draw calls. | ||||||
|  | 	/// </summary> | ||||||
|  | 	/// <param name="bufferBinding">Buffer to bind and associated offset.</param> | ||||||
|  | 	/// <param name="firstBinding">The index of the first vertex input binding whose state is updated by the command.</param> | ||||||
|  | 	public unsafe void BindVertexBuffer( | ||||||
|  | 		in BufferBinding bufferBinding, | ||||||
|  | 		uint firstBinding = 0 | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertGraphicsPipelineBound(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		var refreshBufferBinding = bufferBinding.ToRefresh(); | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_BindVertexBuffers( | ||||||
|  | 			Handle, | ||||||
|  | 			firstBinding, | ||||||
|  | 			&refreshBufferBinding, | ||||||
|  | 			1 | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Binds an index buffer to be used by subsequent draw calls. | ||||||
|  | 	/// </summary> | ||||||
|  | 	/// <param name="indexBuffer">The index buffer to bind.</param> | ||||||
|  | 	/// <param name="indexElementSize">The size in bytes of the index buffer elements.</param> | ||||||
|  | 	/// <param name="offset">The offset index for the buffer.</param> | ||||||
|  | 	public void BindIndexBuffer( | ||||||
|  | 		BufferBinding bufferBinding, | ||||||
|  | 		IndexElementSize indexElementSize | ||||||
|  | 	) | ||||||
|  | 	{ | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertGraphicsPipelineBound(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_BindIndexBuffer( | ||||||
|  | 			Handle, | ||||||
|  | 			bufferBinding.ToRefresh(), | ||||||
|  | 			(Refresh.IndexElementSize) indexElementSize | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Binds samplers to be used by the vertex shader. | ||||||
|  | 	/// </summary> | ||||||
|  | 	/// <param name="textureSamplerBindings">The texture-sampler to bind.</param> | ||||||
|  | 	public unsafe void BindVertexSampler( | ||||||
|  | 		in TextureSamplerBinding textureSamplerBinding, | ||||||
|  | 		uint slot = 0 | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertGraphicsPipelineBound(); | ||||||
|  | 		AssertTextureSamplerBindingNonNull(textureSamplerBinding); | ||||||
|  | 		AssertTextureHasSamplerFlag(textureSamplerBinding.Texture); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		var refreshTextureSamplerBinding = textureSamplerBinding.ToRefresh(); | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_BindVertexSamplers( | ||||||
|  | 			Handle, | ||||||
|  | 			slot, | ||||||
|  | 			&refreshTextureSamplerBinding, | ||||||
|  | 			1 | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public unsafe void BindVertexStorageTexture( | ||||||
|  | 		in TextureSlice textureSlice, | ||||||
|  | 		uint slot = 0 | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertGraphicsPipelineBound(); | ||||||
|  | 		AssertTextureNonNull(textureSlice.Texture); | ||||||
|  | 		AssertTextureHasGraphicsStorageFlag(textureSlice.Texture); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		var refreshTextureSlice = textureSlice.ToRefresh(); | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_BindVertexStorageTextures( | ||||||
|  | 			Handle, | ||||||
|  | 			slot, | ||||||
|  | 			&refreshTextureSlice, | ||||||
|  | 			1 | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public unsafe void BindVertexStorageBuffer( | ||||||
|  | 		GpuBuffer buffer, | ||||||
|  | 		uint slot = 0 | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertGraphicsPipelineBound(); | ||||||
|  | 		AssertBufferNonNull(buffer); | ||||||
|  | 		AssertBufferHasGraphicsStorageFlag(buffer); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		var bufferHandle = buffer.Handle; | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_BindVertexStorageBuffers( | ||||||
|  | 			Handle, | ||||||
|  | 			slot, | ||||||
|  | 			&bufferHandle, | ||||||
|  | 			1 | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public unsafe void BindFragmentSampler( | ||||||
|  | 		in TextureSamplerBinding textureSamplerBinding, | ||||||
|  | 		uint slot = 0 | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertGraphicsPipelineBound(); | ||||||
|  | 		AssertTextureSamplerBindingNonNull(textureSamplerBinding); | ||||||
|  | 		AssertTextureHasSamplerFlag(textureSamplerBinding.Texture); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		var refreshTextureSamplerBinding = textureSamplerBinding.ToRefresh(); | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_BindFragmentSamplers( | ||||||
|  | 			Handle, | ||||||
|  | 			slot, | ||||||
|  | 			&refreshTextureSamplerBinding, | ||||||
|  | 			1 | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public unsafe void BindFragmentStorageTexture( | ||||||
|  | 		in TextureSlice textureSlice, | ||||||
|  | 		uint slot = 0 | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertGraphicsPipelineBound(); | ||||||
|  | 		AssertTextureNonNull(textureSlice.Texture); | ||||||
|  | 		AssertTextureHasGraphicsStorageFlag(textureSlice.Texture); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		var refreshTextureSlice = textureSlice.ToRefresh(); | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_BindFragmentStorageTextures( | ||||||
|  | 			Handle, | ||||||
|  | 			slot, | ||||||
|  | 			&refreshTextureSlice, | ||||||
|  | 			1 | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public unsafe void BindFragmentStorageBuffer( | ||||||
|  | 		GpuBuffer buffer, | ||||||
|  | 		uint slot = 0 | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertGraphicsPipelineBound(); | ||||||
|  | 		AssertBufferNonNull(buffer); | ||||||
|  | 		AssertBufferHasGraphicsStorageFlag(buffer); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		var bufferHandle = buffer.Handle; | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_BindFragmentStorageBuffers( | ||||||
|  | 			Handle, | ||||||
|  | 			slot, | ||||||
|  | 			&bufferHandle, | ||||||
|  | 			1 | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public unsafe void PushVertexUniformData( | ||||||
|  | 		void* uniformsPtr, | ||||||
|  | 		uint size, | ||||||
|  | 		uint slot = 0 | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertGraphicsPipelineBound(); | ||||||
|  | 
 | ||||||
|  | 		if (slot >= currentGraphicsPipeline.VertexShaderResourceInfo.UniformBufferCount) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.ArgumentException($"Slot {slot} given, but {currentGraphicsPipeline.VertexShaderResourceInfo.UniformBufferCount} uniform buffers are used on the shader!"); | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_PushVertexUniformData( | ||||||
|  | 			Handle, | ||||||
|  | 			slot, | ||||||
|  | 			(nint) uniformsPtr, | ||||||
|  | 			size | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public unsafe void PushVertexUniformData<T>( | ||||||
|  | 		in T uniforms, | ||||||
|  | 		uint slot = 0 | ||||||
|  | 	) where T : unmanaged | ||||||
|  | 	{ | ||||||
|  | 		fixed (T* uniformsPtr = &uniforms) | ||||||
|  | 		{ | ||||||
|  | 			PushVertexUniformData(uniformsPtr, (uint) Marshal.SizeOf<T>(), slot); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public unsafe void PushFragmentUniformData( | ||||||
|  | 		void* uniformsPtr, | ||||||
|  | 		uint size, | ||||||
|  | 		uint slot = 0 | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertGraphicsPipelineBound(); | ||||||
|  | 
 | ||||||
|  | 		if (slot >= currentGraphicsPipeline.FragmentShaderResourceInfo.UniformBufferCount) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.ArgumentException($"Slot {slot} given, but {currentGraphicsPipeline.FragmentShaderResourceInfo.UniformBufferCount} uniform buffers are used on the shader!"); | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_PushFragmentUniformData( | ||||||
|  | 			Handle, | ||||||
|  | 			slot, | ||||||
|  | 			(nint) uniformsPtr, | ||||||
|  | 			size | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public unsafe void PushFragmentUniformData<T>( | ||||||
|  | 		in T uniforms, | ||||||
|  | 		uint slot = 0 | ||||||
|  | 	) where T : unmanaged | ||||||
|  | 	{ | ||||||
|  | 		fixed (T* uniformsPtr = &uniforms) | ||||||
|  | 		{ | ||||||
|  | 			PushFragmentUniformData(uniformsPtr, (uint) Marshal.SizeOf<T>(), slot); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Draws using a vertex buffer and an index buffer, and an optional instance count. | ||||||
|  | 	/// </summary> | ||||||
|  | 	/// <param name="baseVertex">The starting index offset for the vertex buffer.</param> | ||||||
|  | 	/// <param name="startIndex">The starting index offset for the index buffer.</param> | ||||||
|  | 	/// <param name="primitiveCount">The number of primitives to draw.</param> | ||||||
|  | 	/// <param name="instanceCount">The number of instances to draw.</param> | ||||||
|  | 	public void DrawIndexedPrimitives( | ||||||
|  | 		uint baseVertex, | ||||||
|  | 		uint startIndex, | ||||||
|  | 		uint primitiveCount, | ||||||
|  | 		uint instanceCount = 1 | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertGraphicsPipelineBound(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_DrawIndexedPrimitives( | ||||||
|  | 			Handle, | ||||||
|  | 			baseVertex, | ||||||
|  | 			startIndex, | ||||||
|  | 			primitiveCount, | ||||||
|  | 			instanceCount | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Draws using a vertex buffer and an index buffer. | ||||||
|  | 	/// </summary> | ||||||
|  | 	/// <param name="baseVertex">The starting index offset for the vertex buffer.</param> | ||||||
|  | 	/// <param name="startIndex">The starting index offset for the index buffer.</param> | ||||||
|  | 	/// <param name="primitiveCount">The number of primitives to draw.</param> | ||||||
|  | 	public void DrawPrimitives( | ||||||
|  | 		uint vertexStart, | ||||||
|  | 		uint primitiveCount | ||||||
|  | 	) | ||||||
|  | 	{ | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertGraphicsPipelineBound(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_DrawPrimitives( | ||||||
|  | 			Handle, | ||||||
|  | 			vertexStart, | ||||||
|  | 			primitiveCount | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Similar to DrawPrimitives, but parameters are set from a buffer. | ||||||
|  | 	/// The buffer must have the Indirect usage flag set. | ||||||
|  | 	/// </summary> | ||||||
|  | 	/// <param name="buffer">The draw parameters buffer.</param> | ||||||
|  | 	/// <param name="offsetInBytes">The offset to start reading from the draw parameters buffer.</param> | ||||||
|  | 	/// <param name="drawCount">The number of draw parameter sets that should be read from the buffer.</param> | ||||||
|  | 	/// <param name="stride">The byte stride between sets of draw parameters.</param> | ||||||
|  | 	public void DrawPrimitivesIndirect( | ||||||
|  | 		GpuBuffer buffer, | ||||||
|  | 		uint offsetInBytes, | ||||||
|  | 		uint drawCount, | ||||||
|  | 		uint stride | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertGraphicsPipelineBound(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_DrawPrimitivesIndirect( | ||||||
|  | 			Handle, | ||||||
|  | 			buffer.Handle, | ||||||
|  | 			offsetInBytes, | ||||||
|  | 			drawCount, | ||||||
|  | 			stride | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Similar to DrawIndexedPrimitives, but parameters are set from a buffer. | ||||||
|  | 	/// The buffer must have the Indirect usage flag set. | ||||||
|  | 	/// </summary> | ||||||
|  | 	/// <param name="buffer">The draw parameters buffer.</param> | ||||||
|  | 	/// <param name="offsetInBytes">The offset to start reading from the draw parameters buffer.</param> | ||||||
|  | 	/// <param name="drawCount">The number of draw parameter sets that should be read from the buffer.</param> | ||||||
|  | 	/// <param name="stride">The byte stride between sets of draw parameters.</param> | ||||||
|  | 	public void DrawIndexedPrimitivesIndirect( | ||||||
|  | 		GpuBuffer buffer, | ||||||
|  | 		uint offsetInBytes, | ||||||
|  | 		uint drawCount, | ||||||
|  | 		uint stride | ||||||
|  | 	) { | ||||||
|  | #if DEBUG | ||||||
|  | 		AssertGraphicsPipelineBound(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		Refresh.Refresh_DrawIndexedPrimitivesIndirect( | ||||||
|  | 			Handle, | ||||||
|  | 			buffer.Handle, | ||||||
|  | 			offsetInBytes, | ||||||
|  | 			drawCount, | ||||||
|  | 			stride | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #if DEBUG | ||||||
|  | 	private void AssertRenderPassPipelineFormatMatch(GraphicsPipeline graphicsPipeline) | ||||||
|  | 	{ | ||||||
|  | 		for (var i = 0; i < graphicsPipeline.AttachmentInfo.ColorAttachmentDescriptions.Length; i += 1) | ||||||
|  | 		{ | ||||||
|  | 			TextureFormat format; | ||||||
|  | 			if (i == 0) | ||||||
|  | 			{ | ||||||
|  | 				format = colorFormatOne; | ||||||
|  | 			} | ||||||
|  | 			else if (i == 1) | ||||||
|  | 			{ | ||||||
|  | 				format = colorFormatTwo; | ||||||
|  | 			} | ||||||
|  | 			else if (i == 2) | ||||||
|  | 			{ | ||||||
|  | 				format = colorFormatThree; | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				format = colorFormatFour; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			var pipelineFormat = graphicsPipeline.AttachmentInfo.ColorAttachmentDescriptions[i].Format; | ||||||
|  | 			if (pipelineFormat != format) | ||||||
|  | 			{ | ||||||
|  | 				throw new System.InvalidOperationException($"Color texture format mismatch! Pipeline expects {pipelineFormat}, render pass attachment is {format}"); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (graphicsPipeline.AttachmentInfo.HasDepthStencilAttachment) | ||||||
|  | 		{ | ||||||
|  | 			var pipelineDepthFormat = graphicsPipeline.AttachmentInfo.DepthStencilFormat; | ||||||
|  | 
 | ||||||
|  | 			if (!hasDepthStencilAttachment) | ||||||
|  | 			{ | ||||||
|  | 				throw new System.InvalidOperationException("Pipeline expects depth attachment!"); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (pipelineDepthFormat != depthStencilFormat) | ||||||
|  | 			{ | ||||||
|  | 				throw new System.InvalidOperationException($"Depth texture format mismatch! Pipeline expects {pipelineDepthFormat}, render pass attachment is {depthStencilFormat}"); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void AssertGraphicsPipelineBound(string message = "No graphics pipeline is bound!") | ||||||
|  | 	{ | ||||||
|  | 		if (currentGraphicsPipeline == null) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.InvalidOperationException(message); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void AssertTextureNonNull(in TextureSlice textureSlice) | ||||||
|  | 	{ | ||||||
|  | 		if (textureSlice.Texture == null) | ||||||
|  | 		{ | ||||||
|  | 			throw new NullReferenceException("Texture must not be null!"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void AssertTextureSamplerBindingNonNull(in TextureSamplerBinding binding) | ||||||
|  | 	{ | ||||||
|  | 		if (binding.Texture == null || binding.Texture.Handle == IntPtr.Zero) | ||||||
|  | 		{ | ||||||
|  | 			throw new NullReferenceException("Texture binding must not be null!"); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (binding.Sampler == null || binding.Sampler.Handle == IntPtr.Zero) | ||||||
|  | 		{ | ||||||
|  | 			throw new NullReferenceException("Sampler binding must not be null!"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void AssertTextureHasSamplerFlag(Texture texture) | ||||||
|  | 	{ | ||||||
|  | 		if ((texture.UsageFlags & TextureUsageFlags.Sampler) == 0) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.ArgumentException("The bound Texture's UsageFlags must include TextureUsageFlags.Sampler!"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void AssertTextureHasGraphicsStorageFlag(Texture texture) | ||||||
|  | 	{ | ||||||
|  | 		if ((texture.UsageFlags & TextureUsageFlags.GraphicsStorage) == 0) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.ArgumentException("The bound Texture's UsageFlags must include TextureUsageFlags.GraphicsStorage!"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void AssertBufferNonNull(GpuBuffer buffer) | ||||||
|  | 	{ | ||||||
|  | 		if (buffer == null || buffer.Handle == IntPtr.Zero) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.NullReferenceException("Buffer must not be null!"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void AssertBufferHasGraphicsStorageFlag(GpuBuffer buffer) | ||||||
|  | 	{ | ||||||
|  | 		if ((buffer.UsageFlags & BufferUsageFlags.GraphicsStorage) == 0) | ||||||
|  | 		{ | ||||||
|  | 			throw new System.ArgumentException("The bound Buffer's UsageFlags must include BufferUsageFlag.GraphicsStorage!"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | @ -0,0 +1,25 @@ | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | 
 | ||||||
|  | namespace MoonWorks.Graphics; | ||||||
|  | 
 | ||||||
|  | internal class RenderPassPool | ||||||
|  | { | ||||||
|  | 	private ConcurrentQueue<RenderPass> RenderPasses = new ConcurrentQueue<RenderPass>(); | ||||||
|  | 
 | ||||||
|  | 	public RenderPass Obtain() | ||||||
|  | 	{ | ||||||
|  | 		if (RenderPasses.TryDequeue(out var renderPass)) | ||||||
|  | 		{ | ||||||
|  | 			return renderPass; | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 		{ | ||||||
|  | 			return new RenderPass(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public void Return(RenderPass renderPass) | ||||||
|  | 	{ | ||||||
|  | 		RenderPasses.Enqueue(renderPass); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,381 @@ | ||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.IO; | ||||||
|  | using System.Runtime.InteropServices; | ||||||
|  | 
 | ||||||
|  | namespace MoonWorks.Graphics | ||||||
|  | { | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// A convenience structure for creating resources and uploading their data. | ||||||
|  | 	/// | ||||||
|  | 	/// Note that Upload or UploadAndWait must be called after the Create methods for the data to actually be uploaded. | ||||||
|  | 	/// | ||||||
|  | 	/// Note that this structure does not magically keep memory usage down - | ||||||
|  | 	/// you may want to stagger uploads over multiple submissions to minimize memory usage. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public unsafe class ResourceUploader : GraphicsResource | ||||||
|  | 	{ | ||||||
|  | 		TransferBuffer BufferTransferBuffer; | ||||||
|  | 		TransferBuffer TextureTransferBuffer; | ||||||
|  | 
 | ||||||
|  | 		byte* bufferData; | ||||||
|  | 		uint bufferDataOffset = 0; | ||||||
|  | 		uint bufferDataSize = 1024; | ||||||
|  | 
 | ||||||
|  | 		byte* textureData; | ||||||
|  | 		uint textureDataOffset = 0; | ||||||
|  | 		uint textureDataSize = 1024; | ||||||
|  | 
 | ||||||
|  | 		List<(GpuBuffer, BufferCopy, bool)> BufferUploads = new List<(GpuBuffer, BufferCopy, bool)>(); | ||||||
|  | 		List<(TextureRegion, uint, bool)> TextureUploads = new List<(TextureRegion, uint, bool)>(); | ||||||
|  | 
 | ||||||
|  | 		public ResourceUploader(GraphicsDevice device) : base(device) | ||||||
|  | 		{ | ||||||
|  | 			bufferData = (byte*) NativeMemory.Alloc(bufferDataSize); | ||||||
|  | 			textureData = (byte*) NativeMemory.Alloc(textureDataSize); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Buffers | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Creates a GpuBuffer with data to be uploaded. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public GpuBuffer CreateBuffer<T>(Span<T> data, BufferUsageFlags usageFlags) where T : unmanaged | ||||||
|  | 		{ | ||||||
|  | 			var lengthInBytes = (uint) (Marshal.SizeOf<T>() * data.Length); | ||||||
|  | 			var gpuBuffer = new GpuBuffer(Device, usageFlags, lengthInBytes); | ||||||
|  | 
 | ||||||
|  | 			SetBufferData(gpuBuffer, 0, data, false); | ||||||
|  | 
 | ||||||
|  | 			return gpuBuffer; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Prepares upload of data into a GpuBuffer. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public void SetBufferData<T>(GpuBuffer buffer, uint bufferOffsetInElements, Span<T> data, bool cycle) where T : unmanaged | ||||||
|  | 		{ | ||||||
|  | 			uint elementSize = (uint) Marshal.SizeOf<T>(); | ||||||
|  | 			uint offsetInBytes = elementSize * bufferOffsetInElements; | ||||||
|  | 			uint lengthInBytes = (uint) (elementSize * data.Length); | ||||||
|  | 
 | ||||||
|  | 			uint resourceOffset; | ||||||
|  | 			fixed (void* spanPtr = data) | ||||||
|  | 			{ | ||||||
|  | 				resourceOffset = CopyBufferData(spanPtr, lengthInBytes); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			var bufferCopyParams = new BufferCopy(resourceOffset, offsetInBytes, lengthInBytes); | ||||||
|  | 			BufferUploads.Add((buffer, bufferCopyParams, cycle)); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Textures | ||||||
|  | 
 | ||||||
|  | 		public Texture CreateTexture2D<T>(Span<T> pixelData, uint width, uint height) where T : unmanaged | ||||||
|  | 		{ | ||||||
|  | 			var texture = Texture.CreateTexture2D(Device, width, height, TextureFormat.R8G8B8A8, TextureUsageFlags.Sampler); | ||||||
|  | 			SetTextureData(texture, pixelData, false); | ||||||
|  | 			return texture; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Creates a 2D Texture from compressed image data to be uploaded. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public Texture CreateTexture2DFromCompressed(Span<byte> compressedImageData) | ||||||
|  | 		{ | ||||||
|  | 			ImageUtils.ImageInfoFromBytes(compressedImageData, out var width, out var height, out var _); | ||||||
|  | 			var texture = Texture.CreateTexture2D(Device, width, height, TextureFormat.R8G8B8A8, TextureUsageFlags.Sampler); | ||||||
|  | 			SetTextureDataFromCompressed(texture, compressedImageData); | ||||||
|  | 			return texture; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Creates a 2D Texture from a compressed image stream to be uploaded. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public Texture CreateTexture2DFromCompressed(Stream compressedImageStream) | ||||||
|  | 		{ | ||||||
|  | 			var length = compressedImageStream.Length; | ||||||
|  | 			var buffer = NativeMemory.Alloc((nuint) length); | ||||||
|  | 			var span = new Span<byte>(buffer, (int) length); | ||||||
|  | 			compressedImageStream.ReadExactly(span); | ||||||
|  | 
 | ||||||
|  | 			var texture = CreateTexture2DFromCompressed(span); | ||||||
|  | 
 | ||||||
|  | 			NativeMemory.Free(buffer); | ||||||
|  | 
 | ||||||
|  | 			return texture; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Creates a 2D Texture from a compressed image file to be uploaded. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public Texture CreateTexture2DFromCompressed(string compressedImageFilePath) | ||||||
|  | 		{ | ||||||
|  | 			var fileStream = new FileStream(compressedImageFilePath, FileMode.Open, FileAccess.Read); | ||||||
|  | 			return CreateTexture2DFromCompressed(fileStream); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Creates a texture from a DDS stream. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public Texture CreateTextureFromDDS(Stream stream) | ||||||
|  | 		{ | ||||||
|  | 			using var reader = new BinaryReader(stream); | ||||||
|  | 			Texture texture; | ||||||
|  | 			int faces; | ||||||
|  | 			ImageUtils.ParseDDS(reader, out var format, out var width, out var height, out var levels, out var isCube); | ||||||
|  | 
 | ||||||
|  | 			if (isCube) | ||||||
|  | 			{ | ||||||
|  | 				texture = Texture.CreateTextureCube(Device, (uint) width, format, TextureUsageFlags.Sampler, (uint) levels); | ||||||
|  | 				faces = 6; | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				texture = Texture.CreateTexture2D(Device, (uint) width, (uint) height, format, TextureUsageFlags.Sampler, (uint) levels); | ||||||
|  | 				faces = 1; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			for (int face = 0; face < faces; face += 1) | ||||||
|  | 			{ | ||||||
|  | 				for (int level = 0; level < levels; level += 1) | ||||||
|  | 				{ | ||||||
|  | 					var levelWidth = width >> level; | ||||||
|  | 					var levelHeight = height >> level; | ||||||
|  | 
 | ||||||
|  | 					var levelSize = ImageUtils.CalculateDDSLevelSize(levelWidth, levelHeight, format); | ||||||
|  | 					var byteBuffer = NativeMemory.Alloc((nuint) levelSize); | ||||||
|  | 					var byteSpan = new Span<byte>(byteBuffer, levelSize); | ||||||
|  | 					stream.ReadExactly(byteSpan); | ||||||
|  | 
 | ||||||
|  | 					var textureRegion = new TextureRegion | ||||||
|  | 					{ | ||||||
|  | 						TextureSlice = new TextureSlice | ||||||
|  | 						{ | ||||||
|  | 							Texture = texture, | ||||||
|  | 							Layer = (uint) face, | ||||||
|  | 							MipLevel = (uint) level | ||||||
|  | 						}, | ||||||
|  | 						X = 0, | ||||||
|  | 						Y = 0, | ||||||
|  | 						Z = 0, | ||||||
|  | 						Width = (uint) levelWidth, | ||||||
|  | 						Height = (uint) levelHeight, | ||||||
|  | 						Depth = 1 | ||||||
|  | 					}; | ||||||
|  | 
 | ||||||
|  | 					SetTextureData(textureRegion, byteSpan, false); | ||||||
|  | 
 | ||||||
|  | 					NativeMemory.Free(byteBuffer); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return texture; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Creates a texture from a DDS file. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public Texture CreateTextureFromDDS(string path) | ||||||
|  | 		{ | ||||||
|  | 			var stream = new FileStream(path, FileMode.Open, FileAccess.Read); | ||||||
|  | 			return CreateTextureFromDDS(stream); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public void SetTextureDataFromCompressed(TextureRegion textureRegion, Span<byte> compressedImageData) | ||||||
|  | 		{ | ||||||
|  | 			var pixelData = ImageUtils.GetPixelDataFromBytes(compressedImageData, out var _, out var _, out var sizeInBytes); | ||||||
|  | 			var pixelSpan = new Span<byte>((void*) pixelData, (int) sizeInBytes); | ||||||
|  | 
 | ||||||
|  | 			SetTextureData(textureRegion, pixelSpan, false); | ||||||
|  | 
 | ||||||
|  | 			ImageUtils.FreePixelData(pixelData); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public void SetTextureDataFromCompressed(TextureRegion textureRegion, Stream compressedImageStream) | ||||||
|  | 		{ | ||||||
|  | 			var length = compressedImageStream.Length; | ||||||
|  | 			var buffer = NativeMemory.Alloc((nuint) length); | ||||||
|  | 			var span = new Span<byte>(buffer, (int) length); | ||||||
|  | 			compressedImageStream.ReadExactly(span); | ||||||
|  | 			SetTextureDataFromCompressed(textureRegion, span); | ||||||
|  | 			NativeMemory.Free(buffer); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public void SetTextureDataFromCompressed(TextureRegion textureRegion, string compressedImageFilePath) | ||||||
|  | 		{ | ||||||
|  | 			var fileStream = new FileStream(compressedImageFilePath, FileMode.Open, FileAccess.Read); | ||||||
|  | 			SetTextureDataFromCompressed(textureRegion, fileStream); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Prepares upload of pixel data into a TextureSlice. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public void SetTextureData<T>(TextureRegion textureRegion, Span<T> data, bool cycle) where T : unmanaged | ||||||
|  | 		{ | ||||||
|  | 			var elementSize = Marshal.SizeOf<T>(); | ||||||
|  | 			var dataLengthInBytes = (uint) (elementSize * data.Length); | ||||||
|  | 
 | ||||||
|  | 			uint resourceOffset; | ||||||
|  | 			fixed (T* dataPtr = data) | ||||||
|  | 			{ | ||||||
|  | 				resourceOffset = CopyTextureData(dataPtr, dataLengthInBytes, Texture.BytesPerPixel(textureRegion.TextureSlice.Texture.Format)); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			TextureUploads.Add((textureRegion, resourceOffset, cycle)); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Upload | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Uploads all the data corresponding to the created resources. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public void Upload() | ||||||
|  | 		{ | ||||||
|  | 			CopyToTransferBuffer(); | ||||||
|  | 
 | ||||||
|  | 			var commandBuffer = Device.AcquireCommandBuffer(); | ||||||
|  | 			RecordUploadCommands(commandBuffer); | ||||||
|  | 			Device.Submit(commandBuffer); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Uploads and then blocks until the upload is finished. | ||||||
|  | 		/// This is useful for keeping memory usage down during threaded upload. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public void UploadAndWait() | ||||||
|  | 		{ | ||||||
|  | 			CopyToTransferBuffer(); | ||||||
|  | 
 | ||||||
|  | 			var commandBuffer = Device.AcquireCommandBuffer(); | ||||||
|  | 			RecordUploadCommands(commandBuffer); | ||||||
|  | 			var fence = Device.SubmitAndAcquireFence(commandBuffer); | ||||||
|  | 			Device.WaitForFence(fence); | ||||||
|  | 			Device.ReleaseFence(fence); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Helper methods | ||||||
|  | 
 | ||||||
|  | 		private void CopyToTransferBuffer() | ||||||
|  | 		{ | ||||||
|  | 			if (BufferUploads.Count > 0) | ||||||
|  | 			{ | ||||||
|  | 				if (BufferTransferBuffer == null || BufferTransferBuffer.Size < bufferDataSize) | ||||||
|  | 				{ | ||||||
|  | 					BufferTransferBuffer?.Dispose(); | ||||||
|  | 					BufferTransferBuffer = new TransferBuffer(Device, TransferUsage.Buffer, TransferBufferMapFlags.Write, bufferDataSize); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				var dataSpan = new Span<byte>(bufferData, (int) bufferDataSize); | ||||||
|  | 				BufferTransferBuffer.SetData(dataSpan, true); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 			if (TextureUploads.Count > 0) | ||||||
|  | 			{ | ||||||
|  | 				if (TextureTransferBuffer == null || TextureTransferBuffer.Size < textureDataSize) | ||||||
|  | 				{ | ||||||
|  | 					TextureTransferBuffer?.Dispose(); | ||||||
|  | 					TextureTransferBuffer = new TransferBuffer(Device, TransferUsage.Texture, TransferBufferMapFlags.Write, textureDataSize); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				var dataSpan = new Span<byte>(textureData, (int) textureDataSize); | ||||||
|  | 				TextureTransferBuffer.SetData(dataSpan, true); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private void RecordUploadCommands(CommandBuffer commandBuffer) | ||||||
|  | 		{ | ||||||
|  | 			var copyPass = commandBuffer.BeginCopyPass(); | ||||||
|  | 
 | ||||||
|  | 			foreach (var (gpuBuffer, bufferCopyParams, option) in BufferUploads) | ||||||
|  | 			{ | ||||||
|  | 				copyPass.UploadToBuffer( | ||||||
|  | 					BufferTransferBuffer, | ||||||
|  | 					gpuBuffer, | ||||||
|  | 					bufferCopyParams, | ||||||
|  | 					option | ||||||
|  | 				); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			foreach (var (textureRegion, offset, option) in TextureUploads) | ||||||
|  | 			{ | ||||||
|  | 				copyPass.UploadToTexture( | ||||||
|  | 					TextureTransferBuffer, | ||||||
|  | 					textureRegion, | ||||||
|  | 					new BufferImageCopy( | ||||||
|  | 						offset, | ||||||
|  | 						0, | ||||||
|  | 						0 | ||||||
|  | 					), | ||||||
|  | 					option | ||||||
|  | 				); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			commandBuffer.EndCopyPass(copyPass); | ||||||
|  | 
 | ||||||
|  | 			BufferUploads.Clear(); | ||||||
|  | 			TextureUploads.Clear(); | ||||||
|  | 			bufferDataOffset = 0; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private uint CopyBufferData(void* ptr, uint lengthInBytes) | ||||||
|  | 		{ | ||||||
|  | 			if (bufferDataOffset + lengthInBytes >= bufferDataSize) | ||||||
|  | 			{ | ||||||
|  | 				bufferDataSize = bufferDataOffset + lengthInBytes; | ||||||
|  | 				bufferData = (byte*) NativeMemory.Realloc(bufferData, bufferDataSize); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			var resourceOffset = bufferDataOffset; | ||||||
|  | 
 | ||||||
|  | 			NativeMemory.Copy(ptr, bufferData + bufferDataOffset, lengthInBytes); | ||||||
|  | 			bufferDataOffset += lengthInBytes; | ||||||
|  | 
 | ||||||
|  | 			return resourceOffset; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private uint CopyTextureData(void* ptr, uint lengthInBytes, uint alignment) | ||||||
|  | 		{ | ||||||
|  | 			textureDataOffset = RoundToAlignment(textureDataOffset, alignment); | ||||||
|  | 
 | ||||||
|  | 			if (textureDataOffset + lengthInBytes >= textureDataSize) | ||||||
|  | 			{ | ||||||
|  | 				textureDataSize = textureDataOffset + lengthInBytes; | ||||||
|  | 				textureData = (byte*) NativeMemory.Realloc(textureData, textureDataSize); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			var resourceOffset = textureDataOffset; | ||||||
|  | 
 | ||||||
|  | 			NativeMemory.Copy(ptr, textureData + textureDataOffset, lengthInBytes); | ||||||
|  | 			textureDataOffset += lengthInBytes; | ||||||
|  | 
 | ||||||
|  | 			return resourceOffset; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private uint RoundToAlignment(uint value, uint alignment) | ||||||
|  | 		{ | ||||||
|  | 			return alignment * ((value + alignment - 1) / alignment); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Dispose | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// It is valid to immediately call Dispose after calling Upload. | ||||||
|  | 		/// </summary> | ||||||
|  | 		protected override void Dispose(bool disposing) | ||||||
|  | 		{ | ||||||
|  | 			if (!IsDisposed) | ||||||
|  | 			{ | ||||||
|  | 				if (disposing) | ||||||
|  | 				{ | ||||||
|  | 					BufferTransferBuffer?.Dispose(); | ||||||
|  | 					TextureTransferBuffer?.Dispose(); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				NativeMemory.Free(bufferData); | ||||||
|  | 			} | ||||||
|  | 			base.Dispose(disposing); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -1,141 +0,0 @@ | ||||||
| using System; |  | ||||||
| using System.Runtime.InteropServices; |  | ||||||
| using RefreshCS; |  | ||||||
| 
 |  | ||||||
| namespace MoonWorks.Graphics |  | ||||||
| { |  | ||||||
| 	/// <summary> |  | ||||||
| 	/// Buffers are generic data containers that can be used by the GPU. |  | ||||||
| 	/// </summary> |  | ||||||
| 	public class Buffer : RefreshResource |  | ||||||
| 	{ |  | ||||||
| 		protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyBuffer; |  | ||||||
| 
 |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Size in bytes. |  | ||||||
| 		/// </summary> |  | ||||||
| 		public uint Size { get; } |  | ||||||
| 
 |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Creates a buffer of appropriate size given a type and element count. |  | ||||||
| 		/// </summary> |  | ||||||
| 		/// <typeparam name="T">The type that the buffer will contain.</typeparam> |  | ||||||
| 		/// <param name="device">The GraphicsDevice.</param> |  | ||||||
| 		/// <param name="usageFlags">Specifies how the buffer will be used.</param> |  | ||||||
| 		/// <param name="elementCount">How many elements of type T the buffer will contain.</param> |  | ||||||
| 		/// <returns></returns> |  | ||||||
| 		public unsafe static Buffer Create<T>( |  | ||||||
| 			GraphicsDevice device, |  | ||||||
| 			BufferUsageFlags usageFlags, |  | ||||||
| 			uint elementCount |  | ||||||
| 		) where T : unmanaged |  | ||||||
| 		{ |  | ||||||
| 			return new Buffer( |  | ||||||
| 				device, |  | ||||||
| 				usageFlags, |  | ||||||
| 				(uint) Marshal.SizeOf<T>() * elementCount |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Creates a buffer. |  | ||||||
| 		/// </summary> |  | ||||||
| 		/// <param name="device">An initialized GraphicsDevice.</param> |  | ||||||
| 		/// <param name="usageFlags">Specifies how the buffer will be used.</param> |  | ||||||
| 		/// <param name="sizeInBytes">The length of the array. Cannot be resized.</param> |  | ||||||
| 		public Buffer( |  | ||||||
| 			GraphicsDevice device, |  | ||||||
| 			BufferUsageFlags usageFlags, |  | ||||||
| 			uint sizeInBytes |  | ||||||
| 		) : base(device) |  | ||||||
| 		{ |  | ||||||
| 			Handle = Refresh.Refresh_CreateBuffer( |  | ||||||
| 				device.Handle, |  | ||||||
| 				(Refresh.BufferUsageFlags) usageFlags, |  | ||||||
| 				sizeInBytes |  | ||||||
| 			); |  | ||||||
| 			Size = sizeInBytes; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Reads data out of a buffer and into a span. |  | ||||||
| 		/// This operation is only guaranteed to read up-to-date data if GraphicsDevice.Wait or GraphicsDevice.WaitForFences is called first. |  | ||||||
| 		/// </summary> |  | ||||||
| 		/// <param name="data">The span that data will be copied to.</param> |  | ||||||
| 		/// <param name="dataLengthInBytes">The length of the data to read.</param> |  | ||||||
| 		public unsafe void GetData<T>( |  | ||||||
| 			Span<T> data, |  | ||||||
| 			uint dataLengthInBytes |  | ||||||
| 		) where T : unmanaged |  | ||||||
| 		{ |  | ||||||
| #if DEBUG |  | ||||||
| 			if (dataLengthInBytes > Size) |  | ||||||
| 			{ |  | ||||||
| 				Logger.LogWarn("Requested too many bytes from buffer!"); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if (dataLengthInBytes > data.Length * Marshal.SizeOf<T>()) |  | ||||||
| 			{ |  | ||||||
| 				Logger.LogWarn("Data length is larger than the provided Span!"); |  | ||||||
| 			} |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| 			fixed (T* ptr = data) |  | ||||||
| 			{ |  | ||||||
| 				Refresh.Refresh_GetBufferData( |  | ||||||
| 					Device.Handle, |  | ||||||
| 					Handle, |  | ||||||
| 					(IntPtr) ptr, |  | ||||||
| 					dataLengthInBytes |  | ||||||
| 				); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Reads data out of a buffer and into an array. |  | ||||||
| 		/// This operation is only guaranteed to read up-to-date data if GraphicsDevice.Wait or GraphicsDevice.WaitForFences is called first. |  | ||||||
| 		/// </summary> |  | ||||||
| 		/// <param name="data">The span that data will be copied to.</param> |  | ||||||
| 		/// <param name="dataLengthInBytes">The length of the data to read.</param> |  | ||||||
| 		public unsafe void GetData<T>( |  | ||||||
| 			T[] data, |  | ||||||
| 			uint dataLengthInBytes |  | ||||||
| 		) where T : unmanaged |  | ||||||
| 		{ |  | ||||||
| 			GetData(new Span<T>(data), dataLengthInBytes); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Reads data out of a buffer and into a span. |  | ||||||
| 		/// This operation is only guaranteed to read up-to-date data if GraphicsDevice.Wait or GraphicsDevice.WaitForFences is called first. |  | ||||||
| 		/// Fills the span with as much data from the buffer as it can. |  | ||||||
| 		/// </summary> |  | ||||||
| 		/// <param name="data">The span that data will be copied to.</param> |  | ||||||
| 		public unsafe void GetData<T>( |  | ||||||
| 			Span<T> data |  | ||||||
| 		) where T : unmanaged |  | ||||||
| 		{ |  | ||||||
| 			var lengthInBytes = System.Math.Min(data.Length * Marshal.SizeOf<T>(), Size); |  | ||||||
| 			GetData(data, (uint) lengthInBytes); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Reads data out of a buffer and into an array. |  | ||||||
| 		/// This operation is only guaranteed to read up-to-date data if GraphicsDevice.Wait or GraphicsDevice.WaitForFences is called first. |  | ||||||
| 		/// Fills the array with as much data from the buffer as it can. |  | ||||||
| 		/// </summary> |  | ||||||
| 		/// <param name="data">The span that data will be copied to.</param> |  | ||||||
| 		public unsafe void GetData<T>( |  | ||||||
| 			T[] data |  | ||||||
| 		) where T : unmanaged |  | ||||||
| 		{ |  | ||||||
| 			var lengthInBytes = System.Math.Min(data.Length * Marshal.SizeOf<T>(), Size); |  | ||||||
| 			GetData(new Span<T>(data), (uint) lengthInBytes); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public static implicit operator BufferBinding(Buffer b) |  | ||||||
| 		{ |  | ||||||
| 			return new BufferBinding(b, 0); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -8,34 +8,33 @@ namespace MoonWorks.Graphics | ||||||
| 	/// </summary> | 	/// </summary> | ||||||
| 	public class ComputePipeline : RefreshResource | 	public class ComputePipeline : RefreshResource | ||||||
| 	{ | 	{ | ||||||
| 		protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyComputePipeline; | 		protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_ReleaseComputePipeline; | ||||||
| 
 | 
 | ||||||
| 		public ComputeShaderInfo ComputeShaderInfo { get; } | 		public ComputePipelineResourceInfo ResourceInfo { get; } | ||||||
| 
 | 
 | ||||||
| 		public unsafe ComputePipeline( | 		public unsafe ComputePipeline( | ||||||
| 			GraphicsDevice device, | 			GraphicsDevice device, | ||||||
| 			ComputeShaderInfo computeShaderInfo | 			Shader computeShader, | ||||||
|  | 			ComputePipelineResourceInfo resourceInfo | ||||||
| 		) : base(device) | 		) : base(device) | ||||||
| 		{ | 		{ | ||||||
| 			var refreshComputeShaderInfo = new Refresh.ComputeShaderInfo | 			var refreshComputePipelineCreateInfo = new Refresh.ComputePipelineCreateInfo | ||||||
| 			{ | 			{ | ||||||
| 				entryPointName = computeShaderInfo.EntryPointName, | 				ComputeShader = computeShader.Handle, | ||||||
| 				shaderModule = computeShaderInfo.ShaderModule.Handle, | 				PipelineResourceInfo = resourceInfo.ToRefresh() | ||||||
| 				uniformBufferSize = computeShaderInfo.UniformBufferSize, |  | ||||||
| 				bufferBindingCount = computeShaderInfo.BufferBindingCount, |  | ||||||
| 				imageBindingCount = computeShaderInfo.ImageBindingCount |  | ||||||
| 			}; | 			}; | ||||||
| 
 | 
 | ||||||
| 			Handle = Refresh.Refresh_CreateComputePipeline( | 			Handle = Refresh.Refresh_CreateComputePipeline( | ||||||
| 				device.Handle, | 				device.Handle, | ||||||
| 				refreshComputeShaderInfo | 				refreshComputePipelineCreateInfo | ||||||
| 			); | 			); | ||||||
|  | 
 | ||||||
| 			if (Handle == IntPtr.Zero) | 			if (Handle == IntPtr.Zero) | ||||||
| 			{ | 			{ | ||||||
| 				throw new Exception("Could not create compute pipeline!"); | 				throw new Exception("Could not create compute pipeline!"); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			ComputeShaderInfo = computeShaderInfo; | 			ResourceInfo = resourceInfo; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,8 +1,8 @@ | ||||||
| using System; | using System; | ||||||
| using RefreshCS; | using RefreshCS; | ||||||
| 
 | 
 | ||||||
| namespace MoonWorks.Graphics | namespace MoonWorks.Graphics; | ||||||
| { | 
 | ||||||
| /// <summary> | /// <summary> | ||||||
| /// Fences allow you to track the status of a submitted command buffer. <br/> | /// Fences allow you to track the status of a submitted command buffer. <br/> | ||||||
| /// You should only acquire a Fence if you will need to track the command buffer. <br/> | /// You should only acquire a Fence if you will need to track the command buffer. <br/> | ||||||
|  | @ -12,7 +12,7 @@ namespace MoonWorks.Graphics | ||||||
| /// </summary> | /// </summary> | ||||||
| public class Fence : RefreshResource | public class Fence : RefreshResource | ||||||
| { | { | ||||||
| 		protected override Action<nint, nint> QueueDestroyFunction => Refresh.Refresh_ReleaseFence; | 	protected override Action<nint, nint> ReleaseFunction => Refresh.Refresh_ReleaseFence; | ||||||
| 
 | 
 | ||||||
| 	internal Fence(GraphicsDevice device) : base(device) | 	internal Fence(GraphicsDevice device) : base(device) | ||||||
| 	{ | 	{ | ||||||
|  | @ -23,4 +23,3 @@ namespace MoonWorks.Graphics | ||||||
| 		Handle = handle; | 		Handle = handle; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -0,0 +1,88 @@ | ||||||
|  | using System; | ||||||
|  | using System.Runtime.InteropServices; | ||||||
|  | using RefreshCS; | ||||||
|  | 
 | ||||||
|  | namespace MoonWorks.Graphics; | ||||||
|  | 
 | ||||||
|  | /// <summary> | ||||||
|  | /// GpuBuffers are generic data containers that can be used by the GPU. | ||||||
|  | /// </summary> | ||||||
|  | public class GpuBuffer : RefreshResource | ||||||
|  | { | ||||||
|  | 	protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_ReleaseBuffer; | ||||||
|  | 
 | ||||||
|  | 	public BufferUsageFlags UsageFlags { get; } | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Size in bytes. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public uint Size { get; } | ||||||
|  | 
 | ||||||
|  | 	private string name; | ||||||
|  | 	public string Name | ||||||
|  | 	{ | ||||||
|  | 		get => name; | ||||||
|  | 
 | ||||||
|  | 		set | ||||||
|  | 		{ | ||||||
|  | 			if (Device.DebugMode) | ||||||
|  | 			{ | ||||||
|  | 				Refresh.Refresh_SetBufferName( | ||||||
|  | 					Device.Handle, | ||||||
|  | 					Handle, | ||||||
|  | 					value | ||||||
|  | 				); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			name = value; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Creates a buffer of appropriate size given a type and element count. | ||||||
|  | 	/// </summary> | ||||||
|  | 	/// <typeparam name="T">The type that the buffer will contain.</typeparam> | ||||||
|  | 	/// <param name="device">The GraphicsDevice.</param> | ||||||
|  | 	/// <param name="usageFlags">Specifies how the buffer will be used.</param> | ||||||
|  | 	/// <param name="elementCount">How many elements of type T the buffer will contain.</param> | ||||||
|  | 	/// <returns></returns> | ||||||
|  | 	public unsafe static GpuBuffer Create<T>( | ||||||
|  | 		GraphicsDevice device, | ||||||
|  | 		BufferUsageFlags usageFlags, | ||||||
|  | 		uint elementCount | ||||||
|  | 	) where T : unmanaged | ||||||
|  | 	{ | ||||||
|  | 		return new GpuBuffer( | ||||||
|  | 			device, | ||||||
|  | 			usageFlags, | ||||||
|  | 			(uint) Marshal.SizeOf<T>() * elementCount | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Creates a buffer. | ||||||
|  | 	/// </summary> | ||||||
|  | 	/// <param name="device">An initialized GraphicsDevice.</param> | ||||||
|  | 	/// <param name="usageFlags">Specifies how the buffer will be used.</param> | ||||||
|  | 	/// <param name="sizeInBytes">The length of the array. Cannot be resized.</param> | ||||||
|  | 	public GpuBuffer( | ||||||
|  | 		GraphicsDevice device, | ||||||
|  | 		BufferUsageFlags usageFlags, | ||||||
|  | 		uint sizeInBytes | ||||||
|  | 	) : base(device) | ||||||
|  | 	{ | ||||||
|  | 		Handle = Refresh.Refresh_CreateBuffer( | ||||||
|  | 			device.Handle, | ||||||
|  | 			(Refresh.BufferUsageFlags) usageFlags, | ||||||
|  | 			sizeInBytes | ||||||
|  | 		); | ||||||
|  | 		UsageFlags = usageFlags; | ||||||
|  | 		Size = sizeInBytes; | ||||||
|  | 		name = ""; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public static implicit operator BufferBinding(GpuBuffer b) | ||||||
|  | 	{ | ||||||
|  | 		return new BufferBinding(b, 0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -10,10 +10,10 @@ namespace MoonWorks.Graphics | ||||||
| 	/// </summary> | 	/// </summary> | ||||||
| 	public class GraphicsPipeline : RefreshResource | 	public class GraphicsPipeline : RefreshResource | ||||||
| 	{ | 	{ | ||||||
| 		protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyGraphicsPipeline; | 		protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_ReleaseGraphicsPipeline; | ||||||
| 
 | 
 | ||||||
| 		public GraphicsShaderInfo VertexShaderInfo { get; } | 		public GraphicsPipelineResourceInfo VertexShaderResourceInfo { get; } | ||||||
| 		public GraphicsShaderInfo FragmentShaderInfo { get; } | 		public GraphicsPipelineResourceInfo FragmentShaderResourceInfo { get; } | ||||||
| 		public SampleCount SampleCount { get; } | 		public SampleCount SampleCount { get; } | ||||||
| 
 | 
 | ||||||
| #if DEBUG | #if DEBUG | ||||||
|  | @ -25,84 +25,62 @@ namespace MoonWorks.Graphics | ||||||
| 			in GraphicsPipelineCreateInfo graphicsPipelineCreateInfo | 			in GraphicsPipelineCreateInfo graphicsPipelineCreateInfo | ||||||
| 		) : base(device) | 		) : base(device) | ||||||
| 		{ | 		{ | ||||||
| 			DepthStencilState depthStencilState = graphicsPipelineCreateInfo.DepthStencilState; |  | ||||||
| 			GraphicsShaderInfo vertexShaderInfo = graphicsPipelineCreateInfo.VertexShaderInfo; |  | ||||||
| 			GraphicsShaderInfo fragmentShaderInfo = graphicsPipelineCreateInfo.FragmentShaderInfo; |  | ||||||
| 			MultisampleState multisampleState = graphicsPipelineCreateInfo.MultisampleState; |  | ||||||
| 			RasterizerState rasterizerState = graphicsPipelineCreateInfo.RasterizerState; |  | ||||||
| 			PrimitiveType primitiveType = graphicsPipelineCreateInfo.PrimitiveType; |  | ||||||
| 			VertexInputState vertexInputState = graphicsPipelineCreateInfo.VertexInputState; |  | ||||||
| 			GraphicsPipelineAttachmentInfo attachmentInfo = graphicsPipelineCreateInfo.AttachmentInfo; |  | ||||||
| 			BlendConstants blendConstants = graphicsPipelineCreateInfo.BlendConstants; |  | ||||||
| 
 |  | ||||||
| 			var vertexAttributesHandle = GCHandle.Alloc( |  | ||||||
| 				vertexInputState.VertexAttributes, |  | ||||||
| 				GCHandleType.Pinned |  | ||||||
| 			); |  | ||||||
| 			var vertexBindingsHandle = GCHandle.Alloc( |  | ||||||
| 				vertexInputState.VertexBindings, |  | ||||||
| 				GCHandleType.Pinned |  | ||||||
| 			); |  | ||||||
| 
 |  | ||||||
| 			var colorAttachmentDescriptions = stackalloc Refresh.ColorAttachmentDescription[ |  | ||||||
| 				(int) attachmentInfo.ColorAttachmentDescriptions.Length |  | ||||||
| 			]; |  | ||||||
| 
 |  | ||||||
| 			for (var i = 0; i < attachmentInfo.ColorAttachmentDescriptions.Length; i += 1) |  | ||||||
| 			{ |  | ||||||
| 				colorAttachmentDescriptions[i].format = (Refresh.TextureFormat) attachmentInfo.ColorAttachmentDescriptions[i].Format; |  | ||||||
| 				colorAttachmentDescriptions[i].blendState = attachmentInfo.ColorAttachmentDescriptions[i].BlendState.ToRefresh(); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			Refresh.GraphicsPipelineCreateInfo refreshGraphicsPipelineCreateInfo; | 			Refresh.GraphicsPipelineCreateInfo refreshGraphicsPipelineCreateInfo; | ||||||
| 
 | 
 | ||||||
| 			refreshGraphicsPipelineCreateInfo.blendConstants[0] = blendConstants.R; | 			var vertexAttributes = (Refresh.VertexAttribute*) NativeMemory.Alloc( | ||||||
| 			refreshGraphicsPipelineCreateInfo.blendConstants[1] = blendConstants.G; | 				(nuint) (graphicsPipelineCreateInfo.VertexInputState.VertexAttributes.Length * Marshal.SizeOf<Refresh.VertexAttribute>()) | ||||||
| 			refreshGraphicsPipelineCreateInfo.blendConstants[2] = blendConstants.B; | 			); | ||||||
| 			refreshGraphicsPipelineCreateInfo.blendConstants[3] = blendConstants.A; |  | ||||||
| 
 | 
 | ||||||
| 			refreshGraphicsPipelineCreateInfo.depthStencilState.backStencilState = depthStencilState.BackStencilState.ToRefresh(); | 			for (var i = 0; i < graphicsPipelineCreateInfo.VertexInputState.VertexAttributes.Length; i += 1) | ||||||
| 			refreshGraphicsPipelineCreateInfo.depthStencilState.compareOp = (Refresh.CompareOp) depthStencilState.CompareOp; | 			{ | ||||||
| 			refreshGraphicsPipelineCreateInfo.depthStencilState.depthBoundsTestEnable = Conversions.BoolToByte(depthStencilState.DepthBoundsTestEnable); | 				vertexAttributes[i] = graphicsPipelineCreateInfo.VertexInputState.VertexAttributes[i].ToRefresh(); | ||||||
| 			refreshGraphicsPipelineCreateInfo.depthStencilState.depthTestEnable = Conversions.BoolToByte(depthStencilState.DepthTestEnable); | 			} | ||||||
| 			refreshGraphicsPipelineCreateInfo.depthStencilState.depthWriteEnable = Conversions.BoolToByte(depthStencilState.DepthWriteEnable); |  | ||||||
| 			refreshGraphicsPipelineCreateInfo.depthStencilState.frontStencilState = depthStencilState.FrontStencilState.ToRefresh(); |  | ||||||
| 			refreshGraphicsPipelineCreateInfo.depthStencilState.maxDepthBounds = depthStencilState.MaxDepthBounds; |  | ||||||
| 			refreshGraphicsPipelineCreateInfo.depthStencilState.minDepthBounds = depthStencilState.MinDepthBounds; |  | ||||||
| 			refreshGraphicsPipelineCreateInfo.depthStencilState.stencilTestEnable = Conversions.BoolToByte(depthStencilState.StencilTestEnable); |  | ||||||
| 
 | 
 | ||||||
| 			refreshGraphicsPipelineCreateInfo.vertexShaderInfo.entryPointName = vertexShaderInfo.EntryPointName; | 			var vertexBindings = (Refresh.VertexBinding*) NativeMemory.Alloc( | ||||||
| 			refreshGraphicsPipelineCreateInfo.vertexShaderInfo.shaderModule = vertexShaderInfo.ShaderModule.Handle; | 				(nuint) (graphicsPipelineCreateInfo.VertexInputState.VertexBindings.Length * Marshal.SizeOf<Refresh.VertexBinding>()) | ||||||
| 			refreshGraphicsPipelineCreateInfo.vertexShaderInfo.uniformBufferSize = vertexShaderInfo.UniformBufferSize; | 			); | ||||||
| 			refreshGraphicsPipelineCreateInfo.vertexShaderInfo.samplerBindingCount = vertexShaderInfo.SamplerBindingCount; |  | ||||||
| 
 | 
 | ||||||
| 			refreshGraphicsPipelineCreateInfo.fragmentShaderInfo.entryPointName = fragmentShaderInfo.EntryPointName; | 			for (var i = 0; i < graphicsPipelineCreateInfo.VertexInputState.VertexBindings.Length; i += 1) | ||||||
| 			refreshGraphicsPipelineCreateInfo.fragmentShaderInfo.shaderModule = fragmentShaderInfo.ShaderModule.Handle; | 			{ | ||||||
| 			refreshGraphicsPipelineCreateInfo.fragmentShaderInfo.uniformBufferSize = fragmentShaderInfo.UniformBufferSize; | 				vertexBindings[i] = graphicsPipelineCreateInfo.VertexInputState.VertexBindings[i].ToRefresh(); | ||||||
| 			refreshGraphicsPipelineCreateInfo.fragmentShaderInfo.samplerBindingCount = fragmentShaderInfo.SamplerBindingCount; | 			} | ||||||
| 
 | 
 | ||||||
| 			refreshGraphicsPipelineCreateInfo.multisampleState.multisampleCount = (Refresh.SampleCount) multisampleState.MultisampleCount; | 			var colorAttachmentDescriptions = stackalloc Refresh.ColorAttachmentDescription[ | ||||||
| 			refreshGraphicsPipelineCreateInfo.multisampleState.sampleMask = multisampleState.SampleMask; | 				graphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions.Length | ||||||
|  | 			]; | ||||||
| 
 | 
 | ||||||
| 			refreshGraphicsPipelineCreateInfo.rasterizerState.cullMode = (Refresh.CullMode) rasterizerState.CullMode; | 			for (var i = 0; i < graphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions.Length; i += 1) | ||||||
| 			refreshGraphicsPipelineCreateInfo.rasterizerState.depthBiasClamp = rasterizerState.DepthBiasClamp; | 			{ | ||||||
| 			refreshGraphicsPipelineCreateInfo.rasterizerState.depthBiasConstantFactor = rasterizerState.DepthBiasConstantFactor; | 				colorAttachmentDescriptions[i].Format = (Refresh.TextureFormat) graphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions[i].Format; | ||||||
| 			refreshGraphicsPipelineCreateInfo.rasterizerState.depthBiasEnable = Conversions.BoolToByte(rasterizerState.DepthBiasEnable); | 				colorAttachmentDescriptions[i].BlendState = graphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions[i].BlendState.ToRefresh(); | ||||||
| 			refreshGraphicsPipelineCreateInfo.rasterizerState.depthBiasSlopeFactor = rasterizerState.DepthBiasSlopeFactor; | 			} | ||||||
| 			refreshGraphicsPipelineCreateInfo.rasterizerState.fillMode = (Refresh.FillMode) rasterizerState.FillMode; |  | ||||||
| 			refreshGraphicsPipelineCreateInfo.rasterizerState.frontFace = (Refresh.FrontFace) rasterizerState.FrontFace; |  | ||||||
| 
 | 
 | ||||||
| 			refreshGraphicsPipelineCreateInfo.vertexInputState.vertexAttributes = vertexAttributesHandle.AddrOfPinnedObject(); | 			refreshGraphicsPipelineCreateInfo.VertexShader = graphicsPipelineCreateInfo.VertexShader.Handle; | ||||||
| 			refreshGraphicsPipelineCreateInfo.vertexInputState.vertexAttributeCount = (uint) vertexInputState.VertexAttributes.Length; | 			refreshGraphicsPipelineCreateInfo.FragmentShader = graphicsPipelineCreateInfo.FragmentShader.Handle; | ||||||
| 			refreshGraphicsPipelineCreateInfo.vertexInputState.vertexBindings = vertexBindingsHandle.AddrOfPinnedObject(); |  | ||||||
| 			refreshGraphicsPipelineCreateInfo.vertexInputState.vertexBindingCount = (uint) vertexInputState.VertexBindings.Length; |  | ||||||
| 
 | 
 | ||||||
| 			refreshGraphicsPipelineCreateInfo.primitiveType = (Refresh.PrimitiveType) primitiveType; | 			refreshGraphicsPipelineCreateInfo.VertexInputState.VertexAttributes = vertexAttributes; | ||||||
|  | 			refreshGraphicsPipelineCreateInfo.VertexInputState.VertexAttributeCount = (uint) graphicsPipelineCreateInfo.VertexInputState.VertexAttributes.Length; | ||||||
|  | 			refreshGraphicsPipelineCreateInfo.VertexInputState.VertexBindings = vertexBindings; | ||||||
|  | 			refreshGraphicsPipelineCreateInfo.VertexInputState.VertexBindingCount = (uint) graphicsPipelineCreateInfo.VertexInputState.VertexBindings.Length; | ||||||
| 
 | 
 | ||||||
| 			refreshGraphicsPipelineCreateInfo.attachmentInfo.colorAttachmentCount = (uint) attachmentInfo.ColorAttachmentDescriptions.Length; | 			refreshGraphicsPipelineCreateInfo.PrimitiveType = (Refresh.PrimitiveType) graphicsPipelineCreateInfo.PrimitiveType; | ||||||
| 			refreshGraphicsPipelineCreateInfo.attachmentInfo.colorAttachmentDescriptions = (IntPtr) colorAttachmentDescriptions; | 
 | ||||||
| 			refreshGraphicsPipelineCreateInfo.attachmentInfo.depthStencilFormat = (Refresh.TextureFormat) attachmentInfo.DepthStencilFormat; | 			refreshGraphicsPipelineCreateInfo.RasterizerState = graphicsPipelineCreateInfo.RasterizerState.ToRefresh(); | ||||||
| 			refreshGraphicsPipelineCreateInfo.attachmentInfo.hasDepthStencilAttachment = Conversions.BoolToByte(attachmentInfo.HasDepthStencilAttachment); | 			refreshGraphicsPipelineCreateInfo.MultisampleState = graphicsPipelineCreateInfo.MultisampleState.ToRefresh(); | ||||||
|  | 			refreshGraphicsPipelineCreateInfo.DepthStencilState = graphicsPipelineCreateInfo.DepthStencilState.ToRefresh(); | ||||||
|  | 
 | ||||||
|  | 			refreshGraphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentCount = (uint) graphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions.Length; | ||||||
|  | 			refreshGraphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions = colorAttachmentDescriptions; | ||||||
|  | 			refreshGraphicsPipelineCreateInfo.AttachmentInfo.DepthStencilFormat = (Refresh.TextureFormat) graphicsPipelineCreateInfo.AttachmentInfo.DepthStencilFormat; | ||||||
|  | 			refreshGraphicsPipelineCreateInfo.AttachmentInfo.HasDepthStencilAttachment = Conversions.BoolToInt(graphicsPipelineCreateInfo.AttachmentInfo.HasDepthStencilAttachment); | ||||||
|  | 
 | ||||||
|  | 			refreshGraphicsPipelineCreateInfo.VertexResourceInfo = graphicsPipelineCreateInfo.VertexShaderResourceInfo.ToRefresh(); | ||||||
|  | 			refreshGraphicsPipelineCreateInfo.FragmentResourceInfo = graphicsPipelineCreateInfo.FragmentShaderResourceInfo.ToRefresh(); | ||||||
|  | 
 | ||||||
|  | 			refreshGraphicsPipelineCreateInfo.BlendConstants[0] = graphicsPipelineCreateInfo.BlendConstants.R; | ||||||
|  | 			refreshGraphicsPipelineCreateInfo.BlendConstants[1] = graphicsPipelineCreateInfo.BlendConstants.G; | ||||||
|  | 			refreshGraphicsPipelineCreateInfo.BlendConstants[2] = graphicsPipelineCreateInfo.BlendConstants.B; | ||||||
|  | 			refreshGraphicsPipelineCreateInfo.BlendConstants[3] = graphicsPipelineCreateInfo.BlendConstants.A; | ||||||
| 
 | 
 | ||||||
| 			Handle = Refresh.Refresh_CreateGraphicsPipeline(device.Handle, refreshGraphicsPipelineCreateInfo); | 			Handle = Refresh.Refresh_CreateGraphicsPipeline(device.Handle, refreshGraphicsPipelineCreateInfo); | ||||||
| 			if (Handle == IntPtr.Zero) | 			if (Handle == IntPtr.Zero) | ||||||
|  | @ -110,15 +88,15 @@ namespace MoonWorks.Graphics | ||||||
| 				throw new Exception("Could not create graphics pipeline!"); | 				throw new Exception("Could not create graphics pipeline!"); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			vertexAttributesHandle.Free(); | 			NativeMemory.Free(vertexAttributes); | ||||||
| 			vertexBindingsHandle.Free(); | 			NativeMemory.Free(vertexBindings); | ||||||
| 
 | 
 | ||||||
| 			VertexShaderInfo = vertexShaderInfo; | 			VertexShaderResourceInfo = graphicsPipelineCreateInfo.VertexShaderResourceInfo; | ||||||
| 			FragmentShaderInfo = fragmentShaderInfo; | 			FragmentShaderResourceInfo = graphicsPipelineCreateInfo.FragmentShaderResourceInfo; | ||||||
| 			SampleCount = multisampleState.MultisampleCount; | 			SampleCount = graphicsPipelineCreateInfo.MultisampleState.MultisampleCount; | ||||||
| 
 | 
 | ||||||
| #if DEBUG | #if DEBUG | ||||||
| 			AttachmentInfo = attachmentInfo; | 			AttachmentInfo = graphicsPipelineCreateInfo.AttachmentInfo; | ||||||
| #endif | #endif | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -1,14 +1,14 @@ | ||||||
| using System; | using System; | ||||||
| using RefreshCS; | using RefreshCS; | ||||||
| 
 | 
 | ||||||
| namespace MoonWorks.Graphics | namespace MoonWorks.Graphics; | ||||||
| { | 
 | ||||||
| /// <summary> | /// <summary> | ||||||
| /// A sampler specifies how a texture will be sampled in a shader. | /// A sampler specifies how a texture will be sampled in a shader. | ||||||
| /// </summary> | /// </summary> | ||||||
| public class Sampler : RefreshResource | public class Sampler : RefreshResource | ||||||
| { | { | ||||||
| 		protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroySampler; | 	protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_ReleaseSampler; | ||||||
| 
 | 
 | ||||||
| 	public Sampler( | 	public Sampler( | ||||||
| 		GraphicsDevice device, | 		GraphicsDevice device, | ||||||
|  | @ -17,8 +17,7 @@ namespace MoonWorks.Graphics | ||||||
| 	{ | 	{ | ||||||
| 		Handle = Refresh.Refresh_CreateSampler( | 		Handle = Refresh.Refresh_CreateSampler( | ||||||
| 			device.Handle, | 			device.Handle, | ||||||
| 				samplerCreateInfo.ToRefreshSamplerStateCreateInfo() | 			samplerCreateInfo.ToRefresh() | ||||||
| 		); | 		); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -0,0 +1,65 @@ | ||||||
|  | using RefreshCS; | ||||||
|  | using System; | ||||||
|  | using System.IO; | ||||||
|  | using System.Runtime.InteropServices; | ||||||
|  | 
 | ||||||
|  | namespace MoonWorks.Graphics | ||||||
|  | { | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Shader modules expect input in Refresh bytecode format. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public class Shader : RefreshResource | ||||||
|  | 	{ | ||||||
|  | 		protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_ReleaseShader; | ||||||
|  | 
 | ||||||
|  | 		public unsafe Shader( | ||||||
|  | 			GraphicsDevice device, | ||||||
|  | 			string filePath, | ||||||
|  | 			string entryPointName, | ||||||
|  | 			ShaderStage shaderStage, | ||||||
|  | 			ShaderFormat shaderFormat | ||||||
|  | 		) : base(device) | ||||||
|  | 		{ | ||||||
|  | 			using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); | ||||||
|  | 			Handle = CreateFromStream(device, stream, entryPointName, shaderStage, shaderFormat); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public unsafe Shader( | ||||||
|  | 			GraphicsDevice device, | ||||||
|  | 			Stream stream, | ||||||
|  | 			string entryPointName, | ||||||
|  | 			ShaderStage shaderStage, | ||||||
|  | 			ShaderFormat shaderFormat | ||||||
|  | 		) : base(device) | ||||||
|  | 		{ | ||||||
|  | 			Handle = CreateFromStream(device, stream, entryPointName, shaderStage, shaderFormat); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private static unsafe IntPtr CreateFromStream( | ||||||
|  | 			GraphicsDevice device, | ||||||
|  | 			Stream stream, | ||||||
|  | 			string entryPointName, | ||||||
|  | 			ShaderStage shaderStage, | ||||||
|  | 			ShaderFormat shaderFormat | ||||||
|  | 		) { | ||||||
|  | 			var bytecodeBuffer = NativeMemory.Alloc((nuint) stream.Length); | ||||||
|  | 			var bytecodeSpan = new Span<byte>(bytecodeBuffer, (int) stream.Length); | ||||||
|  | 			stream.ReadExactly(bytecodeSpan); | ||||||
|  | 
 | ||||||
|  | 			Refresh.ShaderCreateInfo shaderCreateInfo; | ||||||
|  | 			shaderCreateInfo.CodeSize = (nuint) stream.Length; | ||||||
|  | 			shaderCreateInfo.Code = (byte*) bytecodeBuffer; | ||||||
|  | 			shaderCreateInfo.EntryPointName = entryPointName; | ||||||
|  | 			shaderCreateInfo.Stage = (Refresh.ShaderStage) shaderStage; | ||||||
|  | 			shaderCreateInfo.Format = (Refresh.ShaderFormat) shaderFormat; | ||||||
|  | 
 | ||||||
|  | 			var shaderModule = Refresh.Refresh_CreateShader( | ||||||
|  | 				device.Handle, | ||||||
|  | 				shaderCreateInfo | ||||||
|  | 			); | ||||||
|  | 
 | ||||||
|  | 			NativeMemory.Free(bytecodeBuffer); | ||||||
|  | 			return shaderModule; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -1,42 +0,0 @@ | ||||||
| using RefreshCS; |  | ||||||
| using System; |  | ||||||
| using System.IO; |  | ||||||
| using System.Runtime.InteropServices; |  | ||||||
| 
 |  | ||||||
| namespace MoonWorks.Graphics |  | ||||||
| { |  | ||||||
| 	/// <summary> |  | ||||||
| 	/// Shader modules expect input in Refresh bytecode format. |  | ||||||
| 	/// </summary> |  | ||||||
| 	public class ShaderModule : RefreshResource |  | ||||||
| 	{ |  | ||||||
| 		protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyShaderModule; |  | ||||||
| 
 |  | ||||||
| 		public unsafe ShaderModule(GraphicsDevice device, string filePath) : base(device) |  | ||||||
| 		{ |  | ||||||
| 			using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); |  | ||||||
| 			Handle = CreateFromStream(device, stream); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public unsafe ShaderModule(GraphicsDevice device, Stream stream) : base(device) |  | ||||||
| 		{ |  | ||||||
| 			Handle = CreateFromStream(device, stream); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		private static unsafe IntPtr CreateFromStream(GraphicsDevice device, Stream stream) |  | ||||||
| 		{ |  | ||||||
| 			var bytecodeBuffer = NativeMemory.Alloc((nuint) stream.Length); |  | ||||||
| 			var bytecodeSpan = new Span<byte>(bytecodeBuffer, (int) stream.Length); |  | ||||||
| 			stream.ReadExactly(bytecodeSpan); |  | ||||||
| 
 |  | ||||||
| 			Refresh.ShaderModuleCreateInfo shaderModuleCreateInfo; |  | ||||||
| 			shaderModuleCreateInfo.codeSize = (nuint) stream.Length; |  | ||||||
| 			shaderModuleCreateInfo.byteCode = (nint) bytecodeBuffer; |  | ||||||
| 
 |  | ||||||
| 			var shaderModule = Refresh.Refresh_CreateShaderModule(device.Handle, shaderModuleCreateInfo); |  | ||||||
| 
 |  | ||||||
| 			NativeMemory.Free(bytecodeBuffer); |  | ||||||
| 			return shaderModule; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -1,6 +1,4 @@ | ||||||
| using System; | using System; | ||||||
| using System.IO; |  | ||||||
| using System.Runtime.InteropServices; |  | ||||||
| using RefreshCS; | using RefreshCS; | ||||||
| 
 | 
 | ||||||
| namespace MoonWorks.Graphics | namespace MoonWorks.Graphics | ||||||
|  | @ -15,169 +13,34 @@ namespace MoonWorks.Graphics | ||||||
| 		public uint Depth { get; } | 		public uint Depth { get; } | ||||||
| 		public TextureFormat Format { get; internal set; } | 		public TextureFormat Format { get; internal set; } | ||||||
| 		public bool IsCube { get; } | 		public bool IsCube { get; } | ||||||
|  | 		public uint LayerCount { get; } | ||||||
| 		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; } | 		public uint Size { get; } | ||||||
| 
 | 
 | ||||||
| 		// FIXME: this allocates a delegate instance | 		private string name; | ||||||
| 		protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyTexture; | 		public string Name | ||||||
| 
 |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Creates a 2D Texture using PNG or QOI data from raw byte data. |  | ||||||
| 		/// </summary> |  | ||||||
| 		public static unsafe Texture FromImageBytes( |  | ||||||
| 			GraphicsDevice device, |  | ||||||
| 			CommandBuffer commandBuffer, |  | ||||||
| 			Span<byte> data |  | ||||||
| 		) { |  | ||||||
| 			Texture texture; |  | ||||||
| 
 |  | ||||||
| 			fixed (byte *dataPtr = data) |  | ||||||
| 		{ | 		{ | ||||||
| 				var pixels = Refresh.Refresh_Image_Load((nint) dataPtr, data.Length, out var width, out var height, out var len); | 			get => name; | ||||||
| 
 | 
 | ||||||
| 				TextureCreateInfo textureCreateInfo = new TextureCreateInfo(); | 			set | ||||||
| 				textureCreateInfo.Width = (uint) width; |  | ||||||
| 				textureCreateInfo.Height = (uint) height; |  | ||||||
| 				textureCreateInfo.Depth = 1; |  | ||||||
| 				textureCreateInfo.Format = TextureFormat.R8G8B8A8; |  | ||||||
| 				textureCreateInfo.IsCube = false; |  | ||||||
| 				textureCreateInfo.LevelCount = 1; |  | ||||||
| 				textureCreateInfo.SampleCount = SampleCount.One; |  | ||||||
| 				textureCreateInfo.UsageFlags = TextureUsageFlags.Sampler; |  | ||||||
| 
 |  | ||||||
| 				texture = new Texture(device, textureCreateInfo); |  | ||||||
| 				commandBuffer.SetTextureData(texture, pixels, (uint) len); |  | ||||||
| 
 |  | ||||||
| 				Refresh.Refresh_Image_Free(pixels); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			return texture; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Creates a 2D Texture using PNG or QOI data from a stream. |  | ||||||
| 		/// </summary> |  | ||||||
| 		public static unsafe Texture FromImageStream( |  | ||||||
| 			GraphicsDevice device, |  | ||||||
| 			CommandBuffer commandBuffer, |  | ||||||
| 			Stream stream |  | ||||||
| 		) { |  | ||||||
| 			var length = stream.Length; |  | ||||||
| 			var buffer = NativeMemory.Alloc((nuint) length); |  | ||||||
| 			var span = new Span<byte>(buffer, (int) length); |  | ||||||
| 			stream.ReadExactly(span); |  | ||||||
| 
 |  | ||||||
| 			var texture = FromImageBytes(device, commandBuffer, span); |  | ||||||
| 
 |  | ||||||
| 			NativeMemory.Free((void*) buffer); |  | ||||||
| 
 |  | ||||||
| 			return texture; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Creates a 2D Texture using PNG or QOI data from a file. |  | ||||||
| 		/// </summary> |  | ||||||
| 		public static Texture FromImageFile( |  | ||||||
| 			GraphicsDevice device, |  | ||||||
| 			CommandBuffer commandBuffer, |  | ||||||
| 			string path |  | ||||||
| 		) { |  | ||||||
| 			var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); |  | ||||||
| 			return FromImageStream(device, commandBuffer, fileStream); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public static unsafe void SetDataFromImageBytes( |  | ||||||
| 			CommandBuffer commandBuffer, |  | ||||||
| 			TextureSlice textureSlice, |  | ||||||
| 			Span<byte> data |  | ||||||
| 		) { |  | ||||||
| 			fixed (byte* ptr = data) |  | ||||||
| 			{ | 			{ | ||||||
| 				var pixels = Refresh.Refresh_Image_Load( | 				if (Device.DebugMode) | ||||||
| 					(nint) ptr, | 				{ | ||||||
| 					(int) data.Length, | 					Refresh.Refresh_SetTextureName( | ||||||
| 					out var w, | 						Device.Handle, | ||||||
| 					out var h, | 						Handle, | ||||||
| 					out var len | 						value | ||||||
| 					); | 					); | ||||||
|  | 				} | ||||||
| 
 | 
 | ||||||
| 				commandBuffer.SetTextureData(textureSlice, pixels, (uint) len); | 				name = value; | ||||||
| 
 |  | ||||||
| 				Refresh.Refresh_Image_Free(pixels); |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		/// <summary> | 		// FIXME: this allocates a delegate instance | ||||||
| 		/// Sets data for a texture slice using PNG or QOI data from a stream. | 		protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_ReleaseTexture; | ||||||
| 		/// </summary> |  | ||||||
| 		public static unsafe void SetDataFromImageStream( |  | ||||||
| 			CommandBuffer commandBuffer, |  | ||||||
| 			TextureSlice textureSlice, |  | ||||||
| 			Stream stream |  | ||||||
| 		) { |  | ||||||
| 			var length = stream.Length; |  | ||||||
| 			var buffer = NativeMemory.Alloc((nuint) length); |  | ||||||
| 			var span = new Span<byte>(buffer, (int) length); |  | ||||||
| 			stream.ReadExactly(span); |  | ||||||
| 
 |  | ||||||
| 			SetDataFromImageBytes(commandBuffer, textureSlice, span); |  | ||||||
| 
 |  | ||||||
| 			NativeMemory.Free((void*) buffer); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Sets data for a texture slice using PNG or QOI data from a file. |  | ||||||
| 		/// </summary> |  | ||||||
| 		public static void SetDataFromImageFile( |  | ||||||
| 			CommandBuffer commandBuffer, |  | ||||||
| 			TextureSlice textureSlice, |  | ||||||
| 			string path |  | ||||||
| 		) { |  | ||||||
| 			var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); |  | ||||||
| 			SetDataFromImageStream(commandBuffer, textureSlice, fileStream); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public unsafe static Texture LoadDDS(GraphicsDevice graphicsDevice, CommandBuffer commandBuffer, System.IO.Stream stream) |  | ||||||
| 		{ |  | ||||||
| 			using var reader = new BinaryReader(stream); |  | ||||||
| 			Texture texture; |  | ||||||
| 			int faces; |  | ||||||
| 			ParseDDS(reader, out var format, out var width, out var height, out var levels, out var isCube); |  | ||||||
| 
 |  | ||||||
| 			if (isCube) |  | ||||||
| 			{ |  | ||||||
| 				texture = CreateTextureCube(graphicsDevice, (uint) width, format, TextureUsageFlags.Sampler, (uint) levels); |  | ||||||
| 				faces = 6; |  | ||||||
| 			} |  | ||||||
| 			else |  | ||||||
| 			{ |  | ||||||
| 				texture = CreateTexture2D(graphicsDevice, (uint) width, (uint) height, format, TextureUsageFlags.Sampler, (uint) levels); |  | ||||||
| 				faces = 1; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			for (int i = 0; i < faces; i += 1) |  | ||||||
| 			{ |  | ||||||
| 				for (int j = 0; j < levels; j += 1) |  | ||||||
| 				{ |  | ||||||
| 					var levelWidth = width >> j; |  | ||||||
| 					var levelHeight = height >> j; |  | ||||||
| 
 |  | ||||||
| 					var levelSize = CalculateDDSLevelSize(levelWidth, levelHeight, format); |  | ||||||
| 					var byteBuffer = NativeMemory.Alloc((nuint) levelSize); |  | ||||||
| 					var byteSpan = new Span<byte>(byteBuffer, levelSize); |  | ||||||
| 					stream.ReadExactly(byteSpan); |  | ||||||
| 
 |  | ||||||
| 					var textureSlice = new TextureSlice(texture, new Rect(0, 0, levelWidth, levelHeight), 0, (uint) i, (uint) j); |  | ||||||
| 					commandBuffer.SetTextureData(textureSlice, (nint) byteBuffer, (uint) levelSize); |  | ||||||
| 
 |  | ||||||
| 					NativeMemory.Free(byteBuffer); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			return texture; |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Creates a 2D texture. | 		/// Creates a 2D texture. | ||||||
|  | @ -203,6 +66,7 @@ namespace MoonWorks.Graphics | ||||||
| 				Height = height, | 				Height = height, | ||||||
| 				Depth = 1, | 				Depth = 1, | ||||||
| 				IsCube = false, | 				IsCube = false, | ||||||
|  | 				LayerCount = 1, | ||||||
| 				LevelCount = levelCount, | 				LevelCount = levelCount, | ||||||
| 				SampleCount = sampleCount, | 				SampleCount = sampleCount, | ||||||
| 				Format = format, | 				Format = format, | ||||||
|  | @ -213,15 +77,43 @@ namespace MoonWorks.Graphics | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Creates a 3D texture. | 		/// Creates a 2D texture array. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		/// <param name="device">An initialized GraphicsDevice.</param> | 		/// <param name="device">An initialized GraphicsDevice.</param> | ||||||
| 		/// <param name="width">The width of the texture.</param> | 		/// <param name="width">The width of the texture.</param> | ||||||
| 		/// <param name="height">The height of the texture.</param> | 		/// <param name="height">The height of the texture.</param> | ||||||
| 		/// <param name="depth">The depth of the texture.</param> | 		/// <param name="layerCount">The layer count of the texture.</param> | ||||||
| 		/// <param name="format">The format of the texture.</param> | 		/// <param name="format">The format of the texture.</param> | ||||||
| 		/// <param name="usageFlags">Specifies how the texture will be used.</param> | 		/// <param name="usageFlags">Specifies how the texture will be used.</param> | ||||||
| 		/// <param name="levelCount">Specifies the number of mip levels.</param> | 		/// <param name="levelCount">Specifies the number of mip levels.</param> | ||||||
|  | 		public static Texture CreateTexture2DArray( | ||||||
|  | 			GraphicsDevice device, | ||||||
|  | 			uint width, | ||||||
|  | 			uint height, | ||||||
|  | 			uint layerCount, | ||||||
|  | 			TextureFormat format, | ||||||
|  | 			TextureUsageFlags usageFlags, | ||||||
|  | 			uint levelCount = 1 | ||||||
|  | 		) { | ||||||
|  | 			var textureCreateInfo = new TextureCreateInfo | ||||||
|  | 			{ | ||||||
|  | 				Width = width, | ||||||
|  | 				Height = height, | ||||||
|  | 				Depth = 1, | ||||||
|  | 				IsCube = false, | ||||||
|  | 				LayerCount = layerCount, | ||||||
|  | 				LevelCount = levelCount, | ||||||
|  | 				Format = format, | ||||||
|  | 				UsageFlags = usageFlags | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			return new Texture(device, textureCreateInfo); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Creates a 3D texture. | ||||||
|  | 		/// Note that the width, height and depth all form one slice and cannot be subdivided in a texture slice. | ||||||
|  | 		/// </summary> | ||||||
| 		public static Texture CreateTexture3D( | 		public static Texture CreateTexture3D( | ||||||
| 			GraphicsDevice device, | 			GraphicsDevice device, | ||||||
| 			uint width, | 			uint width, | ||||||
|  | @ -237,6 +129,7 @@ namespace MoonWorks.Graphics | ||||||
| 				Height = height, | 				Height = height, | ||||||
| 				Depth = depth, | 				Depth = depth, | ||||||
| 				IsCube = false, | 				IsCube = false, | ||||||
|  | 				LayerCount = 1, | ||||||
| 				LevelCount = levelCount, | 				LevelCount = levelCount, | ||||||
| 				Format = format, | 				Format = format, | ||||||
| 				UsageFlags = usageFlags | 				UsageFlags = usageFlags | ||||||
|  | @ -266,6 +159,7 @@ namespace MoonWorks.Graphics | ||||||
| 				Height = size, | 				Height = size, | ||||||
| 				Depth = 1, | 				Depth = 1, | ||||||
| 				IsCube = true, | 				IsCube = true, | ||||||
|  | 				LayerCount = 6, | ||||||
| 				LevelCount = levelCount, | 				LevelCount = levelCount, | ||||||
| 				Format = format, | 				Format = format, | ||||||
| 				UsageFlags = usageFlags | 				UsageFlags = usageFlags | ||||||
|  | @ -286,7 +180,7 @@ namespace MoonWorks.Graphics | ||||||
| 		{ | 		{ | ||||||
| 			Handle = Refresh.Refresh_CreateTexture( | 			Handle = Refresh.Refresh_CreateTexture( | ||||||
| 				device.Handle, | 				device.Handle, | ||||||
| 				textureCreateInfo.ToRefreshTextureCreateInfo() | 				textureCreateInfo.ToRefresh() | ||||||
| 			); | 			); | ||||||
| 
 | 
 | ||||||
| 			Format = textureCreateInfo.Format; | 			Format = textureCreateInfo.Format; | ||||||
|  | @ -294,16 +188,15 @@ namespace MoonWorks.Graphics | ||||||
| 			Height = textureCreateInfo.Height; | 			Height = textureCreateInfo.Height; | ||||||
| 			Depth = textureCreateInfo.Depth; | 			Depth = textureCreateInfo.Depth; | ||||||
| 			IsCube = textureCreateInfo.IsCube; | 			IsCube = textureCreateInfo.IsCube; | ||||||
|  | 			LayerCount = textureCreateInfo.LayerCount; | ||||||
| 			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); | 			Size = Width * Height * BytesPerPixel(Format) / BlockSizeSquared(Format); | ||||||
|  | 			name = ""; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public static implicit operator TextureSlice(Texture t) => new TextureSlice(t); | 		// Used by Window. Swapchain texture handles are managed by the driver backend. | ||||||
| 
 |  | ||||||
| 		// Used by AcquireSwapchainTexture. |  | ||||||
| 		// Should not be tracked, because swapchain textures are managed by Vulkan. |  | ||||||
| 		internal Texture( | 		internal Texture( | ||||||
| 			GraphicsDevice device, | 			GraphicsDevice device, | ||||||
| 			TextureFormat format | 			TextureFormat format | ||||||
|  | @ -322,333 +215,6 @@ namespace MoonWorks.Graphics | ||||||
| 			Size = Width * Height * BytesPerPixel(Format) / BlockSizeSquared(Format); | 			Size = Width * Height * BytesPerPixel(Format) / BlockSizeSquared(Format); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// DDS loading extension, based on MojoDDS |  | ||||||
| 		// Taken from https://github.com/FNA-XNA/FNA/blob/1e49f868f595f62bc6385db45949a03186a7cd7f/src/Graphics/Texture.cs#L194 |  | ||||||
| 		private static void ParseDDS( |  | ||||||
| 			BinaryReader reader, |  | ||||||
| 			out TextureFormat format, |  | ||||||
| 			out int width, |  | ||||||
| 			out int height, |  | ||||||
| 			out int levels, |  | ||||||
| 			out bool isCube |  | ||||||
| 		) { |  | ||||||
| 			// A whole bunch of magic numbers, yay DDS! |  | ||||||
| 			const uint DDS_MAGIC = 0x20534444; |  | ||||||
| 			const uint DDS_HEADERSIZE = 124; |  | ||||||
| 			const uint DDS_PIXFMTSIZE = 32; |  | ||||||
| 			const uint DDSD_HEIGHT = 0x2; |  | ||||||
| 			const uint DDSD_WIDTH = 0x4; |  | ||||||
| 			const uint DDSD_PITCH = 0x8; |  | ||||||
| 			const uint DDSD_LINEARSIZE = 0x80000; |  | ||||||
| 			const uint DDSD_REQ = ( |  | ||||||
| 				DDSD_HEIGHT | DDSD_WIDTH |  | ||||||
| 			); |  | ||||||
| 			const uint DDSCAPS_MIPMAP = 0x400000; |  | ||||||
| 			const uint DDSCAPS_TEXTURE = 0x1000; |  | ||||||
| 			const uint DDSCAPS2_CUBEMAP = 0x200; |  | ||||||
| 			const uint DDPF_FOURCC = 0x4; |  | ||||||
| 			const uint DDPF_RGB = 0x40; |  | ||||||
| 			const uint FOURCC_DXT1 = 0x31545844; |  | ||||||
| 			const uint FOURCC_DXT3 = 0x33545844; |  | ||||||
| 			const uint FOURCC_DXT5 = 0x35545844; |  | ||||||
| 			const uint FOURCC_DX10 = 0x30315844; |  | ||||||
| 			const uint pitchAndLinear = ( |  | ||||||
| 				DDSD_PITCH | DDSD_LINEARSIZE |  | ||||||
| 			); |  | ||||||
| 
 |  | ||||||
| 			// File should start with 'DDS ' |  | ||||||
| 			if (reader.ReadUInt32() != DDS_MAGIC) |  | ||||||
| 			{ |  | ||||||
| 				throw new NotSupportedException("Not a DDS!"); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// Texture info |  | ||||||
| 			uint size = reader.ReadUInt32(); |  | ||||||
| 			if (size != DDS_HEADERSIZE) |  | ||||||
| 			{ |  | ||||||
| 				throw new NotSupportedException("Invalid DDS header!"); |  | ||||||
| 			} |  | ||||||
| 			uint flags = reader.ReadUInt32(); |  | ||||||
| 			if ((flags & DDSD_REQ) != DDSD_REQ) |  | ||||||
| 			{ |  | ||||||
| 				throw new NotSupportedException("Invalid DDS flags!"); |  | ||||||
| 			} |  | ||||||
| 			if ((flags & pitchAndLinear) == pitchAndLinear) |  | ||||||
| 			{ |  | ||||||
| 				throw new NotSupportedException("Invalid DDS flags!"); |  | ||||||
| 			} |  | ||||||
| 			height = reader.ReadInt32(); |  | ||||||
| 			width = reader.ReadInt32(); |  | ||||||
| 			reader.ReadUInt32(); // dwPitchOrLinearSize, unused |  | ||||||
| 			reader.ReadUInt32(); // dwDepth, unused |  | ||||||
| 			levels = reader.ReadInt32(); |  | ||||||
| 
 |  | ||||||
| 			// "Reserved" |  | ||||||
| 			reader.ReadBytes(4 * 11); |  | ||||||
| 
 |  | ||||||
| 			// Format info |  | ||||||
| 			uint formatSize = reader.ReadUInt32(); |  | ||||||
| 			if (formatSize != DDS_PIXFMTSIZE) |  | ||||||
| 			{ |  | ||||||
| 				throw new NotSupportedException("Bogus PIXFMTSIZE!"); |  | ||||||
| 			} |  | ||||||
| 			uint formatFlags = reader.ReadUInt32(); |  | ||||||
| 			uint formatFourCC = reader.ReadUInt32(); |  | ||||||
| 			uint formatRGBBitCount = reader.ReadUInt32(); |  | ||||||
| 			uint formatRBitMask = reader.ReadUInt32(); |  | ||||||
| 			uint formatGBitMask = reader.ReadUInt32(); |  | ||||||
| 			uint formatBBitMask = reader.ReadUInt32(); |  | ||||||
| 			uint formatABitMask = reader.ReadUInt32(); |  | ||||||
| 
 |  | ||||||
| 			// dwCaps "stuff" |  | ||||||
| 			uint caps = reader.ReadUInt32(); |  | ||||||
| 			if ((caps & DDSCAPS_TEXTURE) == 0) |  | ||||||
| 			{ |  | ||||||
| 				throw new NotSupportedException("Not a texture!"); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			isCube = false; |  | ||||||
| 
 |  | ||||||
| 			uint caps2 = reader.ReadUInt32(); |  | ||||||
| 			if (caps2 != 0) |  | ||||||
| 			{ |  | ||||||
| 				if ((caps2 & DDSCAPS2_CUBEMAP) == DDSCAPS2_CUBEMAP) |  | ||||||
| 				{ |  | ||||||
| 					isCube = true; |  | ||||||
| 				} |  | ||||||
| 				else |  | ||||||
| 				{ |  | ||||||
| 					throw new NotSupportedException("Invalid caps2!"); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			reader.ReadUInt32(); // dwCaps3, unused |  | ||||||
| 			reader.ReadUInt32(); // dwCaps4, unused |  | ||||||
| 
 |  | ||||||
| 			// "Reserved" |  | ||||||
| 			reader.ReadUInt32(); |  | ||||||
| 
 |  | ||||||
| 			// Mipmap sanity check |  | ||||||
| 			if ((caps & DDSCAPS_MIPMAP) != DDSCAPS_MIPMAP) |  | ||||||
| 			{ |  | ||||||
| 				levels = 1; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// Determine texture format |  | ||||||
| 			if ((formatFlags & DDPF_FOURCC) == DDPF_FOURCC) |  | ||||||
| 			{ |  | ||||||
| 				switch (formatFourCC) |  | ||||||
| 				{ |  | ||||||
| 					case 0x71: // D3DFMT_A16B16G16R16F |  | ||||||
| 						format = TextureFormat.R16G16B16A16_SFLOAT; |  | ||||||
| 						break; |  | ||||||
| 					case 0x74: // D3DFMT_A32B32G32R32F |  | ||||||
| 						format = TextureFormat.R32G32B32A32_SFLOAT; |  | ||||||
| 						break; |  | ||||||
| 					case FOURCC_DXT1: |  | ||||||
| 						format = TextureFormat.BC1; |  | ||||||
| 						break; |  | ||||||
| 					case FOURCC_DXT3: |  | ||||||
| 						format = TextureFormat.BC2; |  | ||||||
| 						break; |  | ||||||
| 					case FOURCC_DXT5: |  | ||||||
| 						format = TextureFormat.BC3; |  | ||||||
| 						break; |  | ||||||
| 					case FOURCC_DX10: |  | ||||||
| 						// If the fourCC is DX10, there is an extra header with additional format information. |  | ||||||
| 						uint dxgiFormat = reader.ReadUInt32(); |  | ||||||
| 
 |  | ||||||
| 						// These values are taken from the DXGI_FORMAT enum. |  | ||||||
| 						switch (dxgiFormat) |  | ||||||
| 						{ |  | ||||||
| 							case 2: |  | ||||||
| 								format = TextureFormat.R32G32B32A32_SFLOAT; |  | ||||||
| 								break; |  | ||||||
| 
 |  | ||||||
| 							case 10: |  | ||||||
| 								format = TextureFormat.R16G16B16A16_SFLOAT; |  | ||||||
| 								break; |  | ||||||
| 
 |  | ||||||
| 							case 71: |  | ||||||
| 								format = TextureFormat.BC1; |  | ||||||
| 								break; |  | ||||||
| 
 |  | ||||||
| 							case 74: |  | ||||||
| 								format = TextureFormat.BC2; |  | ||||||
| 								break; |  | ||||||
| 
 |  | ||||||
| 							case 77: |  | ||||||
| 								format = TextureFormat.BC3; |  | ||||||
| 								break; |  | ||||||
| 
 |  | ||||||
| 							case 98: |  | ||||||
| 								format = TextureFormat.BC7; |  | ||||||
| 								break; |  | ||||||
| 
 |  | ||||||
| 							default: |  | ||||||
| 								throw new NotSupportedException( |  | ||||||
| 									"Unsupported DDS texture format" |  | ||||||
| 								); |  | ||||||
| 						} |  | ||||||
| 
 |  | ||||||
| 						uint resourceDimension = reader.ReadUInt32(); |  | ||||||
| 
 |  | ||||||
| 						// These values are taken from the D3D10_RESOURCE_DIMENSION enum. |  | ||||||
| 						switch (resourceDimension) |  | ||||||
| 						{ |  | ||||||
| 							case 0: // Unknown |  | ||||||
| 							case 1: // Buffer |  | ||||||
| 								throw new NotSupportedException( |  | ||||||
| 									"Unsupported DDS texture format" |  | ||||||
| 								); |  | ||||||
| 							default: |  | ||||||
| 								break; |  | ||||||
| 						} |  | ||||||
| 
 |  | ||||||
| 						/* |  | ||||||
| 						 * This flag seemingly only indicates if the texture is a cube map. |  | ||||||
| 						 * This is already determined above. Cool! |  | ||||||
| 						 */ |  | ||||||
| 						uint miscFlag = reader.ReadUInt32(); |  | ||||||
| 
 |  | ||||||
| 						/* |  | ||||||
| 						 * Indicates the number of elements in the texture array. |  | ||||||
| 						 * We don't support texture arrays so just throw if it's greater than 1. |  | ||||||
| 						 */ |  | ||||||
| 						uint arraySize = reader.ReadUInt32(); |  | ||||||
| 
 |  | ||||||
| 						if (arraySize > 1) |  | ||||||
| 						{ |  | ||||||
| 							throw new NotSupportedException( |  | ||||||
| 								"Unsupported DDS texture format" |  | ||||||
| 							); |  | ||||||
| 						} |  | ||||||
| 
 |  | ||||||
| 						reader.ReadUInt32(); // reserved |  | ||||||
| 
 |  | ||||||
| 						break; |  | ||||||
| 					default: |  | ||||||
| 						throw new NotSupportedException( |  | ||||||
| 							"Unsupported DDS texture format" |  | ||||||
| 						); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			else if ((formatFlags & DDPF_RGB) == DDPF_RGB) |  | ||||||
| 			{ |  | ||||||
| 				if (	formatRGBBitCount != 32 || |  | ||||||
| 					formatRBitMask != 0x00FF0000 || |  | ||||||
| 					formatGBitMask != 0x0000FF00 || |  | ||||||
| 					formatBBitMask != 0x000000FF || |  | ||||||
| 					formatABitMask != 0xFF000000	) |  | ||||||
| 				{ |  | ||||||
| 					throw new NotSupportedException( |  | ||||||
| 						"Unsupported DDS texture format" |  | ||||||
| 					); |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				format = TextureFormat.B8G8R8A8; |  | ||||||
| 			} |  | ||||||
| 			else |  | ||||||
| 			{ |  | ||||||
| 				throw new NotSupportedException( |  | ||||||
| 					"Unsupported DDS texture format" |  | ||||||
| 				); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		private static int CalculateDDSLevelSize( |  | ||||||
| 			int width, |  | ||||||
| 			int height, |  | ||||||
| 			TextureFormat format |  | ||||||
| 		) { |  | ||||||
| 			if (format == TextureFormat.R8G8B8A8) |  | ||||||
| 			{ |  | ||||||
| 				return (((width * 32) + 7) / 8) * height; |  | ||||||
| 			} |  | ||||||
| 			else if (format == TextureFormat.R16G16B16A16_SFLOAT) |  | ||||||
| 			{ |  | ||||||
| 				return (((width * 64) + 7) / 8) * height; |  | ||||||
| 			} |  | ||||||
| 			else if (format == TextureFormat.R32G32B32A32_SFLOAT) |  | ||||||
| 			{ |  | ||||||
| 				return (((width * 128) + 7) / 8) * height; |  | ||||||
| 			} |  | ||||||
| 			else |  | ||||||
| 			{ |  | ||||||
| 				int blockSize = 16; |  | ||||||
| 				if (format == TextureFormat.BC1) |  | ||||||
| 				{ |  | ||||||
| 					blockSize = 8; |  | ||||||
| 				} |  | ||||||
| 				width = System.Math.Max(width, 1); |  | ||||||
| 				height = System.Math.Max(height, 1); |  | ||||||
| 				return ( |  | ||||||
| 					((width + 3) / 4) * |  | ||||||
| 					((height + 3) / 4) * |  | ||||||
| 					blockSize |  | ||||||
| 				); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/// <summary> |  | ||||||
| 		/// Asynchronously saves RGBA or BGRA pixel data to a file in PNG format. <br/> |  | ||||||
| 		/// Warning: this is expensive and will block to wait for data download from GPU! <br/> |  | ||||||
| 		/// You can avoid blocking by calling this method from a thread. |  | ||||||
| 		/// </summary> |  | ||||||
| 		public unsafe void SavePNG(string path) |  | ||||||
| 		{ |  | ||||||
| #if DEBUG |  | ||||||
| 			if (Format != TextureFormat.R8G8B8A8 && Format != TextureFormat.B8G8R8A8) |  | ||||||
| 			{ |  | ||||||
| 				throw new ArgumentException("Texture format must be RGBA or BGRA!", "format"); |  | ||||||
| 			} |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| 			var buffer = new Buffer(Device, 0, Width * Height * 4); // this creates garbage... oh well |  | ||||||
| 
 |  | ||||||
| 			// immediately request the data copy |  | ||||||
| 			var commandBuffer = Device.AcquireCommandBuffer(); |  | ||||||
| 			commandBuffer.CopyTextureToBuffer(this, buffer); |  | ||||||
| 			var fence = Device.SubmitAndAcquireFence(commandBuffer); |  | ||||||
| 
 |  | ||||||
| 			var byteCount = buffer.Size; |  | ||||||
| 
 |  | ||||||
| 			var pixelsPtr = NativeMemory.Alloc((nuint) byteCount); |  | ||||||
| 			var pixelsSpan = new Span<byte>(pixelsPtr, (int) byteCount); |  | ||||||
| 
 |  | ||||||
| 			Device.WaitForFences(fence); // make sure the data transfer is done... |  | ||||||
| 			Device.ReleaseFence(fence); // and then release the fence |  | ||||||
| 
 |  | ||||||
| 			buffer.GetData(pixelsSpan); |  | ||||||
| 
 |  | ||||||
| 			if (Format == TextureFormat.B8G8R8A8) |  | ||||||
| 			{ |  | ||||||
| 				var rgbaPtr = NativeMemory.Alloc((nuint) byteCount); |  | ||||||
| 				var rgbaSpan = new Span<byte>(rgbaPtr, (int) byteCount); |  | ||||||
| 
 |  | ||||||
| 				for (var i = 0; i < byteCount; i += 4) |  | ||||||
| 				{ |  | ||||||
| 					rgbaSpan[i] = pixelsSpan[i + 2]; |  | ||||||
| 					rgbaSpan[i + 1] = pixelsSpan[i + 1]; |  | ||||||
| 					rgbaSpan[i + 2] = pixelsSpan[i]; |  | ||||||
| 					rgbaSpan[i + 3] = pixelsSpan[i + 3]; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				Refresh.Refresh_Image_SavePNG(path, (nint) rgbaPtr, (int) Width, (int) Height); |  | ||||||
| 
 |  | ||||||
| 				NativeMemory.Free((void*) rgbaPtr); |  | ||||||
| 			} |  | ||||||
| 			else |  | ||||||
| 			{ |  | ||||||
| 				fixed (byte* ptr = pixelsSpan) |  | ||||||
| 				{ |  | ||||||
| 					Refresh.Refresh_Image_SavePNG(path, (nint) ptr, (int) Width, (int) Height); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			NativeMemory.Free(pixelsPtr); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public static uint BytesPerPixel(TextureFormat format) | 		public static uint BytesPerPixel(TextureFormat format) | ||||||
| 		{ | 		{ | ||||||
| 			switch (format) | 			switch (format) | ||||||
|  | @ -663,10 +229,8 @@ namespace MoonWorks.Graphics | ||||||
| 				case TextureFormat.R8G8_SNORM: | 				case TextureFormat.R8G8_SNORM: | ||||||
| 				case TextureFormat.R8G8_UINT: | 				case TextureFormat.R8G8_UINT: | ||||||
| 				case TextureFormat.R16_UINT: | 				case TextureFormat.R16_UINT: | ||||||
| 				case TextureFormat.D16: | 				case TextureFormat.D16_UNORM: | ||||||
| 					return 2; | 					return 2; | ||||||
| 				case TextureFormat.D16S8: |  | ||||||
| 					return 3; |  | ||||||
| 				case TextureFormat.R8G8B8A8: | 				case TextureFormat.R8G8B8A8: | ||||||
| 				case TextureFormat.B8G8R8A8: | 				case TextureFormat.B8G8R8A8: | ||||||
| 				case TextureFormat.R32_SFLOAT: | 				case TextureFormat.R32_SFLOAT: | ||||||
|  | @ -676,9 +240,10 @@ namespace MoonWorks.Graphics | ||||||
| 				case TextureFormat.A2R10G10B10: | 				case TextureFormat.A2R10G10B10: | ||||||
| 				case TextureFormat.R8G8B8A8_UINT: | 				case TextureFormat.R8G8B8A8_UINT: | ||||||
| 				case TextureFormat.R16G16_UINT: | 				case TextureFormat.R16G16_UINT: | ||||||
| 				case TextureFormat.D32: | 				case TextureFormat.D24_UNORM_S8_UINT: | ||||||
|  | 				case TextureFormat.D32_SFLOAT: | ||||||
| 					return 4; | 					return 4; | ||||||
| 				case TextureFormat.D32S8: | 				case TextureFormat.D32_SFLOAT_S8_UINT: | ||||||
| 					return 5; | 					return 5; | ||||||
| 				case TextureFormat.R16G16B16A16_SFLOAT: | 				case TextureFormat.R16G16B16A16_SFLOAT: | ||||||
| 				case TextureFormat.R16G16B16A16: | 				case TextureFormat.R16G16B16A16: | ||||||
|  | @ -697,6 +262,21 @@ namespace MoonWorks.Graphics | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		public static uint TexelSize(TextureFormat format) | ||||||
|  | 		{ | ||||||
|  | 			switch (format) | ||||||
|  | 			{ | ||||||
|  | 				case TextureFormat.BC2: | ||||||
|  | 				case TextureFormat.BC3: | ||||||
|  | 				case TextureFormat.BC7: | ||||||
|  | 					return 16; | ||||||
|  | 				case TextureFormat.BC1: | ||||||
|  | 					return 8; | ||||||
|  | 				default: | ||||||
|  | 					return 1; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		public static uint BlockSizeSquared(TextureFormat format) | 		public static uint BlockSizeSquared(TextureFormat format) | ||||||
| 		{ | 		{ | ||||||
| 			switch (format) | 			switch (format) | ||||||
|  | @ -729,15 +309,19 @@ namespace MoonWorks.Graphics | ||||||
| 				case TextureFormat.R16_UINT: | 				case TextureFormat.R16_UINT: | ||||||
| 				case TextureFormat.R16G16_UINT: | 				case TextureFormat.R16G16_UINT: | ||||||
| 				case TextureFormat.R16G16B16A16_UINT: | 				case TextureFormat.R16G16B16A16_UINT: | ||||||
| 				case TextureFormat.D16: | 				case TextureFormat.D16_UNORM: | ||||||
| 				case TextureFormat.D32: | 				case TextureFormat.D24_UNORM: | ||||||
| 				case TextureFormat.D16S8: | 				case TextureFormat.D24_UNORM_S8_UINT: | ||||||
| 				case TextureFormat.D32S8: | 				case TextureFormat.D32_SFLOAT: | ||||||
|  | 				case TextureFormat.D32_SFLOAT_S8_UINT: | ||||||
| 					return 1; | 					return 1; | ||||||
| 				default: | 				default: | ||||||
| 					Logger.LogError("Texture format not recognized!"); | 					Logger.LogError("Texture format not recognized!"); | ||||||
| 					return 0; | 					return 0; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		public static implicit operator TextureSlice(Texture t) => new TextureSlice(t); | ||||||
|  | 		public static implicit operator TextureRegion(Texture t) => new TextureRegion(t); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,161 @@ | ||||||
|  | using System; | ||||||
|  | using System.Runtime.InteropServices; | ||||||
|  | using RefreshCS; | ||||||
|  | 
 | ||||||
|  | namespace MoonWorks.Graphics | ||||||
|  | { | ||||||
|  | 	public unsafe class TransferBuffer : RefreshResource | ||||||
|  | 	{ | ||||||
|  | 		protected override Action<IntPtr, IntPtr> ReleaseFunction => Refresh.Refresh_ReleaseTransferBuffer; | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Size in bytes. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public uint Size { get; } | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Creates a buffer of requested size given a type and element count. | ||||||
|  | 		/// </summary> | ||||||
|  | 		/// <typeparam name="T">The type that the buffer will contain.</typeparam> | ||||||
|  | 		/// <param name="device">The GraphicsDevice.</param> | ||||||
|  | 		/// <param name="elementCount">How many elements of type T the buffer will contain.</param> | ||||||
|  | 		/// <returns></returns> | ||||||
|  | 		public unsafe static TransferBuffer Create<T>( | ||||||
|  | 			GraphicsDevice device, | ||||||
|  | 			TransferUsage usage, | ||||||
|  | 			TransferBufferMapFlags mapFlags, | ||||||
|  | 			uint elementCount | ||||||
|  | 		) where T : unmanaged | ||||||
|  | 		{ | ||||||
|  | 			return new TransferBuffer( | ||||||
|  | 				device, | ||||||
|  | 				usage, | ||||||
|  | 				mapFlags, | ||||||
|  | 				(uint) Marshal.SizeOf<T>() * elementCount | ||||||
|  | 			); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Creates a TransferBuffer. | ||||||
|  | 		/// </summary> | ||||||
|  | 		/// <param name="device">An initialized GraphicsDevice.</param> | ||||||
|  | 		/// <param name="sizeInBytes">The length of the buffer. Cannot be resized.</param> | ||||||
|  | 		/// <param name="usage">Whether this will be used to upload buffers or textures.</param> | ||||||
|  | 		public TransferBuffer( | ||||||
|  | 			GraphicsDevice device, | ||||||
|  | 			TransferUsage usage, | ||||||
|  | 			TransferBufferMapFlags mapFlags, | ||||||
|  | 			uint sizeInBytes | ||||||
|  | 		) : base(device) | ||||||
|  | 		{ | ||||||
|  | 			Handle = Refresh.Refresh_CreateTransferBuffer( | ||||||
|  | 				device.Handle, | ||||||
|  | 				(Refresh.TransferUsage) usage, | ||||||
|  | 				(Refresh.TransferBufferMapFlags) mapFlags, | ||||||
|  | 				sizeInBytes | ||||||
|  | 			); | ||||||
|  | 			Size = sizeInBytes; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Immediately copies data from a Span to the TransferBuffer. | ||||||
|  | 		/// Returns the length of the copy in bytes. | ||||||
|  | 		/// | ||||||
|  | 		/// If cycle is set to true and this TransferBuffer was used in an Upload command, | ||||||
|  | 		/// that command will still use the corret data at the cost of increased memory usage. | ||||||
|  | 		/// | ||||||
|  | 		/// If cycle is set to false, the data will be overwritten immediately, | ||||||
|  | 		/// which could cause a data race. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public unsafe uint SetData<T>( | ||||||
|  | 			Span<T> data, | ||||||
|  | 			uint bufferOffsetInBytes, | ||||||
|  | 			bool cycle | ||||||
|  | 		) where T : unmanaged | ||||||
|  | 		{ | ||||||
|  | 			var elementSize = Marshal.SizeOf<T>(); | ||||||
|  | 			var dataLengthInBytes = (uint) (elementSize * data.Length); | ||||||
|  | 
 | ||||||
|  | #if DEBUG | ||||||
|  | 			AssertBufferBoundsCheck(Size, bufferOffsetInBytes, dataLengthInBytes); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 			fixed (T* dataPtr = data) | ||||||
|  | 			{ | ||||||
|  | 				Refresh.Refresh_SetTransferData( | ||||||
|  | 					Device.Handle, | ||||||
|  | 					(nint) dataPtr, | ||||||
|  | 					Handle, | ||||||
|  | 					new Refresh.BufferCopy | ||||||
|  | 					{ | ||||||
|  | 						SourceOffset = 0, | ||||||
|  | 						DestinationOffset = bufferOffsetInBytes, | ||||||
|  | 						Size = dataLengthInBytes | ||||||
|  | 					}, | ||||||
|  | 					Conversions.BoolToInt(cycle) | ||||||
|  | 				); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return dataLengthInBytes; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Immediately copies data from a Span to the TransferBuffer. | ||||||
|  | 		/// Returns the length of the copy in bytes. | ||||||
|  | 		/// | ||||||
|  | 		/// If cycle is set to true and this TransferBuffer was used in an Upload command, | ||||||
|  | 		/// that command will still use the corret data at the cost of increased memory usage. | ||||||
|  | 		/// | ||||||
|  | 		/// If cycle is set to false, the data will be overwritten immediately, | ||||||
|  | 		/// which could cause a data race. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public unsafe uint SetData<T>( | ||||||
|  | 			Span<T> data, | ||||||
|  | 			bool cycle | ||||||
|  | 		) where T : unmanaged | ||||||
|  | 		{ | ||||||
|  | 			return SetData(data, 0, cycle); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Immediately copies data from the TransferBuffer into a Span. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public unsafe void GetData<T>( | ||||||
|  | 			Span<T> data, | ||||||
|  | 			uint bufferOffsetInBytes = 0 | ||||||
|  | 		) where T : unmanaged | ||||||
|  | 		{ | ||||||
|  | 			var elementSize = Marshal.SizeOf<T>(); | ||||||
|  | 			var dataLengthInBytes = (uint) (elementSize * data.Length); | ||||||
|  | 
 | ||||||
|  | #if DEBUG | ||||||
|  | 			AssertBufferBoundsCheck(Size, bufferOffsetInBytes, dataLengthInBytes); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 			fixed (T* dataPtr = data) | ||||||
|  | 			{ | ||||||
|  | 				Refresh.Refresh_GetTransferData( | ||||||
|  | 					Device.Handle, | ||||||
|  | 					Handle, | ||||||
|  | 					(nint) dataPtr, | ||||||
|  | 					new Refresh.BufferCopy | ||||||
|  | 					{ | ||||||
|  | 						SourceOffset = bufferOffsetInBytes, | ||||||
|  | 						DestinationOffset = 0, | ||||||
|  | 						Size = dataLengthInBytes | ||||||
|  | 					} | ||||||
|  | 				); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | #if DEBUG | ||||||
|  | 		private void AssertBufferBoundsCheck(uint bufferLengthInBytes, uint offsetInBytes, uint copyLengthInBytes) | ||||||
|  | 		{ | ||||||
|  | 			if (copyLengthInBytes > bufferLengthInBytes + offsetInBytes) | ||||||
|  | 			{ | ||||||
|  | 				throw new InvalidOperationException($"Data overflow! Transfer buffer length {bufferLengthInBytes}, offset {offsetInBytes}, copy length {copyLengthInBytes}"); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -110,14 +110,14 @@ namespace MoonWorks.Graphics | ||||||
| 		{ | 		{ | ||||||
| 			return new Refresh.ColorAttachmentBlendState | 			return new Refresh.ColorAttachmentBlendState | ||||||
| 			{ | 			{ | ||||||
| 				blendEnable = Conversions.BoolToByte(BlendEnable), | 				BlendEnable = Conversions.BoolToInt(BlendEnable), | ||||||
| 				alphaBlendOp = (Refresh.BlendOp) AlphaBlendOp, | 				AlphaBlendOp = (Refresh.BlendOp) AlphaBlendOp, | ||||||
| 				colorBlendOp = (Refresh.BlendOp) ColorBlendOp, | 				ColorBlendOp = (Refresh.BlendOp) ColorBlendOp, | ||||||
| 				colorWriteMask = (Refresh.ColorComponentFlags) ColorWriteMask, | 				ColorWriteMask = (Refresh.ColorComponentFlags) ColorWriteMask, | ||||||
| 				destinationAlphaBlendFactor = (Refresh.BlendFactor) DestinationAlphaBlendFactor, | 				DestinationAlphaBlendFactor = (Refresh.BlendFactor) DestinationAlphaBlendFactor, | ||||||
| 				destinationColorBlendFactor = (Refresh.BlendFactor) DestinationColorBlendFactor, | 				DestinationColorBlendFactor = (Refresh.BlendFactor) DestinationColorBlendFactor, | ||||||
| 				sourceAlphaBlendFactor = (Refresh.BlendFactor) SourceAlphaBlendFactor, | 				SourceAlphaBlendFactor = (Refresh.BlendFactor) SourceAlphaBlendFactor, | ||||||
| 				sourceColorBlendFactor = (Refresh.BlendFactor) SourceColorBlendFactor | 				SourceColorBlendFactor = (Refresh.BlendFactor) SourceColorBlendFactor | ||||||
| 			}; | 			}; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -1,50 +0,0 @@ | ||||||
| using System.Runtime.InteropServices; |  | ||||||
| 
 |  | ||||||
| namespace MoonWorks.Graphics |  | ||||||
| { |  | ||||||
| 	/// <summary> |  | ||||||
| 	/// Information that the compute pipeline needs about a compute shader. |  | ||||||
| 	/// </summary> |  | ||||||
| 	public struct ComputeShaderInfo |  | ||||||
| 	{ |  | ||||||
| 		public ShaderModule ShaderModule; |  | ||||||
| 		public string EntryPointName; |  | ||||||
| 		public uint UniformBufferSize; |  | ||||||
| 		public uint BufferBindingCount; |  | ||||||
| 		public uint ImageBindingCount; |  | ||||||
| 
 |  | ||||||
| 		public unsafe static ComputeShaderInfo Create<T>( |  | ||||||
| 			ShaderModule shaderModule, |  | ||||||
| 			string entryPointName, |  | ||||||
| 			uint bufferBindingCount, |  | ||||||
| 			uint imageBindingCount |  | ||||||
| 		) where T : unmanaged |  | ||||||
| 		{ |  | ||||||
| 			return new ComputeShaderInfo |  | ||||||
| 			{ |  | ||||||
| 				ShaderModule = shaderModule, |  | ||||||
| 				EntryPointName = entryPointName, |  | ||||||
| 				UniformBufferSize = (uint) Marshal.SizeOf<T>(), |  | ||||||
| 				BufferBindingCount = bufferBindingCount, |  | ||||||
| 				ImageBindingCount = imageBindingCount |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
|         public static ComputeShaderInfo Create( |  | ||||||
|             ShaderModule shaderModule, |  | ||||||
|             string entryPointName, |  | ||||||
|             uint bufferBindingCount, |  | ||||||
|             uint imageBindingCount |  | ||||||
|         ) |  | ||||||
|         { |  | ||||||
|             return new ComputeShaderInfo |  | ||||||
| 			{ |  | ||||||
| 				ShaderModule = shaderModule, |  | ||||||
| 				EntryPointName = entryPointName, |  | ||||||
| 				UniformBufferSize = 0, |  | ||||||
| 				BufferBindingCount = bufferBindingCount, |  | ||||||
| 				ImageBindingCount = imageBindingCount |  | ||||||
| 			}; |  | ||||||
|         } |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -1,4 +1,6 @@ | ||||||
| namespace MoonWorks.Graphics | using RefreshCS; | ||||||
|  | 
 | ||||||
|  | namespace MoonWorks.Graphics | ||||||
| { | { | ||||||
| 	/// <summary> | 	/// <summary> | ||||||
| 	/// Determines how data is written to and read from the depth/stencil buffer. | 	/// Determines how data is written to and read from the depth/stencil buffer. | ||||||
|  | @ -11,15 +13,30 @@ | ||||||
| 		public bool DepthTestEnable; | 		public bool DepthTestEnable; | ||||||
| 
 | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Describes the stencil operation for back-facing primitives. | 		/// Describes the back-face stencil operation. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		public StencilOpState BackStencilState; | 		public StencilOpState BackStencilState; | ||||||
| 
 | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Describes the stencil operation for front-facing primitives. | 		/// Describes the front-face stencil operation. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		public StencilOpState FrontStencilState; | 		public StencilOpState FrontStencilState; | ||||||
| 
 | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// The compare mask for stencil ops. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public uint CompareMask; | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// The write mask for stencil ops. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public uint WriteMask; | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// The stencil reference value. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public uint Reference; | ||||||
|  | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// The comparison operator used in the depth test. | 		/// The comparison operator used in the depth test. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
|  | @ -75,5 +92,24 @@ | ||||||
| 			DepthBoundsTestEnable = false, | 			DepthBoundsTestEnable = false, | ||||||
| 			StencilTestEnable = false | 			StencilTestEnable = false | ||||||
| 		}; | 		}; | ||||||
|  | 
 | ||||||
|  | 		public Refresh.DepthStencilState ToRefresh() | ||||||
|  | 		{ | ||||||
|  | 			return new Refresh.DepthStencilState | ||||||
|  | 			{ | ||||||
|  | 				DepthTestEnable = Conversions.BoolToInt(DepthTestEnable), | ||||||
|  | 				BackStencilState = BackStencilState.ToRefresh(), | ||||||
|  | 				FrontStencilState = FrontStencilState.ToRefresh(), | ||||||
|  | 				CompareMask = CompareMask, | ||||||
|  | 				WriteMask = WriteMask, | ||||||
|  | 				Reference = Reference, | ||||||
|  | 				CompareOp = (Refresh.CompareOp) CompareOp, | ||||||
|  | 				DepthBoundsTestEnable = Conversions.BoolToInt(DepthBoundsTestEnable), | ||||||
|  | 				DepthWriteEnable = Conversions.BoolToInt(DepthWriteEnable), | ||||||
|  | 				MinDepthBounds = MinDepthBounds, | ||||||
|  | 				MaxDepthBounds = MaxDepthBounds, | ||||||
|  | 				StencilTestEnable = Conversions.BoolToInt(StencilTestEnable) | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ namespace MoonWorks.Graphics | ||||||
| 		) { | 		) { | ||||||
| 			ColorAttachmentDescriptions = colorAttachmentDescriptions; | 			ColorAttachmentDescriptions = colorAttachmentDescriptions; | ||||||
| 			HasDepthStencilAttachment = false; | 			HasDepthStencilAttachment = false; | ||||||
| 			DepthStencilFormat = TextureFormat.D16; | 			DepthStencilFormat = TextureFormat.D16_UNORM; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public GraphicsPipelineAttachmentInfo( | 		public GraphicsPipelineAttachmentInfo( | ||||||
|  |  | ||||||
|  | @ -1,18 +0,0 @@ | ||||||
| namespace MoonWorks.Graphics |  | ||||||
| { |  | ||||||
| 	/// <summary> |  | ||||||
| 	/// All of the information that is used to create a GraphicsPipeline. |  | ||||||
| 	/// </summary> |  | ||||||
| 	public struct GraphicsPipelineCreateInfo |  | ||||||
| 	{ |  | ||||||
| 		public DepthStencilState DepthStencilState; |  | ||||||
| 		public GraphicsShaderInfo VertexShaderInfo; |  | ||||||
| 		public GraphicsShaderInfo FragmentShaderInfo; |  | ||||||
| 		public MultisampleState MultisampleState; |  | ||||||
| 		public RasterizerState RasterizerState; |  | ||||||
| 		public PrimitiveType PrimitiveType; |  | ||||||
| 		public VertexInputState VertexInputState; |  | ||||||
| 		public GraphicsPipelineAttachmentInfo AttachmentInfo; |  | ||||||
| 		public BlendConstants BlendConstants; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -1,44 +0,0 @@ | ||||||
| using System.Runtime.InteropServices; |  | ||||||
| 
 |  | ||||||
| namespace MoonWorks.Graphics |  | ||||||
| { |  | ||||||
| 	/// <summary> |  | ||||||
| 	/// Information that the pipeline needs about a graphics shader. |  | ||||||
| 	/// </summary> |  | ||||||
| 	public struct GraphicsShaderInfo |  | ||||||
| 	{ |  | ||||||
| 		public ShaderModule ShaderModule; |  | ||||||
| 		public string EntryPointName; |  | ||||||
| 		public uint UniformBufferSize; |  | ||||||
| 		public uint SamplerBindingCount; |  | ||||||
| 
 |  | ||||||
| 		public unsafe static GraphicsShaderInfo Create<T>( |  | ||||||
| 			ShaderModule shaderModule, |  | ||||||
| 			string entryPointName, |  | ||||||
| 			uint samplerBindingCount |  | ||||||
| 		) where T : unmanaged |  | ||||||
| 		{ |  | ||||||
| 			return new GraphicsShaderInfo |  | ||||||
| 			{ |  | ||||||
| 				ShaderModule = shaderModule, |  | ||||||
| 				EntryPointName = entryPointName, |  | ||||||
| 				UniformBufferSize = (uint) Marshal.SizeOf<T>(), |  | ||||||
| 				SamplerBindingCount = samplerBindingCount |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public static GraphicsShaderInfo Create( |  | ||||||
| 			ShaderModule shaderModule, |  | ||||||
| 			string entryPointName, |  | ||||||
| 			uint samplerBindingCount |  | ||||||
| 		) { |  | ||||||
| 			return new GraphicsShaderInfo |  | ||||||
| 			{ |  | ||||||
| 				ShaderModule = shaderModule, |  | ||||||
| 				EntryPointName = entryPointName, |  | ||||||
| 				UniformBufferSize = 0, |  | ||||||
| 				SamplerBindingCount = samplerBindingCount |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -1,4 +1,6 @@ | ||||||
| namespace MoonWorks.Graphics | using RefreshCS; | ||||||
|  | 
 | ||||||
|  | namespace MoonWorks.Graphics | ||||||
| { | { | ||||||
| 	/// <summary> | 	/// <summary> | ||||||
| 	/// Specifies how many samples should be used in rasterization. | 	/// Specifies how many samples should be used in rasterization. | ||||||
|  | @ -21,5 +23,14 @@ | ||||||
| 			MultisampleCount = sampleCount; | 			MultisampleCount = sampleCount; | ||||||
| 			SampleMask = sampleMask; | 			SampleMask = sampleMask; | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		public Refresh.MultisampleState ToRefresh() | ||||||
|  | 		{ | ||||||
|  | 			return new Refresh.MultisampleState | ||||||
|  | 			{ | ||||||
|  | 				MultisampleCount = (Refresh.SampleCount) MultisampleCount, | ||||||
|  | 				SampleMask = SampleMask | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,7 @@ | ||||||
| namespace MoonWorks.Graphics | using RefreshCS; | ||||||
| { | 
 | ||||||
|  | namespace MoonWorks.Graphics; | ||||||
|  | 
 | ||||||
| /// <summary> | /// <summary> | ||||||
| /// Specifies how the rasterizer should be configured for a graphics pipeline. | /// Specifies how the rasterizer should be configured for a graphics pipeline. | ||||||
| /// </summary> | /// </summary> | ||||||
|  | @ -103,5 +105,18 @@ | ||||||
| 		FillMode = FillMode.Line, | 		FillMode = FillMode.Line, | ||||||
| 		DepthBiasEnable = false | 		DepthBiasEnable = false | ||||||
| 	}; | 	}; | ||||||
|  | 
 | ||||||
|  | 	public Refresh.RasterizerState ToRefresh() | ||||||
|  | 	{ | ||||||
|  | 		return new Refresh.RasterizerState | ||||||
|  | 		{ | ||||||
|  | 			CullMode = (Refresh.CullMode) CullMode, | ||||||
|  | 			DepthBiasClamp = DepthBiasClamp, | ||||||
|  | 			DepthBiasConstantFactor = DepthBiasConstantFactor, | ||||||
|  | 			DepthBiasEnable = Conversions.BoolToInt(DepthBiasEnable), | ||||||
|  | 			DepthBiasSlopeFactor = DepthBiasSlopeFactor, | ||||||
|  | 			FillMode = (Refresh.FillMode) FillMode, | ||||||
|  | 			FrontFace = (Refresh.FrontFace) FrontFace | ||||||
|  | 		}; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| using RefreshCS; | using RefreshCS; | ||||||
| 
 | 
 | ||||||
| namespace MoonWorks.Graphics | namespace MoonWorks.Graphics; | ||||||
| { | 
 | ||||||
| /// <summary> | /// <summary> | ||||||
| /// All of the information that is used to create a sampler. | /// All of the information that is used to create a sampler. | ||||||
| /// </summary> | /// </summary> | ||||||
|  | @ -150,25 +150,24 @@ namespace MoonWorks.Graphics | ||||||
| 		MaxLod = 1000 | 		MaxLod = 1000 | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 		public Refresh.SamplerStateCreateInfo ToRefreshSamplerStateCreateInfo() | 	public Refresh.SamplerCreateInfo ToRefresh() | ||||||
| 	{ | 	{ | ||||||
| 			return new Refresh.SamplerStateCreateInfo | 		return new Refresh.SamplerCreateInfo | ||||||
| 		{ | 		{ | ||||||
| 				minFilter = (Refresh.Filter) MinFilter, | 			MinFilter = (Refresh.Filter) MinFilter, | ||||||
| 				magFilter = (Refresh.Filter) MagFilter, | 			MagFilter = (Refresh.Filter) MagFilter, | ||||||
| 				mipmapMode = (Refresh.SamplerMipmapMode) MipmapMode, | 			MipmapMode = (Refresh.SamplerMipmapMode) MipmapMode, | ||||||
| 				addressModeU = (Refresh.SamplerAddressMode) AddressModeU, | 			AddressModeU = (Refresh.SamplerAddressMode) AddressModeU, | ||||||
| 				addressModeV = (Refresh.SamplerAddressMode) AddressModeV, | 			AddressModeV = (Refresh.SamplerAddressMode) AddressModeV, | ||||||
| 				addressModeW = (Refresh.SamplerAddressMode) AddressModeW, | 			AddressModeW = (Refresh.SamplerAddressMode) AddressModeW, | ||||||
| 				mipLodBias = MipLodBias, | 			MipLodBias = MipLodBias, | ||||||
| 				anisotropyEnable = Conversions.BoolToByte(AnisotropyEnable), | 			AnisotropyEnable = Conversions.BoolToInt(AnisotropyEnable), | ||||||
| 				maxAnisotropy = MaxAnisotropy, | 			MaxAnisotropy = MaxAnisotropy, | ||||||
| 				compareEnable = Conversions.BoolToByte(CompareEnable), | 			CompareEnable = Conversions.BoolToInt(CompareEnable), | ||||||
| 				compareOp = (Refresh.CompareOp) CompareOp, | 			CompareOp = (Refresh.CompareOp) CompareOp, | ||||||
| 				minLod = MinLod, | 			MinLod = MinLod, | ||||||
| 				maxLod = MaxLod, | 			MaxLod = MaxLod, | ||||||
| 				borderColor = (Refresh.BorderColor) BorderColor | 			BorderColor = (Refresh.BorderColor) BorderColor | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -11,23 +11,25 @@ namespace MoonWorks.Graphics | ||||||
| 		public uint Height; | 		public uint Height; | ||||||
| 		public uint Depth; | 		public uint Depth; | ||||||
| 		public bool IsCube; | 		public bool IsCube; | ||||||
|  | 		public uint LayerCount; | ||||||
| 		public uint LevelCount; | 		public uint LevelCount; | ||||||
| 		public SampleCount SampleCount; | 		public SampleCount SampleCount; | ||||||
| 		public TextureFormat Format; | 		public TextureFormat Format; | ||||||
| 		public TextureUsageFlags UsageFlags; | 		public TextureUsageFlags UsageFlags; | ||||||
| 
 | 
 | ||||||
| 		public Refresh.TextureCreateInfo ToRefreshTextureCreateInfo() | 		public Refresh.TextureCreateInfo ToRefresh() | ||||||
| 		{ | 		{ | ||||||
| 			return new Refresh.TextureCreateInfo | 			return new Refresh.TextureCreateInfo | ||||||
| 			{ | 			{ | ||||||
| 				width = Width, | 				Width = Width, | ||||||
| 				height = Height, | 				Height = Height, | ||||||
| 				depth = Depth, | 				Depth = Depth, | ||||||
| 				isCube = Conversions.BoolToByte(IsCube), | 				IsCube = Conversions.BoolToInt(IsCube), | ||||||
| 				levelCount = LevelCount, | 				LayerCount = LayerCount, | ||||||
| 				sampleCount = (Refresh.SampleCount) SampleCount, | 				LevelCount = LevelCount, | ||||||
| 				format = (Refresh.TextureFormat) Format, | 				SampleCount = (Refresh.SampleCount) SampleCount, | ||||||
| 				usageFlags = (Refresh.TextureUsageFlags) UsageFlags | 				Format = (Refresh.TextureFormat) Format, | ||||||
|  | 				UsageFlags = (Refresh.TextureUsageFlags) UsageFlags | ||||||
| 			}; | 			}; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							|  | @ -5,5 +5,5 @@ layout(location = 0) out vec2 outTexCoord; | ||||||
| void main() | void main() | ||||||
| { | { | ||||||
| 	outTexCoord = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); | 	outTexCoord = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); | ||||||
| 	gl_Position = vec4(outTexCoord * 2.0 - 1.0, 0.0, 1.0); | 	gl_Position = vec4(outTexCoord * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 0.0, 1.0); | ||||||
| } | } | ||||||
|  | @ -1,13 +1,13 @@ | ||||||
| #version 450 | #version 450 | ||||||
| 
 | 
 | ||||||
| layout(set = 1, binding = 0) uniform sampler2D msdf; |  | ||||||
| 
 |  | ||||||
| layout(location = 0) in vec2 inTexCoord; | layout(location = 0) in vec2 inTexCoord; | ||||||
| layout(location = 1) in vec4 inColor; | layout(location = 1) in vec4 inColor; | ||||||
| 
 | 
 | ||||||
| layout(location = 0) out vec4 outColor; | layout(location = 0) out vec4 outColor; | ||||||
| 
 | 
 | ||||||
| layout(binding = 0, set = 3) uniform UBO | layout(set = 2, binding = 0) uniform sampler2D msdf; | ||||||
|  | 
 | ||||||
|  | layout(set = 3, binding = 0) uniform UBO | ||||||
| { | { | ||||||
| 	float pxRange; | 	float pxRange; | ||||||
| } ubo; | } ubo; | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ layout(location = 2) in vec4 inColor; | ||||||
| layout(location = 0) out vec2 outTexCoord; | layout(location = 0) out vec2 outTexCoord; | ||||||
| layout(location = 1) out vec4 outColor; | layout(location = 1) out vec4 outColor; | ||||||
| 
 | 
 | ||||||
| layout(binding = 0, set = 2) uniform UBO | layout(set = 1, binding = 0) uniform UBO | ||||||
| { | { | ||||||
| 	mat4 ViewProjection; | 	mat4 ViewProjection; | ||||||
| } ubo; | } ubo; | ||||||
|  |  | ||||||
|  | @ -9,9 +9,9 @@ layout(location = 0) in vec2 TexCoord; | ||||||
| 
 | 
 | ||||||
| layout(location = 0) out vec4 FragColor; | layout(location = 0) out vec4 FragColor; | ||||||
| 
 | 
 | ||||||
| layout(binding = 0, set = 1) uniform sampler2D YSampler; | layout(set = 2, binding = 0) uniform sampler2D YSampler; | ||||||
| layout(binding = 1, set = 1) uniform sampler2D USampler; | layout(set = 2, binding = 1) uniform sampler2D USampler; | ||||||
| layout(binding = 2, set = 1) uniform sampler2D VSampler; | layout(set = 2, binding = 2) uniform sampler2D VSampler; | ||||||
| 
 | 
 | ||||||
| /* More info about colorspace conversion: | /* More info about colorspace conversion: | ||||||
|  * http://www.equasys.de/colorconversion.html |  * http://www.equasys.de/colorconversion.html | ||||||
|  |  | ||||||
|  | @ -0,0 +1,46 @@ | ||||||
|  | using RefreshCS; | ||||||
|  | 
 | ||||||
|  | namespace MoonWorks.Graphics | ||||||
|  | { | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// A texture region specifies a subregion of a texture. | ||||||
|  | 	/// These are used by copy commands. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public struct TextureRegion | ||||||
|  | 	{ | ||||||
|  | 		public TextureSlice TextureSlice; | ||||||
|  | 		public uint X; | ||||||
|  | 		public uint Y; | ||||||
|  | 		public uint Z; | ||||||
|  | 		public uint Width; | ||||||
|  | 		public uint Height; | ||||||
|  | 		public uint Depth; | ||||||
|  | 
 | ||||||
|  | 		public uint Size => (Width * Height * Depth * Texture.BytesPerPixel(TextureSlice.Texture.Format) / Texture.BlockSizeSquared(TextureSlice.Texture.Format)) >> (int) TextureSlice.MipLevel; | ||||||
|  | 
 | ||||||
|  | 		public TextureRegion(Texture texture) | ||||||
|  | 		{ | ||||||
|  | 			TextureSlice = new TextureSlice(texture); | ||||||
|  | 			X = 0; | ||||||
|  | 			Y = 0; | ||||||
|  | 			Z = 0; | ||||||
|  | 			Width = texture.Width; | ||||||
|  | 			Height = texture.Height; | ||||||
|  | 			Depth = texture.Depth; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public Refresh.TextureRegion ToRefresh() | ||||||
|  | 		{ | ||||||
|  | 			return new Refresh.TextureRegion | ||||||
|  | 			{ | ||||||
|  | 				TextureSlice = TextureSlice.ToRefresh(), | ||||||
|  | 				X = X, | ||||||
|  | 				Y = Y, | ||||||
|  | 				Z = Z, | ||||||
|  | 				W = Width, | ||||||
|  | 				H = Height, | ||||||
|  | 				D = Depth | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -1,57 +1,31 @@ | ||||||
| using RefreshCS; | using RefreshCS; | ||||||
|  | namespace MoonWorks.Graphics; | ||||||
| 
 | 
 | ||||||
| namespace MoonWorks.Graphics |  | ||||||
| { |  | ||||||
| /// <summary> | /// <summary> | ||||||
| 	/// A texture slice specifies a subregion of a texture. | /// A texture slice specifies a subresource of a texture. | ||||||
| 	/// Many operations can use texture slices in place of textures for the sake of convenience. |  | ||||||
| /// </summary> | /// </summary> | ||||||
| public struct TextureSlice | public struct TextureSlice | ||||||
| { | { | ||||||
| 		public Texture Texture { get; } | 	public Texture Texture; | ||||||
| 		public Rect Rectangle { get; } | 	public uint MipLevel; | ||||||
| 		public uint Depth { get; } | 	public uint Layer; | ||||||
| 		public uint Layer { get; } |  | ||||||
| 		public uint Level { get; } |  | ||||||
| 
 | 
 | ||||||
| 		public uint Size => (uint) (Rectangle.W * Rectangle.H * Texture.BytesPerPixel(Texture.Format) / Texture.BlockSizeSquared(Texture.Format)); | 	public uint Size => (Texture.Width * Texture.Height * Texture.Depth * Texture.BytesPerPixel(Texture.Format) / Texture.BlockSizeSquared(Texture.Format)) >> (int) MipLevel; | ||||||
| 
 | 
 | ||||||
| 	public TextureSlice(Texture texture) | 	public TextureSlice(Texture texture) | ||||||
| 	{ | 	{ | ||||||
| 		Texture = texture; | 		Texture = texture; | ||||||
| 			Rectangle = new Rect | 		MipLevel = 0; | ||||||
| 			{ |  | ||||||
| 				X = 0, |  | ||||||
| 				Y = 0, |  | ||||||
| 				W = (int) texture.Width, |  | ||||||
| 				H = (int) texture.Height |  | ||||||
| 			}; |  | ||||||
| 			Depth = 0; |  | ||||||
| 		Layer = 0; | 		Layer = 0; | ||||||
| 			Level = 0; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 		public TextureSlice(Texture texture, Rect rectangle, uint depth = 0, uint layer = 0, uint level = 0) | 	public Refresh.TextureSlice ToRefresh() | ||||||
| 	{ | 	{ | ||||||
| 			Texture = texture; | 		return new Refresh.TextureSlice | ||||||
| 			Rectangle = rectangle; |  | ||||||
| 			Depth = depth; |  | ||||||
| 			Layer = layer; |  | ||||||
| 			Level = level; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		public Refresh.TextureSlice ToRefreshTextureSlice() |  | ||||||
| 		{ | 		{ | ||||||
| 			Refresh.TextureSlice textureSlice = new Refresh.TextureSlice | 			Texture = Texture.Handle, | ||||||
| 			{ | 			MipLevel = MipLevel, | ||||||
| 				texture = Texture.Handle, | 			Layer = Layer | ||||||
| 				rectangle = Rectangle.ToRefresh(), |  | ||||||
| 				depth = Depth, |  | ||||||
| 				layer = Layer, |  | ||||||
| 				level = Level |  | ||||||
| 		}; | 		}; | ||||||
| 
 |  | ||||||
| 			return textureSlice; |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,4 @@ | ||||||
| using System; | using System; | ||||||
| using RefreshCS; |  | ||||||
| 
 | 
 | ||||||
| namespace MoonWorks | namespace MoonWorks | ||||||
| { | { | ||||||
|  | @ -9,19 +8,6 @@ namespace MoonWorks | ||||||
| 		public static Action<string> LogWarn = LogWarnDefault; | 		public static Action<string> LogWarn = LogWarnDefault; | ||||||
| 		public static Action<string> LogError = LogErrorDefault; | 		public static Action<string> LogError = LogErrorDefault; | ||||||
| 
 | 
 | ||||||
| 		private static RefreshCS.Refresh.Refresh_LogFunc LogInfoFunc = RefreshLogInfo; |  | ||||||
| 		private static RefreshCS.Refresh.Refresh_LogFunc LogWarnFunc = RefreshLogWarn; |  | ||||||
| 		private static RefreshCS.Refresh.Refresh_LogFunc LogErrorFunc = RefreshLogError; |  | ||||||
| 
 |  | ||||||
| 		internal static void Initialize() |  | ||||||
| 		{ |  | ||||||
| 			Refresh.Refresh_HookLogFunctions( |  | ||||||
| 				LogInfoFunc, |  | ||||||
| 				LogWarnFunc, |  | ||||||
| 				LogErrorFunc |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		private static void LogInfoDefault(string str) | 		private static void LogInfoDefault(string str) | ||||||
| 		{ | 		{ | ||||||
| 			Console.ForegroundColor = ConsoleColor.Green; | 			Console.ForegroundColor = ConsoleColor.Green; | ||||||
|  |  | ||||||
|  | @ -1054,7 +1054,7 @@ namespace MoonWorks.Math.Float | ||||||
| 			result.M13 = 0.0f; | 			result.M13 = 0.0f; | ||||||
| 			result.M14 = 0.0f; | 			result.M14 = 0.0f; | ||||||
| 			result.M21 = 0.0f; | 			result.M21 = 0.0f; | ||||||
| 			result.M22 = (float) (2.0 / ((double) bottom - (double) top)); | 			result.M22 = (float) (2.0 / ((double) top - (double) bottom)); | ||||||
| 			result.M23 = 0.0f; | 			result.M23 = 0.0f; | ||||||
| 			result.M24 = 0.0f; | 			result.M24 = 0.0f; | ||||||
| 			result.M31 = 0.0f; | 			result.M31 = 0.0f; | ||||||
|  | @ -1062,11 +1062,11 @@ namespace MoonWorks.Math.Float | ||||||
| 			result.M33 = (float) (1.0 / ((double) zNearPlane - (double) zFarPlane)); | 			result.M33 = (float) (1.0 / ((double) zNearPlane - (double) zFarPlane)); | ||||||
| 			result.M34 = 0.0f; | 			result.M34 = 0.0f; | ||||||
| 			result.M41 = (float) ( | 			result.M41 = (float) ( | ||||||
| 				-((double) left + (double) right) / | 				((double) left + (double) right) / | ||||||
| 				((double) right - (double) left) | 				((double) left - (double) right) | ||||||
| 			); | 			); | ||||||
| 			result.M42 = (float) ( | 			result.M42 = (float) ( | ||||||
| 				-((double) top + (double) bottom) / | 				((double) top + (double) bottom) / | ||||||
| 				((double) bottom - (double) top) | 				((double) bottom - (double) top) | ||||||
| 			); | 			); | ||||||
| 			result.M43 = (float) ( | 			result.M43 = (float) ( | ||||||
|  | @ -1126,7 +1126,7 @@ namespace MoonWorks.Math.Float | ||||||
| 			} | 			} | ||||||
| 			result.M11 = (2f * nearPlaneDistance) / width; | 			result.M11 = (2f * nearPlaneDistance) / width; | ||||||
| 			result.M12 = result.M13 = result.M14 = 0f; | 			result.M12 = result.M13 = result.M14 = 0f; | ||||||
| 			result.M22 = -(2f * nearPlaneDistance) / height; | 			result.M22 = (2f * nearPlaneDistance) / height; | ||||||
| 			result.M21 = result.M23 = result.M24 = 0f; | 			result.M21 = result.M23 = result.M24 = 0f; | ||||||
| 			result.M33 = farPlaneDistance / (nearPlaneDistance - farPlaneDistance); | 			result.M33 = farPlaneDistance / (nearPlaneDistance - farPlaneDistance); | ||||||
| 			result.M31 = result.M32 = 0f; | 			result.M31 = result.M32 = 0f; | ||||||
|  | @ -1199,7 +1199,7 @@ namespace MoonWorks.Math.Float | ||||||
| 			float num = 1f / ((float) System.Math.Tan((double) (fieldOfView * 0.5f))); | 			float num = 1f / ((float) System.Math.Tan((double) (fieldOfView * 0.5f))); | ||||||
| 			result.M11 = num / aspectRatio; | 			result.M11 = num / aspectRatio; | ||||||
| 			result.M12 = result.M13 = result.M14 = 0; | 			result.M12 = result.M13 = result.M14 = 0; | ||||||
| 			result.M22 = -num; | 			result.M22 = num; | ||||||
| 			result.M21 = result.M23 = result.M24 = 0; | 			result.M21 = result.M23 = result.M24 = 0; | ||||||
| 			result.M31 = result.M32 = 0f; | 			result.M31 = result.M32 = 0f; | ||||||
| 			result.M33 = farPlaneDistance / (nearPlaneDistance - farPlaneDistance); | 			result.M33 = farPlaneDistance / (nearPlaneDistance - farPlaneDistance); | ||||||
|  | @ -1277,7 +1277,7 @@ namespace MoonWorks.Math.Float | ||||||
| 			} | 			} | ||||||
| 			result.M11 = (2f * nearPlaneDistance) / (right - left); | 			result.M11 = (2f * nearPlaneDistance) / (right - left); | ||||||
| 			result.M12 = result.M13 = result.M14 = 0; | 			result.M12 = result.M13 = result.M14 = 0; | ||||||
| 			result.M22 = -(2f * nearPlaneDistance) / (top - bottom); | 			result.M22 = (2f * nearPlaneDistance) / (top - bottom); | ||||||
| 			result.M21 = result.M23 = result.M24 = 0; | 			result.M21 = result.M23 = result.M24 = 0; | ||||||
| 			result.M31 = (left + right) / (right - left); | 			result.M31 = (left + right) / (right - left); | ||||||
| 			result.M32 = (top + bottom) / (top - bottom); | 			result.M32 = (top + bottom) / (top - bottom); | ||||||
|  |  | ||||||
|  | @ -11,10 +11,6 @@ namespace MoonWorks.Video | ||||||
| 	{ | 	{ | ||||||
| 		public string Filename { get; } | 		public string Filename { get; } | ||||||
| 
 | 
 | ||||||
| 		// "double buffering" so we can loop without a stutter |  | ||||||
| 		internal VideoAV1Stream StreamA { get; } |  | ||||||
| 		internal VideoAV1Stream StreamB { get; } |  | ||||||
| 
 |  | ||||||
| 		public int Width => width; | 		public int Width => width; | ||||||
| 		public int Height => height; | 		public int Height => height; | ||||||
| 		public double FramesPerSecond { get; set; } | 		public double FramesPerSecond { get; set; } | ||||||
|  | @ -67,23 +63,6 @@ namespace MoonWorks.Video | ||||||
| 			FramesPerSecond = framesPerSecond; | 			FramesPerSecond = framesPerSecond; | ||||||
| 
 | 
 | ||||||
| 			Filename = filename; | 			Filename = filename; | ||||||
| 
 |  | ||||||
| 			StreamA = new VideoAV1Stream(device, this); |  | ||||||
| 			StreamB = new VideoAV1Stream(device, this); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// NOTE: if you call this while a VideoPlayer is playing the stream, your program will explode |  | ||||||
| 		protected override void Dispose(bool disposing) |  | ||||||
| 		{ |  | ||||||
| 			if (!IsDisposed) |  | ||||||
| 			{ |  | ||||||
| 				if (disposing) |  | ||||||
| 				{ |  | ||||||
| 					StreamA.Dispose(); |  | ||||||
| 					StreamB.Dispose(); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			base.Dispose(disposing); |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,13 +1,17 @@ | ||||||
| using System; | using System; | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | using System.Threading; | ||||||
| using MoonWorks.Graphics; | using MoonWorks.Graphics; | ||||||
| 
 | 
 | ||||||
| namespace MoonWorks.Video | namespace MoonWorks.Video | ||||||
| { | { | ||||||
|  | 	// Note that all public methods are async. | ||||||
| 	internal class VideoAV1Stream : GraphicsResource | 	internal class VideoAV1Stream : GraphicsResource | ||||||
| 	{ | 	{ | ||||||
| 		public IntPtr Handle => handle; | 		public IntPtr Handle => handle; | ||||||
| 		IntPtr handle; | 		IntPtr handle; | ||||||
| 
 | 
 | ||||||
|  | 		public bool Loaded => handle != IntPtr.Zero; | ||||||
| 		public bool Ended => Dav1dfile.df_eos(Handle) == 1; | 		public bool Ended => Dav1dfile.df_eos(Handle) == 1; | ||||||
| 
 | 
 | ||||||
| 		public IntPtr yDataHandle; | 		public IntPtr yDataHandle; | ||||||
|  | @ -20,33 +24,89 @@ namespace MoonWorks.Video | ||||||
| 
 | 
 | ||||||
| 		public bool FrameDataUpdated { get; set; } | 		public bool FrameDataUpdated { get; set; } | ||||||
| 
 | 
 | ||||||
| 		public VideoAV1Stream(GraphicsDevice device, VideoAV1 video) : base(device) | 		private BlockingCollection<Action> Actions = new BlockingCollection<Action>(); | ||||||
|  | 
 | ||||||
|  | 		private bool Running = false; | ||||||
|  | 
 | ||||||
|  | 		Thread Thread; | ||||||
|  | 
 | ||||||
|  | 		public VideoAV1Stream(GraphicsDevice device) : base(device) | ||||||
| 		{ | 		{ | ||||||
| 			if (Dav1dfile.df_fopen(video.Filename, out handle) == 0) | 			handle = IntPtr.Zero; | ||||||
| 			{ | 
 | ||||||
| 				throw new Exception("Failed to open video file!"); | 			Running = true; | ||||||
|  | 
 | ||||||
|  | 			Thread = new Thread(ThreadMain); | ||||||
|  | 			Thread.Start(); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 			Reset(); | 		private void ThreadMain() | ||||||
|  | 		{ | ||||||
|  | 			while (Running) | ||||||
|  | 			{ | ||||||
|  | 				// block until we can take an action, then run it | ||||||
|  | 				var action = Actions.Take(); | ||||||
|  | 				action.Invoke(); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// shutting down... | ||||||
|  | 			while (Actions.TryTake(out var action)) | ||||||
|  | 			{ | ||||||
|  | 				action.Invoke(); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public void Load(string filename) | ||||||
|  | 		{ | ||||||
|  | 			Actions.Add(() => LoadHelper(filename)); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public void Reset() | 		public void Reset() | ||||||
| 		{ | 		{ | ||||||
| 			lock (this) | 			Actions.Add(ResetHelper); | ||||||
| 			{ |  | ||||||
| 				Dav1dfile.df_reset(Handle); |  | ||||||
| 				ReadNextFrame(); |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public void ReadNextFrame() | 		public void ReadNextFrame() | ||||||
|  | 		{ | ||||||
|  | 			Actions.Add(ReadNextFrameHelper); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public void Unload() | ||||||
|  | 		{ | ||||||
|  | 			Actions.Add(UnloadHelper); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private void LoadHelper(string filename) | ||||||
|  | 		{ | ||||||
|  | 			if (!Loaded) | ||||||
|  | 			{ | ||||||
|  | 				if (Dav1dfile.df_fopen(filename, out handle) == 0) | ||||||
|  | 				{ | ||||||
|  | 					Logger.LogError("Failed to load video file: " + filename); | ||||||
|  | 					throw new Exception("Failed to load video file!"); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				Reset(); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private void ResetHelper() | ||||||
|  | 		{ | ||||||
|  | 			if (Loaded) | ||||||
|  | 			{ | ||||||
|  | 				Dav1dfile.df_reset(handle); | ||||||
|  | 				ReadNextFrame(); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private void ReadNextFrameHelper() | ||||||
|  | 		{ | ||||||
|  | 			if (Loaded && !Ended) | ||||||
| 			{ | 			{ | ||||||
| 				lock (this) | 				lock (this) | ||||||
| 			{ |  | ||||||
| 				if (!Ended) |  | ||||||
| 				{ | 				{ | ||||||
| 					if (Dav1dfile.df_readvideo( | 					if (Dav1dfile.df_readvideo( | ||||||
| 						Handle, | 						handle, | ||||||
| 						1, | 						1, | ||||||
| 						out var yDataHandle, | 						out var yDataHandle, | ||||||
| 						out var uDataHandle, | 						out var uDataHandle, | ||||||
|  | @ -70,11 +130,26 @@ namespace MoonWorks.Video | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		private void UnloadHelper() | ||||||
|  | 		{ | ||||||
|  | 			if (Loaded) | ||||||
|  | 			{ | ||||||
|  | 				Dav1dfile.df_close(handle); | ||||||
|  | 				handle = IntPtr.Zero; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		protected override void Dispose(bool disposing) | 		protected override void Dispose(bool disposing) | ||||||
| 		{ | 		{ | ||||||
| 			if (!IsDisposed) | 			if (!IsDisposed) | ||||||
| 			{ | 			{ | ||||||
| 				Dav1dfile.df_close(Handle); | 				Unload(); | ||||||
|  | 				Running = false; | ||||||
|  | 
 | ||||||
|  | 				if (disposing) | ||||||
|  | 				{ | ||||||
|  | 					Thread.Join(); | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 			base.Dispose(disposing); | 			base.Dispose(disposing); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -16,18 +16,15 @@ namespace MoonWorks.Video | ||||||
| 		public float PlaybackSpeed { get; set; } = 1; | 		public float PlaybackSpeed { get; set; } = 1; | ||||||
| 
 | 
 | ||||||
| 		private VideoAV1 Video = null; | 		private VideoAV1 Video = null; | ||||||
| 		private VideoAV1Stream CurrentStream = null; | 		private VideoAV1Stream Stream { get; } | ||||||
| 
 | 
 | ||||||
| 		private Task ReadNextFrameTask; |  | ||||||
| 		private Task ResetStreamATask; |  | ||||||
| 		private Task ResetStreamBTask; |  | ||||||
| 
 |  | ||||||
| 		private GraphicsDevice GraphicsDevice; |  | ||||||
| 		private Texture yTexture = null; | 		private Texture yTexture = null; | ||||||
| 		private Texture uTexture = null; | 		private Texture uTexture = null; | ||||||
| 		private Texture vTexture = null; | 		private Texture vTexture = null; | ||||||
| 		private Sampler LinearSampler; | 		private Sampler LinearSampler; | ||||||
| 
 | 
 | ||||||
|  | 		private TransferBuffer TransferBuffer; | ||||||
|  | 
 | ||||||
| 		private int currentFrame; | 		private int currentFrame; | ||||||
| 
 | 
 | ||||||
| 		private Stopwatch timer; | 		private Stopwatch timer; | ||||||
|  | @ -36,7 +33,7 @@ namespace MoonWorks.Video | ||||||
| 
 | 
 | ||||||
| 		public VideoPlayer(GraphicsDevice device) : base(device) | 		public VideoPlayer(GraphicsDevice device) : base(device) | ||||||
| 		{ | 		{ | ||||||
| 			GraphicsDevice = device; | 			Stream = new VideoAV1Stream(device); | ||||||
| 
 | 
 | ||||||
| 			LinearSampler = new Sampler(device, SamplerCreateInfo.LinearClamp); | 			LinearSampler = new Sampler(device, SamplerCreateInfo.LinearClamp); | ||||||
| 
 | 
 | ||||||
|  | @ -51,50 +48,50 @@ namespace MoonWorks.Video | ||||||
| 		{ | 		{ | ||||||
| 			if (Video != video) | 			if (Video != video) | ||||||
| 			{ | 			{ | ||||||
| 				Stop(); | 				Unload(); | ||||||
| 
 | 
 | ||||||
| 				if (RenderTexture == null) | 				if (RenderTexture == null) | ||||||
| 				{ | 				{ | ||||||
| 					RenderTexture = CreateRenderTexture(GraphicsDevice, video.Width, video.Height); | 					RenderTexture = CreateRenderTexture(Device, video.Width, video.Height); | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				if (yTexture == null) | 				if (yTexture == null) | ||||||
| 				{ | 				{ | ||||||
| 					yTexture = CreateSubTexture(GraphicsDevice, video.Width, video.Height); | 					yTexture = CreateSubTexture(Device, video.Width, video.Height); | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				if (uTexture == null) | 				if (uTexture == null) | ||||||
| 				{ | 				{ | ||||||
| 					uTexture = CreateSubTexture(GraphicsDevice, video.UVWidth, video.UVHeight); | 					uTexture = CreateSubTexture(Device, video.UVWidth, video.UVHeight); | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				if (vTexture == null) | 				if (vTexture == null) | ||||||
| 				{ | 				{ | ||||||
| 					vTexture = CreateSubTexture(GraphicsDevice, video.UVWidth, video.UVHeight); | 					vTexture = CreateSubTexture(Device, video.UVWidth, video.UVHeight); | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				if (video.Width != RenderTexture.Width || video.Height != RenderTexture.Height) | 				if (video.Width != RenderTexture.Width || video.Height != RenderTexture.Height) | ||||||
| 				{ | 				{ | ||||||
| 					RenderTexture.Dispose(); | 					RenderTexture.Dispose(); | ||||||
| 					RenderTexture = CreateRenderTexture(GraphicsDevice, video.Width, video.Height); | 					RenderTexture = CreateRenderTexture(Device, video.Width, video.Height); | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				if (video.Width != yTexture.Width || video.Height != yTexture.Height) | 				if (video.Width != yTexture.Width || video.Height != yTexture.Height) | ||||||
| 				{ | 				{ | ||||||
| 					yTexture.Dispose(); | 					yTexture.Dispose(); | ||||||
| 					yTexture = CreateSubTexture(GraphicsDevice, video.Width, video.Height); | 					yTexture = CreateSubTexture(Device, video.Width, video.Height); | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				if (video.UVWidth != uTexture.Width || video.UVHeight != uTexture.Height) | 				if (video.UVWidth != uTexture.Width || video.UVHeight != uTexture.Height) | ||||||
| 				{ | 				{ | ||||||
| 					uTexture.Dispose(); | 					uTexture.Dispose(); | ||||||
| 					uTexture = CreateSubTexture(GraphicsDevice, video.UVWidth, video.UVHeight); | 					uTexture = CreateSubTexture(Device, video.UVWidth, video.UVHeight); | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				if (video.UVWidth != vTexture.Width || video.UVHeight != vTexture.Height) | 				if (video.UVWidth != vTexture.Width || video.UVHeight != vTexture.Height) | ||||||
| 				{ | 				{ | ||||||
| 					vTexture.Dispose(); | 					vTexture.Dispose(); | ||||||
| 					vTexture = CreateSubTexture(GraphicsDevice, video.UVWidth, video.UVHeight); | 					vTexture = CreateSubTexture(Device, video.UVWidth, video.UVHeight); | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				Video = video; | 				Video = video; | ||||||
|  | @ -155,7 +152,7 @@ namespace MoonWorks.Video | ||||||
| 			lastTimestamp = 0; | 			lastTimestamp = 0; | ||||||
| 			timeElapsed = 0; | 			timeElapsed = 0; | ||||||
| 
 | 
 | ||||||
| 			InitializeDav1dStream(); | 			ResetDav1dStreams(); | ||||||
| 
 | 
 | ||||||
| 			State = VideoState.Stopped; | 			State = VideoState.Stopped; | ||||||
| 		} | 		} | ||||||
|  | @ -165,9 +162,21 @@ namespace MoonWorks.Video | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		public void Unload() | 		public void Unload() | ||||||
| 		{ | 		{ | ||||||
| 			Stop(); | 			if (Video == null) | ||||||
| 			ResetStreamATask?.Wait(); | 			{ | ||||||
| 			ResetStreamBTask?.Wait(); | 				return; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			timer.Stop(); | ||||||
|  | 			timer.Reset(); | ||||||
|  | 
 | ||||||
|  | 			lastTimestamp = 0; | ||||||
|  | 			timeElapsed = 0; | ||||||
|  | 
 | ||||||
|  | 			State = VideoState.Stopped; | ||||||
|  | 
 | ||||||
|  | 			Stream.Unload(); | ||||||
|  | 
 | ||||||
| 			Video = null; | 			Video = null; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -187,38 +196,26 @@ namespace MoonWorks.Video | ||||||
| 			int thisFrame = ((int) (timeElapsed / (1000.0 / Video.FramesPerSecond))); | 			int thisFrame = ((int) (timeElapsed / (1000.0 / Video.FramesPerSecond))); | ||||||
| 			if (thisFrame > currentFrame) | 			if (thisFrame > currentFrame) | ||||||
| 			{ | 			{ | ||||||
| 				if (CurrentStream.FrameDataUpdated) | 				if (Stream.FrameDataUpdated) | ||||||
| 				{ | 				{ | ||||||
| 					UpdateRenderTexture(); | 					UpdateRenderTexture(); | ||||||
| 					CurrentStream.FrameDataUpdated = false; | 					Stream.FrameDataUpdated = false; | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				currentFrame = thisFrame; | 				currentFrame = thisFrame; | ||||||
| 				ReadNextFrameTask = Task.Run(CurrentStream.ReadNextFrame); | 				Stream.ReadNextFrame(); | ||||||
| 				ReadNextFrameTask.ContinueWith(HandleTaskException, TaskContinuationOptions.OnlyOnFaulted); |  | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (CurrentStream.Ended) | 			if (Stream.Ended) | ||||||
| 			{ | 			{ | ||||||
| 				timer.Stop(); | 				timer.Stop(); | ||||||
| 				timer.Reset(); | 				timer.Reset(); | ||||||
| 
 | 
 | ||||||
| 				var task = Task.Run(CurrentStream.Reset); | 				Stream.Reset(); | ||||||
| 				task.ContinueWith(HandleTaskException, TaskContinuationOptions.OnlyOnFaulted); |  | ||||||
| 
 |  | ||||||
| 				if (CurrentStream == Video.StreamA) |  | ||||||
| 				{ |  | ||||||
| 					ResetStreamATask = task; |  | ||||||
| 				} |  | ||||||
| 				else |  | ||||||
| 				{ |  | ||||||
| 					ResetStreamBTask = task; |  | ||||||
| 				} |  | ||||||
| 
 | 
 | ||||||
| 				if (Loop) | 				if (Loop) | ||||||
| 				{ | 				{ | ||||||
| 					// Start over on the next stream! | 					// Start over! | ||||||
| 					CurrentStream = (CurrentStream == Video.StreamA) ? Video.StreamB : Video.StreamA; |  | ||||||
| 					currentFrame = -1; | 					currentFrame = -1; | ||||||
| 					timer.Start(); | 					timer.Start(); | ||||||
| 				} | 				} | ||||||
|  | @ -231,40 +228,87 @@ namespace MoonWorks.Video | ||||||
| 
 | 
 | ||||||
| 		private void UpdateRenderTexture() | 		private void UpdateRenderTexture() | ||||||
| 		{ | 		{ | ||||||
| 			lock (CurrentStream) | 			uint uOffset; | ||||||
|  | 			uint vOffset; | ||||||
|  | 			uint yStride; | ||||||
|  | 			uint uvStride; | ||||||
|  | 
 | ||||||
|  | 			lock (Stream) | ||||||
| 			{ | 			{ | ||||||
| 				var commandBuffer = GraphicsDevice.AcquireCommandBuffer(); | 				var ySpan = new Span<byte>((void*) Stream.yDataHandle, (int) Stream.yDataLength); | ||||||
|  | 				var uSpan = new Span<byte>((void*) Stream.uDataHandle, (int) Stream.uvDataLength); | ||||||
|  | 				var vSpan = new Span<byte>((void*) Stream.vDataHandle, (int) Stream.uvDataLength); | ||||||
| 
 | 
 | ||||||
| 				commandBuffer.SetTextureDataYUV( | 				if (TransferBuffer == null || TransferBuffer.Size < ySpan.Length + uSpan.Length + vSpan.Length) | ||||||
| 					yTexture, | 				{ | ||||||
| 					uTexture, | 					TransferBuffer?.Dispose(); | ||||||
| 					vTexture, | 					TransferBuffer = new TransferBuffer(Device, TransferUsage.Texture, TransferBufferMapFlags.Write, (uint) (ySpan.Length + uSpan.Length + vSpan.Length)); | ||||||
| 					CurrentStream.yDataHandle, |  | ||||||
| 					CurrentStream.uDataHandle, |  | ||||||
| 					CurrentStream.vDataHandle, |  | ||||||
| 					CurrentStream.yDataLength, |  | ||||||
| 					CurrentStream.uvDataLength, |  | ||||||
| 					CurrentStream.yStride, |  | ||||||
| 					CurrentStream.uvStride |  | ||||||
| 				); |  | ||||||
| 
 |  | ||||||
| 				commandBuffer.BeginRenderPass( |  | ||||||
| 					new ColorAttachmentInfo(RenderTexture, Color.Black) |  | ||||||
| 				); |  | ||||||
| 
 |  | ||||||
| 				commandBuffer.BindGraphicsPipeline(GraphicsDevice.VideoPipeline); |  | ||||||
| 				commandBuffer.BindFragmentSamplers( |  | ||||||
| 					new TextureSamplerBinding(yTexture, LinearSampler), |  | ||||||
| 					new TextureSamplerBinding(uTexture, LinearSampler), |  | ||||||
| 					new TextureSamplerBinding(vTexture, LinearSampler) |  | ||||||
| 				); |  | ||||||
| 
 |  | ||||||
| 				commandBuffer.DrawPrimitives(0, 1, 0, 0); |  | ||||||
| 
 |  | ||||||
| 				commandBuffer.EndRenderPass(); |  | ||||||
| 
 |  | ||||||
| 				GraphicsDevice.Submit(commandBuffer); |  | ||||||
| 				} | 				} | ||||||
|  | 				TransferBuffer.SetData(ySpan, 0, true); | ||||||
|  | 				TransferBuffer.SetData(uSpan, (uint) ySpan.Length, false); | ||||||
|  | 				TransferBuffer.SetData(vSpan, (uint) (ySpan.Length + uSpan.Length), false); | ||||||
|  | 
 | ||||||
|  | 				uOffset = (uint) ySpan.Length; | ||||||
|  | 				vOffset = (uint) (ySpan.Length + vSpan.Length); | ||||||
|  | 
 | ||||||
|  | 				yStride = Stream.yStride; | ||||||
|  | 				uvStride = Stream.uvStride; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			var commandBuffer = Device.AcquireCommandBuffer(); | ||||||
|  | 
 | ||||||
|  | 			var copyPass = commandBuffer.BeginCopyPass(); | ||||||
|  | 
 | ||||||
|  | 			copyPass.UploadToTexture( | ||||||
|  | 				TransferBuffer, | ||||||
|  | 				yTexture, | ||||||
|  | 				new BufferImageCopy | ||||||
|  | 				{ | ||||||
|  | 					BufferOffset = 0, | ||||||
|  | 					BufferStride = yStride, | ||||||
|  | 					BufferImageHeight = yTexture.Height | ||||||
|  | 				}, | ||||||
|  | 				true | ||||||
|  | 			); | ||||||
|  | 
 | ||||||
|  | 			copyPass.UploadToTexture( | ||||||
|  | 				TransferBuffer, | ||||||
|  | 				uTexture, | ||||||
|  | 				new BufferImageCopy{ | ||||||
|  | 					BufferOffset = uOffset, | ||||||
|  | 					BufferStride = uvStride, | ||||||
|  | 					BufferImageHeight = uTexture.Height | ||||||
|  | 				}, | ||||||
|  | 				true | ||||||
|  | 			); | ||||||
|  | 
 | ||||||
|  | 			copyPass.UploadToTexture( | ||||||
|  | 				TransferBuffer, | ||||||
|  | 				vTexture, | ||||||
|  | 				new BufferImageCopy | ||||||
|  | 				{ | ||||||
|  | 					BufferOffset = vOffset, | ||||||
|  | 					BufferStride = uvStride, | ||||||
|  | 					BufferImageHeight = vTexture.Height | ||||||
|  | 				}, | ||||||
|  | 				true | ||||||
|  | 			); | ||||||
|  | 
 | ||||||
|  | 			commandBuffer.EndCopyPass(copyPass); | ||||||
|  | 
 | ||||||
|  | 			var renderPass = commandBuffer.BeginRenderPass( | ||||||
|  | 				new ColorAttachmentInfo(RenderTexture, true, Color.Black) | ||||||
|  | 			); | ||||||
|  | 
 | ||||||
|  | 			renderPass.BindGraphicsPipeline(Device.VideoPipeline); | ||||||
|  | 			renderPass.BindFragmentSampler(new TextureSamplerBinding(yTexture, LinearSampler), 0); | ||||||
|  | 			renderPass.BindFragmentSampler(new TextureSamplerBinding(uTexture, LinearSampler), 1); | ||||||
|  | 			renderPass.BindFragmentSampler(new TextureSamplerBinding(vTexture, LinearSampler), 2); | ||||||
|  | 			renderPass.DrawPrimitives(0, 1); | ||||||
|  | 
 | ||||||
|  | 			commandBuffer.EndRenderPass(renderPass); | ||||||
|  | 
 | ||||||
|  | 			Device.Submit(commandBuffer); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		private static Texture CreateRenderTexture(GraphicsDevice graphicsDevice, int width, int height) | 		private static Texture CreateRenderTexture(GraphicsDevice graphicsDevice, int width, int height) | ||||||
|  | @ -291,23 +335,14 @@ namespace MoonWorks.Video | ||||||
| 
 | 
 | ||||||
| 		private void InitializeDav1dStream() | 		private void InitializeDav1dStream() | ||||||
| 		{ | 		{ | ||||||
| 			ReadNextFrameTask?.Wait(); | 			Stream.Load(Video.Filename); | ||||||
| 
 |  | ||||||
| 			ResetStreamATask = Task.Run(Video.StreamA.Reset); |  | ||||||
| 			ResetStreamATask.ContinueWith(HandleTaskException, TaskContinuationOptions.OnlyOnFaulted); |  | ||||||
| 			ResetStreamBTask = Task.Run(Video.StreamB.Reset); |  | ||||||
| 			ResetStreamBTask.ContinueWith(HandleTaskException, TaskContinuationOptions.OnlyOnFaulted); |  | ||||||
| 
 |  | ||||||
| 			CurrentStream = Video.StreamA; |  | ||||||
| 			currentFrame = -1; | 			currentFrame = -1; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		private static void HandleTaskException(Task task) | 		private void ResetDav1dStreams() | ||||||
| 		{ | 		{ | ||||||
| 			if (task.Exception.InnerException is not TaskCanceledException) | 			Stream.Reset(); | ||||||
| 			{ | 			currentFrame = -1; | ||||||
| 				throw task.Exception; |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		protected override void Dispose(bool disposing) | 		protected override void Dispose(bool disposing) | ||||||
|  |  | ||||||
|  | @ -19,8 +19,20 @@ namespace MoonWorks | ||||||
| 		internal Texture SwapchainTexture { get; set; } | 		internal Texture SwapchainTexture { get; set; } | ||||||
| 
 | 
 | ||||||
| 		public bool Claimed { get; internal set; } | 		public bool Claimed { get; internal set; } | ||||||
|  | 		public MoonWorks.Graphics.SwapchainComposition SwapchainComposition { get; internal set; } | ||||||
| 		public MoonWorks.Graphics.TextureFormat SwapchainFormat { get; internal set; } | 		public MoonWorks.Graphics.TextureFormat SwapchainFormat { get; internal set; } | ||||||
| 
 | 
 | ||||||
|  | 		public (int, int) Position | ||||||
|  | 		{ | ||||||
|  | 			get | ||||||
|  | 			{ | ||||||
|  | 				SDL.SDL_GetWindowPosition(Handle, out var x, out var y); | ||||||
|  | 				return (x, y); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		public string Title { get; private set;} | ||||||
|  | 
 | ||||||
| 		private bool IsDisposed; | 		private bool IsDisposed; | ||||||
| 
 | 
 | ||||||
| 		private static Dictionary<uint, Window> idLookup = new Dictionary<uint, Window>(); | 		private static Dictionary<uint, Window> idLookup = new Dictionary<uint, Window>(); | ||||||
|  | @ -101,7 +113,7 @@ namespace MoonWorks | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		/// <param name="width"></param> | 		/// <param name="width"></param> | ||||||
| 		/// <param name="height"></param> | 		/// <param name="height"></param> | ||||||
| 		public void SetWindowSize(uint width, uint height) | 		public void SetSize(uint width, uint height) | ||||||
| 		{ | 		{ | ||||||
| 			SDL.SDL_SetWindowSize(Handle, (int) width, (int) height); | 			SDL.SDL_SetWindowSize(Handle, (int) width, (int) height); | ||||||
| 			Width = width; | 			Width = width; | ||||||
|  | @ -113,6 +125,23 @@ namespace MoonWorks | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Sets the window position. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public void SetPosition(int x, int y) | ||||||
|  | 		{ | ||||||
|  | 			SDL.SDL_SetWindowPosition(Handle, x, y); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Sets the window title. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public void SetTitle(string title) | ||||||
|  | 		{ | ||||||
|  | 			SDL.SDL_SetWindowTitle(Handle, title); | ||||||
|  | 			Title = title; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		internal static Window Lookup(uint windowID) | 		internal static Window Lookup(uint windowID) | ||||||
| 		{ | 		{ | ||||||
| 			return idLookup.ContainsKey(windowID) ? idLookup[windowID] : null; | 			return idLookup.ContainsKey(windowID) ? idLookup[windowID] : null; | ||||||
|  |  | ||||||
|  | @ -22,9 +22,13 @@ | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		public ScreenMode ScreenMode; | 		public ScreenMode ScreenMode; | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
|  | 		/// Specifies the swapchain composition. Use SDR unless you know what you're doing. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public Graphics.SwapchainComposition SwapchainComposition; | ||||||
|  | 		/// <summary> | ||||||
| 		/// Specifies the presentation mode for the window. Roughly equivalent to V-Sync. | 		/// Specifies the presentation mode for the window. Roughly equivalent to V-Sync. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		public PresentMode PresentMode; | 		public Graphics.PresentMode PresentMode; | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Whether the window can be resized using the operating system's window dragging feature. | 		/// Whether the window can be resized using the operating system's window dragging feature. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
|  | @ -39,7 +43,8 @@ | ||||||
| 			uint windowWidth, | 			uint windowWidth, | ||||||
| 			uint windowHeight, | 			uint windowHeight, | ||||||
| 			ScreenMode screenMode, | 			ScreenMode screenMode, | ||||||
| 			PresentMode presentMode, | 			Graphics.SwapchainComposition swapchainComposition, | ||||||
|  | 			Graphics.PresentMode presentMode, | ||||||
| 			bool systemResizable = false, | 			bool systemResizable = false, | ||||||
| 			bool startMaximized = false | 			bool startMaximized = false | ||||||
| 		) { | 		) { | ||||||
|  | @ -47,6 +52,7 @@ | ||||||
| 			WindowWidth = windowWidth; | 			WindowWidth = windowWidth; | ||||||
| 			WindowHeight = windowHeight; | 			WindowHeight = windowHeight; | ||||||
| 			ScreenMode = screenMode; | 			ScreenMode = screenMode; | ||||||
|  | 			SwapchainComposition = swapchainComposition; | ||||||
| 			PresentMode = presentMode; | 			PresentMode = presentMode; | ||||||
| 			SystemResizable = systemResizable; | 			SystemResizable = systemResizable; | ||||||
| 			StartMaximized = startMaximized; | 			StartMaximized = startMaximized; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue