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