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