Refactor libXCB and libX11 wrappers

Previously these wrappers had slightly different interfaces, and the X11
one had its own header/source file while libXCB was part of
XcbSurfaceKHR.

This change separates the libXCB wrapper into its own files as well.
While it is only used by XcbSurfaceKHR and likely won't ever be used
elsewhere, the other WSI surface implementations don't use/need
wrapper classes so it helps to keep that these two responsibilities
separate.

Bug: b/203541908
Change-Id: I052d5079cd15183eb658fbd96fabbc4885cada12
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/58508
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Sean Risser <srisser@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/System/SharedLibrary.hpp b/src/System/SharedLibrary.hpp
index fc12842..a2c3d1b 100644
--- a/src/System/SharedLibrary.hpp
+++ b/src/System/SharedLibrary.hpp
@@ -133,4 +133,10 @@
 }
 #endif
 
+template<typename FunctionPointer>
+inline void getFuncAddress(void *library, const char *functionName, FunctionPointer *out)
+{
+	*out = reinterpret_cast<FunctionPointer>(getProcAddress(library, functionName));
+}
+
 #endif  // SharedLibrary_hpp
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 8bf30f7..0eb0ba1 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -312,10 +312,10 @@
 	{ { VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_SPEC_VERSION } },
 #endif
 #ifdef VK_USE_PLATFORM_XCB_KHR
-	{ { VK_KHR_XCB_SURFACE_EXTENSION_NAME, VK_KHR_XCB_SURFACE_SPEC_VERSION }, [] { return vk::XcbSurfaceKHR::hasLibXCB(); } },
+	{ { VK_KHR_XCB_SURFACE_EXTENSION_NAME, VK_KHR_XCB_SURFACE_SPEC_VERSION }, [] { return vk::XcbSurfaceKHR::isSupported(); } },
 #endif
 #ifdef VK_USE_PLATFORM_XLIB_KHR
-	{ { VK_KHR_XLIB_SURFACE_EXTENSION_NAME, VK_KHR_XLIB_SURFACE_SPEC_VERSION }, [] { return static_cast<bool>(libX11); } },
+	{ { VK_KHR_XLIB_SURFACE_EXTENSION_NAME, VK_KHR_XLIB_SURFACE_SPEC_VERSION }, [] { return vk::XlibSurfaceKHR::isSupported(); } },
 #endif
 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
 	{ { VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, VK_KHR_WAYLAND_SURFACE_SPEC_VERSION } },
diff --git a/src/WSI/BUILD.gn b/src/WSI/BUILD.gn
index 248ba31..22af89f 100644
--- a/src/WSI/BUILD.gn
+++ b/src/WSI/BUILD.gn
@@ -41,6 +41,8 @@
       "XlibSurfaceKHR.hpp",
       "libX11.cpp",
       "libX11.hpp",
+      "libXCB.cpp",
+      "libXCB.hpp",
     ]
   }
 
diff --git a/src/WSI/CMakeLists.txt b/src/WSI/CMakeLists.txt
index 84f5d80..4a27242 100644
--- a/src/WSI/CMakeLists.txt
+++ b/src/WSI/CMakeLists.txt
@@ -45,6 +45,8 @@
         list(APPEND WSI_SRC_FILES
             XcbSurfaceKHR.cpp
             XcbSurfaceKHR.hpp
+            libXCB.cpp
+            libXCB.hpp
         )
     endif()
 
diff --git a/src/WSI/XcbSurfaceKHR.cpp b/src/WSI/XcbSurfaceKHR.cpp
index b0fe106..4e2f4e6 100644
--- a/src/WSI/XcbSurfaceKHR.cpp
+++ b/src/WSI/XcbSurfaceKHR.cpp
@@ -14,83 +14,18 @@
 
 #include "XcbSurfaceKHR.hpp"
 
+#include "libXCB.hpp"
 #include "Vulkan/VkDeviceMemory.hpp"
 #include "Vulkan/VkImage.hpp"
 
-#include "System/SharedLibrary.hpp"
-
 #include <memory>
 
-namespace {
-
-template<typename FPTR>
-void getFuncAddress(void *lib, const char *name, FPTR *out)
-{
-	*out = reinterpret_cast<FPTR>(getProcAddress(lib, name));
-}
-
-struct LibXcbExports
-{
-	LibXcbExports(void *lib)
-	{
-		getFuncAddress(lib, "xcb_create_gc", &xcb_create_gc);
-		getFuncAddress(lib, "xcb_flush", &xcb_flush);
-		getFuncAddress(lib, "xcb_free_gc", &xcb_free_gc);
-		getFuncAddress(lib, "xcb_generate_id", &xcb_generate_id);
-		getFuncAddress(lib, "xcb_get_geometry", &xcb_get_geometry);
-		getFuncAddress(lib, "xcb_get_geometry_reply", &xcb_get_geometry_reply);
-		getFuncAddress(lib, "xcb_put_image", &xcb_put_image);
-	}
-
-	xcb_void_cookie_t (*xcb_create_gc)(xcb_connection_t *c, xcb_gcontext_t cid, xcb_drawable_t drawable, uint32_t value_mask, const void *value_list);
-	int (*xcb_flush)(xcb_connection_t *c);
-	xcb_void_cookie_t (*xcb_free_gc)(xcb_connection_t *c, xcb_gcontext_t gc);
-	uint32_t (*xcb_generate_id)(xcb_connection_t *c);
-	xcb_get_geometry_cookie_t (*xcb_get_geometry)(xcb_connection_t *c, xcb_drawable_t drawable);
-	xcb_get_geometry_reply_t *(*xcb_get_geometry_reply)(xcb_connection_t *c, xcb_get_geometry_cookie_t cookie, xcb_generic_error_t **e);
-	xcb_void_cookie_t (*xcb_put_image)(xcb_connection_t *c, uint8_t format, xcb_drawable_t drawable, xcb_gcontext_t gc, uint16_t width, uint16_t height, int16_t dst_x, int16_t dst_y, uint8_t left_pad, uint8_t depth, uint32_t data_len, const uint8_t *data);
-};
-
-class LibXcb
-{
-public:
-	operator bool()
-	{
-		return loadExports();
-	}
-
-	LibXcbExports *operator->()
-	{
-		return loadExports();
-	}
-
-private:
-	LibXcbExports *loadExports()
-	{
-		static auto exports = [] {
-			if(getProcAddress(RTLD_DEFAULT, "xcb_create_gc"))
-			{
-				return std::make_unique<LibXcbExports>(RTLD_DEFAULT);
-			}
-
-			if(auto lib = loadLibrary("libxcb.so.1"))
-			{
-				return std::make_unique<LibXcbExports>(lib);
-			}
-
-			return std::unique_ptr<LibXcbExports>();
-		}();
-
-		return exports.get();
-	}
-};
-
-LibXcb libXcb;
+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))
+	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);
@@ -101,19 +36,16 @@
 	return false;
 }
 
-}  // anonymous namespace
-
-namespace vk {
-
-bool XcbSurfaceKHR::hasLibXCB()
+bool XcbSurfaceKHR::isSupported()
 {
-	return libXcb;
+	return libXCB.isPresent();
 }
 
 XcbSurfaceKHR::XcbSurfaceKHR(const VkXcbSurfaceCreateInfoKHR *pCreateInfo, void *mem)
     : connection(pCreateInfo->connection)
     , window(pCreateInfo->window)
 {
+	ASSERT(isSupported());
 }
 
 void XcbSurfaceKHR::destroySurface(const VkAllocationCallbacks *pAllocator)
@@ -144,10 +76,10 @@
 
 void XcbSurfaceKHR::attachImage(PresentImage *image)
 {
-	auto gc = libXcb->xcb_generate_id(connection);
+	auto 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);
+	libXCB->xcb_create_gc(connection, gc, window, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, values);
 
 	graphicsContexts[image] = gc;
 }
@@ -157,7 +89,7 @@
 	auto it = graphicsContexts.find(image);
 	if(it != graphicsContexts.end())
 	{
-		libXcb->xcb_free_gc(connection, it->second);
+		libXCB->xcb_free_gc(connection, it->second);
 		graphicsContexts.erase(it);
 	}
 }
@@ -185,7 +117,7 @@
 		int stride = image->getImage()->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
 		auto buffer = reinterpret_cast<uint8_t *>(image->getImageMemory()->getOffsetPointer(0));
 		size_t bufferSize = extent.height * stride;
-		libXcb->xcb_put_image(
+		libXCB->xcb_put_image(
 		    connection,
 		    XCB_IMAGE_FORMAT_Z_PIXMAP,
 		    window,
@@ -199,7 +131,7 @@
 		    buffer       // data
 		);
 
-		libXcb->xcb_flush(connection);
+		libXCB->xcb_flush(connection);
 	}
 
 	return VK_SUCCESS;
diff --git a/src/WSI/XcbSurfaceKHR.hpp b/src/WSI/XcbSurfaceKHR.hpp
index 91c1d7e..4f2ee8b 100644
--- a/src/WSI/XcbSurfaceKHR.hpp
+++ b/src/WSI/XcbSurfaceKHR.hpp
@@ -28,6 +28,7 @@
 class XcbSurfaceKHR : public SurfaceKHR, public ObjectBase<XcbSurfaceKHR, VkSurfaceKHR>
 {
 public:
+	static bool isSupported();
 	XcbSurfaceKHR(const VkXcbSurfaceCreateInfoKHR *pCreateInfo, void *mem);
 
 	void destroySurface(const VkAllocationCallbacks *pAllocator) override;
@@ -40,8 +41,6 @@
 	virtual void detachImage(PresentImage *image) override;
 	VkResult present(PresentImage *image) override;
 
-	static bool hasLibXCB();
-
 private:
 	xcb_connection_t *connection;
 	xcb_window_t window;
diff --git a/src/WSI/XlibSurfaceKHR.cpp b/src/WSI/XlibSurfaceKHR.cpp
index 7885180..881f436 100644
--- a/src/WSI/XlibSurfaceKHR.cpp
+++ b/src/WSI/XlibSurfaceKHR.cpp
@@ -19,10 +19,17 @@
 
 namespace vk {
 
+bool XlibSurfaceKHR::isSupported()
+{
+	return libX11.isPresent();
+}
+
 XlibSurfaceKHR::XlibSurfaceKHR(const VkXlibSurfaceCreateInfoKHR *pCreateInfo, void *mem)
     : pDisplay(pCreateInfo->dpy)
     , window(pCreateInfo->window)
 {
+	ASSERT(isSupported());
+
 	int screen = DefaultScreen(pDisplay);
 	gc = libX11->XDefaultGC(pDisplay, screen);
 
diff --git a/src/WSI/XlibSurfaceKHR.hpp b/src/WSI/XlibSurfaceKHR.hpp
index fbba62a..a0e6aba 100644
--- a/src/WSI/XlibSurfaceKHR.hpp
+++ b/src/WSI/XlibSurfaceKHR.hpp
@@ -28,6 +28,7 @@
 class XlibSurfaceKHR : public SurfaceKHR, public ObjectBase<XlibSurfaceKHR, VkSurfaceKHR>
 {
 public:
+	static bool isSupported();
 	XlibSurfaceKHR(const VkXlibSurfaceCreateInfoKHR *pCreateInfo, void *mem);
 
 	void destroySurface(const VkAllocationCallbacks *pAllocator) override;
diff --git a/src/WSI/libX11.cpp b/src/WSI/libX11.cpp
index 72d9fdb..5d72c4f 100644
--- a/src/WSI/libX11.cpp
+++ b/src/WSI/libX11.cpp
@@ -15,18 +15,9 @@
 #include "libX11.hpp"
 
 #include "System/SharedLibrary.hpp"
+
 #include <memory>
 
-namespace {
-
-template<typename FPTR>
-void getFuncAddress(void *lib, const char *name, FPTR *out)
-{
-	*out = reinterpret_cast<FPTR>(getProcAddress(lib, name));
-}
-
-}  // anonymous namespace
-
 LibX11exports::LibX11exports(void *libX11, void *libXext)
 {
 	getFuncAddress(libX11, "XOpenDisplay", &XOpenDisplay);
diff --git a/src/WSI/libX11.hpp b/src/WSI/libX11.hpp
index 65fe593..b40bc6c 100644
--- a/src/WSI/libX11.hpp
+++ b/src/WSI/libX11.hpp
@@ -52,9 +52,9 @@
 class LibX11
 {
 public:
-	operator bool()
+	bool isPresent()
 	{
-		return loadExports();
+		return loadExports() != nullptr;
 	}
 
 	LibX11exports *operator->();
diff --git a/src/WSI/libXCB.cpp b/src/WSI/libXCB.cpp
new file mode 100644
index 0000000..c540cb8
--- /dev/null
+++ b/src/WSI/libXCB.cpp
@@ -0,0 +1,56 @@
+// Copyright 2021 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 "libXCB.hpp"
+
+#include "System/SharedLibrary.hpp"
+
+#include <memory>
+
+LibXcbExports::LibXcbExports(void *lib)
+{
+	getFuncAddress(lib, "xcb_create_gc", &xcb_create_gc);
+	getFuncAddress(lib, "xcb_flush", &xcb_flush);
+	getFuncAddress(lib, "xcb_free_gc", &xcb_free_gc);
+	getFuncAddress(lib, "xcb_generate_id", &xcb_generate_id);
+	getFuncAddress(lib, "xcb_get_geometry", &xcb_get_geometry);
+	getFuncAddress(lib, "xcb_get_geometry_reply", &xcb_get_geometry_reply);
+	getFuncAddress(lib, "xcb_put_image", &xcb_put_image);
+}
+
+LibXcbExports *LibXCB::operator->()
+{
+	return loadExports();
+}
+
+LibXcbExports *LibXCB::loadExports()
+{
+	static auto exports = [] {
+		if(getProcAddress(RTLD_DEFAULT, "xcb_create_gc"))
+		{
+			return std::make_unique<LibXcbExports>(RTLD_DEFAULT);
+		}
+
+		if(void *lib = loadLibrary("libxcb.so.1"))
+		{
+			return std::make_unique<LibXcbExports>(lib);
+		}
+
+		return std::unique_ptr<LibXcbExports>();
+	}();
+
+	return exports.get();
+}
+
+LibXCB libXCB;
diff --git a/src/WSI/libXCB.hpp b/src/WSI/libXCB.hpp
new file mode 100644
index 0000000..1e535cb
--- /dev/null
+++ b/src/WSI/libXCB.hpp
@@ -0,0 +1,49 @@
+// Copyright 2021 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.
+
+#ifndef libXCB_hpp
+#define libXCB_hpp
+
+#include <xcb/xcb.h>
+
+struct LibXcbExports
+{
+	LibXcbExports(void *lib);
+
+	xcb_void_cookie_t (*xcb_create_gc)(xcb_connection_t *c, xcb_gcontext_t cid, xcb_drawable_t drawable, uint32_t value_mask, const void *value_list);
+	int (*xcb_flush)(xcb_connection_t *c);
+	xcb_void_cookie_t (*xcb_free_gc)(xcb_connection_t *c, xcb_gcontext_t gc);
+	uint32_t (*xcb_generate_id)(xcb_connection_t *c);
+	xcb_get_geometry_cookie_t (*xcb_get_geometry)(xcb_connection_t *c, xcb_drawable_t drawable);
+	xcb_get_geometry_reply_t *(*xcb_get_geometry_reply)(xcb_connection_t *c, xcb_get_geometry_cookie_t cookie, xcb_generic_error_t **e);
+	xcb_void_cookie_t (*xcb_put_image)(xcb_connection_t *c, uint8_t format, xcb_drawable_t drawable, xcb_gcontext_t gc, uint16_t width, uint16_t height, int16_t dst_x, int16_t dst_y, uint8_t left_pad, uint8_t depth, uint32_t data_len, const uint8_t *data);
+};
+
+class LibXCB
+{
+public:
+	bool isPresent()
+	{
+		return loadExports() != nullptr;
+	}
+
+	LibXcbExports *operator->();
+
+private:
+	LibXcbExports *loadExports();
+};
+
+extern LibXCB libXCB;
+
+#endif  // libXCB_hpp