Compare commits
50 Commits
Author | SHA1 | Date |
---|---|---|
cosmonaut | 3cfb43438c | |
cosmonaut | 97dee2a170 | |
cosmonaut | fe6734d6db | |
cosmonaut | eab282cdca | |
cosmonaut | 4e8653fb1f | |
cosmonaut | 8b35b6b0b7 | |
cosmonaut | cc7cae9d6b | |
cosmonaut | 39dc2fb7f4 | |
cosmonaut | b4021156cf | |
cosmonaut | 871b32d742 | |
cosmonaut | 0fbe241848 | |
cosmonaut | 1b40b5e7d3 | |
cosmonaut | ee8331a96b | |
cosmonaut | 8813a0139d | |
cosmonaut | 217ae96888 | |
cosmonaut | 23b6499479 | |
cosmonaut | 929d8f5cc9 | |
cosmonaut | 69d2c9cc31 | |
cosmonaut | 9195e445b2 | |
cosmonaut | f30d8f0197 | |
cosmonaut | bde31fbe07 | |
cosmonaut | a762a80c4f | |
cosmonaut | 099c07aa39 | |
cosmonaut | cba6ca59d3 | |
cosmonaut | a004488f81 | |
cosmonaut | 33ed8b2364 | |
cosmonaut | 3c832550d0 | |
cosmonaut | c84752f38c | |
cosmonaut | 019afa91f5 | |
cosmonaut | 00adec189c | |
cosmonaut | 0e723514df | |
cosmonaut | e0f4c19dc2 | |
cosmonaut | 178a5ea3cf | |
cosmonaut | 50b8cb11c9 | |
cosmonaut | d83501437d | |
cosmonaut | fe520dc9cc | |
cosmonaut | b29341eca3 | |
cosmonaut | 22bcd2e471 | |
cosmonaut | fe31e23ccc | |
cosmonaut | 848b1c706c | |
cosmonaut | a207f404b9 | |
cosmonaut | 31c79d3179 | |
cosmonaut | 8229e5dd33 | |
cosmonaut | 1eae01c95c | |
cosmonaut | b80527d793 | |
cosmonaut | ecfcb666a8 | |
cosmonaut | ad97aed60f | |
cosmonaut | 0a5ec9e82d | |
cosmonaut | 8648eef5d1 | |
cosmonaut | 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"]
|
[submodule "lib/WellspringCS"]
|
||||||
path = lib/WellspringCS
|
path = lib/WellspringCS
|
||||||
url = https://gitea.moonside.games/MoonsideGames/WellspringCS.git
|
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 "SDL2_gpuCS"]
|
||||||
|
path = lib/SDL2_gpuCS
|
||||||
|
url = https://github.com/MoonsideGames/SDL2_gpuCS.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\SDL2_gpuCS\SDL2_gpuCS.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,8 +25,8 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="src\Graphics\StockShaders\Binary\video_fullscreen.vert.refresh">
|
<EmbeddedResource Include="src\Graphics\StockShaders\Binary\fullscreen.vert.refresh">
|
||||||
<LogicalName>MoonWorks.Graphics.StockShaders.VideoFullscreen.vert.refresh</LogicalName>
|
<LogicalName>MoonWorks.Graphics.StockShaders.Fullscreen.vert.refresh</LogicalName>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="src\Graphics\StockShaders\Binary\video_yuv2rgba.frag.refresh">
|
<EmbeddedResource Include="src\Graphics\StockShaders\Binary\video_yuv2rgba.frag.refresh">
|
||||||
<LogicalName>MoonWorks.Graphics.StockShaders.VideoYUV2RGBA.frag.refresh</LogicalName>
|
<LogicalName>MoonWorks.Graphics.StockShaders.VideoYUV2RGBA.frag.refresh</LogicalName>
|
||||||
|
@ -38,5 +37,8 @@
|
||||||
<EmbeddedResource Include="src\Graphics\StockShaders\Binary\text_msdf.frag.refresh">
|
<EmbeddedResource Include="src\Graphics\StockShaders\Binary\text_msdf.frag.refresh">
|
||||||
<LogicalName>MoonWorks.Graphics.StockShaders.TextMSDF.frag.refresh</LogicalName>
|
<LogicalName>MoonWorks.Graphics.StockShaders.TextMSDF.frag.refresh</LogicalName>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="src\Graphics\StockShaders\Binary\blit.frag.refresh">
|
||||||
|
<LogicalName>MoonWorks.Graphics.StockShaders.Blit.frag.refresh</LogicalName>
|
||||||
|
</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 +0,0 @@
|
||||||
Subproject commit b5325e6d0329eeb35b074091a569a5f679852d28
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 70925aa394c0f24388f3cfb97f1c6ee1f91baf52
|
|
@ -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];
|
||||||
|
|
11
src/Game.cs
11
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,19 @@ 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...");
|
Logger.LogInfo("Initializing main window...");
|
||||||
MainWindow = new Window(windowCreateInfo, GraphicsDevice.WindowFlags | SDL.SDL_WindowFlags.SDL_WINDOW_HIDDEN);
|
MainWindow = new Window(windowCreateInfo, GraphicsDevice.WindowFlags | SDL.SDL_WindowFlags.SDL_WINDOW_HIDDEN);
|
||||||
|
|
||||||
if (!GraphicsDevice.ClaimWindow(MainWindow, windowCreateInfo.PresentMode))
|
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,213 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using SDL2;
|
||||||
|
using SDL2_gpuCS;
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuBindComputePipeline(
|
||||||
|
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 sdlTextureSlice = textureSlice.ToSDL();
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuBindComputeStorageTextures(
|
||||||
|
Handle,
|
||||||
|
slot,
|
||||||
|
&sdlTextureSlice,
|
||||||
|
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;
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuBindComputeStorageBuffers(
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuPushComputeUniformData(
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuDispatchCompute(
|
||||||
|
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,203 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using SDL2_gpuCS;
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuUploadToTexture(
|
||||||
|
Handle,
|
||||||
|
transferBuffer.Handle,
|
||||||
|
textureRegion.ToSDL(),
|
||||||
|
copyParams.ToSDL(),
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuUploadToBuffer(
|
||||||
|
Handle,
|
||||||
|
transferBuffer.Handle,
|
||||||
|
buffer.Handle,
|
||||||
|
copyParams.ToSDL(),
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuCopyTextureToTexture(
|
||||||
|
Handle,
|
||||||
|
source.ToSDL(),
|
||||||
|
destination.ToSDL(),
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuCopyBufferToBuffer(
|
||||||
|
Handle,
|
||||||
|
source.Handle,
|
||||||
|
destination.Handle,
|
||||||
|
copyParams.ToSDL(),
|
||||||
|
Conversions.BoolToInt(cycle)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#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, VertexBuffer.Size + IndexBuffer.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call this to initialize or reset the batch.
|
// Call this to initialize or reset the batch.
|
||||||
|
@ -93,22 +97,40 @@ 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, VertexBuffer.Size + IndexBuffer.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vertexDataLengthInBytes > 0 && indexDataLengthInBytes > 0)
|
if (vertexDataLengthInBytes > 0 && indexDataLengthInBytes > 0)
|
||||||
{
|
{
|
||||||
commandBuffer.SetBufferData(VertexBuffer, vertexDataPointer, 0, vertexDataLengthInBytes);
|
TransferBuffer.SetData(vertexSpan, TransferOptions.Cycle);
|
||||||
commandBuffer.SetBufferData(IndexBuffer, indexDataPointer, 0, indexDataLengthInBytes);
|
TransferBuffer.SetData(indexSpan, (uint) vertexSpan.Length, TransferOptions.Unsafe);
|
||||||
|
|
||||||
|
commandBuffer.BeginCopyPass();
|
||||||
|
commandBuffer.UploadToBuffer(TransferBuffer, VertexBuffer, new BufferCopy(0, 0, (uint) vertexSpan.Length), WriteOptions.Cycle);
|
||||||
|
commandBuffer.UploadToBuffer(TransferBuffer, IndexBuffer, new BufferCopy((uint) vertexSpan.Length, 0, (uint) indexSpan.Length), WriteOptions.Cycle);
|
||||||
|
commandBuffer.EndCopyPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
PrimitiveCount = vertexCount / 2;
|
PrimitiveCount = vertexCount / 2;
|
||||||
|
@ -123,12 +145,12 @@ namespace MoonWorks.Graphics.Font
|
||||||
));
|
));
|
||||||
commandBuffer.BindVertexBuffers(VertexBuffer);
|
commandBuffer.BindVertexBuffers(VertexBuffer);
|
||||||
commandBuffer.BindIndexBuffer(IndexBuffer, IndexElementSize.ThirtyTwo);
|
commandBuffer.BindIndexBuffer(IndexBuffer, IndexElementSize.ThirtyTwo);
|
||||||
|
commandBuffer.PushVertexShaderUniforms(transformMatrix);
|
||||||
|
commandBuffer.PushFragmentShaderUniforms(CurrentFont.DistanceRange);
|
||||||
commandBuffer.DrawIndexedPrimitives(
|
commandBuffer.DrawIndexedPrimitives(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
PrimitiveCount,
|
PrimitiveCount
|
||||||
commandBuffer.PushVertexShaderUniforms(transformMatrix),
|
|
||||||
commandBuffer.PushFragmentShaderUniforms(CurrentFont.DistanceRange)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using MoonWorks.Video;
|
using MoonWorks.Video;
|
||||||
using RefreshCS;
|
using SDL2;
|
||||||
using WellspringCS;
|
using SDL2_gpuCS;
|
||||||
|
|
||||||
namespace MoonWorks.Graphics
|
namespace MoonWorks.Graphics
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,8 @@ 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;
|
private uint windowFlags;
|
||||||
public SDL2.SDL.SDL_WindowFlags WindowFlags => (SDL2.SDL.SDL_WindowFlags) windowFlags;
|
public SDL2.SDL.SDL_WindowFlags WindowFlags => (SDL2.SDL.SDL_WindowFlags) windowFlags;
|
||||||
|
@ -23,8 +24,10 @@ namespace MoonWorks.Graphics
|
||||||
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,62 +37,115 @@ 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;
|
||||||
|
internal ComputePassPool ComputePassPool;
|
||||||
|
internal CopyPassPool 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 = SDL_Gpu.SDL_GpuCreateDevice(
|
||||||
|
(SDL_Gpu.BackendFlags) preferredBackends,
|
||||||
Conversions.BoolToByte(debugMode)
|
Conversions.BoolToByte(debugMode)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
DebugMode = debugMode;
|
||||||
// TODO: check for CreateDevice fail
|
// TODO: check for CreateDevice fail
|
||||||
|
|
||||||
|
Backend = (BackendFlags) SDL_Gpu.SDL_GpuGetBackend(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;
|
string blitFragPath = Path.Combine(basePath, "blit.frag.refresh");
|
||||||
|
|
||||||
ShaderModule textVertShader;
|
Shader fullscreenVertShader;
|
||||||
ShaderModule textFragShader;
|
|
||||||
|
|
||||||
if (File.Exists(videoVertPath) && File.Exists(videoFragPath))
|
Shader textVertShader;
|
||||||
|
Shader textFragShader;
|
||||||
|
|
||||||
|
Shader videoFragShader;
|
||||||
|
Shader blitFragShader;
|
||||||
|
|
||||||
|
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.refresh");
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
videoFragShader = new Shader(
|
||||||
|
this,
|
||||||
|
videoFragPath,
|
||||||
|
"main",
|
||||||
|
ShaderStage.Fragment,
|
||||||
|
ShaderFormat.SECRET
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// use defaults
|
||||||
|
var assembly = typeof(GraphicsDevice).Assembly;
|
||||||
using var fragStream = assembly.GetManifestResourceStream("MoonWorks.Graphics.StockShaders.VideoYUV2RGBA.frag.refresh");
|
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);
|
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
|
||||||
{
|
{
|
||||||
|
@ -99,8 +155,46 @@ namespace MoonWorks.Graphics
|
||||||
using var vertStream = assembly.GetManifestResourceStream("MoonWorks.Graphics.StockShaders.TextTransform.vert.refresh");
|
using var vertStream = assembly.GetManifestResourceStream("MoonWorks.Graphics.StockShaders.TextTransform.vert.refresh");
|
||||||
using var fragStream = assembly.GetManifestResourceStream("MoonWorks.Graphics.StockShaders.TextMSDF.frag.refresh");
|
using var fragStream = assembly.GetManifestResourceStream("MoonWorks.Graphics.StockShaders.TextMSDF.frag.refresh");
|
||||||
|
|
||||||
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(blitFragPath))
|
||||||
|
{
|
||||||
|
blitFragShader = new Shader(
|
||||||
|
this,
|
||||||
|
blitFragPath,
|
||||||
|
"main",
|
||||||
|
ShaderStage.Fragment,
|
||||||
|
ShaderFormat.SECRET
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// use defaults
|
||||||
|
var assembly = typeof(GraphicsDevice).Assembly;
|
||||||
|
|
||||||
|
using var fragStream = assembly.GetManifestResourceStream("MoonWorks.Graphics.StockShaders.Blit.frag.refresh");
|
||||||
|
blitFragShader = new Shader(
|
||||||
|
this,
|
||||||
|
fragStream,
|
||||||
|
"main",
|
||||||
|
ShaderStage.Fragment,
|
||||||
|
ShaderFormat.SPIRV
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoPipeline = new GraphicsPipeline(
|
VideoPipeline = new GraphicsPipeline(
|
||||||
|
@ -114,16 +208,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 +221,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,28 +242,35 @@ 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(
|
SDL_Gpu.SDL_GpuClaimWindow(
|
||||||
Handle,
|
Handle,
|
||||||
window.Handle,
|
window.Handle,
|
||||||
(Refresh.PresentMode) presentMode
|
(SDL_Gpu.SwapchainComposition) swapchainComposition,
|
||||||
|
(SDL_Gpu.PresentMode) presentMode
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
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);
|
||||||
|
@ -183,7 +287,7 @@ namespace MoonWorks.Graphics
|
||||||
{
|
{
|
||||||
if (window.Claimed)
|
if (window.Claimed)
|
||||||
{
|
{
|
||||||
Refresh.Refresh_UnclaimWindow(
|
SDL_Gpu.SDL_GpuUnclaimWindow(
|
||||||
Handle,
|
Handle,
|
||||||
window.Handle
|
window.Handle
|
||||||
);
|
);
|
||||||
|
@ -201,18 +305,22 @@ 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(
|
SDL_Gpu.SDL_GpuSetSwapchainParameters(
|
||||||
Handle,
|
Handle,
|
||||||
window.Handle,
|
window.Handle,
|
||||||
(Refresh.PresentMode) presentMode
|
(SDL_Gpu.SwapchainComposition) swapchainComposition,
|
||||||
|
(SDL_Gpu.PresentMode) presentMode
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +332,7 @@ namespace MoonWorks.Graphics
|
||||||
public CommandBuffer AcquireCommandBuffer()
|
public CommandBuffer AcquireCommandBuffer()
|
||||||
{
|
{
|
||||||
var commandBuffer = CommandBufferPool.Obtain();
|
var commandBuffer = CommandBufferPool.Obtain();
|
||||||
commandBuffer.SetHandle(Refresh.Refresh_AcquireCommandBuffer(Handle));
|
commandBuffer.SetHandle(SDL_Gpu.SDL_GpuAcquireCommandBuffer(Handle));
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
commandBuffer.ResetStateTracking();
|
commandBuffer.ResetStateTracking();
|
||||||
#endif
|
#endif
|
||||||
|
@ -243,8 +351,7 @@ namespace MoonWorks.Graphics
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Refresh.Refresh_Submit(
|
SDL_Gpu.SDL_GpuSubmit(
|
||||||
Handle,
|
|
||||||
commandBuffer.Handle
|
commandBuffer.Handle
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -261,8 +368,7 @@ namespace MoonWorks.Graphics
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Fence SubmitAndAcquireFence(CommandBuffer commandBuffer)
|
public Fence SubmitAndAcquireFence(CommandBuffer commandBuffer)
|
||||||
{
|
{
|
||||||
var fenceHandle = Refresh.Refresh_SubmitAndAcquireFence(
|
var fenceHandle = SDL_Gpu.SDL_GpuSubmitAndAcquireFence(
|
||||||
Handle,
|
|
||||||
commandBuffer.Handle
|
commandBuffer.Handle
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -277,7 +383,7 @@ namespace MoonWorks.Graphics
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Wait()
|
public void Wait()
|
||||||
{
|
{
|
||||||
Refresh.Refresh_Wait(Handle);
|
SDL_Gpu.SDL_GpuWait(Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -285,14 +391,13 @@ namespace MoonWorks.Graphics
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public unsafe void WaitForFences(Fence fence)
|
public unsafe void WaitForFences(Fence fence)
|
||||||
{
|
{
|
||||||
var handlePtr = stackalloc nint[1];
|
var fenceHandle = fence.Handle;
|
||||||
handlePtr[0] = fence.Handle;
|
|
||||||
|
|
||||||
Refresh.Refresh_WaitForFences(
|
SDL_Gpu.SDL_GpuWaitForFences(
|
||||||
Handle,
|
Handle,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
(nint) handlePtr
|
&fenceHandle
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,11 +414,11 @@ namespace MoonWorks.Graphics
|
||||||
handlePtr[0] = fenceOne.Handle;
|
handlePtr[0] = fenceOne.Handle;
|
||||||
handlePtr[1] = fenceTwo.Handle;
|
handlePtr[1] = fenceTwo.Handle;
|
||||||
|
|
||||||
Refresh.Refresh_WaitForFences(
|
SDL_Gpu.SDL_GpuWaitForFences(
|
||||||
Handle,
|
Handle,
|
||||||
Conversions.BoolToByte(waitAll),
|
Conversions.BoolToInt(waitAll),
|
||||||
2,
|
2,
|
||||||
(nint) handlePtr
|
handlePtr
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,11 +437,11 @@ namespace MoonWorks.Graphics
|
||||||
handlePtr[1] = fenceTwo.Handle;
|
handlePtr[1] = fenceTwo.Handle;
|
||||||
handlePtr[2] = fenceThree.Handle;
|
handlePtr[2] = fenceThree.Handle;
|
||||||
|
|
||||||
Refresh.Refresh_WaitForFences(
|
SDL_Gpu.SDL_GpuWaitForFences(
|
||||||
Handle,
|
Handle,
|
||||||
Conversions.BoolToByte(waitAll),
|
Conversions.BoolToInt(waitAll),
|
||||||
3,
|
3,
|
||||||
(nint) handlePtr
|
handlePtr
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,11 +462,11 @@ namespace MoonWorks.Graphics
|
||||||
handlePtr[2] = fenceThree.Handle;
|
handlePtr[2] = fenceThree.Handle;
|
||||||
handlePtr[3] = fenceFour.Handle;
|
handlePtr[3] = fenceFour.Handle;
|
||||||
|
|
||||||
Refresh.Refresh_WaitForFences(
|
SDL_Gpu.SDL_GpuWaitForFences(
|
||||||
Handle,
|
Handle,
|
||||||
Conversions.BoolToByte(waitAll),
|
Conversions.BoolToInt(waitAll),
|
||||||
4,
|
4,
|
||||||
(nint) handlePtr
|
handlePtr
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,7 +474,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(Fence[] fences, bool waitAll)
|
public unsafe void WaitForFences(Span<Fence> fences, bool waitAll)
|
||||||
{
|
{
|
||||||
var handlePtr = stackalloc nint[fences.Length];
|
var handlePtr = stackalloc nint[fences.Length];
|
||||||
|
|
||||||
|
@ -378,11 +483,11 @@ namespace MoonWorks.Graphics
|
||||||
handlePtr[i] = fences[i].Handle;
|
handlePtr[i] = fences[i].Handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
Refresh.Refresh_WaitForFences(
|
SDL_Gpu.SDL_GpuWaitForFences(
|
||||||
Handle,
|
Handle,
|
||||||
Conversions.BoolToByte(waitAll),
|
Conversions.BoolToInt(waitAll),
|
||||||
4,
|
4,
|
||||||
(nint) handlePtr
|
handlePtr
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,7 +497,7 @@ namespace MoonWorks.Graphics
|
||||||
/// <exception cref="InvalidOperationException">Throws if the fence query indicates that the graphics device has been lost.</exception>
|
/// <exception cref="InvalidOperationException">Throws if the fence query indicates that the graphics device has been lost.</exception>
|
||||||
public bool QueryFence(Fence fence)
|
public bool QueryFence(Fence fence)
|
||||||
{
|
{
|
||||||
var result = Refresh.Refresh_QueryFence(Handle, fence.Handle);
|
var result = SDL_Gpu.SDL_GpuQueryFence(Handle, fence.Handle);
|
||||||
|
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
{
|
{
|
||||||
|
@ -407,14 +512,19 @@ namespace MoonWorks.Graphics
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ReleaseFence(Fence fence)
|
public void ReleaseFence(Fence fence)
|
||||||
{
|
{
|
||||||
Refresh.Refresh_ReleaseFence(Handle, fence.Handle);
|
SDL_Gpu.SDL_GpuReleaseFence(Handle, fence.Handle);
|
||||||
fence.Handle = IntPtr.Zero;
|
fence.Handle = IntPtr.Zero;
|
||||||
FencePool.Return(fence);
|
FencePool.Return(fence);
|
||||||
}
|
}
|
||||||
|
|
||||||
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) SDL_Gpu.SDL_GpuGetSwapchainTextureFormat(Handle, window.Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddResourceReference(GCHandle resourceReference)
|
internal void AddResourceReference(GCHandle resourceReference)
|
||||||
|
@ -462,7 +572,7 @@ namespace MoonWorks.Graphics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Refresh.Refresh_DestroyDevice(Handle);
|
SDL_Gpu.SDL_GpuDestroyDevice(Handle);
|
||||||
|
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,457 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using RefreshCS;
|
||||||
|
using SDL2;
|
||||||
|
|
||||||
|
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 IntPtr GetPixelDataFromBytes(
|
||||||
|
Span<byte> data,
|
||||||
|
out uint width,
|
||||||
|
out uint height,
|
||||||
|
out uint sizeInBytes
|
||||||
|
) {
|
||||||
|
fixed (byte* ptr = data)
|
||||||
|
{
|
||||||
|
var pixelData =
|
||||||
|
Refresh.Refresh_Image_Load(
|
||||||
|
(nint) 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 IntPtr 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 IntPtr 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(
|
||||||
|
(nint) ptr,
|
||||||
|
data.Length,
|
||||||
|
out var w,
|
||||||
|
out var h,
|
||||||
|
out var len
|
||||||
|
);
|
||||||
|
|
||||||
|
width = (uint) w;
|
||||||
|
height = (uint) h;
|
||||||
|
sizeInBytes = (uint) len;
|
||||||
|
|
||||||
|
return Conversions.ByteToBool(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 static void FreePixelData(IntPtr 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 = 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 = 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, (nint) 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,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,559 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using SDL2_gpuCS;
|
||||||
|
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 bool active;
|
||||||
|
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
|
||||||
|
AssertRenderPassActive();
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuBindGraphicsPipeline(
|
||||||
|
Handle,
|
||||||
|
graphicsPipeline.Handle
|
||||||
|
);
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
currentGraphicsPipeline = graphicsPipeline;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the viewport.
|
||||||
|
/// </summary>
|
||||||
|
public void SetViewport(in Viewport viewport)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
AssertRenderPassActive();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuSetViewport(
|
||||||
|
Handle,
|
||||||
|
viewport.ToSDL()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the scissor area.
|
||||||
|
/// </summary>
|
||||||
|
public void SetScissor(in Rect scissor)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
AssertRenderPassActive();
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuSetScissor(
|
||||||
|
Handle,
|
||||||
|
scissor.ToSDL()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 sdlBufferBinding = bufferBinding.ToSDL();
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuBindVertexBuffers(
|
||||||
|
Handle,
|
||||||
|
firstBinding,
|
||||||
|
&sdlBufferBinding,
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuBindIndexBuffer(
|
||||||
|
Handle,
|
||||||
|
bufferBinding.ToSDL(),
|
||||||
|
(SDL_Gpu.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 sdlTextureSamplerBinding = textureSamplerBinding.ToSDL();
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuBindVertexSamplers(
|
||||||
|
Handle,
|
||||||
|
slot,
|
||||||
|
&sdlTextureSamplerBinding,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void BindVertexStorageTexture(
|
||||||
|
in TextureSlice textureSlice,
|
||||||
|
uint slot = 0
|
||||||
|
) {
|
||||||
|
#if DEBUG
|
||||||
|
AssertGraphicsPipelineBound();
|
||||||
|
AssertTextureNonNull(textureSlice.Texture);
|
||||||
|
AssertTextureHasGraphicsStorageFlag(textureSlice.Texture);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
var sdlTextureSlice = textureSlice.ToSDL();
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuBindVertexStorageTextures(
|
||||||
|
Handle,
|
||||||
|
slot,
|
||||||
|
&sdlTextureSlice,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void BindVertexStorageBuffer(
|
||||||
|
GpuBuffer buffer,
|
||||||
|
uint slot = 0
|
||||||
|
) {
|
||||||
|
#if DEBUG
|
||||||
|
AssertGraphicsPipelineBound();
|
||||||
|
AssertBufferNonNull(buffer);
|
||||||
|
AssertBufferHasGraphicsStorageFlag(buffer);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
var bufferHandle = buffer.Handle;
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuBindVertexStorageBuffers(
|
||||||
|
Handle,
|
||||||
|
slot,
|
||||||
|
&bufferHandle,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void BindFragmentSamplers(
|
||||||
|
in TextureSamplerBinding textureSamplerBinding,
|
||||||
|
uint slot = 0
|
||||||
|
) {
|
||||||
|
#if DEBUG
|
||||||
|
AssertGraphicsPipelineBound();
|
||||||
|
AssertTextureSamplerBindingNonNull(textureSamplerBinding);
|
||||||
|
AssertTextureHasSamplerFlag(textureSamplerBinding.Texture);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
var sdlTextureSamplerBinding = textureSamplerBinding.ToSDL();
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuBindFragmentSamplers(
|
||||||
|
Handle,
|
||||||
|
slot,
|
||||||
|
&sdlTextureSamplerBinding,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void BindFragmentStorageTexture(
|
||||||
|
in TextureSlice textureSlice,
|
||||||
|
uint slot = 0
|
||||||
|
) {
|
||||||
|
#if DEBUG
|
||||||
|
AssertGraphicsPipelineBound();
|
||||||
|
AssertTextureNonNull(textureSlice.Texture);
|
||||||
|
AssertTextureHasGraphicsStorageFlag(textureSlice.Texture);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
var sdlTextureSlice = textureSlice.ToSDL();
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuBindFragmentStorageTextures(
|
||||||
|
Handle,
|
||||||
|
slot,
|
||||||
|
&sdlTextureSlice,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void BindFragmentStorageBuffer(
|
||||||
|
GpuBuffer buffer,
|
||||||
|
uint slot = 0
|
||||||
|
) {
|
||||||
|
#if DEBUG
|
||||||
|
AssertGraphicsPipelineBound();
|
||||||
|
AssertBufferNonNull(buffer);
|
||||||
|
AssertBufferHasGraphicsStorageFlag(buffer);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
var bufferHandle = buffer.Handle;
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuBindFragmentStorageBuffers(
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuPushVertexUniformData(
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuPushFragmentUniformData(
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuDrawIndexedPrimitives(
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuDrawPrimitives(
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuDrawPrimitivesIndirect(
|
||||||
|
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
|
||||||
|
|
||||||
|
SDL_Gpu.SDL_GpuDrawIndexedPrimitivesIndirect(
|
||||||
|
Handle,
|
||||||
|
buffer.Handle,
|
||||||
|
offsetInBytes,
|
||||||
|
drawCount,
|
||||||
|
stride
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
private void AssertRenderPassActive(string message = "Render pass is not active!")
|
||||||
|
{
|
||||||
|
if (!active)
|
||||||
|
{
|
||||||
|
throw new System.InvalidOperationException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.WaitForFences(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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
using RefreshCS;
|
using SDL2_gpuCS;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace MoonWorks.Graphics
|
namespace MoonWorks.Graphics
|
||||||
|
@ -6,36 +6,35 @@ namespace MoonWorks.Graphics
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compute pipelines perform arbitrary parallel processing on input data.
|
/// Compute pipelines perform arbitrary parallel processing on input data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ComputePipeline : RefreshResource
|
public class ComputePipeline : SDL_GpuResource
|
||||||
{
|
{
|
||||||
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyComputePipeline;
|
protected override Action<IntPtr, IntPtr> ReleaseFunction => SDL_Gpu.SDL_GpuReleaseComputePipeline;
|
||||||
|
|
||||||
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 sdlComputePipelineCreateInfo = new SDL_Gpu.ComputePipelineCreateInfo
|
||||||
{
|
{
|
||||||
entryPointName = computeShaderInfo.EntryPointName,
|
ComputeShader = computeShader.Handle,
|
||||||
shaderModule = computeShaderInfo.ShaderModule.Handle,
|
PipelineResourceInfo = resourceInfo.ToSDL()
|
||||||
uniformBufferSize = computeShaderInfo.UniformBufferSize,
|
|
||||||
bufferBindingCount = computeShaderInfo.BufferBindingCount,
|
|
||||||
imageBindingCount = computeShaderInfo.ImageBindingCount
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Handle = Refresh.Refresh_CreateComputePipeline(
|
Handle = SDL_Gpu.SDL_GpuCreateComputePipeline(
|
||||||
device.Handle,
|
device.Handle,
|
||||||
refreshComputeShaderInfo
|
sdlComputePipelineCreateInfo
|
||||||
);
|
);
|
||||||
|
|
||||||
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,18 +1,18 @@
|
||||||
using System;
|
using System;
|
||||||
using RefreshCS;
|
using SDL2_gpuCS;
|
||||||
|
|
||||||
namespace MoonWorks.Graphics
|
namespace MoonWorks.Graphics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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 make sure to call GraphicsDevice.ReleaseFence when done with a Fence to avoid memory growth. <br/>
|
||||||
|
/// The Fence object itself is basically just a wrapper for the Refresh_Fence. <br/>
|
||||||
|
/// The internal handle is replaced so that we can pool Fence objects to manage garbage.
|
||||||
|
/// </summary>
|
||||||
|
public class Fence : SDL_GpuResource
|
||||||
{
|
{
|
||||||
/// <summary>
|
protected override Action<nint, nint> ReleaseFunction => SDL_Gpu.SDL_GpuReleaseFence;
|
||||||
/// 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 make sure to call GraphicsDevice.ReleaseFence when done with a Fence to avoid memory growth. <br/>
|
|
||||||
/// The Fence object itself is basically just a wrapper for the Refresh_Fence. <br/>
|
|
||||||
/// The internal handle is replaced so that we can pool Fence objects to manage garbage.
|
|
||||||
/// </summary>
|
|
||||||
public class Fence : RefreshResource
|
|
||||||
{
|
|
||||||
protected override Action<nint, nint> QueueDestroyFunction => Refresh.Refresh_ReleaseFence;
|
|
||||||
|
|
||||||
internal Fence(GraphicsDevice device) : base(device)
|
internal Fence(GraphicsDevice device) : base(device)
|
||||||
{
|
{
|
||||||
|
@ -22,5 +22,4 @@ namespace MoonWorks.Graphics
|
||||||
{
|
{
|
||||||
Handle = handle;
|
Handle = handle;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using SDL2_gpuCS;
|
||||||
|
|
||||||
|
namespace MoonWorks.Graphics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// GpuBuffers are generic data containers that can be used by the GPU.
|
||||||
|
/// </summary>
|
||||||
|
public class GpuBuffer : SDL_GpuResource
|
||||||
|
{
|
||||||
|
protected override Action<IntPtr, IntPtr> ReleaseFunction => SDL_Gpu.SDL_GpuReleaseBuffer;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
SDL_Gpu.SDL_GpuSetBufferName(
|
||||||
|
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 = SDL_Gpu.SDL_GpuCreateBuffer(
|
||||||
|
device.Handle,
|
||||||
|
(SDL_Gpu.BufferUsageFlags) usageFlags,
|
||||||
|
sizeInBytes
|
||||||
|
);
|
||||||
|
UsageFlags = usageFlags;
|
||||||
|
Size = sizeInBytes;
|
||||||
|
name = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator BufferBinding(GpuBuffer b)
|
||||||
|
{
|
||||||
|
return new BufferBinding(b, 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using RefreshCS;
|
using SDL2_gpuCS;
|
||||||
|
|
||||||
namespace MoonWorks.Graphics
|
namespace MoonWorks.Graphics
|
||||||
{
|
{
|
||||||
|
@ -8,12 +8,12 @@ namespace MoonWorks.Graphics
|
||||||
/// Graphics pipelines encapsulate all of the render state in a single object. <br/>
|
/// Graphics pipelines encapsulate all of the render state in a single object. <br/>
|
||||||
/// These pipelines are bound before draw calls are issued.
|
/// These pipelines are bound before draw calls are issued.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class GraphicsPipeline : RefreshResource
|
public class GraphicsPipeline : SDL_GpuResource
|
||||||
{
|
{
|
||||||
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyGraphicsPipeline;
|
protected override Action<IntPtr, IntPtr> ReleaseFunction => SDL_Gpu.SDL_GpuReleaseGraphicsPipeline;
|
||||||
|
|
||||||
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,100 +25,78 @@ namespace MoonWorks.Graphics
|
||||||
in GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
|
in GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
|
||||||
) : base(device)
|
) : base(device)
|
||||||
{
|
{
|
||||||
DepthStencilState depthStencilState = graphicsPipelineCreateInfo.DepthStencilState;
|
SDL_Gpu.GraphicsPipelineCreateInfo sdlGraphicsPipelineCreateInfo;
|
||||||
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(
|
var vertexAttributes = (SDL_Gpu.VertexAttribute*) NativeMemory.Alloc(
|
||||||
vertexInputState.VertexAttributes,
|
(nuint) (graphicsPipelineCreateInfo.VertexInputState.VertexAttributes.Length * Marshal.SizeOf<SDL_Gpu.VertexAttribute>())
|
||||||
GCHandleType.Pinned
|
|
||||||
);
|
|
||||||
var vertexBindingsHandle = GCHandle.Alloc(
|
|
||||||
vertexInputState.VertexBindings,
|
|
||||||
GCHandleType.Pinned
|
|
||||||
);
|
);
|
||||||
|
|
||||||
var colorAttachmentDescriptions = stackalloc Refresh.ColorAttachmentDescription[
|
for (var i = 0; i < graphicsPipelineCreateInfo.VertexInputState.VertexAttributes.Length; i += 1)
|
||||||
(int) attachmentInfo.ColorAttachmentDescriptions.Length
|
|
||||||
];
|
|
||||||
|
|
||||||
for (var i = 0; i < attachmentInfo.ColorAttachmentDescriptions.Length; i += 1)
|
|
||||||
{
|
{
|
||||||
colorAttachmentDescriptions[i].format = (Refresh.TextureFormat) attachmentInfo.ColorAttachmentDescriptions[i].Format;
|
vertexAttributes[i] = graphicsPipelineCreateInfo.VertexInputState.VertexAttributes[i].ToSDL();
|
||||||
colorAttachmentDescriptions[i].blendState = attachmentInfo.ColorAttachmentDescriptions[i].BlendState.ToRefresh();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Refresh.GraphicsPipelineCreateInfo refreshGraphicsPipelineCreateInfo;
|
var vertexBindings = (SDL_Gpu.VertexBinding*) NativeMemory.Alloc(
|
||||||
|
(nuint) (graphicsPipelineCreateInfo.VertexInputState.VertexBindings.Length * Marshal.SizeOf<SDL_Gpu.VertexBinding>())
|
||||||
|
);
|
||||||
|
|
||||||
refreshGraphicsPipelineCreateInfo.blendConstants[0] = blendConstants.R;
|
for (var i = 0; i < graphicsPipelineCreateInfo.VertexInputState.VertexBindings.Length; i += 1)
|
||||||
refreshGraphicsPipelineCreateInfo.blendConstants[1] = blendConstants.G;
|
{
|
||||||
refreshGraphicsPipelineCreateInfo.blendConstants[2] = blendConstants.B;
|
vertexBindings[i] = graphicsPipelineCreateInfo.VertexInputState.VertexBindings[i].ToSDL();
|
||||||
refreshGraphicsPipelineCreateInfo.blendConstants[3] = blendConstants.A;
|
}
|
||||||
|
|
||||||
refreshGraphicsPipelineCreateInfo.depthStencilState.backStencilState = depthStencilState.BackStencilState.ToRefresh();
|
var colorAttachmentDescriptions = stackalloc SDL_Gpu.ColorAttachmentDescription[
|
||||||
refreshGraphicsPipelineCreateInfo.depthStencilState.compareOp = (Refresh.CompareOp) depthStencilState.CompareOp;
|
graphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions.Length
|
||||||
refreshGraphicsPipelineCreateInfo.depthStencilState.depthBoundsTestEnable = Conversions.BoolToByte(depthStencilState.DepthBoundsTestEnable);
|
];
|
||||||
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;
|
for (var i = 0; i < graphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions.Length; i += 1)
|
||||||
refreshGraphicsPipelineCreateInfo.vertexShaderInfo.shaderModule = vertexShaderInfo.ShaderModule.Handle;
|
{
|
||||||
refreshGraphicsPipelineCreateInfo.vertexShaderInfo.uniformBufferSize = vertexShaderInfo.UniformBufferSize;
|
colorAttachmentDescriptions[i].Format = (SDL_Gpu.TextureFormat) graphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions[i].Format;
|
||||||
refreshGraphicsPipelineCreateInfo.vertexShaderInfo.samplerBindingCount = vertexShaderInfo.SamplerBindingCount;
|
colorAttachmentDescriptions[i].BlendState = graphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions[i].BlendState.ToSDL();
|
||||||
|
}
|
||||||
|
|
||||||
refreshGraphicsPipelineCreateInfo.fragmentShaderInfo.entryPointName = fragmentShaderInfo.EntryPointName;
|
sdlGraphicsPipelineCreateInfo.VertexShader = graphicsPipelineCreateInfo.VertexShader.Handle;
|
||||||
refreshGraphicsPipelineCreateInfo.fragmentShaderInfo.shaderModule = fragmentShaderInfo.ShaderModule.Handle;
|
sdlGraphicsPipelineCreateInfo.FragmentShader = graphicsPipelineCreateInfo.FragmentShader.Handle;
|
||||||
refreshGraphicsPipelineCreateInfo.fragmentShaderInfo.uniformBufferSize = fragmentShaderInfo.UniformBufferSize;
|
|
||||||
refreshGraphicsPipelineCreateInfo.fragmentShaderInfo.samplerBindingCount = fragmentShaderInfo.SamplerBindingCount;
|
|
||||||
|
|
||||||
refreshGraphicsPipelineCreateInfo.multisampleState.multisampleCount = (Refresh.SampleCount) multisampleState.MultisampleCount;
|
sdlGraphicsPipelineCreateInfo.VertexInputState.VertexAttributes = vertexAttributes;
|
||||||
refreshGraphicsPipelineCreateInfo.multisampleState.sampleMask = multisampleState.SampleMask;
|
sdlGraphicsPipelineCreateInfo.VertexInputState.VertexAttributeCount = (uint) graphicsPipelineCreateInfo.VertexInputState.VertexAttributes.Length;
|
||||||
|
sdlGraphicsPipelineCreateInfo.VertexInputState.VertexBindings = vertexBindings;
|
||||||
|
sdlGraphicsPipelineCreateInfo.VertexInputState.VertexBindingCount = (uint) graphicsPipelineCreateInfo.VertexInputState.VertexBindings.Length;
|
||||||
|
|
||||||
refreshGraphicsPipelineCreateInfo.rasterizerState.cullMode = (Refresh.CullMode) rasterizerState.CullMode;
|
sdlGraphicsPipelineCreateInfo.PrimitiveType = (SDL_Gpu.PrimitiveType) graphicsPipelineCreateInfo.PrimitiveType;
|
||||||
refreshGraphicsPipelineCreateInfo.rasterizerState.depthBiasClamp = rasterizerState.DepthBiasClamp;
|
|
||||||
refreshGraphicsPipelineCreateInfo.rasterizerState.depthBiasConstantFactor = rasterizerState.DepthBiasConstantFactor;
|
|
||||||
refreshGraphicsPipelineCreateInfo.rasterizerState.depthBiasEnable = Conversions.BoolToByte(rasterizerState.DepthBiasEnable);
|
|
||||||
refreshGraphicsPipelineCreateInfo.rasterizerState.depthBiasSlopeFactor = rasterizerState.DepthBiasSlopeFactor;
|
|
||||||
refreshGraphicsPipelineCreateInfo.rasterizerState.fillMode = (Refresh.FillMode) rasterizerState.FillMode;
|
|
||||||
refreshGraphicsPipelineCreateInfo.rasterizerState.frontFace = (Refresh.FrontFace) rasterizerState.FrontFace;
|
|
||||||
|
|
||||||
refreshGraphicsPipelineCreateInfo.vertexInputState.vertexAttributes = vertexAttributesHandle.AddrOfPinnedObject();
|
sdlGraphicsPipelineCreateInfo.RasterizerState = graphicsPipelineCreateInfo.RasterizerState.ToSDL();
|
||||||
refreshGraphicsPipelineCreateInfo.vertexInputState.vertexAttributeCount = (uint) vertexInputState.VertexAttributes.Length;
|
sdlGraphicsPipelineCreateInfo.MultisampleState = graphicsPipelineCreateInfo.MultisampleState.ToSDL();
|
||||||
refreshGraphicsPipelineCreateInfo.vertexInputState.vertexBindings = vertexBindingsHandle.AddrOfPinnedObject();
|
sdlGraphicsPipelineCreateInfo.DepthStencilState = graphicsPipelineCreateInfo.DepthStencilState.ToSDL();
|
||||||
refreshGraphicsPipelineCreateInfo.vertexInputState.vertexBindingCount = (uint) vertexInputState.VertexBindings.Length;
|
|
||||||
|
|
||||||
refreshGraphicsPipelineCreateInfo.primitiveType = (Refresh.PrimitiveType) primitiveType;
|
sdlGraphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentCount = (uint) graphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions.Length;
|
||||||
|
sdlGraphicsPipelineCreateInfo.AttachmentInfo.ColorAttachmentDescriptions = colorAttachmentDescriptions;
|
||||||
|
sdlGraphicsPipelineCreateInfo.AttachmentInfo.DepthStencilFormat = (SDL_Gpu.TextureFormat) graphicsPipelineCreateInfo.AttachmentInfo.DepthStencilFormat;
|
||||||
|
sdlGraphicsPipelineCreateInfo.AttachmentInfo.HasDepthStencilAttachment = Conversions.BoolToInt(graphicsPipelineCreateInfo.AttachmentInfo.HasDepthStencilAttachment);
|
||||||
|
|
||||||
refreshGraphicsPipelineCreateInfo.attachmentInfo.colorAttachmentCount = (uint) attachmentInfo.ColorAttachmentDescriptions.Length;
|
sdlGraphicsPipelineCreateInfo.VertexResourceInfo = graphicsPipelineCreateInfo.VertexShaderResourceInfo.ToSDL();
|
||||||
refreshGraphicsPipelineCreateInfo.attachmentInfo.colorAttachmentDescriptions = (IntPtr) colorAttachmentDescriptions;
|
sdlGraphicsPipelineCreateInfo.FragmentResourceInfo = graphicsPipelineCreateInfo.FragmentShaderResourceInfo.ToSDL();
|
||||||
refreshGraphicsPipelineCreateInfo.attachmentInfo.depthStencilFormat = (Refresh.TextureFormat) attachmentInfo.DepthStencilFormat;
|
|
||||||
refreshGraphicsPipelineCreateInfo.attachmentInfo.hasDepthStencilAttachment = Conversions.BoolToByte(attachmentInfo.HasDepthStencilAttachment);
|
|
||||||
|
|
||||||
Handle = Refresh.Refresh_CreateGraphicsPipeline(device.Handle, refreshGraphicsPipelineCreateInfo);
|
sdlGraphicsPipelineCreateInfo.BlendConstants[0] = graphicsPipelineCreateInfo.BlendConstants.R;
|
||||||
|
sdlGraphicsPipelineCreateInfo.BlendConstants[1] = graphicsPipelineCreateInfo.BlendConstants.G;
|
||||||
|
sdlGraphicsPipelineCreateInfo.BlendConstants[2] = graphicsPipelineCreateInfo.BlendConstants.B;
|
||||||
|
sdlGraphicsPipelineCreateInfo.BlendConstants[3] = graphicsPipelineCreateInfo.BlendConstants.A;
|
||||||
|
|
||||||
|
Handle = SDL_Gpu.SDL_GpuCreateGraphicsPipeline(device.Handle, sdlGraphicsPipelineCreateInfo);
|
||||||
if (Handle == IntPtr.Zero)
|
if (Handle == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
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,24 +1,23 @@
|
||||||
using System;
|
using System;
|
||||||
using RefreshCS;
|
using SDL2_gpuCS;
|
||||||
|
|
||||||
namespace MoonWorks.Graphics
|
namespace MoonWorks.Graphics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A sampler specifies how a texture will be sampled in a shader.
|
||||||
|
/// </summary>
|
||||||
|
public class Sampler : SDL_GpuResource
|
||||||
{
|
{
|
||||||
/// <summary>
|
protected override Action<IntPtr, IntPtr> ReleaseFunction => SDL_Gpu.SDL_GpuReleaseSampler;
|
||||||
/// A sampler specifies how a texture will be sampled in a shader.
|
|
||||||
/// </summary>
|
|
||||||
public class Sampler : RefreshResource
|
|
||||||
{
|
|
||||||
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroySampler;
|
|
||||||
|
|
||||||
public Sampler(
|
public Sampler(
|
||||||
GraphicsDevice device,
|
GraphicsDevice device,
|
||||||
in SamplerCreateInfo samplerCreateInfo
|
in SamplerCreateInfo samplerCreateInfo
|
||||||
) : base(device)
|
) : base(device)
|
||||||
{
|
{
|
||||||
Handle = Refresh.Refresh_CreateSampler(
|
Handle = SDL_Gpu.SDL_GpuCreateSampler(
|
||||||
device.Handle,
|
device.Handle,
|
||||||
samplerCreateInfo.ToRefreshSamplerStateCreateInfo()
|
samplerCreateInfo.ToSDL()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
using SDL2_gpuCS;
|
||||||
|
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 : SDL_GpuResource
|
||||||
|
{
|
||||||
|
protected override Action<IntPtr, IntPtr> ReleaseFunction => SDL_Gpu.SDL_GpuReleaseShader;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
SDL_Gpu.ShaderCreateInfo shaderCreateInfo;
|
||||||
|
shaderCreateInfo.CodeSize = (nuint) stream.Length;
|
||||||
|
shaderCreateInfo.Code = (byte*) bytecodeBuffer;
|
||||||
|
shaderCreateInfo.EntryPointName = entryPointName;
|
||||||
|
shaderCreateInfo.Stage = (SDL_Gpu.ShaderStage) shaderStage;
|
||||||
|
shaderCreateInfo.Format = (SDL_Gpu.ShaderFormat) shaderFormat;
|
||||||
|
|
||||||
|
var shaderModule = SDL_Gpu.SDL_GpuCreateShader(
|
||||||
|
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,183 +1,47 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using SDL2_gpuCS;
|
||||||
using RefreshCS;
|
|
||||||
|
|
||||||
namespace MoonWorks.Graphics
|
namespace MoonWorks.Graphics
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A container for pixel data.
|
/// A container for pixel data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Texture : RefreshResource
|
public class Texture : SDL_GpuResource
|
||||||
{
|
{
|
||||||
public uint Width { get; internal set; }
|
public uint Width { get; internal set; }
|
||||||
public uint Height { get; internal set; }
|
public uint Height { get; internal set; }
|
||||||
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,
|
SDL_Gpu.SDL_GpuSetTextureName(
|
||||||
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 => SDL_Gpu.SDL_GpuReleaseTexture;
|
||||||
/// </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 +67,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 +78,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 +130,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 +160,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
|
||||||
|
@ -284,9 +179,9 @@ namespace MoonWorks.Graphics
|
||||||
in TextureCreateInfo textureCreateInfo
|
in TextureCreateInfo textureCreateInfo
|
||||||
) : base(device)
|
) : base(device)
|
||||||
{
|
{
|
||||||
Handle = Refresh.Refresh_CreateTexture(
|
Handle = SDL_Gpu.SDL_GpuCreateTexture(
|
||||||
device.Handle,
|
device.Handle,
|
||||||
textureCreateInfo.ToRefreshTextureCreateInfo()
|
textureCreateInfo.ToSDL()
|
||||||
);
|
);
|
||||||
|
|
||||||
Format = textureCreateInfo.Format;
|
Format = textureCreateInfo.Format;
|
||||||
|
@ -294,16 +189,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 +216,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 +230,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 +241,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 +263,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 +310,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 SDL2_gpuCS;
|
||||||
|
|
||||||
|
namespace MoonWorks.Graphics
|
||||||
|
{
|
||||||
|
public unsafe class TransferBuffer : SDL_GpuResource
|
||||||
|
{
|
||||||
|
protected override Action<IntPtr, IntPtr> ReleaseFunction => SDL_Gpu.SDL_GpuReleaseTransferBuffer;
|
||||||
|
|
||||||
|
/// <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 = SDL_Gpu.SDL_GpuCreateTransferBuffer(
|
||||||
|
device.Handle,
|
||||||
|
(SDL_Gpu.TransferUsage) usage,
|
||||||
|
(SDL_Gpu.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)
|
||||||
|
{
|
||||||
|
SDL_Gpu.SDL_GpuSetTransferData(
|
||||||
|
Device.Handle,
|
||||||
|
(nint) dataPtr,
|
||||||
|
Handle,
|
||||||
|
new SDL_Gpu.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)
|
||||||
|
{
|
||||||
|
SDL_Gpu.SDL_GpuGetTransferData(
|
||||||
|
Device.Handle,
|
||||||
|
Handle,
|
||||||
|
(nint) dataPtr,
|
||||||
|
new SDL_Gpu.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
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,16 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace MoonWorks.Graphics;
|
namespace MoonWorks.Graphics;
|
||||||
|
|
||||||
public abstract class RefreshResource : GraphicsResource
|
public abstract class SDL_GpuResource : 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 SDL_GpuResource(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);
|
|
@ -0,0 +1,941 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using SDL2_gpuCS;
|
||||||
|
|
||||||
|
namespace MoonWorks.Graphics;
|
||||||
|
|
||||||
|
// Recreate certain types in here so we can hide the SDL_GpuCS 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 SDL_Gpu.DepthStencilValue ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.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 SDL_Gpu.Rect ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.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 SDL_Gpu.Viewport ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.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 SDL_Gpu.VertexBinding ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.VertexBinding
|
||||||
|
{
|
||||||
|
Binding = Binding,
|
||||||
|
Stride = Stride,
|
||||||
|
InputRate = (SDL_Gpu.VertexInputRate) InputRate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct VertexAttribute
|
||||||
|
{
|
||||||
|
public uint Location;
|
||||||
|
public uint Binding;
|
||||||
|
public VertexElementFormat Format;
|
||||||
|
public uint Offset;
|
||||||
|
|
||||||
|
public SDL_Gpu.VertexAttribute ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.VertexAttribute
|
||||||
|
{
|
||||||
|
Location = Location,
|
||||||
|
Binding = Binding,
|
||||||
|
Format = (SDL_Gpu.VertexElementFormat) Format,
|
||||||
|
Offset = Offset
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct StencilOpState
|
||||||
|
{
|
||||||
|
public StencilOp FailOp;
|
||||||
|
public StencilOp PassOp;
|
||||||
|
public StencilOp DepthFailOp;
|
||||||
|
public CompareOp CompareOp;
|
||||||
|
|
||||||
|
public SDL_Gpu.StencilOpState ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.StencilOpState
|
||||||
|
{
|
||||||
|
FailOp = (SDL_Gpu.StencilOp) FailOp,
|
||||||
|
PassOp = (SDL_Gpu.StencilOp) PassOp,
|
||||||
|
DepthFailOp = (SDL_Gpu.StencilOp) DepthFailOp,
|
||||||
|
CompareOp = (SDL_Gpu.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 SDL_Gpu.ColorAttachmentInfo ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.ColorAttachmentInfo
|
||||||
|
{
|
||||||
|
TextureSlice = TextureSlice.ToSDL(),
|
||||||
|
ClearColor = new SDL_Gpu.Color
|
||||||
|
{
|
||||||
|
R = ClearColor.R / 255f,
|
||||||
|
G = ClearColor.G / 255f,
|
||||||
|
B = ClearColor.B / 255f,
|
||||||
|
A = ClearColor.A / 255f
|
||||||
|
},
|
||||||
|
LoadOp = (SDL_Gpu.LoadOp) LoadOp,
|
||||||
|
StoreOp = (SDL_Gpu.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 SDL_Gpu.DepthStencilAttachmentInfo ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.DepthStencilAttachmentInfo
|
||||||
|
{
|
||||||
|
TextureSlice = TextureSlice.ToSDL(),
|
||||||
|
DepthStencilClearValue = DepthStencilClearValue.ToSDL(),
|
||||||
|
LoadOp = (SDL_Gpu.LoadOp) LoadOp,
|
||||||
|
StoreOp = (SDL_Gpu.StoreOp) StoreOp,
|
||||||
|
StencilLoadOp = (SDL_Gpu.LoadOp) StencilLoadOp,
|
||||||
|
StencilStoreOp = (SDL_Gpu.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 SDL_Gpu.BufferCopy ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.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 SDL_Gpu.BufferImageCopy ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.BufferImageCopy
|
||||||
|
{
|
||||||
|
BufferOffset = BufferOffset,
|
||||||
|
BufferStride = BufferStride,
|
||||||
|
BufferImageHeight = BufferImageHeight
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct GraphicsPipelineResourceInfo(
|
||||||
|
uint SamplerCount,
|
||||||
|
uint StorageBufferCount,
|
||||||
|
uint StorageTextureCount,
|
||||||
|
uint UniformBufferCount
|
||||||
|
) {
|
||||||
|
public SDL_Gpu.GraphicsPipelineResourceInfo ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.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 SDL_Gpu.ComputePipelineResourceInfo ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.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 SDL_Gpu.BufferBinding ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.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 SDL_Gpu.TextureSamplerBinding ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.TextureSamplerBinding
|
||||||
|
{
|
||||||
|
Texture = Texture.Handle,
|
||||||
|
Sampler = Sampler.Handle
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct StorageBufferReadWriteBinding(
|
||||||
|
GpuBuffer Buffer,
|
||||||
|
bool cycle
|
||||||
|
) {
|
||||||
|
public SDL_Gpu.StorageBufferReadWriteBinding ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.StorageBufferReadWriteBinding
|
||||||
|
{
|
||||||
|
Buffer = Buffer.Handle,
|
||||||
|
Cycle = Conversions.BoolToInt(cycle)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct StorageTextureReadWriteBinding(
|
||||||
|
in TextureSlice TextureSlice,
|
||||||
|
bool cycle
|
||||||
|
) {
|
||||||
|
public SDL_Gpu.StorageTextureReadWriteBinding ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.StorageTextureReadWriteBinding
|
||||||
|
{
|
||||||
|
TextureSlice = TextureSlice.ToSDL(),
|
||||||
|
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;
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
using RefreshCS;
|
using SDL2_gpuCS;
|
||||||
|
|
||||||
namespace MoonWorks.Graphics
|
namespace MoonWorks.Graphics
|
||||||
{
|
{
|
||||||
|
@ -106,18 +106,18 @@ namespace MoonWorks.Graphics
|
||||||
ColorWriteMask = ColorComponentFlags.None
|
ColorWriteMask = ColorComponentFlags.None
|
||||||
};
|
};
|
||||||
|
|
||||||
public Refresh.ColorAttachmentBlendState ToRefresh()
|
public SDL_Gpu.ColorAttachmentBlendState ToSDL()
|
||||||
{
|
{
|
||||||
return new Refresh.ColorAttachmentBlendState
|
return new SDL_Gpu.ColorAttachmentBlendState
|
||||||
{
|
{
|
||||||
blendEnable = Conversions.BoolToByte(BlendEnable),
|
BlendEnable = Conversions.BoolToInt(BlendEnable),
|
||||||
alphaBlendOp = (Refresh.BlendOp) AlphaBlendOp,
|
AlphaBlendOp = (SDL_Gpu.BlendOp) AlphaBlendOp,
|
||||||
colorBlendOp = (Refresh.BlendOp) ColorBlendOp,
|
ColorBlendOp = (SDL_Gpu.BlendOp) ColorBlendOp,
|
||||||
colorWriteMask = (Refresh.ColorComponentFlags) ColorWriteMask,
|
ColorWriteMask = (SDL_Gpu.ColorComponentFlags) ColorWriteMask,
|
||||||
destinationAlphaBlendFactor = (Refresh.BlendFactor) DestinationAlphaBlendFactor,
|
DestinationAlphaBlendFactor = (SDL_Gpu.BlendFactor) DestinationAlphaBlendFactor,
|
||||||
destinationColorBlendFactor = (Refresh.BlendFactor) DestinationColorBlendFactor,
|
DestinationColorBlendFactor = (SDL_Gpu.BlendFactor) DestinationColorBlendFactor,
|
||||||
sourceAlphaBlendFactor = (Refresh.BlendFactor) SourceAlphaBlendFactor,
|
SourceAlphaBlendFactor = (SDL_Gpu.BlendFactor) SourceAlphaBlendFactor,
|
||||||
sourceColorBlendFactor = (Refresh.BlendFactor) SourceColorBlendFactor
|
SourceColorBlendFactor = (SDL_Gpu.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 SDL2_gpuCS;
|
||||||
|
|
||||||
|
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 SDL_Gpu.DepthStencilState ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.DepthStencilState
|
||||||
|
{
|
||||||
|
DepthTestEnable = Conversions.BoolToInt(DepthTestEnable),
|
||||||
|
BackStencilState = BackStencilState.ToSDL(),
|
||||||
|
FrontStencilState = FrontStencilState.ToSDL(),
|
||||||
|
CompareMask = CompareMask,
|
||||||
|
WriteMask = WriteMask,
|
||||||
|
Reference = Reference,
|
||||||
|
CompareOp = (SDL_Gpu.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 SDL2_gpuCS;
|
||||||
|
|
||||||
|
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 SDL_Gpu.MultisampleState ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.MultisampleState
|
||||||
|
{
|
||||||
|
MultisampleCount = (SDL_Gpu.SampleCount) MultisampleCount,
|
||||||
|
SampleMask = SampleMask
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
namespace MoonWorks.Graphics
|
using SDL2_gpuCS;
|
||||||
|
|
||||||
|
namespace MoonWorks.Graphics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies how the rasterizer should be configured for a graphics pipeline.
|
||||||
|
/// </summary>
|
||||||
|
public struct RasterizerState
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Specifies how the rasterizer should be configured for a graphics pipeline.
|
|
||||||
/// </summary>
|
|
||||||
public struct RasterizerState
|
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies whether front faces, back faces, none, or both should be culled.
|
/// Specifies whether front faces, back faces, none, or both should be culled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -103,5 +105,18 @@
|
||||||
FillMode = FillMode.Line,
|
FillMode = FillMode.Line,
|
||||||
DepthBiasEnable = false
|
DepthBiasEnable = false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public SDL_Gpu.RasterizerState ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.RasterizerState
|
||||||
|
{
|
||||||
|
CullMode = (SDL_Gpu.CullMode) CullMode,
|
||||||
|
DepthBiasClamp = DepthBiasClamp,
|
||||||
|
DepthBiasConstantFactor = DepthBiasConstantFactor,
|
||||||
|
DepthBiasEnable = Conversions.BoolToInt(DepthBiasEnable),
|
||||||
|
DepthBiasSlopeFactor = DepthBiasSlopeFactor,
|
||||||
|
FillMode = (SDL_Gpu.FillMode) FillMode,
|
||||||
|
FrontFace = (SDL_Gpu.FrontFace) FrontFace
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
using RefreshCS;
|
using SDL2_gpuCS;
|
||||||
|
|
||||||
namespace MoonWorks.Graphics
|
namespace MoonWorks.Graphics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All of the information that is used to create a sampler.
|
||||||
|
/// </summary>
|
||||||
|
public struct SamplerCreateInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// All of the information that is used to create a sampler.
|
|
||||||
/// </summary>
|
|
||||||
public struct SamplerCreateInfo
|
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Minification filter mode. Used when the image is downscaled.
|
/// Minification filter mode. Used when the image is downscaled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -150,25 +150,24 @@ namespace MoonWorks.Graphics
|
||||||
MaxLod = 1000
|
MaxLod = 1000
|
||||||
};
|
};
|
||||||
|
|
||||||
public Refresh.SamplerStateCreateInfo ToRefreshSamplerStateCreateInfo()
|
public SDL_Gpu.SamplerCreateInfo ToSDL()
|
||||||
{
|
{
|
||||||
return new Refresh.SamplerStateCreateInfo
|
return new SDL_Gpu.SamplerCreateInfo
|
||||||
{
|
{
|
||||||
minFilter = (Refresh.Filter) MinFilter,
|
MinFilter = (SDL_Gpu.Filter) MinFilter,
|
||||||
magFilter = (Refresh.Filter) MagFilter,
|
MagFilter = (SDL_Gpu.Filter) MagFilter,
|
||||||
mipmapMode = (Refresh.SamplerMipmapMode) MipmapMode,
|
MipmapMode = (SDL_Gpu.SamplerMipmapMode) MipmapMode,
|
||||||
addressModeU = (Refresh.SamplerAddressMode) AddressModeU,
|
AddressModeU = (SDL_Gpu.SamplerAddressMode) AddressModeU,
|
||||||
addressModeV = (Refresh.SamplerAddressMode) AddressModeV,
|
AddressModeV = (SDL_Gpu.SamplerAddressMode) AddressModeV,
|
||||||
addressModeW = (Refresh.SamplerAddressMode) AddressModeW,
|
AddressModeW = (SDL_Gpu.SamplerAddressMode) AddressModeW,
|
||||||
mipLodBias = MipLodBias,
|
MipLodBias = MipLodBias,
|
||||||
anisotropyEnable = Conversions.BoolToByte(AnisotropyEnable),
|
AnisotropyEnable = Conversions.BoolToByte(AnisotropyEnable),
|
||||||
maxAnisotropy = MaxAnisotropy,
|
MaxAnisotropy = MaxAnisotropy,
|
||||||
compareEnable = Conversions.BoolToByte(CompareEnable),
|
CompareEnable = Conversions.BoolToByte(CompareEnable),
|
||||||
compareOp = (Refresh.CompareOp) CompareOp,
|
CompareOp = (SDL_Gpu.CompareOp) CompareOp,
|
||||||
minLod = MinLod,
|
MinLod = MinLod,
|
||||||
maxLod = MaxLod,
|
MaxLod = MaxLod,
|
||||||
borderColor = (Refresh.BorderColor) BorderColor
|
BorderColor = (SDL_Gpu.BorderColor) BorderColor
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using RefreshCS;
|
using SDL2_gpuCS;
|
||||||
|
|
||||||
namespace MoonWorks.Graphics
|
namespace MoonWorks.Graphics
|
||||||
{
|
{
|
||||||
|
@ -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 SDL_Gpu.TextureCreateInfo ToSDL()
|
||||||
{
|
{
|
||||||
return new Refresh.TextureCreateInfo
|
return new SDL_Gpu.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 = (SDL_Gpu.SampleCount) SampleCount,
|
||||||
usageFlags = (Refresh.TextureUsageFlags) UsageFlags
|
Format = (SDL_Gpu.TextureFormat) Format,
|
||||||
|
UsageFlags = (SDL_Gpu.TextureUsageFlags) UsageFlags
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,12 @@
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout (location = 0) in vec2 TexCoord;
|
||||||
|
|
||||||
|
layout (location = 0) out vec4 FragColor;
|
||||||
|
|
||||||
|
layout (binding = 0, set = 1) uniform sampler2D TexSampler;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
FragColor = texture(TexSampler, TexCoord);
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
using SDL2_gpuCS;
|
||||||
|
|
||||||
|
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 SDL_Gpu.TextureRegion ToSDL()
|
||||||
|
{
|
||||||
|
return new SDL_Gpu.TextureRegion
|
||||||
|
{
|
||||||
|
TextureSlice = TextureSlice.ToSDL(),
|
||||||
|
X = X,
|
||||||
|
Y = Y,
|
||||||
|
Z = Z,
|
||||||
|
W = Width,
|
||||||
|
H = Height,
|
||||||
|
D = Depth
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,57 +1,31 @@
|
||||||
using RefreshCS;
|
using SDL2_gpuCS;
|
||||||
|
namespace MoonWorks.Graphics;
|
||||||
|
|
||||||
namespace MoonWorks.Graphics
|
/// <summary>
|
||||||
|
/// A texture slice specifies a subresource of a texture.
|
||||||
|
/// </summary>
|
||||||
|
public struct TextureSlice
|
||||||
{
|
{
|
||||||
/// <summary>
|
public Texture Texture;
|
||||||
/// A texture slice specifies a subregion of a texture.
|
public uint MipLevel;
|
||||||
/// Many operations can use texture slices in place of textures for the sake of convenience.
|
public uint Layer;
|
||||||
/// </summary>
|
|
||||||
public struct TextureSlice
|
|
||||||
{
|
|
||||||
public Texture Texture { get; }
|
|
||||||
public Rect Rectangle { get; }
|
|
||||||
public uint Depth { get; }
|
|
||||||
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 SDL_Gpu.TextureSlice ToSDL()
|
||||||
{
|
{
|
||||||
Texture = texture;
|
return new SDL_Gpu.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;
|
||||||
|
|
|
@ -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,90 @@ namespace MoonWorks.Video
|
||||||
|
|
||||||
private void UpdateRenderTexture()
|
private void UpdateRenderTexture()
|
||||||
{
|
{
|
||||||
lock (CurrentStream)
|
uint uOffset;
|
||||||
{
|
uint vOffset;
|
||||||
var commandBuffer = GraphicsDevice.AcquireCommandBuffer();
|
uint yStride;
|
||||||
|
uint uvStride;
|
||||||
|
|
||||||
commandBuffer.SetTextureDataYUV(
|
lock (Stream)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (TransferBuffer == null || TransferBuffer.Size < ySpan.Length + uSpan.Length + vSpan.Length)
|
||||||
|
{
|
||||||
|
TransferBuffer?.Dispose();
|
||||||
|
TransferBuffer = new TransferBuffer(Device, TransferUsage.Texture, (uint) (ySpan.Length + uSpan.Length + vSpan.Length));
|
||||||
|
}
|
||||||
|
TransferBuffer.SetData(ySpan, 0, TransferOptions.Cycle);
|
||||||
|
TransferBuffer.SetData(uSpan, (uint) ySpan.Length, TransferOptions.Unsafe);
|
||||||
|
TransferBuffer.SetData(vSpan, (uint) (ySpan.Length + uSpan.Length), TransferOptions.Unsafe);
|
||||||
|
|
||||||
|
uOffset = (uint) ySpan.Length;
|
||||||
|
vOffset = (uint) (ySpan.Length + vSpan.Length);
|
||||||
|
|
||||||
|
yStride = Stream.yStride;
|
||||||
|
uvStride = Stream.uvStride;
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandBuffer = Device.AcquireCommandBuffer();
|
||||||
|
|
||||||
|
commandBuffer.BeginCopyPass();
|
||||||
|
|
||||||
|
commandBuffer.UploadToTexture(
|
||||||
|
TransferBuffer,
|
||||||
yTexture,
|
yTexture,
|
||||||
uTexture,
|
new BufferImageCopy
|
||||||
vTexture,
|
{
|
||||||
CurrentStream.yDataHandle,
|
BufferOffset = 0,
|
||||||
CurrentStream.uDataHandle,
|
BufferStride = yStride,
|
||||||
CurrentStream.vDataHandle,
|
BufferImageHeight = yTexture.Height
|
||||||
CurrentStream.yDataLength,
|
},
|
||||||
CurrentStream.uvDataLength,
|
WriteOptions.Cycle
|
||||||
CurrentStream.yStride,
|
|
||||||
CurrentStream.uvStride
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
commandBuffer.UploadToTexture(
|
||||||
|
TransferBuffer,
|
||||||
|
uTexture,
|
||||||
|
new BufferImageCopy{
|
||||||
|
BufferOffset = uOffset,
|
||||||
|
BufferStride = uvStride,
|
||||||
|
BufferImageHeight = uTexture.Height
|
||||||
|
},
|
||||||
|
WriteOptions.Cycle
|
||||||
|
);
|
||||||
|
|
||||||
|
commandBuffer.UploadToTexture(
|
||||||
|
TransferBuffer,
|
||||||
|
vTexture,
|
||||||
|
new BufferImageCopy
|
||||||
|
{
|
||||||
|
BufferOffset = vOffset,
|
||||||
|
BufferStride = uvStride,
|
||||||
|
BufferImageHeight = vTexture.Height
|
||||||
|
},
|
||||||
|
WriteOptions.Cycle
|
||||||
|
);
|
||||||
|
|
||||||
|
commandBuffer.EndCopyPass();
|
||||||
|
|
||||||
commandBuffer.BeginRenderPass(
|
commandBuffer.BeginRenderPass(
|
||||||
new ColorAttachmentInfo(RenderTexture, Color.Black)
|
new ColorAttachmentInfo(RenderTexture, WriteOptions.Cycle, Color.Black)
|
||||||
);
|
);
|
||||||
|
|
||||||
commandBuffer.BindGraphicsPipeline(GraphicsDevice.VideoPipeline);
|
commandBuffer.BindGraphicsPipeline(Device.VideoPipeline);
|
||||||
commandBuffer.BindFragmentSamplers(
|
commandBuffer.BindFragmentSamplers(
|
||||||
new TextureSamplerBinding(yTexture, LinearSampler),
|
new TextureSamplerBinding(yTexture, LinearSampler),
|
||||||
new TextureSamplerBinding(uTexture, LinearSampler),
|
new TextureSamplerBinding(uTexture, LinearSampler),
|
||||||
new TextureSamplerBinding(vTexture, LinearSampler)
|
new TextureSamplerBinding(vTexture, LinearSampler)
|
||||||
);
|
);
|
||||||
|
|
||||||
commandBuffer.DrawPrimitives(0, 1, 0, 0);
|
commandBuffer.DrawPrimitives(0, 1);
|
||||||
|
|
||||||
commandBuffer.EndRenderPass();
|
commandBuffer.EndRenderPass();
|
||||||
|
|
||||||
GraphicsDevice.Submit(commandBuffer);
|
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 +338,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,18 @@ 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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>();
|
||||||
|
@ -113,6 +123,14 @@ namespace MoonWorks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the window position.
|
||||||
|
/// </summary>
|
||||||
|
public void SetPosition(int x, int y)
|
||||||
|
{
|
||||||
|
SDL.SDL_SetWindowPosition(Handle, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
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,7 @@
|
||||||
uint windowWidth,
|
uint windowWidth,
|
||||||
uint windowHeight,
|
uint windowHeight,
|
||||||
ScreenMode screenMode,
|
ScreenMode screenMode,
|
||||||
PresentMode presentMode,
|
Graphics.PresentMode presentMode,
|
||||||
bool systemResizable = false,
|
bool systemResizable = false,
|
||||||
bool startMaximized = false
|
bool startMaximized = false
|
||||||
) {
|
) {
|
||||||
|
|
Loading…
Reference in New Issue