From 9606401e1de1002e3ad070bc5c6384fa5bc4d9ff Mon Sep 17 00:00:00 2001
From: lucy96chen <47800040+lucy96chen@users.noreply.github.com>
Date: Wed, 8 Dec 2021 11:38:14 -0800
Subject: D3D12 and Vulkan to CUDA Texture Sharing (#2038)
---
.../gfx-unit-test-tool/gfx-unit-test-tool.vcxproj | 4 +-
.../gfx-unit-test-tool.vcxproj.filters | 12 +-
slang-gfx.h | 6 +
tools/gfx-unit-test/get-shared-handle.cpp | 125 -----------
tools/gfx-unit-test/gfx-test-util.cpp | 14 ++
tools/gfx-unit-test/gfx-test-util.h | 24 +++
tools/gfx-unit-test/shared-buffers-tests.cpp | 123 +++++++++++
tools/gfx-unit-test/shared-textures-tests.cpp | 221 ++++++++++++++++++++
tools/gfx-unit-test/trivial-copy.slang | 18 ++
tools/gfx-util/shader-cursor.h | 6 +
tools/gfx/cuda/render-cuda.cpp | 232 +++++++++++++++++----
tools/gfx/d3d12/render-d3d12.cpp | 17 +-
tools/gfx/debug-layer.cpp | 16 ++
tools/gfx/debug-layer.h | 5 +
tools/gfx/renderer-shared.cpp | 14 ++
tools/gfx/renderer-shared.h | 6 +
tools/gfx/vulkan/render-vk.cpp | 162 +++++++++-----
17 files changed, 775 insertions(+), 230 deletions(-)
delete mode 100644 tools/gfx-unit-test/get-shared-handle.cpp
create mode 100644 tools/gfx-unit-test/shared-buffers-tests.cpp
create mode 100644 tools/gfx-unit-test/shared-textures-tests.cpp
create mode 100644 tools/gfx-unit-test/trivial-copy.slang
diff --git a/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj b/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj
index 1463cfecb..81790de85 100644
--- a/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj
+++ b/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj
@@ -280,10 +280,11 @@
-
+
+
@@ -293,6 +294,7 @@
+
diff --git a/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj.filters b/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj.filters
index 80186a7eb..988f78ef4 100644
--- a/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj.filters
+++ b/build/visual-studio/gfx-unit-test-tool/gfx-unit-test-tool.vcxproj.filters
@@ -41,9 +41,6 @@
Source Files
-
- Source Files
-
Source Files
@@ -53,6 +50,12 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
Source Files
@@ -76,5 +79,8 @@
Source Files
+
+ Source Files
+
\ No newline at end of file
diff --git a/slang-gfx.h b/slang-gfx.h
index 0600d5623..8ed99f2c1 100644
--- a/slang-gfx.h
+++ b/slang-gfx.h
@@ -1952,6 +1952,12 @@ public:
const ITextureResource::Desc& srcDesc,
ITextureResource** outResource) = 0;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createTextureFromSharedHandle(
+ InteropHandle handle,
+ const ITextureResource::Desc& srcDesc,
+ const size_t size,
+ ITextureResource** outResource) = 0;
+
/// Create a buffer resource
virtual SLANG_NO_THROW Result SLANG_MCALL createBufferResource(
const IBufferResource::Desc& desc,
diff --git a/tools/gfx-unit-test/get-shared-handle.cpp b/tools/gfx-unit-test/get-shared-handle.cpp
deleted file mode 100644
index 8b991f15c..000000000
--- a/tools/gfx-unit-test/get-shared-handle.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-#include "tools/unit-test/slang-unit-test.h"
-
-#include "slang-gfx.h"
-#include "gfx-test-util.h"
-#include "tools/gfx-util/shader-cursor.h"
-#include "source/core/slang-basic.h"
-
-using namespace gfx;
-
-namespace gfx_test
-{
- void sharedHandleTestImpl(IDevice* srcDevice, IDevice* dstDevice, UnitTestContext* context)
- {
- // Create a shareable buffer using srcDevice, get its handle, then create a buffer using the handle using
- // dstDevice. Read back the buffer and check that its contents are correct.
- const int numberCount = 4;
- float initialData[] = { 0.0f, 1.0f, 2.0f, 3.0f };
- IBufferResource::Desc bufferDesc = {};
- bufferDesc.sizeInBytes = numberCount * sizeof(float);
- bufferDesc.format = gfx::Format::Unknown;
- bufferDesc.elementSize = sizeof(float);
- bufferDesc.allowedStates = ResourceStateSet(
- ResourceState::ShaderResource,
- ResourceState::UnorderedAccess,
- ResourceState::CopyDestination,
- ResourceState::CopySource);
- bufferDesc.defaultState = ResourceState::UnorderedAccess;
- bufferDesc.cpuAccessFlags = AccessFlag::Write | AccessFlag::Read;
- bufferDesc.isShared = true;
-
- ComPtr srcBuffer;
- GFX_CHECK_CALL_ABORT(srcDevice->createBufferResource(
- bufferDesc,
- (void*)initialData,
- srcBuffer.writeRef()));
-
- InteropHandle sharedHandle;
- GFX_CHECK_CALL_ABORT(srcBuffer->getSharedHandle(&sharedHandle));
- ComPtr dstBuffer;
- GFX_CHECK_CALL_ABORT(dstDevice->createBufferFromSharedHandle(sharedHandle, bufferDesc, dstBuffer.writeRef()));
- // Reading back the buffer from srcDevice to make sure it's been filled in before reading anything back from dstDevice
- // TODO: Implement actual synchronization (and not this hacky solution)
- compareComputeResult(srcDevice, srcBuffer, Slang::makeArray(0.0f, 1.0f, 2.0f, 3.0f));
-
- InteropHandle testHandle;
- GFX_CHECK_CALL_ABORT(dstBuffer->getNativeResourceHandle(&testHandle));
- IBufferResource::Desc* testDesc = dstBuffer->getDesc();
- SLANG_CHECK(testDesc->elementSize == sizeof(float));
- SLANG_CHECK(testDesc->sizeInBytes == numberCount * sizeof(float));
- compareComputeResult(dstDevice, dstBuffer, Slang::makeArray(0.0f, 1.0f, 2.0f, 3.0f));
-
- // Check that dstBuffer can be successfully used in a compute dispatch using dstDevice.
- Slang::ComPtr transientHeap;
- ITransientResourceHeap::Desc transientHeapDesc = {};
- transientHeapDesc.constantBufferSize = 4096;
- GFX_CHECK_CALL_ABORT(
- dstDevice->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef()));
-
- ComPtr shaderProgram;
- slang::ProgramLayout* slangReflection;
- GFX_CHECK_CALL_ABORT(loadComputeProgram(dstDevice, shaderProgram, "compute-trivial", "computeMain", slangReflection));
-
- ComputePipelineStateDesc pipelineDesc = {};
- pipelineDesc.program = shaderProgram.get();
- ComPtr pipelineState;
- GFX_CHECK_CALL_ABORT(
- dstDevice->createComputePipelineState(pipelineDesc, pipelineState.writeRef()));
-
- ComPtr bufferView;
- IResourceView::Desc viewDesc = {};
- viewDesc.type = IResourceView::Type::UnorderedAccess;
- viewDesc.format = Format::Unknown;
- GFX_CHECK_CALL_ABORT(dstDevice->createBufferView(dstBuffer, viewDesc, bufferView.writeRef()));
-
- {
- ICommandQueue::Desc queueDesc = { ICommandQueue::QueueType::Graphics };
- auto queue = dstDevice->createCommandQueue(queueDesc);
-
- auto commandBuffer = transientHeap->createCommandBuffer();
- auto encoder = commandBuffer->encodeComputeCommands();
-
- auto rootObject = encoder->bindPipeline(pipelineState);
-
- ShaderCursor rootCursor(rootObject);
- // Bind buffer view to the entry point.
- rootCursor.getPath("buffer").setResource(bufferView);
-
- encoder->dispatchCompute(1, 1, 1);
- encoder->endEncoding();
- commandBuffer->close();
- queue->executeCommandBuffer(commandBuffer);
- queue->waitOnHost();
- }
-
- compareComputeResult(
- dstDevice,
- dstBuffer,
- Slang::makeArray(1.0f, 2.0f, 3.0f, 4.0f));
- }
-
- void sharedHandleTestAPI(UnitTestContext* context, Slang::RenderApiFlag::Enum srcApi, Slang::RenderApiFlag::Enum dstApi)
- {
- auto srcDevice = createTestingDevice(context, srcApi);
- auto dstDevice = createTestingDevice(context, dstApi);
- if (!srcDevice || !dstDevice)
- {
- SLANG_IGNORE_TEST;
- }
-
- sharedHandleTestImpl(srcDevice, dstDevice, context);
- }
-#if SLANG_WIN64
- SLANG_UNIT_TEST(sharedHandleD3D12ToCUDA)
- {
- sharedHandleTestAPI(unitTestContext, Slang::RenderApiFlag::D3D12, Slang::RenderApiFlag::CUDA);
- }
-
-#if SLANG_WINDOWS_FAMILY // TODO: Remove when Linux support is added
- SLANG_UNIT_TEST(sharedHandleVulkanToCUDA)
- {
- sharedHandleTestAPI(unitTestContext, Slang::RenderApiFlag::Vulkan, Slang::RenderApiFlag::CUDA);
- }
-#endif
-#endif
-}
diff --git a/tools/gfx-unit-test/gfx-test-util.cpp b/tools/gfx-unit-test/gfx-test-util.cpp
index aafa3fc59..952638fd8 100644
--- a/tools/gfx-unit-test/gfx-test-util.cpp
+++ b/tools/gfx-unit-test/gfx-test-util.cpp
@@ -66,6 +66,20 @@ namespace gfx_test
return SLANG_OK;
}
+ void compareComputeResult(gfx::IDevice* device, gfx::ITextureResource* texture, gfx::ResourceState state, float* expectedResult, size_t expectedBufferSize)
+ {
+ // Read back the results.
+ ComPtr resultBlob;
+ size_t rowPitch = 0;
+ size_t pixelSize = 0;
+ GFX_CHECK_CALL_ABORT(device->readTextureResource(
+ texture, state, resultBlob.writeRef(), &rowPitch, &pixelSize));
+ SLANG_CHECK(resultBlob->getBufferSize() == expectedBufferSize);
+ auto result = (float*)resultBlob->getBufferPointer();
+ // Compare results.
+ SLANG_CHECK(memcmp(resultBlob->getBufferPointer(), expectedResult, expectedBufferSize) == 0);
+ }
+
void compareComputeResult(gfx::IDevice* device, gfx::IBufferResource* buffer, uint8_t* expectedResult, size_t expectedBufferSize)
{
// Read back the results.
diff --git a/tools/gfx-unit-test/gfx-test-util.h b/tools/gfx-unit-test/gfx-test-util.h
index 01a367915..07513c39a 100644
--- a/tools/gfx-unit-test/gfx-test-util.h
+++ b/tools/gfx-unit-test/gfx-test-util.h
@@ -25,6 +25,14 @@ namespace gfx_test
uint8_t* expectedResult,
size_t expectedBufferSize);
+ /// Reads back the content of `buffer` and compares it against `expectedResult`.
+ void compareComputeResult(
+ gfx::IDevice* device,
+ gfx::ITextureResource* texture,
+ gfx::ResourceState state,
+ float* expectedResult,
+ size_t expectedBufferSize);
+
/// Reads back the content of `buffer` and compares it against `expectedResult` with a set tolerance.
void compareComputeResultFuzzy(
gfx::IDevice* device,
@@ -45,6 +53,22 @@ namespace gfx_test
if (std::is_same::value) return compareComputeResultFuzzy(device, buffer, (float*)expectedBuffer.getBuffer(), bufferSize);
return compareComputeResult(device, buffer, expectedBuffer.getBuffer(), bufferSize);
}
+
+// TODO: Implement compareComputeResultFuzzy() and keep or just directly use compareComputeResult() above and add a second overload for uint/int?
+// template
+// void compareComputeResult(
+// gfx::IDevice* device,
+// gfx::ITextureResource* texture,
+// gfx::ResourceState state,
+// Slang::Array expectedResult)
+// {
+// Slang::List expectedBuffer;
+// size_t bufferSize = sizeof(T) * count;
+// expectedBuffer.setCount(bufferSize);
+// memcpy(expectedBuffer.getBuffer(), expectedResult.begin(), bufferSize);
+// if (std::is_same::value) return compareComputeResultFuzzy(device, buffer, (float*)expectedBuffer.getBuffer(), bufferSize);
+// return compareComputeResult(device, texture, state, expectedBuffer.getBuffer(), bufferSize);
+// }
Slang::ComPtr createTestingDevice(UnitTestContext* context, Slang::RenderApiFlag::Enum api);
diff --git a/tools/gfx-unit-test/shared-buffers-tests.cpp b/tools/gfx-unit-test/shared-buffers-tests.cpp
new file mode 100644
index 000000000..fe7757083
--- /dev/null
+++ b/tools/gfx-unit-test/shared-buffers-tests.cpp
@@ -0,0 +1,123 @@
+#include "tools/unit-test/slang-unit-test.h"
+
+#include "slang-gfx.h"
+#include "gfx-test-util.h"
+#include "tools/gfx-util/shader-cursor.h"
+#include "source/core/slang-basic.h"
+
+using namespace gfx;
+
+namespace gfx_test
+{
+ void sharedBufferTestImpl(IDevice* srcDevice, IDevice* dstDevice, UnitTestContext* context)
+ {
+ // Create a shareable buffer using srcDevice, get its handle, then create a buffer using the handle using
+ // dstDevice. Read back the buffer and check that its contents are correct.
+ const int numberCount = 4;
+ float initialData[] = { 0.0f, 1.0f, 2.0f, 3.0f };
+ IBufferResource::Desc bufferDesc = {};
+ bufferDesc.sizeInBytes = numberCount * sizeof(float);
+ bufferDesc.format = gfx::Format::Unknown;
+ bufferDesc.elementSize = sizeof(float);
+ bufferDesc.allowedStates = ResourceStateSet(
+ ResourceState::ShaderResource,
+ ResourceState::UnorderedAccess,
+ ResourceState::CopyDestination,
+ ResourceState::CopySource);
+ bufferDesc.defaultState = ResourceState::UnorderedAccess;
+ bufferDesc.cpuAccessFlags = AccessFlag::Write | AccessFlag::Read;
+ bufferDesc.isShared = true;
+
+ ComPtr srcBuffer;
+ GFX_CHECK_CALL_ABORT(srcDevice->createBufferResource(
+ bufferDesc,
+ (void*)initialData,
+ srcBuffer.writeRef()));
+
+ InteropHandle sharedHandle;
+ GFX_CHECK_CALL_ABORT(srcBuffer->getSharedHandle(&sharedHandle));
+ ComPtr dstBuffer;
+ GFX_CHECK_CALL_ABORT(dstDevice->createBufferFromSharedHandle(sharedHandle, bufferDesc, dstBuffer.writeRef()));
+ // Reading back the buffer from srcDevice to make sure it's been filled in before reading anything back from dstDevice
+ // TODO: Implement actual synchronization (and not this hacky solution)
+ compareComputeResult(srcDevice, srcBuffer, Slang::makeArray(0.0f, 1.0f, 2.0f, 3.0f));
+
+ InteropHandle testHandle;
+ GFX_CHECK_CALL_ABORT(dstBuffer->getNativeResourceHandle(&testHandle));
+ IBufferResource::Desc* testDesc = dstBuffer->getDesc();
+ SLANG_CHECK(testDesc->elementSize == sizeof(float));
+ SLANG_CHECK(testDesc->sizeInBytes == numberCount * sizeof(float));
+ compareComputeResult(dstDevice, dstBuffer, Slang::makeArray(0.0f, 1.0f, 2.0f, 3.0f));
+
+ // Check that dstBuffer can be successfully used in a compute dispatch using dstDevice.
+ Slang::ComPtr transientHeap;
+ ITransientResourceHeap::Desc transientHeapDesc = {};
+ transientHeapDesc.constantBufferSize = 4096;
+ GFX_CHECK_CALL_ABORT(
+ dstDevice->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef()));
+
+ ComPtr shaderProgram;
+ slang::ProgramLayout* slangReflection;
+ GFX_CHECK_CALL_ABORT(loadComputeProgram(dstDevice, shaderProgram, "compute-trivial", "computeMain", slangReflection));
+
+ ComputePipelineStateDesc pipelineDesc = {};
+ pipelineDesc.program = shaderProgram.get();
+ ComPtr pipelineState;
+ GFX_CHECK_CALL_ABORT(
+ dstDevice->createComputePipelineState(pipelineDesc, pipelineState.writeRef()));
+
+ ComPtr bufferView;
+ IResourceView::Desc viewDesc = {};
+ viewDesc.type = IResourceView::Type::UnorderedAccess;
+ viewDesc.format = Format::Unknown;
+ GFX_CHECK_CALL_ABORT(dstDevice->createBufferView(dstBuffer, viewDesc, bufferView.writeRef()));
+
+ {
+ ICommandQueue::Desc queueDesc = { ICommandQueue::QueueType::Graphics };
+ auto queue = dstDevice->createCommandQueue(queueDesc);
+
+ auto commandBuffer = transientHeap->createCommandBuffer();
+ auto encoder = commandBuffer->encodeComputeCommands();
+
+ auto rootObject = encoder->bindPipeline(pipelineState);
+
+ ShaderCursor rootCursor(rootObject);
+ // Bind buffer view to the entry point.
+ rootCursor.getPath("buffer").setResource(bufferView);
+
+ encoder->dispatchCompute(1, 1, 1);
+ encoder->endEncoding();
+ commandBuffer->close();
+ queue->executeCommandBuffer(commandBuffer);
+ queue->waitOnHost();
+ }
+
+ compareComputeResult(
+ dstDevice,
+ dstBuffer,
+ Slang::makeArray(1.0f, 2.0f, 3.0f, 4.0f));
+ }
+
+ void sharedBufferTestAPI(UnitTestContext* context, Slang::RenderApiFlag::Enum srcApi, Slang::RenderApiFlag::Enum dstApi)
+ {
+ auto srcDevice = createTestingDevice(context, srcApi);
+ auto dstDevice = createTestingDevice(context, dstApi);
+ if (!srcDevice || !dstDevice)
+ {
+ SLANG_IGNORE_TEST;
+ }
+
+ sharedBufferTestImpl(srcDevice, dstDevice, context);
+ }
+#if SLANG_WIN64
+ SLANG_UNIT_TEST(sharedBufferD3D12ToCUDA)
+ {
+ sharedBufferTestAPI(unitTestContext, Slang::RenderApiFlag::D3D12, Slang::RenderApiFlag::CUDA);
+ }
+
+ SLANG_UNIT_TEST(sharedBufferVulkanToCUDA)
+ {
+ sharedBufferTestAPI(unitTestContext, Slang::RenderApiFlag::Vulkan, Slang::RenderApiFlag::CUDA);
+ }
+#endif
+}
diff --git a/tools/gfx-unit-test/shared-textures-tests.cpp b/tools/gfx-unit-test/shared-textures-tests.cpp
new file mode 100644
index 000000000..896d6d6e6
--- /dev/null
+++ b/tools/gfx-unit-test/shared-textures-tests.cpp
@@ -0,0 +1,221 @@
+#include "tools/unit-test/slang-unit-test.h"
+
+#include "slang-gfx.h"
+#include "gfx-test-util.h"
+#include "tools/gfx-util/shader-cursor.h"
+#include "source/core/slang-basic.h"
+
+using namespace gfx;
+
+namespace gfx_test
+{
+ void setUpAndRunShader(
+ IDevice* device,
+ ComPtr tex,
+ ComPtr texView,
+ ComPtr bufferView,
+ const char* entryPoint,
+ ComPtr sampler = nullptr)
+ {
+ Slang::ComPtr transientHeap;
+ ITransientResourceHeap::Desc transientHeapDesc = {};
+ transientHeapDesc.constantBufferSize = 4096;
+ GFX_CHECK_CALL_ABORT(
+ device->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef()));
+
+ ComPtr shaderProgram;
+ slang::ProgramLayout* slangReflection;
+ GFX_CHECK_CALL_ABORT(loadComputeProgram(device, shaderProgram, "trivial-copy", entryPoint, slangReflection));
+
+ ComputePipelineStateDesc pipelineDesc = {};
+ pipelineDesc.program = shaderProgram.get();
+ ComPtr pipelineState;
+ GFX_CHECK_CALL_ABORT(
+ device->createComputePipelineState(pipelineDesc, pipelineState.writeRef()));
+
+ // We have done all the set up work, now it is time to start recording a command buffer for
+ // GPU execution.
+ {
+ ICommandQueue::Desc queueDesc = { ICommandQueue::QueueType::Graphics };
+ auto queue = device->createCommandQueue(queueDesc);
+
+ auto commandBuffer = transientHeap->createCommandBuffer();
+ auto encoder = commandBuffer->encodeComputeCommands();
+
+ auto rootObject = encoder->bindPipeline(pipelineState);
+
+ ShaderCursor entryPointCursor(
+ rootObject->getEntryPoint(0)); // get a cursor the the first entry-point.
+
+ auto& desc = *tex->getDesc();
+ entryPointCursor["width"].setData(desc.size.width);
+ entryPointCursor["height"].setData(desc.size.height);
+
+ // Bind texture view to the entry point
+ entryPointCursor["tex"].setResource(texView);
+
+ if (sampler) entryPointCursor["sampler"].setSampler(sampler);
+
+ // Bind buffer view to the entry point.
+ entryPointCursor["buffer"].setResource(bufferView);
+
+ encoder->dispatchCompute(1, 1, 1);
+ encoder->endEncoding();
+ commandBuffer->close();
+ queue->executeCommandBuffer(commandBuffer);
+ queue->waitOnHost();
+ }
+ }
+
+ ComPtr createTexture(IDevice* device, ITextureResource::Size extents, gfx::Format format, ITextureResource::SubresourceData* initialData)
+ {
+ ITextureResource::Desc texDesc = {};
+ texDesc.type = IResource::Type::Texture2D;
+ texDesc.numMipLevels = 1;
+ texDesc.arraySize = 1;
+ texDesc.size = extents;
+ texDesc.defaultState = ResourceState::UnorderedAccess;
+ texDesc.allowedStates = ResourceStateSet(
+ ResourceState::ShaderResource,
+ ResourceState::UnorderedAccess,
+ ResourceState::CopyDestination,
+ ResourceState::CopySource);
+ texDesc.format = format;
+ texDesc.isShared = true;
+
+ ComPtr inTex;
+ GFX_CHECK_CALL_ABORT(device->createTextureResource(
+ texDesc,
+ initialData,
+ inTex.writeRef()));
+ return inTex;
+ }
+
+ ComPtr createTexView(IDevice* device, ComPtr inTexture)
+ {
+ ComPtr texView;
+ IResourceView::Desc texViewDesc = {};
+ texViewDesc.type = IResourceView::Type::UnorderedAccess;
+ texViewDesc.format = inTexture->getDesc()->format; // TODO: Handle typeless formats - gfxIsTypelessFormat(format) ? convertTypelessFormat(format) : format;
+ GFX_CHECK_CALL_ABORT(device->createTextureView(inTexture, texViewDesc, texView.writeRef()));
+ return texView;
+ }
+
+ template
+ ComPtr createBuffer(IDevice* device, int size, void* initialData)
+ {
+ IBufferResource::Desc bufferDesc = {};
+ bufferDesc.sizeInBytes = size * sizeof(T);
+ bufferDesc.format = gfx::Format::Unknown;
+ bufferDesc.elementSize = sizeof(T);
+ bufferDesc.allowedStates = ResourceStateSet(
+ ResourceState::ShaderResource,
+ ResourceState::UnorderedAccess,
+ ResourceState::CopyDestination,
+ ResourceState::CopySource);
+ bufferDesc.defaultState = ResourceState::UnorderedAccess;
+ bufferDesc.cpuAccessFlags = AccessFlag::Write | AccessFlag::Read;
+
+ ComPtr outBuffer;
+ GFX_CHECK_CALL_ABORT(device->createBufferResource(
+ bufferDesc,
+ initialData,
+ outBuffer.writeRef()));
+ return outBuffer;
+ }
+
+ ComPtr createOutBufferView(IDevice* device, ComPtr outBuffer)
+ {
+ ComPtr bufferView;
+ IResourceView::Desc viewDesc = {};
+ viewDesc.type = IResourceView::Type::UnorderedAccess;
+ viewDesc.format = Format::Unknown;
+ GFX_CHECK_CALL_ABORT(device->createBufferView(outBuffer, viewDesc, bufferView.writeRef()));
+ return bufferView;
+ }
+
+ void sharedTextureTestImpl(IDevice* srcDevice, IDevice* dstDevice, UnitTestContext* context)
+ {
+ ISamplerState::Desc samplerDesc;
+ auto sampler = dstDevice->createSamplerState(samplerDesc);
+
+ float initFloatData[16] = { 0.0f };
+ auto floatResults = createBuffer(dstDevice, 16, initFloatData);
+ auto floatBufferView = createOutBufferView(dstDevice, floatResults);
+
+ uint32_t initUintData[16] = { 0u };
+ auto uintResults = createBuffer(dstDevice, 16, initUintData);
+ auto uintBufferView = createOutBufferView(dstDevice, uintResults);
+
+ int32_t initIntData[16] = { 0 };
+ auto intResults = createBuffer(dstDevice, 16, initIntData);
+ auto intBufferView = createOutBufferView(dstDevice, intResults);
+
+ ITextureResource::Size size = {};
+ size.width = 2;
+ size.height = 2;
+ size.depth = 1;
+
+ ITextureResource::Size bcSize = {};
+ bcSize.width = 4;
+ bcSize.height = 4;
+ bcSize.depth = 1;
+
+ {
+ float texData[] = { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
+ 0.0f, 0.0f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 1.0f };
+ ITextureResource::SubresourceData subData = { (void*)texData, 32, 0 };
+
+ // Create a shareable texture using srcDevice, get its handle, then create a texture using the handle using
+ // dstDevice. Read back the texture and check that its contents are correct.
+ auto srcTexture = createTexture(srcDevice, size, gfx::Format::R32G32B32A32_FLOAT, &subData);
+
+ InteropHandle sharedHandle;
+ GFX_CHECK_CALL_ABORT(srcTexture->getSharedHandle(&sharedHandle));
+ ComPtr dstTexture;
+ size_t sizeInBytes = 0;
+ size_t alignment = 0;
+ GFX_CHECK_CALL_ABORT(srcDevice->getTextureAllocationInfo(*(srcTexture->getDesc()), &sizeInBytes, &alignment));
+ GFX_CHECK_CALL_ABORT(dstDevice->createTextureFromSharedHandle(sharedHandle, *(srcTexture->getDesc()), sizeInBytes, dstTexture.writeRef()));
+ // Reading back the buffer from srcDevice to make sure it's been filled in before reading anything back from dstDevice
+ // TODO: Implement actual synchronization (and not this hacky solution)
+ compareComputeResult(
+ dstDevice,
+ dstTexture,
+ ResourceState::ShaderResource,
+ texData,
+ sizeof(texData));
+
+ auto texView = createTexView(dstDevice, dstTexture);
+ setUpAndRunShader(dstDevice, dstTexture, texView, floatBufferView, "copyTexFloat4");
+ compareComputeResult(
+ dstDevice,
+ floatResults,
+ Slang::makeArray(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
+ 0.0f, 0.0f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 1.0f));
+ }
+ }
+
+ void sharedTextureTestAPI(UnitTestContext* context, Slang::RenderApiFlag::Enum srcApi, Slang::RenderApiFlag::Enum dstApi)
+ {
+ auto srcDevice = createTestingDevice(context, srcApi);
+ auto dstDevice = createTestingDevice(context, dstApi);
+ if (!srcDevice || !dstDevice)
+ {
+ SLANG_IGNORE_TEST;
+ }
+
+ sharedTextureTestImpl(srcDevice, dstDevice, context);
+ }
+#if SLANG_WIN64
+ SLANG_UNIT_TEST(sharedTextureD3D12ToCUDA)
+ {
+ sharedTextureTestAPI(unitTestContext, Slang::RenderApiFlag::D3D12, Slang::RenderApiFlag::CUDA);
+ }
+
+ SLANG_UNIT_TEST(sharedTextureVulkanToCUDA)
+ {
+ sharedTextureTestAPI(unitTestContext, Slang::RenderApiFlag::Vulkan, Slang::RenderApiFlag::CUDA);
+ }
+#endif
+}
diff --git a/tools/gfx-unit-test/trivial-copy.slang b/tools/gfx-unit-test/trivial-copy.slang
new file mode 100644
index 000000000..acf091ffa
--- /dev/null
+++ b/tools/gfx-unit-test/trivial-copy.slang
@@ -0,0 +1,18 @@
+// trivial-copy.slang
+
+// Copy the contents of "tex" into "buffer". These are for textures containing FLOAT data.
+[shader("compute")]
+[numthreads(4,1,1)]
+void copyTexFloat4(
+ uint3 sv_dispatchThreadID : SV_DispatchThreadID,
+ uniform uint width,
+ uniform uint height,
+ uniform RWTexture2D tex,
+ uniform RWStructuredBuffer buffer)
+{
+ float4 result = tex[uint2(sv_dispatchThreadID.x % width, sv_dispatchThreadID.x / width)];
+ buffer[sv_dispatchThreadID.x * 4] = result.r;
+ buffer[sv_dispatchThreadID.x * 4 + 1] = result.g;
+ buffer[sv_dispatchThreadID.x * 4 + 2] = result.b;
+ buffer[sv_dispatchThreadID.x * 4 + 3] = result.a;
+}
diff --git a/tools/gfx-util/shader-cursor.h b/tools/gfx-util/shader-cursor.h
index 41d0b3945..4c281c99a 100644
--- a/tools/gfx-util/shader-cursor.h
+++ b/tools/gfx-util/shader-cursor.h
@@ -87,6 +87,12 @@ struct ShaderCursor
return m_baseObject->setData(m_offset, data, size);
}
+ template
+ SlangResult setData(T const& data) const
+ {
+ return setData(&data, sizeof(data));
+ }
+
SlangResult setObject(IShaderObject* object) const
{
return m_baseObject->setObject(m_offset, object);
diff --git a/tools/gfx/cuda/render-cuda.cpp b/tools/gfx/cuda/render-cuda.cpp
index 7abf51ef3..b97fc819c 100644
--- a/tools/gfx/cuda/render-cuda.cpp
+++ b/tools/gfx/cuda/render-cuda.cpp
@@ -242,6 +242,8 @@ public:
CUarray m_cudaArray = CUarray();
CUmipmappedArray m_cudaMipMappedArray = CUmipmappedArray();
+ void* m_cudaExternalMemory = nullptr;
+
RefPtr m_cudaContext;
virtual SLANG_NO_THROW Result SLANG_MCALL getNativeResourceHandle(InteropHandle* outHandle) override
@@ -1439,6 +1441,62 @@ public:
return SLANG_OK;
}
+ Result getCUDAFormat(Format format, CUarray_format* outFormat)
+ {
+ // TODO: Expand to cover all available formats that can be supported in CUDA
+ switch (format)
+ {
+ case Format::R32G32B32A32_FLOAT:
+ case Format::R32G32B32_FLOAT:
+ case Format::R32G32_FLOAT:
+ case Format::R32_FLOAT:
+ case Format::D32_FLOAT:
+ *outFormat = CU_AD_FORMAT_FLOAT;
+ return SLANG_OK;
+ case Format::R16G16B16A16_FLOAT:
+ case Format::R16G16_FLOAT:
+ case Format::R16_FLOAT:
+ *outFormat = CU_AD_FORMAT_HALF;
+ return SLANG_OK;
+ case Format::R32G32B32A32_UINT:
+ case Format::R32G32B32_UINT:
+ case Format::R32G32_UINT:
+ case Format::R32_UINT:
+ *outFormat = CU_AD_FORMAT_UNSIGNED_INT32;
+ return SLANG_OK;
+ case Format::R16G16B16A16_UINT:
+ case Format::R16G16_UINT:
+ case Format::R16_UINT:
+ *outFormat = CU_AD_FORMAT_UNSIGNED_INT16;
+ return SLANG_OK;
+ case Format::R8G8B8A8_UINT:
+ case Format::R8G8_UINT:
+ case Format::R8_UINT:
+ case Format::R8G8B8A8_UNORM:
+ *outFormat = CU_AD_FORMAT_UNSIGNED_INT8;
+ return SLANG_OK;
+ case Format::R32G32B32A32_SINT:
+ case Format::R32G32B32_SINT:
+ case Format::R32G32_SINT:
+ case Format::R32_SINT:
+ *outFormat = CU_AD_FORMAT_SIGNED_INT32;
+ return SLANG_OK;
+ case Format::R16G16B16A16_SINT:
+ case Format::R16G16_SINT:
+ case Format::R16_SINT:
+ *outFormat = CU_AD_FORMAT_SIGNED_INT16;
+ return SLANG_OK;
+ case Format::R8G8B8A8_SINT:
+ case Format::R8G8_SINT:
+ case Format::R8_SINT:
+ *outFormat = CU_AD_FORMAT_SIGNED_INT8;
+ return SLANG_OK;
+ default:
+ SLANG_ASSERT(!"Only support R32_FLOAT/R8G8B8A8_UNORM formats for now");
+ return SLANG_FAIL;
+ }
+ }
+
virtual SLANG_NO_THROW Result SLANG_MCALL createTextureResource(
const ITextureResource::Desc& desc,
const ITextureResource::SubresourceData* initData,
@@ -1489,45 +1547,34 @@ public:
{
CUarray_format format = CU_AD_FORMAT_FLOAT;
int numChannels = 0;
-
- switch (desc.format)
+
+ SLANG_RETURN_ON_FAIL(getCUDAFormat(desc.format, &format));
+ FormatInfo info;
+ gfxGetFormatInfo(desc.format, &info);
+ numChannels = info.channelCount;
+
+ switch (format)
{
- case Format::R32G32B32A32_FLOAT:
- case Format::R32G32B32_FLOAT:
- case Format::R32G32_FLOAT:
- case Format::R32_FLOAT:
- case Format::D32_FLOAT:
- {
- FormatInfo info;
- gfxGetFormatInfo(desc.format, &info);
- format = CU_AD_FORMAT_FLOAT;
- numChannels = info.channelCount;
- elementSize = sizeof(float) * numChannels;
- break;
- }
- case Format::R16G16B16A16_FLOAT:
- case Format::R16G16_FLOAT:
- case Format::R16_FLOAT:
- {
- FormatInfo info;
- gfxGetFormatInfo(desc.format, &info);
- format = CU_AD_FORMAT_HALF;
- numChannels = info.channelCount;
- elementSize = sizeof(uint16_t) * numChannels;
- break;
- }
- case Format::R8G8B8A8_UNORM:
- {
- format = CU_AD_FORMAT_UNSIGNED_INT8;
- numChannels = 4;
- elementSize = sizeof(uint32_t);
- break;
- }
+ case CU_AD_FORMAT_FLOAT:
+ {
+ elementSize = sizeof(float) * numChannels;
+ break;
+ }
+ case CU_AD_FORMAT_HALF:
+ {
+ elementSize = sizeof(uint16_t) * numChannels;
+ break;
+ }
+ case CU_AD_FORMAT_UNSIGNED_INT8:
+ {
+ elementSize = sizeof(uint32_t) * numChannels;
+ break;
+ }
default:
- {
- SLANG_ASSERT(!"Only support R32_FLOAT/R8G8B8A8_UNORM formats for now");
- return SLANG_FAIL;
- }
+ {
+ SLANG_ASSERT(!"Only support R32_FLOAT/R8G8B8A8_UNORM formats for now");
+ return SLANG_FAIL;
+ }
}
if (desc.numMipLevels > 1)
@@ -1951,6 +1998,83 @@ public:
return SLANG_OK;
}
+ virtual SLANG_NO_THROW Result SLANG_MCALL createTextureFromSharedHandle(
+ InteropHandle handle,
+ const ITextureResource::Desc& desc,
+ const size_t size,
+ ITextureResource** outResource) override
+ {
+ if (handle.handleValue == 0)
+ {
+ *outResource = nullptr;
+ return SLANG_OK;
+ }
+
+ RefPtr resource = new TextureCUDAResource(desc);
+ resource->m_cudaContext = m_context;
+
+ // CUDA manages sharing of buffers through the idea of an
+ // "external memory" object, which represents the relationship
+ // with another API's objects. In order to create this external
+ // memory association, we first need to fill in a descriptor struct.
+ CUDA_EXTERNAL_MEMORY_HANDLE_DESC externalMemoryHandleDesc;
+ memset(&externalMemoryHandleDesc, 0, sizeof(externalMemoryHandleDesc));
+ switch (handle.api)
+ {
+ case InteropHandleAPI::D3D12:
+ externalMemoryHandleDesc.type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE;
+ break;
+ case InteropHandleAPI::Vulkan:
+ externalMemoryHandleDesc.type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32;
+ break;
+ default:
+ return SLANG_FAIL;
+ }
+ externalMemoryHandleDesc.handle.win32.handle = (void*)handle.handleValue;
+ externalMemoryHandleDesc.size = size;
+ externalMemoryHandleDesc.flags = cudaExternalMemoryDedicated;
+
+ CUexternalMemory externalMemory;
+ SLANG_CUDA_RETURN_ON_FAIL(cuImportExternalMemory(&externalMemory, &externalMemoryHandleDesc));
+ resource->m_cudaExternalMemory = externalMemory;
+
+ FormatInfo formatInfo;
+ SLANG_RETURN_ON_FAIL(gfxGetFormatInfo(desc.format, &formatInfo));
+ CUDA_ARRAY3D_DESCRIPTOR arrayDesc;
+ arrayDesc.Depth = desc.size.depth;
+ arrayDesc.Height = desc.size.height;
+ arrayDesc.Width = desc.size.width;
+ arrayDesc.NumChannels = formatInfo.channelCount;
+ getCUDAFormat(desc.format, &arrayDesc.Format);
+ arrayDesc.Flags = 0; // TODO: Flags? CUDA_ARRAY_LAYERED/SURFACE_LDST/CUBEMAP/TEXTURE_GATHER
+
+ CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC externalMemoryMipDesc;
+ memset(&externalMemoryMipDesc, 0, sizeof(externalMemoryMipDesc));
+ externalMemoryMipDesc.offset = 0;
+ externalMemoryMipDesc.arrayDesc = arrayDesc;
+ externalMemoryMipDesc.numLevels = desc.numMipLevels;
+
+ CUmipmappedArray mipArray;
+ SLANG_CUDA_RETURN_ON_FAIL(cuExternalMemoryGetMappedMipmappedArray(&mipArray, externalMemory, &externalMemoryMipDesc));
+ resource->m_cudaMipMappedArray = mipArray;
+
+ CUarray cuArray;
+ SLANG_CUDA_RETURN_ON_FAIL(cuMipmappedArrayGetLevel(&cuArray, mipArray, 0));
+ resource->m_cudaArray = cuArray;
+
+ CUDA_RESOURCE_DESC surfDesc;
+ memset(&surfDesc, 0, sizeof(surfDesc));
+ surfDesc.resType = CU_RESOURCE_TYPE_ARRAY;
+ surfDesc.res.array.hArray = cuArray;
+
+ CUsurfObject surface;
+ SLANG_CUDA_RETURN_ON_FAIL(cuSurfObjectCreate(&surface, &surfDesc));
+ resource->m_cudaSurfObj = surface;
+
+ returnComPtr(outResource, resource);
+ return SLANG_OK;
+ }
+
virtual SLANG_NO_THROW Result SLANG_MCALL createTextureView(
ITextureResource* texture, IResourceView::Desc const& desc, IResourceView** outView) override
{
@@ -2183,12 +2307,36 @@ public:
size_t* outRowPitch,
size_t* outPixelSize) override
{
- SLANG_UNUSED(texture);
- SLANG_UNUSED(outBlob);
- SLANG_UNUSED(outRowPitch);
- SLANG_UNUSED(outPixelSize);
+ auto textureImpl = static_cast(texture);
+ RefPtr blob = new ListBlob();
- return SLANG_E_NOT_AVAILABLE;
+ auto desc = textureImpl->getDesc();
+ auto width = desc->size.width;
+ auto height = desc->size.height;
+ FormatInfo sizeInfo;
+ SLANG_RETURN_ON_FAIL(gfxGetFormatInfo(desc->format, &sizeInfo));
+ size_t pixelSize = sizeInfo.blockSizeInBytes / sizeInfo.pixelsPerBlock;
+ size_t rowPitch = width * pixelSize;
+ size_t size = height * rowPitch;
+ blob->m_data.setCount((Index)size);
+
+ CUDA_MEMCPY2D copyParam;
+ memset(©Param, 0, sizeof(copyParam));
+
+ copyParam.srcMemoryType = CU_MEMORYTYPE_ARRAY;
+ copyParam.srcArray = textureImpl->m_cudaArray;
+
+ copyParam.dstMemoryType = CU_MEMORYTYPE_HOST;
+ copyParam.dstHost = blob->m_data.getBuffer();
+ copyParam.dstPitch = rowPitch;
+ copyParam.WidthInBytes = copyParam.dstPitch;
+ copyParam.Height = height;
+ SLANG_CUDA_RETURN_ON_FAIL(cuMemcpy2D(©Param));
+
+ *outRowPitch = rowPitch;
+ *outPixelSize = pixelSize;
+ returnComPtr(outBlob, blob);
+ return SLANG_OK;
}
virtual SLANG_NO_THROW Result SLANG_MCALL readBufferResource(
diff --git a/tools/gfx/d3d12/render-d3d12.cpp b/tools/gfx/d3d12/render-d3d12.cpp
index 371d22002..ebc894291 100644
--- a/tools/gfx/d3d12/render-d3d12.cpp
+++ b/tools/gfx/d3d12/render-d3d12.cpp
@@ -314,11 +314,19 @@ public:
virtual SLANG_NO_THROW Result SLANG_MCALL getSharedHandle(InteropHandle* outHandle) override
{
+ // Check if a shared handle already exists for this resource.
+ if (sharedHandle.handleValue != 0)
+ {
+ *outHandle = sharedHandle;
+ return SLANG_OK;
+ }
+
+ // If a shared handle doesn't exist, create one and store it.
ComPtr pDevice;
auto pResource = m_resource.getResource();
pResource->GetDevice(IID_PPV_ARGS(pDevice.writeRef()));
SLANG_RETURN_ON_FAIL(pDevice->CreateSharedHandle(pResource, NULL, GENERIC_ALL, nullptr, (HANDLE*)&outHandle->handleValue));
- outHandle->api = InteropHandleAPI::Win32;
+ outHandle->api = InteropHandleAPI::D3D12;
return SLANG_OK;
}
};
@@ -5031,7 +5039,7 @@ Result D3D12Device::getTextureAllocationInfo(
TextureResource::Desc srcDesc = fixupTextureDesc(desc);
D3D12_RESOURCE_DESC resourceDesc = {};
setupResourceDesc(resourceDesc, srcDesc);
- auto allocInfo = m_device->GetResourceAllocationInfo(0xFF, 1, &resourceDesc);
+ auto allocInfo = m_device->GetResourceAllocationInfo(0, 1, &resourceDesc);
*outSize = (size_t)allocInfo.SizeInBytes;
*outAlignment = (size_t)allocInfo.Alignment;
return SLANG_OK;
@@ -5061,6 +5069,9 @@ Result D3D12Device::createTextureResource(const ITextureResource::Desc& descIn,
heapProps.CreationNodeMask = 1;
heapProps.VisibleNodeMask = 1;
+ D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE;
+ if (descIn.isShared) flags |= D3D12_HEAP_FLAG_SHARED;
+
D3D12_CLEAR_VALUE clearValue;
D3D12_CLEAR_VALUE* clearValuePtr = &clearValue;
if ((resourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET |
@@ -5075,7 +5086,7 @@ Result D3D12Device::createTextureResource(const ITextureResource::Desc& descIn,
SLANG_RETURN_ON_FAIL(texture->m_resource.initCommitted(
m_device,
heapProps,
- D3D12_HEAP_FLAG_NONE,
+ flags,
resourceDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
clearValuePtr));
diff --git a/tools/gfx/debug-layer.cpp b/tools/gfx/debug-layer.cpp
index 470b63599..60e726243 100644
--- a/tools/gfx/debug-layer.cpp
+++ b/tools/gfx/debug-layer.cpp
@@ -370,6 +370,22 @@ Result DebugDevice::createTextureFromNativeHandle(
return result;
}
+Result DebugDevice::createTextureFromSharedHandle(
+ InteropHandle handle,
+ const ITextureResource::Desc& srcDesc,
+ const size_t size,
+ ITextureResource** outResource)
+{
+ SLANG_GFX_API_FUNC;
+
+ RefPtr outObject = new DebugTextureResource();
+ auto result = baseObject->createTextureFromSharedHandle(handle, srcDesc, size, outObject->baseObject.writeRef());
+ if (SLANG_FAILED(result))
+ return result;
+ returnComPtr(outResource, outObject);
+ return result;
+}
+
Result DebugDevice::createBufferResource(
const IBufferResource::Desc& desc,
const void* initData,
diff --git a/tools/gfx/debug-layer.h b/tools/gfx/debug-layer.h
index e0a97ee1c..71cf74260 100644
--- a/tools/gfx/debug-layer.h
+++ b/tools/gfx/debug-layer.h
@@ -60,6 +60,11 @@ public:
InteropHandle handle,
const ITextureResource::Desc& srcDesc,
ITextureResource** outResource) override;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createTextureFromSharedHandle(
+ InteropHandle handle,
+ const ITextureResource::Desc& srcDesc,
+ const size_t size,
+ ITextureResource** outResource) override;
virtual SLANG_NO_THROW Result SLANG_MCALL createBufferResource(
const IBufferResource::Desc& desc,
const void* initData,
diff --git a/tools/gfx/renderer-shared.cpp b/tools/gfx/renderer-shared.cpp
index 101e02d47..df6ebd37e 100644
--- a/tools/gfx/renderer-shared.cpp
+++ b/tools/gfx/renderer-shared.cpp
@@ -365,6 +365,19 @@ SLANG_NO_THROW Result SLANG_MCALL RendererBase::createTextureFromNativeHandle(
return SLANG_E_NOT_AVAILABLE;
}
+SLANG_NO_THROW Result SLANG_MCALL RendererBase::createTextureFromSharedHandle(
+ InteropHandle handle,
+ const ITextureResource::Desc& srcDesc,
+ const size_t size,
+ ITextureResource** outResource)
+{
+ SLANG_UNUSED(handle);
+ SLANG_UNUSED(srcDesc);
+ SLANG_UNUSED(size);
+ SLANG_UNUSED(outResource);
+ return SLANG_E_NOT_AVAILABLE;
+}
+
SLANG_NO_THROW Result SLANG_MCALL RendererBase::createBufferFromNativeHandle(
InteropHandle handle,
const IBufferResource::Desc& srcDesc,
@@ -381,6 +394,7 @@ SLANG_NO_THROW Result SLANG_MCALL RendererBase::createBufferFromSharedHandle(
const IBufferResource::Desc& srcDesc,
IBufferResource** outResource)
{
+ SLANG_UNUSED(handle);
SLANG_UNUSED(srcDesc);
SLANG_UNUSED(outResource);
return SLANG_E_NOT_AVAILABLE;
diff --git a/tools/gfx/renderer-shared.h b/tools/gfx/renderer-shared.h
index e7ea1b7f5..7859f1ad9 100644
--- a/tools/gfx/renderer-shared.h
+++ b/tools/gfx/renderer-shared.h
@@ -1206,6 +1206,12 @@ public:
const ITextureResource::Desc& srcDesc,
ITextureResource** outResource) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW Result SLANG_MCALL createTextureFromSharedHandle(
+ InteropHandle handle,
+ const ITextureResource::Desc& srcDesc,
+ const size_t size,
+ ITextureResource** outResource) SLANG_OVERRIDE;
+
virtual SLANG_NO_THROW Result SLANG_MCALL createBufferFromNativeHandle(
InteropHandle handle,
const IBufferResource::Desc& srcDesc,
diff --git a/tools/gfx/vulkan/render-vk.cpp b/tools/gfx/vulkan/render-vk.cpp
index 25609ec6e..b55c78ea0 100644
--- a/tools/gfx/vulkan/render-vk.cpp
+++ b/tools/gfx/vulkan/render-vk.cpp
@@ -381,6 +381,12 @@ public:
vkAPI.vkFreeMemory(vkAPI.m_device, m_imageMemory, nullptr);
vkAPI.vkDestroyImage(vkAPI.m_device, m_image, nullptr);
}
+ if (sharedHandle.handleValue != 0)
+ {
+#if SLANG_WINDOWS_FAMILY
+ CloseHandle((HANDLE)sharedHandle.handleValue);
+#endif
+ }
}
VkImage m_image = VK_NULL_HANDLE;
@@ -398,8 +404,31 @@ public:
virtual SLANG_NO_THROW Result SLANG_MCALL getSharedHandle(InteropHandle* outHandle) override
{
+ // Check if a shared handle already exists for this resource.
+ if (sharedHandle.handleValue != 0)
+ {
+ *outHandle = sharedHandle;
+ return SLANG_OK;
+ }
+
+ // If a shared handle doesn't exist, create one and store it.
+#if SLANG_WINDOWS_FAMILY
+ VkMemoryGetWin32HandleInfoKHR info = {};
+ info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR;
+ info.pNext = nullptr;
+ info.memory = m_imageMemory;
+ info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
+
+ auto& api = m_device->m_api;
+ PFN_vkGetMemoryWin32HandleKHR vkCreateSharedHandle;
+ vkCreateSharedHandle = api.vkGetMemoryWin32HandleKHR;
+ if (!vkCreateSharedHandle)
+ {
+ return SLANG_FAIL;
+ }
+ SLANG_RETURN_ON_FAIL(vkCreateSharedHandle(m_device->m_device, &info, (HANDLE*)&outHandle->handleValue) != VK_SUCCESS);
+#endif
outHandle->api = InteropHandleAPI::Vulkan;
- outHandle->handleValue = 0;
return SLANG_OK;
}
};
@@ -5712,7 +5741,7 @@ Result VKDevice::Buffer::init(
exportMemoryAllocateInfo.pNext =
extMemHandleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR
? &exportMemoryWin32HandleInfo
- : NULL;
+ : nullptr;
exportMemoryAllocateInfo.handleTypes = extMemHandleType;
allocateInfo.pNext = &exportMemoryAllocateInfo;
}
@@ -6921,78 +6950,99 @@ Result VKDevice::createTextureResource(const ITextureResource::Desc& descIn, con
RefPtr texture(new TextureResourceImpl(desc, this));
texture->m_vkformat = format;
// Create the image
+
+ VkImageCreateInfo imageInfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
+ switch (desc.type)
{
- VkImageCreateInfo imageInfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
- switch (desc.type)
+ case IResource::Type::Texture1D:
{
- case IResource::Type::Texture1D:
- {
- imageInfo.imageType = VK_IMAGE_TYPE_1D;
- imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), 1, 1 };
- break;
- }
- case IResource::Type::Texture2D:
- {
- imageInfo.imageType = VK_IMAGE_TYPE_2D;
- imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), 1 };
- break;
- }
- case IResource::Type::TextureCube:
- {
- imageInfo.imageType = VK_IMAGE_TYPE_2D;
- imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), 1 };
- imageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
- break;
- }
- case IResource::Type::Texture3D:
- {
- // Can't have an array and 3d texture
- assert(desc.arraySize <= 1);
+ imageInfo.imageType = VK_IMAGE_TYPE_1D;
+ imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), 1, 1 };
+ break;
+ }
+ case IResource::Type::Texture2D:
+ {
+ imageInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), 1 };
+ break;
+ }
+ case IResource::Type::TextureCube:
+ {
+ imageInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), 1 };
+ imageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
+ break;
+ }
+ case IResource::Type::Texture3D:
+ {
+ // Can't have an array and 3d texture
+ assert(desc.arraySize <= 1);
- imageInfo.imageType = VK_IMAGE_TYPE_3D;
- imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), uint32_t(descIn.size.depth) };
- break;
- }
- default:
- {
- assert(!"Unhandled type");
- return SLANG_FAIL;
- }
+ imageInfo.imageType = VK_IMAGE_TYPE_3D;
+ imageInfo.extent = VkExtent3D{ uint32_t(descIn.size.width), uint32_t(descIn.size.height), uint32_t(descIn.size.depth) };
+ break;
+ }
+ default:
+ {
+ assert(!"Unhandled type");
+ return SLANG_FAIL;
}
+ }
- imageInfo.mipLevels = desc.numMipLevels;
- imageInfo.arrayLayers = arraySize;
+ imageInfo.mipLevels = desc.numMipLevels;
+ imageInfo.arrayLayers = arraySize;
- imageInfo.format = format;
+ imageInfo.format = format;
- imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
- imageInfo.usage = _calcImageUsageFlags(desc.allowedStates, desc.cpuAccessFlags, initData);
- imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+ imageInfo.usage = _calcImageUsageFlags(desc.allowedStates, desc.cpuAccessFlags, initData);
+ imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
- imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
+ imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
- SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateImage(m_device, &imageInfo, nullptr, &texture->m_image));
+ VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo = { VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO };
+#if SLANG_WINDOWS_FAMILY
+ VkExternalMemoryHandleTypeFlags extMemoryHandleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
+ if (descIn.isShared)
+ {
+ externalMemoryImageCreateInfo.pNext = nullptr;
+ externalMemoryImageCreateInfo.handleTypes = extMemoryHandleType;
+ imageInfo.pNext = &externalMemoryImageCreateInfo;
}
+#endif
+ SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateImage(m_device, &imageInfo, nullptr, &texture->m_image));
VkMemoryRequirements memRequirements;
m_api.vkGetImageMemoryRequirements(m_device, texture->m_image, &memRequirements);
// Allocate the memory
- {
- VkMemoryPropertyFlags reqMemoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
-
- VkMemoryAllocateInfo allocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
-
- int memoryTypeIndex = m_api.findMemoryTypeIndex(memRequirements.memoryTypeBits, reqMemoryProperties);
- assert(memoryTypeIndex >= 0);
-
- VkMemoryPropertyFlags actualMemoryProperites = m_api.m_deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags;
+ VkMemoryPropertyFlags reqMemoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+ int memoryTypeIndex = m_api.findMemoryTypeIndex(memRequirements.memoryTypeBits, reqMemoryProperties);
+ assert(memoryTypeIndex >= 0);
- allocInfo.allocationSize = memRequirements.size;
- allocInfo.memoryTypeIndex = memoryTypeIndex;
+ VkMemoryPropertyFlags actualMemoryProperites = m_api.m_deviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags;
+ VkMemoryAllocateInfo allocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
+ allocInfo.allocationSize = memRequirements.size;
+ allocInfo.memoryTypeIndex = memoryTypeIndex;
+#if SLANG_WINDOWS_FAMILY
+ VkExportMemoryWin32HandleInfoKHR exportMemoryWin32HandleInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR };
+ VkExportMemoryAllocateInfoKHR exportMemoryAllocateInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };
+ if (descIn.isShared)
+ {
+ exportMemoryWin32HandleInfo.pNext = nullptr;
+ exportMemoryWin32HandleInfo.pAttributes = nullptr;
+ exportMemoryWin32HandleInfo.dwAccess = DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE;
+ exportMemoryWin32HandleInfo.name = NULL;
- SLANG_VK_RETURN_ON_FAIL(m_api.vkAllocateMemory(m_device, &allocInfo, nullptr, &texture->m_imageMemory));
+ exportMemoryAllocateInfo.pNext =
+ extMemoryHandleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR
+ ? &exportMemoryWin32HandleInfo
+ : nullptr;
+ exportMemoryAllocateInfo.handleTypes = extMemoryHandleType;
+ allocInfo.pNext = &exportMemoryAllocateInfo;
}
+#endif
+ SLANG_VK_RETURN_ON_FAIL(m_api.vkAllocateMemory(m_device, &allocInfo, nullptr, &texture->m_imageMemory));
// Bind the memory to the image
m_api.vkBindImageMemory(m_device, texture->m_image, texture->m_imageMemory, 0);
--
cgit v1.2.3