Refresh 2 changes
parent
bb7e45b9a3
commit
39496c37ea
|
@ -1 +1 @@
|
|||
Subproject commit b5325e6d0329eeb35b074091a569a5f679852d28
|
||||
Subproject commit ad0b168c4794ee0377c825b0149a0d21bd856e43
|
|
@ -5,10 +5,10 @@
|
|||
/// </summary>
|
||||
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;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
namespace MoonWorks.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// A texture-level pair to be used when binding compute textures.
|
||||
/// </summary>
|
||||
public struct TextureLevelBinding
|
||||
{
|
||||
public Texture Texture;
|
||||
public uint MipLevel;
|
||||
|
||||
public TextureLevelBinding(Texture texture, uint mipLevel = 0)
|
||||
{
|
||||
Texture = texture;
|
||||
MipLevel = mipLevel;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<Vertex>(GraphicsDevice, BufferUsageFlags.Vertex, INITIAL_VERTEX_COUNT);
|
||||
IndexBuffer = Buffer.Create<uint>(GraphicsDevice, BufferUsageFlags.Index, INITIAL_INDEX_COUNT);
|
||||
VertexBuffer = GpuBuffer.Create<Vertex>(GraphicsDevice, BufferUsageFlags.Vertex, INITIAL_VERTEX_COUNT);
|
||||
IndexBuffer = GpuBuffer.Create<uint>(GraphicsDevice, BufferUsageFlags.Index, INITIAL_INDEX_COUNT);
|
||||
|
||||
TransferBuffer = CpuBuffer.Create<byte>(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<byte>((void*) vertexDataPointer, (int) vertexDataLengthInBytes);
|
||||
var indexSpan = new Span<byte>((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
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using RefreshCS;
|
||||
|
||||
namespace MoonWorks.Graphics
|
||||
{
|
||||
public static class ImageUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets pointer to pixel data from compressed image byte data.
|
||||
///
|
||||
/// The returned pointer must be freed by calling FreePixelData.
|
||||
/// </summary>
|
||||
public static unsafe 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>
|
||||
/// Decodes image data into a CpuBuffer to prepare for image upload.
|
||||
/// </summary>
|
||||
public static unsafe uint DecodeIntoCpuBuffer(
|
||||
Span<byte> 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<byte>((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option);
|
||||
FreePixelData(pixelData);
|
||||
return length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes an image stream into a CpuBuffer to prepare for image upload.
|
||||
/// </summary>
|
||||
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<byte>((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option);
|
||||
FreePixelData(pixelData);
|
||||
return length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes an image file into a CpuBuffer to prepare for image upload.
|
||||
/// </summary>
|
||||
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<byte>((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option);
|
||||
FreePixelData(pixelData);
|
||||
return length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves pixel data contained in a CpuBuffer to a PNG file.
|
||||
/// </summary>
|
||||
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<byte>(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<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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -297,6 +297,12 @@ namespace MoonWorks.Graphics
|
|||
IntOpaqueWhite
|
||||
}
|
||||
|
||||
public enum SetDataOptions
|
||||
{
|
||||
Discard,
|
||||
Overwrite
|
||||
}
|
||||
|
||||
public enum Backend
|
||||
{
|
||||
DontCare,
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MoonWorks.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <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);
|
||||
|
||||
BufferUploads.Add((gpuBuffer, dataOffset, lengthInBytes));
|
||||
|
||||
ResizeDataIfNeeded(lengthInBytes);
|
||||
|
||||
fixed (void* spanPtr = data)
|
||||
{
|
||||
CopyData(spanPtr, lengthInBytes);
|
||||
}
|
||||
|
||||
return gpuBuffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a 2D Texture from compressed image data to be uploaded.
|
||||
/// </summary>
|
||||
public Texture CreateTexture2D(Span<byte> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a 2D Texture from a compressed image stream to be uploaded.
|
||||
/// </summary>
|
||||
public Texture CreateTexture2D(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 = CreateTexture2D(span);
|
||||
|
||||
NativeMemory.Free(buffer);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a 2D Texture from a compressed image file to be uploaded.
|
||||
/// </summary>
|
||||
public Texture CreateTexture2D(string path)
|
||||
{
|
||||
var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
||||
return CreateTexture2D(fileStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uploads all the data corresponding to the created resources.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using RefreshCS;
|
||||
|
||||
namespace MoonWorks.Graphics
|
||||
{
|
||||
public unsafe class CpuBuffer : RefreshResource
|
||||
{
|
||||
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyCpuBuffer;
|
||||
|
||||
/// <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 CpuBuffer Create<T>(
|
||||
GraphicsDevice device,
|
||||
uint elementCount
|
||||
) where T : unmanaged
|
||||
{
|
||||
return new CpuBuffer(
|
||||
device,
|
||||
(uint) Marshal.SizeOf<T>() * elementCount
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a CpuBuffer.
|
||||
/// </summary>
|
||||
/// <param name="device">An initialized GraphicsDevice.</param>
|
||||
/// <param name="sizeInBytes">The length of the buffer. Cannot be resized.</param>
|
||||
public CpuBuffer(
|
||||
GraphicsDevice device,
|
||||
uint sizeInBytes
|
||||
) : base(device)
|
||||
{
|
||||
Handle = Refresh.Refresh_CreateCpuBuffer(
|
||||
device.Handle,
|
||||
sizeInBytes
|
||||
);
|
||||
Size = sizeInBytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public unsafe void SetData(
|
||||
byte* dataPtr,
|
||||
in BufferCopy copyParams,
|
||||
SetDataOptions setDataOption
|
||||
) {
|
||||
Refresh.Refresh_SetData(
|
||||
Device.Handle,
|
||||
(nint) dataPtr,
|
||||
Handle,
|
||||
copyParams.ToRefresh(),
|
||||
(Refresh.SetDataOptions) setDataOption
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public unsafe uint SetData<T>(
|
||||
Span<T> data,
|
||||
uint bufferOffsetInBytes,
|
||||
SetDataOptions setDataOption
|
||||
) where T : unmanaged
|
||||
{
|
||||
var elementSize = Marshal.SizeOf<T>();
|
||||
var dataLengthInBytes = (uint) (elementSize * data.Length);
|
||||
|
||||
fixed (T* dataPtr = data)
|
||||
{
|
||||
SetData(
|
||||
(byte*) dataPtr,
|
||||
new BufferCopy(0, bufferOffsetInBytes, dataLengthInBytes),
|
||||
setDataOption
|
||||
);
|
||||
}
|
||||
|
||||
return dataLengthInBytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public unsafe uint SetData<T>(
|
||||
Span<T> data,
|
||||
SetDataOptions setDataOption
|
||||
) where T : unmanaged
|
||||
{
|
||||
return SetData(data, 0, setDataOption);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Immediately copies data from the CpuBuffer into a data pointer.
|
||||
/// </summary>
|
||||
public unsafe void GetData(
|
||||
byte* dataPtr,
|
||||
in BufferCopy copyParams
|
||||
) {
|
||||
Refresh.Refresh_GetData(
|
||||
Device.Handle,
|
||||
Handle,
|
||||
(nint) dataPtr,
|
||||
copyParams.ToRefresh()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Immediately copies data from the CpuBuffer into a Span.
|
||||
/// </summary>
|
||||
public unsafe void GetData<T>(
|
||||
Span<T> data,
|
||||
uint bufferOffsetInBytes
|
||||
) where T : unmanaged
|
||||
{
|
||||
var elementSize = Marshal.SizeOf<T>();
|
||||
var dataLengthInBytes = (uint) (elementSize * data.Length);
|
||||
|
||||
fixed (T* dataPtr = data)
|
||||
{
|
||||
GetData(
|
||||
(byte*) dataPtr,
|
||||
new BufferCopy(bufferOffsetInBytes, 0, dataLengthInBytes)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using RefreshCS;
|
||||
|
||||
namespace MoonWorks.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// GpuBuffers are generic data containers that can be used by the GPU.
|
||||
/// </summary>
|
||||
public class GpuBuffer : RefreshResource
|
||||
{
|
||||
protected override Action<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyGpuBuffer;
|
||||
|
||||
/// <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 GpuBuffer Create<T>(
|
||||
GraphicsDevice device,
|
||||
BufferUsageFlags usageFlags,
|
||||
uint elementCount
|
||||
) where T : unmanaged
|
||||
{
|
||||
return new GpuBuffer(
|
||||
device,
|
||||
usageFlags,
|
||||
(uint) Marshal.SizeOf<T>() * elementCount
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a buffer.
|
||||
/// </summary>
|
||||
/// <param name="device">An initialized GraphicsDevice.</param>
|
||||
/// <param name="usageFlags">Specifies how the buffer will be used.</param>
|
||||
/// <param name="sizeInBytes">The length of the array. Cannot be resized.</param>
|
||||
public GpuBuffer(
|
||||
GraphicsDevice device,
|
||||
BufferUsageFlags usageFlags,
|
||||
uint sizeInBytes
|
||||
) : base(device)
|
||||
{
|
||||
Handle = Refresh.Refresh_CreateGpuBuffer(
|
||||
device.Handle,
|
||||
(Refresh.BufferUsageFlags) usageFlags,
|
||||
sizeInBytes
|
||||
);
|
||||
Size = sizeInBytes;
|
||||
}
|
||||
|
||||
public static implicit operator BufferBinding(GpuBuffer b)
|
||||
{
|
||||
return new BufferBinding(b, 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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<IntPtr, IntPtr> QueueDestroyFunction => Refresh.Refresh_QueueDestroyTexture;
|
||||
|
||||
/// <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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <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(
|
||||
(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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets data for a texture slice using PNG or QOI data from a stream.
|
||||
/// </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>
|
||||
/// Creates a 2D texture.
|
||||
/// </summary>
|
||||
|
@ -590,65 +433,6 @@ namespace MoonWorks.Graphics
|
|||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
switch (format)
|
||||
|
|
|
@ -11,14 +11,9 @@
|
|||
public bool DepthTestEnable;
|
||||
|
||||
/// <summary>
|
||||
/// Describes the stencil operation for back-facing primitives.
|
||||
/// Describes the stencil operation.
|
||||
/// </summary>
|
||||
public StencilOpState BackStencilState;
|
||||
|
||||
/// <summary>
|
||||
/// Describes the stencil operation for front-facing primitives.
|
||||
/// </summary>
|
||||
public StencilOpState FrontStencilState;
|
||||
public StencilOpState StencilState;
|
||||
|
||||
/// <summary>
|
||||
/// The comparison operator used in the depth test.
|
||||
|
|
|
@ -8,36 +8,31 @@ namespace MoonWorks.Graphics
|
|||
/// </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 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;
|
||||
|
|
|
@ -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<byte>((void*) CurrentStream.yDataHandle, (int) CurrentStream.yDataLength);
|
||||
var uSpan = new Span<byte>((void*) CurrentStream.uDataHandle, (int) CurrentStream.uvDataLength);
|
||||
var vSpan = new Span<byte>((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();
|
||||
|
||||
|
|
Loading…
Reference in New Issue