Compare commits

..

14 Commits

30 changed files with 847 additions and 798 deletions

View File

@ -3,7 +3,6 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>11</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

@ -1 +1 @@
Subproject commit 2e346d84016fb7b26dbe7c5b6c485109571c30f4 Subproject commit 029f19196a94e13b8ed98d10f47a701221951f2f

View File

@ -53,10 +53,10 @@ namespace MoonWorks
public Game( public Game(
WindowCreateInfo windowCreateInfo, WindowCreateInfo windowCreateInfo,
FrameLimiterSettings frameLimiterSettings, FrameLimiterSettings frameLimiterSettings,
Span<Backend> preferredBackends,
int targetTimestep = 60, int targetTimestep = 60,
bool debugMode = false bool debugMode = false
) ) {
{
Logger.LogInfo("Initializing frame limiter..."); Logger.LogInfo("Initializing frame limiter...");
Timestep = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / targetTimestep); Timestep = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / targetTimestep);
gameTimer = Stopwatch.StartNew(); gameTimer = Stopwatch.StartNew();
@ -82,7 +82,7 @@ namespace MoonWorks
Logger.LogInfo("Initializing graphics device..."); Logger.LogInfo("Initializing graphics device...");
GraphicsDevice = new GraphicsDevice( GraphicsDevice = new GraphicsDevice(
Backend.Vulkan, preferredBackends,
debugMode debugMode
); );

View File

@ -1,17 +1,21 @@
namespace MoonWorks.Graphics using RefreshCS;
namespace MoonWorks.Graphics
{ {
/// <summary> /// <summary>
/// A buffer-offset pair to be used when binding vertex buffers. /// A buffer-offset pair to be used when binding vertex or index buffers.
/// </summary> /// </summary>
public struct BufferBinding public readonly record struct BufferBinding(
{ GpuBuffer Buffer,
public GpuBuffer Buffer; uint Offset
public ulong Offset; ) {
public Refresh.BufferBinding ToRefresh()
public BufferBinding(GpuBuffer buffer, ulong offset)
{ {
Buffer = buffer; return new Refresh.BufferBinding
Offset = offset; {
gpuBuffer = Buffer.Handle,
offset = Offset
};
} }
} }
} }

View File

@ -0,0 +1,35 @@
using RefreshCS;
namespace MoonWorks.Graphics
{
/// <summary>
/// Binding specification to be used when binding buffers for compute shaders.
/// </summary>
/// <param name="GpuBuffer">The GpuBuffer to bind.</param>
/// <param name="WriteOption">
/// Specifies data dependency behavior when this buffer is written to in the shader. <br/>
///
/// Cycle:
/// If this buffer has been used in commands that have not finished,
/// the implementation may choose to prevent a dependency on those commands
/// at the cost of increased memory usage.
/// You may NOT assume that any of the previous data is retained.
/// This may prevent stalls when frequently updating a resource. <br />
///
/// SafeOverwrite:
/// Overwrites the data safely using a GPU memory barrier.
/// </param>
public readonly record struct ComputeBufferBinding(
GpuBuffer GpuBuffer,
WriteOptions WriteOption
) {
public Refresh.ComputeBufferBinding ToRefresh()
{
return new Refresh.ComputeBufferBinding
{
gpuBuffer = GpuBuffer.Handle,
writeOption = (Refresh.WriteOptions) WriteOption
};
}
}
}

View File

@ -0,0 +1,35 @@
using RefreshCS;
namespace MoonWorks.Graphics
{
/// <summary>
/// Binding specification used for binding texture slices for compute shaders.
/// </summary>
/// <param name="TextureSlice">The TextureSlice to bind.</param>
/// <param name="WriteOption">
/// Specifies data dependency behavior when this texture is written to in the shader. <br/>
///
/// Cycle:
/// If this buffer has been used in commands that have not finished,
/// the implementation may choose to prevent a dependency on those commands
/// at the cost of increased memory usage.
/// You may NOT assume that any of the previous data is retained.
/// This may prevent stalls when frequently updating a resource. <br />
///
/// SafeOverwrite:
/// Overwrites the data safely using a GPU memory barrier.
/// </param>
public readonly record struct ComputeTextureBinding(
TextureSlice TextureSlice,
WriteOptions WriteOption
) {
public Refresh.ComputeTextureBinding ToRefresh()
{
return new Refresh.ComputeTextureBinding
{
textureSlice = TextureSlice.ToRefreshTextureSlice(),
writeOption = (Refresh.WriteOptions) WriteOption
};
}
}
}

View File

@ -1,17 +0,0 @@
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;
}
}
}

View File

@ -1,17 +1,21 @@
namespace MoonWorks.Graphics using RefreshCS;
namespace MoonWorks.Graphics
{ {
/// <summary> /// <summary>
/// A texture-sampler pair to be used when binding samplers. /// A texture-sampler pair to be used when binding samplers.
/// </summary> /// </summary>
public struct TextureSamplerBinding public readonly record struct TextureSamplerBinding(
{ Texture Texture,
public Texture Texture; Sampler Sampler
public Sampler Sampler; ) {
public Refresh.TextureSamplerBinding ToRefresh()
public TextureSamplerBinding(Texture texture, Sampler sampler)
{ {
Texture = texture; return new Refresh.TextureSamplerBinding
Sampler = sampler; {
texture = Texture.Handle,
sampler = Sampler.Handle
};
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -49,24 +49,12 @@ namespace MoonWorks.Graphics.Font
var imagePath = Path.ChangeExtension(fontPath, ".png"); var imagePath = Path.ChangeExtension(fontPath, ".png");
ImageUtils.ImageInfoFromFile(imagePath, out var width, out var height, out var sizeInBytes); ImageUtils.ImageInfoFromFile(imagePath, out var width, out var height, out var sizeInBytes);
var texture = Texture.CreateTexture2D(graphicsDevice, width, height, TextureFormat.R8G8B8A8, TextureUsageFlags.Sampler);
var transferBuffer = new TransferBuffer(graphicsDevice, sizeInBytes); var uploader = new ResourceUploader(graphicsDevice);
ImageUtils.DecodeIntoTransferBuffer( var texture = uploader.CreateTexture2DFromCompressed(imagePath);
imagePath, uploader.Upload();
transferBuffer, uploader.Dispose();
0,
SetDataOptions.Overwrite
);
commandBuffer.BeginCopyPass();
commandBuffer.UploadToTexture(
transferBuffer,
texture
);
commandBuffer.EndCopyPass();
transferBuffer.Dispose();
NativeMemory.Free(fontFileByteBuffer); NativeMemory.Free(fontFileByteBuffer);
NativeMemory.Free(atlasFileByteBuffer); NativeMemory.Free(atlasFileByteBuffer);

View File

@ -124,11 +124,11 @@ namespace MoonWorks.Graphics.Font
if (vertexDataLengthInBytes > 0 && indexDataLengthInBytes > 0) if (vertexDataLengthInBytes > 0 && indexDataLengthInBytes > 0)
{ {
TransferBuffer.SetData(vertexSpan, SetDataOptions.Discard); TransferBuffer.SetData(vertexSpan, TransferOptions.Cycle);
TransferBuffer.SetData(indexSpan, (uint) vertexSpan.Length, SetDataOptions.Overwrite); TransferBuffer.SetData(indexSpan, (uint) vertexSpan.Length, TransferOptions.Overwrite);
commandBuffer.UploadToBuffer(TransferBuffer, VertexBuffer, new BufferCopy(0, 0, (uint) vertexSpan.Length)); commandBuffer.UploadToBuffer(TransferBuffer, VertexBuffer, new BufferCopy(0, 0, (uint) vertexSpan.Length), WriteOptions.Cycle);
commandBuffer.UploadToBuffer(TransferBuffer, IndexBuffer, new BufferCopy((uint) vertexSpan.Length, 0, (uint) indexSpan.Length)); commandBuffer.UploadToBuffer(TransferBuffer, IndexBuffer, new BufferCopy((uint) vertexSpan.Length, 0, (uint) indexSpan.Length), WriteOptions.Cycle);
} }
PrimitiveCount = vertexCount / 2; PrimitiveCount = vertexCount / 2;

View File

@ -40,11 +40,17 @@ namespace MoonWorks.Graphics
private FencePool FencePool; private FencePool FencePool;
private CommandBufferPool CommandBufferPool; private CommandBufferPool CommandBufferPool;
internal GraphicsDevice( internal unsafe GraphicsDevice(
Backend preferredBackend, Span<Backend> preferredBackends,
bool debugMode bool debugMode
) { ) {
Backend = (Backend) Refresh.Refresh_SelectBackend((Refresh.Backend) preferredBackend, out windowFlags); var backends = stackalloc Refresh.Backend[preferredBackends.Length];
for (var i = 0; i < preferredBackends.Length; i += 1)
{
backends[i] = (Refresh.Backend) preferredBackends[i];
}
Backend = (Backend) Refresh.Refresh_SelectBackend(backends, (uint) preferredBackends.Length, out windowFlags);
if (Backend == Backend.Invalid) if (Backend == Backend.Invalid)
{ {
@ -467,6 +473,118 @@ namespace MoonWorks.Graphics
FencePool.Return(fence); FencePool.Return(fence);
} }
/// <summary>
/// ⚠️⚠️⚠️ <br/>
/// Downloads data from a Texture to a TransferBuffer.
/// This copy occurs immediately on the CPU timeline.<br/>
///
/// If you modify this texture in a command buffer and then call this function without calling
/// SubmitAndAcquireFence and WaitForFences first, the results will not be what you expect.<br/>
///
/// This method forces a sync point and is generally a bad thing to do.
/// Only use it if you have exhausted all other options.<br/>
///
/// Remember: friends don't let friends readback.<br/>
/// ⚠️⚠️⚠️
/// </summary>
public void DownloadFromTexture(
in TextureRegion textureRegion,
TransferBuffer transferBuffer,
in BufferImageCopy copyParams,
TransferOptions transferOption
) {
Refresh.Refresh_DownloadFromTexture(
Handle,
textureRegion.ToRefreshTextureRegion(),
transferBuffer.Handle,
copyParams.ToRefresh(),
(Refresh.TransferOptions) transferOption
);
}
/// <summary>
/// ⚠️⚠️⚠️ <br/>
/// Downloads all data from a 2D texture with no mips to a TransferBuffer.
/// This copy occurs immediately on the CPU timeline.<br/>
///
/// If you modify this texture in a command buffer and then call this function without calling
/// SubmitAndAcquireFence and WaitForFences first, the results will not be what you expect.<br/>
///
/// This method forces a sync point and is generally a bad thing to do.
/// Only use it if you have exhausted all other options.<br/>
///
/// Remember: friends don't let friends readback.<br/>
/// ⚠️⚠️⚠️
/// </summary>
public void DownloadFromTexture(
Texture texture,
TransferBuffer transferBuffer,
TransferOptions transferOption
) {
DownloadFromTexture(
new TextureRegion(texture),
transferBuffer,
new BufferImageCopy(0, 0, 0),
transferOption
);
}
/// <summary>
/// ⚠️⚠️⚠️ <br/>
/// Downloads data from a GpuBuffer to a TransferBuffer.
/// This copy occurs immediately on the CPU timeline.<br/>
///
/// If you modify this GpuBuffer in a command buffer and then call this function without calling
/// SubmitAndAcquireFence and WaitForFences first, the results will not be what you expect.<br/>
///
/// This method forces a sync point and is generally a bad thing to do.
/// Only use it if you have exhausted all other options.<br/>
///
/// Remember: friends don't let friends readback.<br/>
/// ⚠️⚠️⚠️
/// </summary>
public void DownloadFromBuffer(
GpuBuffer gpuBuffer,
TransferBuffer transferBuffer,
in BufferCopy copyParams,
TransferOptions transferOption
) {
Refresh.Refresh_DownloadFromBuffer(
Handle,
gpuBuffer.Handle,
transferBuffer.Handle,
copyParams.ToRefresh(),
(Refresh.TransferOptions) transferOption
);
}
/// <summary>
/// ⚠️⚠️⚠️ <br/>
/// Downloads all data in a GpuBuffer to a TransferBuffer.
/// This copy occurs immediately on the CPU timeline.<br/>
///
/// If you modify this GpuBuffer in a command buffer and then call this function without calling
/// SubmitAndAcquireFence and WaitForFences first, the results will not be what you expect.<br/>
///
/// This method forces a sync point and is generally a bad thing to do.
/// Only use it if you have exhausted all other options.<br/>
///
/// Remember: friends don't let friends readback.<br/>
/// ⚠️⚠️⚠️
/// </summary>
public void DownloadFromBuffer(
GpuBuffer gpuBuffer,
TransferBuffer transferBuffer,
TransferOptions option
) {
DownloadFromBuffer(
gpuBuffer,
transferBuffer,
new BufferCopy(0, 0, gpuBuffer.Size),
option
);
}
private TextureFormat GetSwapchainFormat(Window window) private TextureFormat GetSwapchainFormat(Window window)
{ {
return (TextureFormat) Refresh.Refresh_GetSwapchainFormat(Handle, window.Handle); return (TextureFormat) Refresh.Refresh_GetSwapchainFormat(Handle, window.Handle);

View File

@ -145,51 +145,6 @@ namespace MoonWorks.Graphics
Refresh.Refresh_Image_Free(pixels); Refresh.Refresh_Image_Free(pixels);
} }
/// <summary>
/// Decodes image data into a TransferBuffer to prepare for image upload.
/// </summary>
public static unsafe uint DecodeIntoTransferBuffer(
Span<byte> data,
TransferBuffer transferBuffer,
uint bufferOffsetInBytes,
SetDataOptions option
) {
var pixelData = GetPixelDataFromBytes(data, out var w, out var h, out var sizeInBytes);
var length = transferBuffer.SetData(new Span<byte>((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option);
FreePixelData(pixelData);
return length;
}
/// <summary>
/// Decodes an image stream into a TransferBuffer to prepare for image upload.
/// </summary>
public static unsafe uint DecodeIntoTransferBuffer(
Stream stream,
TransferBuffer transferBuffer,
uint bufferOffsetInBytes,
SetDataOptions option
) {
var pixelData = GetPixelDataFromStream(stream, out var w, out var h, out var sizeInBytes);
var length = transferBuffer.SetData(new Span<byte>((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option);
FreePixelData(pixelData);
return length;
}
/// <summary>
/// Decodes an image file into a TransferBuffer to prepare for image upload.
/// </summary>
public static unsafe uint DecodeIntoTransferBuffer(
string path,
TransferBuffer transferBuffer,
uint bufferOffsetInBytes,
SetDataOptions option
) {
var pixelData = GetPixelDataFromFile(path, out var w, out var h, out var sizeInBytes);
var length = transferBuffer.SetData(new Span<byte>((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option);
FreePixelData(pixelData);
return length;
}
/// <summary> /// <summary>
/// Saves pixel data contained in a TransferBuffer to a PNG file. /// Saves pixel data contained in a TransferBuffer to a PNG file.
/// </summary> /// </summary>

View File

@ -297,16 +297,22 @@ namespace MoonWorks.Graphics
IntOpaqueWhite IntOpaqueWhite
} }
public enum SetDataOptions public enum TransferOptions
{ {
Discard, Cycle,
Overwrite Overwrite
} }
public enum WriteOptions
{
Cycle,
SafeOverwrite
}
public enum Backend public enum Backend
{ {
DontCare,
Vulkan, Vulkan,
D3D11,
PS5, PS5,
Invalid Invalid
} }

View File

@ -154,11 +154,7 @@ namespace MoonWorks.Graphics
public StencilOp PassOp; public StencilOp PassOp;
public StencilOp DepthFailOp; public StencilOp DepthFailOp;
public CompareOp CompareOp; public CompareOp CompareOp;
public uint CompareMask;
public uint WriteMask;
public uint Reference;
// FIXME: can we do an explicit cast here?
public Refresh.StencilOpState ToRefresh() public Refresh.StencilOpState ToRefresh()
{ {
return new Refresh.StencilOpState return new Refresh.StencilOpState
@ -166,61 +162,97 @@ namespace MoonWorks.Graphics
failOp = (Refresh.StencilOp) FailOp, failOp = (Refresh.StencilOp) FailOp,
passOp = (Refresh.StencilOp) PassOp, passOp = (Refresh.StencilOp) PassOp,
depthFailOp = (Refresh.StencilOp) DepthFailOp, depthFailOp = (Refresh.StencilOp) DepthFailOp,
compareOp = (Refresh.CompareOp) CompareOp, compareOp = (Refresh.CompareOp) CompareOp
compareMask = CompareMask,
writeMask = WriteMask,
reference = Reference
}; };
} }
} }
[StructLayout(LayoutKind.Sequential)] /// <summary>
/// Determines how a color texture will be read/written in a render pass.
/// </summary>
public struct ColorAttachmentInfo public struct ColorAttachmentInfo
{ {
public Texture Texture; public TextureSlice TextureSlice;
public uint Depth;
public uint Layer; /// <summary>
public uint Level; /// If LoadOp is set to Clear, the texture slice will be cleared to this color.
/// </summary>
public Color ClearColor; public Color ClearColor;
/// <summary>
/// Determines what is done with the texture slice memory
/// at the beginning of the render pass. <br/>
///
/// Load:
/// Loads the data currently in the texture slice. <br/>
///
/// Clear:
/// Clears the texture slice to a single color. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice data.
/// This is a good option if you know that every single pixel will be written in the render pass.
/// </summary>
public LoadOp LoadOp; public LoadOp LoadOp;
/// <summary>
/// Determines what is done with the texture slice memory
/// at the end of the render pass. <br/>
///
/// Store:
/// Stores the results of the render pass in the texture slice memory. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice memory.
/// </summary>
public StoreOp StoreOp; public StoreOp StoreOp;
/// <summary>
/// Specifies data dependency behavior. This option is ignored if LoadOp is Load. <br/>
///
/// Cycle:
/// If this texture slice has been used in commands that have not completed,
/// the implementation may prevent a dependency on those commands
/// at the cost of increased memory usage.
/// You may NOT assume that any of the previous texture (not slice!) data is retained.
/// This may prevent stalls when frequently reusing a texture slice in rendering. <br/>
///
/// SafeOverwrite:
/// Overwrites the data safely using a GPU memory barrier.
/// </summary>
public WriteOptions WriteOption;
public ColorAttachmentInfo( public ColorAttachmentInfo(
Texture texture, TextureSlice textureSlice,
WriteOptions writeOption,
Color clearColor, Color clearColor,
StoreOp storeOp = StoreOp.Store StoreOp storeOp = StoreOp.Store
) { ) {
Texture = texture; TextureSlice = textureSlice;
Depth = 0;
Layer = 0;
Level = 0;
ClearColor = clearColor; ClearColor = clearColor;
LoadOp = LoadOp.Clear; LoadOp = LoadOp.Clear;
StoreOp = storeOp; StoreOp = storeOp;
WriteOption = writeOption;
} }
public ColorAttachmentInfo( public ColorAttachmentInfo(
Texture texture, TextureSlice textureSlice,
WriteOptions writeOption,
LoadOp loadOp = LoadOp.DontCare, LoadOp loadOp = LoadOp.DontCare,
StoreOp storeOp = StoreOp.Store StoreOp storeOp = StoreOp.Store
) { ) {
Texture = texture; TextureSlice = textureSlice;
Depth = 0;
Layer = 0;
Level = 0;
ClearColor = Color.White; ClearColor = Color.White;
LoadOp = loadOp; LoadOp = loadOp;
StoreOp = storeOp; StoreOp = storeOp;
WriteOption = writeOption;
} }
public Refresh.ColorAttachmentInfo ToRefresh() public Refresh.ColorAttachmentInfo ToRefresh()
{ {
return new Refresh.ColorAttachmentInfo return new Refresh.ColorAttachmentInfo
{ {
texture = Texture.Handle, textureSlice = TextureSlice.ToRefreshTextureSlice(),
depth = Depth,
layer = Layer,
level = Level,
clearColor = new Refresh.Vec4 clearColor = new Refresh.Vec4
{ {
x = ClearColor.R / 255f, x = ClearColor.R / 255f,
@ -229,92 +261,142 @@ namespace MoonWorks.Graphics
w = ClearColor.A / 255f w = ClearColor.A / 255f
}, },
loadOp = (Refresh.LoadOp) LoadOp, loadOp = (Refresh.LoadOp) LoadOp,
storeOp = (Refresh.StoreOp) StoreOp storeOp = (Refresh.StoreOp) StoreOp,
writeOption = (Refresh.WriteOptions) WriteOption
}; };
} }
} }
[StructLayout(LayoutKind.Sequential)] /// <summary>
/// Determines how a depth/stencil texture will be read/written in a render pass.
/// </summary>
public struct DepthStencilAttachmentInfo public struct DepthStencilAttachmentInfo
{ {
public Texture Texture; public TextureSlice TextureSlice;
public uint Depth;
public uint Layer; /// <summary>
public uint Level; /// If LoadOp is set to Clear, the texture slice depth will be cleared to this depth value. <br/>
/// If StencilLoadOp is set to Clear, the texture slice stencil value will be cleared to this stencil value.
/// </summary>
public DepthStencilValue DepthStencilClearValue; public DepthStencilValue DepthStencilClearValue;
/// <summary>
/// Determines what is done with the texture slice depth values
/// at the beginning of the render pass. <br/>
///
/// Load:
/// Loads the data currently in the texture slice. <br/>
///
/// Clear:
/// Clears the texture slice to a single depth value. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice data.
/// This is a good option if you know that every single pixel will be written in the render pass.
/// </summary>
public LoadOp LoadOp; public LoadOp LoadOp;
/// <summary>
/// Determines what is done with the texture slice depth values
/// at the end of the render pass. <br/>
///
/// Store:
/// Stores the results of the render pass in the texture slice memory. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice memory.
/// This is usually a good option for depth textures that don't need to be reused.
/// </summary>
public StoreOp StoreOp; public StoreOp StoreOp;
/// <summary>
/// Determines what is done with the texture slice stencil values
/// at the beginning of the render pass. <br/>
///
/// Load:
/// Loads the data currently in the texture slice. <br/>
///
/// Clear:
/// Clears the texture slice to a single stencil value. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice data.
/// This is a good option if you know that every single pixel will be written in the render pass.
/// </summary>
public LoadOp StencilLoadOp; public LoadOp StencilLoadOp;
/// <summary>
/// Determines what is done with the texture slice stencil values
/// at the end of the render pass. <br/>
///
/// Store:
/// Stores the results of the render pass in the texture slice memory. <br/>
///
/// DontCare:
/// The driver will do whatever it wants with the texture slice memory.
/// This is usually a good option for stencil textures that don't need to be reused.
/// </summary>
public StoreOp StencilStoreOp; public StoreOp StencilStoreOp;
/// <summary>
/// Specifies data dependency behavior. This option is ignored if LoadOp or StencilLoadOp is Load. <br/>
///
/// Cycle:
/// If this texture slice has been used in commands that have not completed,
/// the implementation may prevent a dependency on those commands
/// at the cost of increased memory usage.
/// You may NOT assume that any of the previous texture (not slice!) data is retained.
/// This may prevent stalls when frequently reusing a texture slice in rendering. <br/>
///
/// SafeOverwrite:
/// Overwrites the data safely using a GPU memory barrier.
/// </summary>
public WriteOptions WriteOption;
public DepthStencilAttachmentInfo( public DepthStencilAttachmentInfo(
Texture texture, TextureSlice textureSlice,
WriteOptions writeOption,
DepthStencilValue clearValue, DepthStencilValue clearValue,
StoreOp depthStoreOp = StoreOp.Store, StoreOp depthStoreOp = StoreOp.DontCare,
StoreOp stencilStoreOp = StoreOp.Store StoreOp stencilStoreOp = StoreOp.DontCare
) ){
{ TextureSlice = textureSlice;
Texture = texture;
Depth = 0;
Layer = 0;
Level = 0;
DepthStencilClearValue = clearValue; DepthStencilClearValue = clearValue;
LoadOp = LoadOp.Clear; LoadOp = LoadOp.Clear;
StoreOp = depthStoreOp; StoreOp = depthStoreOp;
StencilLoadOp = LoadOp.Clear; StencilLoadOp = LoadOp.Clear;
StencilStoreOp = stencilStoreOp; StencilStoreOp = stencilStoreOp;
WriteOption = writeOption;
} }
public DepthStencilAttachmentInfo( public DepthStencilAttachmentInfo(
Texture texture, TextureSlice textureSlice,
WriteOptions writeOption,
LoadOp loadOp = LoadOp.DontCare, LoadOp loadOp = LoadOp.DontCare,
StoreOp storeOp = StoreOp.Store, StoreOp storeOp = StoreOp.DontCare,
LoadOp stencilLoadOp = LoadOp.DontCare, LoadOp stencilLoadOp = LoadOp.DontCare,
StoreOp stencilStoreOp = StoreOp.Store StoreOp stencilStoreOp = StoreOp.DontCare
) { ) {
Texture = texture; TextureSlice = textureSlice;
Depth = 0;
Layer = 0;
Level = 0;
DepthStencilClearValue = new DepthStencilValue(); DepthStencilClearValue = new DepthStencilValue();
LoadOp = loadOp; LoadOp = loadOp;
StoreOp = storeOp; StoreOp = storeOp;
StencilLoadOp = stencilLoadOp; StencilLoadOp = stencilLoadOp;
StencilStoreOp = stencilStoreOp; StencilStoreOp = stencilStoreOp;
} WriteOption = writeOption;
public DepthStencilAttachmentInfo(
Texture texture,
DepthStencilValue depthStencilValue,
LoadOp loadOp,
StoreOp storeOp,
LoadOp stencilLoadOp,
StoreOp stencilStoreOp
) {
Texture = texture;
Depth = 0;
Layer = 0;
Level = 0;
DepthStencilClearValue = depthStencilValue;
LoadOp = loadOp;
StoreOp = storeOp;
StencilLoadOp = stencilLoadOp;
StencilStoreOp = stencilStoreOp;
} }
public Refresh.DepthStencilAttachmentInfo ToRefresh() public Refresh.DepthStencilAttachmentInfo ToRefresh()
{ {
return new Refresh.DepthStencilAttachmentInfo return new Refresh.DepthStencilAttachmentInfo
{ {
texture = Texture.Handle, textureSlice = TextureSlice.ToRefreshTextureSlice(),
depth = Depth,
layer = Layer,
level = Level,
depthStencilClearValue = DepthStencilClearValue.ToRefresh(), depthStencilClearValue = DepthStencilClearValue.ToRefresh(),
loadOp = (Refresh.LoadOp) LoadOp, loadOp = (Refresh.LoadOp) LoadOp,
storeOp = (Refresh.StoreOp) StoreOp, storeOp = (Refresh.StoreOp) StoreOp,
stencilLoadOp = (Refresh.LoadOp) StencilLoadOp, stencilLoadOp = (Refresh.LoadOp) StencilLoadOp,
stencilStoreOp = (Refresh.StoreOp) StencilStoreOp stencilStoreOp = (Refresh.StoreOp) StencilStoreOp,
writeOption = (Refresh.WriteOptions) WriteOption
}; };
} }
} }
@ -383,23 +465,18 @@ namespace MoonWorks.Graphics
} }
} }
/// <summary>
/// Parameters for a copy between buffer and image.
/// </summary>
/// <param name="BufferOffset">The offset into the buffer.</param>
/// <param name="BufferStride">If 0, image data is assumed tightly packed.</param>
/// <param name="BufferImageHeight">If 0, image data is assumed tightly packed.</param>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct BufferImageCopy public readonly record struct BufferImageCopy(
{ uint BufferOffset,
public uint BufferOffset; uint BufferStride,
public uint BufferStride; // if 0, image assumed to be tightly packed uint BufferImageHeight
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() public Refresh.BufferImageCopy ToRefresh()
{ {
return new Refresh.BufferImageCopy return new Refresh.BufferImageCopy

View File

@ -21,8 +21,8 @@ namespace MoonWorks.Graphics
uint dataOffset = 0; uint dataOffset = 0;
uint dataSize = 1024; uint dataSize = 1024;
List<(GpuBuffer, BufferCopy)> BufferUploads = new List<(GpuBuffer, BufferCopy)>(); List<(GpuBuffer, BufferCopy, WriteOptions)> BufferUploads = new List<(GpuBuffer, BufferCopy, WriteOptions)>();
List<(TextureSlice, uint)> TextureUploads = new List<(TextureSlice, uint)>(); List<(TextureRegion, uint, WriteOptions)> TextureUploads = new List<(TextureRegion, uint, WriteOptions)>();
public ResourceUploader(GraphicsDevice device) : base(device) public ResourceUploader(GraphicsDevice device) : base(device)
{ {
@ -39,7 +39,7 @@ namespace MoonWorks.Graphics
var lengthInBytes = (uint) (Marshal.SizeOf<T>() * data.Length); var lengthInBytes = (uint) (Marshal.SizeOf<T>() * data.Length);
var gpuBuffer = new GpuBuffer(Device, usageFlags, lengthInBytes); var gpuBuffer = new GpuBuffer(Device, usageFlags, lengthInBytes);
SetBufferData(gpuBuffer, 0, data); SetBufferData(gpuBuffer, 0, data, WriteOptions.SafeOverwrite);
return gpuBuffer; return gpuBuffer;
} }
@ -47,7 +47,7 @@ namespace MoonWorks.Graphics
/// <summary> /// <summary>
/// Prepares upload of data into a GpuBuffer. /// Prepares upload of data into a GpuBuffer.
/// </summary> /// </summary>
public void SetBufferData<T>(GpuBuffer buffer, uint bufferOffsetInElements, Span<T> data) where T : unmanaged public void SetBufferData<T>(GpuBuffer buffer, uint bufferOffsetInElements, Span<T> data, WriteOptions option) where T : unmanaged
{ {
uint elementSize = (uint) Marshal.SizeOf<T>(); uint elementSize = (uint) Marshal.SizeOf<T>();
uint offsetInBytes = elementSize * bufferOffsetInElements; uint offsetInBytes = elementSize * bufferOffsetInElements;
@ -60,15 +60,15 @@ namespace MoonWorks.Graphics
} }
var bufferCopyParams = new BufferCopy(resourceOffset, offsetInBytes, lengthInBytes); var bufferCopyParams = new BufferCopy(resourceOffset, offsetInBytes, lengthInBytes);
BufferUploads.Add((buffer, bufferCopyParams)); BufferUploads.Add((buffer, bufferCopyParams, option));
} }
// Textures // Textures
public Texture CreateTexture2D(Span<byte> pixelData, uint width, uint height) public Texture CreateTexture2D<T>(Span<T> pixelData, uint width, uint height) where T : unmanaged
{ {
var texture = Texture.CreateTexture2D(Device, width, height, TextureFormat.R8G8B8A8, TextureUsageFlags.Sampler); var texture = Texture.CreateTexture2D(Device, width, height, TextureFormat.R8G8B8A8, TextureUsageFlags.Sampler);
SetTextureData(texture, pixelData); SetTextureData(texture, pixelData, WriteOptions.SafeOverwrite);
return texture; return texture;
} }
@ -142,12 +142,14 @@ namespace MoonWorks.Graphics
var byteSpan = new Span<byte>(byteBuffer, levelSize); var byteSpan = new Span<byte>(byteBuffer, levelSize);
stream.ReadExactly(byteSpan); stream.ReadExactly(byteSpan);
var textureSlice = new TextureSlice var textureRegion = new TextureRegion
{ {
Texture = texture, TextureSlice = new TextureSlice
MipLevel = (uint) level, {
BaseLayer = (uint) face, Texture = texture,
LayerCount = 1, Layer = (uint) face,
MipLevel = (uint) level
},
X = 0, X = 0,
Y = 0, Y = 0,
Z = 0, Z = 0,
@ -156,7 +158,7 @@ namespace MoonWorks.Graphics
Depth = 1 Depth = 1
}; };
SetTextureData(textureSlice, byteSpan); SetTextureData(textureRegion, byteSpan, WriteOptions.SafeOverwrite);
NativeMemory.Free(byteBuffer); NativeMemory.Free(byteBuffer);
} }
@ -174,36 +176,36 @@ namespace MoonWorks.Graphics
return CreateTextureFromDDS(stream); return CreateTextureFromDDS(stream);
} }
public void SetTextureDataFromCompressed(TextureSlice textureSlice, Span<byte> compressedImageData) public void SetTextureDataFromCompressed(TextureRegion textureRegion, Span<byte> compressedImageData)
{ {
var pixelData = ImageUtils.GetPixelDataFromBytes(compressedImageData, out var _, out var _, out var sizeInBytes); var pixelData = ImageUtils.GetPixelDataFromBytes(compressedImageData, out var _, out var _, out var sizeInBytes);
var pixelSpan = new Span<byte>((void*) pixelData, (int) sizeInBytes); var pixelSpan = new Span<byte>((void*) pixelData, (int) sizeInBytes);
SetTextureData(textureSlice, pixelSpan); SetTextureData(textureRegion, pixelSpan, WriteOptions.SafeOverwrite);
ImageUtils.FreePixelData(pixelData); ImageUtils.FreePixelData(pixelData);
} }
public void SetTextureDataFromCompressed(TextureSlice textureSlice, Stream compressedImageStream) public void SetTextureDataFromCompressed(TextureRegion textureRegion, Stream compressedImageStream)
{ {
var length = compressedImageStream.Length; var length = compressedImageStream.Length;
var buffer = NativeMemory.Alloc((nuint) length); var buffer = NativeMemory.Alloc((nuint) length);
var span = new Span<byte>(buffer, (int) length); var span = new Span<byte>(buffer, (int) length);
compressedImageStream.ReadExactly(span); compressedImageStream.ReadExactly(span);
SetTextureDataFromCompressed(textureSlice, span); SetTextureDataFromCompressed(textureRegion, span);
NativeMemory.Free(buffer); NativeMemory.Free(buffer);
} }
public void SetTextureDataFromCompressed(TextureSlice textureSlice, string compressedImageFilePath) public void SetTextureDataFromCompressed(TextureRegion textureRegion, string compressedImageFilePath)
{ {
var fileStream = new FileStream(compressedImageFilePath, FileMode.Open, FileAccess.Read); var fileStream = new FileStream(compressedImageFilePath, FileMode.Open, FileAccess.Read);
SetTextureDataFromCompressed(textureSlice, fileStream); SetTextureDataFromCompressed(textureRegion, fileStream);
} }
/// <summary> /// <summary>
/// Prepares upload of pixel data into a TextureSlice. /// Prepares upload of pixel data into a TextureSlice.
/// </summary> /// </summary>
public void SetTextureData<T>(TextureSlice textureSlice, Span<T> data) where T : unmanaged public void SetTextureData<T>(TextureRegion textureRegion, Span<T> data, WriteOptions option) where T : unmanaged
{ {
var elementSize = Marshal.SizeOf<T>(); var elementSize = Marshal.SizeOf<T>();
var dataLengthInBytes = (uint) (elementSize * data.Length); var dataLengthInBytes = (uint) (elementSize * data.Length);
@ -211,10 +213,10 @@ namespace MoonWorks.Graphics
uint resourceOffset; uint resourceOffset;
fixed (T* dataPtr = data) fixed (T* dataPtr = data)
{ {
resourceOffset = CopyDataAligned(dataPtr, dataLengthInBytes, Texture.TexelSize(textureSlice.Texture.Format)); resourceOffset = CopyDataAligned(dataPtr, dataLengthInBytes, Texture.TexelSize(textureRegion.TextureSlice.Texture.Format));
} }
TextureUploads.Add((textureSlice, resourceOffset)); TextureUploads.Add((textureRegion, resourceOffset, option));
} }
// Upload // Upload
@ -257,32 +259,34 @@ namespace MoonWorks.Graphics
} }
var dataSpan = new Span<byte>(data, (int) dataSize); var dataSpan = new Span<byte>(data, (int) dataSize);
TransferBuffer.SetData(dataSpan, SetDataOptions.Discard); TransferBuffer.SetData(dataSpan, TransferOptions.Cycle);
} }
private void RecordUploadCommands(CommandBuffer commandBuffer) private void RecordUploadCommands(CommandBuffer commandBuffer)
{ {
commandBuffer.BeginCopyPass(); commandBuffer.BeginCopyPass();
foreach (var (gpuBuffer, bufferCopyParams) in BufferUploads) foreach (var (gpuBuffer, bufferCopyParams, option) in BufferUploads)
{ {
commandBuffer.UploadToBuffer( commandBuffer.UploadToBuffer(
TransferBuffer, TransferBuffer,
gpuBuffer, gpuBuffer,
bufferCopyParams bufferCopyParams,
option
); );
} }
foreach (var (textureSlice, offset) in TextureUploads) foreach (var (textureRegion, offset, option) in TextureUploads)
{ {
commandBuffer.UploadToTexture( commandBuffer.UploadToTexture(
TransferBuffer, TransferBuffer,
textureSlice, textureRegion,
new BufferImageCopy( new BufferImageCopy(
offset, offset,
0, 0,
0 0
) ),
option
); );
} }

View File

@ -61,7 +61,11 @@ namespace MoonWorks.Graphics
refreshGraphicsPipelineCreateInfo.blendConstants[2] = blendConstants.B; refreshGraphicsPipelineCreateInfo.blendConstants[2] = blendConstants.B;
refreshGraphicsPipelineCreateInfo.blendConstants[3] = blendConstants.A; refreshGraphicsPipelineCreateInfo.blendConstants[3] = blendConstants.A;
refreshGraphicsPipelineCreateInfo.depthStencilState.stencilState = depthStencilState.StencilState.ToRefresh(); refreshGraphicsPipelineCreateInfo.depthStencilState.backStencilState = depthStencilState.BackStencilState.ToRefresh();
refreshGraphicsPipelineCreateInfo.depthStencilState.frontStencilState = depthStencilState.FrontStencilState.ToRefresh();
refreshGraphicsPipelineCreateInfo.depthStencilState.compareMask = depthStencilState.CompareMask;
refreshGraphicsPipelineCreateInfo.depthStencilState.writeMask = depthStencilState.WriteMask;
refreshGraphicsPipelineCreateInfo.depthStencilState.reference = depthStencilState.Reference;
refreshGraphicsPipelineCreateInfo.depthStencilState.compareOp = (Refresh.CompareOp) depthStencilState.CompareOp; refreshGraphicsPipelineCreateInfo.depthStencilState.compareOp = (Refresh.CompareOp) depthStencilState.CompareOp;
refreshGraphicsPipelineCreateInfo.depthStencilState.depthBoundsTestEnable = Conversions.BoolToByte(depthStencilState.DepthBoundsTestEnable); refreshGraphicsPipelineCreateInfo.depthStencilState.depthBoundsTestEnable = Conversions.BoolToByte(depthStencilState.DepthBoundsTestEnable);
refreshGraphicsPipelineCreateInfo.depthStencilState.depthTestEnable = Conversions.BoolToByte(depthStencilState.DepthTestEnable); refreshGraphicsPipelineCreateInfo.depthStencilState.depthTestEnable = Conversions.BoolToByte(depthStencilState.DepthTestEnable);

View File

@ -14,6 +14,7 @@ namespace MoonWorks.Graphics
public uint Depth { get; } public uint Depth { get; }
public TextureFormat Format { get; internal set; } public TextureFormat Format { get; internal set; }
public bool IsCube { get; } public bool IsCube { get; }
public uint LayerCount { get; }
public uint LevelCount { get; } public uint LevelCount { get; }
public SampleCount SampleCount { get; } public SampleCount SampleCount { get; }
public TextureUsageFlags UsageFlags { get; } public TextureUsageFlags UsageFlags { get; }
@ -46,6 +47,7 @@ namespace MoonWorks.Graphics
Height = height, Height = height,
Depth = 1, Depth = 1,
IsCube = false, IsCube = false,
LayerCount = 1,
LevelCount = levelCount, LevelCount = levelCount,
SampleCount = sampleCount, SampleCount = sampleCount,
Format = format, Format = format,
@ -56,15 +58,43 @@ namespace MoonWorks.Graphics
} }
/// <summary> /// <summary>
/// Creates a 3D texture. /// Creates a 2D texture array.
/// </summary> /// </summary>
/// <param name="device">An initialized GraphicsDevice.</param> /// <param name="device">An initialized GraphicsDevice.</param>
/// <param name="width">The width of the texture.</param> /// <param name="width">The width of the texture.</param>
/// <param name="height">The height of the texture.</param> /// <param name="height">The height of the texture.</param>
/// <param name="depth">The depth of the texture.</param> /// <param name="layerCount">The layer count of the texture.</param>
/// <param name="format">The format of the texture.</param> /// <param name="format">The format of the texture.</param>
/// <param name="usageFlags">Specifies how the texture will be used.</param> /// <param name="usageFlags">Specifies how the texture will be used.</param>
/// <param name="levelCount">Specifies the number of mip levels.</param> /// <param name="levelCount">Specifies the number of mip levels.</param>
public static Texture CreateTexture2DArray(
GraphicsDevice device,
uint width,
uint height,
uint layerCount,
TextureFormat format,
TextureUsageFlags usageFlags,
uint levelCount = 1
) {
var textureCreateInfo = new TextureCreateInfo
{
Width = width,
Height = height,
Depth = 1,
IsCube = false,
LayerCount = layerCount,
LevelCount = levelCount,
Format = format,
UsageFlags = usageFlags
};
return new Texture(device, textureCreateInfo);
}
/// <summary>
/// Creates a 3D texture.
/// Note that the width, height and depth all form one slice and cannot be subdivided in a texture slice.
/// </summary>
public static Texture CreateTexture3D( public static Texture CreateTexture3D(
GraphicsDevice device, GraphicsDevice device,
uint width, uint width,
@ -80,6 +110,7 @@ namespace MoonWorks.Graphics
Height = height, Height = height,
Depth = depth, Depth = depth,
IsCube = false, IsCube = false,
LayerCount = 1,
LevelCount = levelCount, LevelCount = levelCount,
Format = format, Format = format,
UsageFlags = usageFlags UsageFlags = usageFlags
@ -109,6 +140,7 @@ namespace MoonWorks.Graphics
Height = size, Height = size,
Depth = 1, Depth = 1,
IsCube = true, IsCube = true,
LayerCount = 6,
LevelCount = levelCount, LevelCount = levelCount,
Format = format, Format = format,
UsageFlags = usageFlags UsageFlags = usageFlags
@ -137,16 +169,14 @@ namespace MoonWorks.Graphics
Height = textureCreateInfo.Height; Height = textureCreateInfo.Height;
Depth = textureCreateInfo.Depth; Depth = textureCreateInfo.Depth;
IsCube = textureCreateInfo.IsCube; IsCube = textureCreateInfo.IsCube;
LayerCount = textureCreateInfo.LayerCount;
LevelCount = textureCreateInfo.LevelCount; LevelCount = textureCreateInfo.LevelCount;
SampleCount = textureCreateInfo.SampleCount; SampleCount = textureCreateInfo.SampleCount;
UsageFlags = textureCreateInfo.UsageFlags; UsageFlags = textureCreateInfo.UsageFlags;
Size = Width * Height * BytesPerPixel(Format) / BlockSizeSquared(Format); Size = Width * Height * BytesPerPixel(Format) / BlockSizeSquared(Format);
} }
public static implicit operator TextureSlice(Texture t) => new TextureSlice(t); // Used by Window. Swapchain texture handles are managed by the driver backend.
// Used by AcquireSwapchainTexture.
// Should not be tracked, because swapchain textures are managed by Vulkan.
internal Texture( internal Texture(
GraphicsDevice device, GraphicsDevice device,
TextureFormat format TextureFormat format
@ -270,5 +300,8 @@ namespace MoonWorks.Graphics
return 0; return 0;
} }
} }
public static implicit operator TextureSlice(Texture t) => new TextureSlice(t);
public static implicit operator TextureRegion(Texture t) => new TextureRegion(t);
} }
} }

View File

@ -61,7 +61,7 @@ namespace MoonWorks.Graphics
public unsafe uint SetData<T>( public unsafe uint SetData<T>(
Span<T> data, Span<T> data,
uint bufferOffsetInBytes, uint bufferOffsetInBytes,
SetDataOptions setDataOption TransferOptions setDataOption
) where T : unmanaged ) where T : unmanaged
{ {
var elementSize = Marshal.SizeOf<T>(); var elementSize = Marshal.SizeOf<T>();
@ -73,7 +73,7 @@ namespace MoonWorks.Graphics
fixed (T* dataPtr = data) fixed (T* dataPtr = data)
{ {
Refresh.Refresh_SetData( Refresh.Refresh_SetTransferData(
Device.Handle, Device.Handle,
(nint) dataPtr, (nint) dataPtr,
Handle, Handle,
@ -83,7 +83,7 @@ namespace MoonWorks.Graphics
dstOffset = bufferOffsetInBytes, dstOffset = bufferOffsetInBytes,
size = dataLengthInBytes size = dataLengthInBytes
}, },
(Refresh.SetDataOptions) setDataOption (Refresh.TransferOptions) setDataOption
); );
} }
@ -102,7 +102,7 @@ namespace MoonWorks.Graphics
/// </summary> /// </summary>
public unsafe uint SetData<T>( public unsafe uint SetData<T>(
Span<T> data, Span<T> data,
SetDataOptions setDataOption TransferOptions setDataOption
) where T : unmanaged ) where T : unmanaged
{ {
return SetData(data, 0, setDataOption); return SetData(data, 0, setDataOption);
@ -125,7 +125,7 @@ namespace MoonWorks.Graphics
fixed (T* dataPtr = data) fixed (T* dataPtr = data)
{ {
Refresh.Refresh_GetData( Refresh.Refresh_GetTransferData(
Device.Handle, Device.Handle,
Handle, Handle,
(nint) dataPtr, (nint) dataPtr,
@ -144,7 +144,7 @@ namespace MoonWorks.Graphics
{ {
if (copyLengthInBytes > bufferLengthInBytes + offsetInBytes) if (copyLengthInBytes > bufferLengthInBytes + offsetInBytes)
{ {
throw new InvalidOperationException($"SetData overflow! Transfer buffer length {bufferLengthInBytes}, offset {offsetInBytes}, copy length {copyLengthInBytes}"); throw new InvalidOperationException($"Data overflow! Transfer buffer length {bufferLengthInBytes}, offset {offsetInBytes}, copy length {copyLengthInBytes}");
} }
} }
#endif #endif

View File

@ -11,9 +11,29 @@
public bool DepthTestEnable; public bool DepthTestEnable;
/// <summary> /// <summary>
/// Describes the stencil operation. /// Describes the back-face stencil operation.
/// </summary> /// </summary>
public StencilOpState StencilState; public StencilOpState BackStencilState;
/// <summary>
/// Describes the front-face stencil operation.
/// </summary>
public StencilOpState FrontStencilState;
/// <summary>
/// The compare mask for stencil ops.
/// </summary>
public uint CompareMask;
/// <summary>
/// The write mask for stencil ops.
/// </summary>
public uint WriteMask;
/// <summary>
/// The stencil reference value.
/// </summary>
public uint Reference;
/// <summary> /// <summary>
/// The comparison operator used in the depth test. /// The comparison operator used in the depth test.

View File

@ -11,6 +11,7 @@ namespace MoonWorks.Graphics
public uint Height; public uint Height;
public uint Depth; public uint Depth;
public bool IsCube; public bool IsCube;
public uint LayerCount;
public uint LevelCount; public uint LevelCount;
public SampleCount SampleCount; public SampleCount SampleCount;
public TextureFormat Format; public TextureFormat Format;
@ -24,6 +25,7 @@ namespace MoonWorks.Graphics
height = Height, height = Height,
depth = Depth, depth = Depth,
isCube = Conversions.BoolToByte(IsCube), isCube = Conversions.BoolToByte(IsCube),
layerCount = LayerCount,
levelCount = LevelCount, levelCount = LevelCount,
sampleCount = (Refresh.SampleCount) SampleCount, sampleCount = (Refresh.SampleCount) SampleCount,
format = (Refresh.TextureFormat) Format, format = (Refresh.TextureFormat) Format,

View File

@ -0,0 +1,46 @@
using RefreshCS;
namespace MoonWorks.Graphics
{
/// <summary>
/// A texture region specifies a subregion of a texture.
/// These are used by copy commands.
/// </summary>
public struct TextureRegion
{
public TextureSlice TextureSlice;
public uint X;
public uint Y;
public uint Z;
public uint Width;
public uint Height;
public uint Depth;
public uint Size => (Width * Height * Depth * Texture.BytesPerPixel(TextureSlice.Texture.Format) / Texture.BlockSizeSquared(TextureSlice.Texture.Format)) >> (int) TextureSlice.MipLevel;
public TextureRegion(Texture texture)
{
TextureSlice = new TextureSlice(texture);
X = 0;
Y = 0;
Z = 0;
Width = texture.Width;
Height = texture.Height;
Depth = texture.Depth;
}
public Refresh.TextureRegion ToRefreshTextureRegion()
{
return new Refresh.TextureRegion
{
textureSlice = TextureSlice.ToRefreshTextureSlice(),
x = X,
y = Y,
z = Z,
w = Width,
h = Height,
d = Depth
};
}
}
}

View File

@ -3,55 +3,31 @@
namespace MoonWorks.Graphics namespace MoonWorks.Graphics
{ {
/// <summary> /// <summary>
/// A texture slice specifies a subregion of a texture. /// A texture slice specifies a subresource of a texture.
/// Many operations can use texture slices in place of textures for the sake of convenience.
/// </summary> /// </summary>
public struct TextureSlice public struct TextureSlice
{ {
public Texture Texture; public Texture Texture;
public uint MipLevel; public uint MipLevel;
public uint BaseLayer; public uint Layer;
public uint LayerCount;
public uint X;
public uint Y;
public uint Z;
public uint Width;
public uint Height;
public uint Depth;
public uint Size => (Width * Height * Depth * LayerCount * Texture.BytesPerPixel(Texture.Format) / Texture.BlockSizeSquared(Texture.Format)) >> (int) MipLevel; public uint Size => (Texture.Width * Texture.Height * Texture.Depth * Texture.BytesPerPixel(Texture.Format) / Texture.BlockSizeSquared(Texture.Format)) >> (int) MipLevel;
public TextureSlice(Texture texture) public TextureSlice(Texture texture)
{ {
Texture = texture; Texture = texture;
MipLevel = 0; MipLevel = 0;
BaseLayer = 0; Layer = 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() public Refresh.TextureSlice ToRefreshTextureSlice()
{ {
Refresh.TextureSlice textureSlice = new Refresh.TextureSlice return new Refresh.TextureSlice
{ {
texture = Texture.Handle, texture = Texture.Handle,
mipLevel = MipLevel, mipLevel = MipLevel,
baseLayer = BaseLayer, layer = Layer
layerCount = LayerCount,
x = X,
y = Y,
z = Z,
w = Width,
h = Height,
d = Depth
}; };
return textureSlice;
} }
} }
} }

View File

@ -243,9 +243,9 @@ namespace MoonWorks.Video
TransferBuffer?.Dispose(); TransferBuffer?.Dispose();
TransferBuffer = new TransferBuffer(Device, (uint) (ySpan.Length + uSpan.Length + vSpan.Length)); TransferBuffer = new TransferBuffer(Device, (uint) (ySpan.Length + uSpan.Length + vSpan.Length));
} }
TransferBuffer.SetData(ySpan, 0, SetDataOptions.Discard); TransferBuffer.SetData(ySpan, 0, TransferOptions.Cycle);
TransferBuffer.SetData(uSpan, (uint) ySpan.Length, SetDataOptions.Overwrite); TransferBuffer.SetData(uSpan, (uint) ySpan.Length, TransferOptions.Overwrite);
TransferBuffer.SetData(vSpan, (uint) (ySpan.Length + uSpan.Length), SetDataOptions.Overwrite); TransferBuffer.SetData(vSpan, (uint) (ySpan.Length + uSpan.Length), TransferOptions.Overwrite);
commandBuffer.BeginCopyPass(); commandBuffer.BeginCopyPass();
@ -257,7 +257,8 @@ namespace MoonWorks.Video
BufferOffset = 0, BufferOffset = 0,
BufferStride = CurrentStream.yStride, BufferStride = CurrentStream.yStride,
BufferImageHeight = yTexture.Height BufferImageHeight = yTexture.Height
} },
WriteOptions.Cycle
); );
commandBuffer.UploadToTexture( commandBuffer.UploadToTexture(
@ -267,7 +268,8 @@ namespace MoonWorks.Video
BufferOffset = (uint) ySpan.Length, BufferOffset = (uint) ySpan.Length,
BufferStride = CurrentStream.uvStride, BufferStride = CurrentStream.uvStride,
BufferImageHeight = uTexture.Height BufferImageHeight = uTexture.Height
} },
WriteOptions.Cycle
); );
commandBuffer.UploadToTexture( commandBuffer.UploadToTexture(
@ -278,13 +280,14 @@ namespace MoonWorks.Video
BufferOffset = (uint) (ySpan.Length + uSpan.Length), BufferOffset = (uint) (ySpan.Length + uSpan.Length),
BufferStride = CurrentStream.uvStride, BufferStride = CurrentStream.uvStride,
BufferImageHeight = vTexture.Height BufferImageHeight = vTexture.Height
} },
WriteOptions.Cycle
); );
commandBuffer.EndCopyPass(); commandBuffer.EndCopyPass();
commandBuffer.BeginRenderPass( commandBuffer.BeginRenderPass(
new ColorAttachmentInfo(RenderTexture, Color.Black) new ColorAttachmentInfo(RenderTexture, WriteOptions.Cycle, Color.Black)
); );
commandBuffer.BindGraphicsPipeline(Device.VideoPipeline); commandBuffer.BindGraphicsPipeline(Device.VideoPipeline);

View File

@ -21,6 +21,15 @@ namespace MoonWorks
public bool Claimed { get; internal set; } public bool Claimed { get; internal set; }
public MoonWorks.Graphics.TextureFormat SwapchainFormat { get; internal set; } public MoonWorks.Graphics.TextureFormat SwapchainFormat { get; internal set; }
public (int, int) Position
{
get
{
SDL.SDL_GetWindowPosition(Handle, out var x, out var y);
return (x, y);
}
}
private bool IsDisposed; private bool IsDisposed;
private static Dictionary<uint, Window> idLookup = new Dictionary<uint, Window>(); private static Dictionary<uint, Window> idLookup = new Dictionary<uint, Window>();
@ -113,6 +122,14 @@ namespace MoonWorks
} }
} }
/// <summary>
/// Sets the window position.
/// </summary>
public void SetPosition(int x, int y)
{
SDL.SDL_SetWindowPosition(Handle, x, y);
}
internal static Window Lookup(uint windowID) internal static Window Lookup(uint windowID)
{ {
return idLookup.ContainsKey(windowID) ? idLookup[windowID] : null; return idLookup.ContainsKey(windowID) ? idLookup[windowID] : null;