smourier / DirectN

Direct interop Code for .NET Framework, .NET Core and .NET 5+ : DXGI, WIC, DirectX 9 to 12, Direct2D, Direct Write, Direct Composition, Media Foundation, WASAPI, CodecAPI, GDI, Spatial Audio, DVD, Windows Media Player, UWP DXInterop, WinUI3, etc.
MIT License
311 stars 28 forks source link

IDirect3DVertexBuffer9 Lock Usage #41

Closed Revan1985 closed 1 year ago

Revan1985 commented 1 year ago

Hello, I need an help for using DirectN with Directx9. I am using the IDirect3DVertexBuffer9 Lock method, but since this required an IntPtr, I am not able to understand how translate from c++ to c# code. In c++ I am using this code

VERTEX* vertices; HR(m_pVertexBuffer->Lock(0, 0, (void**)&vertices, D3DLOCK_DISCARD)); ::memcpy(vertices, vertexArray, sizeof(vertexArray)); return m_pVertexBuffer->Unlock(); while in c# should become something similar `nint ppbData = nint.Zero; if ((hr = _vertexBuffer.Object.Lock(0, 0, ppbData, Constants.D3DLOCK_DISCARD)).IsError) { return hr; } var gc = GCHandle.Alloc(vertexArray, GCHandleType.Pinned);

    gc.Free();

    return _vertexBuffer.Object.Unlock();`

I don't understand if I am on right way, or is totally messed up. How should I complete it? I don't know if i need to "allocate" the lock ppbData, and in which way.

Thank you for any support.

smourier commented 1 year ago

Hi,

There's a problem with this interface definition. This is because the code is auto-generated from Windows SDK's header and these headers (DirectX9) are very old and miss annotations to help generators understand the semantics of the methods in question (actually, for newer projects you shouldn't use DirectX9, but DirectX11 or DirectX19).

Anyway. Ideally, the method should be like this. Note the out attribute on ppbData parameter because the pointer is given to the caller by the method implementation (you don't allocate for the call):

HRESULT Lock(uint OffsetToLock, uint SizeToLock, out IntPtr ppbData, uint Flags);

To workaround this small issue, you just need one more indirection, so what you can do so is something like this (untested):

IDirect3DVertexBuffer9 buffer = ...
var vertexArray = new float[whatever];

// allocate memory for a pointer which will be the array pointer
using (var ppbData = new ComMemory(IntPtr.Size)) // ComMemory is provided by DirectN, memory will be free'd automatically
{
    buffer.Lock(0, 0, ppbData.Pointer, Constants.D3DLOCK_DISCARD);
    try
    {
        // get array pointer & copy
        var arrayPtr = Marshal.ReadIntPtr(ppbData.Pointer);
        CopyMemory(Marshal.UnsafeAddrOfPinnedArrayElement(vertexArray, 0), arrayPtr, (IntPtr)(vertexArray.Length * Marshal.SizeOf<float>()));
    }
    finally
    {
        buffer.Unlock();
    }
}

// need to declare this somewhere
[DllImport("kernel32", ExactSpelling = true, EntryPoint = "RtlMoveMemory")]
private static extern void CopyMemory(IntPtr destination, IntPtr source, IntPtr length);
Revan1985 commented 1 year ago

Thank you, this is what i needed to know. Thank you for your speedy answer 😃

Revan1985 commented 1 year ago

Hello, I am sorry, but i need to reopen this issue, just to not open a new one... I am having a similar problem now. I need to create pixel\vertex shaders, but signature for creation is this... new HRESULT CreatePixelShader(ref uint pFunction, out IDirect3DPixelShader9 ppShader); How can i use it? I was trying a similar way, cause i need to create the pixelbuffer from a ReadOnlySpan, but uint is not compatible with an intptr. How can i load them (also from a different source, not only byte[] or similar source).

Thank you for your support, but the d3d9 wrapper is cryptical 😅

And another question. How can i get the pointer of a IDirect3DSurface9 to use it with a Wpf D3DImage? StructureToPtr fails, so i don't understand how to get it...

smourier commented 1 year ago

Hi,

This is not the same issue, next time please create a new question.

Anyway, it's again caused by legacy C headers for DirectX9. This is how is defined the method in d3d9.h

STDMETHOD(CreatePixelShader)(THIS_ CONST DWORD* pFunction,IDirect3DPixelShader9** ppShader) PURE;

C# ref int seems equivalent, but pFunctionis in fact an array so the header is indeed not sufficient to generate an equivalent C# code.

I've updated the code https://github.com/smourier/DirectN/commit/839cb42ee7b958cf3e1ab8284a58d34069dbb35c and published new nugets, and it's now generated in C# as

HRESULT CreatePixelShader(IntPtr pFunction, out IDirect3DPixelShader9 ppShader);
Revan1985 commented 1 year ago

thank you for your answer. Yes, you have reason, I should have opened a new issue. Anyway thank you for your patience and support.