blob: 6c2c9dcf4a2ac45b798f334785d8a08a6af08c8b [file] [log] [blame]
// 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 shmQuery = libXCB->xcb_get_extension_data(connection, libXCB->xcb_shm_id);
if(shmQuery->present)
{
auto shmCookie = libXCB->xcb_shm_query_version(connection);
if(auto *reply = libXCB->xcb_shm_query_version_reply(connection, shmCookie, nullptr))
{
mitSHM = reply && reply->shared_pixmaps;
free(reply);
}
}
auto geomCookie = libXCB->xcb_get_geometry(connection, window);
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(const void *pSurfaceInfoPNext, VkSurfaceCapabilitiesKHR *pSurfaceCapabilities, void *pSurfaceCapabilitiesPNext) const
{
if(surfaceLost)
{
return VK_ERROR_SURFACE_LOST_KHR;
}
setCommonSurfaceCapabilities(pSurfaceInfoPNext, pSurfaceCapabilities, pSurfaceCapabilitiesPNext);
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