summaryrefslogtreecommitdiffstats
path: root/video/out/vulkan/context.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/vulkan/context.c')
-rw-r--r--video/out/vulkan/context.c482
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, &params);
+ 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 = {