yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

jarcherNVUpdate cuda context creation to support cuda 13 (#8181)3639e71df

master
40.6 KiB1223 linesraw
1// cuda-device.cpp
2#include "cuda-device.h"
3
4#include "cuda-buffer.h"
5#include "cuda-command-queue.h"
6#include "cuda-pipeline-state.h"
7#include "cuda-query.h"
8#include "cuda-resource-views.h"
9#include "cuda-shader-object-layout.h"
10#include "cuda-shader-object.h"
11#include "cuda-shader-program.h"
12#include "cuda-texture.h"
13
14namespace gfx
15{
16#ifdef GFX_ENABLE_CUDA
17using namespace Slang;
18
19namespace cuda
20{
21
22int DeviceImpl::_calcSMCountPerMultiProcessor(int major, int minor)
23{
24    // Defines for GPU Architecture types (using the SM version to determine
25    // the # of cores per SM
26    struct SMInfo
27    {
28        int sm; // 0xMm (hexadecimal notation), M = SM Major version, and m = SM minor version
29        int coreCount;
30    };
31
32    static const SMInfo infos[] = {
33        {0x30, 192},
34        {0x32, 192},
35        {0x35, 192},
36        {0x37, 192},
37        {0x50, 128},
38        {0x52, 128},
39        {0x53, 128},
40        {0x60, 64},
41        {0x61, 128},
42        {0x62, 128},
43        {0x70, 64},
44        {0x72, 64},
45        {0x75, 64}};
46
47    const int sm = ((major << 4) + minor);
48    for (Index i = 0; i < SLANG_COUNT_OF(infos); ++i)
49    {
50        if (infos[i].sm == sm)
51        {
52            return infos[i].coreCount;
53        }
54    }
55
56    const auto& last = infos[SLANG_COUNT_OF(infos) - 1];
57
58    // It must be newer presumably
59    SLANG_ASSERT(sm > last.sm);
60
61    // Default to the last entry
62    return last.coreCount;
63}
64
65SlangResult DeviceImpl::_findMaxFlopsDeviceIndex(int* outDeviceIndex)
66{
67    int smPerMultiproc = 0;
68    int maxPerfDevice = -1;
69    int deviceCount = 0;
70    int devicesProhibited = 0;
71
72    uint64_t maxComputePerf = 0;
73    SLANG_CUDA_RETURN_ON_FAIL(cuDeviceGetCount(&deviceCount));
74
75    // Find the best CUDA capable GPU device
76    for (int currentDevice = 0; currentDevice < deviceCount; ++currentDevice)
77    {
78        CUdevice device;
79        SLANG_CUDA_RETURN_ON_FAIL(cuDeviceGet(&device, currentDevice));
80        int computeMode = -1, major = 0, minor = 0;
81        SLANG_CUDA_RETURN_ON_FAIL(
82            cuDeviceGetAttribute(&computeMode, CU_DEVICE_ATTRIBUTE_COMPUTE_MODE, device));
83        SLANG_CUDA_RETURN_ON_FAIL(
84            cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, device));
85        SLANG_CUDA_RETURN_ON_FAIL(
86            cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, device));
87
88        // If this GPU is not running on Compute Mode prohibited,
89        // then we can add it to the list
90        if (computeMode != CU_COMPUTEMODE_PROHIBITED)
91        {
92            if (major == 9999 && minor == 9999)
93            {
94                smPerMultiproc = 1;
95            }
96            else
97            {
98                smPerMultiproc = _calcSMCountPerMultiProcessor(major, minor);
99            }
100
101            int multiProcessorCount = 0, clockRate = 0;
102            SLANG_CUDA_RETURN_ON_FAIL(cuDeviceGetAttribute(
103                &multiProcessorCount,
104                CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT,
105                device));
106            SLANG_CUDA_RETURN_ON_FAIL(
107                cuDeviceGetAttribute(&clockRate, CU_DEVICE_ATTRIBUTE_CLOCK_RATE, device));
108            uint64_t compute_perf = uint64_t(multiProcessorCount) * smPerMultiproc * clockRate;
109
110            if (compute_perf > maxComputePerf)
111            {
112                maxComputePerf = compute_perf;
113                maxPerfDevice = currentDevice;
114            }
115        }
116        else
117        {
118            devicesProhibited++;
119        }
120    }
121
122    if (maxPerfDevice < 0)
123    {
124        return SLANG_FAIL;
125    }
126
127    *outDeviceIndex = maxPerfDevice;
128    return SLANG_OK;
129}
130
131SlangResult DeviceImpl::_initCuda(CUDAReportStyle reportType)
132{
133    static CUresult res = cuInit(0);
134    SLANG_CUDA_RETURN_WITH_REPORT_ON_FAIL(res, reportType);
135    return SLANG_OK;
136}
137
138SLANG_NO_THROW Result SLANG_MCALL DeviceImpl::getNativeDeviceHandles(InteropHandles* outHandles)
139{
140    outHandles->handles[0].handleValue = (uint64_t)m_device;
141    outHandles->handles[0].api = InteropHandleAPI::CUDA;
142    return SLANG_OK;
143}
144
145SLANG_NO_THROW SlangResult SLANG_MCALL DeviceImpl::initialize(const Desc& desc)
146{
147    SLANG_RETURN_ON_FAIL(slangContext.initialize(
148        desc.slang,
149        desc.extendedDescCount,
150        desc.extendedDescs,
151        SLANG_PTX,
152        "cuda_sm_5_0",
153        makeArray(slang::PreprocessorMacroDesc{"__CUDA_COMPUTE__", "1"}).getView()));
154
155    SLANG_RETURN_ON_FAIL(RendererBase::initialize(desc));
156
157    SLANG_RETURN_ON_FAIL(_initCuda(reportType));
158
159    if (desc.adapterLUID)
160    {
161        int deviceCount = -1;
162        cuDeviceGetCount(&deviceCount);
163        for (int deviceIndex = 0; deviceIndex < deviceCount; ++deviceIndex)
164        {
165            if (cuda::getAdapterLUID(deviceIndex) == *desc.adapterLUID)
166            {
167                m_deviceIndex = deviceIndex;
168                break;
169            }
170        }
171        if (m_deviceIndex >= deviceCount)
172            return SLANG_E_INVALID_ARG;
173    }
174    else
175    {
176        SLANG_RETURN_ON_FAIL(_findMaxFlopsDeviceIndex(&m_deviceIndex));
177    }
178
179    m_context = new CUDAContext();
180
181    SLANG_CUDA_RETURN_ON_FAIL(cuDeviceGet(&m_device, m_deviceIndex));
182
183    // Use version-aware context creation that works with both CUDA 12 and CUDA 13
184    SLANG_CUDA_RETURN_WITH_REPORT_ON_FAIL(
185        createCudaContext(&m_context->m_context, 0, m_device),
186        reportType);
187
188    {
189        // Not clear how to detect half support on CUDA. For now we'll assume we have it
190        m_features.add("half");
191
192        // CUDA has support for realtime clock
193        m_features.add("realtime-clock");
194
195        // Allows use of a ptr like type
196        m_features.add("has-ptr");
197    }
198
199    // Initialize DeviceInfo
200    {
201        m_info.deviceType = DeviceType::CUDA;
202        m_info.bindingStyle = BindingStyle::CUDA;
203        m_info.projectionStyle = ProjectionStyle::DirectX;
204        m_info.apiName = "CUDA";
205        static const float kIdentity[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
206        ::memcpy(m_info.identityProjectionMatrix, kIdentity, sizeof(kIdentity));
207        char deviceName[256];
208        cuDeviceGetName(deviceName, sizeof(deviceName), m_device);
209        m_adapterName = deviceName;
210        m_info.adapterName = m_adapterName.begin();
211        m_info.timestampFrequency = 1000000;
212    }
213
214    // Get device limits.
215    {
216        CUresult lastResult = CUDA_SUCCESS;
217        auto getAttribute = [&](CUdevice_attribute attribute) -> int
218        {
219            int value;
220            CUresult result = cuDeviceGetAttribute(&value, attribute, m_device);
221            if (result != CUDA_SUCCESS)
222                lastResult = result;
223            return value;
224        };
225
226        DeviceLimits limits = {};
227
228        limits.maxTextureDimension1D = getAttribute(CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE1D_WIDTH);
229        limits.maxTextureDimension2D = Math::Min(
230            getAttribute(CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE2D_WIDTH),
231            getAttribute(CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE2D_HEIGHT));
232        limits.maxTextureDimension3D = Math::Min(
233            getAttribute(CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE3D_WIDTH),
234            Math::Min(
235                getAttribute(CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE3D_HEIGHT),
236                getAttribute(CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE3D_DEPTH)));
237        limits.maxTextureDimensionCube =
238            getAttribute(CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACECUBEMAP_WIDTH);
239        limits.maxTextureArrayLayers = Math::Min(
240            getAttribute(CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE1D_LAYERED_LAYERS),
241            getAttribute(CU_DEVICE_ATTRIBUTE_MAXIMUM_SURFACE2D_LAYERED_LAYERS));
242
243        // limits.maxVertexInputElements
244        // limits.maxVertexInputElementOffset
245        // limits.maxVertexStreams
246        // limits.maxVertexStreamStride
247
248        limits.maxComputeThreadsPerGroup = getAttribute(CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_BLOCK);
249        limits.maxComputeThreadGroupSize[0] = getAttribute(CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_X);
250        limits.maxComputeThreadGroupSize[1] = getAttribute(CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Y);
251        limits.maxComputeThreadGroupSize[2] = getAttribute(CU_DEVICE_ATTRIBUTE_MAX_BLOCK_DIM_Z);
252        limits.maxComputeDispatchThreadGroups[0] = getAttribute(CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_X);
253        limits.maxComputeDispatchThreadGroups[1] = getAttribute(CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Y);
254        limits.maxComputeDispatchThreadGroups[2] = getAttribute(CU_DEVICE_ATTRIBUTE_MAX_GRID_DIM_Z);
255
256        // limits.maxViewports
257        // limits.maxViewportDimensions
258        // limits.maxFramebufferDimensions
259
260        // limits.maxShaderVisibleSamplers
261
262        m_info.limits = limits;
263
264        SLANG_CUDA_RETURN_ON_FAIL(lastResult);
265    }
266
267    return SLANG_OK;
268}
269
270Result DeviceImpl::getCUDAFormat(Format format, CUarray_format* outFormat)
271{
272    // TODO: Expand to cover all available formats that can be supported in CUDA
273    switch (format)
274    {
275    case Format::R32G32B32A32_FLOAT:
276    case Format::R32G32B32_FLOAT:
277    case Format::R32G32_FLOAT:
278    case Format::R32_FLOAT:
279    case Format::D32_FLOAT:
280        *outFormat = CU_AD_FORMAT_FLOAT;
281        return SLANG_OK;
282    case Format::R16G16B16A16_FLOAT:
283    case Format::R16G16_FLOAT:
284    case Format::R16_FLOAT:
285        *outFormat = CU_AD_FORMAT_HALF;
286        return SLANG_OK;
287    case Format::R32G32B32A32_UINT:
288    case Format::R32G32B32_UINT:
289    case Format::R32G32_UINT:
290    case Format::R32_UINT:
291        *outFormat = CU_AD_FORMAT_UNSIGNED_INT32;
292        return SLANG_OK;
293    case Format::R16G16B16A16_UINT:
294    case Format::R16G16_UINT:
295    case Format::R16_UINT:
296        *outFormat = CU_AD_FORMAT_UNSIGNED_INT16;
297        return SLANG_OK;
298    case Format::R8G8B8A8_UINT:
299    case Format::R8G8_UINT:
300    case Format::R8_UINT:
301    case Format::R8G8B8A8_UNORM:
302        *outFormat = CU_AD_FORMAT_UNSIGNED_INT8;
303        return SLANG_OK;
304    case Format::R32G32B32A32_SINT:
305    case Format::R32G32B32_SINT:
306    case Format::R32G32_SINT:
307    case Format::R32_SINT:
308        *outFormat = CU_AD_FORMAT_SIGNED_INT32;
309        return SLANG_OK;
310    case Format::R16G16B16A16_SINT:
311    case Format::R16G16_SINT:
312    case Format::R16_SINT:
313        *outFormat = CU_AD_FORMAT_SIGNED_INT16;
314        return SLANG_OK;
315    case Format::R8G8B8A8_SINT:
316    case Format::R8G8_SINT:
317    case Format::R8_SINT:
318        *outFormat = CU_AD_FORMAT_SIGNED_INT8;
319        return SLANG_OK;
320    default:
321        SLANG_ASSERT(!"Only support R32_FLOAT/R8G8B8A8_UNORM formats for now");
322        return SLANG_FAIL;
323    }
324}
325
326SLANG_NO_THROW Result SLANG_MCALL DeviceImpl::createTextureResource(
327    const ITextureResource::Desc& desc,
328    const ITextureResource::SubresourceData* initData,
329    ITextureResource** outResource)
330{
331    TextureResource::Desc srcDesc = fixupTextureDesc(desc);
332
333    RefPtr<TextureResourceImpl> tex = new TextureResourceImpl(srcDesc);
334    tex->m_cudaContext = m_context;
335
336    CUresourcetype resourceType;
337
338    // The size of the element/texel in bytes
339    size_t elementSize = 0;
340
341    // Our `ITextureResource::Desc` uses an enumeration to specify
342    // the "shape"/rank of a texture (1D, 2D, 3D, Cube), but CUDA's
343    // `cuMipmappedArrayCreate` seemingly relies on a policy where
344    // the extents of the array in dimenions above the rank are
345    // specified as zero (e.g., a 1D texture requires `height==0`).
346    //
347    // We will start by massaging the extents as specified by the
348    // user into a form that CUDA wants/expects, based on the
349    // texture shape as specified in the `desc`.
350    //
351    int width = desc.size.width;
352    int height = desc.size.height;
353    int depth = desc.size.depth;
354    switch (desc.type)
355    {
356    case IResource::Type::Texture1D:
357        height = 0;
358        depth = 0;
359        break;
360
361    case IResource::Type::Texture2D:
362        depth = 0;
363        break;
364
365    case IResource::Type::Texture3D:
366        break;
367
368    case IResource::Type::TextureCube:
369        depth = 1;
370        break;
371    }
372
373    {
374        CUarray_format format = CU_AD_FORMAT_FLOAT;
375        int numChannels = 0;
376
377        SLANG_RETURN_ON_FAIL(getCUDAFormat(desc.format, &format));
378        FormatInfo info;
379        gfxGetFormatInfo(desc.format, &info);
380        numChannels = info.channelCount;
381
382        switch (format)
383        {
384        case CU_AD_FORMAT_FLOAT:
385            {
386                elementSize = sizeof(float) * numChannels;
387                break;
388            }
389        case CU_AD_FORMAT_HALF:
390            {
391                elementSize = sizeof(uint16_t) * numChannels;
392                break;
393            }
394        case CU_AD_FORMAT_UNSIGNED_INT8:
395            {
396                elementSize = sizeof(uint8_t) * numChannels;
397                break;
398            }
399        default:
400            {
401                SLANG_ASSERT(!"Only support R32_FLOAT/R8G8B8A8_UNORM formats for now");
402                return SLANG_FAIL;
403            }
404        }
405
406        if (desc.numMipLevels > 1)
407        {
408            resourceType = CU_RESOURCE_TYPE_MIPMAPPED_ARRAY;
409
410            CUDA_ARRAY3D_DESCRIPTOR arrayDesc;
411            memset(&arrayDesc, 0, sizeof(arrayDesc));
412
413            arrayDesc.Width = width;
414            arrayDesc.Height = height;
415            arrayDesc.Depth = depth;
416            arrayDesc.Format = format;
417            arrayDesc.NumChannels = numChannels;
418            arrayDesc.Flags = 0;
419
420            if (desc.arraySize > 1)
421            {
422                if (desc.type == IResource::Type::Texture1D ||
423                    desc.type == IResource::Type::Texture2D ||
424                    desc.type == IResource::Type::TextureCube)
425                {
426                    arrayDesc.Flags |= CUDA_ARRAY3D_LAYERED;
427                    arrayDesc.Depth = desc.arraySize;
428                }
429                else
430                {
431                    SLANG_ASSERT(!"Arrays only supported for 1D and 2D");
432                    return SLANG_FAIL;
433                }
434            }
435
436            if (desc.type == IResource::Type::TextureCube)
437            {
438                arrayDesc.Flags |= CUDA_ARRAY3D_CUBEMAP;
439                arrayDesc.Depth *= 6;
440            }
441
442            SLANG_CUDA_RETURN_ON_FAIL(
443                cuMipmappedArrayCreate(&tex->m_cudaMipMappedArray, &arrayDesc, desc.numMipLevels));
444        }
445        else
446        {
447            resourceType = CU_RESOURCE_TYPE_ARRAY;
448
449            if (desc.arraySize > 1)
450            {
451                if (desc.type == IResource::Type::Texture1D ||
452                    desc.type == IResource::Type::Texture2D ||
453                    desc.type == IResource::Type::TextureCube)
454                {
455                    SLANG_ASSERT(!"Only 1D, 2D and Cube arrays supported");
456                    return SLANG_FAIL;
457                }
458
459                CUDA_ARRAY3D_DESCRIPTOR arrayDesc;
460                memset(&arrayDesc, 0, sizeof(arrayDesc));
461
462                // Set the depth as the array length
463                arrayDesc.Depth = desc.arraySize;
464                if (desc.type == IResource::Type::TextureCube)
465                {
466                    arrayDesc.Depth *= 6;
467                }
468
469                arrayDesc.Height = height;
470                arrayDesc.Width = width;
471                arrayDesc.Format = format;
472                arrayDesc.NumChannels = numChannels;
473
474                if (desc.type == IResource::Type::TextureCube)
475                {
476                    arrayDesc.Flags |= CUDA_ARRAY3D_CUBEMAP;
477                }
478
479                SLANG_CUDA_RETURN_ON_FAIL(cuArray3DCreate(&tex->m_cudaArray, &arrayDesc));
480            }
481            else if (
482                desc.type == IResource::Type::Texture3D ||
483                desc.type == IResource::Type::TextureCube)
484            {
485                CUDA_ARRAY3D_DESCRIPTOR arrayDesc;
486                memset(&arrayDesc, 0, sizeof(arrayDesc));
487
488                arrayDesc.Depth = depth;
489                arrayDesc.Height = height;
490                arrayDesc.Width = width;
491                arrayDesc.Format = format;
492                arrayDesc.NumChannels = numChannels;
493
494                arrayDesc.Flags = 0;
495
496                // Handle cube texture
497                if (desc.type == IResource::Type::TextureCube)
498                {
499                    arrayDesc.Depth = 6;
500                    arrayDesc.Flags |= CUDA_ARRAY3D_CUBEMAP;
501                }
502
503                SLANG_CUDA_RETURN_ON_FAIL(cuArray3DCreate(&tex->m_cudaArray, &arrayDesc));
504            }
505            else
506            {
507                CUDA_ARRAY_DESCRIPTOR arrayDesc;
508                memset(&arrayDesc, 0, sizeof(arrayDesc));
509
510                arrayDesc.Height = height;
511                arrayDesc.Width = width;
512                arrayDesc.Format = format;
513                arrayDesc.NumChannels = numChannels;
514
515                // Allocate the array, will work for 1D or 2D case
516                SLANG_CUDA_RETURN_ON_FAIL(cuArrayCreate(&tex->m_cudaArray, &arrayDesc));
517            }
518        }
519    }
520
521    // Work space for holding data for uploading if it needs to be rearranged
522    if (initData)
523    {
524        List<uint8_t> workspace;
525        for (int mipLevel = 0; mipLevel < desc.numMipLevels; ++mipLevel)
526        {
527            int mipWidth = width >> mipLevel;
528            int mipHeight = height >> mipLevel;
529            int mipDepth = depth >> mipLevel;
530
531            mipWidth = (mipWidth == 0) ? 1 : mipWidth;
532            mipHeight = (mipHeight == 0) ? 1 : mipHeight;
533            mipDepth = (mipDepth == 0) ? 1 : mipDepth;
534
535            // If it's a cubemap then the depth is always 6
536            if (desc.type == IResource::Type::TextureCube)
537            {
538                mipDepth = 6;
539            }
540
541            auto dstArray = tex->m_cudaArray;
542            if (tex->m_cudaMipMappedArray)
543            {
544                // Get the array for the mip level
545                SLANG_CUDA_RETURN_ON_FAIL(
546                    cuMipmappedArrayGetLevel(&dstArray, tex->m_cudaMipMappedArray, mipLevel));
547            }
548            SLANG_ASSERT(dstArray);
549
550            // Check using the desc to see if it's plausible
551            {
552                CUDA_ARRAY_DESCRIPTOR arrayDesc;
553                SLANG_CUDA_RETURN_ON_FAIL(cuArrayGetDescriptor(&arrayDesc, dstArray));
554
555                SLANG_ASSERT(mipWidth == arrayDesc.Width);
556                SLANG_ASSERT(
557                    mipHeight == arrayDesc.Height || (mipHeight == 1 && arrayDesc.Height == 0));
558            }
559
560            const void* srcDataPtr = nullptr;
561
562            if (desc.arraySize > 1)
563            {
564                SLANG_ASSERT(
565                    desc.type == IResource::Type::Texture1D ||
566                    desc.type == IResource::Type::Texture2D ||
567                    desc.type == IResource::Type::TextureCube);
568
569                // TODO(JS): Here I assume that arrays are just held contiguously within a
570                // 'face' This seems reasonable and works with the Copy3D.
571                const size_t faceSizeInBytes = elementSize * mipWidth * mipHeight;
572
573                Index faceCount = desc.arraySize;
574                if (desc.type == IResource::Type::TextureCube)
575                {
576                    faceCount *= 6;
577                }
578
579                const size_t mipSizeInBytes = faceSizeInBytes * faceCount;
580                workspace.setCount(mipSizeInBytes);
581
582                // We need to add the face data from each mip
583                // We iterate over face count so we copy all of the cubemap faces
584                for (Index j = 0; j < faceCount; j++)
585                {
586                    const auto srcData = initData[mipLevel + j * desc.numMipLevels].data;
587                    // Copy over to the workspace to make contiguous
588                    ::memcpy(workspace.begin() + faceSizeInBytes * j, srcData, faceSizeInBytes);
589                }
590
591                srcDataPtr = workspace.getBuffer();
592            }
593            else
594            {
595                if (desc.type == IResource::Type::TextureCube)
596                {
597                    size_t faceSizeInBytes = elementSize * mipWidth * mipHeight;
598
599                    workspace.setCount(faceSizeInBytes * 6);
600                    // Copy the data over to make contiguous
601                    for (Index j = 0; j < 6; j++)
602                    {
603                        const auto srcData = initData[mipLevel + j * desc.numMipLevels].data;
604                        ::memcpy(
605                            workspace.getBuffer() + faceSizeInBytes * j,
606                            srcData,
607                            faceSizeInBytes);
608                    }
609                    srcDataPtr = workspace.getBuffer();
610                }
611                else
612                {
613                    const auto srcData = initData[mipLevel].data;
614                    srcDataPtr = srcData;
615                }
616            }
617
618            if (desc.arraySize > 1)
619            {
620                SLANG_ASSERT(
621                    desc.type == IResource::Type::Texture1D ||
622                    desc.type == IResource::Type::Texture2D ||
623                    desc.type == IResource::Type::TextureCube);
624
625                CUDA_MEMCPY3D copyParam;
626                memset(&copyParam, 0, sizeof(copyParam));
627
628                copyParam.dstMemoryType = CU_MEMORYTYPE_ARRAY;
629                copyParam.dstArray = dstArray;
630
631                copyParam.srcMemoryType = CU_MEMORYTYPE_HOST;
632                copyParam.srcHost = srcDataPtr;
633                copyParam.srcPitch = mipWidth * elementSize;
634                copyParam.WidthInBytes = copyParam.srcPitch;
635                copyParam.Height = mipHeight;
636                // Set the depth to the array length
637                copyParam.Depth = desc.arraySize;
638
639                if (desc.type == IResource::Type::TextureCube)
640                {
641                    copyParam.Depth *= 6;
642                }
643
644                SLANG_CUDA_RETURN_ON_FAIL(cuMemcpy3D(&copyParam));
645            }
646            else
647            {
648                switch (desc.type)
649                {
650                case IResource::Type::Texture1D:
651                case IResource::Type::Texture2D:
652                    {
653                        CUDA_MEMCPY2D copyParam;
654                        memset(&copyParam, 0, sizeof(copyParam));
655                        copyParam.dstMemoryType = CU_MEMORYTYPE_ARRAY;
656                        copyParam.dstArray = dstArray;
657                        copyParam.srcMemoryType = CU_MEMORYTYPE_HOST;
658                        copyParam.srcHost = srcDataPtr;
659                        copyParam.srcPitch = mipWidth * elementSize;
660                        copyParam.WidthInBytes = copyParam.srcPitch;
661                        copyParam.Height = mipHeight;
662                        SLANG_CUDA_RETURN_ON_FAIL(cuMemcpy2D(&copyParam));
663                        break;
664                    }
665                case IResource::Type::Texture3D:
666                case IResource::Type::TextureCube:
667                    {
668                        CUDA_MEMCPY3D copyParam;
669                        memset(&copyParam, 0, sizeof(copyParam));
670
671                        copyParam.dstMemoryType = CU_MEMORYTYPE_ARRAY;
672                        copyParam.dstArray = dstArray;
673
674                        copyParam.srcMemoryType = CU_MEMORYTYPE_HOST;
675                        copyParam.srcHost = srcDataPtr;
676                        copyParam.srcPitch = mipWidth * elementSize;
677                        copyParam.WidthInBytes = copyParam.srcPitch;
678                        copyParam.Height = mipHeight;
679                        copyParam.Depth = mipDepth;
680
681                        SLANG_CUDA_RETURN_ON_FAIL(cuMemcpy3D(&copyParam));
682                        break;
683                    }
684
685                default:
686                    {
687                        SLANG_ASSERT(!"Not implemented");
688                        break;
689                    }
690                }
691            }
692        }
693    }
694    // Set up texture sampling parameters, and create final texture obj
695
696    {
697        CUDA_RESOURCE_DESC resDesc;
698        memset(&resDesc, 0, sizeof(CUDA_RESOURCE_DESC));
699        resDesc.resType = resourceType;
700
701        if (tex->m_cudaArray)
702        {
703            resDesc.res.array.hArray = tex->m_cudaArray;
704        }
705        if (tex->m_cudaMipMappedArray)
706        {
707            resDesc.res.mipmap.hMipmappedArray = tex->m_cudaMipMappedArray;
708        }
709
710        // If the texture might be used as a UAV, then we need to allocate
711        // a CUDA "surface" for it.
712        //
713        // Note: We cannot do this unconditionally, because it will fail
714        // on surfaces that are not usable as UAVs (e.g., those with
715        // mipmaps).
716        //
717        // TODO: We should really only be allocating the array at the
718        // time we create a resource, and then allocate the surface or
719        // texture objects as part of view creation.
720        //
721        if (desc.allowedStates.contains(ResourceState::UnorderedAccess))
722        {
723            // On CUDA surfaces only support a single MIP map
724            SLANG_ASSERT(desc.numMipLevels == 1);
725
726            SLANG_CUDA_RETURN_ON_FAIL(cuSurfObjectCreate(&tex->m_cudaSurfObj, &resDesc));
727        }
728
729
730        // Create handle for sampling.
731        CUDA_TEXTURE_DESC texDesc;
732        memset(&texDesc, 0, sizeof(CUDA_TEXTURE_DESC));
733        texDesc.addressMode[0] = CU_TR_ADDRESS_MODE_WRAP;
734        texDesc.addressMode[1] = CU_TR_ADDRESS_MODE_WRAP;
735        texDesc.addressMode[2] = CU_TR_ADDRESS_MODE_WRAP;
736        texDesc.filterMode = CU_TR_FILTER_MODE_LINEAR;
737        texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES;
738
739        SLANG_CUDA_RETURN_ON_FAIL(
740            cuTexObjectCreate(&tex->m_cudaTexObj, &resDesc, &texDesc, nullptr));
741    }
742
743    returnComPtr(outResource, tex);
744    return SLANG_OK;
745}
746
747SLANG_NO_THROW Result SLANG_MCALL DeviceImpl::createBufferResource(
748    const IBufferResource::Desc& descIn,
749    const void* initData,
750    IBufferResource** outResource)
751{
752    auto desc = fixupBufferDesc(descIn);
753    RefPtr<BufferResourceImpl> resource = new BufferResourceImpl(desc);
754    resource->m_cudaContext = m_context;
755    SLANG_CUDA_RETURN_ON_FAIL(cuMemAllocManaged(
756        (CUdeviceptr*)(&resource->m_cudaMemory),
757        desc.sizeInBytes,
758        CU_MEM_ATTACH_GLOBAL));
759    if (initData)
760    {
761        SLANG_CUDA_RETURN_ON_FAIL(
762            cuMemcpy((CUdeviceptr)resource->m_cudaMemory, (CUdeviceptr)initData, desc.sizeInBytes));
763    }
764    returnComPtr(outResource, resource);
765    return SLANG_OK;
766}
767
768SLANG_NO_THROW Result SLANG_MCALL DeviceImpl::createBufferFromSharedHandle(
769    InteropHandle handle,
770    const IBufferResource::Desc& desc,
771    IBufferResource** outResource)
772{
773    if (handle.handleValue == 0)
774    {
775        *outResource = nullptr;
776        return SLANG_OK;
777    }
778
779    RefPtr<BufferResourceImpl> resource = new BufferResourceImpl(desc);
780    resource->m_cudaContext = m_context;
781
782    // CUDA manages sharing of buffers through the idea of an
783    // "external memory" object, which represents the relationship
784    // with another API's objects. In order to create this external
785    // memory association, we first need to fill in a descriptor struct.
786    CUDA_EXTERNAL_MEMORY_HANDLE_DESC externalMemoryHandleDesc;
787    memset(&externalMemoryHandleDesc, 0, sizeof(externalMemoryHandleDesc));
788    switch (handle.api)
789    {
790    case InteropHandleAPI::D3D12:
791        externalMemoryHandleDesc.type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE;
792        break;
793    case InteropHandleAPI::Vulkan:
794        externalMemoryHandleDesc.type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32;
795        break;
796    default:
797        return SLANG_FAIL;
798    }
799    externalMemoryHandleDesc.handle.win32.handle = (void*)handle.handleValue;
800    externalMemoryHandleDesc.size = desc.sizeInBytes;
801    externalMemoryHandleDesc.flags = CUDA_EXTERNAL_MEMORY_DEDICATED;
802
803    // Once we have filled in the descriptor, we can request
804    // that CUDA create the required association between the
805    // external buffer and its own memory.
806    CUexternalMemory externalMemory;
807    SLANG_CUDA_RETURN_ON_FAIL(cuImportExternalMemory(&externalMemory, &externalMemoryHandleDesc));
808    resource->m_cudaExternalMemory = externalMemory;
809
810    // The CUDA "external memory" handle is not itself a device
811    // pointer, so we need to query for a suitable device address
812    // for the buffer with another call.
813    //
814    // Just as for the external memory, we fill in a descriptor
815    // structure (although in this case we only need to specify
816    // the size).
817    CUDA_EXTERNAL_MEMORY_BUFFER_DESC bufferDesc;
818    memset(&bufferDesc, 0, sizeof(bufferDesc));
819    bufferDesc.size = desc.sizeInBytes;
820
821    // Finally, we can "map" the buffer to get a device address.
822    void* deviceAddress;
823    SLANG_CUDA_RETURN_ON_FAIL(
824        cuExternalMemoryGetMappedBuffer((CUdeviceptr*)&deviceAddress, externalMemory, &bufferDesc));
825    resource->m_cudaMemory = deviceAddress;
826
827    returnComPtr(outResource, resource);
828    return SLANG_OK;
829}
830
831SLANG_NO_THROW Result SLANG_MCALL DeviceImpl::createTextureFromSharedHandle(
832    InteropHandle handle,
833    const ITextureResource::Desc& desc,
834    const size_t size,
835    ITextureResource** outResource)
836{
837    if (handle.handleValue == 0)
838    {
839        *outResource = nullptr;
840        return SLANG_OK;
841    }
842
843    RefPtr<TextureResourceImpl> resource = new TextureResourceImpl(desc);
844    resource->m_cudaContext = m_context;
845
846    // CUDA manages sharing of buffers through the idea of an
847    // "external memory" object, which represents the relationship
848    // with another API's objects. In order to create this external
849    // memory association, we first need to fill in a descriptor struct.
850    CUDA_EXTERNAL_MEMORY_HANDLE_DESC externalMemoryHandleDesc;
851    memset(&externalMemoryHandleDesc, 0, sizeof(externalMemoryHandleDesc));
852    switch (handle.api)
853    {
854    case InteropHandleAPI::D3D12:
855        externalMemoryHandleDesc.type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE;
856        break;
857    case InteropHandleAPI::Vulkan:
858        externalMemoryHandleDesc.type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32;
859        break;
860    default:
861        return SLANG_FAIL;
862    }
863    externalMemoryHandleDesc.handle.win32.handle = (void*)handle.handleValue;
864    externalMemoryHandleDesc.size = size;
865    externalMemoryHandleDesc.flags = CUDA_EXTERNAL_MEMORY_DEDICATED;
866
867    CUexternalMemory externalMemory;
868    SLANG_CUDA_RETURN_ON_FAIL(cuImportExternalMemory(&externalMemory, &externalMemoryHandleDesc));
869    resource->m_cudaExternalMemory = externalMemory;
870
871    FormatInfo formatInfo;
872    SLANG_RETURN_ON_FAIL(gfxGetFormatInfo(desc.format, &formatInfo));
873    CUDA_ARRAY3D_DESCRIPTOR arrayDesc;
874    arrayDesc.Depth = desc.size.depth;
875    arrayDesc.Height = desc.size.height;
876    arrayDesc.Width = desc.size.width;
877    arrayDesc.NumChannels = formatInfo.channelCount;
878    getCUDAFormat(desc.format, &arrayDesc.Format);
879    arrayDesc.Flags = 0; // TODO: Flags? CUDA_ARRAY_LAYERED/SURFACE_LDST/CUBEMAP/TEXTURE_GATHER
880
881    CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC externalMemoryMipDesc;
882    memset(&externalMemoryMipDesc, 0, sizeof(externalMemoryMipDesc));
883    externalMemoryMipDesc.offset = 0;
884    externalMemoryMipDesc.arrayDesc = arrayDesc;
885    externalMemoryMipDesc.numLevels = desc.numMipLevels;
886
887    CUmipmappedArray mipArray;
888    SLANG_CUDA_RETURN_ON_FAIL(
889        cuExternalMemoryGetMappedMipmappedArray(&mipArray, externalMemory, &externalMemoryMipDesc));
890    resource->m_cudaMipMappedArray = mipArray;
891
892    CUarray cuArray;
893    SLANG_CUDA_RETURN_ON_FAIL(cuMipmappedArrayGetLevel(&cuArray, mipArray, 0));
894    resource->m_cudaArray = cuArray;
895
896    CUDA_RESOURCE_DESC surfDesc;
897    memset(&surfDesc, 0, sizeof(surfDesc));
898    surfDesc.resType = CU_RESOURCE_TYPE_ARRAY;
899    surfDesc.res.array.hArray = cuArray;
900
901    CUsurfObject surface;
902    SLANG_CUDA_RETURN_ON_FAIL(cuSurfObjectCreate(&surface, &surfDesc));
903    resource->m_cudaSurfObj = surface;
904
905    returnComPtr(outResource, resource);
906    return SLANG_OK;
907}
908
909SLANG_NO_THROW Result SLANG_MCALL DeviceImpl::createTextureView(
910    ITextureResource* texture,
911    IResourceView::Desc const& desc,
912    IResourceView** outView)
913{
914    RefPtr<ResourceViewImpl> view = new ResourceViewImpl();
915    view->m_desc = desc;
916    view->textureResource = dynamic_cast<TextureResourceImpl*>(texture);
917    returnComPtr(outView, view);
918    return SLANG_OK;
919}
920
921SLANG_NO_THROW Result SLANG_MCALL DeviceImpl::createBufferView(
922    IBufferResource* buffer,
923    IBufferResource* counterBuffer,
924    IResourceView::Desc const& desc,
925    IResourceView** outView)
926{
927    RefPtr<ResourceViewImpl> view = new ResourceViewImpl();
928    view->m_desc = desc;
929    view->memoryResource = dynamic_cast<BufferResourceImpl*>(buffer);
930    returnComPtr(outView, view);
931    return SLANG_OK;
932}
933
934SLANG_NO_THROW Result SLANG_MCALL
935DeviceImpl::createQueryPool(const IQueryPool::Desc& desc, IQueryPool** outPool)
936{
937    RefPtr<QueryPoolImpl> pool = new QueryPoolImpl();
938    SLANG_RETURN_ON_FAIL(pool->init(desc));
939    returnComPtr(outPool, pool);
940    return SLANG_OK;
941}
942
943Result DeviceImpl::createShaderObjectLayout(
944    slang::ISession* session,
945    slang::TypeLayoutReflection* typeLayout,
946    ShaderObjectLayoutBase** outLayout)
947{
948    RefPtr<ShaderObjectLayoutImpl> cudaLayout;
949    cudaLayout = new ShaderObjectLayoutImpl(this, session, typeLayout);
950    returnRefPtrMove(outLayout, cudaLayout);
951    return SLANG_OK;
952}
953
954Result DeviceImpl::createShaderObject(ShaderObjectLayoutBase* layout, IShaderObject** outObject)
955{
956    RefPtr<ShaderObjectImpl> result = new ShaderObjectImpl();
957    SLANG_RETURN_ON_FAIL(result->init(this, dynamic_cast<ShaderObjectLayoutImpl*>(layout)));
958    returnComPtr(outObject, result);
959    return SLANG_OK;
960}
961
962Result DeviceImpl::createMutableShaderObject(
963    ShaderObjectLayoutBase* layout,
964    IShaderObject** outObject)
965{
966    RefPtr<MutableShaderObjectImpl> result = new MutableShaderObjectImpl();
967    SLANG_RETURN_ON_FAIL(result->init(this, dynamic_cast<ShaderObjectLayoutImpl*>(layout)));
968    returnComPtr(outObject, result);
969    return SLANG_OK;
970}
971
972Result DeviceImpl::createRootShaderObject(IShaderProgram* program, ShaderObjectBase** outObject)
973{
974    auto cudaProgram = dynamic_cast<ShaderProgramImpl*>(program);
975    auto cudaLayout = cudaProgram->layout;
976
977    RefPtr<RootShaderObjectImpl> result = new RootShaderObjectImpl();
978    SLANG_RETURN_ON_FAIL(result->init(this, cudaLayout));
979    returnRefPtrMove(outObject, result);
980    return SLANG_OK;
981}
982
983SLANG_NO_THROW Result SLANG_MCALL DeviceImpl::createProgram(
984    const IShaderProgram::Desc& desc,
985    IShaderProgram** outProgram,
986    ISlangBlob** outDiagnosticBlob)
987{
988    // If this is a specializable program, we just keep a reference to the slang program and
989    // don't actually create any kernels. This program will be specialized later when we know
990    // the shader object bindings.
991    RefPtr<ShaderProgramImpl> cudaProgram = new ShaderProgramImpl();
992    cudaProgram->init(desc);
993    cudaProgram->cudaContext = m_context;
994    if (desc.slangGlobalScope->getSpecializationParamCount() != 0)
995    {
996        cudaProgram->layout =
997            new RootShaderObjectLayoutImpl(this, desc.slangGlobalScope->getLayout());
998        returnComPtr(outProgram, cudaProgram);
999        return SLANG_OK;
1000    }
1001
1002    ComPtr<ISlangBlob> kernelCode;
1003    ComPtr<ISlangBlob> diagnostics;
1004    auto compileResult = getEntryPointCodeFromShaderCache(
1005        desc.slangGlobalScope,
1006        (SlangInt)0,
1007        0,
1008        kernelCode.writeRef(),
1009        diagnostics.writeRef());
1010    if (diagnostics)
1011    {
1012        getDebugCallback()->handleMessage(
1013            compileResult == SLANG_OK ? DebugMessageType::Warning : DebugMessageType::Error,
1014            DebugMessageSource::Slang,
1015            (char*)diagnostics->getBufferPointer());
1016        if (outDiagnosticBlob)
1017            returnComPtr(outDiagnosticBlob, diagnostics);
1018    }
1019    SLANG_RETURN_ON_FAIL(compileResult);
1020
1021    SLANG_CUDA_RETURN_ON_FAIL(
1022        cuModuleLoadData(&cudaProgram->cudaModule, kernelCode->getBufferPointer()));
1023    cudaProgram->kernelName =
1024        desc.slangGlobalScope->getLayout()->getEntryPointByIndex(0)->getName();
1025    SLANG_CUDA_RETURN_ON_FAIL(cuModuleGetFunction(
1026        &cudaProgram->cudaKernel,
1027        cudaProgram->cudaModule,
1028        cudaProgram->kernelName.getBuffer()));
1029
1030    auto slangGlobalScope = desc.slangGlobalScope;
1031    if (slangGlobalScope)
1032    {
1033        cudaProgram->slangGlobalScope = slangGlobalScope;
1034
1035        auto slangProgramLayout = slangGlobalScope->getLayout();
1036        if (!slangProgramLayout)
1037            return SLANG_FAIL;
1038
1039        RefPtr<RootShaderObjectLayoutImpl> cudaLayout;
1040        cudaLayout = new RootShaderObjectLayoutImpl(this, slangProgramLayout);
1041        cudaLayout->programLayout = slangProgramLayout;
1042        cudaProgram->layout = cudaLayout;
1043    }
1044
1045    returnComPtr(outProgram, cudaProgram);
1046    return SLANG_OK;
1047}
1048
1049SLANG_NO_THROW Result SLANG_MCALL DeviceImpl::createComputePipelineState(
1050    const ComputePipelineStateDesc& desc,
1051    IPipelineState** outState)
1052{
1053    RefPtr<ComputePipelineStateImpl> state = new ComputePipelineStateImpl();
1054    state->shaderProgram = static_cast<ShaderProgramImpl*>(desc.program);
1055    state->init(desc);
1056    returnComPtr(outState, state);
1057    return Result();
1058}
1059
1060void* DeviceImpl::map(IBufferResource* buffer)
1061{
1062    return static_cast<BufferResourceImpl*>(buffer)->m_cudaMemory;
1063}
1064
1065void DeviceImpl::unmap(IBufferResource* buffer)
1066{
1067    SLANG_UNUSED(buffer);
1068}
1069
1070SLANG_NO_THROW const DeviceInfo& SLANG_MCALL DeviceImpl::getDeviceInfo() const
1071{
1072    return m_info;
1073}
1074
1075SLANG_NO_THROW Result SLANG_MCALL DeviceImpl::createTransientResourceHeap(
1076    const ITransientResourceHeap::Desc& desc,
1077    ITransientResourceHeap** outHeap)
1078{
1079    RefPtr<TransientResourceHeapImpl> result = new TransientResourceHeapImpl();
1080    SLANG_RETURN_ON_FAIL(result->init(this, desc));
1081    returnComPtr(outHeap, result);
1082    return SLANG_OK;
1083}
1084
1085SLANG_NO_THROW Result SLANG_MCALL
1086DeviceImpl::createCommandQueue(const ICommandQueue::Desc& desc, ICommandQueue** outQueue)
1087{
1088    RefPtr<CommandQueueImpl> queue = new CommandQueueImpl();
1089    queue->init(this);
1090    returnComPtr(outQueue, queue);
1091    return SLANG_OK;
1092}
1093
1094SLANG_NO_THROW Result SLANG_MCALL DeviceImpl::createSwapchain(
1095    const ISwapchain::Desc& desc,
1096    WindowHandle window,
1097    ISwapchain** outSwapchain)
1098{
1099    SLANG_UNUSED(desc);
1100    SLANG_UNUSED(window);
1101    SLANG_UNUSED(outSwapchain);
1102    return SLANG_FAIL;
1103}
1104
1105SLANG_NO_THROW Result SLANG_MCALL DeviceImpl::createFramebufferLayout(
1106    const IFramebufferLayout::Desc& desc,
1107    IFramebufferLayout** outLayout)
1108{
1109    SLANG_UNUSED(desc);
1110    SLANG_UNUSED(outLayout);
1111    return SLANG_FAIL;
1112}
1113
1114SLANG_NO_THROW Result SLANG_MCALL
1115DeviceImpl::createFramebuffer(const IFramebuffer::Desc& desc, IFramebuffer** outFramebuffer)
1116{
1117    SLANG_UNUSED(desc);
1118    SLANG_UNUSED(outFramebuffer);
1119    return SLANG_FAIL;
1120}
1121
1122SLANG_NO_THROW Result SLANG_MCALL DeviceImpl::createRenderPassLayout(
1123    const IRenderPassLayout::Desc& desc,
1124    IRenderPassLayout** outRenderPassLayout)
1125{
1126    SLANG_UNUSED(desc);
1127    SLANG_UNUSED(outRenderPassLayout);
1128    return SLANG_FAIL;
1129}
1130
1131SLANG_NO_THROW Result SLANG_MCALL
1132DeviceImpl::createSamplerState(ISamplerState::Desc const& desc, ISamplerState** outSampler)
1133{
1134    SLANG_UNUSED(desc);
1135    *outSampler = nullptr;
1136    return SLANG_OK;
1137}
1138
1139SLANG_NO_THROW Result SLANG_MCALL
1140DeviceImpl::createInputLayout(IInputLayout::Desc const& desc, IInputLayout** outLayout)
1141{
1142    SLANG_UNUSED(desc);
1143    SLANG_UNUSED(outLayout);
1144    return SLANG_E_NOT_AVAILABLE;
1145}
1146
1147SLANG_NO_THROW Result SLANG_MCALL DeviceImpl::createGraphicsPipelineState(
1148    const GraphicsPipelineStateDesc& desc,
1149    IPipelineState** outState)
1150{
1151    SLANG_UNUSED(desc);
1152    SLANG_UNUSED(outState);
1153    return SLANG_E_NOT_AVAILABLE;
1154}
1155
1156SLANG_NO_THROW SlangResult SLANG_MCALL DeviceImpl::readTextureResource(
1157    ITextureResource* texture,
1158    ResourceState state,
1159    ISlangBlob** outBlob,
1160    size_t* outRowPitch,
1161    size_t* outPixelSize)
1162{
1163    auto textureImpl = static_cast<TextureResourceImpl*>(texture);
1164
1165    List<uint8_t> blobData;
1166
1167    auto desc = textureImpl->getDesc();
1168    auto width = desc->size.width;
1169    auto height = desc->size.height;
1170    FormatInfo sizeInfo;
1171    SLANG_RETURN_ON_FAIL(gfxGetFormatInfo(desc->format, &sizeInfo));
1172    size_t pixelSize = sizeInfo.blockSizeInBytes / sizeInfo.pixelsPerBlock;
1173    size_t rowPitch = width * pixelSize;
1174    size_t size = height * rowPitch;
1175    blobData.setCount((Index)size);
1176
1177    CUDA_MEMCPY2D copyParam;
1178    memset(&copyParam, 0, sizeof(copyParam));
1179
1180    copyParam.srcMemoryType = CU_MEMORYTYPE_ARRAY;
1181    copyParam.srcArray = textureImpl->m_cudaArray;
1182
1183    copyParam.dstMemoryType = CU_MEMORYTYPE_HOST;
1184    copyParam.dstHost = blobData.getBuffer();
1185    copyParam.dstPitch = rowPitch;
1186    copyParam.WidthInBytes = copyParam.dstPitch;
1187    copyParam.Height = height;
1188    SLANG_CUDA_RETURN_ON_FAIL(cuMemcpy2D(&copyParam));
1189
1190    *outRowPitch = rowPitch;
1191    *outPixelSize = pixelSize;
1192
1193    auto blob = ListBlob::moveCreate(blobData);
1194
1195    returnComPtr(outBlob, blob);
1196    return SLANG_OK;
1197}
1198
1199SLANG_NO_THROW Result SLANG_MCALL DeviceImpl::readBufferResource(
1200    IBufferResource* buffer,
1201    size_t offset,
1202    size_t size,
1203    ISlangBlob** outBlob)
1204{
1205    auto bufferImpl = static_cast<BufferResourceImpl*>(buffer);
1206
1207    List<uint8_t> blobData;
1208
1209    blobData.setCount((Index)size);
1210    cuMemcpy(
1211        (CUdeviceptr)blobData.getBuffer(),
1212        (CUdeviceptr)((uint8_t*)bufferImpl->m_cudaMemory + offset),
1213        size);
1214
1215    auto blob = ListBlob::moveCreate(blobData);
1216
1217    returnComPtr(outBlob, blob);
1218    return SLANG_OK;
1219}
1220
1221} // namespace cuda
1222#endif
1223} // namespace gfx