diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82be3cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +CMakeLists.txt.user +build-debug \ No newline at end of file diff --git a/driver/driver.c b/driver/driver.c index 2748052..4256983 100644 --- a/driver/driver.c +++ b/driver/driver.c @@ -15,6 +15,9 @@ #include #include +#include "vkExt.h" + +#include "modeset.h" #ifndef min #define min(a, b) (a < b ? a : b) @@ -56,8 +59,39 @@ typedef struct VkPhysicalDevice_T int dummy; } _physicalDevice; +typedef struct VkDevice_T +{ + int dummy; +} _device; + +typedef struct VkQueue_T +{ + int familyIndex; +} _queue; + +VkQueueFamilyProperties _queueFamilyProperties[] = +{ + { + //TODO maybe sparse textures later? + .queueFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT, + .queueCount = 1, + .timestampValidBits = 32, //TODO dunno, 32 for now + .minImageTransferGranularity = {1, 1, 1} + } +}; +const int numQueueFamilies = sizeof(_queueFamilyProperties)/sizeof(VkQueueFamilyProperties); + +_queue _queuesByFamily[][1] = +{ + { + { + .familyIndex = 0 + } + } +}; + /* - * https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#vkCreateInstance + * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkCreateInstance * There is no global state in Vulkan and all per-application state is stored in a VkInstance object. Creating a VkInstance object initializes the Vulkan library * vkCreateInstance verifies that the requested layers exist. If not, vkCreateInstance will return VK_ERROR_LAYER_NOT_PRESENT. Next vkCreateInstance verifies that * the requested extensions are supported (e.g. in the implementation or in any enabled instance layer) and if any requested extension is not supported, @@ -70,6 +104,7 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance( VkInstance* pInstance) { *pInstance = malloc(sizeof(_instance)); + assert(pInstance); //TODO: allocator is ignored for now assert(pAllocator == 0); @@ -79,12 +114,14 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance( //TODO: need to check here that the requested //extensions are supported + //eg. + //VK_KHR_surface return VK_SUCCESS; } /* - * https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#devsandqueues-physical-device-enumeration + * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#devsandqueues-physical-device-enumeration * If pPhysicalDevices is NULL, then the number of physical devices available is returned in pPhysicalDeviceCount. Otherwise, pPhysicalDeviceCount must point to a * variable set by the user to the number of elements in the pPhysicalDevices array, and on return the variable is overwritten with the number of handles actually * written to pPhysicalDevices. If pPhysicalDeviceCount is less than the number of physical devices available, at most pPhysicalDeviceCount structures will be written. @@ -96,6 +133,8 @@ VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices( uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) { + assert(instance); + //TODO is there a way to check if there's a gpu (and it's the rPi)? int gpuExists = access( "/dev/dri/card0", F_OK ) != -1; @@ -114,7 +153,9 @@ VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices( for(int c = 0; c < elementsWritten; ++c) { + //TODO no allocator, we probably shouldn't allocate pPhysicalDevices[c] = malloc(sizeof(_physicalDevice)); + assert(pPhysicalDevices[c]); } *pPhysicalDeviceCount = elementsWritten; @@ -130,15 +171,191 @@ VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices( } /* - * https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#vkGetPhysicalDeviceQueueFamilyProperties - * + * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkGetPhysicalDeviceQueueFamilyProperties + * If pQueueFamilyProperties is NULL, then the number of queue families available is returned in pQueueFamilyPropertyCount. + * Otherwise, pQueueFamilyPropertyCount must point to a variable set by the user to the number of elements in the pQueueFamilyProperties array, + * and on return the variable is overwritten with the number of structures actually written to pQueueFamilyProperties. If pQueueFamilyPropertyCount + * is less than the number of queue families available, at most pQueueFamilyPropertyCount structures will be written. */ VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties( VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties) { + assert(physicalDevice); + assert(pQueueFamilyPropertyCount); + if(!pQueueFamilyProperties) + { + *pQueueFamilyPropertyCount = 1; + return; + } + + int arraySize = *pQueueFamilyPropertyCount; + int elementsWritten = min(numQueueFamilies, arraySize); + + for(int c = 0; c < elementsWritten; ++c) + { + pQueueFamilyProperties[c] = _queueFamilyProperties[c]; + } + + *pQueueFamilyPropertyCount = elementsWritten; +} + +/* + * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkGetPhysicalDeviceSurfaceSupportKHR + * does this queue family support presentation to this surface? + */ +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceSupportKHR( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + VkSurfaceKHR surface, + VkBool32* pSupported) +{ + assert(pSupported); + assert(surface); + assert(physicalDevice); + + assert(queueFamilyIndex < numQueueFamilies); + + *pSupported = VK_TRUE; //TODO suuure for now, but we should verify if queue supports presenting to surface + return VK_SUCCESS; +} + +/* + * Implementation of our RPI specific "extension" + */ +VkResult vkCreateRpiSurfaceKHR( + VkInstance instance, + const VkRpiSurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface) +{ + assert(pSurface); + //TODO: allocator is ignored for now + assert(pAllocator == 0); + + int ret = modeset_open("/dev/dri/card0"); assert(!ret); + *pSurface = (VkSurfaceKHR)modeset_create(); + + return VK_SUCCESS; +} + +/* + * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkDestroySurfaceKHR + * Destroying a VkSurfaceKHR merely severs the connection between Vulkan and the native surface, + * and does not imply destroying the native surface, closing a window, or similar behavior + * (but we'll do so anyways...) + */ +VKAPI_ATTR void VKAPI_CALL vkDestroySurfaceKHR( + VkInstance instance, + VkSurfaceKHR surface, + const VkAllocationCallbacks* pAllocator) +{ + assert(instance); + assert(surface); + + //TODO: allocator is ignored for now + assert(pAllocator == 0); + + modeset_destroy((modeset_dev*)surface); + modeset_close(); +} + +/* + * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkCreateDevice + * vkCreateDevice verifies that extensions and features requested in the ppEnabledExtensionNames and pEnabledFeatures + * members of pCreateInfo, respectively, are supported by the implementation. If any requested extension is not supported, + * vkCreateDevice must return VK_ERROR_EXTENSION_NOT_PRESENT. If any requested feature is not supported, vkCreateDevice must return + * VK_ERROR_FEATURE_NOT_PRESENT. Support for extensions can be checked before creating a device by querying vkEnumerateDeviceExtensionProperties + * After verifying and enabling the extensions the VkDevice object is created and returned to the application. + * If a requested extension is only supported by a layer, both the layer and the extension need to be specified at vkCreateInstance + * time for the creation to succeed. Multiple logical devices can be created from the same physical device. Logical device creation may + * fail due to lack of device-specific resources (in addition to the other errors). If that occurs, vkCreateDevice will return VK_ERROR_TOO_MANY_OBJECTS. + */ +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice( + VkPhysicalDevice physicalDevice, + const VkDeviceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDevice* pDevice) +{ + assert(physicalDevice); + assert(pDevice); + + //TODO verify extensions, features + + //TODO: allocator is ignored for now + assert(pAllocator == 0); + + *pDevice = malloc(sizeof(_device)); + if(!pDevice) + { + return VK_ERROR_TOO_MANY_OBJECTS; + } + + return VK_SUCCESS; +} + +/* + * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkGetDeviceQueue + * vkGetDeviceQueue must only be used to get queues that were created with the flags parameter of VkDeviceQueueCreateInfo set to zero. + * To get queues that were created with a non-zero flags parameter use vkGetDeviceQueue2. + */ +VKAPI_ATTR void VKAPI_CALL vkGetDeviceQueue( + VkDevice device, + uint32_t queueFamilyIndex, + uint32_t queueIndex, + VkQueue* pQueue) +{ + assert(device); + assert(pQueue); + + assert(queueFamilyIndex < numQueueFamilies); + assert(queueIndex < _queueFamilyProperties[queueFamilyIndex].queueCount); + + *pQueue = &_queuesByFamily[queueFamilyIndex][queueIndex]; +} + +/* + * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkCreateSemaphore + * Semaphores are a synchronization primitive that can be used to insert a dependency between batches submitted to queues. + * Semaphores have two states - signaled and unsignaled. The state of a semaphore can be signaled after execution of a batch of commands is completed. + * A batch can wait for a semaphore to become signaled before it begins execution, and the semaphore is also unsignaled before the batch begins execution. + * As with most objects in Vulkan, semaphores are an interface to internal data which is typically opaque to applications. + * This internal data is referred to as a semaphore’s payload. However, in order to enable communication with agents outside of the current device, + * it is necessary to be able to export that payload to a commonly understood format, and subsequently import from that format as well. + * The internal data of a semaphore may include a reference to any resources and pending work associated with signal or unsignal operations performed on that semaphore object. + * Mechanisms to import and export that internal data to and from semaphores are provided below. + * These mechanisms indirectly enable applications to share semaphore state between two or more semaphores and other synchronization primitives across process and API boundaries. + * When created, the semaphore is in the unsignaled state. + */ +VKAPI_ATTR VkResult VKAPI_CALL vkCreateSemaphore( + VkDevice device, + const VkSemaphoreCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSemaphore* pSemaphore) +{ + assert(device); + assert(pSemaphore); + + //TODO: allocator is ignored for now + assert(pAllocator == 0); + + //we'll probably just use an IOCTL to wait for a GPU sequence number to complete. + *pSemaphore = -1; + + return VK_SUCCESS; +} + +/* + * https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#vkGetPhysicalDeviceSurfaceCapabilitiesKHR + */ +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + VkSurfaceCapabilitiesKHR* pSurfaceCapabilities) +{ + return VK_SUCCESS; } VKAPI_ATTR VkResult VKAPI_CALL vkDeviceWaitIdle( @@ -187,14 +404,6 @@ VKAPI_ATTR void VKAPI_CALL vkDestroyDevice( } -VKAPI_ATTR void VKAPI_CALL vkDestroySurfaceKHR( - VkInstance instance, - VkSurfaceKHR surface, - const VkAllocationCallbacks* pAllocator) -{ - -} - VKAPI_ATTR void VKAPI_CALL vkDestroyInstance( VkInstance instance, const VkAllocationCallbacks* pAllocator) @@ -320,63 +529,3 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceFormatsKHR( { return VK_SUCCESS; } - -VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilitiesKHR( - VkPhysicalDevice physicalDevice, - VkSurfaceKHR surface, - VkSurfaceCapabilitiesKHR* pSurfaceCapabilities) -{ - return VK_SUCCESS; -} - -VKAPI_ATTR VkResult VKAPI_CALL vkCreateSemaphore( - VkDevice device, - const VkSemaphoreCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSemaphore* pSemaphore) -{ - return VK_SUCCESS; -} - -VKAPI_ATTR void VKAPI_CALL vkGetDeviceQueue2( - VkDevice device, - const VkDeviceQueueInfo2* pQueueInfo, - VkQueue* pQueue) -{ - -} - -VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice( - VkPhysicalDevice physicalDevice, - const VkDeviceCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDevice* pDevice) -{ - return VK_SUCCESS; -} - -VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceSupportKHR( - VkPhysicalDevice physicalDevice, - uint32_t queueFamilyIndex, - VkSurfaceKHR surface, - VkBool32* pSupported) -{ - return VK_SUCCESS; -} - -VKAPI_ATTR void VKAPI_CALL vkGetDeviceQueue( - VkDevice device, - uint32_t queueFamilyIndex, - uint32_t queueIndex, - VkQueue* pQueue) -{ - -} - - - - - - - - diff --git a/driver/modeset.c b/driver/modeset.c index aba191f..1f3c843 100644 --- a/driver/modeset.c +++ b/driver/modeset.c @@ -69,11 +69,10 @@ struct modeset_dev { drmModeCrtc *saved_crtc; }; -static struct modeset_dev *modeset_list = NULL; +//static struct modeset_dev *modeset_list = NULL; static int fd = -1; -static int modeset_prepare(); static int modeset_find_crtc(drmModeRes *res, drmModeConnector *conn, struct modeset_dev *dev); static int modeset_create_fb(struct modeset_buf *buf); @@ -124,15 +123,12 @@ int modeset_open(const char *node) return -1; } - return modeset_prepare(); + return 0; } -/* - * modeset_prepare() stays the same. - */ - -static int modeset_prepare() +modeset_dev* modeset_create() { + modeset_dev* ret_dev = 0; drmModeRes *res; drmModeConnector *conn; struct modeset_dev *dev; @@ -142,7 +138,7 @@ static int modeset_prepare() res = drmModeGetResources(fd); if (!res) { printf("cannot retrieve DRM resources (%d): %m\n", errno); - return -1; + return 0; } // iterate all connectors @@ -173,8 +169,8 @@ static int modeset_prepare() // free connector data and link device into global list drmModeFreeConnector(conn); - dev->next = modeset_list; - modeset_list = dev; + dev->next = ret_dev; + ret_dev = dev; } // free resources again @@ -182,7 +178,7 @@ static int modeset_prepare() struct modeset_dev *iter; struct modeset_buf *buf; - for (iter = modeset_list; iter; iter = iter->next) { + for (iter = ret_dev; iter; iter = iter->next) { iter->saved_crtc = drmModeGetCrtc(fd, iter->crtc); buf = &iter->bufs[iter->front_buf]; ret = drmModeSetCrtc(fd, iter->crtc, buf->fb, 0, 0, @@ -191,7 +187,8 @@ static int modeset_prepare() printf("cannot set CRTC for connector %u (%d): %m\n", iter->conn, errno); } - return 0; + + return ret_dev; } /* @@ -263,7 +260,7 @@ static int modeset_setup_dev(drmModeRes *res, drmModeConnector *conn, */ static int modeset_find_crtc(drmModeRes *res, drmModeConnector *conn, - struct modeset_dev *dev) + modeset_dev *dev) { drmModeEncoder *enc; unsigned int i, j; @@ -279,7 +276,7 @@ static int modeset_find_crtc(drmModeRes *res, drmModeConnector *conn, if (enc) { if (enc->crtc_id) { crtc = enc->crtc_id; - for (iter = modeset_list; iter; iter = iter->next) { + for (iter = dev; iter; iter = iter->next) { if (iter->crtc == crtc) { crtc = -1; break; @@ -316,7 +313,7 @@ static int modeset_find_crtc(drmModeRes *res, drmModeConnector *conn, // check that no other device already uses this CRTC crtc = res->crtcs[j]; - for (iter = modeset_list; iter; iter = iter->next) { + for (iter = dev; iter; iter = iter->next) { if (iter->crtc == crtc) { crtc = -1; break; @@ -476,13 +473,13 @@ static void modeset_destroy_fb(struct modeset_buf *buf) * vertical-sync. */ -void modeset_swapbuffer() +void modeset_swapbuffer(modeset_dev* dev) { struct modeset_dev *iter; struct modeset_buf *buf; int ret; - for (iter = modeset_list; iter; iter = iter->next) { + for (iter = dev; iter; iter = iter->next) { buf = &iter->bufs[iter->front_buf ^ 1]; ret = drmModeSetCrtc(fd, iter->crtc, buf->fb, 0, 0, @@ -500,14 +497,14 @@ void modeset_swapbuffer() * modeset_destroy_fb() instead of accessing the framebuffers directly. */ -void modeset_cleanup() +void modeset_destroy(modeset_dev* dev) { struct modeset_dev *iter; - while (modeset_list) { + while (dev) { // remove from global list - iter = modeset_list; - modeset_list = iter->next; + iter = dev; + dev = iter->next; // restore saved CRTC configuration drmModeSetCrtc(fd, @@ -527,7 +524,10 @@ void modeset_cleanup() // free allocated memory free(iter); } +} +void modeset_close() +{ close(fd); } diff --git a/driver/modeset.h b/driver/modeset.h index 9c127fd..4c2360e 100644 --- a/driver/modeset.h +++ b/driver/modeset.h @@ -17,9 +17,13 @@ extern "C" { #include #include -int modeset_open(const char *node); -void modeset_swapbuffer(); -void modeset_cleanup(); +typedef struct modeset_dev modeset_dev; + +int modeset_open(const char* node); +modeset_dev* modeset_create(); +void modeset_swapbuffer(modeset_dev* dev); +void modeset_destroy(modeset_dev* dev); +void modeset_close(); #if defined (__cplusplus) } diff --git a/driver/vkExt.h b/driver/vkExt.h new file mode 100644 index 0000000..a873070 --- /dev/null +++ b/driver/vkExt.h @@ -0,0 +1,32 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +//we need something like the other platforms to create surfaces on the RPI +//so I created this little "extension" +//full spec in this file ;) + +typedef enum VkRpiSurfaceCreateFlagsKHR { + //reserved + VK_RPI_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkRpiSurfaceCreateFlagsKHR; + +typedef struct VkRpiSurfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkRpiSurfaceCreateFlagsKHR flags; + //maybe include some other stuff dunno + } VkRpiSurfaceCreateInfoKHR; + +VkResult vkCreateRpiSurfaceKHR( + VkInstance instance, + const VkRpiSurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); + + +#ifdef __cplusplus +} +#endif diff --git a/test/test.cpp b/test/test.cpp index 050eb7b..20cf5fa 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -5,7 +5,7 @@ #include -#include "driver/modeset.h" +#include "driver/vkExt.h" //#define GLFW_INCLUDE_VULKAN //#define VK_USE_PLATFORM_WIN32_KHR @@ -165,12 +165,12 @@ void createInstance() { } void createWindowSurface() { - //if (glfwCreateWindowSurface(instance, window, NULL, &windowSurface) != VK_SUCCESS) { - // std::cerr << "failed to create window surface!" << std::endl; - // assert(0); - //} + if (vkCreateRpiSurfaceKHR(instance, 0, 0, &windowSurface) != VK_SUCCESS) { + std::cerr << "failed to create window surface!" << std::endl; + assert(0); + } - //std::cout << "created window surface" << std::endl; + std::cout << "created window surface" << std::endl; } void findPhysicalDevice() { @@ -670,17 +670,16 @@ int main() { //window = glfwCreateWindow(640, 480, "The 630 line cornflower blue window", nullptr, nullptr); - int ret = 0; - ret = modeset_open("/dev/dri/card0"); assert(!ret); - modeset_swapbuffer(); + //TODO create surface + //swapbuffer // Use Vulkan setupVulkan(); mainLoop(); + //TODO destroy surface cleanup(); - modeset_cleanup(); return 0; }