yum-mirror/slang

Making it easier to work with shaders

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

Ellie HermaszewskaCorrect incorrect enum usage on metal (#6994)698e43372

master
28.0 KiB893 linesraw
1// metal-device.cpp
2#include "metal-device.h"
3
4#include "../resource-desc-utils.h"
5#include "metal-buffer.h"
6#include "metal-render-pass.h"
7#include "metal-shader-program.h"
8#include "metal-swap-chain.h"
9#include "metal-texture.h"
10#include "metal-util.h"
11#include "metal-vertex-layout.h"
12// #include "metal-command-queue.h"
13#include "metal-fence.h"
14#include "metal-query.h"
15// #include "metal-resource-views.h"
16#include "metal-sampler.h"
17#include "metal-shader-object-layout.h"
18#include "metal-shader-object.h"
19// #include "metal-shader-table.h"
20#include "metal-transient-heap.h"
21// #include "metal-pipeline-dump-layer.h"
22// #include "metal-helper-functions.h"
23
24#include "core/slang-platform.h"
25namespace gfx
26{
27
28using namespace Slang;
29
30namespace metal
31{
32
33static bool shouldDumpPipeline()
34{
35    StringBuilder dumpPipelineSettings;
36    PlatformUtil::getEnvironmentVariable(toSlice("SLANG_GFX_DUMP_PIPELINE"), dumpPipelineSettings);
37    return dumpPipelineSettings.produceString() == "1";
38}
39
40DeviceImpl::~DeviceImpl() {}
41
42Result DeviceImpl::getNativeDeviceHandles(InteropHandles* outHandles)
43{
44    outHandles->handles[0].api = InteropHandleAPI::Metal;
45    outHandles->handles[0].handleValue = reinterpret_cast<intptr_t>(m_device.get());
46    return SLANG_OK;
47}
48
49SlangResult DeviceImpl::initialize(const Desc& desc)
50{
51    AUTORELEASEPOOL
52
53    // Initialize device info.
54    {
55        m_info.apiName = "Metal";
56        m_info.bindingStyle = BindingStyle::Metal;
57        m_info.projectionStyle = ProjectionStyle::Metal;
58        m_info.deviceType = DeviceType::Metal;
59        m_info.adapterName = "default";
60        static const float kIdentity[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
61        ::memcpy(m_info.identityProjectionMatrix, kIdentity, sizeof(kIdentity));
62    }
63
64    m_desc = desc;
65
66    SLANG_RETURN_ON_FAIL(RendererBase::initialize(desc));
67    SlangResult initDeviceResult = SLANG_OK;
68
69    m_device = NS::TransferPtr(MTL::CreateSystemDefaultDevice());
70    m_commandQueue = NS::TransferPtr(m_device->newCommandQueue(64));
71    m_hasArgumentBufferTier2 = m_device->argumentBuffersSupport() >= MTL::ArgumentBuffersTier2;
72
73    if (m_hasArgumentBufferTier2)
74    {
75        m_features.add("argument-buffer-tier-2");
76    }
77
78    SLANG_RETURN_ON_FAIL(slangContext.initialize(
79        desc.slang,
80        desc.extendedDescCount,
81        desc.extendedDescs,
82        SLANG_METAL_LIB,
83        "",
84        makeArray(slang::PreprocessorMacroDesc{"__METAL__", "1"}).getView()));
85
86    // TODO: expose via some other means
87    if (captureEnabled())
88    {
89        MTL::CaptureManager* captureManager = MTL::CaptureManager::sharedCaptureManager();
90        MTL::CaptureDescriptor* d = MTL::CaptureDescriptor::alloc()->init();
91        MTL::CaptureDestination captureDest =
92            MTL::CaptureDestination::CaptureDestinationGPUTraceDocument;
93        if (!captureManager->supportsDestination(MTL::CaptureDestinationGPUTraceDocument))
94        {
95            std::cout << "Cannot capture MTL calls to document; ensure that Info.plist exists with "
96                         "'MetalCaptureEnabled' set to 'true'."
97                      << std::endl;
98            exit(1);
99        }
100        d->setDestination(MTL::CaptureDestinationGPUTraceDocument);
101        d->setCaptureObject(m_device.get());
102        NS::SharedPtr<NS::String> path = MetalUtil::createString("frame.gputrace");
103        NS::SharedPtr<NS::URL> url =
104            NS::TransferPtr(NS::URL::alloc()->initFileURLWithPath(path.get()));
105        d->setOutputURL(url.get());
106        NS::Error* errorCode = NS::Error::alloc();
107        if (!captureManager->startCapture(d, &errorCode))
108        {
109            NS::String* errorString = errorCode->description();
110            std::string estr(errorString->cString(NS::UTF8StringEncoding));
111            std::cout << "Start capture failure: " << estr << std::endl;
112            exit(1);
113        }
114    }
115    return SLANG_OK;
116}
117
118// void DeviceImpl::waitForGpu() { m_deviceQueue.flushAndWait(); }
119
120
121const DeviceInfo& DeviceImpl::getDeviceInfo() const
122{
123    return m_info;
124}
125
126Result DeviceImpl::createTransientResourceHeap(
127    const ITransientResourceHeap::Desc& desc,
128    ITransientResourceHeap** outHeap)
129{
130    AUTORELEASEPOOL
131
132    RefPtr<TransientResourceHeapImpl> result = new TransientResourceHeapImpl();
133    SLANG_RETURN_ON_FAIL(result->init(desc, this));
134    returnComPtr(outHeap, result);
135    return SLANG_OK;
136}
137
138Result DeviceImpl::createCommandQueue(const ICommandQueue::Desc& desc, ICommandQueue** outQueue)
139{
140    AUTORELEASEPOOL
141
142    if (m_queueAllocCount != 0)
143        return SLANG_FAIL;
144
145    RefPtr<CommandQueueImpl> result = new CommandQueueImpl;
146    result->init(this, m_commandQueue);
147    returnComPtr(outQueue, result);
148    m_queueAllocCount++;
149    return SLANG_OK;
150}
151
152Result DeviceImpl::createSwapchain(
153    const ISwapchain::Desc& desc,
154    WindowHandle window,
155    ISwapchain** outSwapchain)
156{
157    AUTORELEASEPOOL
158
159    RefPtr<SwapchainImpl> swapchainImpl = new SwapchainImpl();
160    SLANG_RETURN_ON_FAIL(swapchainImpl->init(this, desc, window));
161    returnComPtr(outSwapchain, swapchainImpl);
162    return SLANG_OK;
163}
164
165Result DeviceImpl::createFramebufferLayout(
166    const IFramebufferLayout::Desc& desc,
167    IFramebufferLayout** outLayout)
168{
169    AUTORELEASEPOOL
170
171    RefPtr<FramebufferLayoutImpl> layoutImpl = new FramebufferLayoutImpl;
172    SLANG_RETURN_ON_FAIL(layoutImpl->init(desc));
173    returnComPtr(outLayout, layoutImpl);
174    return SLANG_OK;
175}
176
177Result DeviceImpl::createRenderPassLayout(
178    const IRenderPassLayout::Desc& desc,
179    IRenderPassLayout** outRenderPassLayout)
180{
181    AUTORELEASEPOOL
182
183    RefPtr<RenderPassLayoutImpl> renderPassLayoutImpl = new RenderPassLayoutImpl;
184    SLANG_RETURN_ON_FAIL(renderPassLayoutImpl->init(this, desc));
185    returnComPtr(outRenderPassLayout, renderPassLayoutImpl);
186    return SLANG_OK;
187}
188
189Result DeviceImpl::createFramebuffer(const IFramebuffer::Desc& desc, IFramebuffer** outFramebuffer)
190{
191    AUTORELEASEPOOL
192
193    RefPtr<FramebufferImpl> framebufferImpl = new FramebufferImpl;
194    SLANG_RETURN_ON_FAIL(framebufferImpl->init(this, desc));
195    returnComPtr(outFramebuffer, framebufferImpl);
196    return SLANG_OK;
197}
198
199SlangResult DeviceImpl::readTextureResource(
200    ITextureResource* texture,
201    ResourceState state,
202    ISlangBlob** outBlob,
203    Size* outRowPitch,
204    Size* outPixelSize)
205{
206    AUTORELEASEPOOL
207
208    TextureResourceImpl* textureImpl = static_cast<TextureResourceImpl*>(texture);
209
210    if (textureImpl->getDesc()->sampleDesc.numSamples > 1)
211    {
212        return SLANG_E_NOT_IMPLEMENTED;
213    }
214
215    NS::SharedPtr<MTL::Texture> srcTexture = textureImpl->m_texture;
216
217    const ITextureResource::Desc& desc = *textureImpl->getDesc();
218    Count width = Math::Max(desc.size.width, 1);
219    Count height = Math::Max(desc.size.height, 1);
220    Count depth = Math::Max(desc.size.depth, 1);
221    FormatInfo formatInfo;
222    gfxGetFormatInfo(desc.format, &formatInfo);
223    Size bytesPerPixel = formatInfo.blockSizeInBytes / formatInfo.pixelsPerBlock;
224    Size bytesPerRow = Size(width) * bytesPerPixel;
225    Size bytesPerSlice = Size(height) * bytesPerRow;
226    Size bufferSize = Size(depth) * bytesPerSlice;
227    if (outRowPitch)
228        *outRowPitch = bytesPerRow;
229    if (outPixelSize)
230        *outPixelSize = bytesPerPixel;
231
232    // create staging buffer
233    NS::SharedPtr<MTL::Buffer> stagingBuffer =
234        NS::TransferPtr(m_device->newBuffer(bufferSize, MTL::StorageModeShared));
235    if (!stagingBuffer)
236    {
237        return SLANG_FAIL;
238    }
239
240    MTL::CommandBuffer* commandBuffer = m_commandQueue->commandBuffer();
241    MTL::BlitCommandEncoder* encoder = commandBuffer->blitCommandEncoder();
242    encoder->copyFromTexture(
243        srcTexture.get(),
244        0,
245        0,
246        MTL::Origin(0, 0, 0),
247        MTL::Size(width, height, depth),
248        stagingBuffer.get(),
249        0,
250        bytesPerRow,
251        bytesPerSlice);
252    encoder->endEncoding();
253    commandBuffer->commit();
254    commandBuffer->waitUntilCompleted();
255
256    List<uint8_t> blobData;
257    blobData.setCount(bufferSize);
258    ::memcpy(blobData.getBuffer(), stagingBuffer->contents(), bufferSize);
259    auto blob = ListBlob::moveCreate(blobData);
260
261    returnComPtr(outBlob, blob);
262    return SLANG_OK;
263}
264
265SlangResult DeviceImpl::readBufferResource(
266    IBufferResource* buffer,
267    Offset offset,
268    Size size,
269    ISlangBlob** outBlob)
270{
271    AUTORELEASEPOOL
272
273    // create staging buffer
274    NS::SharedPtr<MTL::Buffer> stagingBuffer =
275        NS::TransferPtr(m_device->newBuffer(size, MTL::StorageModeShared));
276    if (!stagingBuffer)
277    {
278        return SLANG_FAIL;
279    }
280
281    MTL::CommandBuffer* commandBuffer = m_commandQueue->commandBuffer();
282    MTL::BlitCommandEncoder* blitEncoder = commandBuffer->blitCommandEncoder();
283    blitEncoder->copyFromBuffer(
284        static_cast<BufferResourceImpl*>(buffer)->m_buffer.get(),
285        offset,
286        stagingBuffer.get(),
287        0,
288        size);
289    blitEncoder->endEncoding();
290    commandBuffer->commit();
291    commandBuffer->waitUntilCompleted();
292
293    List<uint8_t> blobData;
294    blobData.setCount(size);
295    ::memcpy(blobData.getBuffer(), stagingBuffer->contents(), size);
296    auto blob = ListBlob::moveCreate(blobData);
297
298    returnComPtr(outBlob, blob);
299    return SLANG_OK;
300}
301
302Result DeviceImpl::getAccelerationStructurePrebuildInfo(
303    const IAccelerationStructure::BuildInputs& buildInputs,
304    IAccelerationStructure::PrebuildInfo* outPrebuildInfo)
305{
306    AUTORELEASEPOOL
307
308    return SLANG_E_NOT_IMPLEMENTED;
309}
310
311Result DeviceImpl::createAccelerationStructure(
312    const IAccelerationStructure::CreateDesc& desc,
313    IAccelerationStructure** outAS)
314{
315    AUTORELEASEPOOL
316
317    return SLANG_E_NOT_IMPLEMENTED;
318}
319
320Result DeviceImpl::getTextureAllocationInfo(
321    const ITextureResource::Desc& descIn,
322    Size* outSize,
323    Size* outAlignment)
324{
325    AUTORELEASEPOOL
326
327    auto alignTo = [&](Size size, Size alignment) -> Size
328    { return ((size + alignment - 1) / alignment) * alignment; };
329
330    TextureResource::Desc desc = fixupTextureDesc(descIn);
331    FormatInfo formatInfo;
332    gfxGetFormatInfo(desc.format, &formatInfo);
333    MTL::PixelFormat pixelFormat = MetalUtil::translatePixelFormat(desc.format);
334    bool isCompressed = gfxIsCompressedFormat(desc.format);
335    Size alignment =
336        isCompressed ? 1 : m_device->minimumLinearTextureAlignmentForPixelFormat(pixelFormat);
337    Size size = 0;
338    ITextureResource::Extents extents = desc.size;
339    extents.width = extents.width ? extents.width : 1;
340    extents.height = extents.height ? extents.height : 1;
341    extents.depth = extents.depth ? extents.depth : 1;
342
343    for (Int i = 0; i < desc.numMipLevels; ++i)
344    {
345        Size rowSize = ((extents.width + formatInfo.blockWidth - 1) / formatInfo.blockWidth) *
346                       formatInfo.blockSizeInBytes;
347        rowSize = alignTo(rowSize, alignment);
348        Size sliceSize = rowSize * alignTo(extents.height, formatInfo.blockHeight);
349        size += sliceSize * extents.depth;
350        extents.width = Math::Max(1, extents.width / 2);
351        extents.height = Math::Max(1, extents.height / 2);
352        extents.depth = Math::Max(1, extents.depth / 2);
353    }
354    size *= desc.arraySize ? desc.arraySize : 1;
355
356    *outSize = size;
357    *outAlignment = alignment;
358
359    return SLANG_OK;
360}
361
362Result DeviceImpl::getTextureRowAlignment(Size* outAlignment)
363{
364    AUTORELEASEPOOL
365
366    *outAlignment = 1;
367    return SLANG_E_NOT_IMPLEMENTED;
368}
369
370Result DeviceImpl::createTextureResource(
371    const ITextureResource::Desc& descIn,
372    const ITextureResource::SubresourceData* initData,
373    ITextureResource** outResource)
374{
375    AUTORELEASEPOOL
376
377    TextureResource::Desc desc = fixupTextureDesc(descIn);
378
379    // Metal doesn't support mip-mapping for 1D textures
380    // However, we still need to use the provided mip level count when initializing the texture
381    Count initMipLevels = desc.numMipLevels;
382    desc.numMipLevels = desc.type == IResource::Type::Texture1D ? 1 : desc.numMipLevels;
383
384    const MTL::PixelFormat pixelFormat = MetalUtil::translatePixelFormat(desc.format);
385    if (pixelFormat == MTL::PixelFormat::PixelFormatInvalid)
386    {
387        assert(!"Unsupported texture format");
388        return SLANG_FAIL;
389    }
390
391    RefPtr<TextureResourceImpl> textureImpl(new TextureResourceImpl(desc, this));
392
393    NS::SharedPtr<MTL::TextureDescriptor> textureDesc =
394        NS::TransferPtr(MTL::TextureDescriptor::alloc()->init());
395    switch (desc.memoryType)
396    {
397    case MemoryType::DeviceLocal:
398        textureDesc->setStorageMode(MTL::StorageModePrivate);
399        break;
400    case MemoryType::Upload:
401        textureDesc->setStorageMode(MTL::StorageModeShared);
402        textureDesc->setCpuCacheMode(MTL::CPUCacheModeWriteCombined);
403        break;
404    case MemoryType::ReadBack:
405        textureDesc->setStorageMode(MTL::StorageModeShared);
406        break;
407    }
408
409    bool isArray = desc.arraySize > 0;
410
411    switch (desc.type)
412    {
413    case IResource::Type::Texture1D:
414        textureDesc->setTextureType(isArray ? MTL::TextureType1DArray : MTL::TextureType1D);
415        textureDesc->setWidth(desc.size.width);
416        break;
417    case IResource::Type::Texture2D:
418        if (desc.sampleDesc.numSamples > 1)
419        {
420            textureDesc->setTextureType(
421                isArray ? MTL::TextureType2DMultisampleArray : MTL::TextureType2DMultisample);
422            textureDesc->setSampleCount(desc.sampleDesc.numSamples);
423        }
424        else
425        {
426            textureDesc->setTextureType(isArray ? MTL::TextureType2DArray : MTL::TextureType2D);
427        }
428        textureDesc->setWidth(descIn.size.width);
429        textureDesc->setHeight(descIn.size.height);
430        break;
431    case IResource::Type::TextureCube:
432        textureDesc->setTextureType(isArray ? MTL::TextureTypeCubeArray : MTL::TextureTypeCube);
433        textureDesc->setWidth(descIn.size.width);
434        textureDesc->setHeight(descIn.size.height);
435        break;
436    case IResource::Type::Texture3D:
437        textureDesc->setTextureType(MTL::TextureType::TextureType3D);
438        textureDesc->setWidth(descIn.size.width);
439        textureDesc->setHeight(descIn.size.height);
440        textureDesc->setDepth(descIn.size.depth);
441        break;
442    default:
443        assert("!Unsupported texture type");
444        return SLANG_FAIL;
445    }
446
447    MTL::TextureUsage textureUsage = MTL::TextureUsageUnknown;
448    if (desc.allowedStates.contains(ResourceState::RenderTarget))
449    {
450        textureUsage |= MTL::TextureUsageRenderTarget;
451    }
452    if (desc.allowedStates.contains(ResourceState::ShaderResource))
453    {
454        textureUsage |= MTL::TextureUsageShaderRead;
455    }
456    if (desc.allowedStates.contains(ResourceState::UnorderedAccess))
457    {
458        textureUsage |= MTL::TextureUsageShaderRead;
459        textureUsage |= MTL::TextureUsageShaderWrite;
460
461        // Request atomic access if the format allows it.
462        switch (desc.format)
463        {
464        case Format::R32_UINT:
465        case Format::R32_SINT:
466        case Format::R32G32_UINT:
467        case Format::R32G32_SINT:
468            textureUsage |= MTL::TextureUsageShaderAtomic;
469            break;
470        }
471    }
472
473    textureDesc->setMipmapLevelCount(desc.numMipLevels);
474    textureDesc->setArrayLength(isArray ? desc.arraySize : 1);
475    textureDesc->setPixelFormat(pixelFormat);
476    textureDesc->setUsage(textureUsage);
477    textureDesc->setSampleCount(desc.sampleDesc.numSamples);
478    textureDesc->setAllowGPUOptimizedContents(desc.memoryType == MemoryType::DeviceLocal);
479
480    textureImpl->m_texture = NS::TransferPtr(m_device->newTexture(textureDesc.get()));
481    if (!textureImpl->m_texture)
482    {
483        return SLANG_FAIL;
484    }
485    textureImpl->m_textureType = textureDesc->textureType();
486    textureImpl->m_pixelFormat = textureDesc->pixelFormat();
487
488    // TODO: handle initData
489    if (initData)
490    {
491        textureDesc->setStorageMode(MTL::StorageModeManaged);
492        textureDesc->setCpuCacheMode(MTL::CPUCacheModeDefaultCache);
493        NS::SharedPtr<MTL::Texture> stagingTexture =
494            NS::TransferPtr(m_device->newTexture(textureDesc.get()));
495
496        MTL::CommandBuffer* commandBuffer = m_commandQueue->commandBuffer();
497        MTL::BlitCommandEncoder* encoder = commandBuffer->blitCommandEncoder();
498        if (!stagingTexture || !commandBuffer || !encoder)
499        {
500            return SLANG_FAIL;
501        }
502
503        Count sliceCount = isArray ? desc.arraySize : 1;
504        if (desc.type == IResource::Type::TextureCube)
505        {
506            sliceCount *= 6;
507        }
508
509        for (Index slice = 0; slice < sliceCount; ++slice)
510        {
511            MTL::Region region;
512            region.origin = MTL::Origin(0, 0, 0);
513            region.size = MTL::Size(desc.size.width, desc.size.height, desc.size.depth);
514            for (Index level = 0; level < initMipLevels; ++level)
515            {
516                if (level >= desc.numMipLevels)
517                    continue;
518                const ITextureResource::SubresourceData& subresourceData =
519                    initData[slice * initMipLevels + level];
520                stagingTexture->replaceRegion(
521                    region,
522                    level,
523                    slice,
524                    subresourceData.data,
525                    subresourceData.strideY,
526                    subresourceData.strideZ);
527                encoder->synchronizeTexture(stagingTexture.get(), slice, level);
528                region.size.width =
529                    region.size.width > 0 ? Math::Max(1ul, region.size.width >> 1) : 0;
530                region.size.height =
531                    region.size.height > 0 ? Math::Max(1ul, region.size.height >> 1) : 0;
532                region.size.depth =
533                    region.size.depth > 0 ? Math::Max(1ul, region.size.depth >> 1) : 0;
534            }
535        }
536
537        encoder->copyFromTexture(stagingTexture.get(), textureImpl->m_texture.get());
538        encoder->endEncoding();
539        commandBuffer->commit();
540        commandBuffer->waitUntilCompleted();
541    }
542
543    returnComPtr(outResource, textureImpl);
544    return SLANG_OK;
545}
546
547Result DeviceImpl::createBufferResource(
548    const IBufferResource::Desc& descIn,
549    const void* initData,
550    IBufferResource** outResource)
551{
552    AUTORELEASEPOOL
553
554    BufferResource::Desc desc = fixupBufferDesc(descIn);
555
556    const Size bufferSize = desc.sizeInBytes;
557
558    MTL::ResourceOptions resourceOptions = MTL::ResourceOptions(0);
559    switch (desc.memoryType)
560    {
561    case MemoryType::DeviceLocal:
562        resourceOptions = MTL::ResourceStorageModePrivate;
563        break;
564    case MemoryType::Upload:
565        resourceOptions = MTL::ResourceStorageModeShared | MTL::ResourceCPUCacheModeWriteCombined;
566        break;
567    case MemoryType::ReadBack:
568        resourceOptions = MTL::ResourceStorageModeShared;
569        break;
570    }
571    resourceOptions |= (desc.memoryType == MemoryType::DeviceLocal)
572                           ? MTL::ResourceStorageModePrivate
573                           : MTL::ResourceStorageModeShared;
574
575    RefPtr<BufferResourceImpl> bufferImpl(new BufferResourceImpl(desc, this));
576    bufferImpl->m_buffer = NS::TransferPtr(m_device->newBuffer(bufferSize, resourceOptions));
577    if (!bufferImpl->m_buffer)
578    {
579        return SLANG_FAIL;
580    }
581
582    if (initData)
583    {
584        NS::SharedPtr<MTL::Buffer> stagingBuffer = NS::TransferPtr(m_device->newBuffer(
585            initData,
586            bufferSize,
587            MTL::ResourceStorageModeShared | MTL::ResourceCPUCacheModeWriteCombined));
588        MTL::CommandBuffer* commandBuffer = m_commandQueue->commandBuffer();
589        MTL::BlitCommandEncoder* encoder = commandBuffer->blitCommandEncoder();
590        if (!stagingBuffer || !commandBuffer || !encoder)
591        {
592            return SLANG_FAIL;
593        }
594        encoder->copyFromBuffer(stagingBuffer.get(), 0, bufferImpl->m_buffer.get(), 0, bufferSize);
595        encoder->endEncoding();
596        commandBuffer->commit();
597        commandBuffer->waitUntilCompleted();
598    }
599
600    returnComPtr(outResource, bufferImpl);
601    return SLANG_OK;
602}
603
604Result DeviceImpl::createBufferFromNativeHandle(
605    InteropHandle handle,
606    const IBufferResource::Desc& srcDesc,
607    IBufferResource** outResource)
608{
609    AUTORELEASEPOOL
610
611    return SLANG_E_NOT_IMPLEMENTED;
612}
613
614Result DeviceImpl::createSamplerState(ISamplerState::Desc const& desc, ISamplerState** outSampler)
615{
616    AUTORELEASEPOOL
617
618    RefPtr<SamplerStateImpl> samplerImpl = new SamplerStateImpl();
619    SLANG_RETURN_ON_FAIL(samplerImpl->init(this, desc));
620    returnComPtr(outSampler, samplerImpl);
621    return SLANG_OK;
622}
623
624Result DeviceImpl::createTextureView(
625    ITextureResource* texture,
626    IResourceView::Desc const& desc,
627    IResourceView** outView)
628{
629    AUTORELEASEPOOL
630
631    auto textureImpl = static_cast<TextureResourceImpl*>(texture);
632    RefPtr<TextureResourceViewImpl> viewImpl = new TextureResourceViewImpl(this);
633    viewImpl->m_desc = desc;
634    viewImpl->m_device = this;
635    viewImpl->m_texture = textureImpl;
636    if (textureImpl == nullptr)
637    {
638        returnComPtr(outView, viewImpl);
639        return SLANG_OK;
640    }
641
642    const ITextureResource::Desc& textureDesc = *textureImpl->getDesc();
643    SubresourceRange sr = desc.subresourceRange;
644    sr.mipLevelCount =
645        sr.mipLevelCount == 0 ? textureDesc.numMipLevels - sr.mipLevel : sr.mipLevelCount;
646    sr.layerCount = sr.layerCount == 0 ? textureDesc.arraySize - sr.baseArrayLayer : sr.layerCount;
647    if (sr.mipLevel == 0 && sr.mipLevelCount == textureDesc.numMipLevels &&
648        sr.baseArrayLayer == 0 && sr.layerCount == textureDesc.arraySize)
649    {
650        viewImpl->m_textureView = textureImpl->m_texture;
651        returnComPtr(outView, viewImpl);
652        return SLANG_OK;
653    }
654
655    MTL::PixelFormat pixelFormat = desc.format == Format::Unknown
656                                       ? textureImpl->m_pixelFormat
657                                       : MetalUtil::translatePixelFormat(desc.format);
658    NS::Range sliceRange(sr.baseArrayLayer, sr.layerCount);
659    NS::Range levelRange(sr.mipLevel, sr.mipLevelCount);
660
661    viewImpl->m_textureView = NS::TransferPtr(textureImpl->m_texture->newTextureView(
662        pixelFormat,
663        textureImpl->m_textureType,
664        levelRange,
665        sliceRange));
666    if (!viewImpl->m_textureView)
667    {
668        return SLANG_FAIL;
669    }
670
671    returnComPtr(outView, viewImpl);
672    return SLANG_OK;
673}
674
675Result DeviceImpl::getFormatSupportedResourceStates(Format format, ResourceStateSet* outStates)
676{
677    AUTORELEASEPOOL
678
679    // TODO - add table based on https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
680    ResourceStateSet allowedStates;
681    allowedStates.add(ResourceState::VertexBuffer);
682    allowedStates.add(ResourceState::IndexBuffer);
683    allowedStates.add(ResourceState::ConstantBuffer);
684    allowedStates.add(ResourceState::ShaderResource);
685    allowedStates.add(ResourceState::UnorderedAccess);
686    allowedStates.add(ResourceState::RenderTarget);
687    allowedStates.add(ResourceState::DepthRead);
688    allowedStates.add(ResourceState::DepthWrite);
689    allowedStates.add(ResourceState::Present);
690    allowedStates.add(ResourceState::IndirectArgument);
691    allowedStates.add(ResourceState::CopySource);
692    allowedStates.add(ResourceState::ResolveSource);
693    allowedStates.add(ResourceState::CopyDestination);
694    allowedStates.add(ResourceState::ResolveDestination);
695    allowedStates.add(ResourceState::AccelerationStructure);
696    allowedStates.add(ResourceState::AccelerationStructureBuildInput);
697
698    *outStates = allowedStates;
699    return SLANG_OK;
700}
701
702Result DeviceImpl::createBufferView(
703    IBufferResource* buffer,
704    IBufferResource* counterBuffer,
705    IResourceView::Desc const& desc,
706    IResourceView** outView)
707{
708    AUTORELEASEPOOL
709
710    // Counter buffers are not supported on metal.
711    if (counterBuffer)
712    {
713        return SLANG_FAIL;
714    }
715
716    if (desc.type != IResourceView::Type::UnorderedAccess &&
717        desc.type != IResourceView::Type::ShaderResource)
718    {
719        return SLANG_FAIL;
720    }
721
722    auto bufferImpl = static_cast<BufferResourceImpl*>(buffer);
723
724    RefPtr<BufferResourceViewImpl> viewImpl = new BufferResourceViewImpl(this);
725    viewImpl->m_desc = desc;
726    viewImpl->m_buffer = bufferImpl;
727    viewImpl->m_offset = desc.bufferRange.offset;
728    viewImpl->m_size =
729        desc.bufferRange.size == 0 ? bufferImpl->getDesc()->sizeInBytes : desc.bufferRange.size;
730    returnComPtr(outView, viewImpl);
731    return SLANG_OK;
732}
733
734Result DeviceImpl::createInputLayout(IInputLayout::Desc const& desc, IInputLayout** outLayout)
735{
736    AUTORELEASEPOOL
737
738    RefPtr<InputLayoutImpl> layoutImpl(new InputLayoutImpl);
739    SLANG_RETURN_ON_FAIL(layoutImpl->init(desc));
740    returnComPtr(outLayout, layoutImpl);
741    return SLANG_OK;
742}
743
744Result DeviceImpl::createProgram(
745    const IShaderProgram::Desc& desc,
746    IShaderProgram** outProgram,
747    ISlangBlob** outDiagnosticBlob)
748{
749    AUTORELEASEPOOL
750
751    RefPtr<ShaderProgramImpl> shaderProgram = new ShaderProgramImpl(this);
752    shaderProgram->init(desc);
753
754    RootShaderObjectLayoutImpl::create(
755        this,
756        shaderProgram->linkedProgram,
757        shaderProgram->linkedProgram->getLayout(),
758        shaderProgram->m_rootObjectLayout.writeRef());
759
760    if (!shaderProgram->isSpecializable())
761    {
762        SLANG_RETURN_ON_FAIL(shaderProgram->compileShaders(this));
763    }
764
765    returnComPtr(outProgram, shaderProgram);
766    return SLANG_OK;
767}
768
769Result DeviceImpl::createShaderObjectLayout(
770    slang::ISession* session,
771    slang::TypeLayoutReflection* typeLayout,
772    ShaderObjectLayoutBase** outLayout)
773{
774    AUTORELEASEPOOL
775
776    RefPtr<ShaderObjectLayoutImpl> layout;
777    SLANG_RETURN_ON_FAIL(
778        ShaderObjectLayoutImpl::createForElementType(this, session, typeLayout, layout.writeRef()));
779    returnRefPtrMove(outLayout, layout);
780    return SLANG_OK;
781}
782
783Result DeviceImpl::createShaderObject(ShaderObjectLayoutBase* layout, IShaderObject** outObject)
784{
785    AUTORELEASEPOOL
786
787    RefPtr<ShaderObjectImpl> shaderObject;
788    SLANG_RETURN_ON_FAIL(ShaderObjectImpl::create(
789        this,
790        static_cast<ShaderObjectLayoutImpl*>(layout),
791        shaderObject.writeRef()));
792    returnComPtr(outObject, shaderObject);
793    return SLANG_OK;
794}
795
796Result DeviceImpl::createMutableShaderObject(
797    ShaderObjectLayoutBase* layout,
798    IShaderObject** outObject)
799{
800    AUTORELEASEPOOL
801
802    return SLANG_E_NOT_IMPLEMENTED;
803}
804
805Result DeviceImpl::createMutableRootShaderObject(IShaderProgram* program, IShaderObject** outObject)
806{
807    AUTORELEASEPOOL
808
809    return SLANG_E_NOT_IMPLEMENTED;
810}
811
812Result DeviceImpl::createShaderTable(const IShaderTable::Desc& desc, IShaderTable** outShaderTable)
813{
814    AUTORELEASEPOOL
815
816    return SLANG_E_NOT_IMPLEMENTED;
817}
818
819Result DeviceImpl::createGraphicsPipelineState(
820    const GraphicsPipelineStateDesc& desc,
821    IPipelineState** outState)
822{
823    AUTORELEASEPOOL
824
825    RefPtr<PipelineStateImpl> pipelineStateImpl = new PipelineStateImpl(this);
826    pipelineStateImpl->init(desc);
827    returnComPtr(outState, pipelineStateImpl);
828    return SLANG_OK;
829}
830
831Result DeviceImpl::createComputePipelineState(
832    const ComputePipelineStateDesc& desc,
833    IPipelineState** outState)
834{
835    AUTORELEASEPOOL
836
837    RefPtr<PipelineStateImpl> pipelineStateImpl = new PipelineStateImpl(this);
838    pipelineStateImpl->init(desc);
839    m_deviceObjectsWithPotentialBackReferences.add(pipelineStateImpl);
840    returnComPtr(outState, pipelineStateImpl);
841    return SLANG_OK;
842}
843
844Result DeviceImpl::createRayTracingPipelineState(
845    const RayTracingPipelineStateDesc& desc,
846    IPipelineState** outState)
847{
848    AUTORELEASEPOOL
849
850    return SLANG_E_NOT_IMPLEMENTED;
851}
852
853Result DeviceImpl::createQueryPool(const IQueryPool::Desc& desc, IQueryPool** outPool)
854{
855    AUTORELEASEPOOL
856
857    RefPtr<QueryPoolImpl> poolImpl = new QueryPoolImpl();
858    SLANG_RETURN_ON_FAIL(poolImpl->init(this, desc));
859    returnComPtr(outPool, poolImpl);
860    return SLANG_OK;
861}
862
863Result DeviceImpl::createFence(const IFence::Desc& desc, IFence** outFence)
864{
865    AUTORELEASEPOOL
866
867    RefPtr<FenceImpl> fenceImpl = new FenceImpl();
868    SLANG_RETURN_ON_FAIL(fenceImpl->init(this, desc));
869    returnComPtr(outFence, fenceImpl);
870    return SLANG_OK;
871}
872
873Result DeviceImpl::waitForFences(
874    GfxCount fenceCount,
875    IFence** fences,
876    uint64_t* fenceValues,
877    bool waitForAll,
878    uint64_t timeout)
879{
880    // return SLANG_E_NOT_IMPLEMENTED;
881    for (GfxCount i = 0; i < fenceCount; ++i)
882    {
883        FenceImpl* fenceImpl = static_cast<FenceImpl*>(fences[i]);
884        if (!fenceImpl->waitForFence(fenceValues[i], timeout))
885        {
886            return SLANG_FAIL;
887        }
888    }
889    return SLANG_OK;
890}
891
892} // namespace metal
893} // namespace gfx