diff options
Diffstat (limited to 'video/out/vulkan/context.c')
-rw-r--r-- | video/out/vulkan/context.c | 482 |
1 files changed, 68 insertions, 414 deletions
diff --git a/video/out/vulkan/context.c b/video/out/vulkan/context.c index 29a2c9b727..c05a5ac209 100644 --- a/video/out/vulkan/context.c +++ b/video/out/vulkan/context.c @@ -16,25 +16,17 @@ */ #include "options/m_config.h" -#include "video/out/gpu/spirv.h" +#include "video/out/placebo/ra_pl.h" #include "context.h" -#include "ra_vk.h" #include "utils.h" -enum { - SWAP_AUTO = 0, - SWAP_FIFO, - SWAP_FIFO_RELAXED, - SWAP_MAILBOX, - SWAP_IMMEDIATE, - SWAP_COUNT, -}; - struct vulkan_opts { - struct mpvk_device_opts dev_opts; // logical device options char *device; // force a specific GPU int swap_mode; + int queue_count; + int async_transfer; + int async_compute; }; static int vk_validate_dev(struct mp_log *log, const struct m_option *opt, @@ -52,7 +44,7 @@ static int vk_validate_dev(struct mp_log *log, const struct m_option *opt, VkPhysicalDevice *devices = NULL; uint32_t num = 0; - res = vkCreateInstance(&info, MPVK_ALLOCATOR, &inst); + res = vkCreateInstance(&info, NULL, &inst); if (res != VK_SUCCESS) goto done; @@ -97,45 +89,30 @@ const struct m_sub_options vulkan_conf = { .opts = (const struct m_option[]) { OPT_STRING_VALIDATE("vulkan-device", device, 0, vk_validate_dev), OPT_CHOICE("vulkan-swap-mode", swap_mode, 0, - ({"auto", SWAP_AUTO}, - {"fifo", SWAP_FIFO}, - {"fifo-relaxed", SWAP_FIFO_RELAXED}, - {"mailbox", SWAP_MAILBOX}, - {"immediate", SWAP_IMMEDIATE})), - OPT_INTRANGE("vulkan-queue-count", dev_opts.queue_count, 0, 1, 8, - OPTDEF_INT(1)), - OPT_FLAG("vulkan-async-transfer", dev_opts.async_transfer, 0), - OPT_FLAG("vulkan-async-compute", dev_opts.async_compute, 0), + ({"auto", -1}, + {"fifo", VK_PRESENT_MODE_FIFO_KHR}, + {"fifo-relaxed", VK_PRESENT_MODE_FIFO_RELAXED_KHR}, + {"mailbox", VK_PRESENT_MODE_MAILBOX_KHR}, + {"immediate", VK_PRESENT_MODE_IMMEDIATE_KHR})), + OPT_INTRANGE("vulkan-queue-count", queue_count, 0, 1, 8), + OPT_FLAG("vulkan-async-transfer", async_transfer, 0), + OPT_FLAG("vulkan-async-compute", async_compute, 0), {0} }, .size = sizeof(struct vulkan_opts), .defaults = &(struct vulkan_opts) { - .dev_opts = { - .async_transfer = 1, - }, + .swap_mode = -1, + .queue_count = 1, + .async_transfer = true, + .async_compute = true, }, }; struct priv { struct mpvk_ctx *vk; struct vulkan_opts *opts; - // Swapchain metadata: - int w, h; // current size - VkSwapchainCreateInfoKHR protoInfo; // partially filled-in prototype - VkSwapchainKHR swapchain; - VkSwapchainKHR old_swapchain; - int frames_in_flight; - // state of the images: - struct ra_tex **images; // ra_tex wrappers for the vkimages - int num_images; // size of images - VkSemaphore *sems_in; // pool of semaphores used to synchronize images - VkSemaphore *sems_out; // outgoing semaphores (rendering complete) - int num_sems; - int idx_sems; // index of next free semaphore pair - int last_imgidx; // the image index last acquired (for submit) - - // This is used to pre-fetch the next frame at the end of swap_buffers - struct ra_fbo queued_fbo; + const struct pl_swapchain *swapchain; + struct ra_tex proxy_tex; }; static const struct ra_swapchain_fns vulkan_swapchain; @@ -149,133 +126,26 @@ struct mpvk_ctx *ra_vk_ctx_get(struct ra_ctx *ctx) return p->vk; } -static bool update_swapchain_info(struct priv *p, - VkSwapchainCreateInfoKHR *info) -{ - struct mpvk_ctx *vk = p->vk; - - // Query the supported capabilities and update this struct as needed - VkSurfaceCapabilitiesKHR caps; - VK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk->physd, vk->surf, &caps)); - - // Sorted by preference - static const VkCompositeAlphaFlagsKHR alphaModes[] = { - VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, - VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - }; - - for (int i = 0; i < MP_ARRAY_SIZE(alphaModes); i++) { - if (caps.supportedCompositeAlpha & alphaModes[i]) { - info->compositeAlpha = alphaModes[i]; - break; - } - } - - if (!info->compositeAlpha) { - MP_ERR(vk, "Failed picking alpha compositing mode (caps: 0x%x)\n", - caps.supportedCompositeAlpha); - goto error; - } - - static const VkSurfaceTransformFlagsKHR rotModes[] = { - VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, - VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR, - }; - - for (int i = 0; i < MP_ARRAY_SIZE(rotModes); i++) { - if (caps.supportedTransforms & rotModes[i]) { - info->preTransform = rotModes[i]; - break; - } - } - - if (!info->preTransform) { - MP_ERR(vk, "Failed picking surface transform mode (caps: 0x%x)\n", - caps.supportedTransforms); - goto error; - } - - // Image count as required - MP_VERBOSE(vk, "Requested image count: %d (min %d max %d)\n", - (int)info->minImageCount, (int)caps.minImageCount, - (int)caps.maxImageCount); - - info->minImageCount = MPMAX(info->minImageCount, caps.minImageCount); - if (caps.maxImageCount) - info->minImageCount = MPMIN(info->minImageCount, caps.maxImageCount); - - // Check the extent against the allowed parameters - if (caps.currentExtent.width != info->imageExtent.width && - caps.currentExtent.width != 0xFFFFFFFF) - { - MP_WARN(vk, "Requested width %d does not match current width %d\n", - (int)info->imageExtent.width, (int)caps.currentExtent.width); - info->imageExtent.width = caps.currentExtent.width; - } - - if (caps.currentExtent.height != info->imageExtent.height && - caps.currentExtent.height != 0xFFFFFFFF) - { - MP_WARN(vk, "Requested height %d does not match current height %d\n", - (int)info->imageExtent.height, (int)caps.currentExtent.height); - info->imageExtent.height = caps.currentExtent.height; - } - - if (caps.minImageExtent.width > info->imageExtent.width || - caps.minImageExtent.height > info->imageExtent.height) - { - MP_ERR(vk, "Requested size %dx%d smaller than device minimum %d%d\n", - (int)info->imageExtent.width, (int)info->imageExtent.height, - (int)caps.minImageExtent.width, (int)caps.minImageExtent.height); - goto error; - } - - if (caps.maxImageExtent.width < info->imageExtent.width || - caps.maxImageExtent.height < info->imageExtent.height) - { - MP_ERR(vk, "Requested size %dx%d larger than device maximum %d%d\n", - (int)info->imageExtent.width, (int)info->imageExtent.height, - (int)caps.maxImageExtent.width, (int)caps.maxImageExtent.height); - goto error; - } - - // We just request whatever usage we can, and let the ra_vk decide what - // ra_tex_params that translates to. This makes the images as flexible - // as possible. - info->imageUsage = caps.supportedUsageFlags; - return true; - -error: - return false; -} - void ra_vk_ctx_uninit(struct ra_ctx *ctx) { - if (ctx->ra) { - struct priv *p = ctx->swapchain->priv; - struct mpvk_ctx *vk = p->vk; - - mpvk_flush_commands(vk); - mpvk_poll_commands(vk, UINT64_MAX); + if (!ctx->swapchain) + return; - for (int i = 0; i < p->num_images; i++) - ra_tex_free(ctx->ra, &p->images[i]); - for (int i = 0; i < p->num_sems; i++) { - vkDestroySemaphore(vk->dev, p->sems_in[i], MPVK_ALLOCATOR); - vkDestroySemaphore(vk->dev, p->sems_out[i], MPVK_ALLOCATOR); - } + struct priv *p = ctx->swapchain->priv; + struct mpvk_ctx *vk = p->vk; - vkDestroySwapchainKHR(vk->dev, p->swapchain, MPVK_ALLOCATOR); + if (ctx->ra) { + pl_gpu_finish(vk->gpu); + pl_swapchain_destroy(&p->swapchain); ctx->ra->fns->destroy(ctx->ra); ctx->ra = NULL; } - talloc_free(ctx->swapchain); - ctx->swapchain = NULL; + vk->gpu = NULL; + pl_vulkan_destroy(&vk->vulkan); + TA_FREEP(&ctx->swapchain); } -static const struct ra_swapchain_fns vulkan_swapchain; - bool ra_vk_ctx_init(struct ra_ctx *ctx, struct mpvk_ctx *vk, VkPresentModeKHR preferred_mode) { @@ -287,56 +157,36 @@ bool ra_vk_ctx_init(struct ra_ctx *ctx, struct mpvk_ctx *vk, p->vk = vk; p->opts = mp_get_config_group(p, ctx->global, &vulkan_conf); - if (!mpvk_find_phys_device(vk, p->opts->device, ctx->opts.allow_sw)) - goto error; - if (!spirv_compiler_init(ctx)) - goto error; - vk->spirv = ctx->spirv; - if (!mpvk_pick_surface_format(vk)) - goto error; - if (!mpvk_device_init(vk, p->opts->dev_opts)) + assert(vk->ctx); + assert(vk->vkinst); + vk->vulkan = pl_vulkan_create(vk->ctx, &(struct pl_vulkan_params) { + .instance = vk->vkinst->instance, + .surface = vk->surface, + .async_transfer = p->opts->async_transfer, + .async_compute = p->opts->async_compute, + .queue_count = p->opts->queue_count, + }); + if (!vk->vulkan) goto error; - ctx->ra = ra_create_vk(vk, ctx->log); + vk->gpu = vk->vulkan->gpu; + ctx->ra = ra_create_pl(vk->gpu, ctx->log); if (!ctx->ra) goto error; - static const VkPresentModeKHR present_modes[SWAP_COUNT] = { - [SWAP_FIFO] = VK_PRESENT_MODE_FIFO_KHR, - [SWAP_FIFO_RELAXED] = VK_PRESENT_MODE_FIFO_RELAXED_KHR, - [SWAP_MAILBOX] = VK_PRESENT_MODE_MAILBOX_KHR, - [SWAP_IMMEDIATE] = VK_PRESENT_MODE_IMMEDIATE_KHR, + // Create the swapchain + struct pl_vulkan_swapchain_params params = { + .surface = vk->surface, + .present_mode = preferred_mode, + .swapchain_depth = ctx->opts.swapchain_depth, }; - p->protoInfo = (VkSwapchainCreateInfoKHR) { - .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - .surface = vk->surf, - .imageFormat = vk->surf_format.format, - .imageColorSpace = vk->surf_format.colorSpace, - .imageArrayLayers = 1, // non-stereoscopic - .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, - .minImageCount = ctx->opts.swapchain_depth + 1, // +1 for FB - .presentMode = p->opts->swap_mode ? present_modes[p->opts->swap_mode] - : preferred_mode, - .clipped = true, - }; + if (p->opts->swap_mode >= 0) // user override + params.present_mode = p->opts->swap_mode; - // Make sure the swapchain present mode is supported - int num_modes; - VK(vkGetPhysicalDeviceSurfacePresentModesKHR(vk->physd, vk->surf, - &num_modes, NULL)); - VkPresentModeKHR *modes = talloc_array(NULL, VkPresentModeKHR, num_modes); - VK(vkGetPhysicalDeviceSurfacePresentModesKHR(vk->physd, vk->surf, - &num_modes, modes)); - bool supported = false; - for (int i = 0; i < num_modes; i++) - supported |= (modes[i] == p->protoInfo.presentMode); - talloc_free(modes); - - if (!supported) { - MP_ERR(ctx, "Requested swap mode unsupported by this device!\n"); + p->swapchain = pl_vulkan_create_swapchain(vk->vulkan, ¶ms); + if (!p->swapchain) goto error; - } return true; @@ -345,245 +195,49 @@ error: return false; } -static void destroy_swapchain(struct mpvk_ctx *vk, struct priv *p) +bool ra_vk_ctx_resize(struct ra_ctx *ctx, int width, int height) { - assert(p->old_swapchain); - vkDestroySwapchainKHR(vk->dev, p->old_swapchain, MPVK_ALLOCATOR); - p->old_swapchain = NULL; -} - -bool ra_vk_ctx_resize(struct ra_swapchain *sw, int w, int h) -{ - struct priv *p = sw->priv; - if (w == p->w && h == p->h) - return true; - - struct ra *ra = sw->ctx->ra; - struct mpvk_ctx *vk = p->vk; - VkImage *vkimages = NULL; - - // It's invalid to trigger another swapchain recreation while there's - // more than one swapchain already active, so we need to flush any pending - // asynchronous swapchain release operations that may be ongoing. - while (p->old_swapchain) - mpvk_poll_commands(vk, 100000); // 100μs - - VkSwapchainCreateInfoKHR sinfo = p->protoInfo; - sinfo.imageExtent = (VkExtent2D){ w, h }; - sinfo.oldSwapchain = p->swapchain; - - if (!update_swapchain_info(p, &sinfo)) - goto error; - - VK(vkCreateSwapchainKHR(vk->dev, &sinfo, MPVK_ALLOCATOR, &p->swapchain)); - p->w = w; - p->h = h; - - // Freeing the old swapchain while it's still in use is an error, so do - // it asynchronously once the device is idle. - if (sinfo.oldSwapchain) { - p->old_swapchain = sinfo.oldSwapchain; - vk_dev_callback(vk, (vk_cb) destroy_swapchain, vk, p); - } - - // Get the new swapchain images - int num; - VK(vkGetSwapchainImagesKHR(vk->dev, p->swapchain, &num, NULL)); - vkimages = talloc_array(NULL, VkImage, num); - VK(vkGetSwapchainImagesKHR(vk->dev, p->swapchain, &num, vkimages)); - - // If needed, allocate some more semaphores - while (num > p->num_sems) { - VkSemaphore sem_in, sem_out; - static const VkSemaphoreCreateInfo seminfo = { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, - }; - VK(vkCreateSemaphore(vk->dev, &seminfo, MPVK_ALLOCATOR, &sem_in)); - VK(vkCreateSemaphore(vk->dev, &seminfo, MPVK_ALLOCATOR, &sem_out)); - - int idx = p->num_sems++; - MP_TARRAY_GROW(p, p->sems_in, idx); - MP_TARRAY_GROW(p, p->sems_out, idx); - p->sems_in[idx] = sem_in; - p->sems_out[idx] = sem_out; - } - - // Invalidate the queued texture - p->queued_fbo = (struct ra_fbo) {0}; - - // Recreate the ra_tex wrappers - for (int i = 0; i < p->num_images; i++) - ra_tex_free(ra, &p->images[i]); - - p->num_images = num; - MP_TARRAY_GROW(p, p->images, p->num_images); - for (int i = 0; i < num; i++) { - p->images[i] = ra_vk_wrap_swapchain_img(ra, vkimages[i], sinfo); - if (!p->images[i]) - goto error; - } + struct priv *p = ctx->swapchain->priv; - talloc_free(vkimages); - return true; + bool ok = pl_swapchain_resize(p->swapchain, &width, &height); + ctx->vo->dwidth = width; + ctx->vo->dheight = height; -error: - talloc_free(vkimages); - vkDestroySwapchainKHR(vk->dev, p->swapchain, MPVK_ALLOCATOR); - p->swapchain = NULL; - return false; + return ok; } static int color_depth(struct ra_swapchain *sw) { - struct priv *p = sw->priv; - int bits = 0; - - if (!p->num_images) - return bits; - - // The channel with the most bits is probably the most authoritative about - // the actual color information (consider e.g. a2bgr10). Slight downside - // in that it results in rounding r/b for e.g. rgb565, but we don't pick - // surfaces with fewer than 8 bits anyway. - const struct ra_format *fmt = p->images[0]->params.format; - for (int i = 0; i < fmt->num_components; i++) { - int depth = fmt->component_depth[i]; - bits = MPMAX(bits, depth ? depth : fmt->component_size[i]); - } - - return bits; + return 0; // TODO: implement this somehow? } static bool start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo) { struct priv *p = sw->priv; - struct mpvk_ctx *vk = p->vk; - if (!p->swapchain) + struct pl_swapchain_frame frame; + if (!pl_swapchain_start_frame(p->swapchain, &frame)) + return false; + if (!mppl_wrap_tex(sw->ctx->ra, frame.fbo, &p->proxy_tex)) return false; - if (p->queued_fbo.tex) { - assert(out_fbo != &p->queued_fbo); - *out_fbo = p->queued_fbo; - p->queued_fbo = (struct ra_fbo) {0}; - return true; - } - - VkSemaphore sem_in = p->sems_in[p->idx_sems]; - MP_TRACE(vk, "vkAcquireNextImageKHR signals %p\n", (void *)sem_in); - - for (int attempts = 0; attempts < 2; attempts++) { - uint32_t imgidx = 0; - VkResult res = vkAcquireNextImageKHR(vk->dev, p->swapchain, UINT64_MAX, - sem_in, NULL, &imgidx); - - switch (res) { - case VK_SUCCESS: - p->last_imgidx = imgidx; - *out_fbo = (struct ra_fbo) { - .tex = p->images[imgidx], - .flip = false, - }; - ra_tex_vk_external_dep(sw->ctx->ra, out_fbo->tex, sem_in); - return true; - - case VK_ERROR_OUT_OF_DATE_KHR: { - // In these cases try recreating the swapchain - int w = p->w, h = p->h; - p->w = p->h = 0; // invalidate the current state - if (!ra_vk_ctx_resize(sw, w, h)) - return false; - continue; - } - - default: - MP_ERR(vk, "Failed acquiring swapchain image: %s\n", vk_err(res)); - return false; - } - } - - // If we've exhausted the number of attempts to recreate the swapchain, - // just give up silently. - return false; -} + *out_fbo = (struct ra_fbo) { + .tex = &p->proxy_tex, + .flip = frame.flipped, + }; -static void present_cb(struct priv *p, void *arg) -{ - p->frames_in_flight--; + return true; } static bool submit_frame(struct ra_swapchain *sw, const struct vo_frame *frame) { struct priv *p = sw->priv; - struct ra *ra = sw->ctx->ra; - struct mpvk_ctx *vk = p->vk; - if (!p->swapchain) - return false; - - struct vk_cmd *cmd = ra_vk_submit(ra, p->images[p->last_imgidx]); - if (!cmd) - return false; - - VkSemaphore sem_out = p->sems_out[p->idx_sems++]; - p->idx_sems %= p->num_sems; - vk_cmd_sig(cmd, sem_out); - - p->frames_in_flight++; - vk_cmd_callback(cmd, (vk_cb) present_cb, p, NULL); - - vk_cmd_queue(vk, cmd); - if (!mpvk_flush_commands(vk)) - return false; - - // Submit to the same queue that we were currently rendering to - struct vk_cmdpool *pool_gfx = vk->pool_graphics; - VkQueue queue = pool_gfx->queues[pool_gfx->idx_queues]; - - // Rotate the queues to ensure good parallelism across frames - for (int i = 0; i < vk->num_pools; i++) { - struct vk_cmdpool *pool = vk->pools[i]; - pool->idx_queues = (pool->idx_queues + 1) % pool->num_queues; - } - - VkPresentInfoKHR pinfo = { - .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, - .waitSemaphoreCount = 1, - .pWaitSemaphores = &sem_out, - .swapchainCount = 1, - .pSwapchains = &p->swapchain, - .pImageIndices = &p->last_imgidx, - }; - - MP_TRACE(vk, "vkQueuePresentKHR waits on %p\n", (void *)sem_out); - VkResult res = vkQueuePresentKHR(queue, &pinfo); - switch (res) { - case VK_SUCCESS: - case VK_SUBOPTIMAL_KHR: - return true; - - case VK_ERROR_OUT_OF_DATE_KHR: - // We can silently ignore this error, since the next start_frame will - // recreate the swapchain automatically. - return true; - - default: - MP_ERR(vk, "Failed presenting to queue %p: %s\n", (void *)queue, - vk_err(res)); - return false; - } + return pl_swapchain_submit_frame(p->swapchain); } static void swap_buffers(struct ra_swapchain *sw) { struct priv *p = sw->priv; - - while (p->frames_in_flight >= sw->ctx->opts.swapchain_depth) - mpvk_poll_commands(p->vk, 100000); // 100μs - - // Also try and block until the next hardware buffer swap early. this - // prevents start_frame from blocking later, thus slightly improving the - // frame timing stats. (since mpv assumes most blocking will happen in - // swap_buffers) - start_frame(sw, &p->queued_fbo); + pl_swapchain_swap_buffers(p->swapchain); } static const struct ra_swapchain_fns vulkan_swapchain = { |