| // Copyright 2018 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 "XcbSurfaceKHR.hpp" | 
 |  | 
 | #include "libXCB.hpp" | 
 | #include "Vulkan/VkDeviceMemory.hpp" | 
 | #include "Vulkan/VkImage.hpp" | 
 |  | 
 | #include <sys/ipc.h> | 
 | #include <sys/shm.h> | 
 |  | 
 | #include <memory> | 
 |  | 
 | namespace vk { | 
 |  | 
 | bool getWindowSizeAndDepth(xcb_connection_t *connection, xcb_window_t window, VkExtent2D *windowExtent, int *depth) | 
 | { | 
 | 	auto cookie = libXCB->xcb_get_geometry(connection, window); | 
 | 	if(auto *geom = libXCB->xcb_get_geometry_reply(connection, cookie, nullptr)) | 
 | 	{ | 
 | 		windowExtent->width = static_cast<uint32_t>(geom->width); | 
 | 		windowExtent->height = static_cast<uint32_t>(geom->height); | 
 | 		*depth = static_cast<int>(geom->depth); | 
 | 		free(geom); | 
 | 		return true; | 
 | 	} | 
 | 	return false; | 
 | } | 
 |  | 
 | bool XcbSurfaceKHR::isSupported() | 
 | { | 
 | 	return libXCB.isPresent(); | 
 | } | 
 |  | 
 | XcbSurfaceKHR::XcbSurfaceKHR(const VkXcbSurfaceCreateInfoKHR *pCreateInfo, void *mem) | 
 | 	: connection(pCreateInfo->connection) | 
 | 	, window(pCreateInfo->window) | 
 | { | 
 | 	ASSERT(isSupported()); | 
 |  | 
 | 	gc = libXCB->xcb_generate_id(connection); | 
 | 	uint32_t values[2] = { 0, 0xffffffff }; | 
 | 	libXCB->xcb_create_gc(connection, gc, window, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, values); | 
 |  | 
 | 	auto shmCookie = libXCB->xcb_shm_query_version(connection); | 
 | 	auto geomCookie = libXCB->xcb_get_geometry(connection, window); | 
 |  | 
 | 	if (auto *reply = libXCB->xcb_shm_query_version_reply(connection, shmCookie, nullptr)) | 
 | 	{ | 
 | 		mitSHM = reply && reply->shared_pixmaps; | 
 | 		free(reply); | 
 | 	} | 
 |  | 
 | 	if (auto *reply = libXCB->xcb_get_geometry_reply(connection, geomCookie, nullptr)) | 
 | 	{ | 
 | 		windowDepth = reply->depth; | 
 | 		free(reply); | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 		surfaceLost = true; | 
 | 	} | 
 | } | 
 |  | 
 | void XcbSurfaceKHR::destroySurface(const VkAllocationCallbacks *pAllocator) | 
 | { | 
 | 	libXCB->xcb_free_gc(connection, gc); | 
 | } | 
 |  | 
 | size_t XcbSurfaceKHR::ComputeRequiredAllocationSize(const VkXcbSurfaceCreateInfoKHR *pCreateInfo) | 
 | { | 
 | 	return 0; | 
 | } | 
 |  | 
 | VkResult XcbSurfaceKHR::getSurfaceCapabilities(VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) const | 
 | { | 
 | 	if (surfaceLost) | 
 | 	{ | 
 | 		return VK_ERROR_SURFACE_LOST_KHR; | 
 | 	} | 
 |  | 
 | 	setCommonSurfaceCapabilities(pSurfaceCapabilities); | 
 |  | 
 | 	VkExtent2D extent; | 
 | 	int depth; | 
 | 	if(!getWindowSizeAndDepth(connection, window, &extent, &depth)) | 
 | 	{ | 
 | 		surfaceLost = true; | 
 | 		return VK_ERROR_SURFACE_LOST_KHR; | 
 | 	} | 
 |  | 
 | 	pSurfaceCapabilities->currentExtent = extent; | 
 | 	pSurfaceCapabilities->minImageExtent = extent; | 
 | 	pSurfaceCapabilities->maxImageExtent = extent; | 
 | 	return VK_SUCCESS; | 
 | } | 
 |  | 
 | void* XcbSurfaceKHR::allocateImageMemory(PresentImage *image, const VkMemoryAllocateInfo &allocateInfo) | 
 | { | 
 | 	if (!mitSHM) | 
 | 		return nullptr; | 
 |  | 
 | 	SHMPixmap& pixmap = pixmaps[image]; | 
 | 	int shmid = shmget(IPC_PRIVATE, allocateInfo.allocationSize, IPC_CREAT | SHM_R | SHM_W); | 
 | 	pixmap.shmaddr = shmat(shmid, 0, 0); | 
 | 	pixmap.shmseg = libXCB->xcb_generate_id(connection); | 
 | 	libXCB->xcb_shm_attach(connection, pixmap.shmseg, shmid, false); | 
 | 	shmctl(shmid, IPC_RMID, 0); | 
 |  | 
 | 	int stride = image->getImage()->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0); | 
 | 	int bytesPerPixel = static_cast<int>(image->getImage()->getFormat(VK_IMAGE_ASPECT_COLOR_BIT).bytes()); | 
 | 	int width = stride / bytesPerPixel; | 
 | 	int height = allocateInfo.allocationSize / stride; | 
 |  | 
 | 	pixmap.pixmap = libXCB->xcb_generate_id(connection); | 
 | 	libXCB->xcb_shm_create_pixmap( | 
 | 		connection, | 
 | 		pixmap.pixmap, | 
 | 		window, | 
 | 		width, height, | 
 | 		windowDepth, | 
 | 		pixmap.shmseg, | 
 | 		0); | 
 |  | 
 | 	return pixmap.shmaddr; | 
 | } | 
 |  | 
 | void XcbSurfaceKHR::releaseImageMemory(PresentImage *image) | 
 | { | 
 | 	if (mitSHM) | 
 | 	{ | 
 | 		auto it = pixmaps.find(image); | 
 | 		assert(it != pixmaps.end()); | 
 | 		libXCB->xcb_shm_detach(connection, it->second.shmseg); | 
 | 		shmdt(it->second.shmaddr); | 
 | 		libXCB->xcb_free_pixmap(connection, it->second.pixmap); | 
 | 		pixmaps.erase(it); | 
 | 	} | 
 | } | 
 |  | 
 | void XcbSurfaceKHR::attachImage(PresentImage *image) | 
 | { | 
 | } | 
 |  | 
 | void XcbSurfaceKHR::detachImage(PresentImage *image) | 
 | { | 
 | } | 
 |  | 
 | VkResult XcbSurfaceKHR::present(PresentImage *image) | 
 | { | 
 | 	VkExtent2D windowExtent; | 
 | 	int depth; | 
 | 	// TODO(penghuang): getWindowSizeAndDepth() call needs a sync IPC, try to remove it. | 
 | 	if(surfaceLost || !getWindowSizeAndDepth(connection, window, &windowExtent, &depth)) | 
 | 	{ | 
 | 		surfaceLost = true; | 
 | 		return VK_ERROR_SURFACE_LOST_KHR; | 
 | 	} | 
 |  | 
 | 	const VkExtent3D &extent = image->getImage()->getExtent(); | 
 |  | 
 | 	if(windowExtent.width != extent.width || windowExtent.height != extent.height) | 
 | 	{ | 
 | 		return VK_ERROR_OUT_OF_DATE_KHR; | 
 | 	} | 
 |  | 
 | 	if (!mitSHM) { | 
 | 		// TODO: Convert image if not RGB888. | 
 | 		int stride = image->getImage()->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0); | 
 | 		int bytesPerPixel = static_cast<int>(image->getImage()->getFormat(VK_IMAGE_ASPECT_COLOR_BIT).bytes()); | 
 | 		int width = stride / bytesPerPixel; | 
 | 		auto buffer = reinterpret_cast<uint8_t *>(image->getImageMemory()->getOffsetPointer(0)); | 
 | 		size_t bufferSize = extent.height * stride; | 
 | 		libXCB->xcb_put_image( | 
 | 			connection, | 
 | 			XCB_IMAGE_FORMAT_Z_PIXMAP, | 
 | 			window, | 
 | 			gc, | 
 | 			width, | 
 | 			extent.height, | 
 | 			0, 0,  // dst x, y | 
 | 			0,     // left_pad | 
 | 			depth, | 
 | 			bufferSize,  // data_len | 
 | 			buffer       // data | 
 | 		); | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 		auto it = pixmaps.find(image); | 
 | 		assert(it != pixmaps.end()); | 
 | 		libXCB->xcb_copy_area( | 
 | 			connection, | 
 | 			it->second.pixmap, | 
 | 			window, | 
 | 			gc, | 
 | 			0, 0,  // src x, y | 
 | 			0, 0,  // dst x, y | 
 | 			extent.width, | 
 | 			extent.height); | 
 | 	} | 
 | 	libXCB->xcb_flush(connection); | 
 |  | 
 | 	return VK_SUCCESS; | 
 | } | 
 |  | 
 | }  // namespace vk |