From 596db81faf44e01dc02d777a9b113d946d11dc8d Mon Sep 17 00:00:00 2001 From: Le Hoang Quyen Date: Fri, 31 Jul 2020 20:12:18 +0800 Subject: [PATCH] Cherry-pick from gles3-dev: WIP: Support importing external metal textures (2D, Cube) via KHR_image_base. Change-Id: I525eb0a0160c67bbbc6baf8107429e08b2d67f6d # Conflicts: # extensions/EGL_MGL_texture_client_buffer.txt # src/libANGLE/Caps.cpp # src/libANGLE/renderer/metal/DisplayMtl.h # src/libANGLE/renderer/metal/DisplayMtl.mm # src/tests/angle_end2end_tests.gni --- extensions/EGL_MGL_texture_client_buffer.txt | 81 ++++ extensions/GL_MGL_EGL_image_cube.txt | 81 ++++ include/EGL/eglext_angle.h | 18 + ios/xcode/OpenGLES.xcodeproj/project.pbxproj | 12 + src/common/utilities.cpp | 1 + src/libANGLE/Caps.cpp | 2 + src/libANGLE/Caps.h | 6 + src/libANGLE/Texture.cpp | 18 +- src/libANGLE/renderer/metal/BUILD.gn | 2 + src/libANGLE/renderer/metal/DisplayMtl.h | 8 + src/libANGLE/renderer/metal/DisplayMtl.mm | 50 ++- src/libANGLE/renderer/metal/ImageMtl.h | 80 ++++ src/libANGLE/renderer/metal/ImageMtl.mm | 170 +++++++ .../renderer/metal/RenderBufferMtl.mm | 18 +- src/libANGLE/renderer/metal/TextureMtl.mm | 28 +- .../renderer/metal/gen_mtl_format_table.py | 46 +- src/libANGLE/renderer/metal/mtl_common.h | 7 + .../metal/mtl_format_table_autogen.mm | 167 +++++++ .../renderer/metal/mtl_format_utils.h | 2 + src/libANGLE/renderer/metal/mtl_resources.h | 1 + src/libANGLE/renderer/metal/mtl_resources.mm | 9 + src/libANGLE/validationEGL.cpp | 14 + src/libANGLE/validationES.cpp | 7 + src/tests/BUILD.gn | 8 + src/tests/angle_end2end_tests.gni | 1 + src/tests/gl_tests/ImageTest.cpp | 2 + src/tests/gl_tests/ImageTestMetal.mm | 425 ++++++++++++++++++ 27 files changed, 1252 insertions(+), 12 deletions(-) create mode 100644 extensions/EGL_MGL_texture_client_buffer.txt create mode 100644 extensions/GL_MGL_EGL_image_cube.txt create mode 100644 src/libANGLE/renderer/metal/ImageMtl.h create mode 100644 src/libANGLE/renderer/metal/ImageMtl.mm create mode 100644 src/tests/gl_tests/ImageTestMetal.mm diff --git a/extensions/EGL_MGL_texture_client_buffer.txt b/extensions/EGL_MGL_texture_client_buffer.txt new file mode 100644 index 0000000000..c74502db2c --- /dev/null +++ b/extensions/EGL_MGL_texture_client_buffer.txt @@ -0,0 +1,81 @@ +Name + + MGL_texture_client_buffer + +Name Strings + + EGL_MGL_mtl_texture_client_buffer + +Contributors + + Le Hoang Quyen + +Contacts + + 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 parameter of eglCreateImageKHR: + + EGL_MTL_TEXTURE_MGL 0x3456 + +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 are listed in Table aaa, below. + + +----------------------------+-----------------------------------------+ + | | Notes | + +----------------------------+-----------------------------------------+ + | EGL_MTL_TEXTURE_MGL | Used for Metal texture objects | + +----------------------------+-----------------------------------------+ + Table aaa. Legal values for eglCreateImageKHR parameter + + ... + + If is EGL_MTL_TEXTURE_MGL, must be a valid display, + must be EGL_NO_CONTEXT, 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 ." + + If the EGL_ANGLE_device_mtl 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 diff --git a/extensions/GL_MGL_EGL_image_cube.txt b/extensions/GL_MGL_EGL_image_cube.txt new file mode 100644 index 0000000000..53b858a501 --- /dev/null +++ b/extensions/GL_MGL_EGL_image_cube.txt @@ -0,0 +1,81 @@ +Name + + MGL_EGL_image_cube + +Name Strings + + GL_MGL_EGL_image_cube + +Contact + + Le Hoang Quyen (lehoangq@gmail.com) + +Contributors + + Le Hoang Quyen + +Status + + Draft + +Version + + Revision: 0.1 + +Number + + OpenGL ES Extension #XXX + +Dependencies + + OpenGL ES 2.0 is required. + + Requires EGL 1.2 and either the EGL_KHR_image or EGL_KHR_image_base + extensions as well as OES_EGL_image. + + This extension is written against the OpenGL ES 2.0 specification and + the OES_EGL_image extension. + +Overview + + This extension adds functionality to that provided by OES_EGL_image in + order to support cube map EGLImage. It extends the existing + EGLImageTargetTexture2DOES entry point from OES_EGL_image. Render buffers + are not extended to include cube map support. + + EGLImage cube map can be created using extended versions of eglCreateImageKHR. + For example, EGL_MGL_mtl_texture_client_buffer can import cube map image native metal textures + on devices where such native textures can be created. + +New Procedures and Functions + + None. + +New Tokens + + None. + +Additions to Chapter 3 of the OpenGL ES 2.0 Specification + + In section 3.8.2 within the specification added by OES_EGL_Image: + + "Currently, must be TEXTURE_2D or TEXTURE_CUBE_MAP." + +Errors + + GL_INVALID_ENUM is generated by EGLImageTargetTexture2DOES if + is not TEXTURE_2D or TEXTURE_CUBE_MAP + + GL_INVALID_OPERATION is generated by EGLImageTargetTexture2DOES if: + - is TEXTURE_CUBE_MAP and is not cube map image. + - is TEXTURE_2D and is not 2d image. + +Issues + + None. + +Revision History + + Rev. Date Author Changes + ---- ---------- -------- ----------------------------------------- + 0.1 07/30/2020 Quyen Initial draft diff --git a/include/EGL/eglext_angle.h b/include/EGL/eglext_angle.h index 85ad56540c..a53cf1f093 100644 --- a/include/EGL/eglext_angle.h +++ b/include/EGL/eglext_angle.h @@ -191,6 +191,24 @@ EGLAPI EGLint EGLAPIENTRY eglProgramCacheResizeANGLE(EGLDisplay dpy, EGLint limi #define EGL_TEXTURE_INTERNAL_FORMAT_ANGLE 0x345D #endif /* EGL_ANGLE_iosurface_client_buffer */ +#ifndef MGL_texture_client_buffer +#define MGL_texture_client_buffer 1 +#define EGL_MTL_TEXTURE_MGL 0x3456 +#define EGL_GL_TEXTURE_MGL 0x3457 +#ifndef EGL_TEXTURE_RECTANGLE_ANGLE +#define EGL_TEXTURE_RECTANGLE_ANGLE 0x345B +#endif +#ifndef EGL_TEXTURE_TYPE_ANGLE +#define EGL_TEXTURE_TYPE_ANGLE 0x345C +#endif +#ifndef EGL_TEXTURE_INTERNAL_FORMAT_ANGLE +#define EGL_TEXTURE_INTERNAL_FORMAT_ANGLE 0x345D +#endif +#ifndef EGL_BIND_TO_TEXTURE_TARGET_ANGLE +#define EGL_BIND_TO_TEXTURE_TARGET_ANGLE 0x348D +#endif +#endif /* MGL_texture_client_buffer */ + #ifndef EGL_ANGLE_create_context_extensions_enabled #define EGL_ANGLE_create_context_extensions_enabled 1 #define EGL_EXTENSIONS_ENABLED_ANGLE 0x345F diff --git a/ios/xcode/OpenGLES.xcodeproj/project.pbxproj b/ios/xcode/OpenGLES.xcodeproj/project.pbxproj index a92ac99308..0401b2565c 100644 --- a/ios/xcode/OpenGLES.xcodeproj/project.pbxproj +++ b/ios/xcode/OpenGLES.xcodeproj/project.pbxproj @@ -2749,6 +2749,10 @@ 0AFDDB91244C9DE1000B60B1 /* libglslang_tvos.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AF956F8244C7C8700F59740 /* libglslang_tvos.a */; }; 0AFDDB92244C9DE1000B60B1 /* libjsoncpp_tvos.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AF956B5244C7BF300F59740 /* libjsoncpp_tvos.a */; }; 0AFDDB93244C9DE1000B60B1 /* libspirv-cross_tvos.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AF956C6244C7C5100F59740 /* libspirv-cross_tvos.a */; }; + 0AFFDCB624D42F7F0015E16A /* ImageMtl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0AFFDCB524D42F760015E16A /* ImageMtl.mm */; }; + 0AFFDCB724D42F820015E16A /* ImageMtl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0AFFDCB524D42F760015E16A /* ImageMtl.mm */; }; + 0AFFDCB824D42F860015E16A /* ImageMtl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0AFFDCB524D42F760015E16A /* ImageMtl.mm */; }; + 0AFFDCB924D42F880015E16A /* ImageMtl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0AFFDCB524D42F760015E16A /* ImageMtl.mm */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -5059,6 +5063,8 @@ 0AF959B3244C7D5800F59740 /* sample_util_tvos.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = sample_util_tvos.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0AF959EB244C7EE900F59740 /* simple_vertex_shader_tvos.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = simple_vertex_shader_tvos.app; sourceTree = BUILT_PRODUCTS_DIR; }; 0AF959ED244C7FF800F59740 /* Info_tvos.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = Info_tvos.plist; path = samples/Info_tvos.plist; sourceTree = ""; }; + 0AFFDCB424D42F760015E16A /* ImageMtl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ImageMtl.h; path = ../../src/libANGLE/renderer/metal/ImageMtl.h; sourceTree = ""; }; + 0AFFDCB524D42F760015E16A /* ImageMtl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = ImageMtl.mm; path = ../../src/libANGLE/renderer/metal/ImageMtl.mm; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -6089,6 +6095,8 @@ 0A605609234655FF005CEA98 /* DisplayMtl.mm */, 0A6055D1234655FC005CEA98 /* FrameBufferMtl.h */, 0A605603234655FE005CEA98 /* FrameBufferMtl.mm */, + 0AFFDCB424D42F760015E16A /* ImageMtl.h */, + 0AFFDCB524D42F760015E16A /* ImageMtl.mm */, 0A605601234655FE005CEA98 /* mtl_glslang_utils.h */, 0A6055D5234655FC005CEA98 /* mtl_glslang_utils.mm */, 0A6055D0234655FC005CEA98 /* mtl_buffer_pool.h */, @@ -10288,6 +10296,7 @@ 0A60564923465667005CEA98 /* ContextMtl.mm in Sources */, 0A60564C23465667005CEA98 /* mtl_glslang_utils.mm in Sources */, 0A60564823465667005CEA98 /* CompilerMtl.mm in Sources */, + 0AFFDCB624D42F7F0015E16A /* ImageMtl.mm in Sources */, 0A60565C23465667005CEA98 /* TextureMtl.mm in Sources */, 0A60565A23465667005CEA98 /* mtl_state_cache.mm in Sources */, 0A60565D23465667005CEA98 /* mtl_render_utils.mm in Sources */, @@ -10514,6 +10523,7 @@ 0A90F76724065B06005BA9A8 /* ContextMtl.mm in Sources */, 0A90F76824065B06005BA9A8 /* mtl_glslang_utils.mm in Sources */, 0A90F76924065B06005BA9A8 /* CompilerMtl.mm in Sources */, + 0AFFDCB724D42F820015E16A /* ImageMtl.mm in Sources */, 0A90F76A24065B06005BA9A8 /* TextureMtl.mm in Sources */, 0A90F76B24065B06005BA9A8 /* mtl_state_cache.mm in Sources */, 0A90F76C24065B06005BA9A8 /* mtl_render_utils.mm in Sources */, @@ -11677,6 +11687,7 @@ 0AA2FFBE234727A400E0B98C /* ContextMtl.mm in Sources */, 0AA2FFBF234727A400E0B98C /* mtl_glslang_utils.mm in Sources */, 0AA2FFC0234727A400E0B98C /* CompilerMtl.mm in Sources */, + 0AFFDCB924D42F880015E16A /* ImageMtl.mm in Sources */, 0AA2FFC1234727A400E0B98C /* TextureMtl.mm in Sources */, 0AA2FFC2234727A400E0B98C /* mtl_state_cache.mm in Sources */, 0AA2FFC4234727A400E0B98C /* mtl_render_utils.mm in Sources */, @@ -11869,6 +11880,7 @@ 0AF95740244C7CC300F59740 /* ContextMtl.mm in Sources */, 0AF95741244C7CC300F59740 /* mtl_glslang_utils.mm in Sources */, 0AF95742244C7CC300F59740 /* CompilerMtl.mm in Sources */, + 0AFFDCB824D42F860015E16A /* ImageMtl.mm in Sources */, 0AF95743244C7CC300F59740 /* TextureMtl.mm in Sources */, 0AF95744244C7CC300F59740 /* mtl_state_cache.mm in Sources */, 0AF95745244C7CC300F59740 /* mtl_render_utils.mm in Sources */, diff --git a/src/common/utilities.cpp b/src/common/utilities.cpp index ad282435d5..849ef72f32 100644 --- a/src/common/utilities.cpp +++ b/src/common/utilities.cpp @@ -1034,6 +1034,7 @@ bool IsExternalImageTarget(EGLenum target) { case EGL_NATIVE_BUFFER_ANDROID: case EGL_D3D11_TEXTURE_ANGLE: + case EGL_MTL_TEXTURE_MGL: return true; default: diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp index 860e0b1de9..2d744153f7 100644 --- a/src/libANGLE/Caps.cpp +++ b/src/libANGLE/Caps.cpp @@ -837,6 +837,7 @@ const ExtensionInfoMap &GetExtensionInfoMap() map["GL_OES_EGL_image"] = enableableExtension(&Extensions::eglImage); map["GL_OES_EGL_image_external"] = enableableExtension(&Extensions::eglImageExternal); map["GL_OES_EGL_image_external_essl3"] = enableableExtension(&Extensions::eglImageExternalEssl3); + map["GL_MGL_EGL_image_cube"] = enableableExtension(&Extensions::eglImageCubeMGL); map["GL_OES_EGL_sync"] = esOnlyExtension(&Extensions::eglSync); map["GL_EXT_memory_object"] = enableableExtension(&Extensions::memoryObject); map["GL_EXT_memory_object_fd"] = enableableExtension(&Extensions::memoryObjectFd); @@ -1245,6 +1246,7 @@ std::vector DisplayExtensions::getStrings() const InsertExtensionString("EGL_ANGLE_program_cache_control", programCacheControl, &extensionStrings); InsertExtensionString("EGL_ANGLE_robust_resource_initialization", robustResourceInitialization, &extensionStrings); InsertExtensionString("EGL_ANGLE_iosurface_client_buffer", iosurfaceClientBuffer, &extensionStrings); + InsertExtensionString("EGL_MGL_mtl_texture_client_buffer", mtlTextureClientBuffer, &extensionStrings); InsertExtensionString("EGL_ANGLE_create_context_extensions_enabled", createContextExtensionsEnabled, &extensionStrings); InsertExtensionString("EGL_ANDROID_presentation_time", presentationTime, &extensionStrings); InsertExtensionString("EGL_ANDROID_blob_cache", blobCache, &extensionStrings); diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h index 846de6dc92..ef2e7cb9cc 100644 --- a/src/libANGLE/Caps.h +++ b/src/libANGLE/Caps.h @@ -361,6 +361,9 @@ struct Extensions // GL_OES_EGL_image_external_essl3 bool eglImageExternalEssl3 = false; + // GL_MGL_EGL_image_cube + bool eglImageCubeMGL = false; + // GL_OES_EGL_sync bool eglSync = false; @@ -908,6 +911,9 @@ struct DisplayExtensions // EGL_ANGLE_iosurface_client_buffer bool iosurfaceClientBuffer = false; + // EGL_mtl_texture_client_buffer + bool mtlTextureClientBuffer = false; + // EGL_ANGLE_create_context_extensions_enabled bool createContextExtensionsEnabled = false; diff --git a/src/libANGLE/Texture.cpp b/src/libANGLE/Texture.cpp index d30611c52f..95a16e655d 100644 --- a/src/libANGLE/Texture.cpp +++ b/src/libANGLE/Texture.cpp @@ -1534,7 +1534,8 @@ angle::Result Texture::setEGLImageTarget(Context *context, egl::Image *imageTarget) { ASSERT(type == mState.mType); - ASSERT(type == TextureType::_2D || type == TextureType::External); + ASSERT(type == TextureType::_2D || type == TextureType::External || + type == TextureType::CubeMap); // Release from previous calls to eglBindTexImage, to avoid calling the Impl after ANGLE_TRY(releaseTexImageInternal(context)); @@ -1550,8 +1551,19 @@ angle::Result Texture::setEGLImageTarget(Context *context, auto initState = imageTarget->sourceInitState(); mState.clearImageDescs(); - mState.setImageDesc(NonCubeTextureTypeToTarget(type), 0, - ImageDesc(size, imageTarget->getFormat(), initState)); + if (type == TextureType::CubeMap) + { + for (int face = 0; face < 6; ++face) + { + mState.setImageDesc(FromGLenum(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), 0, + ImageDesc(size, imageTarget->getFormat(), initState)); + } + } + else + { + mState.setImageDesc(NonCubeTextureTypeToTarget(type), 0, + ImageDesc(size, imageTarget->getFormat(), initState)); + } signalDirtyStorage(initState); return angle::Result::Continue; diff --git a/src/libANGLE/renderer/metal/BUILD.gn b/src/libANGLE/renderer/metal/BUILD.gn index 508a40100c..e916f7d73e 100644 --- a/src/libANGLE/renderer/metal/BUILD.gn +++ b/src/libANGLE/renderer/metal/BUILD.gn @@ -20,6 +20,8 @@ _metal_backend_sources = [ "DisplayMtl.mm", "FrameBufferMtl.h", "FrameBufferMtl.mm", + "ImageMtl.h", + "ImageMtl.mm", "ProgramMtl.h", "ProgramMtl.mm", "QueryMtl.h", diff --git a/src/libANGLE/renderer/metal/DisplayMtl.h b/src/libANGLE/renderer/metal/DisplayMtl.h index f4a73df086..939d2506ed 100644 --- a/src/libANGLE/renderer/metal/DisplayMtl.h +++ b/src/libANGLE/renderer/metal/DisplayMtl.h @@ -75,6 +75,10 @@ class DisplayMtl : public DisplayImpl StreamProducerImpl *createStreamProducerD3DTexture(egl::Stream::ConsumerType consumerType, const egl::AttributeMap &attribs) override; + ExternalImageSiblingImpl *createExternalImageSibling(const gl::Context *context, + EGLenum target, + EGLClientBuffer buffer, + const egl::AttributeMap &attribs) override; gl::Version getMaxSupportedESVersion() const override; gl::Version getMaxConformantESVersion() const override; @@ -88,6 +92,10 @@ class DisplayMtl : public DisplayImpl bool isValidNativeWindow(EGLNativeWindowType window) const override; + egl::Error validateImageClientBuffer(const gl::Context *context, + EGLenum target, + EGLClientBuffer clientBuffer, + const egl::AttributeMap &attribs) const override; egl::ConfigSet generateConfigs() override; std::string getRendererDescription() const; diff --git a/src/libANGLE/renderer/metal/DisplayMtl.mm b/src/libANGLE/renderer/metal/DisplayMtl.mm index 7169d7d1fe..cafcf4c39e 100644 --- a/src/libANGLE/renderer/metal/DisplayMtl.mm +++ b/src/libANGLE/renderer/metal/DisplayMtl.mm @@ -14,6 +14,7 @@ #include "libANGLE/renderer/DeviceImpl.h" #include "libANGLE/renderer/glslang_wrapper_utils.h" #include "libANGLE/renderer/metal/ContextMtl.h" +#include "libANGLE/renderer/metal/ImageMtl.h" #include "libANGLE/renderer/metal/SurfaceMtl.h" #include "libANGLE/renderer/metal/SyncMtl.h" #include "libANGLE/renderer/metal/mtl_common.h" @@ -214,8 +215,7 @@ void generateExtensions(egl::DeviceExtensions *outExtensions) const override EGLenum target, const egl::AttributeMap &attribs) { - UNIMPLEMENTED(); - return nullptr; + return new ImageMtl(state, context); } rx::ContextImpl *DisplayMtl::createContext(const gl::State &state, @@ -235,6 +235,22 @@ void generateExtensions(egl::DeviceExtensions *outExtensions) const override return nullptr; } +ExternalImageSiblingImpl *DisplayMtl::createExternalImageSibling(const gl::Context *context, + EGLenum target, + EGLClientBuffer buffer, + const egl::AttributeMap &attribs) +{ + switch (target) + { + case EGL_MTL_TEXTURE_MGL: + return new TextureImageSiblingMtl(buffer); + + default: + UNREACHABLE(); + return nullptr; + } +} + gl::Version DisplayMtl::getMaxSupportedESVersion() const { return gl::Version(3, 0); @@ -268,7 +284,12 @@ void generateExtensions(egl::DeviceExtensions *outExtensions) const override outExtensions->fenceSync = true; outExtensions->waitSync = true; outExtensions->glColorspace = true; + outExtensions->mtlTextureClientBuffer = true; outExtensions->deviceQuery = true; + + // EGL_KHR_image + outExtensions->image = true; + outExtensions->imageBase = true; } void DisplayMtl::generateCaps(egl::Caps *outCaps) const {} @@ -376,6 +397,26 @@ void generateExtensions(egl::DeviceExtensions *outExtensions) const override return [layer isKindOfClass:[CALayer class]]; } +egl::Error DisplayMtl::validateImageClientBuffer(const gl::Context *context, + EGLenum target, + EGLClientBuffer clientBuffer, + const egl::AttributeMap &attribs) const +{ + switch (target) + { + case EGL_MTL_TEXTURE_MGL: + if (!TextureImageSiblingMtl::ValidateClientBuffer(this, clientBuffer)) + { + return egl::EglBadAttribute(); + } + break; + default: + UNREACHABLE(); + return egl::EglBadAttribute(); + } + return egl::NoError(); +} + std::string DisplayMtl::getRendererDescription() const { ANGLE_MTL_OBJC_SCOPE @@ -598,11 +639,14 @@ void generateExtensions(egl::DeviceExtensions *outExtensions) const override // Enable EXT_blend_minmax mNativeExtensions.blendMinMax = true; - mNativeExtensions.eglImage = false; + mNativeExtensions.eglImage = true; mNativeExtensions.eglImageExternal = false; // NOTE(hqle): Support GL_OES_EGL_image_external_essl3. mNativeExtensions.eglImageExternalEssl3 = false; + // MGL_EGL_image_cube + mNativeExtensions.eglImageCubeMGL = true; + mNativeExtensions.memoryObject = false; mNativeExtensions.memoryObjectFd = false; diff --git a/src/libANGLE/renderer/metal/ImageMtl.h b/src/libANGLE/renderer/metal/ImageMtl.h new file mode 100644 index 0000000000..826dd2e752 --- /dev/null +++ b/src/libANGLE/renderer/metal/ImageMtl.h @@ -0,0 +1,80 @@ +// +// Copyright 2020 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; + + 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 */ diff --git a/src/libANGLE/renderer/metal/ImageMtl.mm b/src/libANGLE/renderer/metal/ImageMtl.mm new file mode 100644 index 0000000000..0e1c7afb1a --- /dev/null +++ b/src/libANGLE/renderer/metal/ImageMtl.mm @@ -0,0 +1,170 @@ +// +// Copyright 2020 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 texture = (__bridge id)(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)(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->size() : gl::Extents(0, 0, 0); +} + +size_t TextureImageSiblingMtl::getSamples() const +{ + uint32_t samples = mNativeTexture ? mNativeTexture->samples() : 0; + return samples > 1 ? samples : 0; +} + +// 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_MTL_TEXTURE_MGL) + { + const TextureImageSiblingMtl *externalImageSibling = + GetImplAs(GetAs(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 diff --git a/src/libANGLE/renderer/metal/RenderBufferMtl.mm b/src/libANGLE/renderer/metal/RenderBufferMtl.mm index 02cc07f970..a995f6c816 100644 --- a/src/libANGLE/renderer/metal/RenderBufferMtl.mm +++ b/src/libANGLE/renderer/metal/RenderBufferMtl.mm @@ -10,6 +10,7 @@ #include "libANGLE/renderer/metal/RenderBufferMtl.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_utils.h" @@ -155,9 +156,20 @@ angle::Result RenderbufferMtl::setStorageEGLImageTarget(const gl::Context *context, egl::Image *image) { - // NOTE(hqle): Support EGLimage - UNIMPLEMENTED(); - return angle::Result::Stop; + releaseTexture(); + + 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, nullptr, 0, 0, mFormat); + + return angle::Result::Continue; } angle::Result RenderbufferMtl::getAttachmentRenderTarget(const gl::Context *context, diff --git a/src/libANGLE/renderer/metal/TextureMtl.mm b/src/libANGLE/renderer/metal/TextureMtl.mm index df59ce5749..1027573751 100644 --- a/src/libANGLE/renderer/metal/TextureMtl.mm +++ b/src/libANGLE/renderer/metal/TextureMtl.mm @@ -19,6 +19,7 @@ #include "libANGLE/renderer/metal/ContextMtl.h" #include "libANGLE/renderer/metal/DisplayMtl.h" #include "libANGLE/renderer/metal/FrameBufferMtl.h" +#include "libANGLE/renderer/metal/ImageMtl.h" #include "libANGLE/renderer/metal/SamplerMtl.h" #include "libANGLE/renderer/metal/mtl_common.h" #include "libANGLE/renderer/metal/mtl_format_utils.h" @@ -981,9 +982,32 @@ GLenum OverrideSwizzleValue(const gl::Context *context, gl::TextureType type, 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->size(); + 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, diff --git a/src/libANGLE/renderer/metal/gen_mtl_format_table.py b/src/libANGLE/renderer/metal/gen_mtl_format_table.py index 68511104b6..8a07f929bb 100644 --- a/src/libANGLE/renderer/metal/gen_mtl_format_table.py +++ b/src/libANGLE/renderer/metal/gen_mtl_format_table.py @@ -45,6 +45,15 @@ 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_) {{ this->intendedFormatId = intendedFormatId_; @@ -119,6 +128,10 @@ """ +case_image_mtl_to_angle_template = """ case {mtl_format}: + return angle::FormatID::{angle_format}; +""" + case_vertex_format_template1 = """ case angle::FormatID::{angle_format}: this->metalFormat = {mtl_format}; this->actualFormatId = angle::FormatID::{actual_angle_format}; @@ -355,6 +368,36 @@ def gen_image_map_switch_common_case(angle_format, actual_angle_format): 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) + + # iOS specific + switch_data += "#elif TARGET_OS_IOS || TARGET_OS_TV // TARGET_OS_OSX || TARGET_OS_MACCATALYST\n" + for angle_format in sorted(ios_specific_map.keys()): + 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_OSX || TARGET_OS_MACCATALYST\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): mtl_format = angle_to_mtl_map[actual_angle_fmt] copy_function, default_alpha, same_gl_type = get_vertex_copy_function_and_default_alpha( @@ -413,7 +456,6 @@ def gen_vertex_map_switch_string(vertex_table): switch_data += " this->actualSameGLType = false;" return switch_data - def main(): data_source_name = 'mtl_format_map.json' # auto_script parameters. @@ -437,6 +479,7 @@ def main(): map_vertex = map_json["vertex"] 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) @@ -445,6 +488,7 @@ def main(): copyright_year=date.today().year, data_source_name=data_source_name, angle_image_format_switch=image_switch_data, + mtl_pixel_format_switch=image_mtl_to_angle_switch_data, angle_vertex_format_switch=vertex_switch_data) with open('mtl_format_table_autogen.mm', 'wt') as out_file: out_file.write(output_cpp) diff --git a/src/libANGLE/renderer/metal/mtl_common.h b/src/libANGLE/renderer/metal/mtl_common.h index c7a9442948..12f6cc3959 100644 --- a/src/libANGLE/renderer/metal/mtl_common.h +++ b/src/libANGLE/renderer/metal/mtl_common.h @@ -95,6 +95,7 @@ class DisplayMtl; class ContextMtl; class FramebufferMtl; class BufferMtl; +class ImageMtl; class VertexArrayMtl; class TextureMtl; class ProgramMtl; @@ -246,6 +247,12 @@ struct ImplTypeHelper using ImplType = DisplayMtl; }; +template <> +struct ImplTypeHelper +{ + using ImplType = ImageMtl; +}; + template using GetImplType = typename ImplTypeHelper::ImplType; diff --git a/src/libANGLE/renderer/metal/mtl_format_table_autogen.mm b/src/libANGLE/renderer/metal/mtl_format_table_autogen.mm index 64555f3278..2b02765c49 100644 --- a/src/libANGLE/renderer/metal/mtl_format_table_autogen.mm +++ b/src/libANGLE/renderer/metal/mtl_format_table_autogen.mm @@ -25,6 +25,173 @@ namespace mtl { +angle::FormatID Format::MetalToAngleFormatID(MTLPixelFormat formatMtl) +{ + // Actual conversion + switch (formatMtl) + { + case MTLPixelFormatA8Unorm: + return angle::FormatID::A8_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 MTLPixelFormatDepth16Unorm: + return angle::FormatID::D16_UNORM; + case MTLPixelFormatDepth24Unorm_Stencil8: + return angle::FormatID::D24_UNORM_S8_UINT; +#elif TARGET_OS_IOS || TARGET_OS_TV // TARGET_OS_OSX || TARGET_OS_MACCATALYST + 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_OSX || TARGET_OS_MACCATALYST + default: + return angle::FormatID::NONE; + } +} + void Format::init(const DisplayMtl *display, angle::FormatID intendedFormatId_) { this->intendedFormatId = intendedFormatId_; diff --git a/src/libANGLE/renderer/metal/mtl_format_utils.h b/src/libANGLE/renderer/metal/mtl_format_utils.h index e1cf271f35..562669ffc7 100644 --- a/src/libANGLE/renderer/metal/mtl_format_utils.h +++ b/src/libANGLE/renderer/metal/mtl_format_utils.h @@ -62,6 +62,8 @@ struct Format : public FormatBase { Format() = default; + static angle::FormatID MetalToAngleFormatID(MTLPixelFormat formatMtl); + const gl::InternalFormat &intendedInternalFormat() const; bool valid() const { return metalFormat != MTLPixelFormatInvalid; } diff --git a/src/libANGLE/renderer/metal/mtl_resources.h b/src/libANGLE/renderer/metal/mtl_resources.h index 7b181b8225..2edbe42fa1 100644 --- a/src/libANGLE/renderer/metal/mtl_resources.h +++ b/src/libANGLE/renderer/metal/mtl_resources.h @@ -203,6 +203,7 @@ class Texture final : public Resource, uint32_t mipmapLevels() const; uint32_t arrayLength() const; uint32_t cubeFacesOrArrayLength() const; + uint32_t cubeFacesOrArrayLengthOrDepth() const; uint32_t width(uint32_t level = 0) const; uint32_t height(uint32_t level = 0) const; diff --git a/src/libANGLE/renderer/metal/mtl_resources.mm b/src/libANGLE/renderer/metal/mtl_resources.mm index 136799bd95..3c08c4e423 100644 --- a/src/libANGLE/renderer/metal/mtl_resources.mm +++ b/src/libANGLE/renderer/metal/mtl_resources.mm @@ -605,6 +605,15 @@ void EnsureContentSynced(ContextMtl *context, const std::shared_ptr &resource return arrayLength(); } +uint32_t Texture::cubeFacesOrArrayLengthOrDepth() const +{ + if (textureType() == MTLTextureType3D) + { + return depth(); + } + return cubeFacesOrArrayLength(); +} + uint32_t Texture::width(uint32_t level) const { return static_cast(GetMipSize(get().width, level)); diff --git a/src/libANGLE/validationEGL.cpp b/src/libANGLE/validationEGL.cpp index 05fb08da81..2a4b520fa0 100644 --- a/src/libANGLE/validationEGL.cpp +++ b/src/libANGLE/validationEGL.cpp @@ -2249,6 +2249,20 @@ Error ValidateCreateImage(const Display *display, ANGLE_TRY(display->validateImageClientBuffer(context, target, buffer, attributes)); break; + case EGL_MTL_TEXTURE_MGL: + if (!displayExtensions.mtlTextureClientBuffer) + { + return EglBadParameter() << "EGL_MGL_mtl_texture_client_buffer not supported."; + } + + if (context != nullptr) + { + return EglBadContext() << "ctx must be EGL_NO_CONTEXT."; + } + + ANGLE_TRY(display->validateImageClientBuffer(context, target, buffer, attributes)); + break; + default: return EglBadParameter() << "invalid target: 0x" << std::hex << std::uppercase << target; diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp index bbf30cc676..4b4f27c888 100644 --- a/src/libANGLE/validationES.cpp +++ b/src/libANGLE/validationES.cpp @@ -3420,6 +3420,13 @@ bool ValidateEGLImageTargetTexture2DOES(Context *context, TextureType type, GLeg } break; + case TextureType::CubeMap: + if (!context->getExtensions().eglImageCubeMGL) + { + context->validationError(GL_INVALID_ENUM, kEnumNotSupported); + } + break; + default: context->validationError(GL_INVALID_ENUM, kInvalidTextureTarget); return false; diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn index cf749d489f..497b3cf06c 100644 --- a/src/tests/BUILD.gn +++ b/src/tests/BUILD.gn @@ -125,6 +125,14 @@ if (is_win || is_linux || is_mac || is_android || is_fuchsia) { "CoreFoundation.framework", "IOSurface.framework", ] + ldflags = [ + "-weak_framework", + "Metal", + ] + cflags_objcc = [ + "-Wno-nullability-completeness", + "-Wno-unguarded-availability", + ] } if (is_win) { sources += angle_end2end_tests_win_sources diff --git a/src/tests/angle_end2end_tests.gni b/src/tests/angle_end2end_tests.gni index c87ea54890..1bfb7e8337 100644 --- a/src/tests/angle_end2end_tests.gni +++ b/src/tests/angle_end2end_tests.gni @@ -165,6 +165,7 @@ angle_end2end_tests_mac_sources = [ "egl_tests/EGLIOSurfaceClientBufferTest.cpp", "test_utils/angle_test_instantiate_apple.mm", "test_utils/angle_test_instantiate_apple.h", + "gl_tests/ImageTestMetal.mm", ] angle_end2end_tests_win_sources = [ "gl_tests/D3DImageFormatConversionTest.cpp", diff --git a/src/tests/gl_tests/ImageTest.cpp b/src/tests/gl_tests/ImageTest.cpp index 5b7536d57f..e72596b89f 100644 --- a/src/tests/gl_tests/ImageTest.cpp +++ b/src/tests/gl_tests/ImageTest.cpp @@ -475,6 +475,8 @@ TEST_P(ImageTest, ANGLEExtensionAvailability) ANGLE_SKIP_TEST_IF(IsOpenGLES() && IsAndroid()); ANGLE_SKIP_TEST_IF(IsOpenGLES() && IsOzone()); + ANGLE_SKIP_TEST_IF(IsMetal()); + if (IsD3D11() || IsD3D9()) { EXPECT_TRUE(hasOESExt()); diff --git a/src/tests/gl_tests/ImageTestMetal.mm b/src/tests/gl_tests/ImageTestMetal.mm new file mode 100644 index 0000000000..850a35f200 --- /dev/null +++ b/src/tests/gl_tests/ImageTestMetal.mm @@ -0,0 +1,425 @@ +// +// Copyright 2020 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 +#include + +namespace angle +{ +namespace +{ +constexpr char kOESExt[] = "GL_OES_EGL_image"; +constexpr char kBaseExt[] = "EGL_KHR_image_base"; +constexpr char kDeviceMtlExt[] = "EGL_ANGLE_device_mtl"; +constexpr char kEGLImageCubeExt[] = "GL_MGL_EGL_image_cube"; +constexpr char kEGLMtlImageNativeTextureExt[] = "EGL_MGL_mtl_texture_client_buffer"; +constexpr EGLint kDefaultAttribs[] = { + EGL_NONE, +}; +} // anonymous namespace + +class ScopeMetalTextureRef : angle::NonCopyable +{ + public: + explicit ScopeMetalTextureRef(id surface) : mSurface(surface) {} + + ~ScopeMetalTextureRef() + { + if (mSurface) + { + release(); + mSurface = nullptr; + } + } + + id get() const { return mSurface; } + + // auto cast to MTLTexture + operator id() 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 mSurface = nil; +}; + +ScopeMetalTextureRef CreateMetalTexture2D(id deviceMtl, + int width, + int height, + MTLPixelFormat format) +{ + @autoreleasepool + { + MTLTextureDescriptor *desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format + width:width + height:width + mipmapped:NO]; + desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; + + id texture = [deviceMtl newTextureWithDescriptor:desc]; + + ScopeMetalTextureRef re(texture); + return re; + } +} + +ScopeMetalTextureRef CreateMetalTextureCube(id deviceMtl, + int width, + MTLPixelFormat format) +{ + @autoreleasepool + { + MTLTextureDescriptor *desc = + [MTLTextureDescriptor textureCubeDescriptorWithPixelFormat:format + size:width + mipmapped:NO]; + desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; + + id 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"; + constexpr char kTextureCubeFS[] = + "precision highp float;\n" + "uniform samplerCube tex;\n" + "varying vec2 texcoord;\n" + "\n" + "void main()\n" + "{\n" + " gl_FragColor = textureCube(tex, vec3(texcoord, 0.0));\n" + "}\n"; + + mTextureProgram = CompileProgram(kVS, kTextureFS); + if (mTextureProgram == 0) + { + FAIL() << "shader compilation failed."; + } + + mTextureUniformLocation = glGetUniformLocation(mTextureProgram, "tex"); + + mTextureCubeProgram = CompileProgram(kVS, kTextureCubeFS); + if (mTextureCubeProgram == 0) + { + FAIL() << "shader compilation failed."; + } + + mTextureCubeUniformLocation = glGetUniformLocation(mTextureCubeProgram, "tex"); + + ASSERT_GL_NO_ERROR(); + } + + void testTearDown() override + { + glDeleteProgram(mTextureProgram); + glDeleteProgram(mTextureCubeProgram); + } + + id getMtlDevice() + { + EGLAttrib angleDevice = 0; + EGLAttrib device = 0; + EXPECT_EGL_TRUE( + eglQueryDisplayAttribEXT(getEGLWindow()->getDisplay(), EGL_DEVICE_EXT, &angleDevice)); + + EXPECT_EGL_TRUE(eglQueryDeviceAttribEXT(reinterpret_cast(angleDevice), + EGL_MTL_DEVICE_ANGLE, &device)); + + return (__bridge id)reinterpret_cast(device); + } + + ScopeMetalTextureRef createMtlTexture2D(int width, int height, MTLPixelFormat format) + { + id device = getMtlDevice(); + + return CreateMetalTexture2D(device, width, height, format); + } + + ScopeMetalTextureRef createMtlTextureCube(int width, MTLPixelFormat format) + { + id device = getMtlDevice(); + + return CreateMetalTextureCube(device, width, format); + } + + void SourceMetalTarget2D_helper(const EGLint *attribs); + void SourceMetalTargetCube_helper(const EGLint *attribs); + + 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); + } + void verifyResultsCube(GLuint texture, GLubyte data[4]) + { + verifyResultsTexture(texture, data, GL_TEXTURE_CUBE_MAP, mTextureCubeProgram, + mTextureCubeUniformLocation); + } + + template + 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(source); + return reinterpret_cast(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( + eglQueryDeviceStringEXT(reinterpret_cast(angleDevice), EGL_EXTENSIONS)); + if (strstr(extensionString, kDeviceMtlExt) == nullptr) + { + return false; + } + return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), + kEGLMtlImageNativeTextureExt); + } + + bool hasEglImageCubeExt() const { return IsGLExtensionEnabled(kEGLImageCubeExt); } + + bool hasOESExt() const { return IsGLExtensionEnabled(kOESExt); } + + bool hasBaseExt() const + { + return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), kBaseExt); + } + + GLuint mTextureProgram; + GLint mTextureUniformLocation; + + GLuint mTextureCubeProgram; + GLint mTextureCubeUniformLocation = -1; +}; + +void ImageTestMetal::SourceMetalTarget2D_helper(const EGLint *attribs) +{ + EGLWindow *window = getEGLWindow(); + + ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); + ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); + + GLubyte data[4] = {7, 51, 197, 231}; + + // Create MTLTexture + ScopeMetalTextureRef textureMtl = createMtlTexture2D(1, 1, MTLPixelFormatRGBA8Unorm); + + // Create image + EGLImageKHR image = + eglCreateImageKHR(window->getDisplay(), EGL_NO_CONTEXT, EGL_MTL_TEXTURE_MGL, + reinterpret_cast(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:sizeof(data) + 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); + + // 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(target, data); + + // Clean up + eglDestroyImageKHR(window->getDisplay(), image); + glDeleteTextures(1, &target); +} + +void ImageTestMetal::SourceMetalTargetCube_helper(const EGLint *attribs) +{ + EGLWindow *window = getEGLWindow(); + + ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasEglImageCubeExt()); + ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); + + GLubyte data[4] = {7, 51, 197, 231}; + std::array floatData{ + gl::float32ToFloat16(data[0] / 255.f), gl::float32ToFloat16(data[1] / 255.f), + gl::float32ToFloat16(data[2] / 255.f), gl::float32ToFloat16(data[3] / 255.f)}; + + // Create MTLTexture + ScopeMetalTextureRef textureMtl = createMtlTextureCube(1, MTLPixelFormatRGBA16Float); + + // Create image + EGLImageKHR image = + eglCreateImageKHR(window->getDisplay(), EGL_NO_CONTEXT, EGL_MTL_TEXTURE_MGL, + reinterpret_cast(textureMtl.get()), attribs); + ASSERT_EGL_SUCCESS(); + + // Write the data to the MTLTexture + for (int face = 0; face < 6; ++face) + { + [textureMtl.get() replaceRegion:MTLRegionMake2D(0, 0, 1, 1) + mipmapLevel:0 + slice:face + withBytes:floatData.data() + bytesPerRow:floatData.size() * sizeof(floatData[0]) + bytesPerImage:0]; + } + + // Create a texture target to bind the egl image + GLuint target; + glGenTextures(1, &target); + glBindTexture(GL_TEXTURE_CUBE_MAP, target); + glEGLImageTargetTexture2DOES(GL_TEXTURE_CUBE_MAP, image); + + // Use texture target bound to egl image as source and render to framebuffer + // Verify that data in framebuffer matches that in the egl image + verifyResultsCube(target, data); + + // Clean up + eglDestroyImageKHR(window->getDisplay(), image); + glDeleteTextures(1, &target); +} + +// Testing source metal EGL image, target 2D texture +TEST_P(ImageTestMetal, SourceMetalTarget2D) +{ + ANGLE_SKIP_TEST_IF(!IsMetal()); + SourceMetalTarget2D_helper(kDefaultAttribs); +} + +// Testing source metal EGL image, target Cube texture +TEST_P(ImageTestMetal, SourceMetalTargetCube) +{ + ANGLE_SKIP_TEST_IF(!IsMetal()); + SourceMetalTargetCube_helper(kDefaultAttribs); +} + +// 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