Add Xlib support to viewer

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=1999213002

Review-Url: https://codereview.chromium.org/1999213002
This commit is contained in:
jvanverth 2016-05-23 13:13:36 -07:00 committed by Commit bot
parent 50134ccafd
commit 1d15596200
12 changed files with 506 additions and 30 deletions

View File

@ -25,11 +25,13 @@
'../src/gpu',
'../src/images',
'../src/image',
'../src/views/unix',
'../tools/timer',
],
'sources': [
'../gm/gm.cpp',
'../src/views/SkTouchGesture.cpp',
'../src/views/unix/keysym2ucs.c',
'<!@(python find.py ../tools/viewer "*.cpp")',
],
'dependencies': [
@ -56,10 +58,23 @@
],
},
}],
[ 'skia_os == "linux"', {
'link_settings': {
'libraries': [
'-lX11-xcb',
],
},
}],
['skia_os != "android"', {
'sources/': [ ['exclude', '_android.(h|cpp)$'],
],
}],
['skia_os != "linux"', {
'sources/': [
['exclude', '_unix.(h|cpp)$'],
['exclude', 'keysym2ucs.c'],
],
}],
['skia_os != "win"', {
'sources/': [ ['exclude', '_win.(h|cpp)$'],
],

View File

@ -25,7 +25,7 @@ enum GrVkExtensionFlags {
kKHR_swapchain_GrVkExtensionFlag = 0x0008,
kKHR_win32_surface_GrVkExtensionFlag = 0x0010,
kKHR_android_surface_GrVkExtensionFlag = 0x0020,
kKHR_xlib_surface_GrVkExtensionFlag = 0x0040,
kKHR_xcb_surface_GrVkExtensionFlag = 0x0040,
};
enum GrVkFeatureFlags {
@ -55,7 +55,9 @@ struct GrVkBackendContext : public SkRefCnt {
// If presentQueueIndex is non-NULL, will try to set up presentQueue as part of device
// creation. canPresent() is a device-dependent function.
static const GrVkBackendContext* Create(uint32_t* presentQueueIndex = nullptr,
bool(*canPresent)(VkInstance, VkPhysicalDevice, uint32_t queueIndex) = nullptr);
bool(*canPresent)(VkInstance, VkPhysicalDevice, uint32_t queueIndex,
void* platformData) = nullptr,
void* platformData = nullptr);
~GrVkBackendContext() override;
};

View File

@ -14,7 +14,7 @@
#elif defined(SK_BUILD_FOR_ANDROID)
# define VK_USE_PLATFORM_ANDROID_KHR
#elif defined(SK_BUILD_FOR_UNIX)
# define VK_USE_PLATFORM_XLIB_KHR
# define VK_USE_PLATFORM_XCB_KHR
#endif
#if defined(Bool) || defined(Status) || defined(True) || defined(False)
@ -23,19 +23,4 @@
#include <vulkan/vulkan.h>
// Xlib.h may define these macros with common names (Grrr)
#ifdef Bool
# undef Bool
#endif
#ifdef Status
# undef Status
#endif
#ifdef True
# undef True
#endif
#ifdef False
# undef False
#endif
#endif

View File

@ -40,7 +40,9 @@ const uint32_t kGrVkMinimumVersion = VK_MAKE_VERSION(1, 0, 8);
// Create the base Vulkan objects needed by the GrVkGpu object
const GrVkBackendContext* GrVkBackendContext::Create(uint32_t* presentQueueIndexPtr,
bool(*canPresent)(VkInstance, VkPhysicalDevice, uint32_t queueIndex)) {
bool(*canPresent)(VkInstance, VkPhysicalDevice, uint32_t queueIndex,
void* platformData),
void* platformData) {
VkPhysicalDevice physDev;
VkDevice device;
VkInstance inst;
@ -91,11 +93,11 @@ const GrVkBackendContext* GrVkBackendContext::Create(uint32_t* presentQueueIndex
if (extensions.hasInstanceExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME)) {
instanceExtensionNames.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
extensionFlags |= kKHR_android_surface_GrVkExtensionFlag;
}
}
#elif SK_BUILD_FOR_UNIX
if (extensions.hasInstanceExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME)) {
instanceExtensionNames.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
extensionFlags |= kKHR_xlib_surface_GrVkExtensionFlag;
if (extensions.hasInstanceExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME)) {
instanceExtensionNames.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
extensionFlags |= kKHR_xcb_surface_GrVkExtensionFlag;
}
#endif
@ -159,7 +161,7 @@ const GrVkBackendContext* GrVkBackendContext::Create(uint32_t* presentQueueIndex
uint32_t presentQueueIndex = graphicsQueueIndex;
if (presentQueueIndexPtr && canPresent) {
for (uint32_t i = 0; i < queueCount; i++) {
if (canPresent(inst, physDev, i)) {
if (canPresent(inst, physDev, i, platformData)) {
presentQueueIndex = i;
break;
}

View File

@ -41,8 +41,9 @@ VulkanWindowContext::VulkanWindowContext(void* platformData, const DisplayParams
}
void VulkanWindowContext::initializeContext(void* platformData, const DisplayParams& params) {
fBackendContext.reset(GrVkBackendContext::Create(&fPresentQueueIndex, canPresent,
platformData));
fBackendContext.reset(GrVkBackendContext::Create(&fPresentQueueIndex, canPresent));
if (!(fBackendContext->fExtensions & kKHR_surface_GrVkExtensionFlag) ||
!(fBackendContext->fExtensions & kKHR_swapchain_GrVkExtensionFlag)) {
fBackendContext.reset(nullptr);
@ -147,6 +148,7 @@ bool VulkanWindowContext::createSwapchain(uint32_t width, uint32_t height,
} else if (extent.height > caps.maxImageExtent.height) {
extent.height = caps.maxImageExtent.height;
}
fWidth = (int)extent.width;
fHeight = (int)extent.height;

View File

@ -23,7 +23,8 @@ public:
// each platform will have to implement these in its CPP file
static VkSurfaceKHR createVkSurface(VkInstance, void* platformData);
static bool canPresent(VkInstance, VkPhysicalDevice, uint32_t queueFamilyIndex);
static bool canPresent(VkInstance, VkPhysicalDevice, uint32_t queueFamilyIndex,
void* platformData);
static VulkanWindowContext* Create(void* platformData, const DisplayParams& params) {
VulkanWindowContext* ctx = new VulkanWindowContext(platformData, params);
@ -99,8 +100,6 @@ private:
VkSwapchainKHR fSwapchain;
uint32_t fPresentQueueIndex;
VkQueue fPresentQueue;
int fWidth;
int fHeight;
uint32_t fImageCount;
VkImage* fImages; // images in the swapchain

View File

@ -40,7 +40,7 @@ VkSurfaceKHR VulkanWindowContext::createVkSurface(VkInstance instance, void* pla
}
bool VulkanWindowContext::canPresent(VkInstance instance, VkPhysicalDevice physDev,
uint32_t queueFamilyIndex) {
uint32_t queueFamilyIndex, void*) {
return true;
}

View File

@ -0,0 +1,74 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "../VulkanWindowContext.h"
#include "Window_unix.h"
#include "vk/GrVkInterface.h"
#include "vk/GrVkUtil.h"
#include <X11/Xlib-xcb.h>
namespace sk_app {
// Platform dependant call
VkSurfaceKHR VulkanWindowContext::createVkSurface(VkInstance instance, void* platformData) {
static PFN_vkCreateXcbSurfaceKHR createXcbSurfaceKHR = nullptr;
if (!createXcbSurfaceKHR) {
createXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR) vkGetInstanceProcAddr(instance,
"vkCreateXcbSurfaceKHR");
}
if (!platformData) {
return VK_NULL_HANDLE;
}
ContextPlatformData_unix* unixPlatformData =
reinterpret_cast<ContextPlatformData_unix*>(platformData);
VkSurfaceKHR surface;
VkXcbSurfaceCreateInfoKHR surfaceCreateInfo;
memset(&surfaceCreateInfo, 0, sizeof(VkXcbSurfaceCreateInfoKHR));
surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
surfaceCreateInfo.pNext = nullptr;
surfaceCreateInfo.flags = 0;
surfaceCreateInfo.connection = XGetXCBConnection(unixPlatformData->fDisplay);
surfaceCreateInfo.window = unixPlatformData->fHWnd;
VkResult res = createXcbSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface);
if (VK_SUCCESS != res) {
return VK_NULL_HANDLE;
}
return surface;
}
// Platform dependant call
bool VulkanWindowContext::canPresent(VkInstance instance, VkPhysicalDevice physDev,
uint32_t queueFamilyIndex, void* platformData) {
static PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR
getPhysicalDeviceXcbPresentationSupportKHR = nullptr;
if (!getPhysicalDeviceXcbPresentationSupportKHR) {
getPhysicalDeviceXcbPresentationSupportKHR =
(PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) vkGetInstanceProcAddr(instance,
"vkGetPhysicalDeviceXcbPresentationSupportKHR");
}
ContextPlatformData_unix* unixPlatformData =
reinterpret_cast<ContextPlatformData_unix*>(platformData);
Display* display = unixPlatformData->fDisplay;
VkBool32 check = getPhysicalDeviceXcbPresentationSupportKHR(physDev,
queueFamilyIndex,
XGetXCBConnection(display),
unixPlatformData->fVisualID);
return (VK_FALSE != check);
}
} // namespace sk_app

View File

@ -0,0 +1,221 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
//#include <tchar.h>
#include "SkUtils.h"
#include "Timer.h"
#include "../VulkanWindowContext.h"
#include "Window_unix.h"
extern "C" {
#include "keysym2ucs.h"
}
#include <X11/Xutil.h>
#include <X11/XKBlib.h>
namespace sk_app {
SkTDynamicHash<Window_unix, XWindow> Window_unix::gWindowMap;
Window* Window::CreateNativeWindow(void* platformData) {
Display* display = (Display*)platformData;
Window_unix* window = new Window_unix();
if (!window->init(display)) {
delete window;
return nullptr;
}
return window;
}
bool Window_unix::init(Display* display) {
fDisplay = display;
fWidth = 1280;
fHeight = 960;
fHWnd = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, fWidth, fHeight,
0, BlackPixel(display, DefaultScreen(display)),
BlackPixel(display, DefaultScreen(display)));
if (!fHWnd) {
return false;
}
// choose the events we care about
XSelectInput(display, fHWnd,
ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask |
PointerMotionMask | ButtonPressMask | ButtonReleaseMask);
// set up to catch window delete message
fWmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, fHWnd, &fWmDeleteMessage, 1);
// add to hashtable of windows
gWindowMap.add(this);
// init event variables
fPendingPaint = false;
fPendingResize = false;
return true;
}
static Window::Key get_key(KeySym keysym) {
static const struct {
KeySym fXK;
Window::Key fKey;
} gPair[] = {
{ XK_BackSpace, Window::Key::kBack },
{ XK_Clear, Window::Key::kBack },
{ XK_Return, Window::Key::kOK },
{ XK_Up, Window::Key::kUp },
{ XK_Down, Window::Key::kDown },
{ XK_Left, Window::Key::kLeft },
{ XK_Right, Window::Key::kRight }
};
for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
if (gPair[i].fXK == keysym) {
return gPair[i].fKey;
}
}
return Window::Key::kNONE;
}
static uint32_t get_modifiers(const XEvent& event) {
static const struct {
unsigned fXMask;
unsigned fSkMask;
} gModifiers[] = {
{ ShiftMask, Window::kShift_ModifierKey },
{ ControlMask, Window::kControl_ModifierKey },
{ Mod1Mask, Window::kOption_ModifierKey },
};
auto modifiers = 0;
for (size_t i = 0; i < SK_ARRAY_COUNT(gModifiers); ++i) {
if (event.xkey.state & gModifiers[i].fXMask) {
modifiers |= gModifiers[i].fSkMask;
}
}
return modifiers;
}
bool Window_unix::handleEvent(const XEvent& event) {
switch (event.type) {
case ClientMessage:
if ((Atom)event.xclient.data.l[0] == fWmDeleteMessage &&
gWindowMap.count() == 1) {
return true;
}
break;
case ButtonPress:
if (event.xbutton.button == Button1) {
this->onMouse(event.xbutton.x, event.xbutton.y,
Window::kDown_InputState, get_modifiers(event));
}
break;
case ButtonRelease:
if (event.xbutton.button == Button1) {
this->onMouse(event.xbutton.x, event.xbutton.y,
Window::kUp_InputState, get_modifiers(event));
}
break;
case MotionNotify:
// only track if left button is down
if (event.xmotion.state & Button1Mask) {
this->onMouse(event.xmotion.x, event.xmotion.y,
Window::kMove_InputState, get_modifiers(event));
}
break;
case KeyPress: {
int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0;
KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode,
0, shiftLevel);
if (keysym == XK_Escape) {
return true;
}
Window::Key key = get_key(keysym);
if (key != Window::Key::kNONE) {
(void) this->onKey(key, Window::kDown_InputState,
get_modifiers(event));
} else {
long uni = keysym2ucs(keysym);
if (uni != -1) {
(void) this->onChar((SkUnichar) uni,
get_modifiers(event));
}
}
} break;
case KeyRelease: {
int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0;
KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode,
0, shiftLevel);
Window::Key key = get_key(keysym);
(void) this->onKey(key, Window::kUp_InputState,
get_modifiers(event));
} break;
default:
// these events should be handled in the main event loop
SkASSERT(event.type != Expose && event.type != ConfigureNotify);
break;
}
return false;
}
void Window_unix::setTitle(const char* title) {
XTextProperty textproperty;
XStringListToTextProperty(const_cast<char**>(&title), 1, &textproperty);
XSetWMName(fDisplay, fHWnd, &textproperty);
}
void Window_unix::show() {
XMapWindow(fDisplay, fHWnd);
}
bool Window_unix::attach(BackendType attachType, const DisplayParams& params) {
ContextPlatformData_unix platformData;
platformData.fDisplay = fDisplay;
platformData.fHWnd = fHWnd;
XWindowAttributes attribs;
XGetWindowAttributes(fDisplay, fHWnd, &attribs);
platformData.fVisualID = XVisualIDFromVisual(attribs.visual);
switch (attachType) {
case kVulkan_BackendType:
default:
fWindowContext = VulkanWindowContext::Create((void*)&platformData, params);
break;
}
return (SkToBool(fWindowContext));
}
void Window_unix::onInval() {
XEvent event;
event.type = Expose;
event.xexpose.send_event = True;
event.xexpose.display = fDisplay;
event.xexpose.window = fHWnd;
event.xexpose.x = 0;
event.xexpose.y = 0;
event.xexpose.width = fWidth;
event.xexpose.height = fHeight;
event.xexpose.count = 0;
XSendEvent(fDisplay, fHWnd, False, 0, &event);
}
} // namespace sk_app

View File

@ -0,0 +1,86 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef Window_unix_DEFINED
#define Window_unix_DEFINED
#include <X11/Xlib.h>
#include "../Window.h"
#include "SkChecksum.h"
#include "SkTDynamicHash.h"
typedef Window XWindow;
namespace sk_app {
struct ContextPlatformData_unix {
Display* fDisplay;
XWindow fHWnd;
VisualID fVisualID;
};
class Window_unix : public Window {
public:
Window_unix() : Window() {}
~Window_unix() override {}
bool init(Display* display);
void setTitle(const char*) override;
void show() override;
bool attach(BackendType attachType, const DisplayParams& params) override;
void onInval() override;
bool handleEvent(const XEvent& event);
static const XWindow& GetKey(const Window_unix& w) {
return w.fHWnd;
}
static uint32_t Hash(const XWindow& w) {
return SkChecksum::Mix(w);
}
static SkTDynamicHash<Window_unix, XWindow> gWindowMap;
void markPendingPaint() { fPendingPaint = true; }
void finishPaint() {
if (fPendingPaint) {
this->onPaint();
fPendingPaint = false;
}
}
void markPendingResize(int width, int height) {
fPendingWidth = width;
fPendingHeight = height;
fPendingResize = true;
}
void finishResize() {
if (fPendingResize) {
this->onResize(fPendingWidth, fPendingHeight);
fPendingResize = false;
}
}
private:
Display* fDisplay;
XWindow fHWnd;
Atom fWmDeleteMessage;
bool fPendingPaint;
int fPendingWidth;
int fPendingHeight;
bool fPendingResize;
};
} // namespace sk_app
#endif

View File

@ -0,0 +1,90 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkTypes.h"
#include "SkTHash.h"
#include "Timer.h"
#include "Window_unix.h"
#include "../Application.h"
using sk_app::Application;
static double now_ms() { return SkTime::GetNSecs() * 1e-6; }
void finishWindow(sk_app::Window_unix* win) {
win->finishResize();
win->finishPaint();
}
int main(int argc, char**argv) {
Display* display = XOpenDisplay(nullptr);
Application* app = Application::Create(argc, argv, (void*)display);
double currentTime = 0.0;
double previousTime = 0.0;
// Get the file descriptor for the X display
int x11_fd = ConnectionNumber(display);
fd_set in_fds;
SkTHashSet<sk_app::Window_unix*> pendingWindows;
bool done = false;
while (!done) {
// Create a file description set containing x11_fd
FD_ZERO(&in_fds);
FD_SET(x11_fd, &in_fds);
// Set a sleep timer
struct timeval tv;
tv.tv_usec = 100;
tv.tv_sec = 0;
// Wait for an event on the file descriptor or for timer expiration
(void) select(1, &in_fds, NULL, NULL, &tv);
// Handle XEvents (if any) and flush the input
XEvent event;
while (XPending(display) && !done) {
XNextEvent(display, &event);
sk_app::Window_unix* win = sk_app::Window_unix::gWindowMap.find(event.xany.window);
// paint and resize events get collapsed
switch (event.type) {
case Expose:
win->markPendingPaint();
pendingWindows.add(win);
break;
case ConfigureNotify:
win->markPendingResize(event.xconfigurerequest.width,
event.xconfigurerequest.height);
pendingWindows.add(win);
break;
default:
if (win->handleEvent(event)) {
done = true;
}
break;
}
}
pendingWindows.foreach(finishWindow);
if (pendingWindows.count() > 0) {
previousTime = currentTime;
currentTime = now_ms();
app->onIdle(currentTime - previousTime);
}
pendingWindows.reset();
}
delete app;
XCloseDisplay(display);
return 0;
}

View File

@ -47,7 +47,7 @@ VkSurfaceKHR VulkanWindowContext::createVkSurface(VkInstance instance, void* pla
// Platform dependant call
bool VulkanWindowContext::canPresent(VkInstance instance, VkPhysicalDevice physDev,
uint32_t queueFamilyIndex) {
uint32_t queueFamilyIndex, void*) {
static PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR
getPhysicalDeviceWin32PresentationSupportKHR = nullptr;
if (!getPhysicalDeviceWin32PresentationSupportKHR) {