Class TexturePixelMemoryManager
Manages and represents the entire pixel memory of a locked Texture or a partial rectangle of it
public abstract class TexturePixelMemoryManager : NativeMemoryManagerBase, IDisposable
- Inheritance
-
TexturePixelMemoryManager
- Implements
- Derived
- Inherited Members
Properties
IsPinned
Gets a value indicating whether the Texture is currently locked (pinned)
public override abstract bool IsPinned { get; }
Property Value
- bool
true if the Texture is currently locked (pinned); otherwise, false (Texture is
nullin that case)
Length
Gets the number of bytes in the allocated memory region this NativeMemoryManagerBase represents
public override sealed nuint Length { get; }
Property Value
- nuint
The number of bytes in the allocated memory region this NativeMemoryManagerBase represents
Memory
Gets a NativeMemory representing the pixel memory of the locked Texture
public override sealed NativeMemory Memory { get; }
Property Value
- NativeMemory
A NativeMemory representing the pixel memory of the locked Texture, or Empty if the Texture is not locked
Examples
This leads to the following row-wise access pattern:
Texture texture;
Rect<int> rect;
...
if (texture.TryLock(rect, out var pixelManager))
{
using (pixelManager)
{
var memory = (NativeMemory<byte>)pixelManager.Memory;
var bytesPerPixel = (nuint)pixelManager.Texture!.Format.BytesPerPixel;
for (nuint row = 0; row < pixelManager.RowCount; row++)
{
var pixels = memory.Slice(0, pixelManager.RowLength * bytesPerPixel).Span;
// Process the pixels in this row
...
memory = memory.Slice(pixelManager.Pitch);
}
}
}
You can also access individual pixels directly using the following pattern:
Texture texture;
Rect<int> rect;
...
if (texture.TryLock(rect, out var pixelManager))
{
using (pixelManager)
{
var memory = (NativeMemory<byte>)pixelManager.Memory;
var bytesPerPixel = (nuint)pixelManager.Texture!.Format.BytesPerPixel;
nuint x, y;
// Access the pixel at (x, y)
ref var pixel = ref memory.Slice(y * pixelManager.Pitch + x * bytesPerPixel).Span[0];
...
}
}
Remarks
The pixel memory data is in the pixel format specified by the Format property of the Texture.
The pixel memory might be longer than the actual pixel data that's safe to access, due to padding at the end of each row. Especially if the Texture was locked with a rectangle smaller than the full size of the texture.
The pixel memory is laid out in rows, where each row is RowLength pixels long, and there are RowCount rows. Between the start of each row, there are Pitch bytes. The NativeMemory returned by this property starts at the beginning of the first row.
As an optimization, the pixel data made available for editing don't necessarily contain the old (at the time of locking) texture data.
Notice: The pixel memory is write-only, and if you need to keep a copy of the texture data you should do that at the application level.
You must dispose the TexturePixelMemoryManager to unlock the Texture and apply the changes made to the pixel data.
Pitch
Gets the pitch of the pixel memory, in bytes
public nuint Pitch { get; }
Property Value
- nuint
The pitch of the pixel memory, in bytes
Remarks
The pitch is the length of a single row of pixel data in bytes, including any padding bytes at the end of the row.
Notice that that's not necessarily equal to RowLength * Texture.Format.BytesPerPixel.
Especially if the Texture was locked with a rectangle smaller than the full size of the texture.
Pointer
Gets a pointer to the start of the allocated memory region this NativeMemoryManagerBase represents
public override sealed nint Pointer { get; }
Property Value
- nint
A pointer to the start of the allocated memory region this NativeMemoryManagerBase represents
RowCount
Gets the number of rows in the pixel memory, in pixels
public nuint RowCount { get; }
Property Value
- nuint
The number of rows in the pixel memory, in pixels
RowLength
Gets the length of each row in the pixel memory, in pixels
public nuint RowLength { get; }
Property Value
- nuint
The length of each row in the pixel memory, in pixels
Remarks
To get the length of each row in bytes, use RowLength * Texture.Format.BytesPerPixel.
Texture
Gets the locked texture
public abstract Texture? Texture { get; }
Property Value
Methods
AddPin(ulong, ulong)
Adds a "pin" to this NativeMemoryManagerBase
protected override sealed void AddPin(ulong oldPinCounter, ulong newPinCounter)
Parameters
oldPinCounterulongThe pin counter before it was increased
newPinCounterulongThe current pin counter after it was increased
Remarks
This method is called each time the pin counter was successfully increased. You can override this method to implement custom logic that should be executed each time a "pin" is added to this NativeMemoryManagerBase.
If you want a more general pinning logic that only triggers when the first pin is added,
you can check whether oldPinCounter is 0 and newPinCounter greater than 0 in your custom implementation.
When implementing this method, remember that it must work in conjunction with RemovePin(ulong, ulong).
There's no guarantee about when this method is called in relation to other threads pinning or unpinning this NativeMemoryManagerBase. Custom implementations must take care of thread-safetiness themselves if needed, especially in regards to the ordering of AddPin(ulong, ulong) and RemovePin(ulong, ulong) operations.
The only guarantee given is that this method is only ever called immediately after the pin counter was successfully increased by a single pinning operation.
DecreasePinCounter(ulong)
Decreases the pin counter
protected override sealed ulong DecreasePinCounter(ulong pinCounter)
Parameters
pinCounterulongThe current pin counter
Returns
- ulong
The new pin counter
Remarks
Override this method to implement custom pin counter decreasing step logic.
You can even just ignore pinning altogether and always return the given pinCounter.
Notice that custom implementations of this method should ensure that the returned value is less than or equal to the given pinCounter.
Also, when implementing this method, remember that the pin counter stepping must work in conjunction with IncreasePinCounter(ulong).
Furthermore, there's no underflow preventing logic in the consumers of this method, so custom implementations should ensure that underflow doesn't happen.
Dispose(bool)
Disposes the TexturePixelMemoryManager, unlocking the associated Texture
protected override void Dispose(bool disposing)
Parameters
disposingboolA value indicating whether this method is called from Dispose() (
true) or from the finalizer (false)
Remarks
Calling this method unlocks the associated Texture, if it's still locked, applying any changes made to the pixel data, and making its pixel memory inaccessible until it's locked again.
- See Also
-
Dispose()
IncreasePinCounter(ulong)
Increases the pin counter
protected override sealed ulong IncreasePinCounter(ulong pinCounter)
Parameters
pinCounterulongThe current pin counter
Returns
- ulong
The new pin counter
Remarks
Override this method to implement custom pin counter increasing step logic.
You can even just ignore pinning altogether and always return the given pinCounter.
Notice that custom implementations of this method should ensure that the returned value is greater than or equal to the given pinCounter.
Also, when implementing this method, remember that the pin counter stepping must work in conjunction with DecreasePinCounter(ulong).
Furthermore, there's no overflow preventing logic in the consumers of this method, so custom implementations should ensure that overflow doesn't happen.
RemovePin(ulong, ulong)
Removes a "pin" to this NativeMemoryManagerBase
protected override sealed void RemovePin(ulong oldPinCounter, ulong newPinCounter)
Parameters
oldPinCounterulongThe pin counter before it was decreased
newPinCounterulongThe current pin counter after it was decreased
Remarks
This method is called each time the pin counter was successfully decreased. You can override this method to implement custom logic that should be executed each time a "pin" is removed from this NativeMemoryManagerBase.
If you want a more general pinning logic that only triggers when the last pin is removed,
you can check whether oldPinCounter is greater than 0 and newPinCounter is 0 in your custom implementation.
When implementing this method, remember that it must work in conjunction with AddPin(ulong, ulong).
There's no guarantee about when this method is called in relation to other threads pinning or unpinning this NativeMemoryManagerBase. Custom implementations must take care of thread-safetiness themselves if needed, especially in regards to the ordering of AddPin(ulong, ulong) and RemovePin(ulong, ulong) operations.
The only guarantee given is that this method is only ever called immediately after the pin counter was successfully decreased by a single unpinning operation.