xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/vulkan/mac/IOSurfaceSurfaceVkMac.mm (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1//
2// Copyright 2020 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// IOSurfaceSurfaceVkMac.mm:
7//    Implements methods from IOSurfaceSurfaceVkMac.
8//
9
10#include "libANGLE/renderer/vulkan/mac/IOSurfaceSurfaceVkMac.h"
11#include "libANGLE/Context.h"
12#include "libANGLE/Display.h"
13#include "libANGLE/Surface.h"
14#include "libANGLE/renderer/vulkan/ContextVk.h"
15#include "libANGLE/renderer/vulkan/DisplayVk.h"
16#include "libANGLE/renderer/vulkan/FramebufferVk.h"
17#include "libANGLE/renderer/vulkan/TextureVk.h"
18#include "libANGLE/renderer/vulkan/vk_format_utils.h"
19#include "libANGLE/renderer/vulkan/vk_helpers.h"
20
21#include <IOSurface/IOSurface.h>
22
23namespace rx
24{
25
26namespace
27{
28
29struct IOSurfaceFormatInfo
30{
31    GLenum internalFormat;
32    GLenum type;
33
34    size_t componentBytes;
35
36    GLenum nativeSizedInternalFormat;
37};
38
39// clang-format off
40constexpr std::array<IOSurfaceFormatInfo, 9> kIOSurfaceFormats = {{
41    {GL_RED,      GL_UNSIGNED_BYTE,                1, GL_R8},
42    {GL_RED,      GL_UNSIGNED_SHORT,               2, GL_R16_EXT},
43    {GL_RG,       GL_UNSIGNED_BYTE,                2, GL_RG8},
44    {GL_RG,       GL_UNSIGNED_SHORT,               4, GL_RG16_EXT},
45    {GL_RGB,      GL_UNSIGNED_BYTE,                4, GL_RGBX8_ANGLE},
46    {GL_BGRA_EXT, GL_UNSIGNED_BYTE,                4, GL_BGRA8_EXT},
47    {GL_RGB10_A2, GL_UNSIGNED_INT_2_10_10_10_REV,  4, GL_BGR10_A2_ANGLEX},
48    {GL_RGBA,     GL_HALF_FLOAT,                   8, GL_RGBA16F},
49}};
50// clang-format on
51
52int FindIOSurfaceFormatIndex(GLenum internalFormat, GLenum type)
53{
54    for (int i = 0; i < static_cast<int>(kIOSurfaceFormats.size()); ++i)
55    {
56        const auto &formatInfo = kIOSurfaceFormats[i];
57        if (formatInfo.internalFormat == internalFormat && formatInfo.type == type)
58        {
59            return i;
60        }
61    }
62    return -1;
63}
64
65}  // anonymous namespace
66
67IOSurfaceSurfaceVkMac::IOSurfaceSurfaceVkMac(const egl::SurfaceState &state,
68                                             EGLClientBuffer buffer,
69                                             const egl::AttributeMap &attribs,
70                                             vk::Renderer *renderer)
71    : OffscreenSurfaceVk(state, renderer), mIOSurface(nullptr), mPlane(0), mFormatIndex(-1)
72{
73    // Keep reference to the IOSurface so it doesn't get deleted while the pbuffer exists.
74    mIOSurface = reinterpret_cast<IOSurfaceRef>(buffer);
75    CFRetain(mIOSurface);
76
77    // Extract attribs useful for the call to CGLTexImageIOSurface2D
78    mWidth  = static_cast<int>(attribs.get(EGL_WIDTH));
79    mHeight = static_cast<int>(attribs.get(EGL_HEIGHT));
80    mPlane  = static_cast<int>(attribs.get(EGL_IOSURFACE_PLANE_ANGLE));
81
82    EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE);
83    EGLAttrib type           = attribs.get(EGL_TEXTURE_TYPE_ANGLE);
84    mFormatIndex =
85        FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type));
86    ASSERT(mFormatIndex >= 0);
87}
88
89IOSurfaceSurfaceVkMac::~IOSurfaceSurfaceVkMac()
90{
91    if (mIOSurface != nullptr)
92    {
93        CFRelease(mIOSurface);
94        mIOSurface = nullptr;
95    }
96}
97
98egl::Error IOSurfaceSurfaceVkMac::initialize(const egl::Display *display)
99{
100    DisplayVk *displayVk = vk::GetImpl(display);
101    angle::Result result = initializeImpl(displayVk);
102    return angle::ToEGL(result, EGL_BAD_SURFACE);
103}
104
105angle::Result IOSurfaceSurfaceVkMac::initializeImpl(DisplayVk *displayVk)
106{
107    vk::Renderer *renderer    = displayVk->getRenderer();
108    const egl::Config *config = mState.config;
109
110    // Should never be > 1
111    GLint samples = 1;
112    if (config->sampleBuffers && config->samples > 1)
113    {
114        samples = config->samples;
115    }
116    ANGLE_VK_CHECK(displayVk, samples == 1, VK_ERROR_INITIALIZATION_FAILED);
117
118    const vk::Format &format =
119        renderer->getFormat(kIOSurfaceFormats[mFormatIndex].nativeSizedInternalFormat);
120
121    // Swiftshader will use the raw pointer to the buffer referenced by the IOSurfaceRef
122    ANGLE_TRY(mColorAttachment.initialize(displayVk, mWidth, mHeight, format, samples,
123                                          mState.isRobustResourceInitEnabled(),
124                                          mState.hasProtectedContent()));
125
126    mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageViews, nullptr, nullptr,
127                            {}, gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default);
128
129    return angle::Result::Continue;
130}
131
132egl::Error IOSurfaceSurfaceVkMac::unMakeCurrent(const gl::Context *context)
133{
134    ASSERT(context != nullptr);
135    ContextVk *contextVk = vk::GetImpl(context);
136    angle::Result result =
137        contextVk->flushAndSubmitCommands(nullptr, nullptr, RenderPassClosureReason::ContextChange);
138    return angle::ToEGL(result, EGL_BAD_SURFACE);
139}
140
141int IOSurfaceSurfaceVkMac::computeAlignment() const
142{
143    size_t rowBytes         = IOSurfaceGetBytesPerRowOfPlane(mIOSurface, mPlane);
144    size_t desiredAlignment = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, 1);
145    size_t alignment        = 1;
146    while (alignment < desiredAlignment)
147    {
148        if (rowBytes & alignment)
149        {
150            break;
151        }
152        alignment <<= 1;
153    }
154    return static_cast<int>(alignment);
155}
156
157egl::Error IOSurfaceSurfaceVkMac::bindTexImage(const gl::Context *context,
158                                               gl::Texture *texture,
159                                               EGLint buffer)
160{
161    IOSurfaceLock(mIOSurface, 0, nullptr);
162
163    ContextVk *contextVk   = vk::GetImpl(context);
164    vk::Renderer *renderer = contextVk->getRenderer();
165
166    size_t width             = IOSurfaceGetWidthOfPlane(mIOSurface, mPlane);
167    size_t height            = IOSurfaceGetHeightOfPlane(mIOSurface, mPlane);
168    size_t rowLengthInPixels = IOSurfaceGetBytesPerRowOfPlane(mIOSurface, mPlane) /
169                               IOSurfaceGetBytesPerElementOfPlane(mIOSurface, mPlane);
170
171    gl::PixelUnpackState pixelUnpack;
172    pixelUnpack.alignment   = computeAlignment();
173    pixelUnpack.rowLength   = static_cast<GLint>(rowLengthInPixels);
174    pixelUnpack.imageHeight = static_cast<GLint>(height);
175
176    void *source = IOSurfaceGetBaseAddressOfPlane(mIOSurface, mPlane);
177
178    const gl::InternalFormat &internalFormatInfo =
179        gl::GetSizedInternalFormatInfo(kIOSurfaceFormats[mFormatIndex].nativeSizedInternalFormat);
180    const vk::Format &format =
181        renderer->getFormat(kIOSurfaceFormats[mFormatIndex].nativeSizedInternalFormat);
182
183    bool updateAppliedImmediately = false;
184    angle::Result result          = mColorAttachment.image.stageSubresourceUpdate(
185        contextVk, gl::ImageIndex::Make2D(0),
186        gl::Extents(static_cast<int>(width), pixelUnpack.imageHeight, 1), gl::Offset(),
187        internalFormatInfo, pixelUnpack, kIOSurfaceFormats[mFormatIndex].type,
188        reinterpret_cast<uint8_t *>(source), format, vk::ImageAccess::Renderable,
189        vk::ApplyImageUpdate::Defer, &updateAppliedImmediately);
190
191    IOSurfaceUnlock(mIOSurface, 0, nullptr);
192
193    return angle::ToEGL(result, EGL_BAD_SURFACE);
194}
195
196egl::Error IOSurfaceSurfaceVkMac::releaseTexImage(const gl::Context *context, EGLint buffer)
197{
198    ASSERT(context != nullptr);
199    ContextVk *contextVk = vk::GetImpl(context);
200
201    angle::Result result = mColorAttachment.image.flushAllStagedUpdates(contextVk);
202
203    if (result != angle::Result::Continue)
204    {
205        return angle::ToEGL(result, EGL_BAD_SURFACE);
206    }
207
208    gl::Rectangle bounds(0, 0, mWidth, mHeight);
209
210    const angle::Format &dstFormat = angle::Format::Get(angle::Format::InternalFormatToID(
211        kIOSurfaceFormats[mFormatIndex].nativeSizedInternalFormat));
212
213    IOSurfaceLock(mIOSurface, 0, nullptr);
214
215    size_t outputRowPitchInBytes = IOSurfaceGetBytesPerRowOfPlane(mIOSurface, mPlane);
216
217    PackPixelsParams params(bounds, dstFormat, static_cast<GLuint>(outputRowPitchInBytes),
218                            contextVk->isViewportFlipEnabledForDrawFBO(), nullptr, 0);
219
220    result = mColorAttachment.image.readPixels(contextVk, bounds, params, VK_IMAGE_ASPECT_COLOR_BIT,
221                                               gl::LevelIndex(0), 0,
222                                               IOSurfaceGetBaseAddressOfPlane(mIOSurface, mPlane));
223
224    IOSurfaceUnlock(mIOSurface, 0, nullptr);
225
226    return angle::ToEGL(result, EGL_BAD_SURFACE);
227}
228
229// static
230bool IOSurfaceSurfaceVkMac::ValidateAttributes(const DisplayVk *displayVk,
231                                               EGLClientBuffer buffer,
232                                               const egl::AttributeMap &attribs)
233{
234    ASSERT(displayVk != nullptr);
235    IOSurfaceRef ioSurface = reinterpret_cast<IOSurfaceRef>(buffer);
236
237    // The plane must exist for this IOSurface. IOSurfaceGetPlaneCount can return 0 for non-planar
238    // ioSurfaces but we will treat non-planar like it is a single plane.
239    size_t surfacePlaneCount = std::max(size_t(1), IOSurfaceGetPlaneCount(ioSurface));
240    EGLAttrib plane          = attribs.get(EGL_IOSURFACE_PLANE_ANGLE);
241    if (plane < 0 || static_cast<size_t>(plane) >= surfacePlaneCount)
242    {
243        return false;
244    }
245
246    // The width height specified must be at least (1, 1) and at most the plane size
247    EGLAttrib width  = attribs.get(EGL_WIDTH);
248    EGLAttrib height = attribs.get(EGL_HEIGHT);
249    if (width <= 0 || static_cast<size_t>(width) > IOSurfaceGetWidthOfPlane(ioSurface, plane) ||
250        height <= 0 || static_cast<size_t>(height) > IOSurfaceGetHeightOfPlane(ioSurface, plane))
251    {
252        return false;
253    }
254
255    // Find this IOSurface format
256    EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE);
257    EGLAttrib type           = attribs.get(EGL_TEXTURE_TYPE_ANGLE);
258
259    int formatIndex =
260        FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type));
261
262    if (formatIndex < 0)
263    {
264        return false;
265    }
266
267    // Check that the format matches this IOSurface plane
268    if (IOSurfaceGetBytesPerElementOfPlane(ioSurface, plane) !=
269        kIOSurfaceFormats[formatIndex].componentBytes)
270    {
271        return false;
272    }
273
274    return true;
275}
276
277}  // namespace rx
278