// // Copyright 2021 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // ImageMtl.cpp: // Implements the class methods for ImageMtl. // #include "libANGLE/renderer/metal/ImageMtl.h" #include "common/debug.h" #include "libANGLE/Context.h" #include "libANGLE/Display.h" #include "libANGLE/renderer/metal/ContextMtl.h" #include "libANGLE/renderer/metal/DisplayMtl.h" #include "libANGLE/renderer/metal/RenderBufferMtl.h" #include "libANGLE/renderer/metal/TextureMtl.h" namespace rx { namespace { angle::FormatID intendedFormatForMTLTexture(id texture, const egl::AttributeMap &attribs) { angle::FormatID angleFormatId = mtl::Format::MetalToAngleFormatID(texture.pixelFormat); if (angleFormatId == angle::FormatID::NONE) { return angle::FormatID::NONE; } const angle::Format *textureAngleFormat = &angle::Format::Get(angleFormatId); ASSERT(textureAngleFormat); GLenum sizedInternalFormat = textureAngleFormat->glInternalFormat; if (attribs.contains(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE)) { const GLenum internalFormat = static_cast(attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE)); GLenum type = gl::GetSizedInternalFormatInfo(sizedInternalFormat).type; const auto format = gl::Format(internalFormat, type); if (!format.valid()) { return angle::FormatID::NONE; } sizedInternalFormat = format.info->sizedInternalFormat; } return angle::Format::InternalFormatToID(sizedInternalFormat); } } // anonymous namespace // TextureImageSiblingMtl implementation TextureImageSiblingMtl::TextureImageSiblingMtl(EGLClientBuffer buffer, const egl::AttributeMap &attribs) : mBuffer(buffer), mAttribs(attribs), mGLFormat(GL_NONE) {} TextureImageSiblingMtl::~TextureImageSiblingMtl() {} // Static egl::Error TextureImageSiblingMtl::ValidateClientBuffer(const DisplayMtl *display, EGLClientBuffer buffer, const egl::AttributeMap &attribs) { id texture = (__bridge id)(buffer); if (!texture || texture.device != display->getMetalDevice()) { return egl::Error(EGL_BAD_ATTRIBUTE); } if (texture.textureType != MTLTextureType2D && texture.textureType != MTLTextureTypeCube && texture.textureType != MTLTextureType2DArray) { return egl::Error(EGL_BAD_ATTRIBUTE); } angle::FormatID angleFormatId = intendedFormatForMTLTexture(texture, attribs); const mtl::Format &format = display->getPixelFormat(angleFormatId); if (!format.valid()) { return egl::Error(EGL_BAD_ATTRIBUTE, "Unrecognized format"); } if (format.metalFormat != texture.pixelFormat) { return egl::Error(EGL_BAD_ATTRIBUTE, "Incompatible format"); } unsigned textureArraySlice = static_cast(attribs.getAsInt(EGL_METAL_TEXTURE_ARRAY_SLICE_ANGLE, 0)); if (texture.textureType != MTLTextureType2DArray && textureArraySlice > 0) { return egl::Error(EGL_BAD_ATTRIBUTE, "Invalid texture type for non-zero texture array slice"); } if (textureArraySlice >= texture.arrayLength) { std::ostringstream err; err << "Invalid texture array slice: " << textureArraySlice; return egl::Error(EGL_BAD_ATTRIBUTE, err.str()); } return egl::NoError(); } egl::Error TextureImageSiblingMtl::initialize(const egl::Display *display) { DisplayMtl *displayMtl = mtl::GetImpl(display); if (initImpl(displayMtl) != angle::Result::Continue) { return egl::Error(EGL_BAD_PARAMETER); } return egl::NoError(); } angle::Result TextureImageSiblingMtl::initImpl(DisplayMtl *displayMtl) { mNativeTexture = mtl::Texture::MakeFromMetal((__bridge id)(mBuffer)); if (mNativeTexture->textureType() == MTLTextureType2DArray) { mtl::TextureRef baseTexture = std::move(mNativeTexture); unsigned textureArraySlice = static_cast(mAttribs.getAsInt(EGL_METAL_TEXTURE_ARRAY_SLICE_ANGLE, 0)); mNativeTexture = baseTexture->createSliceMipView(textureArraySlice, mtl::kZeroNativeMipLevel); } angle::FormatID angleFormatId = intendedFormatForMTLTexture(mNativeTexture->get(), mAttribs); mFormat = displayMtl->getPixelFormat(angleFormatId); if (mNativeTexture) { size_t resourceSize = EstimateTextureSizeInBytes( mFormat, mNativeTexture->widthAt0(), mNativeTexture->heightAt0(), mNativeTexture->depthAt0(), mNativeTexture->samples(), mNativeTexture->mipmapLevels()); mNativeTexture->setEstimatedByteSize(resourceSize); } mGLFormat = gl::Format(mFormat.intendedAngleFormat().glInternalFormat); mRenderable = mFormat.getCaps().depthRenderable || mFormat.getCaps().colorRenderable; // Some formats are not filterable but renderable such as integer formats. In this case, treat // them as texturable as well. mTextureable = mFormat.getCaps().filterable || mRenderable; return angle::Result::Continue; } void TextureImageSiblingMtl::onDestroy(const egl::Display *display) { mNativeTexture = nullptr; } gl::Format TextureImageSiblingMtl::getFormat() const { return mGLFormat; } bool TextureImageSiblingMtl::isRenderable(const gl::Context *context) const { return mRenderable; } bool TextureImageSiblingMtl::isTexturable(const gl::Context *context) const { return mTextureable; } gl::Extents TextureImageSiblingMtl::getSize() const { return mNativeTexture ? mNativeTexture->sizeAt0() : gl::Extents(0, 0, 0); } size_t TextureImageSiblingMtl::getSamples() const { uint32_t samples = mNativeTexture ? mNativeTexture->samples() : 0; return samples > 1 ? samples : 0; } bool TextureImageSiblingMtl::isYUV() const { // NOTE(hqle): not supporting YUV image yet. return false; } bool TextureImageSiblingMtl::hasProtectedContent() const { return false; } // ImageMtl implementation ImageMtl::ImageMtl(const egl::ImageState &state, const gl::Context *context) : ImageImpl(state) {} ImageMtl::~ImageMtl() {} void ImageMtl::onDestroy(const egl::Display *display) { mNativeTexture = nullptr; } egl::Error ImageMtl::initialize(const egl::Display *display) { if (mState.target == EGL_METAL_TEXTURE_ANGLE) { const TextureImageSiblingMtl *externalImageSibling = GetImplAs(GetAs(mState.source)); mNativeTexture = externalImageSibling->getTexture(); switch (mNativeTexture->textureType()) { case MTLTextureType2D: case MTLTextureType2DArray: mImageTextureType = gl::TextureType::_2D; break; case MTLTextureTypeCube: mImageTextureType = gl::TextureType::CubeMap; break; default: UNREACHABLE(); } mImageLevel = 0; mImageLayer = 0; } else { UNREACHABLE(); return egl::Error(EGL_BAD_ACCESS); } return egl::NoError(); } angle::Result ImageMtl::orphan(const gl::Context *context, egl::ImageSibling *sibling) { if (sibling == mState.source) { mNativeTexture = nullptr; } return angle::Result::Continue; } } // namespace rx