Merge pull request #6 from msiglreith/xcb

Surface and Swapchain extensions
This commit is contained in:
Dzmitry Malyshau 2017-12-07 17:32:45 -05:00 committed by GitHub
commit 7ff5dcefbd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 361 additions and 79 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/build/
/target/
**/*.rs.bk
Cargo.lock

14
CMakeLists.txt Normal file
View file

@ -0,0 +1,14 @@
cmake_minimum_required (VERSION 2.6)
project (portability)
include_directories("modules/vulkan-docs/src")
add_executable(native_test native/test.cpp native/window.cpp)
find_library(PORTABILITY_LIB portability "target/debug")
target_link_libraries(native_test ${PORTABILITY_LIB})
if (WIN32)
target_link_libraries(native_test Dwmapi Userenv ws2_32)
else (WIN32)
target_link_libraries(native_test pthread dl m X11 xcb)
endif (WIN32)

View file

@ -2,13 +2,13 @@ VULKAN_DIR=modules/vulkan-docs/src
BINDING=target/vulkan.rs
NATIVE_DIR=target/native
TARGET=$(NATIVE_DIR)/test
OBJECTS=$(NATIVE_DIR)/test.o
OBJECTS=$(NATIVE_DIR)/test.o $(NATIVE_DIR)/window.o
LIBRARY=target/debug/libportability.a
CC=g++
CFLAGS=-ggdb -O0 -I$(VULKAN_DIR)
CFLAGS=-std=c++11 -ggdb -O0 -I$(VULKAN_DIR)
DEPS=
LDFLAGS=-lpthread -ldl -lm -lX11
LDFLAGS=-lpthread -ldl -lm -lX11 -lxcb
.PHONY: all binding run

View file

@ -2,3 +2,26 @@
[![Build Status](https://travis-ci.org/kvark/portability.svg?branch=master)](https://travis-ci.org/kvark/portability)
This is a prototype static library implementing [Vulkan Portability Initiative](https://www.khronos.org/blog/khronos-announces-the-vulkan-portability-initiative) using gfx-rs [low-level core](http://gfx-rs.github.io/2017/07/24/low-level.html). See gfx-rs [meta issue](https://github.com/gfx-rs/gfx/issues/1354) for backend limitations and further details.
## Build
### Makefile (Unix)
```
make
```
### CMake (Window)
Build the Rust library (portability implementation):
```
cargo build
```
Build the native example:
```
mkdir build
cd build
cmake ..
cmake --build . --target native_test
```

View file

@ -1,10 +1,13 @@
/// Sample code adopted from https://github.com/LunarG/VulkanSamples
#if defined(_WIN32)
#define VK_USE_PLATFORM_WIN32_KHR
#endif
#include <vulkan/vulkan.h>
#include <assert.h>
#include <stdio.h>
extern "C" VkSurfaceKHR vkCreateSurfaceGFX(VkInstance);
#include "window.hpp"
int main() {
printf("starting the portability test\n");
@ -24,8 +27,20 @@ int main() {
return -1;
}
VkSurfaceKHR surface = vkCreateSurfaceGFX(instance);
printf("\tvkCreateSurfaceGFX\n");
// Window initialization
Config config = { 10, 10, 800, 600 };
Window window = new_window(config);
VkSurfaceKHR surface;
#if defined(_WIN32)
VkWin32SurfaceCreateInfoKHR surface_info = {};
surface_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
surface_info.hinstance = window.instance;
surface_info.hwnd = window.window;
vkCreateWin32SurfaceKHR(instance, &surface_info, NULL, &surface);
#endif
printf("\tvkCreateSurfaceKHR\n");
uint32_t adapter_count = 1;
VkPhysicalDevice physical_devices[1] = {};
@ -52,6 +67,22 @@ int main() {
printf("\tusing queue family index %d\n", queue_family_index);
assert(queue_family_index >= 0);
VkDeviceQueueCreateInfo queue_info = {};
float queue_priorities[1] = {0.0};
queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_info.queueCount = 1;
queue_info.pQueuePriorities = queue_priorities;
VkDeviceCreateInfo device_info = {};
device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_info.queueCreateInfoCount = 1;
device_info.pQueueCreateInfos = &queue_info;
VkDevice device = 0;
res = vkCreateDevice(physical_devices[0], &device_info, NULL, &device);
printf("\tvkCreateDevice: res=%d\n", res);
assert(!res);
VkSurfaceFormatKHR surfFormats[20];
uint32_t formatCount = sizeof(surfFormats) / sizeof(surfFormats[0]);
res = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_devices[0], surface, &formatCount, surfFormats);
@ -87,11 +118,11 @@ int main() {
VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
/*VkSwapchainCreateInfoKHR swapchain_ci = {0};
VkSwapchainCreateInfoKHR swapchain_ci = {};
swapchain_ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapchain_ci.surface = info.surface;
swapchain_ci.surface = surface;
swapchain_ci.minImageCount = desiredNumberOfSwapChainImages;
swapchain_ci.imageFormat = info.format;
swapchain_ci.imageFormat = surfFormats[0].format;
swapchain_ci.imageExtent.width = swapchainExtent.width;
swapchain_ci.imageExtent.height = swapchainExtent.height;
swapchain_ci.preTransform = preTransform;
@ -102,22 +133,23 @@ int main() {
swapchain_ci.clipped = true;
swapchain_ci.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
swapchain_ci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swapchain_ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;*/
swapchain_ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
VkDeviceQueueCreateInfo queue_info = {};
float queue_priorities[1] = {0.0};
queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_info.queueCount = 1;
queue_info.pQueuePriorities = queue_priorities;
VkSwapchainKHR swapchain = 0;
res = vkCreateSwapchainKHR(device, &swapchain_ci, NULL, &swapchain);
printf("\tvkCreateSwapchainKHR: res=%d\n", res);
VkDeviceCreateInfo device_info = {};
device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_info.queueCreateInfoCount = 1;
device_info.pQueueCreateInfos = &queue_info;
VkDevice device = 0;
res = vkCreateDevice(physical_devices[0], &device_info, NULL, &device);
printf("\tvkCreateDevice: res=%d\n", res);
uint32_t image_count = 0;
res = vkGetSwapchainImagesKHR(device, swapchain, &image_count, NULL);
printf("\tvkCreateSwapchainKHR (query): res=%d image_count=%d\n", res, image_count);
assert(!res);
VkImage *swapchain_images = new VkImage[image_count];
assert(swapchain_images);
res = vkGetSwapchainImagesKHR(device, swapchain, &image_count, swapchain_images);
printf("\tvkCreateSwapchainKHR: res=%d\n", res);
assert(!res);
VkCommandPool cmd_pool = 0;
@ -144,11 +176,21 @@ int main() {
assert(!res);
// Some work...
while(poll_events()) {
}
delete[] swapchain_images;
vkDestroySwapchainKHR(device, swapchain, NULL);
printf("\tvkDestroySwapchainKHR\n");
vkFreeCommandBuffers(device, cmd_pool, 1, &cmd_buffer);
printf("\tvkFreeCommandBuffers\n");
vkDestroyCommandPool(device, cmd_pool, NULL);
printf("\tvkDestroyCommandPool\n");
vkDestroySurfaceKHR(instance, surface, NULL);
printf("\tvkDestroySurfaceKHR\n");
vkDestroyDevice(device, NULL);
printf("\tvkDestroyDevice\n");
vkDestroyInstance(instance, NULL);
printf("done.\n");

116
native/window.cpp Normal file
View file

@ -0,0 +1,116 @@
#include <stdio.h>
#include "window.hpp"
#if defined(_WIN32)
const char *CLASS_NAME = "PortabilityClass";
auto WINAPI window_func(HWND hwnd, UINT u_msg, WPARAM w_param, LPARAM l_param) -> LRESULT {
if (u_msg == WM_CLOSE) {
PostQuitMessage(0);
}
return DefWindowProc(hwnd, u_msg, w_param, l_param);
}
auto register_class(HINSTANCE hinstance) -> bool {
WNDCLASS wclass = {0};
wclass.style = CS_HREDRAW | CS_VREDRAW;
wclass.lpszClassName = CLASS_NAME;
wclass.hInstance = hinstance;
wclass.lpfnWndProc = window_func;
wclass.hCursor = LoadCursor(NULL, IDC_ARROW);
if(!RegisterClass(&wclass)) {
printf("Couldn't register window class");
return false;
}
return true;
}
auto new_window(Config config) -> Window {
auto hinstance = GetModuleHandle(0);
register_class(hinstance);
RECT rect;
rect.left = config.x; rect.right = config.x + config.width;
rect.top = config.y; rect.bottom = config.y + config.height;
AdjustWindowRectEx(&rect, 0, false, 0);
auto hwnd = ::CreateWindow(
CLASS_NAME,
"GfxPortability",
WS_THICKFRAME | WS_SYSMENU,
rect.left,
rect.top,
rect.right-rect.left,
rect.bottom-rect.top,
NULL,
NULL,
::GetModuleHandle(0),
NULL
);
if(!hwnd) {
printf("Couldn't create window! error: %d", ::GetLastError());
}
::ShowWindow(hwnd, SW_SHOWDEFAULT);
::UpdateWindow(hwnd);
Window window = { hinstance, hwnd };
return window;
}
auto poll_events() -> bool {
MSG msg;
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
return false;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return true;
}
#else
auto new_window(Config config) -> Window {
auto connection = xcb_connect(NULL, NULL);
auto setup = xcb_get_setup(connection);
auto screen_iterator = xcb_setup_roots_iterator(setup);
auto screen = screen_iterator.data;
auto hwnd = xcb_generate_id(connection);
xcb_create_window(
connection,
XCB_COPY_FROM_PARENT,
hwnd,
screen->root,
config.x,
config.y,
config.width,
config.height,
0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
0,
NULL);
xcb_map_window(connection, hwnd);
xcb_flush(connection);
Window window = Window { connection, hwnd };
return window;
}
auto poll_events() -> bool {
return true;
}
#endif

29
native/window.hpp Normal file
View file

@ -0,0 +1,29 @@
#pragma once
#include <stdint.h>
#if defined(_WIN32)
#include <windows.h>
#else
#include <xcb/xcb.h>
#endif
struct Window {
#if defined(_WIN32)
HINSTANCE instance;
HWND window;
#else
xcb_connection_t *connection;
xcb_drawable_t window;
#endif
};
struct Config {
uint32_t x;
uint32_t y;
uint32_t width;
uint32_t height;
};
auto new_window(Config config) -> Window;
auto poll_events() -> bool;

View file

@ -41,6 +41,21 @@ pub fn format_from_hal(format: format::Format) -> VkFormat {
}
}
pub fn hal_from_format(format: VkFormat) -> format::Format {
use VkFormat::*;
use hal::format::ChannelType::*;
use hal::format::SurfaceType::*;
let (sf, cf) = match format {
VK_FORMAT_B8G8R8A8_UNORM => (B8_G8_R8_A8, Unorm),
_ => {
panic!("format {:?}", format);
}
};
format::Format(sf, cf)
}
pub fn extent2d_from_hal(extent: window::Extent2d) -> VkExtent2D {
VkExtent2D {
width: extent.width,

View file

@ -498,12 +498,9 @@ pub struct VkBuffer_T {
_unused: [u8; 0],
}
pub type VkBuffer = *mut VkBuffer_T;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct VkImage_T {
_unused: [u8; 0],
}
pub type VkImage = *mut VkImage_T;
pub type VkImage = Handle<<B as hal::Backend>::Image>;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct VkEvent_T {
@ -5206,16 +5203,7 @@ extern "C" {
//NOTE: all *KHR types have to be pure `Handle` things for compatibility with
//`VK_DEFINE_NON_DISPATCHABLE_HANDLE` used in `vulkan.h`
#[repr(C)]
pub struct VkSurfaceInner<Backend: hal::Backend> {
raw: Backend::Surface,
window: winit::Window,
events_loop: winit::EventsLoop,
}
pub type VkSurfaceKHR = Handle<VkSurfaceInner<B>>;
// TODO: temporary surface type, should be replacing `VkSurfaceKHR` in the future.
pub type VkSurfaceRawKHR = Handle<<B as hal::Backend>::Surface>;
pub type VkSurfaceKHR = Handle<<B as hal::Backend>::Surface>;
pub const VkColorSpaceKHR_VK_COLOR_SPACE_BEGIN_RANGE_KHR: VkColorSpaceKHR =
VkColorSpaceKHR::VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
@ -5346,7 +5334,7 @@ pub extern fn vkGetPhysicalDeviceSurfaceSupportKHR(
pSupported: *mut VkBool32,
) -> VkResult {
let family = &adapter.queue_families[queueFamilyIndex as usize];
let supports = surface.raw.supports_queue_family(family);
let supports = surface.supports_queue_family(family);
unsafe { *pSupported = supports as _ };
VkResult::VK_SUCCESS
}
@ -5357,7 +5345,7 @@ pub extern fn vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
surface: VkSurfaceKHR,
pSurfaceCapabilities: *mut VkSurfaceCapabilitiesKHR,
) -> VkResult {
let (caps, _) = surface.raw.capabilities_and_formats(&adapter.physical_device);
let (caps, _) = surface.capabilities_and_formats(&adapter.physical_device);
let output = VkSurfaceCapabilitiesKHR {
minImageCount: caps.image_count.start,
@ -5389,7 +5377,7 @@ pub extern fn vkGetPhysicalDeviceSurfaceFormatsKHR(
pSurfaceFormatCount: *mut u32,
pSurfaceFormats: *mut VkSurfaceFormatKHR,
) -> VkResult {
let (_, formats) = surface.raw.capabilities_and_formats(&adapter.physical_device);
let (_, formats) = surface.capabilities_and_formats(&adapter.physical_device);
let output = unsafe { slice::from_raw_parts_mut(pSurfaceFormats, *pSurfaceFormatCount as usize) };
if output.len() > formats.len() {
@ -5425,27 +5413,11 @@ pub extern fn vkGetPhysicalDeviceSurfacePresentModesKHR(
VkResult::VK_SUCCESS
}
/// This is an EXTRA function not in original vulkan.h
#[no_mangle]
pub extern fn vkCreateSurfaceGFX(instance: VkInstance) -> VkSurfaceKHR {
let events_loop = winit::EventsLoop::new();
let window = winit::Window::new(&events_loop).unwrap();
let inner = VkSurfaceInner {
raw: instance.create_surface(&window),
window: window,
events_loop: events_loop,
};
Handle::new(inner)
pub struct Swapchain {
raw: <B as hal::Backend>::Swapchain,
images: Vec<VkImage>,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct VkSwapchainKHR_T {
_unused: [u8; 0],
}
pub type VkSwapchainKHR = *mut VkSwapchainKHR_T;
pub type VkSwapchainKHR = Handle<Swapchain>;
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum VkSwapchainCreateFlagBitsKHR {
@ -5526,22 +5498,87 @@ pub type PFN_vkQueuePresentKHR =
pPresentInfo:
*const VkPresentInfoKHR)
-> VkResult>;
extern "C" {
pub fn vkCreateSwapchainKHR(device: VkDevice,
#[no_mangle]
pub extern fn vkCreateSwapchainKHR(
gpu: VkDevice,
pCreateInfo: *const VkSwapchainCreateInfoKHR,
_pAllocator: *const VkAllocationCallbacks,
pSwapchain: *mut VkSwapchainKHR,
) -> VkResult {
let info = unsafe { &*pCreateInfo };
// TODO: more checks
assert_eq!(info.clipped, VK_TRUE); // TODO
assert_eq!(info.imageSharingMode, VkSharingMode::VK_SHARING_MODE_EXCLUSIVE); // TODO
let config = hal::SwapchainConfig {
color_format: conv::hal_from_format(info.imageFormat),
depth_stencil_format: None,
image_count: info.minImageCount,
};
let (swapchain, backbuffers) = gpu.device.create_swapchain(&mut info.surface.clone(), config);
let images = match backbuffers {
hal::Backbuffer::Images(images) => {
images.into_iter().map(|image| Handle::new(image)).collect()
},
hal::Backbuffer::Framebuffer(_) => {
panic!("Expected backbuffer images. Backends returning only framebuffers are not supported!")
},
};
let swapchain = Swapchain {
raw: swapchain,
images,
};
unsafe { *pSwapchain = Handle::new(swapchain) };
VkResult::VK_SUCCESS
}
#[no_mangle]
pub extern fn vkDestroySwapchainKHR(
device: VkDevice,
mut swapchain: VkSwapchainKHR,
pAllocator: *const VkAllocationCallbacks,
pSwapchain: *mut VkSwapchainKHR) -> VkResult;
) {
for image in &mut swapchain.images {
let _ = image.unwrap();
}
extern "C" {
pub fn vkDestroySwapchainKHR(device: VkDevice, swapchain: VkSwapchainKHR,
pAllocator: *const VkAllocationCallbacks);
let _ = swapchain.unwrap();
}
extern "C" {
pub fn vkGetSwapchainImagesKHR(device: VkDevice,
#[no_mangle]
pub extern fn vkGetSwapchainImagesKHR(
device: VkDevice,
swapchain: VkSwapchainKHR,
pSwapchainImageCount: *mut u32,
pSwapchainImages: *mut VkImage)
-> VkResult;
pSwapchainImages: *mut VkImage,
) -> VkResult {
debug_assert!(!pSwapchainImageCount.is_null());
let swapchain_image_count = unsafe { *pSwapchainImageCount };
let available_images = swapchain.images.len();
if pSwapchainImages.is_null() {
// If NULL the number of presentable images is returned.
unsafe { *pSwapchainImageCount = swapchain.images.len() as _; }
} else {
let num_images = available_images.min(swapchain_image_count as _);
let swapchain_images = unsafe {
slice::from_raw_parts_mut(pSwapchainImages, num_images)
};
for i in 0..num_images as _ {
swapchain_images[i] = swapchain.images[i];
}
// Overwrite pSwapchainImageCount with actual image count
unsafe { *pSwapchainImageCount = num_images as _; }
if num_images < available_images {
return VkResult::VK_INCOMPLETE;
}
}
VkResult::VK_SUCCESS
}
extern "C" {
pub fn vkAcquireNextImageKHR(device: VkDevice, swapchain: VkSwapchainKHR,
@ -5821,11 +5858,11 @@ impl Clone for VkWin32SurfaceCreateInfoKHR {
fn clone(&self) -> Self { *self }
}
#[no_mangle]
pub fn vkCreateWin32SurfaceKHR(
pub extern fn vkCreateWin32SurfaceKHR(
instance: VkInstance,
pCreateInfos: *const VkWin32SurfaceCreateInfoKHR,
pAllocator: *const VkAllocationCallbacks,
pSurface: *mut VkSurfaceRawKHR,
pSurface: *mut VkSurfaceKHR,
) -> VkResult {
#[cfg(target_os = "windows")]
{
@ -5833,7 +5870,12 @@ pub fn vkCreateWin32SurfaceKHR(
assert_eq!((*pCreateInfos).flags, 0);
assert!(pAllocator.is_null());
// TODO: handle HINSTANCE
*pSurface = Handle::new(instance.create_surface_from_hwnd((*pCreateInfos).hwnd));
*pSurface = Handle::new(
instance.create_surface_from_hwnd(
(*pCreateInfos).hinstance,
(*pCreateInfos).hwnd,
)
);
VkResult::VK_SUCCESS
}
}