using NativeEngine; using System.Runtime.InteropServices; namespace Sandbox; partial class Graphics { private static void ValidateMaterial( Material material ) { if ( material is null || material.native.IsNull ) throw new ArgumentException( $"{nameof( material )} is invalid.", nameof( material ) ); } private static void ValidateVertexBuffer( GpuBuffer vertexBuffer ) { if ( !vertexBuffer.IsValid() ) throw new ArgumentException( $"{nameof( vertexBuffer )} is invalid.", nameof( vertexBuffer ) ); if ( !vertexBuffer.Usage.Contains( GpuBuffer.UsageFlags.Vertex ) ) throw new ArgumentException( $"{nameof( vertexBuffer )} does not have the required usage flag '{GpuBuffer.UsageFlags.Vertex}'.", nameof( vertexBuffer ) ); } private static void ValidateIndexBuffer( GpuBuffer indexBuffer ) { if ( !indexBuffer.IsValid() ) throw new ArgumentException( $"{nameof( indexBuffer )} is invalid.", nameof( indexBuffer ) ); if ( !indexBuffer.Usage.Contains( GpuBuffer.UsageFlags.Index ) ) throw new ArgumentException( $"{nameof( indexBuffer )} does not have the required usage flag '{GpuBuffer.UsageFlags.Index}'.", nameof( indexBuffer ) ); if ( indexBuffer.ElementSize != sizeof( uint ) && indexBuffer.ElementSize != sizeof( ushort ) ) throw new ArgumentOutOfRangeException( nameof( indexBuffer ), $"{nameof( indexBuffer )} must be size of {sizeof( uint )} or {sizeof( ushort )}." ); } private static void ValidateIndirectBuffer( GpuBuffer indirectBuffer ) { if ( !indirectBuffer.IsValid() ) throw new ArgumentException( $"{nameof( indirectBuffer )} is invalid.", nameof( indirectBuffer ) ); if ( !indirectBuffer.Usage.Contains( GpuBuffer.UsageFlags.IndirectDrawArguments ) ) throw new ArgumentException( $"Buffer must have the required usage flag '{GpuBuffer.UsageFlags.IndirectDrawArguments}'", nameof( indirectBuffer ) ); } private static NativeEngine.VertexLayout GetVertexLayout() where T : unmanaged { var vertexType = VertexLayout.Get(); if ( vertexType.IsNull ) throw new ArgumentException( $"{nameof( T )} vertex layout is invalid.", nameof( T ) ); return vertexType; } private static bool SetRenderState( GpuBuffer vertexBuffer, Material material, RenderAttributes attributes ) where T : unmanaged { ValidateVertexBuffer( vertexBuffer ); ValidateMaterial( material ); var layout = GetVertexLayout(); attributes ??= Attributes; return RenderTools.SetRenderState( Context, attributes.Get(), material.native.GetMode( SceneLayer ), layout, Stats ); } private static bool SetRenderState( Material material, RenderAttributes attributes ) { ValidateMaterial( material ); attributes ??= Attributes; return RenderTools.SetRenderState( Context, attributes.Get(), material.native.GetMode( SceneLayer ), default, Stats ); } /// /// Draws geometry using a vertex buffer and material. /// /// The vertex type used for vertex layout. /// The GPU buffer containing vertex data. /// The material to use for rendering. /// The starting vertex index for rendering. /// The number of vertices to render. If 0, uses all vertices in the buffer. /// Optional render attributes to apply only for this draw call. /// The type of primitives to render. Defaults to triangles. public static void Draw( GpuBuffer vertexBuffer, Material material, int startVertex = 0, int vertexCount = 0, RenderAttributes attributes = null, PrimitiveType primitiveType = PrimitiveType.Triangles ) where T : unmanaged { AssertRenderBlock(); if ( !SetRenderState( vertexBuffer, material, attributes ) ) return; if ( vertexCount <= 0 ) vertexCount = vertexBuffer.ElementCount; if ( vertexCount <= 0 ) return; if ( startVertex < 0 || startVertex >= vertexBuffer.ElementCount ) throw new ArgumentOutOfRangeException( nameof( startVertex ), $"{nameof( startVertex )} is out of bounds of the vertex buffer." ); if ( startVertex + vertexCount > vertexBuffer.ElementCount ) throw new ArgumentOutOfRangeException( nameof( vertexCount ), $"{nameof( vertexCount )} exceeds the bounds of the vertex buffer." ); Context.BindVertexBuffer( 0, vertexBuffer.native, 0 ); Context.Draw( (RenderPrimitiveType)primitiveType, startVertex, vertexCount ); } /// /// Draws indexed geometry using vertex and index buffers. /// /// The vertex type used for vertex layout. /// The GPU buffer containing vertex data. /// The GPU buffer containing index data. /// The material to use for rendering. /// The starting index for rendering. /// The number of indices to render. If 0, uses all indices in the buffer. /// Optional render attributes to apply only for this draw call. /// The type of primitives to render. Defaults to triangles. public static void Draw( GpuBuffer vertexBuffer, GpuBuffer indexBuffer, Material material, int startIndex = 0, int indexCount = 0, RenderAttributes attributes = null, PrimitiveType primitiveType = PrimitiveType.Triangles ) where T : unmanaged { AssertRenderBlock(); ValidateIndexBuffer( indexBuffer ); if ( !SetRenderState( vertexBuffer, material, attributes ) ) return; if ( indexCount <= 0 ) indexCount = indexBuffer.ElementCount; if ( indexCount <= 0 ) return; if ( startIndex < 0 || startIndex >= indexBuffer.ElementCount ) throw new ArgumentOutOfRangeException( nameof( startIndex ), $"{nameof( startIndex )} is out of bounds of the index buffer." ); if ( startIndex + indexCount > indexBuffer.ElementCount ) throw new ArgumentOutOfRangeException( nameof( indexCount ), $"{nameof( indexCount )} exceeds the bounds of the index buffer." ); Context.BindVertexBuffer( 0, vertexBuffer.native, 0 ); Context.BindIndexBuffer( indexBuffer.native, indexBuffer.ElementSize, 0 ); Context.DrawIndexed( (RenderPrimitiveType)primitiveType, startIndex, indexCount, 0, 0 ); } internal static void DrawInstancedIndirect( GpuBuffer vertexBuffer, Material material, GpuBuffer indirectBuffer, uint indirectElementOffset = 0, RenderAttributes attributes = null, PrimitiveType primitiveType = PrimitiveType.Triangles ) where T : unmanaged { AssertRenderBlock(); ValidateIndirectBuffer( indirectBuffer ); if ( !SetRenderState( vertexBuffer, material, attributes ) ) return; var bufferOffset = indirectElementOffset * Marshal.SizeOf(); Context.BindVertexBuffer( 0, vertexBuffer.native, 0 ); Context.DrawInstancedIndirect( (RenderPrimitiveType)primitiveType, indirectBuffer.native, (uint)bufferOffset ); } internal static void DrawInstancedIndirect( Material material, GpuBuffer indirectBuffer, uint indirectElementOffset = 0, RenderAttributes attributes = null, PrimitiveType primitiveType = PrimitiveType.Triangles ) { AssertRenderBlock(); ValidateIndirectBuffer( indirectBuffer ); if ( !SetRenderState( material, attributes ) ) return; var bufferOffset = indirectElementOffset * Marshal.SizeOf(); Context.DrawInstancedIndirect( (RenderPrimitiveType)primitiveType, indirectBuffer.native, (uint)bufferOffset ); } internal static void DrawIndexedInstanced( GpuBuffer indexBuffer, Material material, int instanceCount, RenderAttributes attributes = null, PrimitiveType primitiveType = PrimitiveType.Triangles ) { AssertRenderBlock(); ValidateIndexBuffer( indexBuffer ); if ( !SetRenderState( material, attributes ) ) return; Context.BindIndexBuffer( indexBuffer.native, indexBuffer.ElementSize, 0 ); Context.DrawIndexedInstanced( (RenderPrimitiveType)primitiveType, 0, indexBuffer.ElementCount, instanceCount, 0, 0 ); } internal static void DrawIndexedInstancedIndirect( GpuBuffer vertexBuffer, GpuBuffer indexBuffer, Material material, GpuBuffer indirectBuffer, uint indirectElementOffset = 0, RenderAttributes attributes = null, PrimitiveType primitiveType = PrimitiveType.Triangles ) where T : unmanaged { AssertRenderBlock(); ValidateIndirectBuffer( indirectBuffer ); ValidateIndexBuffer( indexBuffer ); if ( !SetRenderState( vertexBuffer, material, attributes ) ) return; var bufferOffset = indirectElementOffset * Marshal.SizeOf(); Context.BindVertexBuffer( 0, vertexBuffer.native, 0 ); Context.BindIndexBuffer( indexBuffer.native, indexBuffer.ElementSize, 0 ); Context.DrawIndexedInstancedIndirect( (RenderPrimitiveType)primitiveType, indirectBuffer.native, (uint)bufferOffset ); } internal static void DrawIndexedInstancedIndirect( GpuBuffer indexBuffer, Material material, GpuBuffer indirectBuffer, uint indirectElementOffset = 0, RenderAttributes attributes = null, PrimitiveType primitiveType = PrimitiveType.Triangles ) { AssertRenderBlock(); ValidateIndirectBuffer( indirectBuffer ); ValidateIndexBuffer( indexBuffer ); if ( !SetRenderState( material, attributes ) ) return; var bufferOffset = indirectElementOffset * Marshal.SizeOf(); Context.BindIndexBuffer( indexBuffer.native, indexBuffer.ElementSize, 0 ); Context.DrawIndexedInstancedIndirect( (RenderPrimitiveType)primitiveType, indirectBuffer.native, (uint)bufferOffset ); } }