Commit ad242535 authored by Niklas Haas's avatar Niklas Haas

swapchain: add resizing API

This is used both for updating the size and querying the size. I don't
want to make these separate functions because it should be painfully
obvious that the size you get may not be the size you request.

This allows libplacebo to work on wayland, which mediates the concept of
swapchain resizing to protocols like xdg_shell that mesa/vulkan can't
know anything about (by design).
parent 49a65490
...@@ -2,7 +2,7 @@ project('libplacebo', ['c', 'cpp'], ...@@ -2,7 +2,7 @@ project('libplacebo', ['c', 'cpp'],
license: 'LGPL2.1+', license: 'LGPL2.1+',
default_options: ['c_std=c99'], default_options: ['c_std=c99'],
meson_version: '>=0.47', meson_version: '>=0.47',
version: '1.17.0', version: '1.18.0',
) )
# Version number # Version number
......
...@@ -47,6 +47,19 @@ void pl_swapchain_destroy(const struct pl_swapchain **sw); ...@@ -47,6 +47,19 @@ void pl_swapchain_destroy(const struct pl_swapchain **sw);
// next for submission. // next for submission.
int pl_swapchain_latency(const struct pl_swapchain *sw); int pl_swapchain_latency(const struct pl_swapchain *sw);
// Update/query the swapchain size. This function performs both roles: it tries
// setting the swapchain size to the values requested by the user, and returns
// in the same variables what width/height the swapchain was actually set to -
// which may be (substantially) different from the values requested by the
// user. A value of 0 means "unknown/none" (in which case, libplacebo won't try
// updating the size - it will simply return the current state of the
// swapchain). It's also possible for libplacebo to return values of 0, such as
// in the case that the swapchain doesn't exist yet.
//
// Returns false on significant errors (e.g. dead surface). This function can
// effectively be used to probe if creating a swapchain works.
bool pl_swapchain_resize(const struct pl_swapchain *sw, int *width, int *height);
// The struct used to hold the results of `pl_swapchain_start_frame` // The struct used to hold the results of `pl_swapchain_start_frame`
struct pl_swapchain_frame { struct pl_swapchain_frame {
// A texture representing the framebuffer users should use for rendering. // A texture representing the framebuffer users should use for rendering.
......
...@@ -37,6 +37,20 @@ int pl_swapchain_latency(const struct pl_swapchain *sw) ...@@ -37,6 +37,20 @@ int pl_swapchain_latency(const struct pl_swapchain *sw)
return sw->impl->latency(sw); return sw->impl->latency(sw);
} }
bool pl_swapchain_resize(const struct pl_swapchain *sw, int *width, int *height)
{
int dummy[2] = {0};
width = PL_DEF(width, &dummy[0]);
height = PL_DEF(height, &dummy[1]);
if (!sw->impl->resize) {
*width = *height = 0;
return true;
}
return sw->impl->resize(sw, width, height);
}
bool pl_swapchain_start_frame(const struct pl_swapchain *sw, bool pl_swapchain_start_frame(const struct pl_swapchain *sw,
struct pl_swapchain_frame *out_frame) struct pl_swapchain_frame *out_frame)
{ {
......
...@@ -25,6 +25,7 @@ struct pl_sw_fns { ...@@ -25,6 +25,7 @@ struct pl_sw_fns {
void (*destroy)(const struct pl_swapchain *sw); void (*destroy)(const struct pl_swapchain *sw);
SW_PFN(latency); // optional SW_PFN(latency); // optional
SW_PFN(resize); // optional
SW_PFN(start_frame); SW_PFN(start_frame);
SW_PFN(submit_frame); SW_PFN(submit_frame);
SW_PFN(swap_buffers); SW_PFN(swap_buffers);
......
...@@ -30,6 +30,7 @@ struct priv { ...@@ -30,6 +30,7 @@ struct priv {
VkSwapchainCreateInfoKHR protoInfo; // partially filled-in prototype VkSwapchainCreateInfoKHR protoInfo; // partially filled-in prototype
VkSwapchainKHR swapchain; VkSwapchainKHR swapchain;
VkSwapchainKHR old_swapchain; VkSwapchainKHR old_swapchain;
int cur_width, cur_height;
int swapchain_depth; int swapchain_depth;
int frames_in_flight; // number of frames currently queued int frames_in_flight; // number of frames currently queued
struct pl_color_repr color_repr; struct pl_color_repr color_repr;
...@@ -325,7 +326,8 @@ static int vk_sw_latency(const struct pl_swapchain *sw) ...@@ -325,7 +326,8 @@ static int vk_sw_latency(const struct pl_swapchain *sw)
return p->swapchain_depth; return p->swapchain_depth;
} }
static bool update_swapchain_info(struct priv *p, VkSwapchainCreateInfoKHR *info) static bool update_swapchain_info(struct priv *p, VkSwapchainCreateInfoKHR *info,
int w, int h)
{ {
struct vk_ctx *vk = p->vk; struct vk_ctx *vk = p->vk;
...@@ -387,25 +389,25 @@ static bool update_swapchain_info(struct priv *p, VkSwapchainCreateInfoKHR *info ...@@ -387,25 +389,25 @@ static bool update_swapchain_info(struct priv *p, VkSwapchainCreateInfoKHR *info
if (caps.maxImageCount) if (caps.maxImageCount)
info->minImageCount = PL_MIN(info->minImageCount, caps.maxImageCount); info->minImageCount = PL_MIN(info->minImageCount, caps.maxImageCount);
// This seems to be an obscure case, and doesn't make sense anyway. So just // Default the requested size based on the reported extent
// ignore it and assume we're using a sane environment where the current if (caps.currentExtent.width != 0xFFFFFFFF)
// window size is known. w = PL_DEF(w, caps.currentExtent.width);
if (caps.currentExtent.width == 0xFFFFFFFF || if (caps.currentExtent.height != 0xFFFFFFFF)
caps.currentExtent.height == 0xFFFFFFFF) h = PL_DEF(h, caps.currentExtent.height);
{
PL_ERR(vk, "The swapchain's current extent is reported as unknown. " // Otherwise, re-use the existing size if available
"In other words, we don't know the size of the window. Giving up!"); w = PL_DEF(w, info->imageExtent.width);
goto error; h = PL_DEF(h, info->imageExtent.height);
}
// This seems to be an obscure case that should technically violate the spec if (!w || !h) {
// anyway, but better safe than sorry.. PL_ERR(vk, "Failed resizing swapchain: unknown size?");
if (!caps.currentExtent.width || !caps.currentExtent.height) {
PL_WARN(vk, "Unable to recreate swapchain: image extent is 0, possibly "
"the window is minimized or hidden?");
goto error; goto error;
} }
// Clamp the extent based on the supported limits
w = PL_MIN(PL_MAX(w, caps.minImageExtent.width), caps.maxImageExtent.width);
h = PL_MIN(PL_MAX(h, caps.minImageExtent.height), caps.maxImageExtent.height);
// We just request whatever usage we can, and let the pl_vk decide what // We just request whatever usage we can, and let the pl_vk decide what
// pl_tex_params that translates to. This makes the images as flexible // pl_tex_params that translates to. This makes the images as flexible
// as possible. However, require at least blitting and rendering. // as possible. However, require at least blitting and rendering.
...@@ -418,7 +420,7 @@ static bool update_swapchain_info(struct priv *p, VkSwapchainCreateInfoKHR *info ...@@ -418,7 +420,7 @@ static bool update_swapchain_info(struct priv *p, VkSwapchainCreateInfoKHR *info
} }
info->imageUsage = caps.supportedUsageFlags; info->imageUsage = caps.supportedUsageFlags;
info->imageExtent = caps.currentExtent; info->imageExtent = (VkExtent2D) { w, h };
return true; return true;
error: error:
...@@ -432,7 +434,7 @@ static void destroy_swapchain(struct vk_ctx *vk, struct priv *p) ...@@ -432,7 +434,7 @@ static void destroy_swapchain(struct vk_ctx *vk, struct priv *p)
p->old_swapchain = VK_NULL_HANDLE; p->old_swapchain = VK_NULL_HANDLE;
} }
static bool vk_sw_recreate(const struct pl_swapchain *sw) static bool vk_sw_recreate(const struct pl_swapchain *sw, int w, int h)
{ {
const struct pl_gpu *gpu = sw->gpu; const struct pl_gpu *gpu = sw->gpu;
struct priv *p = sw->priv; struct priv *p = sw->priv;
...@@ -450,7 +452,7 @@ static bool vk_sw_recreate(const struct pl_swapchain *sw) ...@@ -450,7 +452,7 @@ static bool vk_sw_recreate(const struct pl_swapchain *sw)
VkSwapchainCreateInfoKHR sinfo = p->protoInfo; VkSwapchainCreateInfoKHR sinfo = p->protoInfo;
sinfo.oldSwapchain = p->swapchain; sinfo.oldSwapchain = p->swapchain;
if (!update_swapchain_info(p, &sinfo)) if (!update_swapchain_info(p, &sinfo, w, h))
goto error; goto error;
PL_INFO(sw, "(Re)creating swapchain of size %dx%d", PL_INFO(sw, "(Re)creating swapchain of size %dx%d",
...@@ -459,6 +461,9 @@ static bool vk_sw_recreate(const struct pl_swapchain *sw) ...@@ -459,6 +461,9 @@ static bool vk_sw_recreate(const struct pl_swapchain *sw)
VK(vkCreateSwapchainKHR(vk->dev, &sinfo, VK_ALLOC, &p->swapchain)); VK(vkCreateSwapchainKHR(vk->dev, &sinfo, VK_ALLOC, &p->swapchain));
p->cur_width = sinfo.imageExtent.width;
p->cur_height = sinfo.imageExtent.height;
// Freeing the old swapchain while it's still in use is an error, so do it // Freeing the old swapchain while it's still in use is an error, so do it
// asynchronously once the device is idle // asynchronously once the device is idle
if (sinfo.oldSwapchain) { if (sinfo.oldSwapchain) {
...@@ -523,6 +528,7 @@ error: ...@@ -523,6 +528,7 @@ error:
talloc_free(vkimages); talloc_free(vkimages);
vkDestroySwapchainKHR(vk->dev, p->swapchain, VK_ALLOC); vkDestroySwapchainKHR(vk->dev, p->swapchain, VK_ALLOC);
p->swapchain = VK_NULL_HANDLE; p->swapchain = VK_NULL_HANDLE;
p->cur_width = p->cur_height = 0;
return false; return false;
} }
...@@ -531,7 +537,7 @@ static bool vk_sw_start_frame(const struct pl_swapchain *sw, ...@@ -531,7 +537,7 @@ static bool vk_sw_start_frame(const struct pl_swapchain *sw,
{ {
struct priv *p = sw->priv; struct priv *p = sw->priv;
struct vk_ctx *vk = p->vk; struct vk_ctx *vk = p->vk;
if (!p->swapchain && !vk_sw_recreate(sw)) if (!p->swapchain && !vk_sw_recreate(sw, 0, 0))
return false; return false;
VkSemaphore sem_in = p->sems_in[p->idx_sems]; VkSemaphore sem_in = p->sems_in[p->idx_sems];
...@@ -555,9 +561,10 @@ static bool vk_sw_start_frame(const struct pl_swapchain *sw, ...@@ -555,9 +561,10 @@ static bool vk_sw_start_frame(const struct pl_swapchain *sw,
}; };
return true; return true;
case VK_SUBOPTIMAL_KHR:
case VK_ERROR_OUT_OF_DATE_KHR: { case VK_ERROR_OUT_OF_DATE_KHR: {
// In these cases try recreating the swapchain // In these cases try recreating the swapchain
if (!vk_sw_recreate(sw)) if (!vk_sw_recreate(sw, 0, 0))
return false; return false;
continue; continue;
} }
...@@ -647,9 +654,23 @@ static void vk_sw_swap_buffers(const struct pl_swapchain *sw) ...@@ -647,9 +654,23 @@ static void vk_sw_swap_buffers(const struct pl_swapchain *sw)
vk_poll_commands(p->vk, 1000000); // 1 ms vk_poll_commands(p->vk, 1000000); // 1 ms
} }
static bool vk_sw_resize(const struct pl_swapchain *sw, int *width, int *height)
{
struct priv *p = sw->priv;
bool ok = true;
if ((*width && *width != p->cur_width) || (*height && *height != p->cur_height))
ok = vk_sw_recreate(sw, *width, *height);
*width = p->cur_width;
*height = p->cur_height;
return ok;
}
static struct pl_sw_fns vulkan_swapchain = { static struct pl_sw_fns vulkan_swapchain = {
.destroy = vk_sw_destroy, .destroy = vk_sw_destroy,
.latency = vk_sw_latency, .latency = vk_sw_latency,
.resize = vk_sw_resize,
.start_frame = vk_sw_start_frame, .start_frame = vk_sw_start_frame,
.submit_frame = vk_sw_submit_frame, .submit_frame = vk_sw_submit_frame,
.swap_buffers = vk_sw_swap_buffers, .swap_buffers = vk_sw_swap_buffers,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment