Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote method fixes #98

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 45 additions & 19 deletions OmsiHook/D3DTexture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace OmsiHook
{
public class D3DTexture : OmsiObject
public class D3DTexture : OmsiObject, IDisposable
{
internal D3DTexture(Memory omsiMemory, int baseAddress) : base(omsiMemory, baseAddress) { }
/// <summary>
Expand All @@ -21,9 +21,11 @@ public class D3DTexture : OmsiObject

private uint width, height;
private D3DFORMAT format;
private uint levels;
private int stagingBufferSize;
// private byte[] stagingBuffer;
private uint remoteStagingBufferPtr;
private const bool useFastAlloc = true;

/// <summary>
/// Gets the address of the native <c>IDirect3DTexture9</c> object.
Expand All @@ -42,6 +44,10 @@ public class D3DTexture : OmsiObject
/// </summary>
public D3DFORMAT Format => format;
/// <summary>
/// Gets the number of mipmap levels in the texture.
/// </summary>
public uint Levels => levels;
/// <summary>
/// Returns true if this object is initialised.
/// </summary>
public bool IsValid => TextureAddress != 0 && isCreated;
Expand Down Expand Up @@ -74,19 +80,19 @@ public async Task CreateFromExisting(uint address, bool initialiseForWriting = t
if (Address == 0)
throw new NullReferenceException("Texture was already null!");

var desc = await OmsiGetTextureDescAsync(address);
var desc = await Memory.RemoteMethods.OmsiGetTextureDescAsync(address, 0);
if (HRESULTFailed(desc.hresult))
throw new Exception(desc.hresult.ToString());

width = desc.width;
height = desc.height;
format = desc.format;
levels = await Memory.RemoteMethods.OmsiGetTextureLevelCountAsync(address);
Address = unchecked((int)address);
stagingBufferSize = (int)width * (int)height * BitsPerPixel(format) / 8;
//stagingBuffer = new byte[stagingBufferSize];
if(initialiseForWriting)
remoteStagingBufferPtr = unchecked((uint)await Memory.AllocRemoteMemory(stagingBufferSize, fastAlloc: true));
isCreated = true;
if (initialiseForWriting)
await InitialiseForWriting();
}

/// <summary>
Expand All @@ -96,7 +102,12 @@ public async Task InitialiseForWriting()
{
if (!IsValid)
throw new NotInitialisedException("The texture has not been created yet! Make sure to call CreateD3DTexture().");
remoteStagingBufferPtr = unchecked((uint)await Memory.AllocRemoteMemory(stagingBufferSize, fastAlloc: true));
if(remoteStagingBufferPtr != 0)
{
Memory.FreeRemoteMemory(remoteStagingBufferPtr, fastAlloc: useFastAlloc);
remoteStagingBufferPtr = 0;
}
remoteStagingBufferPtr = unchecked((uint)await Memory.AllocRemoteMemory(stagingBufferSize, fastAlloc: useFastAlloc));
}

/// <summary>
Expand All @@ -105,26 +116,27 @@ public async Task InitialiseForWriting()
/// <param name="width">the width of the texture</param>
/// <param name="height">the height of the texture</param>
/// <param name="format">the <see cref="D3DFORMAT"/> of the texture</param>
/// <param name="levels">the number of mipmap levels to create</param>
/// <param name="initialiseForWriting">whether to allow writing to this texture</param>
/// <exception cref="Exception"></exception>
public async Task CreateD3DTexture(uint width, uint height, D3DFORMAT format = D3DFORMAT.D3DFMT_A8R8G8B8, bool initialiseForWriting = true)
public async Task CreateD3DTexture(uint width, uint height, D3DFORMAT format = D3DFORMAT.D3DFMT_A8R8G8B8, uint levels = 1, bool initialiseForWriting = true)
{
if(Address != 0)
await ReleaseTexture();

var (hresult, pTexture) = await OmsiCreateTextureAsync(width, height, format);
var (hresult, pTexture) = await Memory.RemoteMethods.OmsiCreateTextureAsync(width, height, format, levels);
if (HRESULTFailed(hresult))
throw new Exception("Couldn't create D3D texture! Result: " + hresult);

this.width = width;
this.height = height;
this.format = format;
this.levels = levels;
Address = unchecked((int)pTexture);
stagingBufferSize = (int)width * (int)height * BitsPerPixel(format) / 8;
//stagingBuffer = new byte[stagingBufferSize];
if (initialiseForWriting)
remoteStagingBufferPtr = unchecked((uint)await Memory.AllocRemoteMemory(stagingBufferSize, fastAlloc: true));
isCreated = true;
if (initialiseForWriting)
await InitialiseForWriting();
}

/// <summary>
Expand All @@ -134,44 +146,58 @@ public async Task CreateD3DTexture(uint width, uint height, D3DFORMAT format = D
/// <exception cref="Exception"></exception>
public async Task ReleaseTexture()
{
if(Address == 0)
throw new NullReferenceException("Texture was already null!");
if (Address == 0)
return;
// throw new NullReferenceException("Texture was already null!");

isCreated = false;
HRESULT hr = await OmsiReleaseTextureAsync(unchecked((uint)Address));
HRESULT hr = await Memory.RemoteMethods.OmsiReleaseTextureAsync(TextureAddress);
if(HRESULTFailed(hr))
throw new Exception(hr.ToString());
Address = 0;
Memory.FreeRemoteMemory(remoteStagingBufferPtr, fastAlloc: true);
Memory.FreeRemoteMemory(remoteStagingBufferPtr, fastAlloc: useFastAlloc);
//stagingBuffer = null;
remoteStagingBufferPtr = 0;
}

/// <summary>
/// Updates the contents of the <c>IDirect3DTexture9</c> object from a buffer.
/// </summary>
/// <remarks>
/// This method can cause crashes if the texture becomes invalidated by Omsi. This can in certain circumstances
/// happen if the script texture is updated in OmsiScript.
/// </remarks>
/// <typeparam name="T">The pixel element type</typeparam>
/// <param name="textureData">a buffer of memory containing the new texture data layed out sequentially in row-major order</param>
/// <param name="updateArea">optionally, a rectangle specifying the area of the texture to update; the <paramref name="textureData"/>
/// must match the size of this rectangle; pass in <see langword="null"/> to update the whole texture</param>
/// <param name="level">optionally, the mipmap level to update</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
/// <exception cref="Exception"></exception>
public async Task UpdateTexture<T>(Memory<T> textureData, Rectangle? updateArea = null) where T : unmanaged
public async Task UpdateTexture<T>(Memory<T> textureData, Rectangle? updateArea = null, uint level = 0) where T : unmanaged
{
if (!IsInitialised || remoteStagingBufferPtr == 0)
if (!Memory.RemoteMethods.IsInitialised || remoteStagingBufferPtr == 0)
throw new NotInitialisedException("D3DTexture object must be initialised for write before it can be updated! ");
if((textureData.Length * Marshal.SizeOf<T>()) > stagingBufferSize)
if((textureData.Length * Unsafe.SizeOf<T>()) > stagingBufferSize)
throw new ArgumentOutOfRangeException(nameof(textureData));
Memory.WriteMemory(remoteStagingBufferPtr, textureData);

uint dataWidth = (updateArea?.right - updateArea?.left) ?? width;
uint dataHeight = (updateArea?.bottom - updateArea?.top) ?? height;

HRESULT hr = await OmsiUpdateTextureAsync(TextureAddress, remoteStagingBufferPtr, dataWidth, dataHeight, updateArea);
HRESULT hr = await Memory.RemoteMethods.OmsiUpdateTextureAsync(TextureAddress, remoteStagingBufferPtr, dataWidth, dataHeight, updateArea, level);
if (HRESULTFailed(hr))
throw new Exception("Couldn't update D3D texture! Result: " + hr);
}

/// <summary>
/// Disposes of this texture by calling <see cref="ReleaseTexture"/> and waiting synchronously.
/// </summary>
public void Dispose()
{
ReleaseTexture().Wait();
}

/// <summary>
/// A struct representing RGBA pixel data. Compatible with <see cref="D3DFORMAT.D3DFMT_A8R8G8B8"/>.
/// </summary>
Expand Down
Loading
Loading