From eb455ffaea1f79c97ed7bf782677e414dface0d7 Mon Sep 17 00:00:00 2001 From: Le Hoang Quyen Date: Tue, 13 Jul 2021 20:08:00 +0800 Subject: [PATCH] Support external semaphore imported from MTLSharedEvent --- include/GLES2/gl2ext.h | 5 + ios/xcode/OpenGLES.xcodeproj/project.pbxproj | 20 +- src/libANGLE/Caps.cpp | 1 + src/libANGLE/Caps.h | 3 + src/libANGLE/Context.cpp | 18 +- src/libANGLE/Semaphore.cpp | 9 + src/libANGLE/Semaphore.h | 3 + src/libANGLE/renderer/SemaphoreImpl.h | 3 + src/libANGLE/renderer/metal/BUILD.gn | 2 + src/libANGLE/renderer/metal/ContextMtl.mm | 4 +- src/libANGLE/renderer/metal/DisplayMtl.mm | 9 + src/libANGLE/renderer/metal/SemaphoreMtl.h | 46 +++ src/libANGLE/renderer/metal/SemaphoreMtl.mm | 114 ++++++ src/libANGLE/renderer/metal/mtl_common.h | 2 + src/libANGLE/validationES2.cpp | 23 +- src/tests/gl_tests/ImageTestMetal.mm | 390 +++++++++++++++---- 16 files changed, 557 insertions(+), 95 deletions(-) create mode 100644 src/libANGLE/renderer/metal/SemaphoreMtl.h create mode 100644 src/libANGLE/renderer/metal/SemaphoreMtl.mm diff --git a/include/GLES2/gl2ext.h b/include/GLES2/gl2ext.h index 0ee082a3d0..313023e999 100644 --- a/include/GLES2/gl2ext.h +++ b/include/GLES2/gl2ext.h @@ -3612,6 +3612,11 @@ GL_APICALL void GL_APIENTRY glEndTilingQCOM (GLbitfield preserveMask); #define GL_SHADER_BINARY_VIV 0x8FC4 #endif /* GL_VIV_shader_binary */ +#ifndef GL_MGL_timeline_semaphore +#define GL_MGL_timeline_semaphore 1 +#define GL_TIMELINE_SEMAPHORE_VALUE_MGL 0x9595 +#endif + /* ANGLE GLES2 extensions */ #include "gl2ext_angle.h" diff --git a/ios/xcode/OpenGLES.xcodeproj/project.pbxproj b/ios/xcode/OpenGLES.xcodeproj/project.pbxproj index 60b2b59dcb..d8bf194155 100644 --- a/ios/xcode/OpenGLES.xcodeproj/project.pbxproj +++ b/ios/xcode/OpenGLES.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 52; objects = { /* Begin PBXAggregateTarget section */ @@ -2288,6 +2288,10 @@ 0AC8B2F0241E9761003916D1 /* QueryMtl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0AC8B2EB241E9761003916D1 /* QueryMtl.mm */; }; 0AC8B2F1241E9761003916D1 /* QueryMtl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0AC8B2EB241E9761003916D1 /* QueryMtl.mm */; }; 0AC8B2F2241E9761003916D1 /* QueryMtl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0AC8B2EB241E9761003916D1 /* QueryMtl.mm */; }; + 0AE8E090269F6E9800070E82 /* SemaphoreMtl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0AE8E067269F6E9600070E82 /* SemaphoreMtl.mm */; }; + 0AE8E091269F6E9800070E82 /* SemaphoreMtl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0AE8E067269F6E9600070E82 /* SemaphoreMtl.mm */; }; + 0AE8E092269F6E9800070E82 /* SemaphoreMtl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0AE8E067269F6E9600070E82 /* SemaphoreMtl.mm */; }; + 0AE8E093269F6E9800070E82 /* SemaphoreMtl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0AE8E067269F6E9600070E82 /* SemaphoreMtl.mm */; }; 0AF956AD244C7BF300F59740 /* json_reader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0A9B83F6234CD0C2008BF16F /* json_reader.cpp */; }; 0AF956AE244C7BF300F59740 /* json_value.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0A9B83F7234CD0C2008BF16F /* json_value.cpp */; }; 0AF956AF244C7BF300F59740 /* json_writer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AA2FA9C2346F86700E0B98C /* json_writer.cpp */; }; @@ -5044,6 +5048,8 @@ 0AC8B2EA241E9761003916D1 /* mtl_occlusion_query_pool.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = mtl_occlusion_query_pool.mm; path = ../../src/libANGLE/renderer/metal/mtl_occlusion_query_pool.mm; sourceTree = ""; }; 0AC8B2EB241E9761003916D1 /* QueryMtl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = QueryMtl.mm; path = ../../src/libANGLE/renderer/metal/QueryMtl.mm; sourceTree = ""; }; 0AC8B2EC241E9761003916D1 /* mtl_occlusion_query_pool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mtl_occlusion_query_pool.h; path = ../../src/libANGLE/renderer/metal/mtl_occlusion_query_pool.h; sourceTree = ""; }; + 0AE8E067269F6E9600070E82 /* SemaphoreMtl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SemaphoreMtl.mm; path = ../../src/libANGLE/renderer/metal/SemaphoreMtl.mm; sourceTree = ""; }; + 0AE8E068269F6E9600070E82 /* SemaphoreMtl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SemaphoreMtl.h; path = ../../src/libANGLE/renderer/metal/SemaphoreMtl.h; sourceTree = ""; }; 0AF956B5244C7BF300F59740 /* libjsoncpp_tvos.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libjsoncpp_tvos.a; sourceTree = BUILT_PRODUCTS_DIR; }; 0AF956C6244C7C5100F59740 /* libspirv-cross_tvos.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libspirv-cross_tvos.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 0AF956F8244C7C8700F59740 /* libglslang_tvos.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libglslang_tvos.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -6114,6 +6120,9 @@ 0A6055CE234655FB005CEA98 /* RenderTargetMtl.mm */, 0A9E0A7A242D060000688588 /* SamplerMtl.h */, 0A9E0A7B242D060000688588 /* SamplerMtl.mm */, + 0AE8E068269F6E9600070E82 /* SemaphoreMtl.h */, + 0AE8E067269F6E9600070E82 /* SemaphoreMtl.mm */, + 0A9A6AC423911CAD006A152A /* RendererGL.cpp */, 0A605604234655FE005CEA98 /* ShaderMtl.h */, 0A6055E5234655FD005CEA98 /* ShaderMtl.mm */, 0A0546CD242C1DD200FC05D0 /* TransformFeedbackMtl.h */, @@ -6629,7 +6638,6 @@ 0A9A6B5823911CB3006A152A /* RenderbufferGL.h */, 0A9A6ABE23911CAD006A152A /* renderergl_utils.cpp */, 0A9A6B0823911CB1006A152A /* renderergl_utils.h */, - 0A9A6AC423911CAD006A152A /* RendererGL.cpp */, 0A9A6B4623911CB3006A152A /* RendererGL.h */, 0A9A6AF623911CB0006A152A /* SamplerGL.cpp */, 0A9A6B2023911CB2006A152A /* SamplerGL.h */, @@ -10280,6 +10288,7 @@ 0A60565123465667005CEA98 /* mtl_format_utils.mm in Sources */, 0A60564923465667005CEA98 /* ContextMtl.mm in Sources */, 0A60564C23465667005CEA98 /* mtl_glslang_utils.mm in Sources */, + 0AE8E090269F6E9800070E82 /* SemaphoreMtl.mm in Sources */, 0A60564823465667005CEA98 /* CompilerMtl.mm in Sources */, 0AFFDCB624D42F7F0015E16A /* ImageMtl.mm in Sources */, 0A60565C23465667005CEA98 /* TextureMtl.mm in Sources */, @@ -10507,6 +10516,7 @@ 0A90F76624065B06005BA9A8 /* mtl_format_utils.mm in Sources */, 0A90F76724065B06005BA9A8 /* ContextMtl.mm in Sources */, 0A90F76824065B06005BA9A8 /* mtl_glslang_utils.mm in Sources */, + 0AE8E091269F6E9800070E82 /* SemaphoreMtl.mm in Sources */, 0A90F76924065B06005BA9A8 /* CompilerMtl.mm in Sources */, 0AFFDCB724D42F820015E16A /* ImageMtl.mm in Sources */, 0A90F76A24065B06005BA9A8 /* TextureMtl.mm in Sources */, @@ -11668,6 +11678,7 @@ 0AA2FFBD234727A400E0B98C /* mtl_format_utils.mm in Sources */, 0AA2FFBE234727A400E0B98C /* ContextMtl.mm in Sources */, 0AA2FFBF234727A400E0B98C /* mtl_glslang_utils.mm in Sources */, + 0AE8E093269F6E9800070E82 /* SemaphoreMtl.mm in Sources */, 0AA2FFC0234727A400E0B98C /* CompilerMtl.mm in Sources */, 0AFFDCB924D42F880015E16A /* ImageMtl.mm in Sources */, 0AA2FFC1234727A400E0B98C /* TextureMtl.mm in Sources */, @@ -11861,6 +11872,7 @@ 0AF9573F244C7CC300F59740 /* mtl_format_utils.mm in Sources */, 0AF95740244C7CC300F59740 /* ContextMtl.mm in Sources */, 0AF95741244C7CC300F59740 /* mtl_glslang_utils.mm in Sources */, + 0AE8E092269F6E9800070E82 /* SemaphoreMtl.mm in Sources */, 0AF95742244C7CC300F59740 /* CompilerMtl.mm in Sources */, 0AFFDCB824D42F860015E16A /* ImageMtl.mm in Sources */, 0AF95743244C7CC300F59740 /* TextureMtl.mm in Sources */, @@ -12566,6 +12578,8 @@ OTHER_CFLAGS = "-Wno-shorten-64-to-32"; SDKROOT = iphoneos; TVOS_DEPLOYMENT_TARGET = 9.0; + USER_HEADER_SEARCH_PATHS = Yes; + USE_HEADERMAP = NO; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -12652,6 +12666,8 @@ OTHER_CFLAGS = "-Wno-shorten-64-to-32"; SDKROOT = iphoneos; TVOS_DEPLOYMENT_TARGET = 9.0; + USER_HEADER_SEARCH_PATHS = Yes; + USE_HEADERMAP = NO; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp index 2d744153f7..d24ad00f73 100644 --- a/src/libANGLE/Caps.cpp +++ b/src/libANGLE/Caps.cpp @@ -892,6 +892,7 @@ const ExtensionInfoMap &GetExtensionInfoMap() map["GL_ANGLE_texture_external_update"] = enableableExtension(&Extensions::textureExternalUpdateANGLE); map["GL_ANGLE_base_vertex_base_instance"] = enableableExtension(&Extensions::baseVertexBaseInstance); map["GL_APPLE_clip_distance"] = enableableExtension(&Extensions::clipDistanceAPPLE); + map["GL_MGL_timeline_semaphore"] = enableableExtension(&Extensions::timelineSemaphoreMGL); // GLES1 extensinos map["GL_OES_point_size_array"] = enableableExtension(&Extensions::pointSizeArray); map["GL_OES_texture_cube_map"] = enableableExtension(&Extensions::textureCubeMap); diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h index ef2e7cb9cc..c651939cad 100644 --- a/src/libANGLE/Caps.h +++ b/src/libANGLE/Caps.h @@ -547,6 +547,9 @@ struct Extensions // GL_APPLE_clip_distance bool clipDistanceAPPLE = false; + + // GL_MGL_timeline_semaphore + bool timelineSemaphoreMGL = false; }; struct ExtensionInfo diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp index 69306d8979..28276fffc2 100644 --- a/src/libANGLE/Context.cpp +++ b/src/libANGLE/Context.cpp @@ -7861,14 +7861,24 @@ GLboolean Context::isSemaphore(SemaphoreID semaphore) return ConvertToGLBoolean(getSemaphore(semaphore)); } -void Context::semaphoreParameterui64v(SemaphoreID semaphore, GLenum pname, const GLuint64 *params) +void Context::semaphoreParameterui64v(SemaphoreID semaphoreHandle, + GLenum pname, + const GLuint64 *params) { - UNIMPLEMENTED(); + Semaphore *semaphore = getSemaphore(semaphoreHandle); + ASSERT(semaphore); + + semaphore->parameterui64v(pname, params); } -void Context::getSemaphoreParameterui64v(SemaphoreID semaphore, GLenum pname, GLuint64 *params) +void Context::getSemaphoreParameterui64v(SemaphoreID semaphoreHandle, + GLenum pname, + GLuint64 *params) { - UNIMPLEMENTED(); + Semaphore *semaphore = getSemaphore(semaphoreHandle); + ASSERT(semaphore); + + semaphore->getParameterui64v(pname, params); } void Context::waitSemaphore(SemaphoreID semaphoreHandle, diff --git a/src/libANGLE/Semaphore.cpp b/src/libANGLE/Semaphore.cpp index 5f7e0bc3f6..c3ecddb7d7 100644 --- a/src/libANGLE/Semaphore.cpp +++ b/src/libANGLE/Semaphore.cpp @@ -44,4 +44,13 @@ angle::Result Semaphore::signal(Context *context, return mImplementation->signal(context, bufferBarriers, textureBarriers); } +void Semaphore::parameterui64v(GLenum pname, const GLuint64 *params) +{ + mImplementation->parameterui64v(pname, params); +} +void Semaphore::getParameterui64v(GLenum pname, GLuint64 *params) +{ + mImplementation->getParameterui64v(pname, params); +} + } // namespace gl diff --git a/src/libANGLE/Semaphore.h b/src/libANGLE/Semaphore.h index 62230a3b83..d91f98ff25 100644 --- a/src/libANGLE/Semaphore.h +++ b/src/libANGLE/Semaphore.h @@ -47,6 +47,9 @@ class Semaphore final : public RefCountObject const BufferBarrierVector &bufferBarriers, const TextureBarrierVector &textureBarriers); + void parameterui64v(GLenum pname, const GLuint64 *params); + void getParameterui64v(GLenum pname, GLuint64 *params); + private: std::unique_ptr mImplementation; }; diff --git a/src/libANGLE/renderer/SemaphoreImpl.h b/src/libANGLE/renderer/SemaphoreImpl.h index 059ea157b7..2c0a9b68cb 100644 --- a/src/libANGLE/renderer/SemaphoreImpl.h +++ b/src/libANGLE/renderer/SemaphoreImpl.h @@ -39,6 +39,9 @@ class SemaphoreImpl : angle::NonCopyable virtual angle::Result signal(gl::Context *context, const gl::BufferBarrierVector &bufferBarriers, const gl::TextureBarrierVector &textureBarriers) = 0; + + virtual void parameterui64v(GLenum pname, const GLuint64 *params) {} + virtual void getParameterui64v(GLenum pname, GLuint64 *params) {} }; } // namespace rx diff --git a/src/libANGLE/renderer/metal/BUILD.gn b/src/libANGLE/renderer/metal/BUILD.gn index e916f7d73e..cecde17d06 100644 --- a/src/libANGLE/renderer/metal/BUILD.gn +++ b/src/libANGLE/renderer/metal/BUILD.gn @@ -32,6 +32,8 @@ _metal_backend_sources = [ "RenderTargetMtl.mm", "SamplerMtl.h", "SamplerMtl.mm", + "SemaphoreMtl.h", + "SemaphoreMtl.mm", "ShaderMtl.h", "ShaderMtl.mm", "SyncMtl.h", diff --git a/src/libANGLE/renderer/metal/ContextMtl.mm b/src/libANGLE/renderer/metal/ContextMtl.mm index 3cedf32ff0..a6c256a042 100644 --- a/src/libANGLE/renderer/metal/ContextMtl.mm +++ b/src/libANGLE/renderer/metal/ContextMtl.mm @@ -21,6 +21,7 @@ #include "libANGLE/renderer/metal/QueryMtl.h" #include "libANGLE/renderer/metal/RenderBufferMtl.h" #include "libANGLE/renderer/metal/SamplerMtl.h" +#include "libANGLE/renderer/metal/SemaphoreMtl.h" #include "libANGLE/renderer/metal/ShaderMtl.h" #include "libANGLE/renderer/metal/SyncMtl.h" #include "libANGLE/renderer/metal/TextureMtl.h" @@ -1182,8 +1183,7 @@ bool IsTransformFeedbackOnly(const gl::State &glState) // Semaphore creation. SemaphoreImpl *ContextMtl::createSemaphore() { - UNIMPLEMENTED(); - return nullptr; + return new SemaphoreMtl(); } OverlayImpl *ContextMtl::createOverlay(const gl::OverlayState &state) diff --git a/src/libANGLE/renderer/metal/DisplayMtl.mm b/src/libANGLE/renderer/metal/DisplayMtl.mm index 9898592810..4093db78ea 100644 --- a/src/libANGLE/renderer/metal/DisplayMtl.mm +++ b/src/libANGLE/renderer/metal/DisplayMtl.mm @@ -701,6 +701,15 @@ void generateExtensions(egl::DeviceExtensions *outExtensions) const override // GL_OES_EGL_sync mNativeExtensions.eglSync = true; + + // GL_EXT_semaphore + mNativeExtensions.semaphore = true; + + // GL_EXT_semaphore_fd + mNativeExtensions.semaphoreFd = true; + + // GL_MGL_timeline_semaphore + mNativeExtensions.timelineSemaphoreMGL = true; } } diff --git a/src/libANGLE/renderer/metal/SemaphoreMtl.h b/src/libANGLE/renderer/metal/SemaphoreMtl.h new file mode 100644 index 0000000000..f797028c5b --- /dev/null +++ b/src/libANGLE/renderer/metal/SemaphoreMtl.h @@ -0,0 +1,46 @@ +// 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. +// +// SemaphoreMtl.h: Defines the class interface for SemaphoreMtl, +// implementing SemaphoreImpl. + +#ifndef LIBANGLE_RENDERER_METAL_SEMAPHOREMTL_H_ +#define LIBANGLE_RENDERER_METAL_SEMAPHOREMTL_H_ + +#include "libANGLE/renderer/SemaphoreImpl.h" +#include "libANGLE/renderer/metal/mtl_common.h" + +namespace rx +{ + +class SemaphoreMtl : public SemaphoreImpl +{ + public: + SemaphoreMtl(); + ~SemaphoreMtl() override; + + void onDestroy(const gl::Context *context) override; + + angle::Result importFd(gl::Context *context, gl::HandleType handleType, GLint fd) override; + + angle::Result wait(gl::Context *context, + const gl::BufferBarrierVector &bufferBarriers, + const gl::TextureBarrierVector &textureBarriers) override; + + angle::Result signal(gl::Context *context, + const gl::BufferBarrierVector &bufferBarriers, + const gl::TextureBarrierVector &textureBarriers) override; + + void parameterui64v(GLenum pname, const GLuint64 *params) override; + void getParameterui64v(GLenum pname, GLuint64 *params) override; + private: + angle::Result importOpaqueFd(gl::Context *context, GLint fd); + + mtl::SharedEventRef mMetalSharedEvent; + GLuint64 mTimelineValue = 0; +}; + +} // namespace rx + +#endif // LIBANGLE_RENDERER_METAL_SEMAPHOREMTL_H_ diff --git a/src/libANGLE/renderer/metal/SemaphoreMtl.mm b/src/libANGLE/renderer/metal/SemaphoreMtl.mm new file mode 100644 index 0000000000..c5cefdf352 --- /dev/null +++ b/src/libANGLE/renderer/metal/SemaphoreMtl.mm @@ -0,0 +1,114 @@ +// 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. +// +// SemaphoreMtl.cpp: Defines the class interface for SemaphoreMtl, implementing +// SemaphoreImpl. + +#include "libANGLE/renderer/metal/SemaphoreMtl.h" + +#include "common/debug.h" +#include "libANGLE/Context.h" +#include "libANGLE/renderer/metal/ContextMtl.h" + +namespace rx +{ + +SemaphoreMtl::SemaphoreMtl() = default; + +SemaphoreMtl::~SemaphoreMtl() = default; + +void SemaphoreMtl::onDestroy(const gl::Context *context) +{ + mMetalSharedEvent = nil; +} + +angle::Result SemaphoreMtl::importFd(gl::Context *context, gl::HandleType handleType, GLint fd) +{ + switch (handleType) + { + case gl::HandleType::OpaqueFd: + return importOpaqueFd(context, fd); + + default: + UNREACHABLE(); + return angle::Result::Stop; + } +} + +angle::Result SemaphoreMtl::wait(gl::Context *context, + const gl::BufferBarrierVector &bufferBarriers, + const gl::TextureBarrierVector &textureBarriers) +{ + // bufferBarriers & textureBarriers are unused for now because metal always end + // current render pass when there is a wait command. + +#if ANGLE_MTL_EVENT_AVAILABLE + ContextMtl *contextMtl = mtl::GetImpl(context); + contextMtl->serverWaitEvent(mMetalSharedEvent, mTimelineValue); +#endif // ANGLE_MTL_EVENT_AVAILABLE + + return angle::Result::Continue; +} + +angle::Result SemaphoreMtl::signal(gl::Context *context, + const gl::BufferBarrierVector &bufferBarriers, + const gl::TextureBarrierVector &textureBarriers) +{ +#if ANGLE_MTL_EVENT_AVAILABLE + ContextMtl *contextMtl = mtl::GetImpl(context); + contextMtl->queueEventSignal(mMetalSharedEvent, mTimelineValue); +#endif // ANGLE_MTL_EVENT_AVAILABLE + + contextMtl->flushCommandBufer(); + return angle::Result::Continue; +} + +void SemaphoreMtl::parameterui64v(GLenum pname, const GLuint64 *params) +{ + switch (pname) + { + case GL_TIMELINE_SEMAPHORE_VALUE_MGL: + mTimelineValue = *params; + break; + default: + UNREACHABLE(); + } +} + +void SemaphoreMtl::getParameterui64v(GLenum pname, GLuint64 *params) +{ + switch (pname) + { + case GL_TIMELINE_SEMAPHORE_VALUE_MGL: + *params = mTimelineValue; + break; + default: + UNREACHABLE(); + } +} + +angle::Result SemaphoreMtl::importOpaqueFd(gl::Context *context, GLint fd) +{ +#if !ANGLE_MTL_EVENT_AVAILABLE + UNREACHABLE(); +#else + ContextMtl *contextMtl = mtl::GetImpl(context); + + // NOTE(hqle): This import assumes the address of MTLSharedEvent is stored in the file. + void* sharedEventPtr = nullptr; + ANGLE_MTL_TRY(contextMtl, read(fd, &sharedEventPtr, sizeof(sharedEventPtr))); + ANGLE_MTL_TRY(contextMtl, sharedEventPtr); + + // The ownership of this fd belongs to Semaphore now, so we can close it here. + close(fd); + + auto sharedEvent = (__bridge id)sharedEventPtr; + + mMetalSharedEvent = std::move(sharedEvent); +#endif // ANGLE_MTL_EVENT_AVAILABLE + + return angle::Result::Continue; +} + +} // namespace rx diff --git a/src/libANGLE/renderer/metal/mtl_common.h b/src/libANGLE/renderer/metal/mtl_common.h index 0345cb8ead..7fe0c0c2bf 100644 --- a/src/libANGLE/renderer/metal/mtl_common.h +++ b/src/libANGLE/renderer/metal/mtl_common.h @@ -418,8 +418,10 @@ using AutoObjCObj = AutoObjCPtr; // NOTE: SharedEvent is only declared on iOS 12.0+ or mac 10.14+ #if defined(__IPHONE_12_0) || defined(__MAC_10_14) +# define ANGLE_MTL_EVENT_AVAILABLE 1 using SharedEventRef = AutoObjCPtr>; #else +# define ANGLE_MTL_EVENT_AVAILABLE 0 using SharedEventRef = AutoObjCObj; #endif diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp index c8a10af441..93282dac6e 100644 --- a/src/libANGLE/validationES2.cpp +++ b/src/libANGLE/validationES2.cpp @@ -1227,6 +1227,23 @@ bool ValidateES2TexImageParameters(Context *context, format, type, imageSize, pixels); } +bool IsValidSemaphoreParameter(const Context *context, GLenum pname) +{ + switch (pname) + { + case GL_TIMELINE_SEMAPHORE_VALUE_MGL: + if (context->getExtensions().timelineSemaphoreMGL) + { + return true; + } + break; + default: + UNIMPLEMENTED(); + } + + return false; +} + } // anonymous namespace bool ValidateES2TexImageParametersBase(Context *context, @@ -3539,8 +3556,7 @@ bool ValidateGetSemaphoreParameterui64vEXT(Context *context, return false; } - UNIMPLEMENTED(); - return false; + return IsValidSemaphoreParameter(context, pname); } bool ValidateIsSemaphoreEXT(Context *context, SemaphoreID semaphore) @@ -3565,8 +3581,7 @@ bool ValidateSemaphoreParameterui64vEXT(Context *context, return false; } - UNIMPLEMENTED(); - return false; + return IsValidSemaphoreParameter(context, pname); } bool ValidateSignalSemaphoreEXT(Context *context, diff --git a/src/tests/gl_tests/ImageTestMetal.mm b/src/tests/gl_tests/ImageTestMetal.mm index 850a35f200..d3ed7e1c04 100644 --- a/src/tests/gl_tests/ImageTestMetal.mm +++ b/src/tests/gl_tests/ImageTestMetal.mm @@ -25,67 +25,75 @@ 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 char kSemaphoreExt[] = "GL_EXT_semaphore"; +constexpr char kSemaphoreFdExt[] = "GL_EXT_semaphore_fd"; +constexpr char kSemaphoreTimelineExt[] = "GL_MGL_timeline_semaphore"; constexpr EGLint kDefaultAttribs[] = { EGL_NONE, }; } // anonymous namespace -class ScopeMetalTextureRef : angle::NonCopyable +template +class ScopedMetalRef : angle::NonCopyable { public: - explicit ScopeMetalTextureRef(id surface) : mSurface(surface) {} + ScopedMetalRef() = default; + explicit ScopedMetalRef(T ref) : mMetalRef(ref) {} - ~ScopeMetalTextureRef() + ~ScopedMetalRef() { - if (mSurface) + if (mMetalRef) { release(); - mSurface = nullptr; + mMetalRef = nullptr; } } - id get() const { return mSurface; } + T get() const { return mMetalRef; } // auto cast to MTLTexture - operator id() const { return mSurface; } - ScopeMetalTextureRef(const ScopeMetalTextureRef &other) + operator T() const { return mMetalRef; } + ScopedMetalRef(const ScopedMetalRef &other) { - if (mSurface) + if (mMetalRef) { release(); } - mSurface = other.mSurface; + mMetalRef = other.mMetalRef; } - explicit ScopeMetalTextureRef(ScopeMetalTextureRef &&other) + explicit ScopedMetalRef(ScopedMetalRef &&other) { - if (mSurface) + if (mMetalRef) { release(); } - mSurface = other.mSurface; - other.mSurface = nil; + mMetalRef = other.mMetalRef; + other.mMetalRef = nil; } - ScopeMetalTextureRef &operator=(ScopeMetalTextureRef &&other) + ScopedMetalRef &operator=(ScopedMetalRef &&other) { - if (mSurface) + if (mMetalRef) { release(); } - mSurface = other.mSurface; - other.mSurface = nil; + mMetalRef = other.mMetalRef; + other.mMetalRef = nil; return *this; } - ScopeMetalTextureRef &operator=(const ScopeMetalTextureRef &other) + ScopedMetalRef &operator=(const ScopedMetalRef &other) { - if (mSurface) + if (mMetalRef) { release(); } - mSurface = other.mSurface; + mMetalRef = other.mMetalRef; +#if !__has_feature(objc_arc) + [mMetalRef retain]; +#endif return *this; } @@ -94,17 +102,20 @@ explicit ScopeMetalTextureRef(ScopeMetalTextureRef &&other) void release() { #if !__has_feature(objc_arc) - [mSurface release]; + [mMetalRef release]; #endif } - id mSurface = nil; + T mMetalRef = nil; }; -ScopeMetalTextureRef CreateMetalTexture2D(id deviceMtl, - int width, - int height, - MTLPixelFormat format) +using ScopedMetalTextureRef = ScopedMetalRef>; +using ScopedMetalSharedEventRef = ScopedMetalRef>; + +ScopedMetalTextureRef CreateMetalTexture2D(id deviceMtl, + int width, + int height, + MTLPixelFormat format) { @autoreleasepool { @@ -116,14 +127,14 @@ ScopeMetalTextureRef CreateMetalTexture2D(id deviceMtl, id texture = [deviceMtl newTextureWithDescriptor:desc]; - ScopeMetalTextureRef re(texture); + ScopedMetalTextureRef re(texture); return re; } } -ScopeMetalTextureRef CreateMetalTextureCube(id deviceMtl, - int width, - MTLPixelFormat format) +ScopedMetalTextureRef CreateMetalTextureCube(id deviceMtl, + int width, + MTLPixelFormat format) { @autoreleasepool { @@ -135,7 +146,7 @@ ScopeMetalTextureRef CreateMetalTextureCube(id deviceMtl, id texture = [deviceMtl newTextureWithDescriptor:desc]; - ScopeMetalTextureRef re(texture); + ScopedMetalTextureRef re(texture); return re; } } @@ -223,22 +234,41 @@ void testTearDown() override return (__bridge id)reinterpret_cast(device); } - ScopeMetalTextureRef createMtlTexture2D(int width, int height, MTLPixelFormat format) + ScopedMetalTextureRef createMtlTexture2D(int width, int height, MTLPixelFormat format) { id device = getMtlDevice(); return CreateMetalTexture2D(device, width, height, format); } - ScopeMetalTextureRef createMtlTextureCube(int width, MTLPixelFormat format) + ScopedMetalTextureRef 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 sourceMetalTarget2D_helper(const EGLint *attribs, + int width, + int height, + MTLPixelFormat format, + ScopedMetalTextureRef *metalTexture, + EGLImageKHR *eglImage, + GLuint *textureTarget); + void sourceMetalTargetCube_helper(const EGLint *attribs, + int size, + MTLPixelFormat format, + ScopedMetalTextureRef *metalTexture, + EGLImageKHR *eglImage, + GLuint *textureTarget); + + void sourceMetalSharedEvent_helper(ScopedMetalSharedEventRef *sharedEventMtlOut, + GLuint *semaphoreOut); + + void clearTextureInMtl(id textureMtl, + id cmdBufferMtl, + const GLubyte data[4]); + void clearTexture(GLuint texture, const GLubyte data[4]); void verifyResultsTexture(GLuint texture, GLubyte data[4], @@ -308,6 +338,12 @@ bool hasBaseExt() const return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), kBaseExt); } + bool hasSemaphoreExts() const + { + return IsGLExtensionEnabled(kSemaphoreExt) && IsGLExtensionEnabled(kSemaphoreFdExt) && + IsGLExtensionEnabled(kSemaphoreTimelineExt); + } + GLuint mTextureProgram; GLint mTextureUniformLocation; @@ -315,17 +351,49 @@ bool hasBaseExt() const GLint mTextureCubeUniformLocation = -1; }; -void ImageTestMetal::SourceMetalTarget2D_helper(const EGLint *attribs) +void ImageTestMetal::sourceMetalTarget2D_helper(const EGLint *attribs, + int width, + int height, + MTLPixelFormat format, + ScopedMetalTextureRef *metalTextureOut, + EGLImageKHR *eglImageOut, + GLuint *textureTargetOut) { EGLWindow *window = getEGLWindow(); - ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); - ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); + // Create MTLTexture + ScopedMetalTextureRef textureMtl = createMtlTexture2D(width, height, format); - GLubyte data[4] = {7, 51, 197, 231}; + // Create image + EGLImageKHR image = + eglCreateImageKHR(window->getDisplay(), EGL_NO_CONTEXT, EGL_MTL_TEXTURE_MGL, + reinterpret_cast(textureMtl.get()), attribs); + ASSERT_EGL_SUCCESS(); + + // Create a texture target to bind the egl image + GLuint target; + glGenTextures(1, &target); + glBindTexture(GL_TEXTURE_2D, target); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + // return results + *metalTextureOut = std::move(textureMtl); + *eglImageOut = image; + *textureTargetOut = target; +} + +void ImageTestMetal::sourceMetalTargetCube_helper(const EGLint *attribs, + int size, + MTLPixelFormat format, + ScopedMetalTextureRef *metalTextureOut, + EGLImageKHR *eglImageOut, + GLuint *textureTargetOut) +{ + EGLWindow *window = getEGLWindow(); // Create MTLTexture - ScopeMetalTextureRef textureMtl = createMtlTexture2D(1, 1, MTLPixelFormatRGBA8Unorm); + ScopedMetalTextureRef textureMtl = createMtlTextureCube(size, format); // Create image EGLImageKHR image = @@ -333,6 +401,97 @@ bool hasBaseExt() const reinterpret_cast(textureMtl.get()), attribs); ASSERT_EGL_SUCCESS(); + // 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); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + // return results + *metalTextureOut = std::move(textureMtl); + *eglImageOut = image; + *textureTargetOut = target; +} + +void ImageTestMetal::sourceMetalSharedEvent_helper(ScopedMetalSharedEventRef *sharedEventMtlOut, + GLuint *semaphoreOut) +{ + id deviceMtl = getMtlDevice(); + ScopedMetalSharedEventRef sharedEventMtl([deviceMtl newSharedEvent]); + + // Write to file and pass its fd to OpenGL. + // NOTE: fd will be owned by OpenGL, so don't close it. + char name[] = "/tmp/XXXXXX"; + int tmpFd; + ASSERT_NE((tmpFd = mkstemp(name)), -1); + unlink(name); + + void *sharedEventPtr = (__bridge void *)sharedEventMtl; + pwrite(tmpFd, &sharedEventPtr, sizeof(sharedEventPtr), 0); + + // Import to OpenGL + GLuint glSemaphore; + glGenSemaphoresEXT(1, &glSemaphore); + ASSERT_GL_NO_ERROR(); + + glImportSemaphoreFdEXT(glSemaphore, GL_HANDLE_TYPE_OPAQUE_FD_EXT, tmpFd); + ASSERT_GL_NO_ERROR(); + + // Return values + *sharedEventMtlOut = std::move(sharedEventMtl); + *semaphoreOut = glSemaphore; +} + +void ImageTestMetal::clearTextureInMtl(id textureMtl, + id cmdBufferMtl, + const GLubyte data[4]) +{ + ScopedMetalRef clearPassMtl( + [MTLRenderPassDescriptor renderPassDescriptor]); + clearPassMtl.get().colorAttachments[0].texture = textureMtl; + clearPassMtl.get().colorAttachments[0].loadAction = MTLLoadActionClear; + clearPassMtl.get().colorAttachments[0].storeAction = MTLStoreActionStore; + clearPassMtl.get().colorAttachments[0].clearColor = + MTLClearColorMake(data[0] / 255.0, data[1] / 255.0, data[2] / 255.0, data[3] / 255.0); + + ScopedMetalRef> clearPassEncoderMtl( + [cmdBufferMtl renderCommandEncoderWithDescriptor:clearPassMtl]); + [clearPassEncoderMtl.get() endEncoding]; +} + +void ImageTestMetal::clearTexture(GLuint texture, const GLubyte data[4]) +{ + GLFramebuffer fbo; + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + EXPECT_GL_NO_ERROR(); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); + EXPECT_GL_NO_ERROR(); + EXPECT_GLENUM_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE); + EXPECT_GL_NO_ERROR(); + + glClearColor(data[0] / 255.0f, data[1] / 255.0f, data[2] / 255.0f, data[3] / 255.0f); + EXPECT_GL_NO_ERROR(); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_GL_NO_ERROR(); +} + +// Testing source metal EGL image, target 2D texture +TEST_P(ImageTestMetal, SourceMetalTarget2D) +{ + ANGLE_SKIP_TEST_IF(!IsMetal()); + ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); + ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); + + GLubyte data[4] = {7, 51, 197, 231}; + + ScopedMetalTextureRef textureMtl; + + EGLImageKHR eglImage; + GLuint glTarget; + sourceMetalTarget2D_helper(kDefaultAttribs, 1, 1, MTLPixelFormatRGBA8Unorm, &textureMtl, + &eglImage, &glTarget); + // Write the data to the MTLTexture [textureMtl.get() replaceRegion:MTLRegionMake2D(0, 0, 1, 1) mipmapLevel:0 @@ -341,42 +500,34 @@ bool hasBaseExt() const 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); + verifyResults2D(glTarget, data); // Clean up - eglDestroyImageKHR(window->getDisplay(), image); - glDeleteTextures(1, &target); + eglDestroyImageKHR(getEGLWindow()->getDisplay(), eglImage); + glDeleteTextures(1, &glTarget); } -void ImageTestMetal::SourceMetalTargetCube_helper(const EGLint *attribs) +// Testing source metal EGL image, target Cube texture +TEST_P(ImageTestMetal, SourceMetalTargetCube) { - EGLWindow *window = getEGLWindow(); - + ANGLE_SKIP_TEST_IF(!IsMetal()); ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt() || !hasEglImageCubeExt()); ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); + ScopedMetalTextureRef textureMtl; + + EGLImageKHR eglImage; + GLuint glTarget; + sourceMetalTargetCube_helper(kDefaultAttribs, 1, MTLPixelFormatRGBA16Float, &textureMtl, + &eglImage, &glTarget); + 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) { @@ -388,38 +539,111 @@ bool hasBaseExt() const 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); + verifyResultsCube(glTarget, data); // Clean up - eglDestroyImageKHR(window->getDisplay(), image); - glDeleteTextures(1, &target); + eglDestroyImageKHR(getEGLWindow()->getDisplay(), eglImage); + glDeleteTextures(1, &glTarget); } -// Testing source metal EGL image, target 2D texture -TEST_P(ImageTestMetal, SourceMetalTarget2D) +// Testing texture interop drawing +TEST_P(ImageTestMetal, SourceMetalInteropDraws) { ANGLE_SKIP_TEST_IF(!IsMetal()); - SourceMetalTarget2D_helper(kDefaultAttribs); -} + ANGLE_SKIP_TEST_IF(!hasOESExt() || !hasBaseExt()); + ANGLE_SKIP_TEST_IF(!hasImageNativeMetalTextureExt()); + ANGLE_SKIP_TEST_IF(!hasSemaphoreExts()); + + constexpr int kWidth = 4; + constexpr int kHeight = 4; + + // Create interop texture + ScopedMetalTextureRef sharedTextureMtl; + + EGLImageKHR eglImage; + GLuint glTarget; + sourceMetalTarget2D_helper(kDefaultAttribs, kWidth, kHeight, MTLPixelFormatRGBA8Unorm, + &sharedTextureMtl, &eglImage, &glTarget); + + // Create interop semaphore + ScopedMetalSharedEventRef sharedEventMtl; + GLuint glSemaphore; + + sourceMetalSharedEvent_helper(&sharedEventMtl, &glSemaphore); + + // 1: Use Metal to clear texture to desired color + GLColor mtlData = {7, 51, 197, 231}; + uint64_t eventTimeline = 0; + + id deviceMtl = getMtlDevice(); + ScopedMetalRef> cmdQueueMtl([deviceMtl newCommandQueue]); + ScopedMetalRef> cmdBufferMtl([cmdQueueMtl.get() commandBuffer]); + + clearTextureInMtl(sharedTextureMtl, cmdBufferMtl, mtlData.data()); + + [cmdBufferMtl.get() encodeSignalEvent:sharedEventMtl value:++eventTimeline]; + [cmdBufferMtl.get() commit]; + + // 2: Verify the result in OpenGL + glSemaphoreParameterui64vEXT(glSemaphore, GL_TIMELINE_SEMAPHORE_VALUE_MGL, &eventTimeline); + GLenum imageLayout = GL_LAYOUT_COLOR_ATTACHMENT_EXT; + glWaitSemaphoreEXT(glSemaphore, 0, nullptr, 1, &glTarget, &imageLayout); + verifyResults2D(glTarget, mtlData.data()); + + // 3: clear texture to red in OpenGL + GLColor redData = {255, 0, 0, 255}; + clearTexture(glTarget, redData.data()); + eventTimeline++; + glSemaphoreParameterui64vEXT(glSemaphore, GL_TIMELINE_SEMAPHORE_VALUE_MGL, &eventTimeline); + glSignalSemaphoreEXT(glSemaphore, 0, nullptr, 1, &glTarget, &imageLayout); + + // 4: Mix the final result in Metal (copy shared texture's color to first pixel of final + // texture) + ScopedMetalTextureRef finalTextureMtl = + createMtlTexture2D(kWidth, kHeight, MTLPixelFormatRGBA8Unorm); + cmdBufferMtl = ScopedMetalRef>([cmdQueueMtl.get() commandBuffer]); + [cmdBufferMtl.get() encodeWaitForEvent:sharedEventMtl value:eventTimeline]; + + GLColor blueData = {0, 0, 255, 255}; + clearTextureInMtl(finalTextureMtl, cmdBufferMtl, blueData.data()); + + ScopedMetalRef> blitEncoderMtl( + [cmdBufferMtl.get() blitCommandEncoder]); + [blitEncoderMtl.get() copyFromTexture:sharedTextureMtl + sourceSlice:0 + sourceLevel:0 + sourceOrigin:MTLOriginMake(0, 0, 0) + sourceSize:MTLSizeMake(1, 1, 1) + toTexture:finalTextureMtl + destinationSlice:0 + destinationLevel:0 + destinationOrigin:MTLOriginMake(0, 0, 0)]; + [blitEncoderMtl.get() synchronizeResource:finalTextureMtl.get()]; + [blitEncoderMtl.get() endEncoding]; + + [cmdBufferMtl.get() commit]; + + // 5: Verify the final result + [cmdBufferMtl.get() waitUntilCompleted]; + GLColor finalData[kHeight][kWidth]; + [finalTextureMtl.get() getBytes:finalData + bytesPerRow:kWidth * 4 + fromRegion:MTLRegionMake2D(0, 0, kWidth, kHeight) + mipmapLevel:0]; + EXPECT_COLOR_NEAR(finalData[0][0], redData, 2); // region with color from shared texture + EXPECT_COLOR_NEAR(finalData[0][1], blueData, 2); // region with color from clear op in Metal + EXPECT_COLOR_NEAR(finalData[1][0], blueData, 2); // region with color from clear op in Metal + EXPECT_COLOR_NEAR(finalData[1][1], blueData, 2); // region with color from clear op in Metal -// Testing source metal EGL image, target Cube texture -TEST_P(ImageTestMetal, SourceMetalTargetCube) -{ - ANGLE_SKIP_TEST_IF(!IsMetal()); - SourceMetalTargetCube_helper(kDefaultAttribs); + // Clean up + eglDestroyImageKHR(getEGLWindow()->getDisplay(), eglImage); + glDeleteTextures(1, &glTarget); + glDeleteSemaphoresEXT(1, &glSemaphore); } // 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()); +ANGLE_INSTANTIATE_TEST(ImageTestMetal, ES2_METAL(), ES3_METAL()); } // namespace angle