blob: 29f8f59906801cf6218a1517f9b749ce0b1e1d3e [file] [log] [blame]
//
// 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<MTLTexture> 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<GLenum>(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<MTLTexture> texture = (__bridge id<MTLTexture>)(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<unsigned>(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<MTLTexture>)(mBuffer));
if (mNativeTexture->textureType() == MTLTextureType2DArray)
{
mtl::TextureRef baseTexture = std::move(mNativeTexture);
unsigned textureArraySlice =
static_cast<unsigned>(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<TextureImageSiblingMtl>(GetAs<egl::ExternalImageSibling>(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