// Copyright 2020 The SwiftShader Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "DisplaySurfaceKHR.hpp" #include "Vulkan/VkDeviceMemory.hpp" #include "Vulkan/VkImage.hpp" #include #include #include #include #include #include namespace vk { static int openCard() { constexpr size_t DIR_NAME_MAX = sizeof("/dev/dri/") - 1; constexpr size_t PRE_NODE_NAME_MAX = sizeof("card") - 1; constexpr size_t POST_NODE_NAME_MAX = sizeof("255") - 1; constexpr size_t NODE_NAME_MAX = DIR_NAME_MAX + PRE_NODE_NAME_MAX + POST_NODE_NAME_MAX; char name[NODE_NAME_MAX] = "/dev/dri/"; int fd = -VK_NOT_READY; /* * Open the first DRM/KMS device. The libdrm drmOpen*() functions * from drmOpen() is of no practical use as any modern system will * handle that through udev or an equivalent component. */ DIR *folder = opendir(name); if(!folder) { return -errno; } strncat(name, "card", 5); for(struct dirent *res; (res = readdir(folder));) { if(!strncmp(res->d_name, "card", 4)) { strncat(name, res->d_name + PRE_NODE_NAME_MAX, 4); fd = open(name, O_RDWR); if(fd >= 0) { break; } name[DIR_NAME_MAX + PRE_NODE_NAME_MAX] = 0; fd = -errno; } } closedir(folder); return fd; } VkResult DisplaySurfaceKHR::GetDisplayModeProperties(uint32_t *pPropertyCount, VkDisplayModePropertiesKHR *pProperties) { *pPropertyCount = 1; if(pProperties) { const int fd = openCard(); if(fd < 0) { return VK_NOT_READY; } drmModeRes *res = drmModeGetResources(fd); drmModeConnector *connector = drmModeGetConnector(fd, res->connectors[0]); pProperties->displayMode = (uintptr_t)connector->modes[0].name; pProperties->parameters.visibleRegion.width = connector->modes[0].hdisplay; pProperties->parameters.visibleRegion.height = connector->modes[0].vdisplay; pProperties->parameters.refreshRate = connector->modes[0].vrefresh * 1000; drmModeFreeConnector(connector); drmModeFreeResources(res); close(fd); } return VK_SUCCESS; } VkResult DisplaySurfaceKHR::GetDisplayPlaneCapabilities(VkDisplayPlaneCapabilitiesKHR *pCapabilities) { const int fd = openCard(); if(fd < 0) { return VK_NOT_READY; } drmModeRes *res = drmModeGetResources(fd); drmModeConnector *connector = drmModeGetConnector(fd, res->connectors[0]); pCapabilities->supportedAlpha = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR; pCapabilities->minSrcPosition.x = 0; pCapabilities->minSrcPosition.y = 0; pCapabilities->maxSrcPosition.x = 0; pCapabilities->maxSrcPosition.y = 0; pCapabilities->minSrcExtent.width = connector->modes[0].hdisplay; pCapabilities->minSrcExtent.height = connector->modes[0].vdisplay; pCapabilities->maxSrcExtent.width = connector->modes[0].hdisplay; pCapabilities->maxSrcExtent.height = connector->modes[0].vdisplay; pCapabilities->minDstPosition.x = 0; pCapabilities->minDstPosition.y = 0; pCapabilities->maxDstPosition.x = 0; pCapabilities->maxDstPosition.y = 0; pCapabilities->minDstExtent.width = connector->modes[0].hdisplay; pCapabilities->minDstExtent.height = connector->modes[0].vdisplay; pCapabilities->maxDstExtent.width = connector->modes[0].hdisplay; pCapabilities->maxDstExtent.height = connector->modes[0].vdisplay; drmModeFreeConnector(connector); drmModeFreeResources(res); close(fd); return VK_SUCCESS; } VkResult DisplaySurfaceKHR::GetDisplayPlaneSupportedDisplays(uint32_t *pDisplayCount, VkDisplayKHR *pDisplays) { *pDisplayCount = 1; if(pDisplays) { const int fd = openCard(); if(fd < 0) { return VK_NOT_READY; } drmModeRes *res = drmModeGetResources(fd); *pDisplays = res->connectors[0]; drmModeFreeResources(res); close(fd); } return VK_SUCCESS; } VkResult DisplaySurfaceKHR::GetPhysicalDeviceDisplayPlaneProperties(uint32_t *pPropertyCount, VkDisplayPlanePropertiesKHR *pProperties) { *pPropertyCount = 1; if(pProperties) { const int fd = openCard(); if(fd < 0) { return VK_NOT_READY; } drmModeRes *res = drmModeGetResources(fd); pProperties->currentDisplay = res->connectors[0]; pProperties->currentStackIndex = 0; drmModeFreeResources(res); close(fd); } return VK_SUCCESS; } VkResult DisplaySurfaceKHR::GetPhysicalDeviceDisplayProperties(uint32_t *pPropertyCount, VkDisplayPropertiesKHR *pProperties) { *pPropertyCount = 1; if(pProperties) { const int fd = openCard(); if(fd < 0) { return VK_NOT_READY; } drmModeRes *res = drmModeGetResources(fd); drmModeConnector *connector = drmModeGetConnector(fd, res->connectors[0]); pProperties->display = res->connectors[0]; pProperties->displayName = "monitor"; pProperties->physicalDimensions.width = connector->mmWidth; pProperties->physicalDimensions.height = connector->mmHeight; if(pProperties->physicalDimensions.width <= 0 || pProperties->physicalDimensions.height <= 0) { pProperties->physicalDimensions.width = connector->modes[0].hdisplay * 25.4 / 96; pProperties->physicalDimensions.height = connector->modes[0].vdisplay * 25.4 / 96; } pProperties->physicalResolution.width = connector->modes[0].hdisplay; pProperties->physicalResolution.height = connector->modes[0].vdisplay; pProperties->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; pProperties->planeReorderPossible = VK_FALSE; pProperties->persistentContent = VK_FALSE; drmModeFreeConnector(connector); drmModeFreeResources(res); close(fd); } return VK_SUCCESS; } DisplaySurfaceKHR::DisplaySurfaceKHR(const VkDisplaySurfaceCreateInfoKHR *pCreateInfo, void *mem) { fd = openCard(); if(fd < 0) { return; } drmModeRes *res = drmModeGetResources(fd); connector_id = res->connectors[0]; drmModeFreeResources(res); drmModeConnector *connector = drmModeGetConnector(fd, connector_id); encoder_id = connector->encoder_id; memcpy(&mode_info, &connector->modes[0], sizeof(drmModeModeInfo)); drmModeFreeConnector(connector); drmModeEncoder *encoder = drmModeGetEncoder(fd, encoder_id); crtc_id = encoder->crtc_id; drmModeFreeEncoder(encoder); crtc = drmModeGetCrtc(fd, crtc_id); struct drm_mode_create_dumb creq; memset(&creq, 0, sizeof(struct drm_mode_create_dumb)); creq.width = mode_info.hdisplay; creq.height = mode_info.vdisplay; creq.bpp = 32; drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); handle = creq.handle; width = creq.width; height = creq.height; pitch = creq.pitch; size = creq.size; drmModeAddFB(fd, width, height, 24, 32, pitch, handle, &fb_id); struct drm_mode_map_dumb mreq; memset(&mreq, 0, sizeof(struct drm_mode_map_dumb)); mreq.handle = handle; drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); fb_buffer = static_cast(mmap(NULL, size, PROT_WRITE, MAP_SHARED, fd, mreq.offset)); } void DisplaySurfaceKHR::destroySurface(const VkAllocationCallbacks *pAllocator) { munmap(fb_buffer, size); drmModeRmFB(fd, fb_id); struct drm_mode_destroy_dumb dreq; memset(&dreq, 0, sizeof(struct drm_mode_destroy_dumb)); dreq.handle = handle; drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); drmModeSetCrtc(fd, crtc->crtc_id, crtc->buffer_id, crtc->x, crtc->y, &connector_id, 1, &crtc->mode); drmModeFreeCrtc(crtc); close(fd); } size_t DisplaySurfaceKHR::ComputeRequiredAllocationSize(const VkDisplaySurfaceCreateInfoKHR *pCreateInfo) { return 0; } VkResult DisplaySurfaceKHR::getSurfaceCapabilities(const void *pSurfaceInfoPNext, VkSurfaceCapabilitiesKHR *pSurfaceCapabilities, void *pSurfaceCapabilitiesPNext) const { VkExtent2D extent = { width, height }; pSurfaceCapabilities->currentExtent = extent; pSurfaceCapabilities->minImageExtent = extent; pSurfaceCapabilities->maxImageExtent = extent; setCommonSurfaceCapabilities(pSurfaceInfoPNext, pSurfaceCapabilities, pSurfaceCapabilitiesPNext); return VK_SUCCESS; } void DisplaySurfaceKHR::attachImage(PresentImage *image) { } void DisplaySurfaceKHR::detachImage(PresentImage *image) { } VkResult DisplaySurfaceKHR::present(PresentImage *image) { image->getImage()->copyTo(fb_buffer, pitch); drmModeSetCrtc(fd, crtc_id, fb_id, 0, 0, &connector_id, 1, &mode_info); return VK_SUCCESS; } } // namespace vk