Commit 2ec3e0a1 by Le Hoang Quyen Committed by Commit Bot

Metal: Support importing external metal textures

via KHR_image_base and ANGLE_metal_texture_client_buffer Bug: angleproject:5763 Change-Id: I4d4a88cfbb77d8b7508b787c7fec44073d3b11b0 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2757811 Commit-Queue: Le Hoang Quyen <le.hoang.q@gmail.com> Reviewed-by: 's avatarKenneth Russell <kbr@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 46769f82
Name
ANGLE_metal_texture_client_buffer
Name Strings
EGL_ANGLE_metal_texture_client_buffer
Contributors
Le Hoang Quyen
Contacts
Jamie Madill, Google (jmadill 'at' google 'dot' com)
Le Hoang Quyen (lehoangq 'at' gmail.com)
Status
Draft
Version
Version 1, Jul 19, 2020
Number
EGL Extension #??
Dependencies
This extension is written against the wording of the EGL 1.4
Specification.
Overview
This extension allows creating EGL images from external metal texture objects.
New Types
None
New Procedures and Functions
None
New Tokens
Accepted in the <target> parameter of eglCreateImageKHR:
EGL_METAL_TEXTURE_ANGLE 0x34A7
Additions to Chapter 2 of the EGL 1.2 Specification (EGL Operation)
Add to section 2.5.1 "EGLImage Specification" (as defined by the
EGL_KHR_image_base specification), in the description of
eglCreateImageKHR:
"Values accepted for <target> are listed in Table aaa, below.
+----------------------------+-----------------------------------------+
| <target> | Notes |
+----------------------------+-----------------------------------------+
| EGL_METAL_TEXTURE_ANGLE | Used for Metal texture objects |
+----------------------------+-----------------------------------------+
Table aaa. Legal values for eglCreateImageKHR <target> parameter
...
If <target> is EGL_METAL_TEXTURE_ANGLE, <dpy> must be a valid display, <ctx>
must be EGL_NO_CONTEXT, <buffer> must be a pointer to a valid MTLTexture
object (cast into the type EGLClientBuffer), and attributes are ignored.
The width and height of the pbuffer are determined by the width and height
of <buffer>."
If the EGL_ANGLE_device_metal extension is present, the provided Metal texture
object must have been created by the same Metal device queried from the
display. If these requirements are not met, an EGL_BAD_PARAMETER error is
generated."
Revision History
Version 1, 2020/19/07 - First draft
...@@ -228,6 +228,11 @@ EGLAPI EGLint EGLAPIENTRY eglProgramCacheResizeANGLE(EGLDisplay dpy, EGLint limi ...@@ -228,6 +228,11 @@ EGLAPI EGLint EGLAPIENTRY eglProgramCacheResizeANGLE(EGLDisplay dpy, EGLint limi
#define EGL_BIND_TO_TEXTURE_TARGET_ANGLE 0x348D #define EGL_BIND_TO_TEXTURE_TARGET_ANGLE 0x348D
#endif /* EGL_ANGLE_iosurface_client_buffer */ #endif /* EGL_ANGLE_iosurface_client_buffer */
#ifndef ANGLE_metal_texture_client_buffer
#define ANGLE_metal_texture_client_buffer 1
#define EGL_METAL_TEXTURE_ANGLE 0x34A7
#endif /* ANGLE_metal_texture_client_buffer */
#ifndef EGL_ANGLE_create_context_extensions_enabled #ifndef EGL_ANGLE_create_context_extensions_enabled
#define EGL_ANGLE_create_context_extensions_enabled 1 #define EGL_ANGLE_create_context_extensions_enabled 1
#define EGL_EXTENSIONS_ENABLED_ANGLE 0x345F #define EGL_EXTENSIONS_ENABLED_ANGLE 0x345F
......
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
"src/libANGLE/renderer/angle_format_map.json": "src/libANGLE/renderer/angle_format_map.json":
"ae32dfce450f7b7ffc62d746d6fddf83", "ae32dfce450f7b7ffc62d746d6fddf83",
"src/libANGLE/renderer/metal/gen_mtl_format_table.py": "src/libANGLE/renderer/metal/gen_mtl_format_table.py":
"ee0b5db245a6672b86b122426a16d395", "433b86d27cf7b7d7ab58b41c1f16104b",
"src/libANGLE/renderer/metal/mtl_format_map.json": "src/libANGLE/renderer/metal/mtl_format_map.json":
"848c1e2f6d76115e3d0499346b5684d5", "848c1e2f6d76115e3d0499346b5684d5",
"src/libANGLE/renderer/metal/mtl_format_table_autogen.mm": "src/libANGLE/renderer/metal/mtl_format_table_autogen.mm":
"7765df9be304bf08d1c1c510c7b171ac" "6412a844f457e4fcc5502fbdd8729fea"
} }
\ No newline at end of file
...@@ -1233,6 +1233,7 @@ bool IsExternalImageTarget(EGLenum target) ...@@ -1233,6 +1233,7 @@ bool IsExternalImageTarget(EGLenum target)
case EGL_NATIVE_BUFFER_ANDROID: case EGL_NATIVE_BUFFER_ANDROID:
case EGL_D3D11_TEXTURE_ANGLE: case EGL_D3D11_TEXTURE_ANGLE:
case EGL_LINUX_DMA_BUF_EXT: case EGL_LINUX_DMA_BUF_EXT:
case EGL_METAL_TEXTURE_ANGLE:
return true; return true;
default: default:
......
...@@ -1452,6 +1452,7 @@ std::vector<std::string> DisplayExtensions::getStrings() const ...@@ -1452,6 +1452,7 @@ std::vector<std::string> DisplayExtensions::getStrings() const
InsertExtensionString("EGL_ANGLE_program_cache_control", programCacheControl, &extensionStrings); InsertExtensionString("EGL_ANGLE_program_cache_control", programCacheControl, &extensionStrings);
InsertExtensionString("EGL_ANGLE_robust_resource_initialization", robustResourceInitialization, &extensionStrings); InsertExtensionString("EGL_ANGLE_robust_resource_initialization", robustResourceInitialization, &extensionStrings);
InsertExtensionString("EGL_ANGLE_iosurface_client_buffer", iosurfaceClientBuffer, &extensionStrings); InsertExtensionString("EGL_ANGLE_iosurface_client_buffer", iosurfaceClientBuffer, &extensionStrings);
InsertExtensionString("EGL_ANGLE_metal_texture_client_buffer", mtlTextureClientBuffer, &extensionStrings);
InsertExtensionString("EGL_ANGLE_create_context_extensions_enabled", createContextExtensionsEnabled, &extensionStrings); InsertExtensionString("EGL_ANGLE_create_context_extensions_enabled", createContextExtensionsEnabled, &extensionStrings);
InsertExtensionString("EGL_ANDROID_presentation_time", presentationTime, &extensionStrings); InsertExtensionString("EGL_ANDROID_presentation_time", presentationTime, &extensionStrings);
InsertExtensionString("EGL_ANDROID_blob_cache", blobCache, &extensionStrings); InsertExtensionString("EGL_ANDROID_blob_cache", blobCache, &extensionStrings);
......
...@@ -1135,6 +1135,9 @@ struct DisplayExtensions ...@@ -1135,6 +1135,9 @@ struct DisplayExtensions
// EGL_ANGLE_iosurface_client_buffer // EGL_ANGLE_iosurface_client_buffer
bool iosurfaceClientBuffer = false; bool iosurfaceClientBuffer = false;
// EGL_ANGLE_metal_texture_client_buffer
bool mtlTextureClientBuffer = false;
// EGL_ANGLE_create_context_extensions_enabled // EGL_ANGLE_create_context_extensions_enabled
bool createContextExtensionsEnabled = false; bool createContextExtensionsEnabled = false;
......
...@@ -23,6 +23,8 @@ _metal_backend_sources = [ ...@@ -23,6 +23,8 @@ _metal_backend_sources = [
"DisplayMtl_api.h", "DisplayMtl_api.h",
"FrameBufferMtl.h", "FrameBufferMtl.h",
"FrameBufferMtl.mm", "FrameBufferMtl.mm",
"ImageMtl.h",
"ImageMtl.mm",
"ProgramMtl.h", "ProgramMtl.h",
"ProgramMtl.mm", "ProgramMtl.mm",
"QueryMtl.h", "QueryMtl.h",
......
...@@ -84,6 +84,10 @@ class DisplayMtl : public DisplayImpl ...@@ -84,6 +84,10 @@ class DisplayMtl : public DisplayImpl
ShareGroupImpl *createShareGroup() override; ShareGroupImpl *createShareGroup() override;
ExternalImageSiblingImpl *createExternalImageSibling(const gl::Context *context,
EGLenum target,
EGLClientBuffer buffer,
const egl::AttributeMap &attribs) override;
gl::Version getMaxSupportedESVersion() const override; gl::Version getMaxSupportedESVersion() const override;
gl::Version getMaxConformantESVersion() const override; gl::Version getMaxConformantESVersion() const override;
...@@ -103,6 +107,11 @@ class DisplayMtl : public DisplayImpl ...@@ -103,6 +107,11 @@ class DisplayMtl : public DisplayImpl
EGLClientBuffer clientBuffer, EGLClientBuffer clientBuffer,
const egl::AttributeMap &attribs) const override; const egl::AttributeMap &attribs) const override;
egl::Error validateImageClientBuffer(const gl::Context *context,
EGLenum target,
EGLClientBuffer clientBuffer,
const egl::AttributeMap &attribs) const override;
egl::ConfigSet generateConfigs() override; egl::ConfigSet generateConfigs() override;
gl::Caps getNativeCaps() const; gl::Caps getNativeCaps() const;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "libANGLE/renderer/glslang_wrapper_utils.h" #include "libANGLE/renderer/glslang_wrapper_utils.h"
#include "libANGLE/renderer/metal/ContextMtl.h" #include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/DeviceMtl.h" #include "libANGLE/renderer/metal/DeviceMtl.h"
#include "libANGLE/renderer/metal/ImageMtl.h"
#include "libANGLE/renderer/metal/SurfaceMtl.h" #include "libANGLE/renderer/metal/SurfaceMtl.h"
#include "libANGLE/renderer/metal/SyncMtl.h" #include "libANGLE/renderer/metal/SyncMtl.h"
#include "libANGLE/renderer/metal/mtl_common.h" #include "libANGLE/renderer/metal/mtl_common.h"
...@@ -229,8 +230,7 @@ ImageImpl *DisplayMtl::createImage(const egl::ImageState &state, ...@@ -229,8 +230,7 @@ ImageImpl *DisplayMtl::createImage(const egl::ImageState &state,
EGLenum target, EGLenum target,
const egl::AttributeMap &attribs) const egl::AttributeMap &attribs)
{ {
UNIMPLEMENTED(); return new ImageMtl(state, context);
return nullptr;
} }
rx::ContextImpl *DisplayMtl::createContext(const gl::State &state, rx::ContextImpl *DisplayMtl::createContext(const gl::State &state,
...@@ -255,6 +255,22 @@ ShareGroupImpl *DisplayMtl::createShareGroup() ...@@ -255,6 +255,22 @@ ShareGroupImpl *DisplayMtl::createShareGroup()
return new ShareGroupMtl(); return new ShareGroupMtl();
} }
ExternalImageSiblingImpl *DisplayMtl::createExternalImageSibling(const gl::Context *context,
EGLenum target,
EGLClientBuffer buffer,
const egl::AttributeMap &attribs)
{
switch (target)
{
case EGL_METAL_TEXTURE_ANGLE:
return new TextureImageSiblingMtl(buffer);
default:
UNREACHABLE();
return nullptr;
}
}
gl::Version DisplayMtl::getMaxSupportedESVersion() const gl::Version DisplayMtl::getMaxSupportedESVersion() const
{ {
// NOTE(hqle): Supports GLES 3.0 on iOS GPU family 4+ for now. // NOTE(hqle): Supports GLES 3.0 on iOS GPU family 4+ for now.
...@@ -295,6 +311,7 @@ void DisplayMtl::generateExtensions(egl::DisplayExtensions *outExtensions) const ...@@ -295,6 +311,7 @@ void DisplayMtl::generateExtensions(egl::DisplayExtensions *outExtensions) const
outExtensions->surfacelessContext = true; outExtensions->surfacelessContext = true;
outExtensions->displayTextureShareGroup = true; outExtensions->displayTextureShareGroup = true;
outExtensions->displaySemaphoreShareGroup = true; outExtensions->displaySemaphoreShareGroup = true;
outExtensions->mtlTextureClientBuffer = true;
if (mFeatures.hasEvents.enabled) if (mFeatures.hasEvents.enabled)
{ {
...@@ -307,6 +324,10 @@ void DisplayMtl::generateExtensions(egl::DisplayExtensions *outExtensions) const ...@@ -307,6 +324,10 @@ void DisplayMtl::generateExtensions(egl::DisplayExtensions *outExtensions) const
// this extension so that ANGLE can be initialized in Chrome. WebGL will fail to use // this extension so that ANGLE can be initialized in Chrome. WebGL will fail to use
// this extension (anglebug.com/4929) // this extension (anglebug.com/4929)
outExtensions->robustResourceInitialization = true; outExtensions->robustResourceInitialization = true;
// EGL_KHR_image
outExtensions->image = true;
outExtensions->imageBase = true;
} }
void DisplayMtl::generateCaps(egl::Caps *outCaps) const {} void DisplayMtl::generateCaps(egl::Caps *outCaps) const {}
...@@ -439,6 +460,26 @@ egl::Error DisplayMtl::validateClientBuffer(const egl::Config *configuration, ...@@ -439,6 +460,26 @@ egl::Error DisplayMtl::validateClientBuffer(const egl::Config *configuration,
return egl::NoError(); return egl::NoError();
} }
egl::Error DisplayMtl::validateImageClientBuffer(const gl::Context *context,
EGLenum target,
EGLClientBuffer clientBuffer,
const egl::AttributeMap &attribs) const
{
switch (target)
{
case EGL_METAL_TEXTURE_ANGLE:
if (!TextureImageSiblingMtl::ValidateClientBuffer(this, clientBuffer))
{
return egl::EglBadAttribute();
}
break;
default:
UNREACHABLE();
return egl::EglBadAttribute();
}
return egl::NoError();
}
gl::Caps DisplayMtl::getNativeCaps() const gl::Caps DisplayMtl::getNativeCaps() const
{ {
ensureCapsInitialized(); ensureCapsInitialized();
...@@ -649,7 +690,7 @@ void DisplayMtl::initializeExtensions() const ...@@ -649,7 +690,7 @@ void DisplayMtl::initializeExtensions() const
// Enable EXT_blend_minmax // Enable EXT_blend_minmax
mNativeExtensions.blendMinMax = true; mNativeExtensions.blendMinMax = true;
mNativeExtensions.eglImageOES = false; mNativeExtensions.eglImageOES = true;
mNativeExtensions.eglImageExternalOES = false; mNativeExtensions.eglImageExternalOES = false;
// NOTE(hqle): Support GL_OES_EGL_image_external_essl3. // NOTE(hqle): Support GL_OES_EGL_image_external_essl3.
mNativeExtensions.eglImageExternalEssl3OES = false; mNativeExtensions.eglImageExternalEssl3OES = false;
......
//
// 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.h:
// Defines the class interface for ImageMtl, implementing ImageImpl.
//
#ifndef LIBANGLE_RENDERER_METAL_IMAGEMTL_H
#define LIBANGLE_RENDERER_METAL_IMAGEMTL_H
#include "libANGLE/renderer/ImageImpl.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
namespace rx
{
class DisplayMtl;
class TextureImageSiblingMtl : public ExternalImageSiblingImpl
{
public:
TextureImageSiblingMtl(EGLClientBuffer buffer);
~TextureImageSiblingMtl() override;
static bool ValidateClientBuffer(const DisplayMtl *display, EGLClientBuffer buffer);
egl::Error initialize(const egl::Display *display) override;
void onDestroy(const egl::Display *display) override;
// ExternalImageSiblingImpl interface
gl::Format getFormat() const override;
bool isRenderable(const gl::Context *context) const override;
bool isTexturable(const gl::Context *context) const override;
gl::Extents getSize() const override;
size_t getSamples() const override;
bool isYUV() const override;
const mtl::TextureRef &getTexture() const { return mNativeTexture; }
const mtl::Format &getFormatMtl() const { return mFormat; }
private:
angle::Result initImpl(DisplayMtl *display);
EGLClientBuffer mBuffer;
gl::Format mGLFormat;
mtl::Format mFormat;
bool mRenderable = false;
bool mTextureable = false;
mtl::TextureRef mNativeTexture;
};
class ImageMtl : public ImageImpl
{
public:
ImageMtl(const egl::ImageState &state, const gl::Context *context);
~ImageMtl() override;
void onDestroy(const egl::Display *display) override;
egl::Error initialize(const egl::Display *display) override;
angle::Result orphan(const gl::Context *context, egl::ImageSibling *sibling) override;
const mtl::TextureRef &getTexture() const { return mNativeTexture; }
gl::TextureType getImageTextureType() const { return mImageTextureType; }
uint32_t getImageLevel() const { return mImageLevel; }
uint32_t getImageLayer() const { return mImageLayer; }
private:
gl::TextureType mImageTextureType;
uint32_t mImageLevel = 0;
uint32_t mImageLayer = 0;
mtl::TextureRef mNativeTexture;
};
} // namespace rx
#endif /* LIBANGLE_RENDERER_METAL_IMAGEMTL_H */
//
// 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
{
// TextureImageSiblingMtl implementation
TextureImageSiblingMtl::TextureImageSiblingMtl(EGLClientBuffer buffer)
: mBuffer(buffer), mGLFormat(GL_NONE)
{}
TextureImageSiblingMtl::~TextureImageSiblingMtl() {}
// Static
bool TextureImageSiblingMtl::ValidateClientBuffer(const DisplayMtl *display, EGLClientBuffer buffer)
{
id<MTLTexture> texture = (__bridge id<MTLTexture>)(buffer);
if (!texture || texture.device != display->getMetalDevice())
{
return false;
}
if (texture.textureType != MTLTextureType2D && texture.textureType != MTLTextureTypeCube)
{
return false;
}
angle::FormatID angleFormatId = mtl::Format::MetalToAngleFormatID(texture.pixelFormat);
const mtl::Format &format = display->getPixelFormat(angleFormatId);
if (!format.valid())
{
ERR() << "Unrecognized format";
// Not supported
return false;
}
return true;
}
egl::Error TextureImageSiblingMtl::initialize(const egl::Display *display)
{
DisplayMtl *displayMtl = mtl::GetImpl(display);
if (initImpl(displayMtl) != angle::Result::Continue)
{
return egl::EglBadParameter();
}
return egl::NoError();
}
angle::Result TextureImageSiblingMtl::initImpl(DisplayMtl *displayMtl)
{
mNativeTexture = mtl::Texture::MakeFromMetal((__bridge id<MTLTexture>)(mBuffer));
angle::FormatID angleFormatId =
mtl::Format::MetalToAngleFormatID(mNativeTexture->pixelFormat());
mFormat = displayMtl->getPixelFormat(angleFormatId);
mGLFormat = gl::Format(mFormat.intendedAngleFormat().glInternalFormat);
mRenderable = mFormat.getCaps().depthRenderable || mFormat.getCaps().colorRenderable;
mTextureable = mFormat.getCaps().filterable || mFormat.hasDepthOrStencilBits();
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;
}
// 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:
mImageTextureType = gl::TextureType::_2D;
break;
case MTLTextureTypeCube:
mImageTextureType = gl::TextureType::CubeMap;
break;
default:
UNREACHABLE();
}
mImageLevel = 0;
mImageLayer = 0;
}
else
{
UNREACHABLE();
return egl::EglBadAccess();
}
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
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "libANGLE/renderer/metal/RenderBufferMtl.h" #include "libANGLE/renderer/metal/RenderBufferMtl.h"
#include "libANGLE/renderer/metal/ContextMtl.h" #include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/ImageMtl.h"
#include "libANGLE/renderer/metal/mtl_format_utils.h" #include "libANGLE/renderer/metal/mtl_format_utils.h"
#include "libANGLE/renderer/metal/mtl_utils.h" #include "libANGLE/renderer/metal/mtl_utils.h"
...@@ -122,9 +123,20 @@ angle::Result RenderbufferMtl::setStorageMultisample(const gl::Context *context, ...@@ -122,9 +123,20 @@ angle::Result RenderbufferMtl::setStorageMultisample(const gl::Context *context,
angle::Result RenderbufferMtl::setStorageEGLImageTarget(const gl::Context *context, angle::Result RenderbufferMtl::setStorageEGLImageTarget(const gl::Context *context,
egl::Image *image) egl::Image *image)
{ {
// NOTE(hqle): Support EGLimage releaseTexture();
UNIMPLEMENTED();
return angle::Result::Stop; ContextMtl *contextMtl = mtl::GetImpl(context);
ImageMtl *imageMtl = mtl::GetImpl(image);
mTexture = imageMtl->getTexture();
const angle::FormatID angleFormatId =
angle::Format::InternalFormatToID(image->getFormat().info->sizedInternalFormat);
mFormat = contextMtl->getPixelFormat(angleFormatId);
mRenderTarget.set(mTexture, mtl::kZeroNativeMipLevel, 0, mFormat);
return angle::Result::Continue;
} }
angle::Result RenderbufferMtl::getAttachmentRenderTarget(const gl::Context *context, angle::Result RenderbufferMtl::getAttachmentRenderTarget(const gl::Context *context,
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "libANGLE/renderer/metal/ContextMtl.h" #include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/DisplayMtl.h" #include "libANGLE/renderer/metal/DisplayMtl.h"
#include "libANGLE/renderer/metal/FrameBufferMtl.h" #include "libANGLE/renderer/metal/FrameBufferMtl.h"
#include "libANGLE/renderer/metal/ImageMtl.h"
#include "libANGLE/renderer/metal/SamplerMtl.h" #include "libANGLE/renderer/metal/SamplerMtl.h"
#include "libANGLE/renderer/metal/SurfaceMtl.h" #include "libANGLE/renderer/metal/SurfaceMtl.h"
#include "libANGLE/renderer/metal/mtl_common.h" #include "libANGLE/renderer/metal/mtl_common.h"
...@@ -1081,9 +1082,32 @@ angle::Result TextureMtl::setEGLImageTarget(const gl::Context *context, ...@@ -1081,9 +1082,32 @@ angle::Result TextureMtl::setEGLImageTarget(const gl::Context *context,
gl::TextureType type, gl::TextureType type,
egl::Image *image) egl::Image *image)
{ {
UNIMPLEMENTED(); releaseTexture(true);
return angle::Result::Stop; ContextMtl *contextMtl = mtl::GetImpl(context);
ImageMtl *imageMtl = mtl::GetImpl(image);
if (type != imageMtl->getImageTextureType())
{
return angle::Result::Stop;
}
mNativeTexture = imageMtl->getTexture();
const angle::FormatID angleFormatId =
angle::Format::InternalFormatToID(image->getFormat().info->sizedInternalFormat);
mFormat = contextMtl->getPixelFormat(angleFormatId);
mSlices = mNativeTexture->cubeFacesOrArrayLength();
gl::Extents size = mNativeTexture->sizeAt0();
mIsPow2 = gl::isPow2(size.width) && gl::isPow2(size.height) && gl::isPow2(size.depth);
ANGLE_TRY(ensureSamplerStateCreated(context));
// Tell context to rebind textures
contextMtl->invalidateCurrentTextures();
return angle::Result::Continue;
} }
angle::Result TextureMtl::setImageExternal(const gl::Context *context, angle::Result TextureMtl::setImageExternal(const gl::Context *context,
......
...@@ -44,6 +44,15 @@ namespace rx ...@@ -44,6 +44,15 @@ namespace rx
namespace mtl namespace mtl
{{ {{
angle::FormatID Format::MetalToAngleFormatID(MTLPixelFormat formatMtl)
{{
// Actual conversion
switch (formatMtl)
{{
{mtl_pixel_format_switch}
}}
}}
void Format::init(const DisplayMtl *display, angle::FormatID intendedFormatId_) void Format::init(const DisplayMtl *display, angle::FormatID intendedFormatId_)
{{ {{
this->intendedFormatId = intendedFormatId_; this->intendedFormatId = intendedFormatId_;
...@@ -133,6 +142,10 @@ case_image_format_template2 = """ case angle::FormatID::{angle_format}: ...@@ -133,6 +142,10 @@ case_image_format_template2 = """ case angle::FormatID::{angle_format}:
""" """
case_image_mtl_to_angle_template = """ case {mtl_format}:
return angle::FormatID::{angle_format};
"""
case_vertex_format_template1 = """ case angle::FormatID::{angle_format}: case_vertex_format_template1 = """ case angle::FormatID::{angle_format}:
this->metalFormat = {mtl_format}; this->metalFormat = {mtl_format};
this->actualFormatId = angle::FormatID::{actual_angle_format}; this->actualFormatId = angle::FormatID::{actual_angle_format};
...@@ -277,6 +290,8 @@ def gen_image_map_switch_mac_case(angle_format, actual_angle_format_info, angle_ ...@@ -277,6 +290,8 @@ def gen_image_map_switch_mac_case(angle_format, actual_angle_format_info, angle_
# This format requires fallback when depth24Stencil8PixelFormatSupported flag is false. # This format requires fallback when depth24Stencil8PixelFormatSupported flag is false.
# Fallback format: # Fallback format:
actual_angle_format_fallback = mac_fallbacks[actual_angle_format] actual_angle_format_fallback = mac_fallbacks[actual_angle_format]
fallback_condition = "metalDevice.depth24Stencil8PixelFormatSupported && \
!display->getFeatures().forceD24S8AsUnsupported.enabled"
# return if else block: # return if else block:
return image_format_assign_template2.format( return image_format_assign_template2.format(
actual_angle_format=actual_angle_format, actual_angle_format=actual_angle_format,
...@@ -287,8 +302,7 @@ def gen_image_map_switch_mac_case(angle_format, actual_angle_format_info, angle_ ...@@ -287,8 +302,7 @@ def gen_image_map_switch_mac_case(angle_format, actual_angle_format_info, angle_
mtl_format_fallback=angle_to_mtl_map[actual_angle_format_fallback], mtl_format_fallback=angle_to_mtl_map[actual_angle_format_fallback],
init_function_fallback=angle_format_utils.get_internal_format_initializer( init_function_fallback=angle_format_utils.get_internal_format_initializer(
gl_format, actual_angle_format_fallback), gl_format, actual_angle_format_fallback),
fallback_condition="metalDevice.depth24Stencil8PixelFormatSupported && \ fallback_condition=fallback_condition)
!display->getFeatures().forceD24S8AsUnsupported.enabled")
else: else:
# return ordinary block: # return ordinary block:
return image_format_assign_template1.format( return image_format_assign_template1.format(
...@@ -399,6 +413,40 @@ def gen_image_map_switch_string(image_table, angle_to_gl): ...@@ -399,6 +413,40 @@ def gen_image_map_switch_string(image_table, angle_to_gl):
return switch_data return switch_data
def gen_image_mtl_to_angle_switch_string(image_table):
angle_to_mtl = image_table["map"]
mac_specific_map = image_table["map_mac"]
ios_specific_map = image_table["map_ios"]
switch_data = ''
# Common case
for angle_format in sorted(angle_to_mtl.keys()):
switch_data += case_image_mtl_to_angle_template.format(
mtl_format=angle_to_mtl[angle_format], angle_format=angle_format)
# Mac specific
switch_data += "#if TARGET_OS_OSX || TARGET_OS_MACCATALYST\n"
for angle_format in sorted(mac_specific_map.keys()):
switch_data += case_image_mtl_to_angle_template.format(
mtl_format=mac_specific_map[angle_format], angle_format=angle_format)
switch_data += "#endif // TARGET_OS_OSX || TARGET_OS_MACCATALYST\n"
# iOS + macOS 11.0+ specific
switch_data += "#if TARGET_OS_IOS || TARGET_OS_TV || (TARGET_OS_OSX && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 101600))\n"
for angle_format in sorted(ios_specific_map.keys()):
# ETC1_R8G8B8_UNORM_BLOCK is a duplicated of ETC2_R8G8B8_UNORM_BLOCK
if angle_format == 'ETC1_R8G8B8_UNORM_BLOCK':
continue
switch_data += case_image_mtl_to_angle_template.format(
mtl_format=ios_specific_map[angle_format], angle_format=angle_format)
switch_data += "#endif // TARGET_OS_IOS || TARGET_OS_TV || mac 11.0+\n"
switch_data += " default:\n"
switch_data += " return angle::FormatID::NONE;\n"
return switch_data
def gen_vertex_map_switch_case(angle_fmt, actual_angle_fmt, angle_to_mtl_map, override_packed_map): def gen_vertex_map_switch_case(angle_fmt, actual_angle_fmt, angle_to_mtl_map, override_packed_map):
mtl_format = angle_to_mtl_map[actual_angle_fmt] mtl_format = angle_to_mtl_map[actual_angle_fmt]
copy_function, default_alpha, same_gl_type = get_vertex_copy_function_and_default_alpha( copy_function, default_alpha, same_gl_type = get_vertex_copy_function_and_default_alpha(
...@@ -524,6 +572,7 @@ def main(): ...@@ -524,6 +572,7 @@ def main():
map_vertex = map_json["vertex"] map_vertex = map_json["vertex"]
image_switch_data = gen_image_map_switch_string(map_image, angle_to_gl) image_switch_data = gen_image_map_switch_string(map_image, angle_to_gl)
image_mtl_to_angle_switch_data = gen_image_mtl_to_angle_switch_string(map_image)
vertex_switch_data = gen_vertex_map_switch_string(map_vertex) vertex_switch_data = gen_vertex_map_switch_string(map_vertex)
...@@ -533,6 +582,7 @@ def main(): ...@@ -533,6 +582,7 @@ def main():
script_name=sys.argv[0], script_name=sys.argv[0],
data_source_name=data_source_name, data_source_name=data_source_name,
angle_image_format_switch=image_switch_data, angle_image_format_switch=image_switch_data,
mtl_pixel_format_switch=image_mtl_to_angle_switch_data,
angle_vertex_format_switch=vertex_switch_data, angle_vertex_format_switch=vertex_switch_data,
metal_format_caps=caps_init_str) metal_format_caps=caps_init_str)
with open('mtl_format_table_autogen.mm', 'wt') as out_file: with open('mtl_format_table_autogen.mm', 'wt') as out_file:
......
...@@ -97,6 +97,7 @@ class DisplayMtl; ...@@ -97,6 +97,7 @@ class DisplayMtl;
class ContextMtl; class ContextMtl;
class FramebufferMtl; class FramebufferMtl;
class BufferMtl; class BufferMtl;
class ImageMtl;
class VertexArrayMtl; class VertexArrayMtl;
class TextureMtl; class TextureMtl;
class ProgramMtl; class ProgramMtl;
...@@ -220,6 +221,12 @@ struct ImplTypeHelper<egl::Display> ...@@ -220,6 +221,12 @@ struct ImplTypeHelper<egl::Display>
using ImplType = DisplayMtl; using ImplType = DisplayMtl;
}; };
template <>
struct ImplTypeHelper<egl::Image>
{
using ImplType = ImageMtl;
};
template <typename T> template <typename T>
using GetImplType = typename ImplTypeHelper<T>::ImplType; using GetImplType = typename ImplTypeHelper<T>::ImplType;
......
...@@ -25,6 +25,248 @@ namespace rx ...@@ -25,6 +25,248 @@ namespace rx
namespace mtl namespace mtl
{ {
angle::FormatID Format::MetalToAngleFormatID(MTLPixelFormat formatMtl)
{
// Actual conversion
switch (formatMtl)
{
case MTLPixelFormatA8Unorm:
return angle::FormatID::A8_UNORM;
case MTLPixelFormatBGR10A2Unorm:
return angle::FormatID::B10G10R10A2_UNORM;
case MTLPixelFormatBGRA8Unorm:
return angle::FormatID::B8G8R8A8_UNORM;
case MTLPixelFormatBGRA8Unorm_sRGB:
return angle::FormatID::B8G8R8A8_UNORM_SRGB;
case MTLPixelFormatDepth32Float:
return angle::FormatID::D32_FLOAT;
case MTLPixelFormatDepth32Float_Stencil8:
return angle::FormatID::D32_FLOAT_S8X24_UINT;
case MTLPixelFormatInvalid:
return angle::FormatID::NONE;
case MTLPixelFormatRGB10A2Uint:
return angle::FormatID::R10G10B10A2_UINT;
case MTLPixelFormatRGB10A2Unorm:
return angle::FormatID::R10G10B10A2_UNORM;
case MTLPixelFormatRG11B10Float:
return angle::FormatID::R11G11B10_FLOAT;
case MTLPixelFormatRGBA16Float:
return angle::FormatID::R16G16B16A16_FLOAT;
case MTLPixelFormatRGBA16Sint:
return angle::FormatID::R16G16B16A16_SINT;
case MTLPixelFormatRGBA16Snorm:
return angle::FormatID::R16G16B16A16_SNORM;
case MTLPixelFormatRGBA16Uint:
return angle::FormatID::R16G16B16A16_UINT;
case MTLPixelFormatRGBA16Unorm:
return angle::FormatID::R16G16B16A16_UNORM;
case MTLPixelFormatRG16Float:
return angle::FormatID::R16G16_FLOAT;
case MTLPixelFormatRG16Sint:
return angle::FormatID::R16G16_SINT;
case MTLPixelFormatRG16Snorm:
return angle::FormatID::R16G16_SNORM;
case MTLPixelFormatRG16Uint:
return angle::FormatID::R16G16_UINT;
case MTLPixelFormatRG16Unorm:
return angle::FormatID::R16G16_UNORM;
case MTLPixelFormatR16Float:
return angle::FormatID::R16_FLOAT;
case MTLPixelFormatR16Sint:
return angle::FormatID::R16_SINT;
case MTLPixelFormatR16Snorm:
return angle::FormatID::R16_SNORM;
case MTLPixelFormatR16Uint:
return angle::FormatID::R16_UINT;
case MTLPixelFormatR16Unorm:
return angle::FormatID::R16_UNORM;
case MTLPixelFormatRGBA32Float:
return angle::FormatID::R32G32B32A32_FLOAT;
case MTLPixelFormatRGBA32Sint:
return angle::FormatID::R32G32B32A32_SINT;
case MTLPixelFormatRGBA32Uint:
return angle::FormatID::R32G32B32A32_UINT;
case MTLPixelFormatRG32Float:
return angle::FormatID::R32G32_FLOAT;
case MTLPixelFormatRG32Sint:
return angle::FormatID::R32G32_SINT;
case MTLPixelFormatRG32Uint:
return angle::FormatID::R32G32_UINT;
case MTLPixelFormatR32Float:
return angle::FormatID::R32_FLOAT;
case MTLPixelFormatR32Sint:
return angle::FormatID::R32_SINT;
case MTLPixelFormatR32Uint:
return angle::FormatID::R32_UINT;
case MTLPixelFormatRGBA8Sint:
return angle::FormatID::R8G8B8A8_SINT;
case MTLPixelFormatRGBA8Snorm:
return angle::FormatID::R8G8B8A8_SNORM;
case MTLPixelFormatRGBA8Uint:
return angle::FormatID::R8G8B8A8_UINT;
case MTLPixelFormatRGBA8Unorm:
return angle::FormatID::R8G8B8A8_UNORM;
case MTLPixelFormatRGBA8Unorm_sRGB:
return angle::FormatID::R8G8B8A8_UNORM_SRGB;
case MTLPixelFormatRG8Sint:
return angle::FormatID::R8G8_SINT;
case MTLPixelFormatRG8Snorm:
return angle::FormatID::R8G8_SNORM;
case MTLPixelFormatRG8Uint:
return angle::FormatID::R8G8_UINT;
case MTLPixelFormatRG8Unorm:
return angle::FormatID::R8G8_UNORM;
case MTLPixelFormatR8Sint:
return angle::FormatID::R8_SINT;
case MTLPixelFormatR8Snorm:
return angle::FormatID::R8_SNORM;
case MTLPixelFormatR8Uint:
return angle::FormatID::R8_UINT;
case MTLPixelFormatR8Unorm:
return angle::FormatID::R8_UNORM;
case MTLPixelFormatRGB9E5Float:
return angle::FormatID::R9G9B9E5_SHAREDEXP;
case MTLPixelFormatStencil8:
return angle::FormatID::S8_UINT;
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
case MTLPixelFormatBC1_RGBA:
return angle::FormatID::BC1_RGBA_UNORM_BLOCK;
case MTLPixelFormatBC1_RGBA_sRGB:
return angle::FormatID::BC1_RGBA_UNORM_SRGB_BLOCK;
case MTLPixelFormatBC2_RGBA:
return angle::FormatID::BC2_RGBA_UNORM_BLOCK;
case MTLPixelFormatBC2_RGBA_sRGB:
return angle::FormatID::BC2_RGBA_UNORM_SRGB_BLOCK;
case MTLPixelFormatBC3_RGBA:
return angle::FormatID::BC3_RGBA_UNORM_BLOCK;
case MTLPixelFormatBC3_RGBA_sRGB:
return angle::FormatID::BC3_RGBA_UNORM_SRGB_BLOCK;
case MTLPixelFormatBC4_RSnorm:
return angle::FormatID::BC4_RED_SNORM_BLOCK;
case MTLPixelFormatBC4_RUnorm:
return angle::FormatID::BC4_RED_UNORM_BLOCK;
case MTLPixelFormatBC5_RGSnorm:
return angle::FormatID::BC5_RG_SNORM_BLOCK;
case MTLPixelFormatBC5_RGUnorm:
return angle::FormatID::BC5_RG_UNORM_BLOCK;
case MTLPixelFormatBC7_RGBAUnorm:
return angle::FormatID::BPTC_RGBA_UNORM_BLOCK;
case MTLPixelFormatBC6H_RGBFloat:
return angle::FormatID::BPTC_RGB_SIGNED_FLOAT_BLOCK;
case MTLPixelFormatBC6H_RGBUfloat:
return angle::FormatID::BPTC_RGB_UNSIGNED_FLOAT_BLOCK;
case MTLPixelFormatBC7_RGBAUnorm_sRGB:
return angle::FormatID::BPTC_SRGB_ALPHA_UNORM_BLOCK;
case MTLPixelFormatDepth16Unorm:
return angle::FormatID::D16_UNORM;
case MTLPixelFormatDepth24Unorm_Stencil8:
return angle::FormatID::D24_UNORM_S8_UINT;
#endif // TARGET_OS_OSX || TARGET_OS_MACCATALYST
#if TARGET_OS_IOS || TARGET_OS_TV || (TARGET_OS_OSX && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 101600))
case MTLPixelFormatASTC_10x10_sRGB:
return angle::FormatID::ASTC_10x10_SRGB_BLOCK;
case MTLPixelFormatASTC_10x10_LDR:
return angle::FormatID::ASTC_10x10_UNORM_BLOCK;
case MTLPixelFormatASTC_10x5_sRGB:
return angle::FormatID::ASTC_10x5_SRGB_BLOCK;
case MTLPixelFormatASTC_10x5_LDR:
return angle::FormatID::ASTC_10x5_UNORM_BLOCK;
case MTLPixelFormatASTC_10x6_sRGB:
return angle::FormatID::ASTC_10x6_SRGB_BLOCK;
case MTLPixelFormatASTC_10x6_LDR:
return angle::FormatID::ASTC_10x6_UNORM_BLOCK;
case MTLPixelFormatASTC_10x8_sRGB:
return angle::FormatID::ASTC_10x8_SRGB_BLOCK;
case MTLPixelFormatASTC_10x8_LDR:
return angle::FormatID::ASTC_10x8_UNORM_BLOCK;
case MTLPixelFormatASTC_12x10_sRGB:
return angle::FormatID::ASTC_12x10_SRGB_BLOCK;
case MTLPixelFormatASTC_12x10_LDR:
return angle::FormatID::ASTC_12x10_UNORM_BLOCK;
case MTLPixelFormatASTC_12x12_sRGB:
return angle::FormatID::ASTC_12x12_SRGB_BLOCK;
case MTLPixelFormatASTC_12x12_LDR:
return angle::FormatID::ASTC_12x12_UNORM_BLOCK;
case MTLPixelFormatASTC_4x4_sRGB:
return angle::FormatID::ASTC_4x4_SRGB_BLOCK;
case MTLPixelFormatASTC_4x4_LDR:
return angle::FormatID::ASTC_4x4_UNORM_BLOCK;
case MTLPixelFormatASTC_5x4_sRGB:
return angle::FormatID::ASTC_5x4_SRGB_BLOCK;
case MTLPixelFormatASTC_5x4_LDR:
return angle::FormatID::ASTC_5x4_UNORM_BLOCK;
case MTLPixelFormatASTC_5x5_sRGB:
return angle::FormatID::ASTC_5x5_SRGB_BLOCK;
case MTLPixelFormatASTC_5x5_LDR:
return angle::FormatID::ASTC_5x5_UNORM_BLOCK;
case MTLPixelFormatASTC_6x5_sRGB:
return angle::FormatID::ASTC_6x5_SRGB_BLOCK;
case MTLPixelFormatASTC_6x5_LDR:
return angle::FormatID::ASTC_6x5_UNORM_BLOCK;
case MTLPixelFormatASTC_6x6_sRGB:
return angle::FormatID::ASTC_6x6_SRGB_BLOCK;
case MTLPixelFormatASTC_6x6_LDR:
return angle::FormatID::ASTC_6x6_UNORM_BLOCK;
case MTLPixelFormatASTC_8x5_sRGB:
return angle::FormatID::ASTC_8x5_SRGB_BLOCK;
case MTLPixelFormatASTC_8x5_LDR:
return angle::FormatID::ASTC_8x5_UNORM_BLOCK;
case MTLPixelFormatASTC_8x6_sRGB:
return angle::FormatID::ASTC_8x6_SRGB_BLOCK;
case MTLPixelFormatASTC_8x6_LDR:
return angle::FormatID::ASTC_8x6_UNORM_BLOCK;
case MTLPixelFormatASTC_8x8_sRGB:
return angle::FormatID::ASTC_8x8_SRGB_BLOCK;
case MTLPixelFormatASTC_8x8_LDR:
return angle::FormatID::ASTC_8x8_UNORM_BLOCK;
case MTLPixelFormatEAC_RG11Snorm:
return angle::FormatID::EAC_R11G11_SNORM_BLOCK;
case MTLPixelFormatEAC_RG11Unorm:
return angle::FormatID::EAC_R11G11_UNORM_BLOCK;
case MTLPixelFormatEAC_R11Snorm:
return angle::FormatID::EAC_R11_SNORM_BLOCK;
case MTLPixelFormatEAC_R11Unorm:
return angle::FormatID::EAC_R11_UNORM_BLOCK;
case MTLPixelFormatETC2_RGB8A1_sRGB:
return angle::FormatID::ETC2_R8G8B8A1_SRGB_BLOCK;
case MTLPixelFormatETC2_RGB8A1:
return angle::FormatID::ETC2_R8G8B8A1_UNORM_BLOCK;
case MTLPixelFormatEAC_RGBA8_sRGB:
return angle::FormatID::ETC2_R8G8B8A8_SRGB_BLOCK;
case MTLPixelFormatEAC_RGBA8:
return angle::FormatID::ETC2_R8G8B8A8_UNORM_BLOCK;
case MTLPixelFormatETC2_RGB8_sRGB:
return angle::FormatID::ETC2_R8G8B8_SRGB_BLOCK;
case MTLPixelFormatETC2_RGB8:
return angle::FormatID::ETC2_R8G8B8_UNORM_BLOCK;
case MTLPixelFormatPVRTC_RGBA_2BPP:
return angle::FormatID::PVRTC1_RGBA_2BPP_UNORM_BLOCK;
case MTLPixelFormatPVRTC_RGBA_2BPP_sRGB:
return angle::FormatID::PVRTC1_RGBA_2BPP_UNORM_SRGB_BLOCK;
case MTLPixelFormatPVRTC_RGBA_4BPP:
return angle::FormatID::PVRTC1_RGBA_4BPP_UNORM_BLOCK;
case MTLPixelFormatPVRTC_RGBA_4BPP_sRGB:
return angle::FormatID::PVRTC1_RGBA_4BPP_UNORM_SRGB_BLOCK;
case MTLPixelFormatPVRTC_RGB_2BPP:
return angle::FormatID::PVRTC1_RGB_2BPP_UNORM_BLOCK;
case MTLPixelFormatPVRTC_RGB_2BPP_sRGB:
return angle::FormatID::PVRTC1_RGB_2BPP_UNORM_SRGB_BLOCK;
case MTLPixelFormatPVRTC_RGB_4BPP:
return angle::FormatID::PVRTC1_RGB_4BPP_UNORM_BLOCK;
case MTLPixelFormatPVRTC_RGB_4BPP_sRGB:
return angle::FormatID::PVRTC1_RGB_4BPP_UNORM_SRGB_BLOCK;
case MTLPixelFormatABGR4Unorm:
return angle::FormatID::R4G4B4A4_UNORM;
case MTLPixelFormatA1BGR5Unorm:
return angle::FormatID::R5G5B5A1_UNORM;
case MTLPixelFormatB5G6R5Unorm:
return angle::FormatID::R5G6B5_UNORM;
#endif // TARGET_OS_IOS || TARGET_OS_TV || mac 11.0+
default:
return angle::FormatID::NONE;
}
}
void Format::init(const DisplayMtl *display, angle::FormatID intendedFormatId_) void Format::init(const DisplayMtl *display, angle::FormatID intendedFormatId_)
{ {
this->intendedFormatId = intendedFormatId_; this->intendedFormatId = intendedFormatId_;
......
...@@ -62,6 +62,8 @@ struct Format : public FormatBase ...@@ -62,6 +62,8 @@ struct Format : public FormatBase
{ {
Format() = default; Format() = default;
static angle::FormatID MetalToAngleFormatID(MTLPixelFormat formatMtl);
const gl::InternalFormat &intendedInternalFormat() const; const gl::InternalFormat &intendedInternalFormat() const;
const gl::InternalFormat &actualInternalFormat() const; const gl::InternalFormat &actualInternalFormat() const;
......
...@@ -3399,6 +3399,26 @@ bool ValidateCreateImage(const ValidationContext *val, ...@@ -3399,6 +3399,26 @@ bool ValidateCreateImage(const ValidationContext *val,
} }
break; break;
case EGL_METAL_TEXTURE_ANGLE:
if (!displayExtensions.mtlTextureClientBuffer)
{
val->setError(EGL_BAD_PARAMETER,
"EGL_ANGLE_metal_texture_client_buffer not supported.");
return false;
}
if (context != nullptr)
{
val->setError(EGL_BAD_CONTEXT, "ctx must be EGL_NO_CONTEXT.");
return false;
}
ANGLE_EGL_TRY_RETURN(
val->eglThread,
display->validateImageClientBuffer(context, target, buffer, attributes),
val->entryPoint, val->labeledObject, false);
break;
default: default:
val->setError(EGL_BAD_PARAMETER, "invalid target: 0x%X", target); val->setError(EGL_BAD_PARAMETER, "invalid target: 0x%X", target);
return false; return false;
......
...@@ -173,6 +173,14 @@ if (is_win || is_linux || is_chromeos || is_android || is_fuchsia || is_apple) { ...@@ -173,6 +173,14 @@ if (is_win || is_linux || is_chromeos || is_android || is_fuchsia || is_apple) {
"CoreFoundation.framework", "CoreFoundation.framework",
"IOSurface.framework", "IOSurface.framework",
] ]
ldflags = [
"-weak_framework",
"Metal",
]
cflags_objcc = [
"-Wno-nullability-completeness",
"-Wno-unguarded-availability",
]
} }
if (is_win) { if (is_win) {
sources += angle_end2end_tests_win_sources sources += angle_end2end_tests_win_sources
......
...@@ -187,6 +187,7 @@ angle_end2end_tests_mac_sources = [ ...@@ -187,6 +187,7 @@ angle_end2end_tests_mac_sources = [
"egl_tests/EGLDeviceCGLTest.cpp", "egl_tests/EGLDeviceCGLTest.cpp",
"egl_tests/EGLIOSurfaceClientBufferTest.cpp", "egl_tests/EGLIOSurfaceClientBufferTest.cpp",
"egl_tests/EGLSurfaceTestMac.mm", "egl_tests/EGLSurfaceTestMac.mm",
"gl_tests/ImageTestMetal.mm",
] ]
angle_end2end_tests_win_sources = [ angle_end2end_tests_win_sources = [
"egl_tests/EGLDeviceTest.cpp", "egl_tests/EGLDeviceTest.cpp",
......
...@@ -1003,6 +1003,18 @@ TEST_P(ImageTest, ANGLEExtensionAvailability) ...@@ -1003,6 +1003,18 @@ TEST_P(ImageTest, ANGLEExtensionAvailability)
EXPECT_FALSE(hasExternalESSL3Ext()); EXPECT_FALSE(hasExternalESSL3Ext());
} }
} }
else if (IsMetal())
{
// NOTE(hqle): Metal currently doesn't implement any image extensions besides
// EGL_ANGLE_metal_texture_client_buffer
EXPECT_TRUE(hasOESExt());
EXPECT_TRUE(hasBaseExt());
EXPECT_FALSE(hasExternalExt());
EXPECT_FALSE(hasExternalESSL3Ext());
EXPECT_FALSE(has2DTextureExt());
EXPECT_FALSE(has3DTextureExt());
EXPECT_FALSE(hasRenderbufferExt());
}
else else
{ {
EXPECT_FALSE(hasOESExt()); EXPECT_FALSE(hasOESExt());
......
//
// 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.
//
// ImageTestMetal:
// Tests the correctness of eglImage with native Metal texture extensions.
//
#include "test_utils/ANGLETest.h"
#include "common/mathutil.h"
#include "test_utils/gl_raii.h"
#include "util/EGLWindow.h"
#include <CoreFoundation/CoreFoundation.h>
#include <Metal/Metal.h>
namespace angle
{
namespace
{
constexpr char kOESExt[] = "GL_OES_EGL_image";
constexpr char kBaseExt[] = "EGL_KHR_image_base";
constexpr char kDeviceMtlExt[] = "EGL_ANGLE_device_metal";
constexpr char kEGLMtlImageNativeTextureExt[] = "EGL_ANGLE_metal_texture_client_buffer";
constexpr EGLint kDefaultAttribs[] = {
EGL_NONE,
};
} // anonymous namespace
class ScopeMetalTextureRef : angle::NonCopyable
{
public:
explicit ScopeMetalTextureRef(id<MTLTexture> surface) : mSurface(surface) {}
~ScopeMetalTextureRef()
{
if (mSurface)
{
release();
mSurface = nullptr;
}
}
id<MTLTexture> get() const { return mSurface; }
// auto cast to MTLTexture
operator id<MTLTexture>() const { return mSurface; }
ScopeMetalTextureRef(const ScopeMetalTextureRef &other)
{
if (mSurface)
{
release();
}
mSurface = other.mSurface;
}
explicit ScopeMetalTextureRef(ScopeMetalTextureRef &&other)
{
if (mSurface)
{
release();
}
mSurface = other.mSurface;
other.mSurface = nil;
}
ScopeMetalTextureRef &operator=(ScopeMetalTextureRef &&other)
{
if (mSurface)
{
release();
}
mSurface = other.mSurface;
other.mSurface = nil;
return *this;
}
ScopeMetalTextureRef &operator=(const ScopeMetalTextureRef &other)
{
if (mSurface)
{
release();
}
mSurface = other.mSurface;
return *this;
}
private:
void release()
{
#if !__has_feature(objc_arc)
[mSurface release];
#endif
}
id<MTLTexture> mSurface = nil;
};
ScopeMetalTextureRef CreateMetalTexture2D(id<MTLDevice> deviceMtl,
int width,
int height,
MTLPixelFormat format)
{
@autoreleasepool
{
MTLTextureDescriptor *desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format
width:width
height:width
mipmapped:NO];
desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
id<MTLTexture> texture = [deviceMtl newTextureWithDescriptor:desc];
ScopeMetalTextureRef re(texture);
return re;
}
}
class ImageTestMetal : public ANGLETest
{
protected:
ImageTestMetal()
{
setWindowWidth(128);
setWindowHeight(128);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
setConfigDepthBits(24);
}
void testSetUp() override
{
constexpr char kVS[] = "precision highp float;\n"
"attribute vec4 position;\n"
"varying vec2 texcoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = position;\n"
" texcoord = (position.xy * 0.5) + 0.5;\n"
" texcoord.y = 1.0 - texcoord.y;\n"
"}\n";
constexpr char kTextureFS[] = "precision highp float;\n"
"uniform sampler2D tex;\n"
"varying vec2 texcoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_FragColor = texture2D(tex, texcoord);\n"
"}\n";
mTextureProgram = CompileProgram(kVS, kTextureFS);
if (mTextureProgram == 0)
{
FAIL() << "shader compilation failed.";
}
mTextureUniformLocation = glGetUniformLocation(mTextureProgram, "tex");
ASSERT_GL_NO_ERROR();
}
void testTearDown() override { glDeleteProgram(mTextureProgram); }
id<MTLDevice> getMtlDevice()
{
EGLAttrib angleDevice = 0;
EGLAttrib device = 0;
EXPECT_EGL_TRUE(
eglQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice));
EXPECT_EGL_TRUE(eglQueryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(angleDevice),
EGL_METAL_DEVICE_ANGLE, &device));
return (__bridge id<MTLDevice>)reinterpret_cast<void *>(device);
}
ScopeMetalTextureRef createMtlTexture2D(int width, int height, MTLPixelFormat format)
{
id<MTLDevice> device = getMtlDevice();
return CreateMetalTexture2D(device, width, height, format);
}
void sourceMetalTarget2D_helper(GLubyte data[4],
const EGLint *attribs,
EGLImageKHR *imageOut,
GLuint *textureOut);
void verifyResultsTexture(GLuint texture,
GLubyte data[4],
GLenum textureTarget,
GLuint program,
GLuint textureUniform)
{
// Draw a quad with the target texture
glUseProgram(program);
glBindTexture(textureTarget, texture);
glUniform1i(textureUniform, 0);
drawQuad(program, "position", 0.5f);
// Expect that the rendered quad has the same color as the source texture
EXPECT_PIXEL_NEAR(0, 0, data[0], data[1], data[2], data[3], 1.0);
}
void verifyResults2D(GLuint texture, GLubyte data[4])
{
verifyResultsTexture(texture, data, GL_TEXTURE_2D, mTextureProgram,
mTextureUniformLocation);
}
template <typename destType, typename sourcetype>
destType reinterpretHelper(sourcetype source)
{
static_assert(sizeof(destType) == sizeof(size_t),
"destType should be the same size as a size_t");
size_t sourceSizeT = static_cast<size_t>(source);
return reinterpret_cast<destType>(sourceSizeT);
}
bool hasImageNativeMetalTextureExt() const
{
if (!IsMetal())
{
return false;
}
EGLAttrib angleDevice = 0;
eglQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice);
if (!angleDevice)
{
return false;
}
auto extensionString = static_cast<const char *>(
eglQueryDeviceStringEXT(reinterpret_cast<EGLDeviceEXT>(angleDevice), EGL_EXTENSIONS));
if (strstr(extensionString, kDeviceMtlExt) == nullptr)
{
return false;
}
return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(),
kEGLMtlImageNativeTextureExt);
}
bool hasOESExt() const { return IsGLExtensionEnabled(kOESExt); }
bool hasBaseExt() const
{
return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), kBaseExt);
}
GLuint mTextureProgram;
GLint mTextureUniformLocation;
};
void ImageTestMetal::sourceMetalTarget2D_helper(GLubyte data[4],
const EGLint *attribs,
EGLImageKHR *imageOut,
GLuint *textureOut)
{
EGLWindow *window = getEGLWindow();
// Create MTLTexture
ScopeMetalTextureRef textureMtl = createMtlTexture2D(1, 1, MTLPixelFormatRGBA8Unorm);
// Create image
EGLImageKHR image =
eglCreateImageKHR(window->getDisplay(), EGL_NO_CONTEXT, EGL_METAL_TEXTURE_ANGLE,
reinterpret_cast<EGLClientBuffer>(textureMtl.get()), attribs);
ASSERT_EGL_SUCCESS();
// Write the data to the MTLTexture
[textureMtl.get() replaceRegion:MTLRegionMake2D(0, 0, 1, 1)
mipmapLevel:0
slice:0
withBytes:data
bytesPerRow:4
bytesPerImage:0];
// Create a texture target to bind the egl image
GLuint target;
glGenTextures(1, &target);
glBindTexture(GL_TEXTURE_2D, target);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
*imageOut = image;
*textureOut = target;
}
// Testing source metal EGL image, target 2D texture
TEST_P(ImageTestMetal, SourceMetalTarget2D)
{
ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt());
ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt());
EGLWindow *window = getEGLWindow();
// Create the Image
EGLImageKHR image;
GLuint texTarget;
GLubyte data[4] = {7, 51, 197, 231};
sourceMetalTarget2D_helper(data, kDefaultAttribs, &image, &texTarget);
// Use texture target bound to egl image as source and render to framebuffer
// Verify that data in framebuffer matches that in the egl image
verifyResults2D(texTarget, data);
// Clean up
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteTextures(1, &texTarget);
}
// Create source metal EGL image, target 2D texture, then trigger texture respecification.
TEST_P(ImageTestMetal, SourceMetal2DTargetTextureRespecifySize)
{
ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt());
ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt());
EGLWindow *window = getEGLWindow();
// Create the Image
EGLImageKHR image;
GLuint texTarget;
GLubyte data[4] = {7, 51, 197, 231};
sourceMetalTarget2D_helper(data, kDefaultAttribs, &image, &texTarget);
// Use texture target bound to egl image as source and render to framebuffer
// Verify that data in framebuffer matches that in the egl image
verifyResults2D(texTarget, data);
// Respecify texture size and verify results
std::array<GLubyte, 16> referenceColor;
referenceColor.fill(127);
glBindTexture(GL_TEXTURE_2D, texTarget);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
referenceColor.data());
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
referenceColor.data());
ASSERT_GL_NO_ERROR();
// Expect that the target texture has the reference color values
verifyResults2D(texTarget, referenceColor.data());
// Clean up
eglDestroyImageKHR(window->getDisplay(), image);
glDeleteTextures(1, &texTarget);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST(ImageTestMetal, ES2_METAL(), ES3_METAL());
} // namespace angle
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment