| /* |
| * Copyright (C) 2018 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #import "config.h" |
| #import "GPUTexture.h" |
| |
| #if ENABLE(WEBGPU) |
| |
| #import "GPUDevice.h" |
| #import "GPUTextureDescriptor.h" |
| #import "GPUUtils.h" |
| #import "Logging.h" |
| #import <Metal/Metal.h> |
| #import <wtf/BlockObjCExceptions.h> |
| #import <wtf/Optional.h> |
| |
| namespace WebCore { |
| |
| static MTLTextureType mtlTextureTypeForGPUTextureDescriptor(const GPUTextureDescriptor& descriptor) |
| { |
| switch (descriptor.dimension) { |
| case GPUTextureDimension::_1d: |
| return (descriptor.arrayLayerCount == 1) ? MTLTextureType1D : MTLTextureType1DArray; |
| case GPUTextureDimension::_2d: { |
| if (descriptor.arrayLayerCount == 1) |
| return (descriptor.sampleCount == 1) ? MTLTextureType2D : MTLTextureType2DMultisample; |
| |
| return MTLTextureType2DArray; |
| } |
| case GPUTextureDimension::_3d: |
| return MTLTextureType3D; |
| } |
| } |
| |
| static Optional<MTLTextureUsage> mtlTextureUsageForGPUTextureUsageFlags(OptionSet<GPUTextureUsage::Flags> flags, const char* const functionName) |
| { |
| #if LOG_DISABLED |
| UNUSED_PARAM(functionName); |
| #endif |
| |
| if (flags.containsAny({ GPUTextureUsage::Flags::CopySource, GPUTextureUsage::Flags::Sampled }) && (flags & GPUTextureUsage::Flags::Storage)) { |
| LOG(WebGPU, "%s: Texture cannot have both STORAGE and a read-only usage!", functionName); |
| return WTF::nullopt; |
| } |
| |
| if (flags & GPUTextureUsage::Flags::OutputAttachment && flags.containsAny({ GPUTextureUsage::Flags::Storage, GPUTextureUsage::Flags::Sampled })) { |
| LOG(WebGPU, "%s: Texture cannot have OUTPUT_ATTACHMENT usage with STORAGE or SAMPLED usages!", functionName); |
| return WTF::nullopt; |
| } |
| |
| MTLTextureUsage result = MTLTextureUsagePixelFormatView; |
| if (flags.contains(GPUTextureUsage::Flags::OutputAttachment)) |
| result |= MTLTextureUsageRenderTarget; |
| if (flags.containsAny({ GPUTextureUsage::Flags::Storage, GPUTextureUsage::Flags::Sampled })) |
| result |= MTLTextureUsageShaderRead; |
| if (flags.contains(GPUTextureUsage::Flags::Storage)) |
| result |= MTLTextureUsageShaderWrite; |
| |
| return result; |
| } |
| |
| #if !PLATFORM(MAC) |
| static MTLStorageMode storageModeForPixelFormatAndSampleCount(MTLPixelFormat format, unsigned samples) |
| { |
| // Depth, Stencil, DepthStencil, and Multisample textures must be allocated with the MTLStorageModePrivate resource option. |
| if (format == MTLPixelFormatDepth32Float_Stencil8 || samples > 1) |
| return MTLStorageModePrivate; |
| |
| return MTLStorageModeShared; |
| } |
| #endif |
| |
| static RetainPtr<MTLTextureDescriptor> tryCreateMtlTextureDescriptor(const char* const functionName, const GPUTextureDescriptor& descriptor, OptionSet<GPUTextureUsage::Flags> usage) |
| { |
| RetainPtr<MTLTextureDescriptor> mtlDescriptor; |
| |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| |
| mtlDescriptor = adoptNS([MTLTextureDescriptor new]); |
| |
| END_BLOCK_OBJC_EXCEPTIONS; |
| |
| if (!mtlDescriptor) { |
| LOG(WebGPU, "%s: Unable to create new MTLTextureDescriptor!", functionName); |
| return nullptr; |
| } |
| |
| // FIXME: Add more validation as constraints are added to spec. |
| auto pixelFormat = static_cast<MTLPixelFormat>(platformTextureFormatForGPUTextureFormat(descriptor.format)); |
| |
| auto mtlUsage = mtlTextureUsageForGPUTextureUsageFlags(usage, functionName); |
| if (!mtlUsage) |
| return nullptr; |
| |
| #if PLATFORM(MAC) |
| auto storageMode = MTLStorageModePrivate; |
| #else |
| auto storageMode = storageModeForPixelFormatAndSampleCount(pixelFormat, descriptor.sampleCount); |
| #endif |
| |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| |
| [mtlDescriptor setWidth:descriptor.size.width]; |
| [mtlDescriptor setHeight:descriptor.size.height]; |
| [mtlDescriptor setDepth:descriptor.size.depth]; |
| [mtlDescriptor setArrayLength:descriptor.arrayLayerCount]; |
| [mtlDescriptor setMipmapLevelCount:descriptor.mipLevelCount]; |
| [mtlDescriptor setSampleCount:descriptor.sampleCount]; |
| [mtlDescriptor setTextureType:mtlTextureTypeForGPUTextureDescriptor(descriptor)]; |
| [mtlDescriptor setPixelFormat:pixelFormat]; |
| [mtlDescriptor setUsage:*mtlUsage]; |
| |
| [mtlDescriptor setStorageMode:storageMode]; |
| |
| END_BLOCK_OBJC_EXCEPTIONS; |
| |
| return mtlDescriptor; |
| } |
| |
| RefPtr<GPUTexture> GPUTexture::tryCreate(const GPUDevice& device, const GPUTextureDescriptor& descriptor) |
| { |
| const char* const functionName = "GPUTexture::tryCreate()"; |
| |
| if (!device.platformDevice()) { |
| LOG(WebGPU, "%s: Invalid GPUDevice!", functionName); |
| return nullptr; |
| } |
| |
| auto usage = OptionSet<GPUTextureUsage::Flags>::fromRaw(descriptor.usage); |
| auto mtlDescriptor = tryCreateMtlTextureDescriptor(functionName, descriptor, usage); |
| if (!mtlDescriptor) |
| return nullptr; |
| |
| RetainPtr<MTLTexture> mtlTexture; |
| |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| |
| mtlTexture = adoptNS([device.platformDevice() newTextureWithDescriptor:mtlDescriptor.get()]); |
| |
| END_BLOCK_OBJC_EXCEPTIONS; |
| |
| if (!mtlTexture) { |
| LOG(WebGPU, "%s: Unable to create MTLTexture!", functionName); |
| return nullptr; |
| } |
| |
| return adoptRef(new GPUTexture(WTFMove(mtlTexture), usage)); |
| } |
| |
| Ref<GPUTexture> GPUTexture::create(RetainPtr<MTLTexture>&& texture, OptionSet<GPUTextureUsage::Flags> usage) |
| { |
| return adoptRef(*new GPUTexture(WTFMove(texture), usage)); |
| } |
| |
| GPUTexture::GPUTexture(RetainPtr<MTLTexture>&& texture, OptionSet<GPUTextureUsage::Flags> usage) |
| : m_platformTexture(WTFMove(texture)) |
| , m_usage(usage) |
| { |
| m_platformUsage = MTLResourceUsageRead; |
| if (isSampled()) |
| m_platformUsage |= MTLResourceUsageSample; |
| else if (isStorage()) |
| m_platformUsage |= MTLResourceUsageWrite; |
| } |
| |
| RefPtr<GPUTexture> GPUTexture::tryCreateDefaultTextureView() |
| { |
| RetainPtr<MTLTexture> texture; |
| |
| BEGIN_BLOCK_OBJC_EXCEPTIONS; |
| |
| texture = adoptNS([m_platformTexture newTextureViewWithPixelFormat:m_platformTexture.get().pixelFormat]); |
| |
| END_BLOCK_OBJC_EXCEPTIONS; |
| |
| if (!texture) { |
| LOG(WebGPU, "GPUTexture::createDefaultTextureView(): Unable to create MTLTexture view!"); |
| return nullptr; |
| } |
| |
| return GPUTexture::create(WTFMove(texture), m_usage); |
| } |
| |
| } // namespace WebCore |
| |
| #endif // ENABLE(WEBGPU) |