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