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

fails in device.CreateTexture2D() #46

Closed vmx17 closed 11 months ago

vmx17 commented 11 months ago

Hi! It's me, again. I'm sorry my too much questions. Recently I hit same situation with issue #26. Because I'm using Microsoft.UI.Xaml.Controls.SwapChainPanel, I try to use DirectX resources in single thread. I'm making a string texture to show text on D3D11 space. The texture is made by CPU. Then, the first, I made font textures using Windows.Win32.PInvoke.GetGlyphOutline. It seems working. The second, I try to make Texture2D using the font bitmap with CreateTexture2D() method.

My code is as follows (using DirectNCore on .Net6);

public HRESULT BuildNewStringTexture(in FONT_TEXTURE_DATA[] ftd)
{
  // make texture2d of text which can be written by CPU
  DirectN.D3D11_TEXTURE2D_DESC m_textTextureDesc = new()
  {
    Width = 500,
    Height = 250,
    MipLevels = 1,
    Format = DirectN.DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM,
    SampleDesc = new DirectN.DXGI_SAMPLE_DESC()
    {
      Count = 1,
      Quality = 0
    },
    Usage = DirectN.D3D11_USAGE.D3D11_USAGE_DYNAMIC,
    BindFlags = (uint)DirectN.D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE,
    CPUAccessFlags = (uint)DirectN.D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_WRITE,
    MiscFlags = 0
  };
  lock (m_DeviceLock)
  {
    m_build_texture = true;   // a flag to make texture
  }
  // wait here for build m_texture: m_build_texture to be false;

Actually the parameters are in struct, I expand it to values above. On the other hand, there is iterating Render() method in main thread. Then I put there

public bool Render()
{
  lock (m_DeviceLock)
  {
    if (m_build_texture)
    {
      m_texture = m_device.CreateTexture2D(m_textTextureDesc, null);
      m_build_texture = false;
    }
  ...

The issue is at the time m_texture = m_device.CreateTexture2D(m_textTextureDesc, null); executed, I got an error saying;
"System.ComponentModel.Win32Exception: 'The parameter is incorrect.'" Do you have any idea about this?
The error seems just a simple parameter mismatch. The MiscFlags is meanless because I specified CPUAccessFlags. (Because this error occurred in "Main Thread", the issue #26 seems to be avoided.) Even though I put the definition m_textTextureDesc in front of m_texture = m_device.CreateTexture2D(m_textTextureDesc, null);, I got same error.

The ID3D11Device (m_device) is created with following code.

private IComObject<DirectN.ID3D11Device> m_device;
private IComObject<DirectN.ID3D11DeviceContext> m_deviceContext;
private IComObject<DirectN.ID3D11Texture2D> m_texture;
...
  D3D_FEATURE_LEVEL[] featureLevels = new D3D_FEATURE_LEVEL[]{ D3D_FEATURE_LEVEL.D3D_FEATURE_LEVEL_11_1 };
  var flags = D3D11_CREATE_DEVICE_FLAG.D3D11_CREATE_DEVICE_BGRA_SUPPORT |  
D3D11_CREATE_DEVICE_FLAG.D3D11_CREATE_DEVICE_DEBUG;
  m_device = DirectN.D3D11Functions.D3D11CreateDevice(null, D3D_DRIVER_TYPE.D3D_DRIVER_TYPE_HARDWARE, flags, out m_deviceContext, featureLevels);

ah... I realized the way current m_device should be modified if I use device context out of Main Thread. Are there any API to make second device context?

I defined the texture as DYNAMIC and D3D11_CPU_ACCESS_WRITE, so I'll copy font bitmaps to the m_texture after. That's not smart at all.

vmx17 commented 11 months ago

I may get thread safe device context with

private IComObject<DirectN.ID3D11DeviceContext> m_deviceContext4TextMapping = m_device.GetImmediateContext();
smourier commented 11 months ago

"The parameter is incorrect" isn't a priori related to threading. If you enabled the DirectX debug layer in D3D and DXGI (seems you have), you should see more information output by DirectX in the debug output.

vmx17 commented 11 months ago

Thank you. Yes, it is not related threading. DirectX debug layer? I'll search it. but in previous post, I was wrong. private IComObject<DirectN.ID3D11DeviceContext> m_deviceContext4TextMapping = m_device.GetImmediateContext(); The call from other thread falls on issue #26 because it use m_device. hmm...

vmx17 commented 11 months ago

I had already set graphic debug layer up and was using (was default).

OK, I've set DirectN(Core) as project referemce in Debug mode and look into error source. Then I find out ("ID3D11DeviceExtensions.cs" ---> "ID3D11Device.cs" ---> "HRESULT.cs") the error I got was just comming from Win32 API "CreateTexture2D". The HRESULT code is 0x80070057 in this page, "An invalid parameter was passed to the returning function.". This means no further information available. The "invalid parameter" may be in the D3D11_TEXTURE2D_DESC.

vmx17 commented 11 months ago

At last I get the reason of error. I just forget to specify ArraySize = 1, in the D3D11_TEXTURE_2D... orz And now, I'm in the storm of issue #26. Map/Unmap is also the case. Everything I have to en-flatten to UI thread. Otherwise, I got the error "Unable to cast COM object of type 'System.__ComObject' to interface type 'DirectN.ID3D11DeviceContext'." As far as I use DispatcherQueue or SynchronizationContext it works but no ref, out data or structs are not allowed to pass over thread boundary.
Anyway, I'm sorry about this fuss.