| // |
| // Copyright 2016 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. |
| // |
| // DisplayVk.cpp: |
| // Implements the class methods for DisplayVk. |
| // |
| |
| #include "libANGLE/renderer/vulkan/DisplayVk.h" |
| |
| #include "common/debug.h" |
| #include "common/system_utils.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/Display.h" |
| #include "libANGLE/renderer/vulkan/BufferVk.h" |
| #include "libANGLE/renderer/vulkan/ContextVk.h" |
| #include "libANGLE/renderer/vulkan/DeviceVk.h" |
| #include "libANGLE/renderer/vulkan/ImageVk.h" |
| #include "libANGLE/renderer/vulkan/RendererVk.h" |
| #include "libANGLE/renderer/vulkan/ShareGroupVk.h" |
| #include "libANGLE/renderer/vulkan/SurfaceVk.h" |
| #include "libANGLE/renderer/vulkan/SyncVk.h" |
| #include "libANGLE/renderer/vulkan/TextureVk.h" |
| #include "libANGLE/renderer/vulkan/VkImageImageSiblingVk.h" |
| |
| namespace rx |
| { |
| |
| namespace |
| { |
| // Query surface format and colorspace support. |
| void GetSupportedFormatColorspaces(VkPhysicalDevice physicalDevice, |
| const angle::FeaturesVk &featuresVk, |
| VkSurfaceKHR surface, |
| std::vector<VkSurfaceFormat2KHR> *surfaceFormatsOut) |
| { |
| ASSERT(surfaceFormatsOut); |
| surfaceFormatsOut->clear(); |
| |
| constexpr VkSurfaceFormat2KHR kSurfaceFormat2Initializer = { |
| VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR, |
| nullptr, |
| {VK_FORMAT_UNDEFINED, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}; |
| |
| if (featuresVk.supportsSurfaceCapabilities2Extension.enabled) |
| { |
| VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo2 = {}; |
| surfaceInfo2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR; |
| surfaceInfo2.surface = surface; |
| uint32_t surfaceFormatCount = 0; |
| |
| // Query the count first |
| VkResult result = vkGetPhysicalDeviceSurfaceFormats2KHR(physicalDevice, &surfaceInfo2, |
| &surfaceFormatCount, nullptr); |
| ASSERT(result == VK_SUCCESS); |
| ASSERT(surfaceFormatCount > 0); |
| |
| // Query the VkSurfaceFormat2KHR list |
| std::vector<VkSurfaceFormat2KHR> surfaceFormats2(surfaceFormatCount, |
| kSurfaceFormat2Initializer); |
| result = vkGetPhysicalDeviceSurfaceFormats2KHR(physicalDevice, &surfaceInfo2, |
| &surfaceFormatCount, surfaceFormats2.data()); |
| ASSERT(result == VK_SUCCESS); |
| |
| *surfaceFormatsOut = std::move(surfaceFormats2); |
| } |
| else |
| { |
| uint32_t surfaceFormatCount = 0; |
| // Query the count first |
| VkResult result = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, |
| &surfaceFormatCount, nullptr); |
| ASSERT(result == VK_SUCCESS); |
| |
| // Query the VkSurfaceFormatKHR list |
| std::vector<VkSurfaceFormatKHR> surfaceFormats(surfaceFormatCount); |
| result = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatCount, |
| surfaceFormats.data()); |
| ASSERT(result == VK_SUCCESS); |
| |
| // Copy over data from std::vector<VkSurfaceFormatKHR> to std::vector<VkSurfaceFormat2KHR> |
| std::vector<VkSurfaceFormat2KHR> surfaceFormats2(surfaceFormatCount, |
| kSurfaceFormat2Initializer); |
| for (size_t index = 0; index < surfaceFormatCount; index++) |
| { |
| surfaceFormats2[index].surfaceFormat.format = surfaceFormats[index].format; |
| } |
| |
| *surfaceFormatsOut = std::move(surfaceFormats2); |
| } |
| } |
| } // namespace |
| |
| DisplayVk::DisplayVk(const egl::DisplayState &state) |
| : DisplayImpl(state), |
| vk::Context(new RendererVk()), |
| mScratchBuffer(1000u), |
| mSupportedColorspaceFormatsMap{} |
| {} |
| |
| DisplayVk::~DisplayVk() |
| { |
| delete mRenderer; |
| } |
| |
| egl::Error DisplayVk::initialize(egl::Display *display) |
| { |
| ASSERT(mRenderer != nullptr && display != nullptr); |
| angle::Result result = mRenderer->initialize(this, display, getWSIExtension(), getWSILayer()); |
| ANGLE_TRY(angle::ToEGL(result, EGL_NOT_INITIALIZED)); |
| // Query and cache supported surface format and colorspace for later use. |
| initSupportedSurfaceFormatColorspaces(); |
| return egl::NoError(); |
| } |
| |
| void DisplayVk::terminate() |
| { |
| mRenderer->reloadVolkIfNeeded(); |
| |
| ASSERT(mRenderer); |
| mRenderer->onDestroy(this); |
| } |
| |
| egl::Error DisplayVk::makeCurrent(egl::Display * /*display*/, |
| egl::Surface * /*drawSurface*/, |
| egl::Surface * /*readSurface*/, |
| gl::Context * /*context*/) |
| { |
| // Ensure the appropriate global DebugAnnotator is used |
| ASSERT(mRenderer); |
| mRenderer->setGlobalDebugAnnotator(); |
| |
| return egl::NoError(); |
| } |
| |
| bool DisplayVk::testDeviceLost() |
| { |
| return mRenderer->isDeviceLost(); |
| } |
| |
| egl::Error DisplayVk::restoreLostDevice(const egl::Display *display) |
| { |
| // A vulkan device cannot be restored, the entire renderer would have to be re-created along |
| // with any other EGL objects that reference it. |
| return egl::EglBadDisplay(); |
| } |
| |
| std::string DisplayVk::getRendererDescription() |
| { |
| if (mRenderer) |
| { |
| return mRenderer->getRendererDescription(); |
| } |
| return std::string(); |
| } |
| |
| std::string DisplayVk::getVendorString() |
| { |
| if (mRenderer) |
| { |
| return mRenderer->getVendorString(); |
| } |
| return std::string(); |
| } |
| |
| std::string DisplayVk::getVersionString(bool includeFullVersion) |
| { |
| if (mRenderer) |
| { |
| return mRenderer->getVersionString(includeFullVersion); |
| } |
| return std::string(); |
| } |
| |
| DeviceImpl *DisplayVk::createDevice() |
| { |
| return new DeviceVk(); |
| } |
| |
| egl::Error DisplayVk::waitClient(const gl::Context *context) |
| { |
| ANGLE_TRACE_EVENT0("gpu.angle", "DisplayVk::waitClient"); |
| ContextVk *contextVk = vk::GetImpl(context); |
| return angle::ToEGL(contextVk->finishImpl(RenderPassClosureReason::EGLWaitClient), |
| EGL_BAD_ACCESS); |
| } |
| |
| egl::Error DisplayVk::waitNative(const gl::Context *context, EGLint engine) |
| { |
| ANGLE_TRACE_EVENT0("gpu.angle", "DisplayVk::waitNative"); |
| return angle::ResultToEGL(waitNativeImpl()); |
| } |
| |
| angle::Result DisplayVk::waitNativeImpl() |
| { |
| return angle::Result::Continue; |
| } |
| |
| SurfaceImpl *DisplayVk::createWindowSurface(const egl::SurfaceState &state, |
| EGLNativeWindowType window, |
| const egl::AttributeMap &attribs) |
| { |
| return createWindowSurfaceVk(state, window); |
| } |
| |
| SurfaceImpl *DisplayVk::createPbufferSurface(const egl::SurfaceState &state, |
| const egl::AttributeMap &attribs) |
| { |
| ASSERT(mRenderer); |
| return new OffscreenSurfaceVk(state, mRenderer); |
| } |
| |
| SurfaceImpl *DisplayVk::createPbufferFromClientBuffer(const egl::SurfaceState &state, |
| EGLenum buftype, |
| EGLClientBuffer clientBuffer, |
| const egl::AttributeMap &attribs) |
| { |
| UNIMPLEMENTED(); |
| return static_cast<SurfaceImpl *>(0); |
| } |
| |
| SurfaceImpl *DisplayVk::createPixmapSurface(const egl::SurfaceState &state, |
| NativePixmapType nativePixmap, |
| const egl::AttributeMap &attribs) |
| { |
| UNIMPLEMENTED(); |
| return static_cast<SurfaceImpl *>(0); |
| } |
| |
| ImageImpl *DisplayVk::createImage(const egl::ImageState &state, |
| const gl::Context *context, |
| EGLenum target, |
| const egl::AttributeMap &attribs) |
| { |
| return new ImageVk(state, context); |
| } |
| |
| ShareGroupImpl *DisplayVk::createShareGroup(const egl::ShareGroupState &state) |
| { |
| return new ShareGroupVk(state); |
| } |
| |
| bool DisplayVk::isConfigFormatSupported(VkFormat format) const |
| { |
| // Requires VK_GOOGLE_surfaceless_query extension to be supported. |
| ASSERT(mRenderer->getFeatures().supportsSurfacelessQueryExtension.enabled); |
| |
| // A format is considered supported if it is supported in atleast 1 colorspace. |
| using ColorspaceFormatSetItem = |
| const std::pair<const VkColorSpaceKHR, std::unordered_set<VkFormat>>; |
| for (ColorspaceFormatSetItem &colorspaceFormatSetItem : mSupportedColorspaceFormatsMap) |
| { |
| if (colorspaceFormatSetItem.second.count(format) > 0) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool DisplayVk::isSurfaceFormatColorspacePairSupported(VkSurfaceKHR surface, |
| VkFormat format, |
| VkColorSpaceKHR colorspace) const |
| { |
| if (mSupportedColorspaceFormatsMap.size() > 0) |
| { |
| return mSupportedColorspaceFormatsMap.count(colorspace) > 0 && |
| mSupportedColorspaceFormatsMap.at(colorspace).count(format) > 0; |
| } |
| else |
| { |
| const angle::FeaturesVk &featuresVk = mRenderer->getFeatures(); |
| std::vector<VkSurfaceFormat2KHR> surfaceFormats; |
| GetSupportedFormatColorspaces(mRenderer->getPhysicalDevice(), featuresVk, surface, |
| &surfaceFormats); |
| |
| if (!featuresVk.supportsSurfaceCapabilities2Extension.enabled) |
| { |
| if (surfaceFormats.size() == 1u && |
| surfaceFormats[0].surfaceFormat.format == VK_FORMAT_UNDEFINED) |
| { |
| return true; |
| } |
| } |
| |
| for (const VkSurfaceFormat2KHR &surfaceFormat : surfaceFormats) |
| { |
| if (surfaceFormat.surfaceFormat.format == format && |
| surfaceFormat.surfaceFormat.colorSpace == colorspace) |
| { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| bool DisplayVk::isColorspaceSupported(VkColorSpaceKHR colorspace) const |
| { |
| return mSupportedColorspaceFormatsMap.count(colorspace) > 0; |
| } |
| |
| void DisplayVk::initSupportedSurfaceFormatColorspaces() |
| { |
| const angle::FeaturesVk &featuresVk = mRenderer->getFeatures(); |
| if (featuresVk.supportsSurfacelessQueryExtension.enabled && |
| featuresVk.supportsSurfaceCapabilities2Extension.enabled) |
| { |
| // Use the VK_GOOGLE_surfaceless_query extension to query supported surface formats and |
| // colorspaces by using a VK_NULL_HANDLE for the VkSurfaceKHR handle. |
| std::vector<VkSurfaceFormat2KHR> surfaceFormats; |
| GetSupportedFormatColorspaces(mRenderer->getPhysicalDevice(), featuresVk, VK_NULL_HANDLE, |
| &surfaceFormats); |
| for (const VkSurfaceFormat2KHR &surfaceFormat : surfaceFormats) |
| { |
| // Cache supported VkFormat and VkColorSpaceKHR for later use |
| VkFormat format = surfaceFormat.surfaceFormat.format; |
| VkColorSpaceKHR colorspace = surfaceFormat.surfaceFormat.colorSpace; |
| |
| ASSERT(format != VK_FORMAT_UNDEFINED); |
| |
| mSupportedColorspaceFormatsMap[colorspace].insert(format); |
| } |
| |
| ASSERT(mSupportedColorspaceFormatsMap.size() > 0); |
| } |
| else |
| { |
| mSupportedColorspaceFormatsMap.clear(); |
| } |
| } |
| |
| ContextImpl *DisplayVk::createContext(const gl::State &state, |
| gl::ErrorSet *errorSet, |
| const egl::Config *configuration, |
| const gl::Context *shareContext, |
| const egl::AttributeMap &attribs) |
| { |
| return new ContextVk(state, errorSet, mRenderer); |
| } |
| |
| StreamProducerImpl *DisplayVk::createStreamProducerD3DTexture( |
| egl::Stream::ConsumerType consumerType, |
| const egl::AttributeMap &attribs) |
| { |
| UNIMPLEMENTED(); |
| return static_cast<StreamProducerImpl *>(0); |
| } |
| |
| EGLSyncImpl *DisplayVk::createSync(const egl::AttributeMap &attribs) |
| { |
| return new EGLSyncVk(attribs); |
| } |
| |
| gl::Version DisplayVk::getMaxSupportedESVersion() const |
| { |
| return mRenderer->getMaxSupportedESVersion(); |
| } |
| |
| gl::Version DisplayVk::getMaxConformantESVersion() const |
| { |
| return mRenderer->getMaxConformantESVersion(); |
| } |
| |
| Optional<gl::Version> DisplayVk::getMaxSupportedDesktopVersion() const |
| { |
| return gl::Version{4, 6}; |
| } |
| |
| egl::Error DisplayVk::validateImageClientBuffer(const gl::Context *context, |
| EGLenum target, |
| EGLClientBuffer clientBuffer, |
| const egl::AttributeMap &attribs) const |
| { |
| switch (target) |
| { |
| case EGL_VULKAN_IMAGE_ANGLE: |
| { |
| VkImage *vkImage = reinterpret_cast<VkImage *>(clientBuffer); |
| if (!vkImage || *vkImage == VK_NULL_HANDLE) |
| { |
| return egl::EglBadParameter() << "clientBuffer is invalid."; |
| } |
| |
| GLenum internalFormat = |
| static_cast<GLenum>(attribs.get(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_NONE)); |
| switch (internalFormat) |
| { |
| case GL_RGBA: |
| case GL_BGRA_EXT: |
| case GL_RGB: |
| case GL_RED_EXT: |
| case GL_RG_EXT: |
| case GL_RGB10_A2_EXT: |
| case GL_R16_EXT: |
| case GL_RG16_EXT: |
| case GL_NONE: |
| break; |
| default: |
| return egl::EglBadParameter() << "Invalid EGLImage texture internal format: 0x" |
| << std::hex << internalFormat; |
| } |
| |
| uint64_t hi = static_cast<uint64_t>(attribs.get(EGL_VULKAN_IMAGE_CREATE_INFO_HI_ANGLE)); |
| uint64_t lo = static_cast<uint64_t>(attribs.get(EGL_VULKAN_IMAGE_CREATE_INFO_LO_ANGLE)); |
| uint64_t info = ((hi & 0xffffffff) << 32) | (lo & 0xffffffff); |
| if (reinterpret_cast<const VkImageCreateInfo *>(info)->sType != |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO) |
| { |
| return egl::EglBadParameter() |
| << "EGL_VULKAN_IMAGE_CREATE_INFO_HI_ANGLE and " |
| "EGL_VULKAN_IMAGE_CREATE_INFO_LO_ANGLE are not pointing to a " |
| "valid VkImageCreateInfo structure."; |
| } |
| |
| return egl::NoError(); |
| } |
| default: |
| return DisplayImpl::validateImageClientBuffer(context, target, clientBuffer, attribs); |
| } |
| } |
| |
| ExternalImageSiblingImpl *DisplayVk::createExternalImageSibling(const gl::Context *context, |
| EGLenum target, |
| EGLClientBuffer buffer, |
| const egl::AttributeMap &attribs) |
| { |
| switch (target) |
| { |
| case EGL_VULKAN_IMAGE_ANGLE: |
| return new VkImageImageSiblingVk(buffer, attribs); |
| default: |
| return DisplayImpl::createExternalImageSibling(context, target, buffer, attribs); |
| } |
| } |
| |
| void DisplayVk::generateExtensions(egl::DisplayExtensions *outExtensions) const |
| { |
| outExtensions->createContextRobustness = getRenderer()->getNativeExtensions().robustnessEXT; |
| outExtensions->surfaceOrientation = true; |
| outExtensions->displayTextureShareGroup = true; |
| outExtensions->displaySemaphoreShareGroup = true; |
| outExtensions->robustResourceInitializationANGLE = true; |
| |
| // The Vulkan implementation will always say that EGL_KHR_swap_buffers_with_damage is supported. |
| // When the Vulkan driver supports VK_KHR_incremental_present, it will use it. Otherwise, it |
| // will ignore the hint and do a regular swap. |
| outExtensions->swapBuffersWithDamage = true; |
| |
| outExtensions->fenceSync = true; |
| outExtensions->waitSync = true; |
| |
| outExtensions->image = true; |
| outExtensions->imageBase = true; |
| outExtensions->imagePixmap = false; // ANGLE does not support pixmaps |
| outExtensions->glTexture2DImage = true; |
| outExtensions->glTextureCubemapImage = true; |
| outExtensions->glTexture3DImage = |
| getRenderer()->getFeatures().supportsSampler2dViewOf3d.enabled; |
| outExtensions->glRenderbufferImage = true; |
| outExtensions->imageNativeBuffer = |
| getRenderer()->getFeatures().supportsAndroidHardwareBuffer.enabled; |
| outExtensions->surfacelessContext = true; |
| outExtensions->glColorspace = true; |
| outExtensions->imageGlColorspace = |
| outExtensions->glColorspace && getRenderer()->getFeatures().supportsImageFormatList.enabled; |
| |
| #if defined(ANGLE_PLATFORM_ANDROID) |
| outExtensions->getNativeClientBufferANDROID = true; |
| outExtensions->framebufferTargetANDROID = true; |
| #endif // defined(ANGLE_PLATFORM_ANDROID) |
| |
| // EGL_EXT_image_dma_buf_import is only exposed if EGL_EXT_image_dma_buf_import_modifiers can |
| // also be exposed. The Vulkan extensions that support these EGL extensions are not split in |
| // the same way; both Vulkan extensions are needed for EGL_EXT_image_dma_buf_import, and with |
| // both Vulkan extensions, EGL_EXT_image_dma_buf_import_modifiers is also supportable. |
| outExtensions->imageDmaBufImportEXT = |
| getRenderer()->getFeatures().supportsExternalMemoryDmaBufAndModifiers.enabled; |
| outExtensions->imageDmaBufImportModifiersEXT = outExtensions->imageDmaBufImportEXT; |
| |
| // Disable context priority when non-zero memory init is enabled. This enforces a queue order. |
| outExtensions->contextPriority = !getRenderer()->getFeatures().allocateNonZeroMemory.enabled; |
| outExtensions->noConfigContext = true; |
| |
| #if defined(ANGLE_PLATFORM_ANDROID) || defined(ANGLE_PLATFORM_LINUX) |
| outExtensions->nativeFenceSyncANDROID = |
| getRenderer()->getFeatures().supportsAndroidNativeFenceSync.enabled; |
| #endif // defined(ANGLE_PLATFORM_ANDROID) || defined(ANGLE_PLATFORM_LINUX) |
| |
| #if defined(ANGLE_PLATFORM_GGP) |
| outExtensions->ggpStreamDescriptor = true; |
| outExtensions->swapWithFrameToken = getRenderer()->getFeatures().supportsGGPFrameToken.enabled; |
| #endif // defined(ANGLE_PLATFORM_GGP) |
| |
| outExtensions->bufferAgeEXT = true; |
| |
| outExtensions->protectedContentEXT = |
| (getRenderer()->getFeatures().supportsProtectedMemory.enabled && |
| getRenderer()->getFeatures().supportsSurfaceProtectedSwapchains.enabled); |
| |
| outExtensions->createSurfaceSwapIntervalANGLE = true; |
| |
| outExtensions->mutableRenderBufferKHR = |
| getRenderer()->getFeatures().supportsSharedPresentableImageExtension.enabled; |
| |
| outExtensions->vulkanImageANGLE = true; |
| |
| outExtensions->lockSurface3KHR = |
| getRenderer()->getFeatures().supportsLockSurfaceExtension.enabled; |
| |
| outExtensions->partialUpdateKHR = true; |
| |
| outExtensions->timestampSurfaceAttributeANGLE = |
| getRenderer()->getFeatures().supportsTimestampSurfaceAttribute.enabled; |
| |
| outExtensions->eglColorspaceAttributePassthroughANGLE = |
| outExtensions->glColorspace && |
| getRenderer()->getFeatures().eglColorspaceAttributePassthrough.enabled; |
| |
| // If EGL_KHR_gl_colorspace extension is supported check if other colorspace extensions |
| // can be supported as well. |
| if (outExtensions->glColorspace) |
| { |
| if (isColorspaceSupported(VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT)) |
| { |
| outExtensions->glColorspaceDisplayP3 = true; |
| outExtensions->glColorspaceDisplayP3Passthrough = true; |
| } |
| |
| outExtensions->glColorspaceDisplayP3Linear = |
| isColorspaceSupported(VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT); |
| outExtensions->glColorspaceScrgb = |
| isColorspaceSupported(VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT); |
| outExtensions->glColorspaceScrgbLinear = |
| isColorspaceSupported(VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT); |
| } |
| } |
| |
| void DisplayVk::generateCaps(egl::Caps *outCaps) const |
| { |
| outCaps->textureNPOT = true; |
| outCaps->stencil8 = getRenderer()->getNativeExtensions().textureStencil8OES; |
| } |
| |
| const char *DisplayVk::getWSILayer() const |
| { |
| return nullptr; |
| } |
| |
| bool DisplayVk::isUsingSwapchain() const |
| { |
| return true; |
| } |
| |
| bool DisplayVk::getScratchBuffer(size_t requstedSizeBytes, |
| angle::MemoryBuffer **scratchBufferOut) const |
| { |
| return mScratchBuffer.get(requstedSizeBytes, scratchBufferOut); |
| } |
| |
| void DisplayVk::handleError(VkResult result, |
| const char *file, |
| const char *function, |
| unsigned int line) |
| { |
| ASSERT(result != VK_SUCCESS); |
| |
| std::stringstream errorStream; |
| errorStream << "Internal Vulkan error (" << result << "): " << VulkanResultString(result) |
| << ", in " << file << ", " << function << ":" << line << "."; |
| std::string errorString = errorStream.str(); |
| |
| if (result == VK_ERROR_DEVICE_LOST) |
| { |
| WARN() << errorString; |
| mRenderer->notifyDeviceLost(); |
| } |
| |
| // Note: the errorCode will be set later in angle::ToEGL where it's available. |
| *egl::Display::GetCurrentThreadErrorScratchSpace() = egl::Error(0, 0, std::move(errorString)); |
| } |
| |
| void DisplayVk::initializeFrontendFeatures(angle::FrontendFeatures *features) const |
| { |
| mRenderer->initializeFrontendFeatures(features); |
| } |
| |
| void DisplayVk::populateFeatureList(angle::FeatureList *features) |
| { |
| mRenderer->getFeatures().populateFeatureList(features); |
| } |
| } // namespace rx |