xref: /aosp_15_r20/external/swiftshader/src/WSI/XcbSurfaceKHR.cpp (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
1 // Copyright 2018 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //	http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "XcbSurfaceKHR.hpp"
16 
17 #include "libXCB.hpp"
18 #include "Vulkan/VkDeviceMemory.hpp"
19 #include "Vulkan/VkImage.hpp"
20 
21 #include <sys/ipc.h>
22 #include <sys/shm.h>
23 
24 #include <memory>
25 
26 namespace vk {
27 
getWindowSizeAndDepth(xcb_connection_t * connection,xcb_window_t window,VkExtent2D * windowExtent,int * depth)28 bool getWindowSizeAndDepth(xcb_connection_t *connection, xcb_window_t window, VkExtent2D *windowExtent, int *depth)
29 {
30 	auto cookie = libXCB->xcb_get_geometry(connection, window);
31 	if(auto *geom = libXCB->xcb_get_geometry_reply(connection, cookie, nullptr))
32 	{
33 		windowExtent->width = static_cast<uint32_t>(geom->width);
34 		windowExtent->height = static_cast<uint32_t>(geom->height);
35 		*depth = static_cast<int>(geom->depth);
36 		free(geom);
37 		return true;
38 	}
39 	return false;
40 }
41 
isSupported()42 bool XcbSurfaceKHR::isSupported()
43 {
44 	return libXCB.isPresent();
45 }
46 
XcbSurfaceKHR(const VkXcbSurfaceCreateInfoKHR * pCreateInfo,void * mem)47 XcbSurfaceKHR::XcbSurfaceKHR(const VkXcbSurfaceCreateInfoKHR *pCreateInfo, void *mem)
48     : connection(pCreateInfo->connection)
49     , window(pCreateInfo->window)
50 {
51 	ASSERT(isSupported());
52 
53 	gc = libXCB->xcb_generate_id(connection);
54 	uint32_t values[2] = { 0, 0xFFFFFFFF };
55 	libXCB->xcb_create_gc(connection, gc, window, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, values);
56 
57 	auto shmQuery = libXCB->xcb_get_extension_data(connection, libXCB->xcb_shm_id);
58 	if(shmQuery->present)
59 	{
60 		auto shmCookie = libXCB->xcb_shm_query_version(connection);
61 		if(auto *reply = libXCB->xcb_shm_query_version_reply(connection, shmCookie, nullptr))
62 		{
63 			mitSHM = reply && reply->shared_pixmaps;
64 			free(reply);
65 		}
66 	}
67 
68 	auto geomCookie = libXCB->xcb_get_geometry(connection, window);
69 	if(auto *reply = libXCB->xcb_get_geometry_reply(connection, geomCookie, nullptr))
70 	{
71 		windowDepth = reply->depth;
72 		free(reply);
73 	}
74 	else
75 	{
76 		surfaceLost = true;
77 	}
78 }
79 
destroySurface(const VkAllocationCallbacks * pAllocator)80 void XcbSurfaceKHR::destroySurface(const VkAllocationCallbacks *pAllocator)
81 {
82 	libXCB->xcb_free_gc(connection, gc);
83 }
84 
ComputeRequiredAllocationSize(const VkXcbSurfaceCreateInfoKHR * pCreateInfo)85 size_t XcbSurfaceKHR::ComputeRequiredAllocationSize(const VkXcbSurfaceCreateInfoKHR *pCreateInfo)
86 {
87 	return 0;
88 }
89 
getSurfaceCapabilities(const void * pSurfaceInfoPNext,VkSurfaceCapabilitiesKHR * pSurfaceCapabilities,void * pSurfaceCapabilitiesPNext) const90 VkResult XcbSurfaceKHR::getSurfaceCapabilities(const void *pSurfaceInfoPNext, VkSurfaceCapabilitiesKHR *pSurfaceCapabilities, void *pSurfaceCapabilitiesPNext) const
91 {
92 	if(surfaceLost)
93 	{
94 		return VK_ERROR_SURFACE_LOST_KHR;
95 	}
96 
97 	VkExtent2D extent;
98 	int depth;
99 	if(!getWindowSizeAndDepth(connection, window, &extent, &depth))
100 	{
101 		surfaceLost = true;
102 		return VK_ERROR_SURFACE_LOST_KHR;
103 	}
104 
105 	pSurfaceCapabilities->currentExtent = extent;
106 	pSurfaceCapabilities->minImageExtent = extent;
107 	pSurfaceCapabilities->maxImageExtent = extent;
108 
109 	setCommonSurfaceCapabilities(pSurfaceInfoPNext, pSurfaceCapabilities, pSurfaceCapabilitiesPNext);
110 	return VK_SUCCESS;
111 }
112 
allocateImageMemory(PresentImage * image,const VkMemoryAllocateInfo & allocateInfo)113 void *XcbSurfaceKHR::allocateImageMemory(PresentImage *image, const VkMemoryAllocateInfo &allocateInfo)
114 {
115 	if(!mitSHM)
116 	{
117 		return nullptr;
118 	}
119 
120 	SHMPixmap &pixmap = pixmaps[image];
121 	int shmid = shmget(IPC_PRIVATE, allocateInfo.allocationSize, IPC_CREAT | SHM_R | SHM_W);
122 	pixmap.shmaddr = shmat(shmid, 0, 0);
123 	pixmap.shmseg = libXCB->xcb_generate_id(connection);
124 	libXCB->xcb_shm_attach(connection, pixmap.shmseg, shmid, false);
125 	shmctl(shmid, IPC_RMID, 0);
126 
127 	int stride = image->getImage()->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
128 	int bytesPerPixel = static_cast<int>(image->getImage()->getFormat(VK_IMAGE_ASPECT_COLOR_BIT).bytes());
129 	int width = stride / bytesPerPixel;
130 	int height = allocateInfo.allocationSize / stride;
131 
132 	pixmap.pixmap = libXCB->xcb_generate_id(connection);
133 	libXCB->xcb_shm_create_pixmap(
134 	    connection,
135 	    pixmap.pixmap,
136 	    window,
137 	    width, height,
138 	    windowDepth,
139 	    pixmap.shmseg,
140 	    0);
141 
142 	return pixmap.shmaddr;
143 }
144 
releaseImageMemory(PresentImage * image)145 void XcbSurfaceKHR::releaseImageMemory(PresentImage *image)
146 {
147 	if(mitSHM)
148 	{
149 		auto it = pixmaps.find(image);
150 		assert(it != pixmaps.end());
151 		libXCB->xcb_shm_detach(connection, it->second.shmseg);
152 		shmdt(it->second.shmaddr);
153 		libXCB->xcb_free_pixmap(connection, it->second.pixmap);
154 		pixmaps.erase(it);
155 	}
156 }
157 
attachImage(PresentImage * image)158 void XcbSurfaceKHR::attachImage(PresentImage *image)
159 {
160 }
161 
detachImage(PresentImage * image)162 void XcbSurfaceKHR::detachImage(PresentImage *image)
163 {
164 }
165 
present(PresentImage * image)166 VkResult XcbSurfaceKHR::present(PresentImage *image)
167 {
168 	VkExtent2D windowExtent;
169 	int depth;
170 	// TODO(penghuang): getWindowSizeAndDepth() call needs a sync IPC, try to remove it.
171 	if(surfaceLost || !getWindowSizeAndDepth(connection, window, &windowExtent, &depth))
172 	{
173 		surfaceLost = true;
174 		return VK_ERROR_SURFACE_LOST_KHR;
175 	}
176 
177 	const VkExtent3D &extent = image->getImage()->getExtent();
178 
179 	if(windowExtent.width != extent.width || windowExtent.height != extent.height)
180 	{
181 		return VK_ERROR_OUT_OF_DATE_KHR;
182 	}
183 
184 	if(!mitSHM)
185 	{
186 		// TODO: Convert image if not RGB888.
187 		int stride = image->getImage()->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
188 		int bytesPerPixel = static_cast<int>(image->getImage()->getFormat(VK_IMAGE_ASPECT_COLOR_BIT).bytes());
189 		int width = stride / bytesPerPixel;
190 		auto buffer = reinterpret_cast<uint8_t *>(image->getImageMemory()->getOffsetPointer(0));
191 		size_t max_request_size = static_cast<size_t>(libXCB->xcb_get_maximum_request_length(connection)) * 4;
192 		size_t max_strides = (max_request_size - sizeof(xcb_put_image_request_t)) / stride;
193 		for(size_t y = 0; y < extent.height; y += max_strides)
194 		{
195 			size_t num_strides = std::min(max_strides, extent.height - y);
196 			libXCB->xcb_put_image(
197 			    connection,
198 			    XCB_IMAGE_FORMAT_Z_PIXMAP,
199 			    window,
200 			    gc,
201 			    width,
202 			    num_strides,
203 			    0, y,                  // dst x, y
204 			    0,                     // left_pad
205 			    depth,
206 			    num_strides * stride,  // data_len
207 			    buffer + y * stride    // data
208 			);
209 		}
210 		assert(libXCB->xcb_connection_has_error(connection) == 0);
211 	}
212 	else
213 	{
214 		auto it = pixmaps.find(image);
215 		assert(it != pixmaps.end());
216 		libXCB->xcb_copy_area(
217 		    connection,
218 		    it->second.pixmap,
219 		    window,
220 		    gc,
221 		    0, 0,  // src x, y
222 		    0, 0,  // dst x, y
223 		    extent.width,
224 		    extent.height);
225 	}
226 	libXCB->xcb_flush(connection);
227 
228 	return VK_SUCCESS;
229 }
230 
231 }  // namespace vk
232