#include "ra_vk.h"
#include "malloc.h"
#include "video/out/opengl/utils.h"
static struct ra_fns ra_fns_vk;
// For ra.priv
struct ra_vk {
struct mpvk_ctx *vk;
struct ra_tex *clear_tex; // stupid hack for clear()
struct vk_cmd *cmd; // currently recording cmd
};
struct mpvk_ctx *ra_vk_get(struct ra *ra)
{
if (ra->fns != &ra_fns_vk)
return NULL;
struct ra_vk *p = ra->priv;
return p->vk;
}
// Returns a command buffer, or NULL on error
static struct vk_cmd *vk_require_cmd(struct ra *ra)
{
struct ra_vk *p = ra->priv;
struct mpvk_ctx *vk = ra_vk_get(ra);
if (!p->cmd)
p->cmd = vk_cmd_begin(vk, vk->pool);
return p->cmd;
}
// Note: This technically follows the flush() API, but we don't need
// to expose that (and in fact, it's a bad idea) since we control flushing
// behavior with ra_vk_present_frame already.
static bool vk_flush(struct ra *ra, VkSemaphore *done)
{
struct ra_vk *p = ra->priv;
struct mpvk_ctx *vk = ra_vk_get(ra);
if (p->cmd) {
if (!vk_cmd_submit(vk, p->cmd, done))
return false;
p->cmd = NULL;
}
return true;
}
// The callback's *priv will always be set to `ra`
static void vk_callback(struct ra *ra, vk_cb callback, void *arg)
{
struct ra_vk *p = ra->priv;
struct mpvk_ctx *vk = ra_vk_get(ra);
if (p->cmd) {
vk_cmd_callback(p->cmd, callback, ra, arg);
} else {
vk_dev_callback(vk, callback, ra, arg);
}
}
#define MAKE_LAZY_DESTRUCTOR(fun, argtype) \
static void fun##_lazy(struct ra *ra, argtype *arg) { \
vk_callback(ra, (vk_cb) fun, arg); \
}
static void vk_destroy_ra(struct ra *ra)
{
struct ra_vk *p = ra->priv;
struct mpvk_ctx *vk = ra_vk_get(ra);
vk_flush(ra, NULL);
mpvk_dev_wait_idle(vk);
ra_tex_free(ra, &p->clear_tex);
talloc_free(ra);
}
static bool vk_setup_formats(struct ra *ra)
{
struct mpvk_ctx *vk = ra_vk_get(ra);
for (const struct vk_format *vk_fmt = vk_formats; vk_fmt->name; vk_fmt++) {
VkFormatProperties prop;
vkGetPhysicalDeviceFormatProperties(vk->physd, vk_fmt->iformat, &prop);
// As a bare minimum, we need to sample from an allocated image
VkFormatFeatureFlags flags = prop.optimalTilingFeatures;
if (!(flags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT))
continue;
VkFormatFeatureFlags linear_bits, render_bits;
linear_bits = VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
render_bits = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT |
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT;
struct ra_format *fmt = talloc_zero(ra, struct ra_format);
*fmt = (struct ra_format) {
.name = vk_fmt->name,
.priv = (void *)vk_fmt,
.ctype = vk_fmt->ctype,
.ordered = !vk_fmt->fucked_order,
.num_components = vk_fmt->components,
.pixel_size = vk_fmt->bytes,
.linear_filter = !!(flags & linear_bits),
.renderable = !!(flags & render_bits),
};
for (int i = 0; i < 4; i++)
fmt->component_size[i] = fmt->component_depth[i] = vk_fmt->bits[i];
MP_TARRAY_APPEND(ra, ra->formats, ra->num_formats, fmt);
}
// Populate some other capabilities related to formats while we're at it
VkImageType imgType[3] = {
VK_IMAGE_TYPE_1D,
VK_IMAGE_TYPE_2D,
VK_IMAGE_TYPE_3D
};
// R8_UNORM is supported on literally every single vulkan implementation
const VkFormat testfmt = VK_FORMAT_R8_UNORM;
for (int d = 0; d < 3; d++) {
VkImageFormatProperties iprop;
VkResult res = vkGetPhysicalDeviceImageFormatProperties(vk->physd,
testfmt, imgType[d], VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_SAMPLED_BIT, 0, &iprop);
switch (imgType[d]) {
case VK_IMAGE_TYPE_1D:
if (res == VK_SUCCESS)
ra->caps |= RA_CAP_TEX_1D;
break;
case VK_IMAGE_TYPE_2D:
// 2D formats must be supported by RA, so ensure this is the case
VK_ASSERT(res, "Querying 2D format limits");
ra->max_texture_wh = MPMIN(iprop.maxExtent.width, iprop.maxExtent.height);
break;
case VK_IMAGE_TYPE_3D:
if (res == VK_SUCCESS)
ra->caps |= RA_CAP_TEX_3D;
break;
}
}
// RA_CAP_BLIT implies both blitting between images as well as blitting
// directly to
|