xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/metal/IOSurfaceSurfaceMtl.mm (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1//
2// Copyright 2019 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6// IOSurfaceSurfaceMtl.mm:
7//    Implements the class methods for IOSurfaceSurfaceMtl.
8//
9
10#include "libANGLE/renderer/metal/IOSurfaceSurfaceMtl.h"
11
12#include <TargetConditionals.h>
13
14#include "libANGLE/Display.h"
15#include "libANGLE/Surface.h"
16#include "libANGLE/renderer/metal/ContextMtl.h"
17#include "libANGLE/renderer/metal/DisplayMtl.h"
18#include "libANGLE/renderer/metal/FrameBufferMtl.h"
19#include "libANGLE/renderer/metal/mtl_format_utils.h"
20#include "libANGLE/renderer/metal/mtl_utils.h"
21
22// Compiler can turn on programmatical frame capture in release build by defining
23// ANGLE_METAL_FRAME_CAPTURE flag.
24#if defined(NDEBUG) && !defined(ANGLE_METAL_FRAME_CAPTURE)
25#    define ANGLE_METAL_FRAME_CAPTURE_ENABLED 0
26#else
27#    define ANGLE_METAL_FRAME_CAPTURE_ENABLED 1
28#endif
29namespace rx
30{
31
32namespace
33{
34
35struct IOSurfaceFormatInfo
36{
37    GLenum internalFormat;
38    GLenum type;
39    size_t componentBytes;
40
41    angle::FormatID nativeAngleFormatId;
42};
43
44// clang-format off
45// GL_RGB is a special case. The native angle::FormatID would be either R8G8B8X8_UNORM
46// or B8G8R8X8_UNORM based on the IOSurface's pixel format.
47constexpr std::array<IOSurfaceFormatInfo, 9> kIOSurfaceFormats = {{
48    {GL_RED,         GL_UNSIGNED_BYTE,                  1, angle::FormatID::R8_UNORM},
49    {GL_RED,         GL_UNSIGNED_SHORT,                 2, angle::FormatID::R16_UNORM},
50    {GL_RG,          GL_UNSIGNED_BYTE,                  2, angle::FormatID::R8G8_UNORM},
51    {GL_RG,          GL_UNSIGNED_SHORT,                 4, angle::FormatID::R16G16_UNORM},
52    {GL_RGB,         GL_UNSIGNED_BYTE,                  4, angle::FormatID::NONE},
53    {GL_RGBA,        GL_UNSIGNED_BYTE,                  4, angle::FormatID::R8G8B8A8_UNORM},
54    {GL_BGRA_EXT,    GL_UNSIGNED_BYTE,                  4, angle::FormatID::B8G8R8A8_UNORM},
55    {GL_RGBA,        GL_HALF_FLOAT,                     8, angle::FormatID::R16G16B16A16_FLOAT},
56    {GL_RGB10_A2,    GL_UNSIGNED_INT_2_10_10_10_REV,    4, angle::FormatID::B10G10R10A2_UNORM},
57}};
58// clang-format on
59
60int FindIOSurfaceFormatIndex(GLenum internalFormat, GLenum type)
61{
62    for (int i = 0; i < static_cast<int>(kIOSurfaceFormats.size()); ++i)
63    {
64        const auto &formatInfo = kIOSurfaceFormats[i];
65        if (formatInfo.internalFormat == internalFormat && formatInfo.type == type)
66        {
67            return i;
68        }
69    }
70    return -1;
71}
72
73}  // anonymous namespace
74
75// IOSurfaceSurfaceMtl implementation.
76IOSurfaceSurfaceMtl::IOSurfaceSurfaceMtl(DisplayMtl *display,
77                                         const egl::SurfaceState &state,
78                                         EGLClientBuffer buffer,
79                                         const egl::AttributeMap &attribs)
80    : OffscreenSurfaceMtl(display, state, attribs), mIOSurface((__bridge IOSurfaceRef)(buffer))
81{
82    CFRetain(mIOSurface);
83
84    mIOSurfacePlane = static_cast<int>(attribs.get(EGL_IOSURFACE_PLANE_ANGLE));
85
86    EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE);
87    EGLAttrib type           = attribs.get(EGL_TEXTURE_TYPE_ANGLE);
88    mIOSurfaceFormatIdx =
89        FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type));
90    ASSERT(mIOSurfaceFormatIdx >= 0);
91
92    angle::FormatID actualAngleFormatId =
93        kIOSurfaceFormats[mIOSurfaceFormatIdx].nativeAngleFormatId;
94    if (actualAngleFormatId == angle::FormatID::NONE)
95    {
96        // The actual angle::Format depends on the IOSurface's format.
97        ASSERT(internalFormat == GL_RGB);
98        switch (IOSurfaceGetPixelFormat(mIOSurface))
99        {
100            case 'BGRA':
101                actualAngleFormatId = angle::FormatID::B8G8R8X8_UNORM;
102                break;
103            case 'RGBA':
104                actualAngleFormatId = angle::FormatID::R8G8B8X8_UNORM;
105                break;
106            default:
107                UNREACHABLE();
108        }
109    }
110
111    mColorFormat = display->getPixelFormat(actualAngleFormatId);
112}
113IOSurfaceSurfaceMtl::~IOSurfaceSurfaceMtl()
114{
115    if (mIOSurface != nullptr)
116    {
117        CFRelease(mIOSurface);
118        mIOSurface = nullptr;
119    }
120}
121
122egl::Error IOSurfaceSurfaceMtl::bindTexImage(const gl::Context *context,
123                                             gl::Texture *texture,
124                                             EGLint buffer)
125{
126    ContextMtl *contextMtl = mtl::GetImpl(context);
127    StartFrameCapture(contextMtl);
128
129    // Initialize offscreen texture if needed:
130    ANGLE_TO_EGL_TRY(ensureColorTextureCreated(context));
131
132    return OffscreenSurfaceMtl::bindTexImage(context, texture, buffer);
133}
134
135egl::Error IOSurfaceSurfaceMtl::releaseTexImage(const gl::Context *context, EGLint buffer)
136{
137    egl::Error re = OffscreenSurfaceMtl::releaseTexImage(context, buffer);
138    StopFrameCapture();
139    return re;
140}
141
142angle::Result IOSurfaceSurfaceMtl::getAttachmentRenderTarget(
143    const gl::Context *context,
144    GLenum binding,
145    const gl::ImageIndex &imageIndex,
146    GLsizei samples,
147    FramebufferAttachmentRenderTarget **rtOut)
148{
149    // Initialize offscreen texture if needed:
150    ANGLE_TRY(ensureColorTextureCreated(context));
151
152    return OffscreenSurfaceMtl::getAttachmentRenderTarget(context, binding, imageIndex, samples,
153                                                          rtOut);
154}
155
156angle::Result IOSurfaceSurfaceMtl::ensureColorTextureCreated(const gl::Context *context)
157{
158    if (mColorTexture)
159    {
160        return angle::Result::Continue;
161    }
162    ContextMtl *contextMtl = mtl::GetImpl(context);
163    ANGLE_MTL_OBJC_SCOPE
164    {
165        auto texDesc =
166            [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:mColorFormat.metalFormat
167                                                               width:mSize.width
168                                                              height:mSize.height
169                                                           mipmapped:NO];
170
171        texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
172
173        mColorTexture =
174            mtl::Texture::MakeFromMetal(contextMtl->getMetalDevice().newTextureWithDescriptor(
175                texDesc, mIOSurface, mIOSurfacePlane));
176
177        if (mColorTexture)
178        {
179            size_t resourceSize = EstimateTextureSizeInBytes(
180                mColorFormat, mColorTexture->widthAt0(), mColorTexture->heightAt0(),
181                mColorTexture->depthAt0(), mColorTexture->samples(), mColorTexture->mipmapLevels());
182
183            mColorTexture->setEstimatedByteSize(resourceSize);
184        }
185    }
186
187    mColorRenderTarget.set(mColorTexture, mtl::kZeroNativeMipLevel, 0, mColorFormat);
188
189    if (kIOSurfaceFormats[mIOSurfaceFormatIdx].internalFormat == GL_RGB)
190    {
191        // This format has emulated alpha channel. Initialize texture's alpha channel to 1.0.
192        const mtl::Format &rgbClearFormat =
193            contextMtl->getPixelFormat(angle::FormatID::R8G8B8_UNORM);
194        ANGLE_TRY(mtl::InitializeTextureContentsGPU(
195            context, mColorTexture, rgbClearFormat,
196            mtl::ImageNativeIndex::FromBaseZeroGLIndex(gl::ImageIndex::Make2D(0)),
197            MTLColorWriteMaskAlpha));
198
199        // Disable subsequent rendering to alpha channel.
200        mColorTexture->setColorWritableMask(MTLColorWriteMaskAll & (~MTLColorWriteMaskAlpha));
201    }
202    // Robust resource init: currently we do not allow passing contents with IOSurfaces.
203    mColorTextureInitialized = false;
204
205    return angle::Result::Continue;
206}
207
208// static
209bool IOSurfaceSurfaceMtl::ValidateAttributes(EGLClientBuffer buffer,
210                                             const egl::AttributeMap &attribs)
211{
212    IOSurfaceRef ioSurface = (__bridge IOSurfaceRef)(buffer);
213
214    // The plane must exist for this IOSurface. IOSurfaceGetPlaneCount can return 0 for non-planar
215    // ioSurfaces but we will treat non-planar like it is a single plane.
216    size_t surfacePlaneCount = std::max(size_t(1), IOSurfaceGetPlaneCount(ioSurface));
217    EGLAttrib plane          = attribs.get(EGL_IOSURFACE_PLANE_ANGLE);
218    if (plane < 0 || static_cast<size_t>(plane) >= surfacePlaneCount)
219    {
220        return false;
221    }
222
223    // The width height specified must be at least (1, 1) and at most the plane size
224    EGLAttrib width  = attribs.get(EGL_WIDTH);
225    EGLAttrib height = attribs.get(EGL_HEIGHT);
226    if (width <= 0 || static_cast<size_t>(width) > IOSurfaceGetWidthOfPlane(ioSurface, plane) ||
227        height <= 0 || static_cast<size_t>(height) > IOSurfaceGetHeightOfPlane(ioSurface, plane))
228    {
229        return false;
230    }
231
232    // Find this IOSurface format
233    EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE);
234    EGLAttrib type           = attribs.get(EGL_TEXTURE_TYPE_ANGLE);
235
236    int formatIndex =
237        FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type));
238
239    if (formatIndex < 0)
240    {
241        return false;
242    }
243
244    // FIXME: Check that the format matches this IOSurface plane for pixel formats that we know of.
245    // We could map IOSurfaceGetPixelFormat to expected type plane and format type.
246    // However, the caller might supply us non-public pixel format, which makes exhaustive checks
247    // problematic.
248    if (IOSurfaceGetBytesPerElementOfPlane(ioSurface, plane) !=
249        kIOSurfaceFormats[formatIndex].componentBytes)
250    {
251        WARN() << "IOSurface bytes per elements does not match the pbuffer internal format.";
252    }
253
254    return true;
255}
256}  // namespace rx
257