Error Running jextract on d3d12.h
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Tue Sep 15 17:35:06 UTC 2020
After throwing the kitchen sink at your example (I set up Windows on a
virtual instance to play with it), I was able to reproduce the issue.
I discussed this with Jorn offline and Jorn pointed out at an issue that
I should have spotted much earlier (thanks!) - the issue is that when
looking at API docs like:
https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createcommandqueue
This is actually the description of the C++ API.
The C API almost always takes some extra parameter - in the case of this
function, the first parameter must be a device pointer.
Here's the rewritten example (I took the liberty to streamline the Java
code a bit, I hope you don't mind):
public class Main {
public enum IID {
IID_IDXGIAdapter1(0x29038f61, 0x3839, 0x4626, 0x91, 0xfd, 0x08,
0x68, 0x79, 0x01, 0x1a, 0x05),
IID_IDXGIFactory1(0x770aae78, 0xf26f, 0x4dba, 0xa8, 0x29, 0x25,
0x3c, 0x83, 0xd1, 0xb3, 0x87),
IID_ID3D12Device(0x189819f1, 0x1db6, 0x4b57, 0xbe, 0x54, 0x18,
0x21, 0x33, 0x9b, 0x85, 0xf7),
IID_ID3D12CommandQueue(0x0ec870a6, 0x5d7e, 0x4c22, 0x8c, 0xfc,
0x5b, 0xaa, 0xe0, 0x76, 0x16, 0xed);
final MemorySegment guid;
IID(int data1, int data2, int data3, int... data4) {
MemorySegment segment =
MemorySegment.allocateNative(guiddef_h.GUID.$LAYOUT());
GUID.Data1$set(segment, data1);
GUID.Data2$set(segment, (short)data2);
GUID.Data3$set(segment, (short)data3);
MemorySegment data4_segment = GUID.Data4$slice(segment);
for (int i = 0 ; i < data4.length ; i++) {
MemoryAccess.setByteAtIndex(data4_segment, i,
(byte)data4[i]);
}
this.guid = segment;
}
}
public static void main(String[] args) throws Throwable {
try (NativeScope scope = NativeScope.unboundedScope()) {
// IDXGIFactory1** dxgiFactory;
var ppDxgiFactory = IDXGIFactory1.allocatePointer(scope);
// HRESULT = CreateDXGIFactory1(_uuid(dxgiFactory),
&dxgiFactory))
checkResult(dxgi_h.CreateDXGIFactory1(IID.IID_IDXGIFactory1.guid,
ppDxgiFactory));
// IDXGIFactory1*
MemorySegment pDxgiFactory =
IDXGIFactory1.ofAddressRestricted(MemoryAccess.getAddress(ppDxgiFactory));
// (This)->lpVtbl
MemorySegment vtbl =
IDXGIFactory1Vtbl.ofAddressRestricted(IDXGIFactory1.lpVtbl$get(pDxgiFactory));
// lpVtbl->EnumAdapters1
MemoryAddress addrEnumAdapters =
IDXGIFactory1Vtbl.EnumAdapters1$get(vtbl);
// link the pointer
MethodHandle MH_EnumAdapters1 =
getSystemLinker().downcallHandle(
addrEnumAdapters,
MethodType.methodType(int.class,
MemoryAddress.class, int.class, MemoryAddress.class),
FunctionDescriptor.of(C_INT, C_POINTER, C_INT,
C_POINTER));
/* [annotation][out] _COM_Outptr_ IDXGIAdapter1** */
MemorySegment ppOut = IDXGIAdapter1.allocatePointer(scope);
checkResult((int)
MH_EnumAdapters1.invokeExact(pDxgiFactory.address(), 0, ppOut.address()));
// IDXGIAdapter1*
MemorySegment pAdapter =
IDXGIAdapter1.ofAddressRestricted(MemoryAccess.getAddress(ppOut));
// (This)->lpVtbl
MemorySegment vtbl2 =
IDXGIAdapter1Vtbl.ofAddressRestricted(IDXGIAdapter1.lpVtbl$get(pAdapter));
// lpVtbl->EnumAdapters1
// HRESULT(*)(IDXGIAdapter1*,DXGI_ADAPTER_DESC1*)
MemoryAddress addrGetDesc1 =
IDXGIAdapter1Vtbl.GetDesc1$get(vtbl2);
// link the pointer
MethodHandle MH_GetDesc1 = getSystemLinker().downcallHandle(
addrGetDesc1,
MethodType.methodType(int.class,
MemoryAddress.class, MemoryAddress.class),
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
/* DXGI_ADAPTER_DESC1* */
MemorySegment pDesc = DXGI_ADAPTER_DESC1.allocate(scope);
checkResult((int)
MH_GetDesc1.invokeExact(pAdapter.address(), pDesc.address()));
// print description
MemorySegment descStr =
DXGI_ADAPTER_DESC1.Description$slice(pDesc);
String str = new String(descStr.toByteArray(),
StandardCharsets.UTF_16LE);
System.out.println(str);
//D3D12CreateDevice$MH()
// ID3D12Device** d3d12Device;
var ppDevice = ID3D12Device.allocatePointer(scope);
// D3D12CreateDevice(pAdapter, D3D_FEATURE_LEVEL_11_0,
IID_PPV_ARGS(&ppDevice))
checkResult(D3D12CreateDevice(pAdapter, (int) 45056L,
IID.IID_ID3D12Device.guid, ppDevice));
// ID3D12Device*
MemorySegment pDevice =
ID3D12Device.ofAddressRestricted(MemoryAccess.getAddress(ppDevice));
// (This)->lpVtbl
MemorySegment deviceVtbl =
ID3D12DeviceVtbl.ofAddressRestricted(ID3D12Device.lpVtbl$get(pDevice));
// lpVtbl->CreateCommandQueue
MemoryAddress addrCreateCommandQueue =
ID3D12DeviceVtbl.CreateCommandQueue$get(deviceVtbl);
// D3D12_COMMAND_QUEUE_DESC queueDesc = {};
// queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
// queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
MemorySegment pQueueDesc =
D3D12_COMMAND_QUEUE_DESC.allocate(scope);
//MemorySegment queueDesc = asSegment(pQueueDesc.address(),
D3D12_COMMAND_QUEUE_DESC.$LAYOUT());
D3D12_COMMAND_QUEUE_DESC.Type$set(pQueueDesc,
D3D12_COMMAND_LIST_TYPE_DIRECT());
D3D12_COMMAND_QUEUE_DESC.Flags$set(pQueueDesc,
D3D12_COMMAND_QUEUE_FLAG_NONE());
// link the pointer
MethodHandle MH_ID3D12Device_CreateCommandQueue =
getSystemLinker().downcallHandle(
addrCreateCommandQueue,
MethodType.methodType(int.class,
MemoryAddress.class, MemoryAddress.class, MemoryAddress.class,
MemoryAddress.class),
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER,
C_POINTER, C_POINTER));
var ppQueue = ID3D12CommandQueue.allocatePointer(scope);
checkResult((int)
MH_ID3D12Device_CreateCommandQueue.invokeExact(pDevice.address(),
pQueueDesc.address(),
IID.IID_ID3D12CommandQueue.guid.address(),
ppQueue.address()));
}
}
private static final int S_OK = 0x00000000;
private static void checkResult(int result) {
switch (result) {
case S_OK -> {}
default -> throw new IllegalStateException("Unknown result:
" + String.format("%X8", result));
}
}
}
Cheers
Maurizio
On 15/09/2020 11:12, Maurizio Cimadamore wrote:
> Hi Michael,
> You are trying to put a fairly complex example together using the
> foreign API so (1) thanks for sticking with it and (2) there's no harm
> in asking :-)
>
> Again, big disclaimer - I'm not on Windows, so I can't test it out -
> here are some comments from eyeballing your code (although, I can't
> see anything particularly wrong). The one thing I initially focussed
> on was this:
>
> var ppQueue = ID3D12CommandQueue.allocatePointer(scope);
>
> This will allocate a 64-bit long memory segment (enough to contain a
> pointer). But the contents will be blank. But I think this is ok, as
> the API is probably meant to fill in the pointer to the newly
> allocated command queue area (e.g. output parameter idiom).
>
> Another thing I noticed is that you do things like:
>
>> MemorySegment pQueueDesc = D3D12_COMMAND_QUEUE_DESC.allocate(scope);
>> MemorySegment queueDesc = asSegment(pQueueDesc.address(),
>> D3D12_COMMAND_QUEUE_DESC.$LAYOUT()); // <--------
>> D3D12_COMMAND_QUEUE_DESC.Type$set(queueDesc,
>> D3D12_COMMAND_LIST_TYPE_DIRECT());
>> D3D12_COMMAND_QUEUE_DESC.Flags$set(queueDesc,
>> D3D12_COMMAND_QUEUE_FLAG_NONE());
>>
>
> I find the second instruction above redundant here. You already have a
> fully readable/writable segment for the struct, you don't need to go
> to the extra jump of re-creating another segment from its address. You
> can just remove that second line and just use pQueueDesc in the two
> set operations. In other words, you should only use `asSegment` if you
> get a MemoryAddress from the library, but you want to turn it into a
> memory segment with certain known size (so that you can read and write
> from it).
>
>
> Looking at the GUID function, I also see some issues:
>
>> MemorySegment segment = MemorySegment.allocateNative(GUID.map(l ->
>> ((SequenceLayout) l).withElementCount(8),
>> MemoryLayout.PathElement.groupElement("Data4")));
>>
>>
> This doesn't seem to do anything - the layout you start with already
> has a sequence layout with 8 elements in "Data4", so you are just
> replacing that sequence layout with another identical one. That said,
> this is just a stylistic issue, the main logic of the code seems to be
> correct in initializing the contents of the segment (you could use, if
> you wanted a bulk copy to transfer the byte array into "Data4"
> directly, but that's not necessary to get correctness - can be
> improved later).
>
>
> Another Java-style comment - you have an Enum defining various IIDs -
> and then you have a unique mapping between an IID and a segment. I
> wonder if the MemorySegment could become a field of the IID enum - so
> that you don't even need the map.
>
>
> That said, I can't find anything obviously "wrong" with your usage of
> this DX12 API - I've gone back and forth between code and API docs
> several times to check the various structures and pointers you pass,
> but it all looks ok. Are you 100% sure that the crash happens in the
> invokeExact to the CreateCommandQueue? That could suggest one of the
> following:
>
> 1) the descriptor you use for linking the function is wrong (e.g.
> wrong argument arity, types) - that doesn't seem to be the case
> 2) the arguments you are passing are wrong (e.g. unitialized
> args/pointers) - again, this doesn't seem to be the case
> 3) the function pointer itself being linked is wrong - this I have no
> way to verify
>
> I think it would help to get "crash log" from the VM; that will
> contain extra stack information, such as what exactly the thread was
> doing when the VM crashed - this could tell us for instance if the
> crash occurred inside the dx12 library, or if we didn't even get there
> - which would suggest that the function pointer to CreateCommandQueue
> is wrong somehow.
>
>
> I hope this helps a bit. I'm sure we're close.
>
> Cheers
> Maurizio
>
>
>
>
>
> On 15/09/2020 00:05, Michael Ennen wrote:
>> At this point I may be beating a dead horse and should be able to
>> figure this out from the awesome example you provided but
>> in the case of using "invokeExact" on a MethodHandle, how can one, in
>> that case, send a GUID as an argument?
>>
>> That is for a global function it was done thusly:
>>
>> checkResult(dxgi_h.CreateDXGIFactory1(GUID(IID.IID_IDXGIFactory1),
>> ppDxgiFactory));
>>
>> In the case of "invokeExact" it needs a MemoryAddress so I figured it
>> would just be:
>>
>> checkResult((int)
>> MH_ID3D12Device_CreateCommandQueue.invokeExact(pQueueDesc.address(),GUID(IID.IID_ID3D12CommandQueue).address(),
>> ppQueue.address()));
>>
>> But I am getting an access violation.
>>
>> I am trying to extend the example to create an ID3D12Device and
>> ID3D12CommandQueue thusly:
>>
>> //D3D12CreateDevice$MH()
>> // ID3D12Device** d3d12Device;
>> var ppDevice = ID3D12Device.allocatePointer(scope);
>>
>> // D3D12CreateDevice(pAdapter, D3D_FEATURE_LEVEL_11_0,
>> IID_PPV_ARGS(&ppDevice))
>> checkResult(D3D12CreateDevice(pAdapter, (int) 45056L,
>> GUID(IID.IID_ID3D12Device), ppDevice));
>> // ID3D12Device*
>> MemorySegment pDevice = asSegment(MemoryAccess.getAddress(ppDevice),
>> ID3D12Device.$LAYOUT());
>>
>> // (This)->lpVtbl
>> MemorySegment deviceVtbl =
>> asSegment(ID3D12Device.lpVtbl$get(pDevice), ID3D12DeviceVtbl.$LAYOUT());
>>
>> // lpVtbl->CreateCommandQueue
>> MemoryAddress addrCreateCommandQueue =
>> ID3D12DeviceVtbl.CreateCommandQueue$get(deviceVtbl);
>>
>> // D3D12_COMMAND_QUEUE_DESC queueDesc = {};
>> // queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
>> // queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
>> MemorySegment pQueueDesc = D3D12_COMMAND_QUEUE_DESC.allocate(scope);
>> MemorySegment queueDesc = asSegment(pQueueDesc.address(),
>> D3D12_COMMAND_QUEUE_DESC.$LAYOUT());
>> D3D12_COMMAND_QUEUE_DESC.Type$set(queueDesc,
>> D3D12_COMMAND_LIST_TYPE_DIRECT());
>> D3D12_COMMAND_QUEUE_DESC.Flags$set(queueDesc,
>> D3D12_COMMAND_QUEUE_FLAG_NONE());
>>
>> // link the pointer
>> MethodHandle MH_ID3D12Device_CreateCommandQueue =
>> getSystemLinker().downcallHandle(
>> addrCreateCommandQueue,
>> MethodType.methodType(int.class, MemoryAddress.class,
>> MemoryAddress.class, MemoryAddress.class),
>> FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER, C_POINTER));
>>
>> var ppQueue = ID3D12CommandQueue.allocatePointer(scope);
>>
>> checkResult((int)
>> MH_ID3D12Device_CreateCommandQueue.invokeExact(pQueueDesc.address(),
>> GUID(IID.IID_ID3D12CommandQueue).address(), ppQueue.address()));
>>
>> Full example here:
>>
>> https://github.com/brcolow/java-dx12/blob/master/src/main/java/com/dx12/DX12.java
>> <https://urldefense.com/v3/__https://github.com/brcolow/java-dx12/blob/master/src/main/java/com/dx12/DX12.java__;!!GqivPVa7Brio!NNn4WtIsLKjlTcTCh-TY2gYE6mbNNXrzVKMHA4rEupoedndflmeMdLXktTMoasJKTw999Fs$>
>>
>>
>> Sorry if it's painfully obvious, this is the last question on this
>> topic I will ask!
>>
>> On Mon, Sep 14, 2020 at 11:45 AM Jorn Vernee <jorn.vernee at oracle.com
>> <mailto:jorn.vernee at oracle.com>> wrote:
>>
>> Hi Michael,
>>
>> Note that I passed -l dxgi to jextract when extracting dxgi.h,
>> that will making it so the dxgi library is used to link the
>> functions. d3d12 didn't seem to be needed. Doing that should avoid
>> the need to manually load the library like you've shown.
>>
>> This is what I ended up with:
>>
>> $jdk = ...
>> nal -Name jextract -Value "$jdk\bin\jextract.exe"
>> $I = "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0"
>> jextract -d out -t org.jextract --filter "dxgi.h" -l dxgi --
>> "$I\shared\dxgi.h"
>> jextract -d out -t org.jextract --filter "guiddef.h" --
>> "$I\shared\guiddef.h"
>> jar -cf dxgi.jar -C out org
>>
>> Jorn
>>
>> On 14/09/2020 19:51, Michael Ennen wrote:
>>> It worked for me! Awesome!
>>>
>>> One quick question:
>>>
>>> I had to add the following:
>>>
>>> LibraryLookup d3d12 = LibraryLookup.ofLibrary("D3D12");
>>> LibraryLookup dxgi = LibraryLookup.ofLibrary("dxgi");
>>>
>>> To get the methods to be wired up, is that expected? I don't see
>>> it in your example.
>>>
>>> Thanks again *so much*. This is a fantastic reference to get
>>> started as it handles some complexities like function
>>> pointers and the complexities of having a C API that really
>>> corresponds to a C++ API which seems to be the case
>>> with DirectX12.
>>>
>>> On Mon, Sep 14, 2020 at 10:39 AM Michael Ennen
>>> <mike.ennen at gmail.com <mailto:mike.ennen at gmail.com>> wrote:
>>>
>>> Ahhh, this makes so much more sense now.
>>>
>>> Thank you both tremendously.
>>>
>>> On Mon, Sep 14, 2020 at 6:39 AM Jorn Vernee
>>> <jorn.vernee at oracle.com <mailto:jorn.vernee at oracle.com>> wrote:
>>>
>>> Ok, it looks like the formatting got messed up in the
>>> email, so I've
>>> uploaded the example here as well:
>>>
>>> http://cr.openjdk.java.net/~jvernee/d3d12_example/Main.java
>>>
>>> Jorn
>>>
>>> On 14/09/2020 15:28, Jorn Vernee wrote:
>>> > I've tried it out on my machine as well (I'm also on
>>> Windows). Here is
>>> > a little bit more complete example that prints out the
>>> adapter
>>> > description
>>> >
>>> > If you have extracted the shared/dxgi.h and the
>>> shared/guiddef.h
>>> > headers as well, you can do something like this:
>>> >
>>> >
>>> > import jdk.incubator.foreign.FunctionDescriptor;
>>> > import jdk.incubator.foreign.MemoryAccess;
>>> > import jdk.incubator.foreign.MemoryAddress;
>>> > import jdk.incubator.foreign.MemoryLayout;
>>> > import jdk.incubator.foreign.MemorySegment;
>>> > import jdk.incubator.foreign.NativeScope;
>>> > import org.jextract.dxgi_h;
>>> > import org.jextract.guiddef_h;
>>> >
>>> > import java.lang.invoke.MethodHandle;
>>> > import java.lang.invoke.MethodType;
>>> > import java.nio.charset.StandardCharsets;
>>> >
>>> > import static jdk.incubator.foreign.CSupport.*;
>>> > import static org.jextract.dxgi_h.*;
>>> >
>>> > public class Main {
>>> >
>>> > public static void main(String[] args)throws
>>> Throwable {
>>> > try (NativeScope scope
>>> =NativeScope.unboundedScope()) {
>>> > // IDXGIFactory1** dxgiFactory;
>>> > var ppDxgiFactory =IDXGIFactory1.allocatePointer(scope);
>>> > // HRESULT =
>>> CreateDXGIFactory1(_uuid(dxgiFactory),
>>> > &dxgiFactory))
>>> >
>>> checkResult(dxgi_h.CreateDXGIFactory1(IID_IDXGIFactory1,ppDxgiFactory));
>>>
>>> > // IDXGIFactory1* MemorySegment pDxgiFactory
>>> >
>>> =asSegment(MemoryAccess.getAddress(ppDxgiFactory),IDXGIFactory1.$LAYOUT());
>>> >
>>> > // (This)->lpVtbl MemorySegment vtbl
>>> >
>>> =asSegment(IDXGIFactory1.lpVtbl$get(pDxgiFactory),IDXGIFactory1Vtbl.$LAYOUT());
>>> > // lpVtbl->EnumAdapters1 MemoryAddress
>>> addrEnumAdapters
>>> > =IDXGIFactory1Vtbl.EnumAdapters1$get(vtbl);
>>> >
>>> > // link the pointer MethodHandle
>>> MH_EnumAdapters1
>>> > =getSystemLinker().downcallHandle(
>>> > addrEnumAdapters,
>>> >
>>> MethodType.methodType(int.class,MemoryAddress.class,int.class,MemoryAddress.class),
>>> > FunctionDescriptor.of(C_INT,C_POINTER,C_INT,C_POINTER));
>>> >
>>> > /* [annotation][out] _COM_Outptr_
>>> IDXGIAdapter1** */
>>> > MemorySegment ppOut
>>> =IDXGIAdapter1.allocatePointer(scope);
>>> >
>>> checkResult((int)MH_EnumAdapters1.invokeExact(pDxgiFactory.address(),0,ppOut.address()));//
>>>
>>> > IDXGIAdapter1* MemorySegment pAdapter
>>> >
>>> =asSegment(MemoryAccess.getAddress(ppOut),IDXGIAdapter1.$LAYOUT());
>>> >
>>> > // (This)->lpVtbl MemorySegment vtbl2
>>> >
>>> =asSegment(IDXGIAdapter1.lpVtbl$get(pAdapter),IDXGIAdapter1Vtbl.$LAYOUT());
>>> > // lpVtbl->EnumAdapters1 //
>>> > HRESULT(*)(IDXGIAdapter1*,DXGI_ADAPTER_DESC1*)
>>> MemoryAddress
>>> > addrGetDesc1 =IDXGIAdapter1Vtbl.GetDesc1$get(vtbl2);
>>> >
>>> > // link the pointer MethodHandle MH_GetDesc1
>>> > =getSystemLinker().downcallHandle(
>>> > addrGetDesc1,
>>> >
>>> MethodType.methodType(int.class,MemoryAddress.class,MemoryAddress.class),
>>> > FunctionDescriptor.of(C_INT,C_POINTER,C_POINTER));
>>> >
>>> > /* DXGI_ADAPTER_DESC1* */ MemorySegment pDesc
>>> > =DXGI_ADAPTER_DESC1.allocate(scope);
>>> >
>>> checkResult((int)MH_GetDesc1.invokeExact(pAdapter.address(),pDesc.address()));
>>> >
>>> > // print description MemorySegment descStr
>>> > =DXGI_ADAPTER_DESC1.Description$slice(pDesc);
>>> > String str =new
>>> > String(descStr.toByteArray(),StandardCharsets.UTF_16LE);
>>> > System.out.println(str);
>>> > }
>>> > }
>>> >
>>> > public static MemorySegment asSegment(MemoryAddress
>>> > addr,MemoryLayout layout) {
>>> > return MemorySegment.ofNativeRestricted(addr,
>>> > layout.byteSize(),Thread.currentThread(),null,null);
>>> > }
>>> >
>>> > static final MemorySegment IID_IDXGIFactory1 =GUID(
>>> > (int)0x770aae78,
>>> > (short)0xf26f,
>>> > (short)0x4dba,
>>> > new byte[]{(byte)0xa8,0x29,0x25,0x3c,
>>> (byte)0x83,
>>> > (byte)0xd1, (byte)0xb3, (byte)0x87});
>>> >
>>> > static final MemorySegment GUID(int Data1,short
>>> Data2,short
>>> > Data3,byte[] Data4) {
>>> > MemorySegment ms
>>> > =MemorySegment.allocateNative(guiddef_h.GUID.$LAYOUT());
>>> > guiddef_h.GUID.Data1$set(ms, Data1);
>>> > guiddef_h.GUID.Data2$set(ms, Data2);
>>> > guiddef_h.GUID.Data3$set(ms, Data3);
>>> >
>>> guiddef_h.GUID.Data4$slice(ms).copyFrom(MemorySegment.ofArray(Data4));
>>> > return ms;
>>> > }
>>> >
>>> > private static final int S_OK =0x00000000;
>>> >
>>> > private static void checkResult(int result) {
>>> > switch (result) {
>>> > case S_OK -> {}
>>> > default ->throw new
>>> IllegalStateException("Unknown result:
>>> > " +String.format("%X8", result));
>>> > }
>>> > }
>>> > }
>>> >
>>> >
>>> > Which on my machine prints: NVIDIA GeForce GTX 1050 Ti
>>> >
>>> > HTH,
>>> > Jorn
>>> >
>>> > On 14/09/2020 12:52, Maurizio Cimadamore wrote:
>>> >> I think there's something off in here:
>>> >>
>>> >>> MemorySegment segment =
>>> >>>
>>> MemorySegment.allocateNative(dxgi_h.IDXGIFactory1Vtbl.$LAYOUT().byteSize());
>>>
>>> >>>
>>> >>> MemoryAddress address =
>>> segment.address().addOffset(64);
>>> >> I see a couple of issues:
>>> >>
>>> >> 1) how do you determine that the offset of the
>>> function address you
>>> >> want to obtain is 64 byte from the start? Didn't
>>> jextract give you a
>>> >> getter for that?
>>> >>
>>> >> 2) Even assuming 64 byte is the correct offset, note
>>> that the segment
>>> >> will be initialized to all zeros - I presume this
>>> vtable must be
>>> >> filled in somehow, or perhaps obtained from a global
>>> variable?
>>> >>
>>> >> From your earlier email I see this:
>>> >>
>>> >>> interface IDXGIFactory
>>> >>> {
>>> >>> CONST_VTBL struct IDXGIFactoryVtbl *lpVtbl;
>>> >>> };
>>> >> So... perhaps once yo create a IDXGIFactory, you can
>>> just grab the
>>> >> pointer to the vtable by accessing its `lpVtbl` field
>>> - no need to
>>> >> allocate a new segment (the library has done the
>>> allocation for you).
>>> >>
>>> >> If I'm correct, this can be achieved with something
>>> like (not sure if
>>> >> the names are going to be 100% correct since I don't
>>> have a
>>> >> jextracted version of the API with me - so beware, I
>>> have NOT tested
>>> >> this :-) ):
>>> >>
>>> >> var dxgiFactoryPtr =
>>> >>
>>> MemoryAccess.getAddress(MemorySegment.ofNativeRestricted(),
>>> >> dxgiFactory);
>>> >> var lpVtbl =
>>> >>
>>> MemoryAccess.getAddress(MemorySegment.ofNativeRestricted(),
>>> >> dxgiFactoryPtr);
>>> >> var vtable = RuntimeHelper.ofArrayRestricted(lpVtbl,
>>> >> IDXGIFactory1Vtbl.$LAYOUT(), 1); //1
>>> >> var address =
>>> IDXGIFactory1Vtbl$EnumAdapters$get(vtable);
>>> >>
>>> >>
>>> >> And this should give the address pointer you are
>>> looking for. We need
>>> >> to smooth the edges especially around (1) - e.g. to
>>> turn a random
>>> >> native pointer into a struct with given layout
>>> (jextract should
>>> >> generate a static helper for that, see [1]) - but with
>>> the code above
>>> >> you should be able to get there, I hope.
>>> >>
>>> >> P.S.
>>> >> In these situations, my suggestion is, especially when
>>> attacking a
>>> >> native library for the first time, to always try the
>>> library directly
>>> >> using C and see what happens, to understand exactly
>>> what kind of
>>> >> pattern/idiom the library requires. I did that many
>>> times and
>>> >> realized my assumptions on how library worked were
>>> wrong; other users
>>> >> have posted similar instances in this mailing list.
>>> Once you get
>>> >> things working in C, porting the code in Java using
>>> the Panama API is
>>> >> typically a trivial matter.
>>> >>
>>> >> Maurizio
>>> >>
>>> >> [1] - https://bugs.openjdk.java.net/browse/JDK-8253102
>>> >>
>>> >>
>>> >> On 13/09/2020 02:37, Michael Ennen wrote:
>>> >>> Well, progress of a kind? This crashes the VM instead
>>> of not
>>> >>> compiling or
>>> >>> throwing an exception:
>>> >>>
>>> >>> // IDXGIFactory1** dxgiFactory;
>>> >>> var dxgiFactory = scope.allocate(C_POINTER);
>>> >>> // HRESULT = CreateDXGIFactory1(_uuid(dxgiFactory),
>>> &dxgiFactory))
>>> >>> int hresult =
>>> dxgi_h.CreateDXGIFactory1(GUID(IID.IID_IDXGIFactory1),
>>> >>> dxgiFactory);
>>> >>> System.out.println("hresult: " + hresult);
>>> >>> var dxgiAdapter = scope.allocate(C_POINTER);
>>> >>> System.out.println("IDXGIFactory1Vtbl byte size: " +
>>> >>> dxgi_h.IDXGIFactory1Vtbl.$LAYOUT().byteSize()); //112
>>> >>> MemorySegment segment =
>>> >>>
>>> MemorySegment.allocateNative(dxgi_h.IDXGIFactory1Vtbl.$LAYOUT().byteSize());
>>>
>>> >>>
>>> >>> MemoryAddress address =
>>> segment.address().addOffset(64);
>>> >>> FunctionDescriptor functionDescriptor =
>>> FunctionDescriptor.of(C_INT,
>>> >>> C_INT,
>>> >>> C_POINTER);
>>> >>> MethodType methodType =
>>> MethodType.methodType(int.class, int.class,
>>> >>> MemoryAddress.class);
>>> >>> MethodHandle methodHandle =
>>> getSystemLinker().downcallHandle(address,
>>> >>> methodType, functionDescriptor);
>>> >>> try {
>>> >>> methodHandle.invokeWithArguments(0,
>>> dxgiAdapter.address());
>>> >>> } catch (Throwable throwable) {
>>> >>> throwable.printStackTrace();
>>> >>> }
>>> >>>
>>> >>> On Sat, Sep 12, 2020 at 6:34 PM Michael Ennen
>>> <mike.ennen at gmail.com <mailto:mike.ennen at gmail.com>>
>>> >>> wrote:
>>> >>>
>>> >>>> I think I see why. It's because I am passing in a
>>> MemorySegment,
>>> >>>> which is
>>> >>>> the dxgiAdapter which I initialize thusly:
>>> >>>>
>>> >>>> var dxgiAdapter = scope.allocate(C_POINTER);
>>> >>>>
>>> >>>> I guess I need to make it a MemoryAddress instead.
>>> >>>>
>>> >>>> On Sat, Sep 12, 2020 at 2:25 PM Michael Ennen
>>> <mike.ennen at gmail.com <mailto:mike.ennen at gmail.com>>
>>> >>>> wrote:
>>> >>>>
>>> >>>>> Thanks so much Ty. That's super helpful. I think I
>>> was able to
>>> >>>>> make quite
>>> >>>>> a bit of progress:
>>> >>>>>
>>> >>>>> var dxgiAdapter = scope.allocate(C_POINTER);
>>> >>>>> System.out.println("IDXGIFactory1Vtbl byte size: " +
>>> >>>>> dxgi_h.IDXGIFactory1Vtbl.$LAYOUT().byteSize());
>>> >>>>> MemorySegment segment =
>>> >>>>>
>>> MemorySegment.allocateNative(dxgi_h.IDXGIFactory1Vtbl.$LAYOUT().byteSize());
>>>
>>> >>>>>
>>> >>>>> MemoryAddress address =
>>> segment.address().addOffset(64);
>>> >>>>> FunctionDescriptor functionDescriptor =
>>> FunctionDescriptor.of(C_INT,
>>> >>>>> C_INT, C_POINTER);
>>> >>>>> MethodType methodType =
>>> MethodType.methodType(int.class, int.class,
>>> >>>>> MemoryAddress.class);
>>> >>>>> MethodHandle methodHandle =
>>> getSystemLinker().downcallHandle(address,
>>> >>>>> methodType, functionDescriptor);
>>> >>>>> try {
>>> >>>>> methodHandle.invokeWithArguments(0, dxgiAdapter);
>>> >>>>> } catch (Throwable throwable) {
>>> >>>>> throwable.printStackTrace();
>>> >>>>> }
>>> >>>>>
>>> >>>>> Note that the EnumAdapters1 function signature is
>>> actually:
>>> >>>>>
>>> >>>>> HRESULT EnumAdapters1(
>>> >>>>> UINT Adapter,
>>> >>>>> IDXGIAdapter1 **ppAdapter
>>> >>>>> );
>>> >>>>>
>>> >>>>>
>>> >>>>> So that's why I went with:
>>> >>>>>
>>> >>>>> FunctionDescriptor functionDescriptor =
>>> FunctionDescriptor.of(C_INT,
>>> >>>>> C_INT, C_POINTER);
>>> >>>>>
>>> >>>>> and then
>>> >>>>>
>>> >>>>> methodHandle.invokeWithArguments(0, dxgiAdapter);
>>> >>>>>
>>> >>>>> I did some fancy stuff as per your suggestion which
>>> I feel should be
>>> >>>>> getting the address of the EumAdapters1 function
>>> >>>>> pointer (basically start at the base address of the
>>> struct and
>>> >>>>> then move
>>> >>>>> 64 bytes past, which should be the 8th function
>>> >>>>> pointer which in this case is EnumAdapters1).
>>> >>>>>
>>> >>>>> I do get the following exception on invoking the
>>> method handle,
>>> >>>>> though:
>>> >>>>>
>>> >>>>> hresult: 0
>>> >>>>> factory: MemorySegment{ id=0x3898fd8a limit: 8 }
>>> >>>>> IDXGIFactory1Vtbl byte size: 112
>>> >>>>> java.lang.ClassCastException: Cannot cast
>>> >>>>> jdk.internal.foreign.NativeMemorySegmentImpl to
>>> >>>>> jdk.incubator.foreign.MemoryAddress
>>> >>>>> at
>>> java.base/java.lang.Class.cast(Class.java:3816)
>>> >>>>> at
>>> >>>>>
>>> java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:733)
>>>
>>> >>>>>
>>> >>>>> at com.dx12.DX12.main(DX12.java:91)
>>> >>>>> at
>>> >>>>>
>>> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native
>>>
>>> >>>>>
>>> >>>>> Method)
>>> >>>>> at
>>> >>>>>
>>> java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
>>>
>>> >>>>>
>>> >>>>> at
>>> >>>>>
>>> java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
>>>
>>> >>>>>
>>> >>>>> at
>>> >>>>>
>>> java.base/java.lang.reflect.Method.invoke(Method.java:564)
>>> >>>>> at
>>> >>>>>
>>> jdk.compiler/com.sun.tools.javac.launcher.Main.execute(Main.java:415)
>>> >>>>> at
>>> >>>>>
>>> jdk.compiler/com.sun.tools.javac.launcher.Main.run(Main.java:192)
>>> >>>>> at
>>> >>>>>
>>> jdk.compiler/com.sun.tools.javac.launcher.Main.main(Main.java:132)
>>> >>>>> vh:
>>> VarHandle[varType=jdk.incubator.foreign.MemoryAddress,
>>> >>>>> coord=[interface
>>> jdk.incubator.foreign.MemorySegment]]
>>> >>>>>
>>> >>>>> Any more assistance would be greatly appreciate but
>>> undeserved :).
>>> >>>>>
>>> >>>>>
>>> >>>>> On Sat, Sep 12, 2020 at 1:41 AM Ty Young
>>> <youngty1997 at gmail.com <mailto:youngty1997 at gmail.com>>
>>> >>>>> wrote:
>>> >>>>>
>>> >>>>>> Not a JDK developer, but if you want to do this by
>>> hand(aka no
>>> >>>>>> jextract), I had to do this for NVAPI which
>>> obfuscates function
>>> >>>>>> pointers
>>> >>>>>> using keys, e.g.:
>>> >>>>>>
>>> >>>>>>
>>> >>>>>> nvapi_h.nvapi_QueryInterface(0x0150e828);
>>> >>>>>>
>>> >>>>>>
>>> >>>>>> where "0x0150e828" is the "NvAPI_Initialize"
>>> function key as per the
>>> >>>>>> Open Source headers Nvidia released sometime ago.
>>> >>>>>>
>>> >>>>>>
>>> >>>>>> The only difference is that, since the function
>>> pointer is in a
>>> >>>>>> struct,
>>> >>>>>> you need to get the segment in the struct
>>> containing the function
>>> >>>>>> pointer(see "slice" MemorySegment method) and
>>> actually read from it
>>> >>>>>> using a VarHandle. You then take that
>>> MemoryAddress, with a
>>> >>>>>> FunctionDescriptor and MethodType, and create a
>>> MethodHandle. For
>>> >>>>>> reference, this my working code("NativeFunction" and
>>> >>>>>> "NativeTypes" do
>>> >>>>>> not exist in Panama, those are mine but they dont
>>> matter) for the
>>> >>>>>> NvAPI_Initalize API:
>>> >>>>>>
>>> >>>>>>
>>> >>>>>> public class NvAPI_Initialize implements
>>> NativeFunction
>>> >>>>>> {
>>> >>>>>> private final MemoryAddress address;
>>> >>>>>> private final FunctionDescriptor descriptor;
>>> >>>>>> private final MethodType type;
>>> >>>>>>
>>> >>>>>> private final MethodHandle handle;
>>> >>>>>>
>>> >>>>>> public NvAPI_Initialize() throws
>>> NoSuchMethodException,
>>> >>>>>> Throwable
>>> >>>>>> {
>>> >>>>>> this.address =
>>> nvapi_h.nvapi_QueryInterface(0x0150e828);
>>> >>>>>>
>>> >>>>>> this.descriptor =
>>> FunctionDescriptor.of(NativeTypes.INT);
>>> >>>>>>
>>> >>>>>> this.type =
>>> MethodType.methodType(int.class);
>>> >>>>>>
>>> >>>>>> this.handle =
>>> >>>>>>
>>> CSupport.getSystemLinker().downcallHandle(this.address,
>>> this.type,
>>> >>>>>> this.descriptor);
>>> >>>>>> }
>>> >>>>>>
>>> >>>>>> // etc
>>> >>>>>>
>>> >>>>>> }
>>> >>>>>>
>>> >>>>>>
>>> >>>>>> It looks like in your case the FunctionDescriptor
>>> would be:
>>> >>>>>>
>>> >>>>>>
>>> >>>>>> this.descriptor =
>>> FunctionDescriptor.of(CSupport.C_POINTER,
>>> >>>>>> CSupport.C_INT, CSupport.C_POINTER);
>>> >>>>>>
>>> >>>>>>
>>> >>>>>> for MethodType:
>>> >>>>>>
>>> >>>>>>
>>> >>>>>> MethodType.methodType(MemoryAddress.class,
>>> int.class,
>>> >>>>>> MemoryAddress.class);
>>> >>>>>>
>>> >>>>>>
>>> >>>>>> where the first argument takes in a IDXGIFactory,
>>> the second an
>>> >>>>>> unsigned
>>> >>>>>> int, and the third a pointer to what looks like an
>>> opaque device
>>> >>>>>> that
>>> >>>>>> you pass to other functions.
>>> >>>>>>
>>> >>>>>>
>>> >>>>>> If you haven't already, it might help to make
>>> bindings with a
>>> >>>>>> smaller,
>>> >>>>>> less complicated library just to get a feel with
>>> how things work
>>> >>>>>> under
>>> >>>>>> the hood.
>>> >>>>>>
>>> >>>>>>
>>> >>>>>> Hope this helps.
>>> >>>>>>
>>> >>>>>>
>>> >>>>>> On 9/12/20 2:23 AM, Michael Ennen wrote:
>>> >>>>>>> Looking into this a bit further I think I chose a
>>> really bad
>>> >>>>>>> library to
>>> >>>>>>> start with as the function I need to call,
>>> EnumAdapters, is inside
>>> >>>>>>> some type of "VTbl" struct that is trying to
>>> emulate a C++ class
>>> >>>>>>> with
>>> >>>>>>> virtual functions. What jextract is returning is
>>> most likely
>>> >>>>>>> *correct*
>>> >>>>>>> but in this case so complicated because of how it
>>> is laid out in
>>> >>>>>>> C. It
>>> >>>>>> is
>>> >>>>>>> not clear at all how to grab the EnumAdapters
>>> function from
>>> >>>>>>> this mess, and it is probably above my head.
>>> >>>>>>>
>>> >>>>>>> EXTERN_C const IID IID_IDXGIFactory;
>>> >>>>>>>
>>> >>>>>>> #if defined(__cplusplus) && !defined(CINTERFACE)
>>> >>>>>>>
>>> >>>>>>>
>>> MIDL_INTERFACE("7b7166ec-21c7-44ae-b21a-c9ae321ae369")
>>> >>>>>>> IDXGIFactory : public IDXGIObject
>>> >>>>>>> {
>>> >>>>>>> public:
>>> >>>>>>> virtual HRESULT STDMETHODCALLTYPE
>>> EnumAdapters(
>>> >>>>>>> /* [in] */ UINT Adapter,
>>> >>>>>>> /* [annotation][out] */
>>> >>>>>>> _COM_Outptr_ IDXGIAdapter **ppAdapter) = 0;
>>> >>>>>>>
>>> >>>>>>> virtual HRESULT STDMETHODCALLTYPE
>>> MakeWindowAssociation(
>>> >>>>>>> HWND WindowHandle,
>>> >>>>>>> UINT Flags) = 0;
>>> >>>>>>>
>>> >>>>>>> virtual HRESULT STDMETHODCALLTYPE
>>> GetWindowAssociation(
>>> >>>>>>> /* [annotation][out] */
>>> >>>>>>> _Out_ HWND *pWindowHandle) = 0;
>>> >>>>>>>
>>> >>>>>>> virtual HRESULT STDMETHODCALLTYPE
>>> CreateSwapChain(
>>> >>>>>>> /* [annotation][in] */
>>> >>>>>>> _In_ IUnknown *pDevice,
>>> >>>>>>> /* [annotation][in] */
>>> >>>>>>> _In_ DXGI_SWAP_CHAIN_DESC *pDesc,
>>> >>>>>>> /* [annotation][out] */
>>> >>>>>>> _COM_Outptr_ IDXGISwapChain **ppSwapChain) = 0;
>>> >>>>>>>
>>> >>>>>>> virtual HRESULT STDMETHODCALLTYPE
>>> CreateSoftwareAdapter(
>>> >>>>>>> /* [in] */ HMODULE Module,
>>> >>>>>>> /* [annotation][out] */
>>> >>>>>>> _COM_Outptr_ IDXGIAdapter **ppAdapter) = 0;
>>> >>>>>>>
>>> >>>>>>> };
>>> >>>>>>>
>>> >>>>>>>
>>> >>>>>>> #else /* C style interface */
>>> >>>>>>>
>>> >>>>>>> typedef struct IDXGIFactoryVtbl
>>> >>>>>>> {
>>> >>>>>>> BEGIN_INTERFACE
>>> >>>>>>>
>>> >>>>>>> HRESULT ( STDMETHODCALLTYPE
>>> *QueryInterface )(
>>> >>>>>>> IDXGIFactory * This,
>>> >>>>>>> /* [in] */ REFIID riid,
>>> >>>>>>> /* [annotation][iid_is][out] */
>>> >>>>>>> _COM_Outptr_ void **ppvObject);
>>> >>>>>>>
>>> >>>>>>> ULONG ( STDMETHODCALLTYPE *AddRef )(
>>> >>>>>>> IDXGIFactory * This);
>>> >>>>>>>
>>> >>>>>>> ULONG ( STDMETHODCALLTYPE *Release )(
>>> >>>>>>> IDXGIFactory * This);
>>> >>>>>>>
>>> >>>>>>> HRESULT ( STDMETHODCALLTYPE
>>> *SetPrivateData )(
>>> >>>>>>> IDXGIFactory * This,
>>> >>>>>>> /* [annotation][in] */
>>> >>>>>>> _In_ REFGUID Name,
>>> >>>>>>> /* [in] */ UINT DataSize,
>>> >>>>>>> /* [annotation][in] */
>>> >>>>>>> _In_reads_bytes_(DataSize) const void *pData);
>>> >>>>>>>
>>> >>>>>>> HRESULT ( STDMETHODCALLTYPE
>>> *SetPrivateDataInterface )(
>>> >>>>>>> IDXGIFactory * This,
>>> >>>>>>> /* [annotation][in] */
>>> >>>>>>> _In_ REFGUID Name,
>>> >>>>>>> /* [annotation][in] */
>>> >>>>>>> _In_opt_ const IUnknown *pUnknown);
>>> >>>>>>>
>>> >>>>>>> HRESULT ( STDMETHODCALLTYPE
>>> *GetPrivateData )(
>>> >>>>>>> IDXGIFactory * This,
>>> >>>>>>> /* [annotation][in] */
>>> >>>>>>> _In_ REFGUID Name,
>>> >>>>>>> /* [annotation][out][in] */
>>> >>>>>>> _Inout_ UINT *pDataSize,
>>> >>>>>>> /* [annotation][out] */
>>> >>>>>>> _Out_writes_bytes_(*pDataSize) void *pData);
>>> >>>>>>>
>>> >>>>>>> HRESULT ( STDMETHODCALLTYPE *GetParent )(
>>> >>>>>>> IDXGIFactory * This,
>>> >>>>>>> /* [annotation][in] */
>>> >>>>>>> _In_ REFIID riid,
>>> >>>>>>> /* [annotation][retval][out] */
>>> >>>>>>> _COM_Outptr_ void **ppParent);
>>> >>>>>>>
>>> >>>>>>> HRESULT ( STDMETHODCALLTYPE
>>> *EnumAdapters )(
>>> >>>>>>> IDXGIFactory * This,
>>> >>>>>>> /* [in] */ UINT Adapter,
>>> >>>>>>> /* [annotation][out] */
>>> >>>>>>> _COM_Outptr_ IDXGIAdapter **ppAdapter);
>>> >>>>>>>
>>> >>>>>>> HRESULT ( STDMETHODCALLTYPE
>>> *MakeWindowAssociation )(
>>> >>>>>>> IDXGIFactory * This,
>>> >>>>>>> HWND WindowHandle,
>>> >>>>>>> UINT Flags);
>>> >>>>>>>
>>> >>>>>>> HRESULT ( STDMETHODCALLTYPE
>>> *GetWindowAssociation )(
>>> >>>>>>> IDXGIFactory * This,
>>> >>>>>>> /* [annotation][out] */
>>> >>>>>>> _Out_ HWND *pWindowHandle);
>>> >>>>>>>
>>> >>>>>>> HRESULT ( STDMETHODCALLTYPE
>>> *CreateSwapChain )(
>>> >>>>>>> IDXGIFactory * This,
>>> >>>>>>> /* [annotation][in] */
>>> >>>>>>> _In_ IUnknown *pDevice,
>>> >>>>>>> /* [annotation][in] */
>>> >>>>>>> _In_ DXGI_SWAP_CHAIN_DESC *pDesc,
>>> >>>>>>> /* [annotation][out] */
>>> >>>>>>> _COM_Outptr_ IDXGISwapChain **ppSwapChain);
>>> >>>>>>>
>>> >>>>>>> HRESULT ( STDMETHODCALLTYPE
>>> *CreateSoftwareAdapter )(
>>> >>>>>>> IDXGIFactory * This,
>>> >>>>>>> /* [in] */ HMODULE Module,
>>> >>>>>>> /* [annotation][out] */
>>> >>>>>>> _COM_Outptr_ IDXGIAdapter **ppAdapter);
>>> >>>>>>>
>>> >>>>>>> END_INTERFACE
>>> >>>>>>> } IDXGIFactoryVtbl;
>>> >>>>>>>
>>> >>>>>>> interface IDXGIFactory
>>> >>>>>>> {
>>> >>>>>>> CONST_VTBL struct IDXGIFactoryVtbl
>>> *lpVtbl;
>>> >>>>>>> };
>>> >>>>>>>
>>> >>>>>>>
>>> >>>>>
>>> >>>>> --
>>> >>>>> Michael Ennen
>>> >>>>>
>>> >>>>
>>> >>>> --
>>> >>>> Michael Ennen
>>> >>>>
>>> >>>
>>>
>>>
>>>
>>> -- Michael Ennen
>>>
>>>
>>>
>>> -- Michael Ennen
>>
>>
>>
>> --
>> Michael Ennen
More information about the panama-dev
mailing list