As seen in the other tutorial that described how to access and use a shared texture in Unity, we can also try to do this in Unreal. The problem is that there are almost no tutorials out there that describe how to accomplish this, so one has to search deep through the Unreal code base. Therefore I am writing down my knowledge about this.

Since Unreal is C++-based, we can access the shared texture with native source code. We just have to get the Handle to the shared texture (which basically is a unsigned long long type) and get the D3D device:

#include "DXTextureSharingManager.h"
#include "Containers/ResourceArray.h"
#include "Engine/Engine.h"

#if PLATFORM_WINDOWS
#define WIN32_LEAN_AND_MEAN
#include "Windows/MinWindows.h"
#include <d3d11.h>
#include "D3D11RHI/Private/D3D11RHIPrivate.h"
#include "D3D11Util.h"
#include "DynamicRHI.h"
#endif

#define WIDTH 1280
#define HEIGHT 720


void DXTextureSharingManager::SetTexHandleFromTexId(byte texId[])
{
    SetTexHandleFromTexId(BytesToString(texId, 1));
}

bool DXTextureSharingManager::UpdateMlabTextureRef(HANDLE mlabTextureHandle)
{
#if PLATFORM_WINDOWS
    if (_texSharingInitiated || mlabTextureHandle == NULL)
        return false;

    HRESULT hr;

    /// Hack into Unreal's D3D device
    ID3D11DeviceContext* D3D11DeviceContext = nullptr;
    auto d3dDevicePtr = static_cast<ID3D11Device*>(GDynamicRHI->RHIGetNativeDevice());
    d3dDevicePtr->GetImmediateContext(&D3D11DeviceContext);

    /// Get Shader Resource View to shared texture
    IDXGIResource* d3d11ResPtr = NULL;
    hr = d3dDevicePtr->OpenSharedResource(mlabTextureHandle, __uuidof(ID3D11Resource), (void**)(&d3d11ResPtr));
    if (FAILED(hr)) return false;
    ID3D11Texture2D* d3d11TexPtr = NULL;
    hr = d3d11ResPtr->QueryInterface(__uuidof(ID3D11Texture2D), (void**)(&d3d11TexPtr));
    if (FAILED(hr)) return false;

    d3d11ResPtr->Release();

    /// Create texture in Unreal context
    _rt = UTexture2DExternal::Create(2 * WIDTH, 2 * HEIGHT, d3d11TexPtr);

    _texSharingInitiated = true;
    return true;
#endif
}

The hardest part is now to create the texture also in the Unreal context. Since Unreal does not provide a simple method for this, we have to create a custom Texture class that, instead of creating a native texture resource itself, uses our shared texture as the underlying D3D texture. As a template/starting point, I used the Texture2DDynamic class and changed it to my needs. The most relevant part is this:

/** Called when the resource is initialized. This is only called by the rendering thread. */
void FTexture2DExternalResource::InitRHI()
{
    FD3D11DynamicRHI* DynamicRHI = static_cast<FD3D11DynamicRHI*>(GDynamicRHI);

    // Create the sampler state RHI resource.
    ESamplerAddressMode SamplerAddressMode = Owner->SamplerAddressMode;
    FSamplerStateInitializerRHI SamplerStateInitializer
    (
        (ESamplerFilter)UDeviceProfileManager::Get().GetActiveProfile()->GetTextureLODSettings()->GetSamplerFilter(Owner),
        SamplerAddressMode,
        SamplerAddressMode,
        SamplerAddressMode
    );
    SamplerStateRHI = RHICreateSamplerState(SamplerStateInitializer);

    uint32 Flags = 0;
    if (Owner->bIsResolveTarget)
    {
        Flags |= TexCreate_ResolveTargetable;
        bIgnoreGammaConversions = true;         // Note, we're ignoring Owner->SRGB (it should be false).
    }
    else if (Owner->SRGB)
    {
        Flags |= TexCreate_SRGB;
    }
    if (Owner->bNoTiling)
    {
        Flags |= TexCreate_NoTiling;
    }
    FRHIResourceCreateInfo CreateInfo;

    /// Central RHI call for creating texture from current handle
    //Texture2DRHI = RHICreateTexture2D(GetSizeX(), GetSizeY(), Owner->Format, Owner->NumMips, 1, Flags, CreateInfo);
    Texture2DRHI = DynamicRHI->RHICreateTexture2DFromResource(PF_R8G8B8A8, TexCreate_None, FClearValueBinding::Green, _d3d11TexPtr);

    TextureRHI = Texture2DRHI;
    TextureRHI->SetName(Owner->GetFName());
    RHIUpdateTextureReference(Owner->TextureReference.TextureReferenceRHI, TextureRHI);
}

Here, instead of creating a new texture via RHICreateTexture2D, I use the RHICreateTexture2DFromResource method and give it my former created texture pointer.

More information following soon.

Tags
, ,

Add a comment

*Please complete all fields correctly

Related Blogs