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