Skip to content

[WIP] Direct3D with Silk.NET#1123

Closed
ykafia wants to merge 37 commits into
stride3d:masterfrom
ykafia:silk-dx
Closed

[WIP] Direct3D with Silk.NET#1123
ykafia wants to merge 37 commits into
stride3d:masterfrom
ykafia:silk-dx

Conversation

@ykafia

@ykafia ykafia commented Aug 22, 2021

Copy link
Copy Markdown
Contributor

PR Details

This PR intends to switch from SharpDX to Silk.NET for Direct3D

Related Issue

#1176

Motivation and Context

SharpDX lacks support and development. As good as this library is, it's great to be able to use ones that are still updated to get access to newer technologies such as Mesh Shaders and other apis.

Types of changes

  • Docs change / refactoring / dependency upgrade
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Related issues

#432

Checklist

  • My change requires a change to the documentation.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

@ykafia ykafia changed the title Implement Direct3D with Silk.NET [WIP] Direct3D with Silk.NET Aug 22, 2021
@ykafia ykafia marked this pull request as draft August 22, 2021 21:10
@ykafia

ykafia commented Sep 6, 2021

Copy link
Copy Markdown
Contributor Author

Just to give a bit more explanation on what's going on in this PR :

SharpDX went to great length abstracting Direct3D with C# OOP. Since Silk.NET uses raw native bindings all of the sweet OOP is gone and we are instead using native structures and interfaces, which will result in more mostly unsafe LOCs.

The pain point of this PR is my limited experience with manipulating references in general and my limited knowledge of DirectX. Since i can't build until i finished porting everything, most of the implementation will be broken so if anything, most of the help needed will be in correcting my wrong uses of functions and values/parameters.

Opinion on using Silk.NET :
Vastly better than SharpDX since C++ tutorials can be translated in C#

@ykafia

ykafia commented Nov 1, 2021

Copy link
Copy Markdown
Contributor Author

So i've got a good news and a bad news :

  • Good news is that now, Stride+Silk.NET.DX11 compiles
  • Bad news is that it doesn't run, issues comes from the runtime library so i have high hopes i can debug it and find the issues with it 🤞

I removed the DXVA implementation in Stride.Video since it's old (it's from DX9 era and isn't used much anymore) in favor of FFMPEG which should be fine.

@ykafia ykafia mentioned this pull request Nov 3, 2021
Comment on lines +9 to +15
public enum DXGIMwaFlags : uint
{
NO_WINDOW_CHANGES = 0x1,
NO_ALT_ENTER = 0x2,
NO_PRINT_SCREEN = 0x4,
VALID = 0x7
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd welcome these contributions upstream in the Silk.NET library itself, our generator is still early days and doesn't necessarily generate everything today. In 3.0 we're rewriting our generator to be more stable, but that version is still in the planning stage at the moment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! I got myself a bit more familiar with generators this week (I knew quasi nothing about it before that). I'll do my best but it might take some time!

@Perksey Perksey Nov 19, 2021

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also just add these as manual files for now, which should be just a simple copy-and-paste job :)
(and maybe changing the names a bit to fit in!)

The generators are absolutely horrible to work with, so I wouldn't wish that upon a community contributor.

Comment on lines +273 to +275
private static IEnumerable<Guid> FindVideoFormatCompatibleProfiles(Silk.NET.Direct3D11.ID3D11VideoDevice1 videoDevice, AVCodec codec)
{
for (var i = 0; i < videoDevice.VideoDecoderProfileCount; ++i)
for (var i = 0; i < videoDevice.GetVideoDecoderProfileCount(); ++i)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't work as intended, and will likely crash.

In general, you should never have a struct representing an interface ever used by-value.

i.e. videoDevice needs to be ID3D11VideoDevice1* (note the pointer) or ComPtr<ID3D11VideoDevice1>, not ID3D11VideoDevice1

Comment on lines +44 to +45
var tex = new ID3D11Texture2D((void**)ptr);
textures[i].InitializeFromImpl(new ComPtr<ID3D11Texture2D>(&tex), false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also won't work as intended, for almost the same reason.

For context, how our struct wrappers work is they take a pointer to &this, which when used as a pointer maps to what DirectX has given us. I explained it to you in our Discord server before, but paraphrasing it here for others' reference:

let's say the variable [tex] is at address 0x1 and [D3D11] returns address 0x2 as the [texture]'s address, with some internal state holding the [texture] info at 0x3 (i.e. 0x2+1). when you dereference 0x2 into 0x1, when [InitializeFromImpl] tries to get 0x1+1 you get a segfault because that state doesn't exist - &this is now 0x1 instead of 0x2.

You're probably looking for:

Suggested change
var tex = new ID3D11Texture2D((void**)ptr);
textures[i].InitializeFromImpl(new ComPtr<ID3D11Texture2D>(&tex), false);
var tex = new ComPtr<ID3D11Texture2D>((ID3D11Texture2D*)ptr);
textures[i].InitializeFromImpl(tex, false);

unsafe
{
var tex = new ID3D11Texture2D((void**)ptr);
textures[i].InitializeFromImpl(new ComPtr<ID3D11Texture2D>(&tex), false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A general note about ComPtrs (not a problem here, but wanted to ensure I put this somewhere): the idea of ComPtr is that it does some of the management of the underlying pointer's lifetime for you.

So:

ID3D11Texture2D* nativePtr = /* some code to get a pointer from native */;
// let's call the refcount of nativePtr here "n"
var ptr1 = new ComPtr<ID3D11Texture2D>(nativePtr); // calls AddRef, meaning the refcount is now n + 1
var ptr2 = new ComPtr<ID3D11Texture2D>(ptr1); // copies ptr1 and calls AddRef again, meaning the refcount is now n + 2
ptr1.Dispose(); // Release is called, decrementing the refcount to n + 1
ptr2.Dispose(); // Release is called, decrementing the refcount back to n
// if n is zero, the native pointer is freed here.

It's best to model this in your code as closely as possible. C# unfortunately does not have copy constructors like C++ does, otherwise this would be a lot easier.

As such, whenever you're passing a ComPtr to a method or something, it's best to clone that ComPtr as illustrated above (ptr2 being created from ptr1). Likewise, to avoid memory leaks always ensure you dispose your ComPtrs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand a bit better, i think i did some mistakes in other parts i will recheck everything to be sure.
If i understood well, everytime i need to "borrow" a ComPtr i should make sure to use it and dispose it after ward ?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. How the C++ implementation does it is every time the struct is copied, the ref counter goes up and every time the struct goes out of scope, the ref counter goes down.

ComPtr<ID3D11Texture2D> thing_one { native_ptr };
{
    ComPtr<ID3D11Texture2D> thing_two { thing_one }; // C++ magic increments the ref counter here because it's a copy
} // thing_two is out of scope now, so ref counter decrements

Obviously it's not as easy to do that in C# so the closest you can get is:

  • where you would otherwise copy: new(ptr) to create a copy of the ptr.
  • at the end of methods which take a ComPtr: ptr.Dispose()

Perhaps that's not the cleanest way, and feel free to do what fits best; but hopefully my explanation on how the C++ original works helps :)

@ykafia

ykafia commented Nov 18, 2023

Copy link
Copy Markdown
Contributor Author

#1715 is much better

@ykafia ykafia closed this Nov 18, 2023
@ykafia ykafia deleted the silk-dx branch November 18, 2023 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants