diff --git a/lib/RefreshCS b/lib/RefreshCS
index b5325e6..ad0b168 160000
--- a/lib/RefreshCS
+++ b/lib/RefreshCS
@@ -1 +1 @@
-Subproject commit b5325e6d0329eeb35b074091a569a5f679852d28
+Subproject commit ad0b168c4794ee0377c825b0149a0d21bd856e43
diff --git a/src/Graphics/Bindings/BufferBinding.cs b/src/Graphics/Bindings/BufferBinding.cs
index 4e318b6..c64f767 100644
--- a/src/Graphics/Bindings/BufferBinding.cs
+++ b/src/Graphics/Bindings/BufferBinding.cs
@@ -5,10 +5,10 @@
///
public struct BufferBinding
{
- public Buffer Buffer;
+ public GpuBuffer Buffer;
public ulong Offset;
- public BufferBinding(Buffer buffer, ulong offset)
+ public BufferBinding(GpuBuffer buffer, ulong offset)
{
Buffer = buffer;
Offset = offset;
diff --git a/src/Graphics/Bindings/TextureLevelBinding.cs b/src/Graphics/Bindings/TextureLevelBinding.cs
new file mode 100644
index 0000000..82f8b87
--- /dev/null
+++ b/src/Graphics/Bindings/TextureLevelBinding.cs
@@ -0,0 +1,17 @@
+namespace MoonWorks.Graphics
+{
+ ///
+ /// A texture-level pair to be used when binding compute textures.
+ ///
+ public struct TextureLevelBinding
+ {
+ public Texture Texture;
+ public uint MipLevel;
+
+ public TextureLevelBinding(Texture texture, uint mipLevel = 0)
+ {
+ Texture = texture;
+ MipLevel = mipLevel;
+ }
+ }
+}
diff --git a/src/Graphics/CommandBuffer.cs b/src/Graphics/CommandBuffer.cs
index 8378ebb..cf1f534 100644
--- a/src/Graphics/CommandBuffer.cs
+++ b/src/Graphics/CommandBuffer.cs
@@ -29,6 +29,10 @@ namespace MoonWorks.Graphics
SampleCount depthStencilAttachmentSampleCount;
TextureFormat depthStencilFormat;
+ bool copyPassActive;
+
+ bool computePassActive;
+
internal bool Submitted;
#endif
@@ -65,6 +69,9 @@ namespace MoonWorks.Graphics
colorFormatFour = TextureFormat.R8G8B8A8;
depthStencilFormat = TextureFormat.D16;
+ copyPassActive = false;
+ computePassActive = false;
+
Submitted = false;
}
#endif
@@ -72,7 +79,7 @@ namespace MoonWorks.Graphics
///
/// Begins a render pass.
/// All render state, resource binding, and draw commands must be made within a render pass.
- /// It is an error to call this after calling BeginRenderPass but before calling EndRenderPass.
+ /// It is an error to call this during any kind of pass.
///
/// The color attachment to use in the render pass.
public unsafe void BeginRenderPass(
@@ -545,7 +552,7 @@ namespace MoonWorks.Graphics
///
/// A buffer to bind.
public unsafe void BindComputeBuffers(
- Buffer buffer
+ GpuBuffer buffer
) {
#if DEBUG
AssertNotSubmitted();
@@ -569,8 +576,8 @@ namespace MoonWorks.Graphics
/// A buffer to bind.
/// A buffer to bind.
public unsafe void BindComputeBuffers(
- Buffer bufferOne,
- Buffer bufferTwo
+ GpuBuffer bufferOne,
+ GpuBuffer bufferTwo
) {
#if DEBUG
AssertNotSubmitted();
@@ -596,9 +603,9 @@ namespace MoonWorks.Graphics
/// A buffer to bind.
/// A buffer to bind.
public unsafe void BindComputeBuffers(
- Buffer bufferOne,
- Buffer bufferTwo,
- Buffer bufferThree
+ GpuBuffer bufferOne,
+ GpuBuffer bufferTwo,
+ GpuBuffer bufferThree
) {
#if DEBUG
AssertNotSubmitted();
@@ -626,10 +633,10 @@ namespace MoonWorks.Graphics
/// A buffer to bind.
/// A buffer to bind.
public unsafe void BindComputeBuffers(
- Buffer bufferOne,
- Buffer bufferTwo,
- Buffer bufferThree,
- Buffer bufferFour
+ GpuBuffer bufferOne,
+ GpuBuffer bufferTwo,
+ GpuBuffer bufferThree,
+ GpuBuffer bufferFour
) {
#if DEBUG
AssertNotSubmitted();
@@ -655,7 +662,7 @@ namespace MoonWorks.Graphics
///
/// A Span of buffers to bind.
public unsafe void BindComputeBuffers(
- in Span buffers
+ in Span buffers
) {
#if DEBUG
AssertNotSubmitted();
@@ -680,9 +687,9 @@ namespace MoonWorks.Graphics
///
/// Binds a texture to be used in the compute shader.
///
- /// A texture to bind.
+ /// A texture-level pair to bind.
public unsafe void BindComputeTextures(
- Texture texture
+ TextureLevelBinding binding
) {
#if DEBUG
AssertNotSubmitted();
@@ -691,23 +698,27 @@ namespace MoonWorks.Graphics
#endif
var texturePtrs = stackalloc IntPtr[1];
- texturePtrs[0] = texture.Handle;
+ texturePtrs[0] = binding.Texture.Handle;
+
+ var mipLevels = stackalloc uint[1];
+ mipLevels[0] = binding.MipLevel;
Refresh.Refresh_BindComputeTextures(
Device.Handle,
Handle,
- (IntPtr) texturePtrs
+ (IntPtr) texturePtrs,
+ (IntPtr) mipLevels
);
}
///
/// Binds textures to be used in the compute shader.
///
- /// A texture to bind.
- /// A texture to bind.
+ /// A texture-level pair to bind.
+ /// A texture-level pair to bind.
public unsafe void BindComputeTextures(
- Texture textureOne,
- Texture textureTwo
+ TextureLevelBinding bindingOne,
+ TextureLevelBinding bindingTwo
) {
#if DEBUG
AssertNotSubmitted();
@@ -716,26 +727,31 @@ namespace MoonWorks.Graphics
#endif
var texturePtrs = stackalloc IntPtr[2];
- texturePtrs[0] = textureOne.Handle;
- texturePtrs[1] = textureTwo.Handle;
+ texturePtrs[0] = bindingOne.Texture.Handle;
+ texturePtrs[1] = bindingTwo.Texture.Handle;
+
+ var mipLevels = stackalloc uint[2];
+ mipLevels[0] = bindingOne.MipLevel;
+ mipLevels[1] = bindingTwo.MipLevel;
Refresh.Refresh_BindComputeTextures(
Device.Handle,
Handle,
- (IntPtr) texturePtrs
+ (IntPtr) texturePtrs,
+ (IntPtr) mipLevels
);
}
///
/// Binds textures to be used in the compute shader.
///
- /// A texture to bind.
- /// A texture to bind.
- /// A texture to bind.
+ /// A texture-level pair to bind.
+ /// A texture-level pair to bind.
+ /// A texture-level pair to bind.
public unsafe void BindComputeTextures(
- Texture textureOne,
- Texture textureTwo,
- Texture textureThree
+ TextureLevelBinding bindingOne,
+ TextureLevelBinding bindingTwo,
+ TextureLevelBinding bindingThree
) {
#if DEBUG
AssertNotSubmitted();
@@ -744,29 +760,35 @@ namespace MoonWorks.Graphics
#endif
var texturePtrs = stackalloc IntPtr[3];
- texturePtrs[0] = textureOne.Handle;
- texturePtrs[1] = textureTwo.Handle;
- texturePtrs[2] = textureThree.Handle;
+ texturePtrs[0] = bindingOne.Texture.Handle;
+ texturePtrs[1] = bindingTwo.Texture.Handle;
+ texturePtrs[2] = bindingThree.Texture.Handle;
+
+ var mipLevels = stackalloc uint[3];
+ mipLevels[0] = bindingOne.MipLevel;
+ mipLevels[1] = bindingTwo.MipLevel;
+ mipLevels[2] = bindingThree.MipLevel;
Refresh.Refresh_BindComputeTextures(
Device.Handle,
Handle,
- (IntPtr) texturePtrs
+ (IntPtr) texturePtrs,
+ (IntPtr) mipLevels
);
}
///
/// Binds textures to be used in the compute shader.
///
- /// A texture to bind.
- /// A texture to bind.
- /// A texture to bind.
- /// A texture to bind.
+ /// A texture-level pair to bind.
+ /// A texture-level pair to bind.
+ /// A texture-level pair to bind.
+ /// A texture-level pair to bind.
public unsafe void BindComputeTextures(
- Texture textureOne,
- Texture textureTwo,
- Texture textureThree,
- Texture textureFour
+ TextureLevelBinding bindingOne,
+ TextureLevelBinding bindingTwo,
+ TextureLevelBinding bindingThree,
+ TextureLevelBinding bindingFour
) {
#if DEBUG
AssertNotSubmitted();
@@ -775,42 +797,52 @@ namespace MoonWorks.Graphics
#endif
var texturePtrs = stackalloc IntPtr[4];
- texturePtrs[0] = textureOne.Handle;
- texturePtrs[1] = textureTwo.Handle;
- texturePtrs[2] = textureThree.Handle;
- texturePtrs[3] = textureFour.Handle;
+ texturePtrs[0] = bindingOne.Texture.Handle;
+ texturePtrs[1] = bindingTwo.Texture.Handle;
+ texturePtrs[2] = bindingThree.Texture.Handle;
+ texturePtrs[3] = bindingFour.Texture.Handle;
+
+ var mipLevels = stackalloc uint[4];
+ mipLevels[0] = bindingOne.MipLevel;
+ mipLevels[1] = bindingTwo.MipLevel;
+ mipLevels[2] = bindingThree.MipLevel;
+ mipLevels[3] = bindingFour.MipLevel;
Refresh.Refresh_BindComputeTextures(
Device.Handle,
Handle,
- (IntPtr) texturePtrs
+ (IntPtr) texturePtrs,
+ (IntPtr) mipLevels
);
}
///
/// Binds textures to be used in the compute shader.
///
- /// A set of textures to bind.
+ /// A set of texture-level pairs to bind.
public unsafe void BindComputeTextures(
- in Span textures
+ in Span bindings
) {
#if DEBUG
AssertNotSubmitted();
AssertComputePipelineBound();
- AssertComputeTextureCount(textures.Length);
+ AssertComputeTextureCount(bindings.Length);
#endif
- var texturePtrs = stackalloc IntPtr[textures.Length];
+ var texturePtrs = stackalloc IntPtr[bindings.Length];
+ var mipLevels = stackalloc uint[bindings.Length];
- for (var i = 0; i < textures.Length; i += 1)
+ for (var i = 0; i < bindings.Length; i += 1)
{
- texturePtrs[i] = textures[i].Handle;
+ texturePtrs[i] = bindings[i].Texture.Handle;
+ mipLevels[i] = bindings[i].MipLevel;
}
Refresh.Refresh_BindComputeTextures(
Device.Handle,
Handle,
- (IntPtr) texturePtrs
+ (IntPtr) texturePtrs,
+ (IntPtr) mipLevels
);
}
@@ -824,8 +856,7 @@ namespace MoonWorks.Graphics
public void DispatchCompute(
uint groupCountX,
uint groupCountY,
- uint groupCountZ,
- uint computeParamOffset
+ uint groupCountZ
) {
#if DEBUG
AssertNotSubmitted();
@@ -842,8 +873,7 @@ namespace MoonWorks.Graphics
Handle,
groupCountX,
groupCountY,
- groupCountZ,
- computeParamOffset
+ groupCountZ
);
}
@@ -1113,7 +1143,7 @@ namespace MoonWorks.Graphics
/// The size in bytes of the index buffer elements.
/// The offset index for the buffer.
public void BindIndexBuffer(
- Buffer indexBuffer,
+ GpuBuffer indexBuffer,
IndexElementSize indexElementSize,
uint offset = 0
)
@@ -1515,7 +1545,7 @@ namespace MoonWorks.Graphics
/// Pushes vertex shader uniforms to the device.
///
/// A starting offset value to be used with draw calls.
- public unsafe uint PushVertexShaderUniforms(
+ public unsafe void PushVertexShaderUniforms(
void* uniformsPtr,
uint size
) {
@@ -1527,9 +1557,14 @@ namespace MoonWorks.Graphics
{
throw new InvalidOperationException("The current vertex shader does not take a uniform buffer!");
}
+
+ if (currentGraphicsPipeline.VertexShaderInfo.UniformBufferSize != size)
+ {
+ throw new InvalidOperationException("Vertex uniform data size mismatch!");
+ }
#endif
- return Refresh.Refresh_PushVertexShaderUniforms(
+ Refresh.Refresh_PushVertexShaderUniforms(
Device.Handle,
Handle,
(IntPtr) uniformsPtr,
@@ -1541,13 +1576,13 @@ namespace MoonWorks.Graphics
/// Pushes vertex shader uniforms to the device.
///
/// A starting offset value to be used with draw calls.
- public unsafe uint PushVertexShaderUniforms(
+ public unsafe void PushVertexShaderUniforms(
in T uniforms
) where T : unmanaged
{
fixed (T* uniformsPtr = &uniforms)
{
- return PushVertexShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf());
+ PushVertexShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf());
}
}
@@ -1555,7 +1590,7 @@ namespace MoonWorks.Graphics
/// Pushes fragment shader uniforms to the device.
///
/// A starting offset to be used with draw calls.
- public unsafe uint PushFragmentShaderUniforms(
+ public unsafe void PushFragmentShaderUniforms(
void* uniformsPtr,
uint size
) {
@@ -1567,9 +1602,14 @@ namespace MoonWorks.Graphics
{
throw new InvalidOperationException("The current fragment shader does not take a uniform buffer!");
}
+
+ if (currentGraphicsPipeline.FragmentShaderInfo.UniformBufferSize != size)
+ {
+ throw new InvalidOperationException("Fragment uniform data size mismatch!");
+ }
#endif
- return Refresh.Refresh_PushFragmentShaderUniforms(
+ Refresh.Refresh_PushFragmentShaderUniforms(
Device.Handle,
Handle,
(IntPtr) uniformsPtr,
@@ -1581,13 +1621,13 @@ namespace MoonWorks.Graphics
/// Pushes fragment shader uniforms to the device.
///
/// A starting offset to be used with draw calls.
- public unsafe uint PushFragmentShaderUniforms(
+ public unsafe void PushFragmentShaderUniforms(
in T uniforms
) where T : unmanaged
{
fixed (T* uniformsPtr = &uniforms)
{
- return PushFragmentShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf());
+ PushFragmentShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf());
}
}
@@ -1595,7 +1635,7 @@ namespace MoonWorks.Graphics
/// Pushes compute shader uniforms to the device.
///
/// A starting offset to be used with dispatch calls.
- public unsafe uint PushComputeShaderUniforms(
+ public unsafe void PushComputeShaderUniforms(
void* uniformsPtr,
uint size
) {
@@ -1607,9 +1647,14 @@ namespace MoonWorks.Graphics
{
throw new System.InvalidOperationException("The current compute shader does not take a uniform buffer!");
}
+
+ if (currentComputePipeline.ComputeShaderInfo.UniformBufferSize != size)
+ {
+ throw new InvalidOperationException("Compute uniform data size mismatch!");
+ }
#endif
- return Refresh.Refresh_PushComputeShaderUniforms(
+ Refresh.Refresh_PushComputeShaderUniforms(
Device.Handle,
Handle,
(IntPtr) uniformsPtr,
@@ -1621,13 +1666,13 @@ namespace MoonWorks.Graphics
/// Pushes compute shader uniforms to the device.
///
/// A starting offset to be used with dispatch calls.
- public unsafe uint PushComputeShaderUniforms(
+ public unsafe void PushComputeShaderUniforms(
in T uniforms
) where T : unmanaged
{
fixed (T* uniformsPtr = &uniforms)
{
- return PushComputeShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf());
+ PushComputeShaderUniforms(uniformsPtr, (uint) Marshal.SizeOf());
}
}
@@ -1638,15 +1683,11 @@ namespace MoonWorks.Graphics
/// The starting index offset for the index buffer.
/// The number of primitives to draw.
/// The number of instances to draw.
- /// An offset value obtained from PushVertexShaderUniforms. If no uniforms are required then use 0.
- /// An offset value obtained from PushFragmentShaderUniforms. If no uniforms are required the use 0.
public void DrawInstancedPrimitives(
uint baseVertex,
uint startIndex,
uint primitiveCount,
- uint instanceCount,
- uint vertexParamOffset,
- uint fragmentParamOffset
+ uint instanceCount
)
{
#if DEBUG
@@ -1660,9 +1701,7 @@ namespace MoonWorks.Graphics
baseVertex,
startIndex,
primitiveCount,
- instanceCount,
- vertexParamOffset,
- fragmentParamOffset
+ instanceCount
);
}
@@ -1672,14 +1711,10 @@ namespace MoonWorks.Graphics
/// The starting index offset for the vertex buffer.
/// The starting index offset for the index buffer.
/// The number of primitives to draw.
- /// An offset value obtained from PushVertexShaderUniforms. If no uniforms are required then use 0.
- /// An offset value obtained from PushFragmentShaderUniforms. If no uniforms are required the use 0.
public void DrawIndexedPrimitives(
uint baseVertex,
uint startIndex,
- uint primitiveCount,
- uint vertexParamOffset,
- uint fragmentParamOffset
+ uint primitiveCount
)
{
#if DEBUG
@@ -1692,9 +1727,7 @@ namespace MoonWorks.Graphics
Handle,
baseVertex,
startIndex,
- primitiveCount,
- vertexParamOffset,
- fragmentParamOffset
+ primitiveCount
);
}
@@ -1703,13 +1736,9 @@ namespace MoonWorks.Graphics
///
///
///
- ///
- ///
public void DrawPrimitives(
uint vertexStart,
- uint primitiveCount,
- uint vertexParamOffset,
- uint fragmentParamOffset
+ uint primitiveCount
)
{
#if DEBUG
@@ -1721,9 +1750,7 @@ namespace MoonWorks.Graphics
Device.Handle,
Handle,
vertexStart,
- primitiveCount,
- vertexParamOffset,
- fragmentParamOffset
+ primitiveCount
);
}
@@ -1737,12 +1764,10 @@ namespace MoonWorks.Graphics
/// An offset value obtained from PushVertexShaderUniforms. If no uniforms are required then use 0.
/// An offset value obtained from PushFragmentShaderUniforms. If no uniforms are required the use 0.
public void DrawPrimitivesIndirect(
- Buffer buffer,
+ GpuBuffer buffer,
uint offsetInBytes,
uint drawCount,
- uint stride,
- uint vertexParamOffset,
- uint fragmentParamOffset
+ uint stride
)
{
#if DEBUG
@@ -1756,9 +1781,7 @@ namespace MoonWorks.Graphics
buffer.Handle,
offsetInBytes,
drawCount,
- stride,
- vertexParamOffset,
- fragmentParamOffset
+ stride
);
}
@@ -1834,352 +1857,350 @@ namespace MoonWorks.Graphics
return window.SwapchainTexture;
}
+ // Copy Pass
+
///
- /// Copies array data into a buffer.
+ /// Begins a copy pass.
+ /// All copy commands must be made within a copy pass.
+ /// It is an error to call this during any kind of pass.
///
- /// The buffer to copy to.
- /// The array to copy from.
- /// Specifies where in the buffer to start copying.
- /// Specifies whether the buffer should be copied in immediate or deferred mode. When in doubt, use deferred.
- public unsafe void SetBufferData(
- Buffer buffer,
- Span data,
- uint bufferOffsetInBytes = 0
- ) where T : unmanaged
+ public void BeginCopyPass()
{
- SetBufferData(
- buffer,
- data,
- bufferOffsetInBytes,
- 0,
- (uint) data.Length
+#if DEBUG
+ AssertNotSubmitted();
+ AssertNotInPass("Cannot begin copy pass while in another pass!");
+ copyPassActive = true;
+#endif
+
+ Refresh.Refresh_BeginCopyPass(
+ Device.Handle,
+ Handle
);
}
///
- /// Copies array data into a buffer.
+ /// Uploads data from a CpuBuffer to a TextureSlice.
+ /// This copy occurs on the GPU timeline.
+ ///
+ /// Overwriting the contents of the CpuBuffer before the command buffer
+ /// has finished execution will cause undefined behavior.
+ ///
+ /// You MAY assume that the copy has finished for subsequent commands.
///
- /// The buffer to copy to.
- /// The array to copy from.
- /// Specifies where in the buffer to start copying.
- /// Specifies whether the buffer should be copied in immediate or deferred mode. When in doubt, use deferred.
- public unsafe void SetBufferData(
- Buffer buffer,
- T[] data,
- uint bufferOffsetInBytes = 0
- ) where T : unmanaged
- {
- SetBufferData(
- buffer,
- new Span(data),
- bufferOffsetInBytes,
- 0,
- (uint) data.Length
- );
- }
-
- ///
- /// Copies arbitrary data into a buffer.
- ///
- /// The buffer to copy into.
- /// Pointer to the data to copy into the buffer.
- /// Specifies where in the buffer to copy data.
- /// The length of data that should be copied.
- /// Specifies whether the buffer should be copied in immediate or deferred mode. When in doubt, use deferred.
- public void SetBufferData(
- Buffer buffer,
- IntPtr dataPtr,
- uint bufferOffsetInBytes,
- uint dataLengthInBytes
+ public void UploadToTexture(
+ CpuBuffer cpuBuffer,
+ in TextureSlice textureSlice,
+ in BufferImageCopy copyParams
)
{
#if DEBUG
AssertNotSubmitted();
- AssertRenderPassInactive("Cannot copy during render pass!");
- AssertNonEmptyCopy(dataLengthInBytes);
- AssertBufferBoundsCheck(buffer.Size, bufferOffsetInBytes, dataLengthInBytes);
+ AssertInCopyPass("Cannot upload to texture outside of copy pass!");
#endif
- Refresh.Refresh_SetBufferData(
+ Refresh.Refresh_UploadToTexture(
Device.Handle,
Handle,
- buffer.Handle,
- bufferOffsetInBytes,
- dataPtr,
- dataLengthInBytes
+ cpuBuffer.Handle,
+ textureSlice.ToRefreshTextureSlice(),
+ copyParams.ToRefresh()
);
}
///
- /// Copies array data into a buffer.
+ /// Uploads the contents of an entire buffer to a texture with no mips.
///
- /// The buffer to copy to.
- /// The span to copy from.
- /// Specifies where in the buffer to start copying.
- /// The index of the first element to copy from the array.
- /// How many elements to copy.
- /// Specifies whether the buffer should be copied in immediate or deferred mode. When in doubt, use deferred.
- public unsafe void SetBufferData(
- Buffer buffer,
- Span data,
- uint bufferOffsetInBytes,
- uint startElement,
- uint numElements
- ) where T : unmanaged
- {
- var elementSize = Marshal.SizeOf();
- var dataLengthInBytes = (uint) (elementSize * numElements);
- var dataOffsetInBytes = (int) startElement * elementSize;
-
-#if DEBUG
- AssertNotSubmitted();
- AssertRenderPassInactive("Cannot copy during render pass!");
- AssertNonEmptyCopy(dataLengthInBytes);
- AssertBufferBoundsCheck(buffer.Size, bufferOffsetInBytes, dataLengthInBytes);
-#endif
-
- fixed (T* ptr = data)
- {
- Refresh.Refresh_SetBufferData(
- Device.Handle,
- Handle,
- buffer.Handle,
- bufferOffsetInBytes,
- (IntPtr) ptr + dataOffsetInBytes,
- dataLengthInBytes
- );
- }
- }
-
- ///
- /// Copies array data into a buffer.
- ///
- /// The buffer to copy to.
- /// The span to copy from.
- /// Specifies where in the buffer to start copying.
- /// The index of the first element to copy from the array.
- /// How many elements to copy.
- /// Specifies whether the buffer should be copied in immediate or deferred mode. When in doubt, use deferred.
- public unsafe void SetBufferData(
- Buffer buffer,
- T[] data,
- uint bufferOffsetInBytes,
- uint startElement,
- uint numElements
- ) where T : unmanaged
- {
- SetBufferData(buffer, new Span(data), bufferOffsetInBytes, startElement, numElements);
- }
-
- public unsafe void SetBufferData(
- Buffer buffer,
- IntPtr dataPtr,
- uint bufferOffsetInElements,
- uint numElements
- ) where T : unmanaged
- {
- var elementSize = Marshal.SizeOf();
- var dataLengthInBytes = (uint) (elementSize * numElements);
- var offsetLengthInBytes = (uint) elementSize * bufferOffsetInElements;
-
-#if DEBUG
- AssertNotSubmitted();
- AssertRenderPassInactive("Cannot copy during render pass!");
- AssertNonEmptyCopy((uint) (elementSize * numElements));
- AssertBufferBoundsCheck(buffer.Size, offsetLengthInBytes, dataLengthInBytes);
-#endif
-
- Refresh.Refresh_SetBufferData(
- Device.Handle,
- Handle,
- buffer.Handle,
- offsetLengthInBytes,
- dataPtr,
- dataLengthInBytes
+ public void UploadToTexture(
+ CpuBuffer cpuBuffer,
+ Texture texture
+ ) {
+ UploadToTexture(
+ cpuBuffer,
+ new TextureSlice(texture),
+ new BufferImageCopy(0, 0, 0)
);
}
///
- /// Asynchronously copies data into a texture.
+ /// Uploads data from a CpuBuffer to a GpuBuffer.
+ /// This copy occurs on the GPU timeline.
+ ///
+ /// Overwriting the contents of the CpuBuffer before the command buffer
+ /// has finished execution will cause undefined behavior.
+ ///
+ /// You MAY assume that the copy has finished for subsequent commands.
///
- /// A span of data to copy into the texture.
- public unsafe void SetTextureData(Texture texture, Span data) where T : unmanaged
- {
- SetTextureData(new TextureSlice(texture), data);
- }
-
- ///
- /// Asynchronously copies data into a texture.
- ///
- /// An array of data to copy into the texture.
- public unsafe void SetTextureData(Texture texture, T[] data) where T : unmanaged
- {
- SetTextureData(new TextureSlice(texture), new Span(data));
- }
-
- ///
- /// Asynchronously copies data into a texture slice.
- ///
- /// The texture slice to copy into.
- /// A span of data to copy into the texture.
- public unsafe void SetTextureData(in TextureSlice textureSlice, Span data) where T : unmanaged
- {
- var dataLengthInBytes = (uint) (data.Length * Marshal.SizeOf());
-
+ public void UploadToBuffer(
+ CpuBuffer cpuBuffer,
+ GpuBuffer gpuBuffer,
+ in BufferCopy copyParams
+ ) {
#if DEBUG
AssertNotSubmitted();
- AssertRenderPassInactive("Cannot copy during render pass!");
- AssertTextureBoundsCheck(textureSlice.Size, dataLengthInBytes);
+ AssertInCopyPass("Cannot upload to texture outside of copy pass!");
#endif
- fixed (T* ptr = data)
+ Refresh.Refresh_UploadToBuffer(
+ Device.Handle,
+ Handle,
+ cpuBuffer.Handle,
+ gpuBuffer.Handle,
+ copyParams.ToRefresh()
+ );
+ }
+
+ ///
+ /// Copies the entire contents of a CpuBuffer to a GpuBuffer.
+ ///
+ public void UploadToBuffer(
+ CpuBuffer cpuBuffer,
+ GpuBuffer gpuBuffer
+ ) {
+#if DEBUG
+ if (cpuBuffer.Size > gpuBuffer.Size)
{
- Refresh.Refresh_SetTextureData(
- Device.Handle,
- Handle,
- textureSlice.ToRefreshTextureSlice(),
- (IntPtr) ptr,
- dataLengthInBytes
- );
+ throw new InvalidOperationException("CpuBuffer copying to GpuBuffer is too large!");
}
+#endif
+ UploadToBuffer(
+ cpuBuffer,
+ gpuBuffer,
+ new BufferCopy(0, 0, cpuBuffer.Size)
+ );
}
///
- /// Asynchronously copies data into a texture slice.
+ /// Downloads data from a Texture to a CpuBuffer.
+ /// This copy occurs on the GPU timeline.
+ ///
+ /// You MAY NOT assume that the data in the CpuBuffer is
+ /// fully copied until the command buffer has finished execution.
///
- /// The texture slice to copy into.
- /// An array of data to copy into the texture.
- public unsafe void SetTextureData(in TextureSlice textureSlice, T[] data) where T : unmanaged
- {
- SetTextureData(textureSlice, new Span(data));
- }
-
- ///
- /// Asynchronously copies data into a texture slice.
- ///
- /// The texture slice to copy into.
- /// A pointer to an array of data to copy from.
- /// The amount of data to copy from the array.
- public void SetTextureData(in TextureSlice textureSlice, IntPtr dataPtr, uint dataLengthInBytes)
- {
+ public void DownloadFromTexture(
+ in TextureSlice textureSlice,
+ CpuBuffer cpuBuffer,
+ in BufferImageCopy copyParams
+ ) {
#if DEBUG
AssertNotSubmitted();
- AssertRenderPassInactive("Cannot copy during render pass!");
- AssertTextureBoundsCheck(textureSlice.Size, dataLengthInBytes);
+ AssertInCopyPass("Cannot download from texture outside of copy pass!");
#endif
- Refresh.Refresh_SetTextureData(
+ Refresh.Refresh_DownloadFromTexture(
Device.Handle,
Handle,
textureSlice.ToRefreshTextureSlice(),
- dataPtr,
- dataLengthInBytes
+ cpuBuffer.Handle,
+ copyParams.ToRefresh()
);
}
///
- /// Asynchronously copies data into a texture.
+ /// Downloads the contents of a Texture with no mips into a CpuBuffer.
///
- /// A pointer to an array of data to copy from.
- /// The amount of data to copy from the array.
- public void SetTextureData(Texture texture, IntPtr dataPtr, uint dataLengthInBytes)
- {
- SetTextureData(new TextureSlice(texture), dataPtr, dataLengthInBytes);
+ public void DownloadFromTexture(
+ Texture texture,
+ CpuBuffer cpuBuffer
+ ) {
+ DownloadFromTexture(
+ new TextureSlice(texture),
+ cpuBuffer,
+ new BufferImageCopy(0, 0, 0)
+ );
}
///
- /// Asynchronously copies YUV data into three textures. Use with compressed video.
+ /// Downloads data from a GpuBuffer to a CpuBuffer.
+ /// This copy occurs on the GPU timeline.
+ ///
+ /// You MAY NOT assume that the data in the CpuBuffer is
+ /// fully copied until the command buffer has finished execution.
///
- public void SetTextureDataYUV(
- Texture yTexture,
- Texture uTexture,
- Texture vTexture,
- IntPtr yDataPtr,
- IntPtr uDataPtr,
- IntPtr vDataPtr,
- uint yDataLengthInBytes,
- uint uvDataLengthInBytes,
- uint yStride,
- uint uvStride)
- {
+ public void DownloadFromBuffer(
+ GpuBuffer gpuBuffer,
+ CpuBuffer cpuBuffer,
+ in BufferCopy copyParams
+ ) {
#if DEBUG
AssertNotSubmitted();
- AssertRenderPassInactive("Cannot copy during render pass!");
+ AssertInCopyPass("Cannot download from texture outside of copy pass!");
#endif
- Refresh.Refresh_SetTextureDataYUV(
+ Refresh.Refresh_DownloadFromBuffer(
Device.Handle,
Handle,
- yTexture.Handle,
- uTexture.Handle,
- vTexture.Handle,
- yTexture.Width,
- yTexture.Height,
- uTexture.Width,
- uTexture.Height,
- yDataPtr,
- uDataPtr,
- vDataPtr,
- yDataLengthInBytes,
- uvDataLengthInBytes,
- yStride,
- uvStride
+ gpuBuffer.Handle,
+ cpuBuffer.Handle,
+ copyParams.ToRefresh()
);
}
///
- /// Performs an asynchronous texture-to-texture copy on the GPU.
+ /// Copies the contents of a TextureSlice to another TextureSlice.
+ /// The slices must have the same dimensions.
+ /// This copy occurs on the GPU timeline.
+ ///
+ /// You MAY assume that the copy has finished in subsequent commands.
///
- /// The texture slice to copy from.
- /// The texture slice to copy to.
- /// The filter to use if the sizes of the texture slices differ.
public void CopyTextureToTexture(
- in TextureSlice sourceTextureSlice,
- in TextureSlice destinationTextureSlice,
- Filter filter
- )
- {
+ in TextureSlice source,
+ in TextureSlice destination
+ ) {
#if DEBUG
AssertNotSubmitted();
- AssertRenderPassInactive("Cannot copy during render pass!");
+ AssertInCopyPass("Cannot download from texture outside of copy pass!");
#endif
- var sourceRefreshTextureSlice = sourceTextureSlice.ToRefreshTextureSlice();
- var destRefreshTextureSlice = destinationTextureSlice.ToRefreshTextureSlice();
-
Refresh.Refresh_CopyTextureToTexture(
Device.Handle,
Handle,
- sourceRefreshTextureSlice,
- destRefreshTextureSlice,
- (Refresh.Filter) filter
+ source.ToRefreshTextureSlice(),
+ destination.ToRefreshTextureSlice()
);
}
///
- /// Performs an asynchronous texture-to-buffer copy.
- /// Note that the buffer is not guaranteed to be filled until you call GraphicsDevice.Wait()
+ /// Copies the contents of an entire Texture with no mips to another Texture with no mips.
+ /// The textures must have the same dimensions.
+ ///
+ public void CopyTextureToTexture(
+ Texture source,
+ Texture destination
+ ) {
+ CopyTextureToTexture(
+ new TextureSlice(source),
+ new TextureSlice(destination)
+ );
+ }
+
+ ///
+ /// Copies the contents of a Texture to a GpuBuffer.
+ /// This copy occurs on the GPU timeline.
+ ///
+ /// You MAY assume that the copy has finished in subsequent commands.
///
- ///
- ///
public void CopyTextureToBuffer(
in TextureSlice textureSlice,
- Buffer buffer
- )
- {
+ GpuBuffer buffer,
+ in BufferImageCopy copyParams
+ ) {
#if DEBUG
AssertNotSubmitted();
- AssertRenderPassInactive("Cannot copy during render pass!");
- AssertBufferBoundsCheck(buffer.Size, 0, textureSlice.Size);
+ AssertInCopyPass("Cannot download from texture outside of copy pass!");
#endif
- var refreshTextureSlice = textureSlice.ToRefreshTextureSlice();
-
Refresh.Refresh_CopyTextureToBuffer(
Device.Handle,
Handle,
- refreshTextureSlice,
- buffer.Handle
+ textureSlice.ToRefreshTextureSlice(),
+ buffer.Handle,
+ copyParams.ToRefresh()
+ );
+ }
+
+ ///
+ /// Copies the entire contents of a Texture to a GpuBuffer.
+ ///
+ public void CopyTextureToBuffer(
+ Texture texture,
+ GpuBuffer buffer
+ ) {
+ CopyTextureToBuffer(
+ new TextureSlice(texture),
+ buffer,
+ new BufferImageCopy(0, 0, 0)
+ );
+ }
+
+ ///
+ /// Copies the contents of a GpuBuffer to a Texture.
+ /// This copy occurs on the GPU timeline.
+ ///
+ /// You MAY assume that the copy has finished in subsequent commands.
+ ///
+ public void CopyBufferToTexture(
+ GpuBuffer buffer,
+ in TextureSlice textureSlice,
+ in BufferImageCopy copyParams
+ ) {
+#if DEBUG
+ AssertNotSubmitted();
+ AssertInCopyPass("Cannot download from texture outside of copy pass!");
+#endif
+
+ Refresh.Refresh_CopyBufferToTexture(
+ Device.Handle,
+ Handle,
+ buffer.Handle,
+ textureSlice.ToRefreshTextureSlice(),
+ copyParams.ToRefresh()
+ );
+ }
+
+ ///
+ /// Copies the entire contents of a Texture with no mips to a GpuBuffer.
+ ///
+ public void CopyBufferToTexture(
+ GpuBuffer buffer,
+ Texture texture
+ ) {
+ CopyBufferToTexture(
+ buffer,
+ new TextureSlice(texture),
+ new BufferImageCopy(0, 0, 0)
+ );
+ }
+
+ ///
+ /// 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.
+ ///
+ public void CopyBufferToBuffer(
+ GpuBuffer source,
+ GpuBuffer destination,
+ in BufferCopy copyParams
+ ) {
+#if DEBUG
+ AssertNotSubmitted();
+ AssertInCopyPass("Cannot download from texture outside of copy pass!");
+#endif
+
+ Refresh.Refresh_CopyBufferToBuffer(
+ Device.Handle,
+ Handle,
+ source.Handle,
+ destination.Handle,
+ copyParams.ToRefresh()
+ );
+ }
+
+ ///
+ /// Copies the entire contents of a GpuBuffer to another GpuBuffer.
+ ///
+ public void CopyBufferToBuffer(
+ GpuBuffer source,
+ GpuBuffer destination
+ ) {
+ CopyBufferToBuffer(
+ source,
+ destination,
+ new BufferCopy(0, 0, source.Size)
+ );
+ }
+
+ public void EndCopyPass()
+ {
+#if DEBUG
+ AssertNotSubmitted();
+ AssertInCopyPass("Cannot end copy pass while not in a copy pass!");
+ copyPassActive = false;
+#endif
+
+ Refresh.Refresh_EndCopyPass(
+ Device.Handle,
+ Handle
);
}
@@ -2376,6 +2397,30 @@ namespace MoonWorks.Graphics
}
}
+ private void AssertNotInPass(string message)
+ {
+ if (renderPassActive || copyPassActive || computePassActive)
+ {
+ throw new System.InvalidOperationException(message);
+ }
+ }
+
+ private void AssertInRenderPass(string message)
+ {
+ if (!renderPassActive)
+ {
+ throw new System.InvalidOperationException(message);
+ }
+ }
+
+ private void AssertInCopyPass(string message)
+ {
+ if (!copyPassActive)
+ {
+ throw new System.InvalidOperationException(message);
+ }
+ }
+
private void AssertNotSubmitted()
{
if (Submitted)
diff --git a/src/Graphics/Font/Font.cs b/src/Graphics/Font/Font.cs
index 0dd6d1c..109badb 100644
--- a/src/Graphics/Font/Font.cs
+++ b/src/Graphics/Font/Font.cs
@@ -47,8 +47,26 @@ namespace MoonWorks.Graphics.Font
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 texture = Texture.CreateTexture2D(graphicsDevice, width, height, TextureFormat.R8G8B8A8, TextureUsageFlags.Sampler);
+ var cpuBuffer = new CpuBuffer(graphicsDevice, sizeInBytes);
+ ImageUtils.DecodeIntoCpuBuffer(
+ imagePath,
+ cpuBuffer,
+ 0,
+ SetDataOptions.Overwrite
+ );
+
+ commandBuffer.BeginCopyPass();
+ commandBuffer.UploadToTexture(
+ cpuBuffer,
+ texture
+ );
+ commandBuffer.EndCopyPass();
+
+ cpuBuffer.Dispose();
NativeMemory.Free(fontFileByteBuffer);
NativeMemory.Free(atlasFileByteBuffer);
diff --git a/src/Graphics/Font/TextBatch.cs b/src/Graphics/Font/TextBatch.cs
index 9cb909c..3dd301b 100644
--- a/src/Graphics/Font/TextBatch.cs
+++ b/src/Graphics/Font/TextBatch.cs
@@ -13,10 +13,12 @@ namespace MoonWorks.Graphics.Font
private GraphicsDevice GraphicsDevice { get; }
public IntPtr Handle { get; }
- public Buffer VertexBuffer { get; protected set; } = null;
- public Buffer IndexBuffer { get; protected set; } = null;
+ public GpuBuffer VertexBuffer { get; protected set; } = null;
+ public GpuBuffer IndexBuffer { get; protected set; } = null;
public uint PrimitiveCount { get; protected set; }
+ private CpuBuffer TransferBuffer;
+
public Font CurrentFont { get; private set; }
private byte* StringBytes;
@@ -30,8 +32,10 @@ namespace MoonWorks.Graphics.Font
StringBytesLength = 128;
StringBytes = (byte*) NativeMemory.Alloc((nuint) StringBytesLength);
- VertexBuffer = Buffer.Create(GraphicsDevice, BufferUsageFlags.Vertex, INITIAL_VERTEX_COUNT);
- IndexBuffer = Buffer.Create(GraphicsDevice, BufferUsageFlags.Index, INITIAL_INDEX_COUNT);
+ VertexBuffer = GpuBuffer.Create(GraphicsDevice, BufferUsageFlags.Vertex, INITIAL_VERTEX_COUNT);
+ IndexBuffer = GpuBuffer.Create(GraphicsDevice, BufferUsageFlags.Index, INITIAL_INDEX_COUNT);
+
+ TransferBuffer = CpuBuffer.Create(GraphicsDevice, VertexBuffer.Size + IndexBuffer.Size);
}
// Call this to initialize or reset the batch.
@@ -93,22 +97,38 @@ namespace MoonWorks.Graphics.Font
out uint indexDataLengthInBytes
);
+ var vertexSpan = new Span((void*) vertexDataPointer, (int) vertexDataLengthInBytes);
+ var indexSpan = new Span((void*) indexDataPointer, (int) indexDataLengthInBytes);
+
+ var newTransferBufferNeeded = false;
+
if (VertexBuffer.Size < vertexDataLengthInBytes)
{
VertexBuffer.Dispose();
- VertexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Vertex, vertexDataLengthInBytes);
+ VertexBuffer = new GpuBuffer(GraphicsDevice, BufferUsageFlags.Vertex, vertexDataLengthInBytes);
+ newTransferBufferNeeded = true;
}
if (IndexBuffer.Size < indexDataLengthInBytes)
{
IndexBuffer.Dispose();
- IndexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Index, vertexDataLengthInBytes);
+ IndexBuffer = new GpuBuffer(GraphicsDevice, BufferUsageFlags.Index, vertexDataLengthInBytes);
+ newTransferBufferNeeded = true;
+ }
+
+ if (newTransferBufferNeeded)
+ {
+ TransferBuffer.Dispose();
+ TransferBuffer = new CpuBuffer(GraphicsDevice, VertexBuffer.Size + IndexBuffer.Size);
}
if (vertexDataLengthInBytes > 0 && indexDataLengthInBytes > 0)
{
- commandBuffer.SetBufferData(VertexBuffer, vertexDataPointer, 0, vertexDataLengthInBytes);
- commandBuffer.SetBufferData(IndexBuffer, indexDataPointer, 0, indexDataLengthInBytes);
+ TransferBuffer.SetData(vertexSpan, SetDataOptions.Discard);
+ TransferBuffer.SetData(indexSpan, (uint) vertexSpan.Length, SetDataOptions.Overwrite);
+
+ commandBuffer.UploadToBuffer(TransferBuffer, VertexBuffer, new BufferCopy(0, 0, (uint) vertexSpan.Length));
+ commandBuffer.UploadToBuffer(TransferBuffer, IndexBuffer, new BufferCopy((uint) vertexSpan.Length, 0, (uint) indexSpan.Length));
}
PrimitiveCount = vertexCount / 2;
@@ -123,12 +143,12 @@ namespace MoonWorks.Graphics.Font
));
commandBuffer.BindVertexBuffers(VertexBuffer);
commandBuffer.BindIndexBuffer(IndexBuffer, IndexElementSize.ThirtyTwo);
+ commandBuffer.PushVertexShaderUniforms(transformMatrix);
+ commandBuffer.PushFragmentShaderUniforms(CurrentFont.DistanceRange);
commandBuffer.DrawIndexedPrimitives(
0,
0,
- PrimitiveCount,
- commandBuffer.PushVertexShaderUniforms(transformMatrix),
- commandBuffer.PushFragmentShaderUniforms(CurrentFont.DistanceRange)
+ PrimitiveCount
);
}
diff --git a/src/Graphics/ImageUtils.cs b/src/Graphics/ImageUtils.cs
new file mode 100644
index 0000000..8d108bf
--- /dev/null
+++ b/src/Graphics/ImageUtils.cs
@@ -0,0 +1,233 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using RefreshCS;
+
+namespace MoonWorks.Graphics
+{
+ public static class ImageUtils
+ {
+ ///
+ /// Gets pointer to pixel data from compressed image byte data.
+ ///
+ /// The returned pointer must be freed by calling FreePixelData.
+ ///
+ public static unsafe IntPtr GetPixelDataFromBytes(
+ Span 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;
+ }
+ }
+
+ ///
+ /// Gets pointer to pixel data from a compressed image stream.
+ ///
+ /// The returned pointer must be freed by calling FreePixelData.
+ ///
+ 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(buffer, (int) length);
+ stream.ReadExactly(span);
+
+ var pixelData = GetPixelDataFromBytes(span, out width, out height, out sizeInBytes);
+
+ NativeMemory.Free(buffer);
+
+ return pixelData;
+ }
+
+ ///
+ /// Gets pointer to pixel data from a compressed image file.
+ ///
+ /// The returned pointer must be freed by calling FreePixelData.
+ ///
+ 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);
+ }
+
+ ///
+ /// Get metadata from compressed image bytes.
+ ///
+ public static unsafe bool ImageInfoFromBytes(
+ Span 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);
+ }
+ }
+
+ ///
+ /// Get metadata from a compressed image stream.
+ ///
+ 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(buffer, (int) length);
+ stream.ReadExactly(span);
+
+ var result = ImageInfoFromBytes(span, out width, out height, out sizeInBytes);
+
+ NativeMemory.Free(buffer);
+
+ return result;
+ }
+
+ ///
+ /// Get metadata from a compressed image file.
+ ///
+ 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);
+ }
+
+ ///
+ /// Frees pixel data obtained from GetPixelData methods.
+ ///
+ public static void FreePixelData(IntPtr pixels)
+ {
+ Refresh.Refresh_Image_Free(pixels);
+ }
+
+ ///
+ /// Decodes image data into a CpuBuffer to prepare for image upload.
+ ///
+ public static unsafe uint DecodeIntoCpuBuffer(
+ Span data,
+ CpuBuffer cpuBuffer,
+ uint bufferOffsetInBytes,
+ SetDataOptions option
+ ) {
+ var pixelData = GetPixelDataFromBytes(data, out var w, out var h, out var sizeInBytes);
+ var length = cpuBuffer.SetData(new Span((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option);
+ FreePixelData(pixelData);
+ return length;
+ }
+
+ ///
+ /// Decodes an image stream into a CpuBuffer to prepare for image upload.
+ ///
+ public static unsafe uint DecodeIntoCpuBuffer(
+ Stream stream,
+ CpuBuffer cpuBuffer,
+ uint bufferOffsetInBytes,
+ SetDataOptions option
+ ) {
+ var pixelData = GetPixelDataFromStream(stream, out var w, out var h, out var sizeInBytes);
+ var length = cpuBuffer.SetData(new Span((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option);
+ FreePixelData(pixelData);
+ return length;
+ }
+
+ ///
+ /// Decodes an image file into a CpuBuffer to prepare for image upload.
+ ///
+ public static unsafe uint DecodeIntoCpuBuffer(
+ string path,
+ CpuBuffer cpuBuffer,
+ uint bufferOffsetInBytes,
+ SetDataOptions option
+ ) {
+ var pixelData = GetPixelDataFromFile(path, out var w, out var h, out var sizeInBytes);
+ var length = cpuBuffer.SetData(new Span((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option);
+ FreePixelData(pixelData);
+ return length;
+ }
+
+ ///
+ /// Saves pixel data contained in a CpuBuffer to a PNG file.
+ ///
+ public static unsafe void SavePNG(
+ string path,
+ CpuBuffer cpuBuffer,
+ uint bufferOffsetInBytes,
+ int width,
+ int height,
+ bool bgra
+ ) {
+ var sizeInBytes = width * height * 4;
+
+ var pixelsPtr = NativeMemory.Alloc((nuint) sizeInBytes);
+ var pixelsSpan = new Span(pixelsPtr, sizeInBytes);
+
+ cpuBuffer.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(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);
+ }
+ }
+}
diff --git a/src/Graphics/RefreshEnums.cs b/src/Graphics/RefreshEnums.cs
index 81cc353..e773f26 100644
--- a/src/Graphics/RefreshEnums.cs
+++ b/src/Graphics/RefreshEnums.cs
@@ -297,6 +297,12 @@ namespace MoonWorks.Graphics
IntOpaqueWhite
}
+ public enum SetDataOptions
+ {
+ Discard,
+ Overwrite
+ }
+
public enum Backend
{
DontCare,
diff --git a/src/Graphics/RefreshStructs.cs b/src/Graphics/RefreshStructs.cs
index 6b046bd..acd60b7 100644
--- a/src/Graphics/RefreshStructs.cs
+++ b/src/Graphics/RefreshStructs.cs
@@ -354,4 +354,60 @@ namespace MoonWorks.Graphics
FirstInstance = firstInstance;
}
}
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct BufferCopy
+ {
+ public uint SrcOffset;
+ public uint DstOffset;
+ public uint Size;
+
+ public BufferCopy(
+ uint srcOffset,
+ uint dstOffset,
+ uint size
+ ) {
+ SrcOffset = srcOffset;
+ DstOffset = dstOffset;
+ Size = size;
+ }
+
+ public Refresh.BufferCopy ToRefresh()
+ {
+ return new Refresh.BufferCopy
+ {
+ srcOffset = SrcOffset,
+ dstOffset = DstOffset,
+ size = Size
+ };
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct BufferImageCopy
+ {
+ public uint BufferOffset;
+ public uint BufferStride; // if 0, image assumed to be tightly packed
+ public uint BufferImageHeight; // if 0, image assumed to be tightly packed
+
+ public BufferImageCopy(
+ uint bufferOffset,
+ uint bufferStride,
+ uint bufferImageHeight
+ ) {
+ BufferOffset = bufferOffset;
+ BufferStride = bufferStride;
+ BufferImageHeight = bufferImageHeight;
+ }
+
+ public Refresh.BufferImageCopy ToRefresh()
+ {
+ return new Refresh.BufferImageCopy
+ {
+ bufferOffset = BufferOffset,
+ bufferStride = BufferStride,
+ bufferImageHeight = BufferImageHeight
+ };
+ }
+ }
}
diff --git a/src/Graphics/ResourceInitializer.cs b/src/Graphics/ResourceInitializer.cs
new file mode 100644
index 0000000..31298a5
--- /dev/null
+++ b/src/Graphics/ResourceInitializer.cs
@@ -0,0 +1,171 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace MoonWorks.Graphics
+{
+ ///
+ /// A convenience structure for simultaneously creating resources and uploading their data.
+ ///
+ /// Note that Upload must be called after the Create methods for the data to actually be uploaded.
+ ///
+ public unsafe class ResourceInitializer : GraphicsResource
+ {
+ CpuBuffer TransferBuffer;
+
+ byte* data;
+ uint dataOffset = 0;
+ uint dataSize = 1024;
+
+ List<(GpuBuffer, uint, uint)> BufferUploads = new List<(GpuBuffer, uint, uint)>();
+ List<(Texture, uint, uint)> TextureUploads = new List<(Texture, uint, uint)>();
+
+ public ResourceInitializer(GraphicsDevice device) : base(device)
+ {
+ data = (byte*) NativeMemory.Alloc(dataSize);
+ }
+
+ ///
+ /// Creates a GpuBuffer with data to be uploaded.
+ ///
+ public GpuBuffer CreateBuffer(Span data, BufferUsageFlags usageFlags) where T : unmanaged
+ {
+ var lengthInBytes = (uint) (Marshal.SizeOf() * data.Length);
+ var gpuBuffer = new GpuBuffer(Device, usageFlags, lengthInBytes);
+
+ BufferUploads.Add((gpuBuffer, dataOffset, lengthInBytes));
+
+ ResizeDataIfNeeded(lengthInBytes);
+
+ fixed (void* spanPtr = data)
+ {
+ CopyData(spanPtr, lengthInBytes);
+ }
+
+ return gpuBuffer;
+ }
+
+ ///
+ /// Creates a 2D Texture from compressed image data to be uploaded.
+ ///
+ public Texture CreateTexture2D(Span data)
+ {
+ var pixelData = ImageUtils.GetPixelDataFromBytes(data, out var width, out var height, out var lengthInBytes);
+ var texture = Texture.CreateTexture2D(Device, width, height, TextureFormat.R8G8B8A8, TextureUsageFlags.Sampler);
+ TextureUploads.Add((texture, dataOffset, lengthInBytes));
+
+ ResizeDataIfNeeded(lengthInBytes);
+ CopyData((void*) pixelData, lengthInBytes);
+ ImageUtils.FreePixelData(pixelData);
+
+ return texture;
+ }
+
+ ///
+ /// Creates a 2D Texture from a compressed image stream to be uploaded.
+ ///
+ public Texture CreateTexture2D(Stream stream)
+ {
+ var length = stream.Length;
+ var buffer = NativeMemory.Alloc((nuint) length);
+ var span = new Span(buffer, (int) length);
+ stream.ReadExactly(span);
+
+ var texture = CreateTexture2D(span);
+
+ NativeMemory.Free(buffer);
+
+ return texture;
+ }
+
+ ///
+ /// Creates a 2D Texture from a compressed image file to be uploaded.
+ ///
+ public Texture CreateTexture2D(string path)
+ {
+ var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
+ return CreateTexture2D(fileStream);
+ }
+
+ ///
+ /// Uploads all the data corresponding to the created resources.
+ ///
+ public void Upload()
+ {
+ if (TransferBuffer == null || TransferBuffer.Size < dataSize)
+ {
+ TransferBuffer?.Dispose();
+ TransferBuffer = new CpuBuffer(Device, dataSize);
+ }
+
+ TransferBuffer.SetData(data, new BufferCopy(0, 0, dataSize), SetDataOptions.Discard);
+
+ var commandBuffer = Device.AcquireCommandBuffer();
+
+ commandBuffer.BeginCopyPass();
+
+ foreach (var (gpuBuffer, offset, size) in BufferUploads)
+ {
+ commandBuffer.UploadToBuffer(
+ TransferBuffer,
+ gpuBuffer,
+ new BufferCopy(
+ offset,
+ 0,
+ size
+ )
+ );
+ }
+
+ foreach (var (texture, offset, size) in TextureUploads)
+ {
+ commandBuffer.UploadToTexture(
+ TransferBuffer,
+ texture,
+ new BufferImageCopy(
+ offset,
+ 0,
+ 0
+ )
+ );
+ }
+
+ commandBuffer.EndCopyPass();
+ Device.Submit(commandBuffer);
+
+ BufferUploads.Clear();
+ TextureUploads.Clear();
+ dataOffset = 0;
+ }
+
+ private void ResizeDataIfNeeded(uint lengthInBytes)
+ {
+ if (dataOffset + lengthInBytes >= dataSize)
+ {
+ dataSize = dataOffset + lengthInBytes;
+ data = (byte*) NativeMemory.Realloc(data, dataSize);
+ }
+ }
+
+ private void CopyData(void* ptr, uint lengthInBytes)
+ {
+ NativeMemory.Copy(ptr, data + dataOffset, lengthInBytes);
+ dataOffset += lengthInBytes;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (!IsDisposed)
+ {
+ if (disposing)
+ {
+ TransferBuffer.Dispose();
+ }
+
+ NativeMemory.Free(data);
+ }
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/src/Graphics/Resources/Buffer.cs b/src/Graphics/Resources/Buffer.cs
deleted file mode 100644
index b034940..0000000
--- a/src/Graphics/Resources/Buffer.cs
+++ /dev/null
@@ -1,141 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using RefreshCS;
-
-namespace MoonWorks.Graphics
-{
- ///
- /// Buffers are generic data containers that can be used by the GPU.
- ///
- public class Buffer : RefreshResource
- {
- protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyBuffer;
-
- ///
- /// Size in bytes.
- ///
- public uint Size { get; }
-
- ///
- /// Creates a buffer of appropriate size given a type and element count.
- ///
- /// The type that the buffer will contain.
- /// The GraphicsDevice.
- /// Specifies how the buffer will be used.
- /// How many elements of type T the buffer will contain.
- ///
- public unsafe static Buffer Create(
- GraphicsDevice device,
- BufferUsageFlags usageFlags,
- uint elementCount
- ) where T : unmanaged
- {
- return new Buffer(
- device,
- usageFlags,
- (uint) Marshal.SizeOf() * elementCount
- );
- }
-
- ///
- /// Creates a buffer.
- ///
- /// An initialized GraphicsDevice.
- /// Specifies how the buffer will be used.
- /// The length of the array. Cannot be resized.
- public Buffer(
- GraphicsDevice device,
- BufferUsageFlags usageFlags,
- uint sizeInBytes
- ) : base(device)
- {
- Handle = Refresh.Refresh_CreateBuffer(
- device.Handle,
- (Refresh.BufferUsageFlags) usageFlags,
- sizeInBytes
- );
- Size = sizeInBytes;
- }
-
- ///
- /// 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.
- ///
- /// The span that data will be copied to.
- /// The length of the data to read.
- public unsafe void GetData(
- Span 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())
- {
- Logger.LogWarn("Data length is larger than the provided Span!");
- }
-#endif
-
- fixed (T* ptr = data)
- {
- Refresh.Refresh_GetBufferData(
- Device.Handle,
- Handle,
- (IntPtr) ptr,
- dataLengthInBytes
- );
- }
- }
-
- ///
- /// 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.
- ///
- /// The span that data will be copied to.
- /// The length of the data to read.
- public unsafe void GetData(
- T[] data,
- uint dataLengthInBytes
- ) where T : unmanaged
- {
- GetData(new Span(data), dataLengthInBytes);
- }
-
- ///
- /// 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.
- ///
- /// The span that data will be copied to.
- public unsafe void GetData(
- Span data
- ) where T : unmanaged
- {
- var lengthInBytes = System.Math.Min(data.Length * Marshal.SizeOf(), Size);
- GetData(data, (uint) lengthInBytes);
- }
-
- ///
- /// 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.
- ///
- /// The span that data will be copied to.
- public unsafe void GetData(
- T[] data
- ) where T : unmanaged
- {
- var lengthInBytes = System.Math.Min(data.Length * Marshal.SizeOf(), Size);
- GetData(new Span(data), (uint) lengthInBytes);
- }
-
- public static implicit operator BufferBinding(Buffer b)
- {
- return new BufferBinding(b, 0);
- }
- }
-}
diff --git a/src/Graphics/Resources/CpuBuffer.cs b/src/Graphics/Resources/CpuBuffer.cs
new file mode 100644
index 0000000..3988ff7
--- /dev/null
+++ b/src/Graphics/Resources/CpuBuffer.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Runtime.InteropServices;
+using RefreshCS;
+
+namespace MoonWorks.Graphics
+{
+ public unsafe class CpuBuffer : RefreshResource
+ {
+ protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyCpuBuffer;
+
+ ///
+ /// Size in bytes.
+ ///
+ public uint Size { get; }
+
+ ///
+ /// Creates a buffer of requested size given a type and element count.
+ ///
+ /// The type that the buffer will contain.
+ /// The GraphicsDevice.
+ /// How many elements of type T the buffer will contain.
+ ///
+ public unsafe static CpuBuffer Create(
+ GraphicsDevice device,
+ uint elementCount
+ ) where T : unmanaged
+ {
+ return new CpuBuffer(
+ device,
+ (uint) Marshal.SizeOf() * elementCount
+ );
+ }
+
+ ///
+ /// Creates a CpuBuffer.
+ ///
+ /// An initialized GraphicsDevice.
+ /// The length of the buffer. Cannot be resized.
+ public CpuBuffer(
+ GraphicsDevice device,
+ uint sizeInBytes
+ ) : base(device)
+ {
+ Handle = Refresh.Refresh_CreateCpuBuffer(
+ device.Handle,
+ sizeInBytes
+ );
+ Size = sizeInBytes;
+ }
+
+ ///
+ /// Immediately copies data from a data pointer to the CpuBuffer.
+ ///
+ /// If setDataOption is DISCARD and this CpuBuffer was used in an Upload command,
+ /// that command will still use the correct data at the cost of increased memory usage.
+ ///
+ /// If setDataOption is OVERWRITE and this CpuBuffer was used in an Upload command,
+ /// this could cause a data race.
+ ///
+ public unsafe void SetData(
+ byte* dataPtr,
+ in BufferCopy copyParams,
+ SetDataOptions setDataOption
+ ) {
+ Refresh.Refresh_SetData(
+ Device.Handle,
+ (nint) dataPtr,
+ Handle,
+ copyParams.ToRefresh(),
+ (Refresh.SetDataOptions) setDataOption
+ );
+ }
+
+ ///
+ /// Immediately copies data from a Span to the CpuBuffer.
+ /// Returns the length of the copy in bytes.
+ ///
+ /// If setDataOption is DISCARD and this CpuBuffer was used in an Upload command,
+ /// that command will still use the correct data at the cost of increased memory usage.
+ ///
+ /// If setDataOption is OVERWRITE and this CpuBuffer was used in an Upload command,
+ /// the data will be overwritten immediately, which could cause a data race.
+ ///
+ public unsafe uint SetData(
+ Span data,
+ uint bufferOffsetInBytes,
+ SetDataOptions setDataOption
+ ) where T : unmanaged
+ {
+ var elementSize = Marshal.SizeOf();
+ var dataLengthInBytes = (uint) (elementSize * data.Length);
+
+ fixed (T* dataPtr = data)
+ {
+ SetData(
+ (byte*) dataPtr,
+ new BufferCopy(0, bufferOffsetInBytes, dataLengthInBytes),
+ setDataOption
+ );
+ }
+
+ return dataLengthInBytes;
+ }
+
+ ///
+ /// Immediately copies data from a Span to the CpuBuffer.
+ /// Returns the length of the copy in bytes.
+ ///
+ /// If setDataOption is DISCARD and this CpuBuffer was used in an Upload command,
+ /// that command will still use the correct data at the cost of increased memory usage.
+ ///
+ /// If setDataOption is OVERWRITE and this CpuBuffer was used in an Upload command,
+ /// the data will be overwritten immediately, which could cause a data race.
+ ///
+ public unsafe uint SetData(
+ Span data,
+ SetDataOptions setDataOption
+ ) where T : unmanaged
+ {
+ return SetData(data, 0, setDataOption);
+ }
+
+ ///
+ /// Immediately copies data from the CpuBuffer into a data pointer.
+ ///
+ public unsafe void GetData(
+ byte* dataPtr,
+ in BufferCopy copyParams
+ ) {
+ Refresh.Refresh_GetData(
+ Device.Handle,
+ Handle,
+ (nint) dataPtr,
+ copyParams.ToRefresh()
+ );
+ }
+
+ ///
+ /// Immediately copies data from the CpuBuffer into a Span.
+ ///
+ public unsafe void GetData(
+ Span data,
+ uint bufferOffsetInBytes
+ ) where T : unmanaged
+ {
+ var elementSize = Marshal.SizeOf();
+ var dataLengthInBytes = (uint) (elementSize * data.Length);
+
+ fixed (T* dataPtr = data)
+ {
+ GetData(
+ (byte*) dataPtr,
+ new BufferCopy(bufferOffsetInBytes, 0, dataLengthInBytes)
+ );
+ }
+ }
+ }
+}
diff --git a/src/Graphics/Resources/GpuBuffer.cs b/src/Graphics/Resources/GpuBuffer.cs
new file mode 100644
index 0000000..e517412
--- /dev/null
+++ b/src/Graphics/Resources/GpuBuffer.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Runtime.InteropServices;
+using RefreshCS;
+
+namespace MoonWorks.Graphics
+{
+ ///
+ /// GpuBuffers are generic data containers that can be used by the GPU.
+ ///
+ public class GpuBuffer : RefreshResource
+ {
+ protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyGpuBuffer;
+
+ ///
+ /// Size in bytes.
+ ///
+ public uint Size { get; }
+
+ ///
+ /// Creates a buffer of appropriate size given a type and element count.
+ ///
+ /// The type that the buffer will contain.
+ /// The GraphicsDevice.
+ /// Specifies how the buffer will be used.
+ /// How many elements of type T the buffer will contain.
+ ///
+ public unsafe static GpuBuffer Create(
+ GraphicsDevice device,
+ BufferUsageFlags usageFlags,
+ uint elementCount
+ ) where T : unmanaged
+ {
+ return new GpuBuffer(
+ device,
+ usageFlags,
+ (uint) Marshal.SizeOf() * elementCount
+ );
+ }
+
+ ///
+ /// Creates a buffer.
+ ///
+ /// An initialized GraphicsDevice.
+ /// Specifies how the buffer will be used.
+ /// The length of the array. Cannot be resized.
+ public GpuBuffer(
+ GraphicsDevice device,
+ BufferUsageFlags usageFlags,
+ uint sizeInBytes
+ ) : base(device)
+ {
+ Handle = Refresh.Refresh_CreateGpuBuffer(
+ device.Handle,
+ (Refresh.BufferUsageFlags) usageFlags,
+ sizeInBytes
+ );
+ Size = sizeInBytes;
+ }
+
+ public static implicit operator BufferBinding(GpuBuffer b)
+ {
+ return new BufferBinding(b, 0);
+ }
+ }
+}
diff --git a/src/Graphics/Resources/GraphicsPipeline.cs b/src/Graphics/Resources/GraphicsPipeline.cs
index 421e9c5..f854bf9 100644
--- a/src/Graphics/Resources/GraphicsPipeline.cs
+++ b/src/Graphics/Resources/GraphicsPipeline.cs
@@ -61,12 +61,11 @@ namespace MoonWorks.Graphics
refreshGraphicsPipelineCreateInfo.blendConstants[2] = blendConstants.B;
refreshGraphicsPipelineCreateInfo.blendConstants[3] = blendConstants.A;
- refreshGraphicsPipelineCreateInfo.depthStencilState.backStencilState = depthStencilState.BackStencilState.ToRefresh();
+ refreshGraphicsPipelineCreateInfo.depthStencilState.stencilState = depthStencilState.StencilState.ToRefresh();
refreshGraphicsPipelineCreateInfo.depthStencilState.compareOp = (Refresh.CompareOp) depthStencilState.CompareOp;
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);
diff --git a/src/Graphics/Resources/Texture.cs b/src/Graphics/Resources/Texture.cs
index ecf9298..6f5714a 100644
--- a/src/Graphics/Resources/Texture.cs
+++ b/src/Graphics/Resources/Texture.cs
@@ -1,6 +1,5 @@
using System;
using System.IO;
-using System.Runtime.InteropServices;
using RefreshCS;
namespace MoonWorks.Graphics
@@ -23,162 +22,6 @@ namespace MoonWorks.Graphics
// FIXME: this allocates a delegate instance
protected override Action QueueDestroyFunction => Refresh.Refresh_QueueDestroyTexture;
- ///
- /// Creates a 2D Texture using PNG or QOI data from raw byte data.
- ///
- public static unsafe Texture FromImageBytes(
- GraphicsDevice device,
- CommandBuffer commandBuffer,
- Span 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);
-
- TextureCreateInfo textureCreateInfo = new TextureCreateInfo();
- 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;
- }
-
- ///
- /// Creates a 2D Texture using PNG or QOI data from a stream.
- ///
- 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(buffer, (int) length);
- stream.ReadExactly(span);
-
- var texture = FromImageBytes(device, commandBuffer, span);
-
- NativeMemory.Free((void*) buffer);
-
- return texture;
- }
-
- ///
- /// Creates a 2D Texture using PNG or QOI data from a file.
- ///
- 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 data
- ) {
- fixed (byte* ptr = data)
- {
- var pixels = Refresh.Refresh_Image_Load(
- (nint) ptr,
- (int) data.Length,
- out var w,
- out var h,
- out var len
- );
-
- commandBuffer.SetTextureData(textureSlice, pixels, (uint) len);
-
- Refresh.Refresh_Image_Free(pixels);
- }
- }
-
- ///
- /// Sets data for a texture slice using PNG or QOI data from a stream.
- ///
- 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(buffer, (int) length);
- stream.ReadExactly(span);
-
- SetDataFromImageBytes(commandBuffer, textureSlice, span);
-
- NativeMemory.Free((void*) buffer);
- }
-
- ///
- /// Sets data for a texture slice using PNG or QOI data from a file.
- ///
- 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(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;
- }
-
///
/// Creates a 2D texture.
///
@@ -590,65 +433,6 @@ namespace MoonWorks.Graphics
}
}
- ///
- /// Asynchronously saves RGBA or BGRA pixel data to a file in PNG format.
- /// Warning: this is expensive and will block to wait for data download from GPU!
- /// You can avoid blocking by calling this method from a thread.
- ///
- 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(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(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)
{
switch (format)
diff --git a/src/Graphics/State/DepthStencilState.cs b/src/Graphics/State/DepthStencilState.cs
index 000b86d..8f0f96c 100644
--- a/src/Graphics/State/DepthStencilState.cs
+++ b/src/Graphics/State/DepthStencilState.cs
@@ -11,14 +11,9 @@
public bool DepthTestEnable;
///
- /// Describes the stencil operation for back-facing primitives.
+ /// Describes the stencil operation.
///
- public StencilOpState BackStencilState;
-
- ///
- /// Describes the stencil operation for front-facing primitives.
- ///
- public StencilOpState FrontStencilState;
+ public StencilOpState StencilState;
///
/// The comparison operator used in the depth test.
diff --git a/src/Graphics/TextureSlice.cs b/src/Graphics/TextureSlice.cs
index f0f8e30..ece6109 100644
--- a/src/Graphics/TextureSlice.cs
+++ b/src/Graphics/TextureSlice.cs
@@ -8,36 +8,31 @@ namespace MoonWorks.Graphics
///
public struct TextureSlice
{
- public Texture Texture { get; }
- public Rect Rectangle { get; }
- public uint Depth { get; }
- public uint Layer { get; }
- public uint Level { get; }
+ public Texture Texture;
+ public uint MipLevel;
+ public uint BaseLayer;
+ public uint LayerCount;
+ public uint X;
+ public uint Y;
+ public uint Z;
+ public uint Width;
+ public uint Height;
+ public uint Depth;
- public uint Size => (uint) (Rectangle.W * Rectangle.H * Texture.BytesPerPixel(Texture.Format) / Texture.BlockSizeSquared(Texture.Format));
+ public uint Size => (Width * Height * Depth * LayerCount * Texture.BytesPerPixel(Texture.Format) / Texture.BlockSizeSquared(Texture.Format)) >> (int) MipLevel;
public TextureSlice(Texture texture)
{
Texture = texture;
- Rectangle = new Rect
- {
- X = 0,
- Y = 0,
- W = (int) texture.Width,
- H = (int) texture.Height
- };
- Depth = 0;
- Layer = 0;
- Level = 0;
- }
-
- public TextureSlice(Texture texture, Rect rectangle, uint depth = 0, uint layer = 0, uint level = 0)
- {
- Texture = texture;
- Rectangle = rectangle;
- Depth = depth;
- Layer = layer;
- Level = level;
+ MipLevel = 0;
+ BaseLayer = 0;
+ LayerCount = (uint) (texture.IsCube ? 6 : 1);
+ X = 0;
+ Y = 0;
+ Z = 0;
+ Width = texture.Width;
+ Height = texture.Height;
+ Depth = texture.Depth;
}
public Refresh.TextureSlice ToRefreshTextureSlice()
@@ -45,10 +40,15 @@ namespace MoonWorks.Graphics
Refresh.TextureSlice textureSlice = new Refresh.TextureSlice
{
texture = Texture.Handle,
- rectangle = Rectangle.ToRefresh(),
- depth = Depth,
- layer = Layer,
- level = Level
+ mipLevel = MipLevel,
+ baseLayer = BaseLayer,
+ layerCount = LayerCount,
+ x = X,
+ y = Y,
+ z = Z,
+ w = Width,
+ h = Height,
+ d = Depth
};
return textureSlice;
diff --git a/src/Video/VideoPlayer.cs b/src/Video/VideoPlayer.cs
index f2d079a..f2c83bb 100644
--- a/src/Video/VideoPlayer.cs
+++ b/src/Video/VideoPlayer.cs
@@ -28,6 +28,8 @@ namespace MoonWorks.Video
private Texture vTexture = null;
private Sampler LinearSampler;
+ private CpuBuffer TransferBuffer;
+
private int currentFrame;
private Stopwatch timer;
@@ -53,6 +55,8 @@ namespace MoonWorks.Video
{
Stop();
+ var needNewTransferBuffer = TransferBuffer == null;
+
if (RenderTexture == null)
{
RenderTexture = CreateRenderTexture(GraphicsDevice, video.Width, video.Height);
@@ -83,18 +87,31 @@ namespace MoonWorks.Video
{
yTexture.Dispose();
yTexture = CreateSubTexture(GraphicsDevice, video.Width, video.Height);
+ needNewTransferBuffer = true;
}
if (video.UVWidth != uTexture.Width || video.UVHeight != uTexture.Height)
{
uTexture.Dispose();
uTexture = CreateSubTexture(GraphicsDevice, video.UVWidth, video.UVHeight);
+ needNewTransferBuffer = true;
}
if (video.UVWidth != vTexture.Width || video.UVHeight != vTexture.Height)
{
vTexture.Dispose();
vTexture = CreateSubTexture(GraphicsDevice, video.UVWidth, video.UVHeight);
+ needNewTransferBuffer = true;
+ }
+
+ if (needNewTransferBuffer)
+ {
+ if (TransferBuffer != null)
+ {
+ TransferBuffer.Dispose();
+ }
+
+ TransferBuffer = new CpuBuffer(Device, yTexture.Size + uTexture.Size + vTexture.Size);
}
Video = video;
@@ -235,17 +252,44 @@ namespace MoonWorks.Video
{
var commandBuffer = GraphicsDevice.AcquireCommandBuffer();
- commandBuffer.SetTextureDataYUV(
+ var ySpan = new Span((void*) CurrentStream.yDataHandle, (int) CurrentStream.yDataLength);
+ var uSpan = new Span((void*) CurrentStream.uDataHandle, (int) CurrentStream.uvDataLength);
+ var vSpan = new Span((void*) CurrentStream.vDataHandle, (int) CurrentStream.uvDataLength);
+
+ TransferBuffer.SetData(ySpan, SetDataOptions.Discard);
+ TransferBuffer.SetData(uSpan, (uint) ySpan.Length, SetDataOptions.Overwrite);
+ TransferBuffer.SetData(vSpan, (uint) (ySpan.Length + uSpan.Length), SetDataOptions.Overwrite);
+
+ commandBuffer.UploadToTexture(
+ TransferBuffer,
yTexture,
+ new BufferImageCopy
+ {
+ BufferOffset = 0,
+ BufferStride = CurrentStream.yStride,
+ BufferImageHeight = yTexture.Height
+ }
+ );
+
+ commandBuffer.UploadToTexture(
+ TransferBuffer,
uTexture,
+ new BufferImageCopy{
+ BufferOffset = (uint) ySpan.Length,
+ BufferStride = CurrentStream.uvStride,
+ BufferImageHeight = uTexture.Height
+ }
+ );
+
+ commandBuffer.UploadToTexture(
+ TransferBuffer,
vTexture,
- CurrentStream.yDataHandle,
- CurrentStream.uDataHandle,
- CurrentStream.vDataHandle,
- CurrentStream.yDataLength,
- CurrentStream.uvDataLength,
- CurrentStream.yStride,
- CurrentStream.uvStride
+ new BufferImageCopy
+ {
+ BufferOffset = (uint) (ySpan.Length + uSpan.Length),
+ BufferStride = CurrentStream.uvStride,
+ BufferImageHeight = vTexture.Height
+ }
);
commandBuffer.BeginRenderPass(
@@ -259,7 +303,7 @@ namespace MoonWorks.Video
new TextureSamplerBinding(vTexture, LinearSampler)
);
- commandBuffer.DrawPrimitives(0, 1, 0, 0);
+ commandBuffer.DrawPrimitives(0, 1);
commandBuffer.EndRenderPass();