blob: 9d7701b329aa7320d5ca844aa46bb74ac94d0e19 [file] [log] [blame]
// Copyright 2017 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dawn_native/metal/TextureMTL.h"
#include "common/Platform.h"
#include "dawn_native/metal/DeviceMTL.h"
namespace dawn_native { namespace metal {
namespace {
bool UsageNeedsTextureView(wgpu::TextureUsage usage) {
constexpr wgpu::TextureUsage kUsageNeedsTextureView =
wgpu::TextureUsage::Storage | wgpu::TextureUsage::Sampled;
return usage & kUsageNeedsTextureView;
}
MTLTextureUsage MetalTextureUsage(wgpu::TextureUsage usage) {
MTLTextureUsage result = MTLTextureUsageUnknown; // This is 0
if (usage & (wgpu::TextureUsage::Storage)) {
result |= MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead;
}
if (usage & (wgpu::TextureUsage::Sampled)) {
result |= MTLTextureUsageShaderRead;
}
if (usage & (wgpu::TextureUsage::OutputAttachment)) {
result |= MTLTextureUsageRenderTarget;
}
if (UsageNeedsTextureView(usage)) {
result |= MTLTextureUsagePixelFormatView;
}
return result;
}
MTLTextureType MetalTextureType(wgpu::TextureDimension dimension,
unsigned int arrayLayers,
unsigned int sampleCount) {
switch (dimension) {
case wgpu::TextureDimension::e2D:
if (sampleCount > 1) {
ASSERT(arrayLayers == 1);
return MTLTextureType2DMultisample;
} else {
return (arrayLayers > 1) ? MTLTextureType2DArray : MTLTextureType2D;
}
default:
UNREACHABLE();
}
}
MTLTextureType MetalTextureViewType(wgpu::TextureViewDimension dimension,
unsigned int sampleCount) {
switch (dimension) {
case wgpu::TextureViewDimension::e2D:
return (sampleCount > 1) ? MTLTextureType2DMultisample : MTLTextureType2D;
case wgpu::TextureViewDimension::e2DArray:
return MTLTextureType2DArray;
case wgpu::TextureViewDimension::Cube:
return MTLTextureTypeCube;
case wgpu::TextureViewDimension::CubeArray:
return MTLTextureTypeCubeArray;
default:
UNREACHABLE();
return MTLTextureType2D;
}
}
bool RequiresCreatingNewTextureView(const TextureBase* texture,
const TextureViewDescriptor* textureViewDescriptor) {
if (texture->GetFormat().format != textureViewDescriptor->format) {
return true;
}
if (texture->GetArrayLayers() != textureViewDescriptor->arrayLayerCount) {
return true;
}
if (texture->GetNumMipLevels() != textureViewDescriptor->mipLevelCount) {
return true;
}
switch (textureViewDescriptor->dimension) {
case wgpu::TextureViewDimension::Cube:
case wgpu::TextureViewDimension::CubeArray:
return true;
default:
break;
}
return false;
}
ResultOrError<wgpu::TextureFormat> GetFormatEquivalentToIOSurfaceFormat(uint32_t format) {
switch (format) {
case 'RGBA':
return wgpu::TextureFormat::RGBA8Unorm;
case 'BGRA':
return wgpu::TextureFormat::BGRA8Unorm;
case '2C08':
return wgpu::TextureFormat::RG8Unorm;
case 'L008':
return wgpu::TextureFormat::R8Unorm;
default:
return DAWN_VALIDATION_ERROR("Unsupported IOSurface format");
}
}
#if defined(DAWN_PLATFORM_MACOS)
MTLStorageMode kIOSurfaceStorageMode = MTLStorageModeManaged;
#elif defined(DAWN_PLATFORM_IOS)
MTLStorageMode kIOSurfaceStorageMode = MTLStorageModePrivate;
#else
# error "Unsupported Apple platform."
#endif
}
MTLPixelFormat MetalPixelFormat(wgpu::TextureFormat format) {
switch (format) {
case wgpu::TextureFormat::R8Unorm:
return MTLPixelFormatR8Unorm;
case wgpu::TextureFormat::R8Snorm:
return MTLPixelFormatR8Snorm;
case wgpu::TextureFormat::R8Uint:
return MTLPixelFormatR8Uint;
case wgpu::TextureFormat::R8Sint:
return MTLPixelFormatR8Sint;
case wgpu::TextureFormat::R16Uint:
return MTLPixelFormatR16Uint;
case wgpu::TextureFormat::R16Sint:
return MTLPixelFormatR16Sint;
case wgpu::TextureFormat::R16Float:
return MTLPixelFormatR16Float;
case wgpu::TextureFormat::RG8Unorm:
return MTLPixelFormatRG8Unorm;
case wgpu::TextureFormat::RG8Snorm:
return MTLPixelFormatRG8Snorm;
case wgpu::TextureFormat::RG8Uint:
return MTLPixelFormatRG8Uint;
case wgpu::TextureFormat::RG8Sint:
return MTLPixelFormatRG8Sint;
case wgpu::TextureFormat::R32Uint:
return MTLPixelFormatR32Uint;
case wgpu::TextureFormat::R32Sint:
return MTLPixelFormatR32Sint;
case wgpu::TextureFormat::R32Float:
return MTLPixelFormatR32Float;
case wgpu::TextureFormat::RG16Uint:
return MTLPixelFormatRG16Uint;
case wgpu::TextureFormat::RG16Sint:
return MTLPixelFormatRG16Sint;
case wgpu::TextureFormat::RG16Float:
return MTLPixelFormatRG16Float;
case wgpu::TextureFormat::RGBA8Unorm:
return MTLPixelFormatRGBA8Unorm;
case wgpu::TextureFormat::RGBA8UnormSrgb:
return MTLPixelFormatRGBA8Unorm_sRGB;
case wgpu::TextureFormat::RGBA8Snorm:
return MTLPixelFormatRGBA8Snorm;
case wgpu::TextureFormat::RGBA8Uint:
return MTLPixelFormatRGBA8Uint;
case wgpu::TextureFormat::RGBA8Sint:
return MTLPixelFormatRGBA8Sint;
case wgpu::TextureFormat::BGRA8Unorm:
return MTLPixelFormatBGRA8Unorm;
case wgpu::TextureFormat::BGRA8UnormSrgb:
return MTLPixelFormatBGRA8Unorm_sRGB;
case wgpu::TextureFormat::RGB10A2Unorm:
return MTLPixelFormatRGB10A2Unorm;
case wgpu::TextureFormat::RG11B10Float:
return MTLPixelFormatRG11B10Float;
case wgpu::TextureFormat::RG32Uint:
return MTLPixelFormatRG32Uint;
case wgpu::TextureFormat::RG32Sint:
return MTLPixelFormatRG32Sint;
case wgpu::TextureFormat::RG32Float:
return MTLPixelFormatRG32Float;
case wgpu::TextureFormat::RGBA16Uint:
return MTLPixelFormatRGBA16Uint;
case wgpu::TextureFormat::RGBA16Sint:
return MTLPixelFormatRGBA16Sint;
case wgpu::TextureFormat::RGBA16Float:
return MTLPixelFormatRGBA16Float;
case wgpu::TextureFormat::RGBA32Uint:
return MTLPixelFormatRGBA32Uint;
case wgpu::TextureFormat::RGBA32Sint:
return MTLPixelFormatRGBA32Sint;
case wgpu::TextureFormat::RGBA32Float:
return MTLPixelFormatRGBA32Float;
case wgpu::TextureFormat::Depth32Float:
return MTLPixelFormatDepth32Float;
case wgpu::TextureFormat::Depth24Plus:
return MTLPixelFormatDepth32Float;
case wgpu::TextureFormat::Depth24PlusStencil8:
return MTLPixelFormatDepth32Float_Stencil8;
#if defined(DAWN_PLATFORM_MACOS)
case wgpu::TextureFormat::BC1RGBAUnorm:
return MTLPixelFormatBC1_RGBA;
case wgpu::TextureFormat::BC1RGBAUnormSrgb:
return MTLPixelFormatBC1_RGBA_sRGB;
case wgpu::TextureFormat::BC2RGBAUnorm:
return MTLPixelFormatBC2_RGBA;
case wgpu::TextureFormat::BC2RGBAUnormSrgb:
return MTLPixelFormatBC2_RGBA_sRGB;
case wgpu::TextureFormat::BC3RGBAUnorm:
return MTLPixelFormatBC3_RGBA;
case wgpu::TextureFormat::BC3RGBAUnormSrgb:
return MTLPixelFormatBC3_RGBA_sRGB;
case wgpu::TextureFormat::BC4RSnorm:
return MTLPixelFormatBC4_RSnorm;
case wgpu::TextureFormat::BC4RUnorm:
return MTLPixelFormatBC4_RUnorm;
case wgpu::TextureFormat::BC5RGSnorm:
return MTLPixelFormatBC5_RGSnorm;
case wgpu::TextureFormat::BC5RGUnorm:
return MTLPixelFormatBC5_RGUnorm;
case wgpu::TextureFormat::BC6HRGBSfloat:
return MTLPixelFormatBC6H_RGBFloat;
case wgpu::TextureFormat::BC6HRGBUfloat:
return MTLPixelFormatBC6H_RGBUfloat;
case wgpu::TextureFormat::BC7RGBAUnorm:
return MTLPixelFormatBC7_RGBAUnorm;
case wgpu::TextureFormat::BC7RGBAUnormSrgb:
return MTLPixelFormatBC7_RGBAUnorm_sRGB;
#endif
default:
UNREACHABLE();
}
}
MaybeError ValidateIOSurfaceCanBeWrapped(const DeviceBase*,
const TextureDescriptor* descriptor,
IOSurfaceRef ioSurface,
uint32_t plane) {
// IOSurfaceGetPlaneCount can return 0 for non-planar IOSurfaces but we will treat
// non-planar like it is a single plane.
size_t surfacePlaneCount = std::max(size_t(1), IOSurfaceGetPlaneCount(ioSurface));
if (plane >= surfacePlaneCount) {
return DAWN_VALIDATION_ERROR("IOSurface plane doesn't exist");
}
if (descriptor->dimension != wgpu::TextureDimension::e2D) {
return DAWN_VALIDATION_ERROR("IOSurface texture must be 2D");
}
if (descriptor->mipLevelCount != 1) {
return DAWN_VALIDATION_ERROR("IOSurface mip level count must be 1");
}
if (descriptor->arrayLayerCount != 1) {
return DAWN_VALIDATION_ERROR("IOSurface array layer count must be 1");
}
if (descriptor->sampleCount != 1) {
return DAWN_VALIDATION_ERROR("IOSurface sample count must be 1");
}
if (descriptor->size.width != IOSurfaceGetWidthOfPlane(ioSurface, plane) ||
descriptor->size.height != IOSurfaceGetHeightOfPlane(ioSurface, plane) ||
descriptor->size.depth != 1) {
return DAWN_VALIDATION_ERROR("IOSurface size doesn't match descriptor");
}
wgpu::TextureFormat ioSurfaceFormat;
DAWN_TRY_ASSIGN(ioSurfaceFormat,
GetFormatEquivalentToIOSurfaceFormat(IOSurfaceGetPixelFormat(ioSurface)));
if (descriptor->format != ioSurfaceFormat) {
return DAWN_VALIDATION_ERROR("IOSurface format doesn't match descriptor");
}
return {};
}
MTLTextureDescriptor* CreateMetalTextureDescriptor(const TextureDescriptor* descriptor) {
MTLTextureDescriptor* mtlDesc = [MTLTextureDescriptor new];
mtlDesc.textureType = MetalTextureType(descriptor->dimension, descriptor->arrayLayerCount,
descriptor->sampleCount);
mtlDesc.usage = MetalTextureUsage(descriptor->usage);
mtlDesc.pixelFormat = MetalPixelFormat(descriptor->format);
mtlDesc.width = descriptor->size.width;
mtlDesc.height = descriptor->size.height;
mtlDesc.depth = descriptor->size.depth;
mtlDesc.mipmapLevelCount = descriptor->mipLevelCount;
mtlDesc.arrayLength = descriptor->arrayLayerCount;
mtlDesc.storageMode = MTLStorageModePrivate;
mtlDesc.sampleCount = descriptor->sampleCount;
return mtlDesc;
}
Texture::Texture(Device* device, const TextureDescriptor* descriptor)
: TextureBase(device, descriptor, TextureState::OwnedInternal) {
MTLTextureDescriptor* mtlDesc = CreateMetalTextureDescriptor(descriptor);
mMtlTexture = [device->GetMTLDevice() newTextureWithDescriptor:mtlDesc];
[mtlDesc release];
}
Texture::Texture(Device* device, const TextureDescriptor* descriptor, id<MTLTexture> mtlTexture)
: TextureBase(device, descriptor, TextureState::OwnedInternal), mMtlTexture(mtlTexture) {
[mMtlTexture retain];
}
Texture::Texture(Device* device,
const TextureDescriptor* descriptor,
IOSurfaceRef ioSurface,
uint32_t plane)
: TextureBase(device, descriptor, TextureState::OwnedInternal) {
MTLTextureDescriptor* mtlDesc = CreateMetalTextureDescriptor(descriptor);
mtlDesc.storageMode = kIOSurfaceStorageMode;
mMtlTexture = [device->GetMTLDevice() newTextureWithDescriptor:mtlDesc
iosurface:ioSurface
plane:plane];
[mtlDesc release];
}
Texture::~Texture() {
DestroyInternal();
}
void Texture::DestroyImpl() {
if (GetTextureState() == TextureState::OwnedInternal) {
[mMtlTexture release];
mMtlTexture = nil;
}
}
id<MTLTexture> Texture::GetMTLTexture() {
return mMtlTexture;
}
TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)
: TextureViewBase(texture, descriptor) {
id<MTLTexture> mtlTexture = ToBackend(texture)->GetMTLTexture();
if (!UsageNeedsTextureView(texture->GetUsage())) {
mMtlTextureView = nil;
} else if (!RequiresCreatingNewTextureView(texture, descriptor)) {
mMtlTextureView = [mtlTexture retain];
} else {
MTLPixelFormat format = MetalPixelFormat(descriptor->format);
MTLTextureType textureViewType =
MetalTextureViewType(descriptor->dimension, texture->GetSampleCount());
auto mipLevelRange = NSMakeRange(descriptor->baseMipLevel, descriptor->mipLevelCount);
auto arrayLayerRange =
NSMakeRange(descriptor->baseArrayLayer, descriptor->arrayLayerCount);
mMtlTextureView = [mtlTexture newTextureViewWithPixelFormat:format
textureType:textureViewType
levels:mipLevelRange
slices:arrayLayerRange];
}
}
TextureView::~TextureView() {
[mMtlTextureView release];
}
id<MTLTexture> TextureView::GetMTLTexture() {
ASSERT(mMtlTextureView != nil);
return mMtlTextureView;
}
}} // namespace dawn_native::metal