diff options
| -rw-r--r-- | CMakeLists.txt | 13 | ||||
| -rw-r--r-- | README.md | 14 | ||||
| -rw-r--r-- | include/vkvg.h | 25 | ||||
| -rw-r--r-- | shaders/vkvg_main.frag | 4 | ||||
| -rw-r--r-- | src/nsvg/vkvg_nsvg.c | 2 | ||||
| -rw-r--r-- | src/vkvg_context.c | 27 | ||||
| -rw-r--r-- | src/vkvg_context_internal.c | 75 | ||||
| -rw-r--r-- | src/vkvg_context_internal.h | 8 | ||||
| -rw-r--r-- | src/vkvg_device.c | 79 | ||||
| -rw-r--r-- | src/vkvg_device_internal.c | 67 | ||||
| -rw-r--r-- | src/vkvg_device_internal.h | 18 | ||||
| -rw-r--r-- | src/vkvg_fonts.c | 5 | ||||
| -rw-r--r-- | src/vkvg_pattern.c | 4 | ||||
| -rw-r--r-- | src/vkvg_pattern.h | 2 | ||||
| -rw-r--r-- | src/vkvg_surface.c | 72 | ||||
| -rw-r--r-- | src/vkvg_surface_internal.c | 67 | ||||
| -rw-r--r-- | src/vkvg_surface_internal.h | 14 | ||||
| -rw-r--r-- | tests/multithreading/multithreaded.c | 2 | ||||
| m--------- | vkh | 0 |
19 files changed, 367 insertions, 131 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a1a47d..624311f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,10 +135,15 @@ CMAKE_DEPENDENT_OPTION(VKVG_BUILD_DOCS "build documentation" OFF "DOXYGEN_FOUND CMAKE_DEPENDENT_OPTION(VKVG_LCD_FONT_FILTER "enable freetype lcd font filtering" OFF "VKVG_USE_FONTCONFIG" OFF) # todo if cmake vers >= 3.23: make dependent option on Vulkan_VERSION >= 1_2: -OPTION(VKVG_VK_SCALAR_BLOCK_SUPPORTED "Enable scalar block layout support." ON) -IF (VKVG_VK_SCALAR_BLOCK_SUPPORTED) - ADD_DEFINITIONS (-DVKVG_VK_SCALAR_BLOCK_SUPPORTED) - SET(GLSLDEFS "-DVKVG_VK_SCALAR_BLOCK_SUPPORTED") +OPTION(VKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT "Enable scalar block layout support." ON) +IF (VKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT) + ADD_DEFINITIONS (-DVKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT) + SET(GLSLDEFS "-DVKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT") +ENDIF () + +OPTION(VKVG_ENABLE_VK_TIMELINE_SEMAPHORE "Enable vulkan TimelineSemaphore support." ON) +IF (VKVG_ENABLE_VK_TIMELINE_SEMAPHORE) + ADD_DEFINITIONS (-DVKVG_ENABLE_VK_TIMELINE_SEMAPHORE) ENDIF () @@ -36,13 +36,12 @@ </h1> +**vkvg** is an open-source *2D graphics library* written in **C** using [Vulkan](https://www.khronos.org/vulkan/) as backend. +The **API** follows the same pattern as [Cairo](https://www.cairographics.org/), but new functions and original drawing mechanics may be added. +**vkvg** is in alpha development stage and the core api is mostly stabilized. All contributions are welcome. -**vkvg** is an open-source *2D graphics library* written in **C** using [Vulkan](https://www.khronos.org/vulkan/) as backend. The **API** follows the same pattern as [Cairo](https://www.cairographics.org/), but new functions and original drawing mechanics may be added. - -**vkvg** is in early development stage, the API is subject to change. All contributions are welcome. - -For API documentation and usage, please refer to the [Cairo](https://www.cairographics.org/) for now. +In progress API documentation is available online at https://www.vkvg.org. <p align="center"> <a href="https://github.com/jpbruyere/vkvg/blob/master/vkvg-tiger.png"> @@ -116,6 +115,11 @@ cmake .. * `-DVKVG_RECORDING=true`: Enable experimental draw commands recording infrastructure. * `-DVKVG_BUILD_DOCS=true`: Build documentation if doxygen is found. +##### Vulkan Features: + +* `-DVKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT=true`: Enable `VK_EXT_scalar_block_layout` that reduce structure padding for gpu. +* `-VKVG_ENABLE_VK_TIMELINE_SEMAPHORE=true`: Enable experimental work syncing with `VK_KHR_timeline_semaphore` instead of Fences. + ##### Text rendering libraries: Those libraries are enabled by default, but disabled if not found. diff --git a/include/vkvg.h b/include/vkvg.h index 7313bd3..212511f 100644 --- a/include/vkvg.h +++ b/include/vkvg.h @@ -88,6 +88,7 @@ extern "C" { #define VKVG_LOG_INFO_VBO 0x00000020 #define VKVG_LOG_INFO_IBO 0x00000040 #define VKVG_LOG_INFO_VAO (VKVG_LOG_INFO_VBO|VKVG_LOG_INFO_IBO) +#define VKVG_LOG_THREAD 0x00000080 #define VKVG_LOG_DBG_ARRAYS 0x00001000 #define VKVG_LOG_STROKE 0x00010000 #define VKVG_LOG_FULL 0xffffffff @@ -136,7 +137,8 @@ typedef enum { VKVG_STATUS_DEVICE_ERROR, /*!< vkvg device initialization error */ VKVG_STATUS_INVALID_IMAGE, /*!< */ VKVG_STATUS_INVALID_SURFACE, /*!< */ - VKVG_STATUS_INVALID_FONT /*!< Unresolved font name*/ + VKVG_STATUS_INVALID_FONT, /*!< Unresolved font name*/ + VKVG_STATUS_ENUM_MAX = 0x7FFFFFFF }vkvg_status_t; typedef enum { @@ -542,13 +544,30 @@ void vkvg_matrix_get_scale (const vkvg_matrix_t *matrix, float *sx, float *sy); * @{ */ /** - * @brief vkvg_device_set_thread_aware + * @brief Set device ready for multithreading. + * + * If thread aware mode is set to true, + * + * This method should be called only once on device creation. If this method is called while some surfaces or patterns are + * in use, this could have unexpected results. + * + * * @param dev * @param thread_awayre */ vkvg_public void vkvg_device_set_thread_aware (VkvgDevice dev, uint32_t thread_awayre); - +vkvg_public +/** + * @brief Set maximum cached context count. + * + * The context cache stored destroyed contexts per thread to speed-up new context creation. + * To disable context cache, call this method with maxCount=0. + * + * @param dev A valid vkvg device pointer. + * @param maxCount The maximum count of saved contexts for fast context instanciation. + */ +void vkvg_device_set_context_cache_size (VkvgDevice dev, uint32_t maxCount); /** * @brief Create a new vkvg device. * diff --git a/shaders/vkvg_main.frag b/shaders/vkvg_main.frag index ce93276..cb1f163 100644 --- a/shaders/vkvg_main.frag +++ b/shaders/vkvg_main.frag @@ -23,13 +23,13 @@ #extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_shading_language_420pack : enable -#ifdef VKVG_VK_SCALAR_BLOCK_SUPPORTED +#ifdef VKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT #extension GL_EXT_scalar_block_layout : enable #endif layout (set=0, binding = 0) uniform sampler2DArray fontMap; layout (set=1, binding = 0) uniform sampler2D source; -#if defined(GL_EXT_scalar_block_layout) && defined(VKVG_VK_SCALAR_BLOCK_SUPPORTED) +#if defined(GL_EXT_scalar_block_layout) && defined(VKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT) layout (scalar, set=2, binding = 0) uniform _uboGrad { vec4 colors[16]; float stops[16]; diff --git a/src/nsvg/vkvg_nsvg.c b/src/nsvg/vkvg_nsvg.c index 0c34450..681471a 100644 --- a/src/nsvg/vkvg_nsvg.c +++ b/src/nsvg/vkvg_nsvg.c @@ -46,7 +46,7 @@ VkvgSurface _svg_load (VkvgDevice dev, NSVGimage* svg) { surf->width = (uint32_t)svg->width; surf->height = (uint32_t)svg->height; - surf->new = true; + surf->newSurf = true; _create_surface_images (surf); diff --git a/src/vkvg_context.c b/src/vkvg_context.c index 88249be..0078598 100644 --- a/src/vkvg_context.c +++ b/src/vkvg_context.c @@ -62,12 +62,16 @@ void _init_ctx (VkvgContext ctx) { ctx->renderPassBeginInfo.renderArea.extent.height = ctx->pSurf->height; ctx->renderPassBeginInfo.pClearValues = clearValues; - if (ctx->pSurf->new) + LOCK_SURFACE (ctx->pSurf) + + if (ctx->pSurf->newSurf) ctx->renderPassBeginInfo.renderPass = ctx->dev->renderPass_ClearAll; else ctx->renderPassBeginInfo.renderPass = ctx->dev->renderPass_ClearStencil; + ctx->pSurf->newSurf = false; + + UNLOCK_SURFACE (ctx->pSurf); - ctx->pSurf->new = false; vkvg_surface_reference (ctx->pSurf); if (ctx->dev->samples == VK_SAMPLE_COUNT_1_BIT) @@ -83,6 +87,9 @@ void _init_ctx (VkvgContext ctx) { ctx->cmdStarted = false; ctx->curClipState = vkvg_clip_state_none; ctx->vertCount = ctx->indCount = 0; +#ifdef VKVG_ENABLE_VK_TIMELINE_SEMAPHORE + ctx->timelineStep = 0; +#endif } VkvgContext vkvg_create(VkvgSurface surf) @@ -149,7 +156,10 @@ VkvgContext vkvg_create(VkvgSurface surf) //for context to be thread safe, command pool and descriptor pool have to be created in the thread of the context. ctx->cmdPool = vkh_cmd_pool_create ((VkhDevice)dev, dev->gQueue->familyIndex, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); + +#ifndef VKVG_ENABLE_VK_TIMELINE_SEMAPHORE ctx->flushFence = vkh_fence_create_signaled ((VkhDevice)ctx->dev); +#endif _create_vertices_buff (ctx); _create_gradient_buff (ctx); @@ -173,8 +183,9 @@ VkvgContext vkvg_create(VkvgSurface surf) vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_COMMAND_POOL, (uint64_t)ctx->cmdPool, "CTX Cmd Pool"); vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_COMMAND_BUFFER, (uint64_t)ctx->cmdBuffers[0], "CTX Cmd Buff A"); vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_COMMAND_BUFFER, (uint64_t)ctx->cmdBuffers[1], "CTX Cmd Buff B"); +#ifndef VKVG_ENABLE_VK_TIMELINE_SEMAPHORE vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_FENCE, (uint64_t)ctx->flushFence, "CTX Flush Fence"); - +#endif vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_DESCRIPTOR_POOL, (uint64_t)ctx->descriptorPool, "CTX Descriptor Pool"); vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_DESCRIPTOR_SET, (uint64_t)ctx->dsSrc, "CTX DescSet SOURCE"); vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_DESCRIPTOR_SET, (uint64_t)ctx->dsFont, "CTX DescSet FONT"); @@ -190,7 +201,7 @@ void vkvg_flush (VkvgContext ctx){ if (ctx->status) return; _flush_cmd_buff (ctx); - _wait_flush_fence (ctx); + _wait_ctx_flush_end (ctx); /* #ifdef DEBUG @@ -1272,7 +1283,7 @@ void vkvg_save (VkvgContext ctx){ vkvg_context_save_t* sav = (vkvg_context_save_t*)calloc(1,sizeof(vkvg_context_save_t)); _flush_cmd_buff (ctx); - if (!_wait_flush_fence (ctx)) { + if (!_wait_ctx_flush_end (ctx)) { free (sav); return; } @@ -1406,7 +1417,7 @@ void vkvg_restore (VkvgContext ctx){ ctx->pSavedCtxs = sav->pNext; _flush_cmd_buff (ctx); - if (!_wait_flush_fence (ctx)) + if (!_wait_ctx_flush_end (ctx)) return; ctx->pushConsts = sav->pushConsts; @@ -1441,7 +1452,7 @@ void vkvg_restore (VkvgContext ctx){ #endif _flush_cmd_buff (ctx); - if (!_wait_flush_fence (ctx)) + if (!_wait_ctx_flush_end (ctx)) return; } } @@ -1483,7 +1494,7 @@ void vkvg_restore (VkvgContext ctx){ VK_CHECK_RESULT(vkEndCommandBuffer(ctx->cmd)); _wait_and_submit_cmd (ctx); - if (!_wait_flush_fence (ctx)) + if (!_wait_ctx_flush_end (ctx)) return; vkh_image_destroy (savStencil); } diff --git a/src/vkvg_context_internal.c b/src/vkvg_context_internal.c index 951e254..bf8078c 100644 --- a/src/vkvg_context_internal.c +++ b/src/vkvg_context_internal.c @@ -270,7 +270,7 @@ void _create_vertices_buff (VkvgContext ctx){ ctx->sizeIBO * sizeof(VKVG_IBO_INDEX_TYPE), &ctx->indices); } void _resize_vbo (VkvgContext ctx, uint32_t new_size) { - if (!_wait_flush_fence (ctx))//wait previous cmd if not completed + if (!_wait_ctx_flush_end (ctx))//wait previous cmd if not completed return; LOG(VKVG_LOG_DBG_ARRAYS, "resize VBO: %d -> ", ctx->sizeVBO); ctx->sizeVBO = new_size; @@ -285,7 +285,7 @@ void _resize_vbo (VkvgContext ctx, uint32_t new_size) { ctx->sizeVBO * sizeof(Vertex), &ctx->vertices); } void _resize_ibo (VkvgContext ctx, size_t new_size) { - if (!_wait_flush_fence (ctx))//wait previous cmd if not completed + if (!_wait_ctx_flush_end (ctx))//wait previous cmd if not completed return; ctx->sizeIBO = new_size; uint32_t mod = ctx->sizeIBO % VKVG_IBO_SIZE; @@ -420,24 +420,64 @@ void _create_cmd_buff (VkvgContext ctx){ void _clear_attachment (VkvgContext ctx) { } -bool _wait_flush_fence (VkvgContext ctx) { + +bool _wait_ctx_flush_end (VkvgContext ctx) { LOG(VKVG_LOG_INFO, "CTX: _wait_flush_fence\n"); +#ifdef VKVG_ENABLE_VK_TIMELINE_SEMAPHORE + if (vkh_timeline_wait ((VkhDevice)ctx->dev, ctx->pSurf->timeline, ctx->timelineStep) == VK_SUCCESS) + return true; +#else if (WaitForFences (ctx->dev->vkDev, 1, &ctx->flushFence, VK_TRUE, VKVG_FENCE_TIMEOUT) == VK_SUCCESS) return true; +#endif LOG(VKVG_LOG_DEBUG, "CTX: _wait_flush_fence timeout\n"); ctx->status = VKVG_STATUS_TIMEOUT; return false; } + + bool _wait_and_submit_cmd (VkvgContext ctx){ if (!ctx->cmdStarted)//current cmd buff is empty, be aware that wait is also canceled!! return true; LOG(VKVG_LOG_INFO, "CTX: _wait_and_submit_cmd\n"); - if (!_wait_flush_fence (ctx)) +#ifdef VKVG_ENABLE_VK_TIMELINE_SEMAPHORE + VkvgSurface surf = ctx->pSurf; + VkvgDevice dev = surf->dev; + //vkh_timeline_wait ((VkhDevice)dev, surf->timeline, ct->timelineStep); + if (ctx->pattern && ctx->pattern->type == VKVG_PATTERN_TYPE_SURFACE) { + //add source surface timeline sync. + VkvgSurface source = (VkvgSurface)ctx->pattern->data; + LOCK_SURFACE(surf) + LOCK_SURFACE(source) + LOCK_DEVICE + vkh_cmd_submit_timelined2 (dev->gQueue, &ctx->cmd, + (VkSemaphore[2]){surf->timeline,source->timeline}, + (uint64_t[2]){surf->timelineStep,source->timelineStep}, + (uint64_t[2]){surf->timelineStep+1,source->timelineStep+1}); + surf->timelineStep++; + source->timelineStep++; + ctx->timelineStep = surf->timelineStep; + UNLOCK_DEVICE + UNLOCK_SURFACE(source) + UNLOCK_SURFACE(surf) + } else { + LOCK_SURFACE(surf) + LOCK_DEVICE + vkh_cmd_submit_timelined (dev->gQueue, &ctx->cmd, surf->timeline, surf->timelineStep, surf->timelineStep+1); + surf->timelineStep++; + ctx->timelineStep = surf->timelineStep; + UNLOCK_DEVICE + UNLOCK_SURFACE(surf) + } +#else + + if (!_wait_ctx_flush_end (ctx)) return false; - _device_reset_fence(ctx->dev, ctx->flushFence); + ResetFences (ctx->dev->vkDev, 1, &ctx->flushFence); _device_submit_cmd (ctx->dev, &ctx->cmd, ctx->flushFence); +#endif if (ctx->cmd == ctx->cmdBuffers[0]) ctx->cmd = ctx->cmdBuffers[1]; @@ -474,7 +514,7 @@ bool _wait_and_submit_cmd (VkvgContext ctx){ //pre flush vertices because of vbo or ibo too small, all vertices except last draw call are flushed //this function expects a vertex offset > 0 void _flush_vertices_caches_until_vertex_base (VkvgContext ctx) { - _wait_flush_fence (ctx); + _wait_ctx_flush_end (ctx); memcpy(ctx->vertices.allocInfo.pMappedData, ctx->vertexCache, ctx->curVertOffset * sizeof (Vertex)); memcpy(ctx->indices.allocInfo.pMappedData, ctx->indexCache, ctx->curIndStart * sizeof (VKVG_IBO_INDEX_TYPE)); @@ -491,7 +531,7 @@ void _flush_vertices_caches_until_vertex_base (VkvgContext ctx) { //copy vertex and index caches to the vbo and ibo vkbuffers used by gpu for drawing //current running cmd has to be completed to free usage of those void _flush_vertices_caches (VkvgContext ctx) { - if (!_wait_flush_fence (ctx)) + if (!_wait_ctx_flush_end (ctx)) return; memcpy(ctx->vertices.allocInfo.pMappedData, ctx->vertexCache, ctx->vertCount * sizeof (Vertex)); @@ -671,7 +711,7 @@ void _update_cur_pattern (VkvgContext ctx, VkvgPattern pat) { switch (newPatternType) { case VKVG_PATTERN_TYPE_SOLID: _flush_cmd_buff (ctx); - if (!_wait_flush_fence (ctx)) + if (!_wait_ctx_flush_end (ctx)) return; if (lastPat->type == VKVG_PATTERN_TYPE_SURFACE)//unbind current source surface by replacing it with empty texture _update_descriptor_set (ctx, ctx->dev->emptyImg, ctx->dsSrc); @@ -686,7 +726,7 @@ void _update_cur_pattern (VkvgContext ctx, VkvgPattern pat) { if (ctx->cmdStarted){//transition of img without appropriate dependencies in subpass must be done outside renderpass. _end_render_pass (ctx); _flush_vertices_caches (ctx); - }else { + } else { vkh_cmd_begin (ctx->cmd,VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); ctx->cmdStarted = true; } @@ -698,11 +738,9 @@ void _update_cur_pattern (VkvgContext ctx, VkvgPattern pat) { vkh_cmd_end (ctx->cmd); _wait_and_submit_cmd (ctx); - if (!_wait_flush_fence (ctx)) + if (!_wait_ctx_flush_end (ctx)) return; - ctx->source = surf->img; - VkSamplerAddressMode addrMode = 0; VkFilter filter = VK_FILTER_NEAREST; switch (pat->extend) { @@ -728,10 +766,10 @@ void _update_cur_pattern (VkvgContext ctx, VkvgPattern pat) { filter = VK_FILTER_NEAREST; break; } - vkh_image_create_sampler (ctx->source, filter, filter, + vkh_image_create_sampler (surf->img, filter, filter, VK_SAMPLER_MIPMAP_MODE_NEAREST, addrMode); - _update_descriptor_set (ctx, ctx->source, ctx->dsSrc); + _update_descriptor_set (ctx, surf->img, ctx->dsSrc); if (pat->hasMatrix) { @@ -744,7 +782,7 @@ void _update_cur_pattern (VkvgContext ctx, VkvgPattern pat) { case VKVG_PATTERN_TYPE_LINEAR: case VKVG_PATTERN_TYPE_RADIAL: _flush_cmd_buff (ctx); - if (!_wait_flush_fence (ctx)) + if (!_wait_ctx_flush_end (ctx)) return; if (lastPat && lastPat->type == VKVG_PATTERN_TYPE_SURFACE) @@ -866,7 +904,10 @@ void _init_descriptor_sets (VkvgContext ctx){ void _release_context_ressources (VkvgContext ctx) { VkDevice dev = ctx->dev->vkDev; - _device_destroy_fence (ctx->dev, ctx->flushFence); +#ifndef VKVG_ENABLE_VK_TIMELINE_SEMAPHORE + vkDestroyFence (dev, ctx->flushFence, NULL); +#endif + vkFreeCommandBuffers(dev, ctx->cmdPool, 2, ctx->cmdBuffers); vkDestroyCommandPool(dev, ctx->cmdPool, NULL); @@ -1609,7 +1650,7 @@ void _poly_fill (VkvgContext ctx, vec4* bounds){ _flush_vertices_caches (ctx); vkh_cmd_end (ctx->cmd); _wait_and_submit_cmd (ctx); - _wait_flush_fence (ctx); + _wait_ctx_flush_end (ctx); if (ctx->sizeVBO - VKVG_ARRAY_THRESHOLD < ctx->pointCount){ _resize_vbo (ctx, ctx->pointCount + VKVG_ARRAY_THRESHOLD); _resize_vertex_cache (ctx, ctx->sizeVBO); diff --git a/src/vkvg_context_internal.h b/src/vkvg_context_internal.h index bd241ec..cff7889 100644 --- a/src/vkvg_context_internal.h +++ b/src/vkvg_context_internal.h @@ -131,8 +131,12 @@ typedef struct _vkvg_context_t { VkvgDevice dev; VkvgSurface pSurf; //surface bound to context, set on creation of ctx +#ifdef VKVG_ENABLE_VK_TIMELINE_SEMAPHORE + uint64_t timelineStep; //context cmd last submission timeline id. +#else VkFence flushFence; //context fence - VkhImage source; //source of painting operation +#endif + //VkDescriptorImageInfo sourceDescriptor; //Store view/sampler in context VkCommandPool cmdPool; //local pools ensure thread safety VkCommandBuffer cmdBuffers[2]; //double cmd buff for context operations @@ -302,7 +306,7 @@ void _flush_cmd_buff (VkvgContext ctx); void _ensure_renderpass_is_started (VkvgContext ctx); void _emit_draw_cmd_undrawn_vertices (VkvgContext ctx); void _flush_cmd_until_vx_base (VkvgContext ctx); -bool _wait_flush_fence (VkvgContext ctx); +bool _wait_ctx_flush_end (VkvgContext ctx); bool _wait_and_submit_cmd (VkvgContext ctx); void _update_push_constants (VkvgContext ctx); void _update_cur_pattern (VkvgContext ctx, VkvgPattern pat); diff --git a/src/vkvg_device.c b/src/vkvg_device.c index 11ff1af..adb8abe 100644 --- a/src/vkvg_device.c +++ b/src/vkvg_device.c @@ -31,6 +31,22 @@ if (vkh_phyinfo_try_get_extension_properties(pi, #ext, NULL)) \ enabledExts[enabledExtsCount++] = #ext; \ } +void vkvg_device_set_context_cache_size (VkvgDevice dev, uint32_t maxCount) { + if (maxCount == dev->cachedContextMaxCount) + return; + + dev->cachedContextMaxCount = maxCount; + + _cached_ctx* cur = dev->cachedContextLast; + while (cur && dev->cachedContextCount > dev->cachedContextMaxCount) { + _release_context_ressources (cur->ctx); + _cached_ctx* prev = cur; + cur = cur->pNext; + free (prev); + dev->cachedContextCount--; + } + dev->cachedContextLast = cur; +} void _device_init (VkvgDevice dev, VkInstance inst, VkPhysicalDevice phy, VkDevice vkdev, uint32_t qFamIdx, uint32_t qIndex, VkSampleCountFlags samples, bool deferredResolve) { dev->instance = inst; dev->hdpi = 72; @@ -43,6 +59,8 @@ void _device_init (VkvgDevice dev, VkInstance inst, VkPhysicalDevice phy, VkDevi dev->vkDev = vkdev; dev->phy = phy; + dev->cachedContextMaxCount = VKVG_MAX_CACHED_CONTEXT_COUNT; + #if VKVG_DBG_STATS dev->debug_stats = (vkvg_debug_stats_t) {0}; #endif @@ -171,20 +189,36 @@ vkvg_status_t vkvg_get_required_device_extensions (VkPhysicalDevice phy, const c //https://vulkan.lunarg.com/doc/view/1.2.162.0/mac/1.2-extensions/vkspec.html#VK_KHR_portability_subset _CHECK_DEV_EXT(VK_KHR_portability_subset); + VkPhysicalDeviceFeatures2 phyFeat2 = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; -#ifdef VKVG_VK_SCALAR_BLOCK_SUPPORTED +#ifdef VKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT //ensure feature is implemented by driver. - VkPhysicalDeviceFeatures2 phyFeat2 = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; VkPhysicalDeviceScalarBlockLayoutFeatures scalarBlockLayoutSupport = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES}; phyFeat2.pNext = &scalarBlockLayoutSupport; +#endif + +#ifdef VKVG_ENABLE_VK_TIMELINE_SEMAPHORE + VkPhysicalDeviceTimelineSemaphoreFeatures timelineSemaphoreSupport = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES}; + timelineSemaphoreSupport.pNext = phyFeat2.pNext; + phyFeat2.pNext = &timelineSemaphoreSupport; +#endif + vkGetPhysicalDeviceFeatures2(phy, &phyFeat2); +#ifdef VKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT if (!scalarBlockLayoutSupport.scalarBlockLayout) { - LOG(VKVG_LOG_ERR, "CREATE Device failed, vkvg compiled with VKVG_VK_SCALAR_BLOCK_SUPPORTED and feature is not implemented for physical device.\n"); + LOG(VKVG_LOG_ERR, "CREATE Device failed, vkvg compiled with VKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT and feature is not implemented for physical device.\n"); return VKVG_STATUS_DEVICE_ERROR; } _CHECK_DEV_EXT(VK_EXT_scalar_block_layout) #endif +#ifdef VKVG_ENABLE_VK_TIMELINE_SEMAPHORE + if (!timelineSemaphoreSupport.timelineSemaphore) { + LOG(VKVG_LOG_ERR, "CREATE Device failed, VK_SEMAPHORE_TYPE_TIMELINE not supported.\n"); + return VKVG_STATUS_DEVICE_ERROR; + } + _CHECK_DEV_EXT(VK_KHR_timeline_semaphore) +#endif return VKVG_STATUS_SUCCESS; } @@ -198,32 +232,42 @@ const void* vkvg_get_device_requirements (VkPhysicalDeviceFeatures* pEnabledFeat void* pNext = NULL; -#ifdef VK_VERSION_1_2 +#ifdef VK_VERSION_1_2_ static VkPhysicalDeviceVulkan12Features enabledFeatures12 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES -#ifdef VKVG_VK_SCALAR_BLOCK_SUPPORTED +#ifdef VKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT ,.scalarBlockLayout = VK_TRUE #endif +#ifdef VKVG_ENABLE_VK_TIMELINE_SEMAPHORE + ,.timelineSemaphore = VK_TRUE +#endif }; enabledFeatures12.pNext = pNext; pNext = &enabledFeatures12; #else +#ifdef VKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT static VkPhysicalDeviceScalarBlockLayoutFeaturesEXT scalarBlockFeat = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT -#ifdef VKVG_VK_SCALAR_BLOCK_SUPPORTED ,.scalarBlockLayout = VK_TRUE -#endif }; scalarBlockFeat.pNext = pNext; pNext = &scalarBlockFeat; - +#endif +#ifdef VKVG_ENABLE_VK_TIMELINE_SEMAPHORE + static VkPhysicalDeviceTimelineSemaphoreFeaturesKHR timelineSemaFeat = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR + ,.timelineSemaphore = VK_TRUE + }; + timelineSemaFeat.pNext = pNext; + pNext = &timelineSemaFeat; +#endif #endif return pNext; } -VkvgDevice vkvg_device_create(VkSampleCountFlags samples, bool deferredResolve) { +VkvgDevice vkvg_device_create (VkSampleCountFlags samples, bool deferredResolve) { LOG(VKVG_LOG_INFO, "CREATE Device\n"); VkvgDevice dev = (vkvg_device*)calloc(1,sizeof(vkvg_device)); if (!dev) { @@ -354,11 +398,22 @@ void vkvg_device_destroy (VkvgDevice dev) } UNLOCK_DEVICE - while (dev->cachedContextCount > 0) - _release_context_ressources (dev->cachedContext[--dev->cachedContextCount]); + + if (dev->cachedContextCount > 0) { + _cached_ctx* cur = dev->cachedContextLast; + while (cur) { + _release_context_ressources (cur->ctx); + _cached_ctx* prev = cur; + cur = cur->pNext; + free (prev); + } + } + LOG(VKVG_LOG_INFO, "DESTROY Device\n"); + vkDeviceWaitIdle (dev->vkDev); + vkh_image_destroy (dev->emptyImg); vkDestroyDescriptorSetLayout (dev->vkDev, dev->dslGrad,NULL); @@ -387,7 +442,7 @@ void vkvg_device_destroy (VkvgDevice dev) vkWaitForFences (dev->vkDev, 1, &dev->fence, VK_TRUE, UINT64_MAX); vkDestroyFence (dev->vkDev, dev->fence,NULL); - //vkFreeCommandBuffers (dev->vkDev, dev->cmdPool, 1, &dev->cmd); + vkFreeCommandBuffers (dev->vkDev, dev->cmdPool, 1, &dev->cmd); vkDestroyCommandPool (dev->vkDev, dev->cmdPool, NULL); vkh_queue_destroy(dev->gQueue); diff --git a/src/vkvg_device_internal.c b/src/vkvg_device_internal.c index 86e528b..ab37f2d 100644 --- a/src/vkvg_device_internal.c +++ b/src/vkvg_device_internal.c @@ -415,59 +415,62 @@ void _device_wait_idle (VkvgDevice dev) { } void _device_wait_and_reset_device_fence (VkvgDevice dev) { vkWaitForFences (dev->vkDev, 1, &dev->fence, VK_TRUE, UINT64_MAX); - _device_reset_fence(dev, dev->fence); + ResetFences (dev->vkDev, 1, &dev->fence); } -void _device_destroy_fence (VkvgDevice dev, VkFence fence) { - LOCK_DEVICE - - if (dev->gQLastFence == fence) - dev->gQLastFence = VK_NULL_HANDLE; - - vkDestroyFence (dev->vkDev, fence, NULL); - - UNLOCK_DEVICE -} -void _device_reset_fence (VkvgDevice dev, VkFence fence){ +bool _device_try_get_cached_context (VkvgDevice dev, VkvgContext* pCtx) { LOCK_DEVICE - if (dev->gQLastFence == fence) - dev->gQLastFence = VK_NULL_HANDLE; - - ResetFences (dev->vkDev, 1, &fence); + if (dev->cachedContextCount) { + thrd_t curThread = thrd_current (); + _cached_ctx* prev = NULL; + _cached_ctx* cur = dev->cachedContextLast; + while (cur) { + if (thrd_equal (cur->thread, curThread)) { + if (prev) + prev->pNext = cur->pNext; + else + dev->cachedContextLast = cur->pNext; - UNLOCK_DEVICE -} -bool _device_try_get_cached_context (VkvgDevice dev, VkvgContext* pCtx) { - LOCK_DEVICE + dev->cachedContextCount--; - if (dev->cachedContextCount) - *pCtx = dev->cachedContext[--dev->cachedContextCount]; - else - *pCtx = NULL; + LOG(VKVG_LOG_THREAD,"get cached context: %p, thd:%lu cached ctx: %d\n", cur->ctx, cur->thread, dev->cachedContextCount); + *pCtx = cur->ctx; + free (cur); + UNLOCK_DEVICE + return true; + } + prev = cur; + cur = cur->pNext; + } + } + *pCtx = NULL; UNLOCK_DEVICE - - return *pCtx != NULL; + return false; } void _device_store_context (VkvgContext ctx) { VkvgDevice dev = ctx->dev; LOCK_DEVICE - if (dev->gQLastFence == ctx->flushFence) - dev->gQLastFence = VK_NULL_HANDLE; - dev->cachedContext[dev->cachedContextCount++] = ctx; + _cached_ctx* cur = (_cached_ctx*)calloc(1, sizeof(_cached_ctx)); + cur->ctx = ctx; + cur->thread = thrd_current (); + cur->pNext = dev->cachedContextLast; + + dev->cachedContextLast = cur; + dev->cachedContextCount++; + + LOG(VKVG_LOG_THREAD,"store context: %p, thd:%lu cached ctx: %d\n", cur->ctx, cur->thread, dev->cachedContextCount); + ctx->references++; UNLOCK_DEVICE } void _device_submit_cmd (VkvgDevice dev, VkCommandBuffer* cmd, VkFence fence) { LOCK_DEVICE - if (dev->gQLastFence != VK_NULL_HANDLE) - WaitForFences (dev->vkDev, 1, &dev->gQLastFence, VK_TRUE, VKVG_FENCE_TIMEOUT); vkh_cmd_submit (dev->gQueue, cmd, fence); - dev->gQLastFence = fence; UNLOCK_DEVICE } diff --git a/src/vkvg_device_internal.h b/src/vkvg_device_internal.h index dc44fd3..5503d17 100644 --- a/src/vkvg_device_internal.h +++ b/src/vkvg_device_internal.h @@ -30,7 +30,7 @@ #define STENCIL_CLIP_BIT 0x2 #define STENCIL_ALL_BIT 0x3 -#define VKVG_MAX_CACHED_CONTEXT_COUNT 2 +#define VKVG_MAX_CACHED_CONTEXT_COUNT 36 extern PFN_vkCmdBindPipeline CmdBindPipeline; extern PFN_vkCmdBindDescriptorSets CmdBindDescriptorSets; @@ -53,7 +53,13 @@ extern PFN_vkWaitForFences WaitForFences; extern PFN_vkResetFences ResetFences; extern PFN_vkResetCommandBuffer ResetCommandBuffer; -typedef struct _vkvg_device_t{ +typedef struct _cached_ctx{ + thrd_t thread; + VkvgContext ctx; + struct _cached_ctx* pNext; +} _cached_ctx; + +typedef struct _vkvg_device_t { VkDevice vkDev; /**< Vulkan Logical Device */ VkPhysicalDeviceMemoryProperties phyMemProps; /**< Vulkan Physical device memory properties */ VkPhysicalDevice phy; /**< Vulkan Physical device */ @@ -68,7 +74,6 @@ typedef struct _vkvg_device_t{ mtx_t mutex; /**< protect device access (queue, cahes, ...)from ctxs in separate threads */ bool threadAware; /**< if true, mutex is created and guard device queue and caches access */ VkhQueue gQueue; /**< Vulkan Queue with Graphic flag */ - VkFence gQLastFence; VkRenderPass renderPass; /**< Vulkan render pass, common for all surfaces */ VkRenderPass renderPass_ClearStencil;/**< Vulkan render pass for first draw with context, stencil has to be cleared */ @@ -106,8 +111,9 @@ typedef struct _vkvg_device_t{ VkvgContext lastCtx; /**< last element of double linked list of context, used to trigger font caching system update on all contexts*/ - int32_t cachedContextCount; - VkvgContext cachedContext[VKVG_MAX_CACHED_CONTEXT_COUNT]; + int32_t cachedContextMaxCount; /**< Maximum context cache element count.*/ + int32_t cachedContextCount; /**< Current context cache element count.*/ + _cached_ctx* cachedContextLast; /**< Last element of single linked list of saved context for fast reuse.*/ #ifdef VKVG_WIRED_DEBUG VkPipeline pipelineWired; @@ -139,8 +145,6 @@ void _device_wait_idle (VkvgDevice dev); void _device_wait_and_reset_device_fence(VkvgDevice dev); void _device_submit_cmd (VkvgDevice dev, VkCommandBuffer* cmd, VkFence fence); -void _device_destroy_fence (VkvgDevice dev, VkFence fence); -void _device_reset_fence (VkvgDevice dev, VkFence fence); bool _device_try_get_cached_context (VkvgDevice dev, VkvgContext* pCtx); void _device_store_context (VkvgContext ctx); #endif diff --git a/src/vkvg_fonts.c b/src/vkvg_fonts.c index efb2c9a..830d9f3 100644 --- a/src/vkvg_fonts.c +++ b/src/vkvg_fonts.c @@ -105,7 +105,7 @@ void _increase_font_tex_array (VkvgDevice dev){ _font_cache_t* cache = dev->fontCache; vkWaitForFences (dev->vkDev, 1, &cache->uploadFence, VK_TRUE, UINT64_MAX); - _device_reset_fence (dev, cache->uploadFence); + ResetFences (dev->vkDev, 1, &cache->uploadFence); vkResetCommandBuffer(cache->cmd, 0); @@ -167,7 +167,8 @@ void _flush_chars_to_tex (VkvgDevice dev, _vkvg_font_t* f) { LOG(VKVG_LOG_INFO, "_flush_chars_to_tex pen(%d, %d)\n",f->curLine.penX, f->curLine.penY); vkWaitForFences (dev->vkDev,1,&cache->uploadFence,VK_TRUE,UINT64_MAX); - _device_reset_fence (dev, cache->uploadFence); + ResetFences (dev->vkDev, 1, &cache->uploadFence); + vkResetCommandBuffer(cache->cmd,0); memcpy(cache->buff.allocInfo.pMappedData, cache->hostBuff, (uint64_t)f->curLine.height * FONT_PAGE_SIZE * cache->texPixelSize); diff --git a/src/vkvg_pattern.c b/src/vkvg_pattern.c index ffc635e..acefab6 100644 --- a/src/vkvg_pattern.c +++ b/src/vkvg_pattern.c @@ -150,7 +150,7 @@ vkvg_status_t vkvg_pattern_add_color_stop (VkvgPattern pat, float offset, float vkvg_color_t c = {r,g,b,a}; #endif grad->colors[grad->count] = c; -#ifdef VKVG_VK_SCALAR_BLOCK_SUPPORTED +#ifdef VKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT grad->stops[grad->count] = offset; #else grad->stops[grad->count].r = offset; @@ -201,7 +201,7 @@ vkvg_status_t vkvg_pattern_get_color_stop_rgba (VkvgPattern pat, uint32_t index, vkvg_gradient_t* grad = (vkvg_gradient_t*)pat->data; if (index >= grad->count) return VKVG_STATUS_INVALID_INDEX; -#ifdef VKVG_VK_SCALAR_BLOCK_SUPPORTED +#ifdef VKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT *offset = grad->stops[index]; #else *offset = grad->stops[index].r; diff --git a/src/vkvg_pattern.h b/src/vkvg_pattern.h index e860012..2ad068d 100644 --- a/src/vkvg_pattern.h +++ b/src/vkvg_pattern.h @@ -37,7 +37,7 @@ typedef struct _vkvg_pattern_t { typedef struct _vkvg_gradient_t { vkvg_color_t colors[16]; -#ifdef VKVG_VK_SCALAR_BLOCK_SUPPORTED +#ifdef VKVG_ENABLE_VK_SCALAR_BLOCK_LAYOUT float stops[16]; #else vec4 stops[16]; diff --git a/src/vkvg_surface.c b/src/vkvg_surface.c index 05dc8dd..701e9c4 100644 --- a/src/vkvg_surface.c +++ b/src/vkvg_surface.c @@ -30,6 +30,31 @@ #include "vkh_image.h" #define max(x,y) +void _transition_surf_images (VkvgSurface surf) { + LOCK_SURFACE(surf) + VkvgDevice dev = surf->dev; + + //_surface_wait_cmd (surf); + + vkh_cmd_begin (surf->cmd,VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + VkhImage imgMs = surf->imgMS; + if (imgMs != NULL) + vkh_image_set_layout(surf->cmd, imgMs, VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + + vkh_image_set_layout(surf->cmd, surf->img, VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + vkh_image_set_layout (surf->cmd, surf->stencil, VK_IMAGE_ASPECT_STENCIL_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT); + vkh_cmd_end (surf->cmd); + + _surface_submit_cmd (surf); + + UNLOCK_SURFACE(surf) +} void vkvg_surface_clear (VkvgSurface surf) { if (surf->status) return; @@ -42,10 +67,12 @@ VkvgSurface vkvg_surface_create (VkvgDevice dev, uint32_t width, uint32_t height surf->width = MAX(1, width); surf->height = MAX(1, height); - surf->new = true;//used to clear all attacments on first render pass + surf->newSurf = true;//used to clear all attacments on first render pass _create_surface_images (surf); + _transition_surf_images (surf); + surf->status = VKVG_STATUS_SUCCESS; vkvg_device_reference (surf->dev); return surf; @@ -71,7 +98,9 @@ VkvgSurface vkvg_surface_create_for_VkhImage (VkvgDevice dev, void* vkhImg) { _create_surface_secondary_images (surf); _create_framebuffer (surf); - _clear_surface (surf, VK_IMAGE_ASPECT_STENCIL_BIT); + + _transition_surf_images (surf); + //_clear_surface (surf, VK_IMAGE_ASPECT_STENCIL_BIT); surf->status = VKVG_STATUS_SUCCESS; vkvg_device_reference (surf->dev); @@ -110,9 +139,7 @@ VkvgSurface vkvg_surface_create_from_bitmap (VkvgDevice dev, unsigned char* img, memcpy (buff.allocInfo.pMappedData, img, imgSize); - VkCommandBuffer cmd = dev->cmd; - - _device_wait_and_reset_device_fence (dev); + VkCommandBuffer cmd = surf->cmd; vkh_cmd_begin (cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); vkh_image_set_layout (cmd, stagImg, VK_IMAGE_ASPECT_COLOR_BIT, @@ -148,15 +175,13 @@ VkvgSurface vkvg_surface_create_from_bitmap (VkvgDevice dev, unsigned char* img, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); vkh_cmd_end (cmd); - _device_submit_cmd (dev, &cmd, dev->fence); - //don't reset fence after completion as this is the last cmd. (signaled idle fence) - vkWaitForFences (dev->vkDev, 1, &dev->fence, VK_TRUE, UINT64_MAX); + _surface_submit_cmd (surf);//lock surface? vkvg_buffer_destroy (&buff); vkh_image_destroy (stagImg); - surf->new = false; + surf->newSurf = false; //create tmp context with rendering pipeline to create the multisample img VkvgContext ctx = vkvg_create (surf); @@ -212,6 +237,7 @@ void vkvg_surface_destroy(VkvgSurface surf) } UNLOCK_SURFACE(surf) + vkDestroyCommandPool(surf->dev->vkDev, surf->cmdPool, NULL); vkDestroyFramebuffer(surf->dev->vkDev, surf->fb, NULL); if (!surf->img->imported) @@ -223,6 +249,12 @@ void vkvg_surface_destroy(VkvgSurface surf) if (surf->dev->threadAware) mtx_destroy (&surf->mutex); +#if VKVG_ENABLE_VK_TIMELINE_SEMAPHORE + vkDestroySemaphore (surf->dev->vkDev, surf->timeline, NULL); +#else + vkDestroyFence (surf->dev->vkDev, surf->flushFence, NULL); +#endif + vkvg_device_destroy (surf->dev); free(surf); } @@ -304,9 +336,7 @@ vkvg_status_t vkvg_surface_write_to_png (VkvgSurface surf, const char* path){ VMA_MEMORY_USAGE_GPU_ONLY, VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT); - VkCommandBuffer cmd = dev->cmd; - _device_wait_and_reset_device_fence (dev); - + VkCommandBuffer cmd = surf->cmd; vkh_cmd_begin (cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); vkh_image_set_layout (cmd, stagImg, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, @@ -326,7 +356,8 @@ vkvg_status_t vkvg_surface_write_to_png (VkvgSurface surf, const char* path){ vkh_image_get_vkimage (stagImg), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_NEAREST); vkh_cmd_end (cmd); - _device_submit_cmd (dev, &cmd, dev->fence); + + _surface_submit_cmd (surf); VkhImage stagImgLinear = stagImg; @@ -341,7 +372,6 @@ vkvg_status_t vkvg_surface_write_to_png (VkvgSurface surf, const char* path){ .dstOffset = {0}, .extent = {(int32_t)surf->width, (int32_t)surf->height, 1} }; - _device_wait_and_reset_device_fence (dev); vkh_cmd_begin (cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); vkh_image_set_layout (cmd, stagImgLinear, VK_IMAGE_ASPECT_COLOR_BIT, @@ -355,12 +385,11 @@ vkvg_status_t vkvg_surface_write_to_png (VkvgSurface surf, const char* path){ vkh_image_get_vkimage (stagImgLinear), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &cpy); vkh_cmd_end (cmd); - _device_submit_cmd (dev, &cmd, dev->fence); - vkWaitForFences (dev->vkDev, 1, &dev->fence, VK_TRUE, UINT64_MAX); + _surface_submit_cmd (surf); + vkh_image_destroy (stagImg); - } else - vkWaitForFences (dev->vkDev, 1, &dev->fence, VK_TRUE, UINT64_MAX); + } void* img = vkh_image_map (stagImgLinear); @@ -395,8 +424,7 @@ vkvg_status_t vkvg_surface_write_to_memory (VkvgSurface surf, unsigned char* con VMA_MEMORY_USAGE_GPU_TO_CPU, VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT); - VkCommandBuffer cmd = dev->cmd; - _device_wait_and_reset_device_fence (dev); + VkCommandBuffer cmd = surf->cmd; vkh_cmd_begin (cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); vkh_image_set_layout (cmd, stagImg, VK_IMAGE_ASPECT_COLOR_BIT, @@ -417,8 +445,8 @@ vkvg_status_t vkvg_surface_write_to_memory (VkvgSurface surf, unsigned char* con vkh_image_get_vkimage (stagImg), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_NEAREST); vkh_cmd_end (cmd); - _device_submit_cmd (dev, &cmd, dev->fence); - vkWaitForFences (dev->vkDev, 1, &dev->fence, VK_TRUE, UINT64_MAX); + + _surface_submit_cmd (surf); uint64_t stride = vkh_image_get_stride(stagImg); uint32_t dest_stride = surf->width * 4; diff --git a/src/vkvg_surface_internal.c b/src/vkvg_surface_internal.c index ffa5983..263489b 100644 --- a/src/vkvg_surface_internal.c +++ b/src/vkvg_surface_internal.c @@ -20,15 +20,15 @@ * THE SOFTWARE. */ -#include "vkvg_surface_internal.h" #include "vkvg_device_internal.h" +#include "vkvg_surface_internal.h" #include "vkh_image.h" +#include "vkh_queue.h" void _explicit_ms_resolve (VkvgSurface surf){ - VkvgDevice dev = surf->dev; - VkCommandBuffer cmd = dev->cmd; + LOCK_SURFACE (surf) - _device_wait_and_reset_device_fence (dev); + VkCommandBuffer cmd = surf->cmd; vkh_cmd_begin (cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); vkh_image_set_layout (cmd, surf->imgMS, VK_IMAGE_ASPECT_COLOR_BIT, @@ -53,15 +53,16 @@ void _explicit_ms_resolve (VkvgSurface surf){ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); vkh_cmd_end (cmd); - _device_submit_cmd (dev, &cmd, dev->fence); + _surface_submit_cmd (surf); + + UNLOCK_SURFACE (surf) } void _clear_surface (VkvgSurface surf, VkImageAspectFlags aspect) { - VkvgDevice dev = surf->dev; - VkCommandBuffer cmd = dev->cmd; + LOCK_SURFACE (surf) - _device_wait_and_reset_device_fence (dev); + VkCommandBuffer cmd = surf->cmd; vkh_cmd_begin (cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); @@ -101,7 +102,9 @@ void _clear_surface (VkvgSurface surf, VkImageAspectFlags aspect) } vkh_cmd_end (cmd); - _device_submit_cmd (dev, &cmd, dev->fence); + _surface_submit_cmd (surf); + + UNLOCK_SURFACE (surf) } void _create_surface_main_image (VkvgSurface surf){ @@ -186,5 +189,51 @@ VkvgSurface _create_surface (VkvgDevice dev, VkFormat format) { surf->format = format; if (dev->threadAware) mtx_init (&surf->mutex, mtx_plain); + surf->cmdPool = vkh_cmd_pool_create ((VkhDevice)dev, dev->gQueue->familyIndex, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); + vkh_cmd_buffs_create((VkhDevice)dev, surf->cmdPool,VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &surf->cmd); + +#if VKVG_ENABLE_VK_TIMELINE_SEMAPHORE + surf->timeline = vkh_timeline_create ((VkhDevice)dev, 0); +#else + surf->flushFence = vkh_fence_create ((VkhDevice)dev); +#endif + +#if defined(DEBUG) && defined(VKVG_DBG_UTILS) + vkh_device_set_object_name((VkhDevice)dev, VK_OBJECT_TYPE_COMMAND_BUFFER, (uint64_t)surf->cmd, "vkvgSurfCmd"); +#endif + return surf; } +//if fence sync, surf mutex must be locked. +/*bool _surface_wait_cmd (VkvgSurface surf) { + LOG(VKVG_LOG_INFO, "SURF: _surface__wait_flush_fence\n"); +#ifdef VKVG_ENABLE_VK_TIMELINE_SEMAPHORE + if (vkh_timeline_wait ((VkhDevice)surf->dev, surf->timeline, surf->timelineStep) == VK_SUCCESS) + return true; +#else + if (WaitForFences (surf->dev->vkDev, 1, &surf->flushFence, VK_TRUE, VKVG_FENCE_TIMEOUT) == VK_SUCCESS) { + ResetFences (surf->dev->vkDev, 1, &surf->flushFence); + return true; + } +#endif + LOG(VKVG_LOG_DEBUG, "CTX: _wait_flush_fence timeout\n"); + surf->status = VKVG_STATUS_TIMEOUT; + return false; +}*/ +//surface mutex must be locked to call this method, locking to guard also the surf->cmd local buffer usage. +void _surface_submit_cmd (VkvgSurface surf) { + VkvgDevice dev = surf->dev; +#ifdef VKVG_ENABLE_VK_TIMELINE_SEMAPHORE + LOCK_DEVICE + vkh_cmd_submit_timelined (dev->gQueue, &surf->cmd, surf->timeline, surf->timelineStep, surf->timelineStep+1); + surf->timelineStep++; + UNLOCK_DEVICE + vkh_timeline_wait ((VkhDevice)dev, surf->timeline, surf->timelineStep); +#else + LOCK_DEVICE + vkh_cmd_submit (surf->dev->gQueue, &surf->cmd, surf->flushFence); + UNLOCK_DEVICE + WaitForFences (surf->dev->vkDev, 1, &surf->flushFence, VK_TRUE, VKVG_FENCE_TIMEOUT); + ResetFences (surf->dev->vkDev, 1, &surf->flushFence); +#endif +} diff --git a/src/vkvg_surface_internal.h b/src/vkvg_surface_internal.h index 79b64e3..5cc47c6 100644 --- a/src/vkvg_surface_internal.h +++ b/src/vkvg_surface_internal.h @@ -37,8 +37,17 @@ typedef struct _vkvg_surface_t { VkhImage img; VkhImage imgMS; VkhImage stencil; - bool new; + VkCommandPool cmdPool; //local pools ensure thread safety + VkCommandBuffer cmd; //surface local command buffer. + bool newSurf; mtx_t mutex; +#ifdef VKVG_ENABLE_VK_TIMELINE_SEMAPHORE + VkSemaphore timeline; /**< Timeline semaphore */ + uint64_t timelineStep; +#else + VkFence flushFence; //unsignaled idle. +#endif + }vkvg_surface; #define LOCK_SURFACE(surf) \ @@ -55,4 +64,7 @@ void _create_surface_secondary_images (VkvgSurface surf); void _create_framebuffer (VkvgSurface surf); void _create_surface_images (VkvgSurface surf); VkvgSurface _create_surface (VkvgDevice dev, VkFormat format); + +void _surface_submit_cmd (VkvgSurface surf); +//bool _surface_wait_cmd (VkvgSurface surf); #endif diff --git a/tests/multithreading/multithreaded.c b/tests/multithreading/multithreaded.c index 4981860..05ee038 100644 --- a/tests/multithreading/multithreaded.c +++ b/tests/multithreading/multithreaded.c @@ -5,7 +5,7 @@ #include "test.h" #include "tinycthread.h" -#define THREAD_COUNT 64 +#define THREAD_COUNT 32 static int finishedThreadCount = 0; diff --git a/vkh b/vkh -Subproject 2a194890dfed6eb82d3a92fd56ecc46f47735d2 +Subproject d966ae76eeb179e41ba0aeb947a32aaabc3bafc |