| // |
| // Copyright 2020 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // IOSurfaceSurfaceVkMac.mm: |
| // Implements methods from IOSurfaceSurfaceVkMac. |
| // |
| |
| #include "libANGLE/renderer/vulkan/mac/IOSurfaceSurfaceVkMac.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/Display.h" |
| #include "libANGLE/Surface.h" |
| #include "libANGLE/renderer/vulkan/ContextVk.h" |
| #include "libANGLE/renderer/vulkan/DisplayVk.h" |
| #include "libANGLE/renderer/vulkan/FramebufferVk.h" |
| #include "libANGLE/renderer/vulkan/TextureVk.h" |
| #include "libANGLE/renderer/vulkan/vk_format_utils.h" |
| #include "libANGLE/renderer/vulkan/vk_helpers.h" |
| |
| #include <IOSurface/IOSurface.h> |
| |
| namespace rx |
| { |
| |
| namespace |
| { |
| |
| struct IOSurfaceFormatInfo |
| { |
| GLenum internalFormat; |
| GLenum type; |
| |
| size_t componentBytes; |
| |
| GLenum nativeSizedInternalFormat; |
| }; |
| |
| // clang-format off |
| constexpr std::array<IOSurfaceFormatInfo, 9> kIOSurfaceFormats = {{ |
| {GL_RED, GL_UNSIGNED_BYTE, 1, GL_R8}, |
| {GL_RED, GL_UNSIGNED_SHORT, 2, GL_R16_EXT}, |
| {GL_R16UI, GL_UNSIGNED_SHORT, 2, GL_R16UI}, |
| {GL_RG, GL_UNSIGNED_BYTE, 2, GL_RG8}, |
| {GL_RG, GL_UNSIGNED_SHORT, 4, GL_RG16_EXT}, |
| {GL_RGB, GL_UNSIGNED_BYTE, 4, GL_RGBX8_ANGLE}, |
| {GL_BGRA_EXT, GL_UNSIGNED_BYTE, 4, GL_BGRA8_EXT}, |
| {GL_RGB10_A2, GL_UNSIGNED_INT_2_10_10_10_REV, 4, GL_BGR10_A2_ANGLEX}, |
| {GL_RGBA, GL_HALF_FLOAT, 8, GL_RGBA16F}, |
| }}; |
| // clang-format on |
| |
| int FindIOSurfaceFormatIndex(GLenum internalFormat, GLenum type) |
| { |
| for (int i = 0; i < static_cast<int>(kIOSurfaceFormats.size()); ++i) |
| { |
| const auto &formatInfo = kIOSurfaceFormats[i]; |
| if (formatInfo.internalFormat == internalFormat && formatInfo.type == type) |
| { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| } // anonymous namespace |
| |
| IOSurfaceSurfaceVkMac::IOSurfaceSurfaceVkMac(const egl::SurfaceState &state, |
| EGLClientBuffer buffer, |
| const egl::AttributeMap &attribs, |
| RendererVk *renderer) |
| : OffscreenSurfaceVk(state, renderer), mIOSurface(nullptr), mPlane(0), mFormatIndex(-1) |
| { |
| // Keep reference to the IOSurface so it doesn't get deleted while the pbuffer exists. |
| mIOSurface = reinterpret_cast<IOSurfaceRef>(buffer); |
| CFRetain(mIOSurface); |
| |
| // Extract attribs useful for the call to CGLTexImageIOSurface2D |
| mWidth = static_cast<int>(attribs.get(EGL_WIDTH)); |
| mHeight = static_cast<int>(attribs.get(EGL_HEIGHT)); |
| mPlane = static_cast<int>(attribs.get(EGL_IOSURFACE_PLANE_ANGLE)); |
| |
| EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE); |
| EGLAttrib type = attribs.get(EGL_TEXTURE_TYPE_ANGLE); |
| mFormatIndex = |
| FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type)); |
| ASSERT(mFormatIndex >= 0); |
| } |
| |
| IOSurfaceSurfaceVkMac::~IOSurfaceSurfaceVkMac() |
| { |
| if (mIOSurface != nullptr) |
| { |
| CFRelease(mIOSurface); |
| mIOSurface = nullptr; |
| } |
| } |
| |
| egl::Error IOSurfaceSurfaceVkMac::initialize(const egl::Display *display) |
| { |
| DisplayVk *displayVk = vk::GetImpl(display); |
| angle::Result result = initializeImpl(displayVk); |
| return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE); |
| } |
| |
| angle::Result IOSurfaceSurfaceVkMac::initializeImpl(DisplayVk *displayVk) |
| { |
| RendererVk *renderer = displayVk->getRenderer(); |
| const egl::Config *config = mState.config; |
| |
| // Should never be > 1 |
| GLint samples = 1; |
| if (config->sampleBuffers && config->samples > 1) |
| { |
| samples = config->samples; |
| } |
| ANGLE_VK_CHECK(displayVk, samples == 1, VK_ERROR_INITIALIZATION_FAILED); |
| |
| const vk::Format &format = |
| renderer->getFormat(kIOSurfaceFormats[mFormatIndex].nativeSizedInternalFormat); |
| |
| // Swiftshader will use the raw pointer to the buffer referenced by the IOSurfaceRef |
| ANGLE_TRY(mColorAttachment.initialize(displayVk, mWidth, mHeight, format, samples, |
| mState.isRobustResourceInitEnabled(), |
| mState.hasProtectedContent())); |
| |
| mColorAttachment.image.initStagingBuffer( |
| renderer, renderer->getMinImportedHostPointerAlignment(), vk::kStagingBufferFlags, |
| IOSurfaceGetBytesPerRowOfPlane(mIOSurface, mPlane) * mHeight); |
| mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageViews, nullptr, nullptr, |
| gl::LevelIndex(0), 0, 1, RenderTargetTransience::Default); |
| |
| return angle::Result::Continue; |
| } |
| |
| egl::Error IOSurfaceSurfaceVkMac::unMakeCurrent(const gl::Context *context) |
| { |
| ASSERT(context != nullptr); |
| ContextVk *contextVk = vk::GetImpl(context); |
| DisplayVk *displayVk = vk::GetImpl(context->getDisplay()); |
| angle::Result result = contextVk->flushImpl(nullptr, RenderPassClosureReason::ContextChange); |
| return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE); |
| } |
| |
| int IOSurfaceSurfaceVkMac::computeAlignment() const |
| { |
| size_t rowBytes = IOSurfaceGetBytesPerRowOfPlane(mIOSurface, mPlane); |
| size_t desiredAlignment = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, 1); |
| size_t alignment = 1; |
| while (alignment < desiredAlignment) |
| { |
| if (rowBytes & alignment) |
| { |
| break; |
| } |
| alignment <<= 1; |
| } |
| return static_cast<int>(alignment); |
| } |
| |
| egl::Error IOSurfaceSurfaceVkMac::bindTexImage(const gl::Context *context, |
| gl::Texture *texture, |
| EGLint buffer) |
| { |
| IOSurfaceLock(mIOSurface, 0, nullptr); |
| |
| ContextVk *contextVk = vk::GetImpl(context); |
| DisplayVk *displayVk = vk::GetImpl(context->getDisplay()); |
| RendererVk *renderer = displayVk->getRenderer(); |
| |
| size_t width = IOSurfaceGetWidthOfPlane(mIOSurface, mPlane); |
| size_t height = IOSurfaceGetHeightOfPlane(mIOSurface, mPlane); |
| size_t rowLengthInPixels = IOSurfaceGetBytesPerRowOfPlane(mIOSurface, mPlane) / |
| IOSurfaceGetBytesPerElementOfPlane(mIOSurface, mPlane); |
| |
| gl::PixelUnpackState pixelUnpack; |
| pixelUnpack.alignment = computeAlignment(); |
| pixelUnpack.rowLength = static_cast<GLint>(rowLengthInPixels); |
| pixelUnpack.imageHeight = static_cast<GLint>(height); |
| |
| void *source = IOSurfaceGetBaseAddressOfPlane(mIOSurface, mPlane); |
| |
| const gl::InternalFormat &internalFormatInfo = |
| gl::GetSizedInternalFormatInfo(kIOSurfaceFormats[mFormatIndex].nativeSizedInternalFormat); |
| const vk::Format &format = |
| renderer->getFormat(kIOSurfaceFormats[mFormatIndex].nativeSizedInternalFormat); |
| |
| angle::Result result = mColorAttachment.image.stageSubresourceUpdate( |
| contextVk, gl::ImageIndex::Make2D(0), |
| gl::Extents(static_cast<int>(width), pixelUnpack.imageHeight, 1), gl::Offset(), |
| internalFormatInfo, pixelUnpack, nullptr, kIOSurfaceFormats[mFormatIndex].type, |
| reinterpret_cast<uint8_t *>(source), format, vk::ImageAccess::Renderable); |
| |
| IOSurfaceUnlock(mIOSurface, 0, nullptr); |
| |
| return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE); |
| } |
| |
| egl::Error IOSurfaceSurfaceVkMac::releaseTexImage(const gl::Context *context, EGLint buffer) |
| { |
| ASSERT(context != nullptr); |
| ContextVk *contextVk = vk::GetImpl(context); |
| DisplayVk *displayVk = vk::GetImpl(context->getDisplay()); |
| |
| angle::Result result = mColorAttachment.image.flushAllStagedUpdates(contextVk); |
| |
| if (result != angle::Result::Continue) |
| { |
| return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE); |
| } |
| |
| gl::Rectangle bounds(0, 0, mWidth, mHeight); |
| |
| const angle::Format &dstFormat = angle::Format::Get(angle::Format::InternalFormatToID( |
| kIOSurfaceFormats[mFormatIndex].nativeSizedInternalFormat)); |
| |
| IOSurfaceLock(mIOSurface, 0, nullptr); |
| |
| size_t outputRowPitchInBytes = IOSurfaceGetBytesPerRowOfPlane(mIOSurface, mPlane); |
| |
| PackPixelsParams params(bounds, dstFormat, static_cast<GLuint>(outputRowPitchInBytes), |
| contextVk->isViewportFlipEnabledForDrawFBO(), nullptr, 0); |
| |
| result = mColorAttachment.image.readPixels( |
| contextVk, bounds, params, VK_IMAGE_ASPECT_COLOR_BIT, gl::LevelIndex(0), 0, |
| IOSurfaceGetBaseAddressOfPlane(mIOSurface, mPlane), contextVk->getStagingBuffer()); |
| |
| IOSurfaceUnlock(mIOSurface, 0, nullptr); |
| |
| return angle::ToEGL(result, displayVk, EGL_BAD_SURFACE); |
| } |
| |
| // static |
| bool IOSurfaceSurfaceVkMac::ValidateAttributes(const DisplayVk *displayVk, |
| EGLClientBuffer buffer, |
| const egl::AttributeMap &attribs) |
| { |
| ASSERT(displayVk != nullptr); |
| RendererVk *renderer = displayVk->getRenderer(); |
| |
| IOSurfaceRef ioSurface = reinterpret_cast<IOSurfaceRef>(buffer); |
| |
| // The plane must exist for this IOSurface. IOSurfaceGetPlaneCount can return 0 for non-planar |
| // ioSurfaces but we will treat non-planar like it is a single plane. |
| size_t surfacePlaneCount = std::max(size_t(1), IOSurfaceGetPlaneCount(ioSurface)); |
| EGLAttrib plane = attribs.get(EGL_IOSURFACE_PLANE_ANGLE); |
| if (plane < 0 || static_cast<size_t>(plane) >= surfacePlaneCount) |
| { |
| return false; |
| } |
| |
| // The width height specified must be at least (1, 1) and at most the plane size |
| EGLAttrib width = attribs.get(EGL_WIDTH); |
| EGLAttrib height = attribs.get(EGL_HEIGHT); |
| if (width <= 0 || static_cast<size_t>(width) > IOSurfaceGetWidthOfPlane(ioSurface, plane) || |
| height <= 0 || static_cast<size_t>(height) > IOSurfaceGetHeightOfPlane(ioSurface, plane)) |
| { |
| return false; |
| } |
| |
| // Find this IOSurface format |
| EGLAttrib internalFormat = attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE); |
| EGLAttrib type = attribs.get(EGL_TEXTURE_TYPE_ANGLE); |
| |
| int formatIndex = |
| FindIOSurfaceFormatIndex(static_cast<GLenum>(internalFormat), static_cast<GLenum>(type)); |
| |
| if (formatIndex < 0) |
| { |
| return false; |
| } |
| |
| // Check that the format matches this IOSurface plane |
| if (IOSurfaceGetBytesPerElementOfPlane(ioSurface, plane) != |
| kIOSurfaceFormats[formatIndex].componentBytes) |
| { |
| return false; |
| } |
| |
| void *pointer = IOSurfaceGetBaseAddressOfPlane(ioSurface, plane); |
| VkDeviceSize alignment = renderer->getMinImportedHostPointerAlignment(); |
| if (reinterpret_cast<size_t>(pointer) % alignment != 0) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace rx |