/// <summary> /// Updates a portion of a <see cref="Texture"/> resource with new data. /// </summary> /// <param name="texture">The resource to update.</param> /// <param name="source">A pointer to the start of the data to upload.</param> /// <param name="sizeInBytes">The number of bytes to upload. This value must match the total size of the texture region specified.</param> /// <param name="x">The minimum X value of the updated region.</param> /// <param name="y">The minimum Y value of the updated region.</param> /// <param name="z">The minimum Z value of the updated region.</param> /// <param name="width">The width of the updated region, in texels.</param> /// <param name="height">The height of the updated region, in texels.</param> /// <param name="depth">The depth of the updated region, in texels.</param> /// <param name="mipLevel">The mipmap level to update. Must be less than the total number of mipmaps contained in the /// <see cref="Texture"/>.</param> /// <param name="arrayLayer">The array layer to update. Must be less than the total array layer count contained in the /// <see cref="Texture"/>.</param> public void UpdateTexture( Texture texture, IntPtr source, uint sizeInBytes, uint x, uint y, uint z, uint width, uint height, uint depth, uint mipLevel, uint arrayLayer) { #if VALIDATE_USAGE if (FormatHelpers.IsCompressedFormat(texture.Format)) { if (x % 4 != 0 || y % 4 != 0 || height % 4 != 0 || width % 4 != 0) { Util.GetMipDimensions(texture, mipLevel, out uint mipWidth, out uint mipHeight, out _); if (width != mipWidth && height != mipHeight) { throw new VeldridException($"Updates to block-compressed textures must use a region that is block-size aligned and sized."); } } } #endif UpdateTextureCore(texture, source, sizeInBytes, x, y, z, width, height, depth, mipLevel, arrayLayer); }
/// <summary> /// Creates a new <see cref="Pipeline"/> object. /// </summary> /// <param name="description">The desired properties of the created object.</param> /// <returns>A new <see cref="Pipeline"/> which, when bound to a CommandList, is used to dispatch draw commands.</returns> public Pipeline CreateGraphicsPipeline(ref GraphicsPipelineDescription description) { #if VALIDATE_USAGE if (!description.RasterizerState.DepthClipEnabled && !Features.DepthClipDisable) { throw new VeldridException( "RasterizerState.DepthClipEnabled must be true if GraphicsDeviceFeatures.DepthClipDisable is not supported."); } if (description.RasterizerState.FillMode == PolygonFillMode.Wireframe && !Features.FillModeWireframe) { throw new VeldridException( "PolygonFillMode.Wireframe requires GraphicsDeviceFeatures.FillModeWireframe."); } if (!Features.IndependentBlend) { if (description.BlendState.AttachmentStates.Length > 0) { BlendAttachmentDescription attachmentState = description.BlendState.AttachmentStates[0]; for (int i = 1; i < description.BlendState.AttachmentStates.Length; i++) { if (!attachmentState.Equals(description.BlendState.AttachmentStates[i])) { throw new VeldridException( $"If GraphcsDeviceFeatures.IndependentBlend is false, then all members of BlendState.AttachmentStates must be equal."); } } } } foreach (VertexLayoutDescription layoutDesc in description.ShaderSet.VertexLayouts) { bool hasExplicitLayout = false; uint minOffset = 0; foreach (VertexElementDescription elementDesc in layoutDesc.Elements) { if (hasExplicitLayout && elementDesc.Offset == 0) { throw new VeldridException( $"If any vertex element has an explicit offset, then all elements must have an explicit offset."); } if (elementDesc.Offset != 0 && elementDesc.Offset < minOffset) { throw new VeldridException( $"Vertex element \"{elementDesc.Name}\" has an explicit offset which overlaps with the previous element."); } minOffset = elementDesc.Offset + FormatHelpers.GetSizeInBytes(elementDesc.Format); hasExplicitLayout |= elementDesc.Offset != 0; } if (minOffset > layoutDesc.Stride) { throw new VeldridException( $"The vertex layout's stride ({layoutDesc.Stride}) is less than the full size of the vertex ({minOffset})"); } } #endif return(CreateGraphicsPipelineCore(ref description)); }
/// <summary> /// Constructs a new VertexLayoutDescription. The stride is assumed to be the sum of the size of all elements. /// </summary> /// <param name="elements">An array of <see cref="VertexElementDescription"/> objects, each describing a single element /// of vertex data.</param> public VertexLayoutDescription(params VertexElementDescription[] elements) { Elements = elements; uint computedStride = 0; for (int i = 0; i < elements.Length; i++) { computedStride += FormatHelpers.GetSizeInBytes(elements[i].Format); } Stride = computedStride; }
public static unsafe void CopyTextureRegion( void *src, uint srcX, uint srcY, uint srcZ, uint srcRowPitch, uint srcDepthPitch, void *dst, uint dstX, uint dstY, uint dstZ, uint dstRowPitch, uint dstDepthPitch, uint width, uint height, uint depth, PixelFormat format) { uint blockSize = FormatHelpers.IsCompressedFormat(format) ? 4u : 1u; uint blockSizeInBytes = blockSize > 1 ? FormatHelpers.GetBlockSizeInBytes(format) : FormatHelpers.GetSizeInBytes(format); uint compressedSrcX = srcX / blockSize; uint compressedSrcY = srcY / blockSize; uint compressedDstX = dstX / blockSize; uint compressedDstY = dstY / blockSize; uint numRows = FormatHelpers.GetNumRows(height, format); uint rowSize = width / blockSize * blockSizeInBytes; if (srcRowPitch == dstRowPitch && srcDepthPitch == dstDepthPitch) { uint totalCopySize = depth * srcDepthPitch; Buffer.MemoryCopy( src, dst, totalCopySize, totalCopySize); } else { for (uint zz = 0; zz < depth; zz++) { for (uint yy = 0; yy < numRows; yy++) { byte *rowCopyDst = (byte *)dst + dstDepthPitch * (zz + dstZ) + dstRowPitch * (yy + compressedDstY) + blockSizeInBytes * compressedDstX; byte *rowCopySrc = (byte *)src + srcDepthPitch * (zz + srcZ) + srcRowPitch * (yy + compressedSrcY) + blockSizeInBytes * compressedSrcX; Unsafe.CopyBlock(rowCopyDst, rowCopySrc, rowSize); } } } }
internal static uint ComputeMipOffset(Texture tex, uint mipLevel) { uint offset = 0; for (uint level = 0; level < mipLevel; level++) { Util.GetMipDimensions(tex, level, out uint mipWidth, out uint mipHeight, out uint mipDepth); offset += mipWidth * mipHeight * mipDepth * FormatHelpers.GetSizeInBytes(tex.Format); } return(offset); }
internal static uint ComputeMipOffset(Texture tex, uint mipLevel) { uint blockSize = FormatHelpers.IsCompressedFormat(tex.Format) ? 4u : 1u; uint offset = 0; for (uint level = 0; level < mipLevel; level++) { GetMipDimensions(tex, level, out uint mipWidth, out uint mipHeight, out uint mipDepth); uint storageWidth = Math.Max(mipWidth, blockSize); uint storageHeight = Math.Max(mipHeight, blockSize); offset += FormatHelpers.GetRegionSize(storageWidth, storageHeight, mipDepth, tex.Format); } return(offset); }
internal static void GetMipDimensions(Texture tex, uint mipLevel, out uint width, out uint height, out uint depth) { bool compressed = FormatHelpers.IsCompressedFormat(tex.Format); width = GetDimension(tex.Width, mipLevel); height = GetDimension(tex.Height, mipLevel); depth = GetDimension(tex.Depth, mipLevel); if (compressed && width >= 4) { width = (width + 3) & ~((uint)0x03); } if (compressed && height >= 4) { height = (height + 3) & ~((uint)0x03); } }
internal static uint ComputeArrayLayerOffset(Texture tex, uint arrayLayer) { if (arrayLayer == 0) { return(0); } uint layerPitch = 0; for (uint level = 0; level < tex.MipLevels; level++) { Util.GetMipDimensions(tex, level, out uint mipWidth, out uint mipHeight, out uint mipDepth); layerPitch += mipWidth * mipHeight * mipDepth * FormatHelpers.GetSizeInBytes(tex.Format); } return(layerPitch * arrayLayer); }
internal static uint ComputeArrayLayerOffset(Texture tex, uint arrayLayer) { if (arrayLayer == 0) { return(0); } uint blockSize = FormatHelpers.IsCompressedFormat(tex.Format) ? 4u : 1u; uint layerPitch = 0; for (uint level = 0; level < tex.MipLevels; level++) { GetMipDimensions(tex, level, out uint mipWidth, out uint mipHeight, out uint mipDepth); uint storageWidth = Math.Max(mipWidth, blockSize); uint storageHeight = Math.Max(mipHeight, blockSize); layerPitch += FormatHelpers.GetRegionSize(storageWidth, storageHeight, mipDepth, tex.Format); } return(layerPitch * arrayLayer); }
/// <summary> /// Constructs a new VertexLayoutDescription. The stride is assumed to be the sum of the size of all elements. /// </summary> /// <param name="elements">An array of <see cref="VertexElementDescription"/> objects, each describing a single element /// of vertex data.</param> public VertexLayoutDescription(params VertexElementDescription[] elements) { Elements = elements; uint computedStride = 0; for (int i = 0; i < elements.Length; i++) { uint elementSize = FormatHelpers.GetSizeInBytes(elements[i].Format); if (elements[i].Offset != 0) { computedStride = elements[i].Offset + elementSize; } else { computedStride += elementSize; } } Stride = computedStride; InstanceStepRate = 0; }
/// <summary> /// Creates a new <see cref="TextureView"/>. /// </summary> /// <param name="description">The desired properties of the created object.</param> /// <returns>A new <see cref="TextureView"/>.</returns> public TextureView CreateTextureView(ref TextureViewDescription description) { #if VALIDATE_USAGE if (description.MipLevels == 0 || description.ArrayLayers == 0 || (description.BaseMipLevel + description.MipLevels) > description.Target.MipLevels || (description.BaseArrayLayer + description.ArrayLayers) > description.Target.ArrayLayers) { throw new VeldridException( "TextureView mip level and array layer range must be contained in the target Texture."); } if ((description.Target.Usage & TextureUsage.Sampled) == 0 && (description.Target.Usage & TextureUsage.Storage) == 0) { throw new VeldridException( "To create a TextureView, the target texture must have either Sampled or Storage usage flags."); } if (!Features.SubsetTextureView && (description.BaseMipLevel != 0 || description.MipLevels != description.Target.MipLevels || description.BaseArrayLayer != 0 || description.ArrayLayers != description.Target.ArrayLayers)) { throw new VeldridException("GraphicsDevice does not support subset TextureViews."); } if (description.Format != null && description.Format != description.Target.Format) { if (!FormatHelpers.IsFormatViewCompatible(description.Format.Value, description.Target.Format)) { throw new VeldridException( $"Cannot create a TextureView with format {description.Format.Value} targeting a Texture with format " + $"{description.Target.Format}. A TextureView's format must have the same size and number of " + $"components as the underlying Texture's format, or the same format."); } } #endif return(CreateTextureViewCore(ref description)); }
/// <summary> /// Resolves a multisampled source <see cref="Texture"/> into a non-multisampled destination <see cref="Texture"/>. /// </summary> /// <param name="source">The source of the resolve operation. Must be a multisampled <see cref="Texture"/> /// (<see cref="Texture.SampleCount"/> > 1).</param> /// <param name="destination">The destination of the resolve operation. Must be a non-multisampled <see cref="Texture"/> /// (<see cref="Texture.SampleCount"/> == 1).</param> public void ResolveTexture(Texture source, Texture destination) { #if VALIDATE_USAGE if (source.SampleCount == TextureSampleCount.Count1) { throw new VeldridException( $"The {nameof(source)} parameter of {nameof(ResolveTexture)} must be a multisample texture."); } if (destination.SampleCount != TextureSampleCount.Count1) { throw new VeldridException( $"The {nameof(destination)} parameter of {nameof(ResolveTexture)} must be a non-multisample texture. Instead, it is a texture with {FormatHelpers.GetSampleCountUInt32(source.SampleCount)} samples."); } #endif ResolveTextureCore(source, destination); }