- Home /
Directx12 CreateExternalTexture
Hi Unity developers and advanced users!
I'm working on a plugin which uses native rendering and needs to create a texture in C++ code, which then used inside Unity.
For DX11 i'm using ID3D11ShaderResourceView as a parameter for Texture2D.CreateExternalTexture(), however there is no documentation on what should be passed in DX12 case: https://docs.unity3d.com/ScriptReference/Texture2D.CreateExternalTexture.html
Here is my current C++ code:
 void* getTextureHandleForUnityDX12(ID3D12Resource* src_tex)
 {
       D3D12_RESOURCE_DESC src_tex_desc;
       src_tex_desc = src_tex->GetDesc();
 
       // Once the texture is created, we must create a shader resource view of it
       // so that shaders may use it.  In general, the view description will match
       // the texture description.
       D3D12_SHADER_RESOURCE_VIEW_DESC textureViewDesc;
       ZeroMemory(&textureViewDesc, sizeof(textureViewDesc));
       textureViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
       textureViewDesc.Format = src_tex_desc.Format;
       textureViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
       textureViewDesc.Texture2D.MipLevels = src_tex_desc.MipLevels;
       textureViewDesc.Texture2D.MostDetailedMip = 0;
 
       // create shader resource view and constant buffer view descriptor heap
       D3D12_DESCRIPTOR_HEAP_DESC descHeapCbvSrv = {};
       descHeapCbvSrv.NumDescriptors = 1;
       descHeapCbvSrv.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
       descHeapCbvSrv.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
 
       auto hr = m_device->CreateDescriptorHeap(
         &descHeapCbvSrv, __uuidof(ID3D12DescriptorHeap), (void**)&m_descriptorHeap);
       if (FAILED(hr)) { return TranslateReturnCode(hr); }
 
       // https://software.intel.com/en-us/articles/introduction-to-resource-binding-in-microsoft-directx-12
       CD3DX12_CPU_DESCRIPTOR_HANDLE srvHandle0(m_descriptorHeap->GetCPUDescriptorHandleForHeapStart());
       m_device->CreateShaderResourceView(src_tex, &textureViewDesc, srvHandle0);
 
       // Doesn't work: the texture in Unity remains black
       return (void*)&srvHandle0;
 }
Maybe you could add an example of that to https://bitbucket.org/Unity-Technologies/graphicsdemos as well, I think some people will find it quite useful.
Thanks, Lev
Answer by Baroque · Jan 07, 2017 at 05:31 PM
In the native rendering plugin example it's not immediately obvious what type to use but if you dig into the implementation of RenderAPI_D3D12 you'll see the following bit of code in EndModifyTexture:
 void RenderAPI_D3D12::EndModifyTexture(void* textureHandle, int textureWidth, int textureHeight, int rowPitch, void* dataPtr)
 {
     ID3D12Device* device = s_D3D12->GetDevice();
     const UINT64 kDataSize = textureWidth * textureHeight * 4;
     ID3D12Resource* upload = GetUploadResource(kDataSize);
     upload->Unmap(0, NULL);
     ID3D12Resource* resource = (ID3D12Resource*)textureHandle;
     D3D12_RESOURCE_DESC desc = resource->GetDesc();
     assert(desc.Width == textureWidth);
     assert(desc.Height == textureHeight);
You can see textureHandle is cast to ID3D12Resource. That's what Unity will return if you call GetNativePtr on a Texture2D in DirectX12, so that's what you should use to create a Unity texture from scratch. 
Answer by leavittx · Jan 29, 2017 at 07:43 PM
Hey. I tried passing ID3D12Resource* to Texture2D.CreateExternalTexture(), but it didn't work at all.
I will post my createTexture2D() method code:
 Result createTexture2D(void **dst_tex, int width, int height, TextureFormat format, ResourceFlags flags)
   {
       D3D12_HEAP_PROPERTIES heap  = {};
       heap.Type                   = D3D12_HEAP_TYPE_DEFAULT;
       heap.CPUPageProperty        = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
       heap.MemoryPoolPreference   = D3D12_MEMORY_POOL_UNKNOWN;
       heap.CreationNodeMask       = 1;
       heap.VisibleNodeMask        = 1;
 
       D3D12_RESOURCE_DESC desc = {};
       desc.Dimension          = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
       desc.Alignment          = 0;
       desc.Width              = (UINT64)width;
       desc.Height             = (UINT)height;
       desc.DepthOrArraySize   = 1;
       desc.MipLevels          = 1;
       desc.Format             = GetDXGIFormat(format);
       desc.SampleDesc.Count   = 1;
       desc.SampleDesc.Quality = 0;
       desc.Layout             = D3D12_TEXTURE_LAYOUT_UNKNOWN;
       desc.Flags              = D3D12_RESOURCE_FLAG_NONE;
 
       // texture can't be created with D3D12_HEAP_TYPE_UPLOAD / D3D12_HEAP_TYPE_READBACK heap type.
       // ResourceFlags::CPU_Write / CPU_Read flag is ignored.
 
       ID3D12Resource *tex = nullptr;
       auto hr = m_device->CreateCommittedResource(&heap, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&tex) );
       if ( FAILED( hr ) ) { return TranslateReturnCode(hr); }
       *dst_tex = tex;
 
       return Result::OK;
   }
Along with writeTexture2D() code:
 Result writeTexture2D(void *dst_tex_, int width, int height, TextureFormat format, const void *src, size_t write_size)
   {
       if (write_size == 0) { return Result::OK; }
       if (!dst_tex_ || !src) { return Result::InvalidParameter; }
 
       auto *dst_tex = (ID3D12Resource*)dst_tex_;
 
       D3D12_RESOURCE_DESC dst_desc = dst_tex->GetDesc();
       D3D12_PLACED_SUBRESOURCE_FOOTPRINT dst_layout;
       UINT dst_num_rows;
       UINT64 dst_row_size;
       UINT64 dst_required_size;
       m_device->GetCopyableFootprints(&dst_desc, 0, 1, 0, &dst_layout, &dst_num_rows, &dst_row_size, &dst_required_size);
 
 
       auto write_proc = [](ID3D12Resource *dst_tex, int width, int height, TextureFormat format, const void *src, size_t write_size, D3D12_PLACED_SUBRESOURCE_FOOTPRINT& dst_layout) {
           void *mapped_data = nullptr;
           auto hr = dst_tex->Map(0, nullptr, &mapped_data);
           if (FAILED(hr)) { return hr; }
 
           int dst_pitch = dst_layout.Footprint.RowPitch;
           int src_pitch = width * GetTexelSize(format);
 
           int num_rows = std::min<int>(std::min<int>(height, dst_layout.Footprint.Height),
                                        ceildiv<size_t>(write_size, src_pitch));
         
           CopyRegion(mapped_data, dst_pitch, src, src_pitch, num_rows);
           dst_tex->Unmap(0, nullptr);
           return S_OK;
       };
 
 
       // try direct access
       auto hr = write_proc(dst_tex, width, height, format, src, write_size, dst_layout);
       if (SUCCEEDED(hr)) { return Result::OK; }
 
 
       // try copy-via-staging
       auto staging = createStagingBuffer(dst_required_size, StagingFlag::Upload);
       if (!staging) { return Result::OutOfMemory; }
 
       hr = write_proc(staging.Get(), width, height, format, src, write_size, dst_layout);
       if (FAILED(hr)) { return TranslateReturnCode(hr); }
 
       hr = executeCommands([&](ID3D12GraphicsCommandList *clist) {
           CD3DX12_TEXTURE_COPY_LOCATION dst_region(dst_tex, 0);
           CD3DX12_TEXTURE_COPY_LOCATION src_region(staging.Get(), dst_layout);
 
           clist->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(dst_tex, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST));
           clist->CopyTextureRegion(&dst_region, 0, 0, 0, &src_region, nullptr);
           clist->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(dst_tex, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COMMON));
       });
       if (FAILED(hr)) { return TranslateReturnCode(hr); }
 
       return Result::OK;
   }
Maybe someone can point me to possible problems with this code.
One good thing is that now I can see that the writeTexture2D() code is rather a DX11 copy-paste with some changes applied to make it compile with DX12 API being used, then a DX12-oriented solution. Should I try something with GetUploadResource() like GraphicsDemos does ?
Your answer
 
 
              koobas.hobune.stream
koobas.hobune.stream 
                       
                
                       
			     
			 
                