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 19#include <IOSurface/IOSurface.h> 20 21namespace rx 22{ 23 24namespace 25{ 26 27struct IOSurfaceFormatInfo 28{ 29 GLenum internalFormat; 30 GLenum type; 31 32 size_t componentBytes; 33 34 GLenum nativeSizedInternalFormat; 35}; 36 37// clang-format off 38constexpr std::array<IOSurfaceFormatInfo, 6> kIOSurfaceFormats = {{ 39 {GL_RED, GL_UNSIGNED_BYTE, 1, GL_R8 }, 40 {GL_R16UI, GL_UNSIGNED_SHORT, 2, GL_R16UI }, 41 {GL_RG, GL_UNSIGNED_BYTE, 2, GL_RG8 }, 42 {GL_RGB, GL_UNSIGNED_BYTE, 4, GL_BGRA8_EXT}, 43 {GL_BGRA_EXT, GL_UNSIGNED_BYTE, 4, GL_BGRA8_EXT }, 44 {GL_RGBA, GL_HALF_FLOAT, 8, GL_RGBA16F }, 45}}; 46// clang-format on 47 48int FindIOSurfaceFormatIndex(GLenum internalFormat, GLenum type) 49{ 50 for (int i = 0; i < static_cast<int>(kIOSurfaceFormats.size()); ++i) 51 { 52 const auto &formatInfo = kIOSurfaceFormats[i]; 53 if (formatInfo.internalFormat == internalFormat && formatInfo.type == type) 54 { 55 return i; 56 } 57 } 58 return -1; 59} 60 61} // anonymous namespace 62 63IOSurfaceSurfaceVkMac::IOSurfaceSurfaceVkMac(const egl::SurfaceState &state, 64 EGLClientBuffer buffer, 65 const egl::AttributeMap &attribs) 66 : OffscreenSurfaceVk(state), mIOSurface(nullptr), mPlane(0), mFormatIndex(-1) 67{ 68 // Keep reference to the IOSurface so it doesn't get deleted while the pbuffer exists. 69 mIOSurface = reinterpret_cast<IOSurfaceRef>(buffer); 70 CFRetain(mIOSurface); 71 72 // Extract attribs useful for the call to CGLTexImageIOSurface2D 73 mWidth = static_cast<int>(attribs.get(EGL_WIDTH)); 74 mHeight = static_cast<int>(attribs.get(EGL_HEIGHT)); 75 mPlane = static_cast<int>(attribs.get(EGL_IOSURFACE_PLANE_ANGLE)); 76 77 EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE); 78 EGLAttrib type = attribs.get(EGL_TEXTURE_TYPE_ANGLE); 79 mFormatIndex = 80 FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type)); 81 ASSERT(mFormatIndex >= 0); 82} 83 84IOSurfaceSurfaceVkMac::~IOSurfaceSurfaceVkMac() 85{ 86 if (mIOSurface != nullptr) 87 { 88 CFRelease(mIOSurface); 89 mIOSurface = nullptr; 90 } 91} 92 93egl::Error IOSurfaceSurfaceVkMac::initialize(const egl::Display *display) 94{ 95 DisplayVk *displayVk = vk::GetImpl(display); 96 angle::Result result = initializeImpl(displayVk); 97 return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE); 98} 99 100angle::Result IOSurfaceSurfaceVkMac::initializeImpl(DisplayVk *displayVk) 101{ 102 RendererVk *renderer = displayVk->getRenderer(); 103 const egl::Config *config = mState.config; 104 105 GLint samples = 1; 106 if (config->sampleBuffers && config->samples > 1) 107 { 108 samples = config->samples; 109 } 110 ANGLE_VK_CHECK(displayVk, samples > 0, VK_ERROR_INITIALIZATION_FAILED); 111 112 // Swiftshader will use the raw pointer to the buffer referenced by the IOSurfaceRef 113 ANGLE_TRY(mColorAttachment.initializeWithExternalMemory( 114 displayVk, mWidth, mHeight, 115 renderer->getFormat(kIOSurfaceFormats[mFormatIndex].nativeSizedInternalFormat), samples, 116 IOSurfaceGetBaseAddressOfPlane(mIOSurface, mPlane))); 117 mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageViews, 0, 0); 118 119 return angle::Result::Continue; 120} 121 122egl::Error IOSurfaceSurfaceVkMac::unMakeCurrent(const gl::Context *context) 123{ 124 ASSERT(context != nullptr); 125 ContextVk *contextVk = vk::GetImpl(context); 126 DisplayVk *displayVk = vk::GetImpl(context->getDisplay()); 127 angle::Result result = contextVk->flushImpl(nullptr); 128 return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE); 129} 130 131egl::Error IOSurfaceSurfaceVkMac::bindTexImage(const gl::Context *context, 132 gl::Texture *texture, 133 EGLint buffer) 134{ 135 IOSurfaceLock(mIOSurface, 0, nullptr); 136 137 return egl::NoError(); 138} 139 140egl::Error IOSurfaceSurfaceVkMac::releaseTexImage(const gl::Context *context, EGLint buffer) 141{ 142 ASSERT(context != nullptr); 143 ContextVk *contextVk = vk::GetImpl(context); 144 DisplayVk *displayVk = vk::GetImpl(context->getDisplay()); 145 angle::Result result = contextVk->finishImpl(); 146 147 IOSurfaceUnlock(mIOSurface, 0, nullptr); 148 149 return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE); 150} 151 152// static 153bool IOSurfaceSurfaceVkMac::ValidateAttributes(const DisplayVk *displayVk, 154 EGLClientBuffer buffer, 155 const egl::AttributeMap &attribs) 156{ 157 ASSERT(displayVk != nullptr); 158 RendererVk *renderer = displayVk->getRenderer(); 159 160 IOSurfaceRef ioSurface = reinterpret_cast<IOSurfaceRef>(buffer); 161 162 // The plane must exist for this IOSurface. IOSurfaceGetPlaneCount can return 0 for non-planar 163 // ioSurfaces but we will treat non-planar like it is a single plane. 164 size_t surfacePlaneCount = std::max(size_t(1), IOSurfaceGetPlaneCount(ioSurface)); 165 EGLAttrib plane = attribs.get(EGL_IOSURFACE_PLANE_ANGLE); 166 if (plane < 0 || static_cast<size_t>(plane) >= surfacePlaneCount) 167 { 168 return false; 169 } 170 171 // The width height specified must be at least (1, 1) and at most the plane size 172 EGLAttrib width = attribs.get(EGL_WIDTH); 173 EGLAttrib height = attribs.get(EGL_HEIGHT); 174 if (width <= 0 || static_cast<size_t>(width) > IOSurfaceGetWidthOfPlane(ioSurface, plane) || 175 height <= 0 || static_cast<size_t>(height) > IOSurfaceGetHeightOfPlane(ioSurface, plane)) 176 { 177 return false; 178 } 179 180 // Find this IOSurface format 181 EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE); 182 EGLAttrib type = attribs.get(EGL_TEXTURE_TYPE_ANGLE); 183 184 int formatIndex = 185 FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type)); 186 187 if (formatIndex < 0) 188 { 189 return false; 190 } 191 192 // Check that the format matches this IOSurface plane 193 if (IOSurfaceGetBytesPerElementOfPlane(ioSurface, plane) != 194 kIOSurfaceFormats[formatIndex].componentBytes) 195 { 196 return false; 197 } 198 199 void *pointer = IOSurfaceGetBaseAddressOfPlane(ioSurface, plane); 200 VkMemoryHostPointerPropertiesEXT memoryHostPointerProperties = {}; 201 memoryHostPointerProperties.sType = VK_STRUCTURE_TYPE_MEMORY_HOST_POINTER_PROPERTIES_EXT; 202 vkGetMemoryHostPointerPropertiesEXT( 203 renderer->getDevice(), VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT, 204 pointer, &memoryHostPointerProperties); 205 206 bool hostVisible = 207 memoryHostPointerProperties.memoryTypeBits & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; 208 if (!hostVisible) 209 { 210 return false; 211 } 212 213 VkDeviceSize alignment = renderer->getMinImportedHostPointerAlignment(); 214 if (reinterpret_cast<size_t>(pointer) % alignment != 0) 215 { 216 return false; 217 } 218 219 return true; 220} 221 222} // namespace rx 223