forked from mpv-player/mpv
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This time based on RA. 2017 is the year of the vulkan desktop! Current problems / limitations / improvement opportunities: 1. The entire thing depends on VK_NV_glsl_shader, which is a god-awful nvidia-exclusive hack that barely works and is held together with duct tape and prayers. Long-term, we really, REALLY need to figure out a way to use a GLSL->SPIR-V middleware like glslang. The problem with glslang in particular is that it's a gigantic pile of awful, but maybe time will help here.. 2. We don't use async transfer at all. This is very difficult, but doable in theory with the newer design. Would require refactoring vk_cmdpool slightly, and also expanding ra_vk.active_cmd to include commands on the async queue as well. Also, async compute is pretty much impossible to benefit from because we need to pingpong with serial dependencies anyway. (Sorry AMD users, you fell for the async compute meme) 3. The custom memory allocator is pretty naive. It's prone to under-allocating memory, allocation thrashing, freeing slabs too aggressively, and general slowness due to allocating from the same thread. In addition to making it smarter, we should also make it multi-threaded: ideally it would free slabs from a different thread, and also pre-allocate slabs from a different thread if it reaches some critical "low" threshold on the amount of available bytes. (Perhaps relative to the current heap size). These limitations manifest themselves as occasional choppy performance when changing the window size. 4. The swapchain code and ANGLE's swapchain code could share common options somehow. Left away for now because I don't want to deal with that headache for the time being. 5. The swapchain/flipping code violates the vulkan spec, by assuming that the presentation queue will be bounded (in cases where rendering is significantly faster than vsync). But apparently, there's simply no better way to do this right now, to the point where even the stupid cube.c examples from LunarG etc. do it wrong. (cf. KhronosGroup/Vulkan-Docs#370)
- Loading branch information
Showing
15 changed files
with
3,559 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,335 @@ | ||
/* | ||
* This file is part of mpv. | ||
* | ||
* mpv is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation; either | ||
* version 2.1 of the License, or (at your option) any later version. | ||
* | ||
* mpv is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with mpv. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
#include "mpv_talloc.h" | ||
#include "options/m_config.h" | ||
#include "osdep/timer.h" | ||
#include "video/mp_image.h" | ||
#include "video/out/x11_common.h" | ||
#include "vo.h" | ||
#include "sub/osd.h" | ||
|
||
#include "opengl/ra.h" | ||
#include "opengl/video.h" | ||
|
||
#include "vulkan/common.h" | ||
#include "vulkan/utils.h" | ||
#include "vulkan/ra_vk.h" | ||
|
||
struct vo_vulkan_opts { | ||
int debug; // whether to load the validation layers or not | ||
int allow_sw; // whether to allow software devices | ||
char *device; // force a specific GPU | ||
int swsize; // swapchain size | ||
int swdepth; // swapchain depth | ||
}; | ||
|
||
struct vk_priv { | ||
struct vo *vo; | ||
struct mp_log *log; | ||
|
||
struct vo_vulkan_opts opts; | ||
|
||
struct mpvk_ctx vk; | ||
struct ra *ra; | ||
struct gl_video *renderer; | ||
|
||
struct vk_swchain swchain; | ||
int frames_in_flight; | ||
}; | ||
|
||
static bool resize(struct vo *vo) | ||
{ | ||
struct vk_priv *p = vo->priv; | ||
|
||
MP_VERBOSE(vo, "Resize: %dx%d\n", vo->dwidth, vo->dheight); | ||
|
||
if (!vk_swchain_resize(&p->swchain, vo->dwidth, vo->dheight)) { | ||
MP_ERR(vo, "Failed resizing swapchain!\n"); | ||
return false; | ||
} | ||
|
||
struct mp_rect src, dst; | ||
struct mp_osd_res osd; | ||
vo_get_src_dst_rects(vo, &src, &dst, &osd); | ||
|
||
gl_video_resize(p->renderer, &src, &dst, &osd); | ||
|
||
vo->want_redraw = true; | ||
return true; | ||
} | ||
|
||
static int reconfig(struct vo *vo, struct mp_image_params *params) | ||
{ | ||
struct vk_priv *p = vo->priv; | ||
|
||
if (vo->x11) | ||
vo_x11_config_vo_window(vo); | ||
|
||
if (!resize(vo)) | ||
return VO_ERROR; | ||
|
||
gl_video_config(p->renderer, params); | ||
|
||
return 0; | ||
} | ||
|
||
static void uninit(struct vo *vo) | ||
{ | ||
struct vk_priv *p = vo->priv; | ||
struct mpvk_ctx *vk = &p->vk; | ||
|
||
gl_video_uninit(p->renderer); | ||
|
||
if (p->ra) { | ||
vk_swchain_uninit(p->ra, &p->swchain); | ||
p->ra->fns->destroy(p->ra); | ||
} | ||
|
||
// Clean up platform-specific windowing stuff. Do this first to prevent | ||
// keeping around the window for long, then we can uninit the device etc. | ||
// afterwards | ||
if (vo->x11) | ||
vo_x11_uninit(vo); | ||
|
||
mpvk_uninit(vk); | ||
} | ||
|
||
static int preinit(struct vo *vo) | ||
{ | ||
struct vk_priv *p = vo->priv; | ||
struct mpvk_ctx *vk = &p->vk; | ||
p->vo = vo; | ||
p->log = vk->log = vo->log; | ||
|
||
if (!mpvk_instance_init(vk, p->opts.debug)) | ||
goto error; | ||
if (!mpvk_surface_init(vo, vk)) | ||
goto error; | ||
if (!mpvk_find_phys_device(vk, p->opts.device, p->opts.allow_sw)) | ||
goto error; | ||
if (!mpvk_pick_surface_format(vk)) | ||
goto error; | ||
if (!mpvk_device_init(vk)) | ||
goto error; | ||
p->ra = ra_create_vk(vk, p->log); | ||
if (!p->ra) | ||
goto error; | ||
if (!vk_swchain_init(vk, p->ra, p->opts.swsize, &p->swchain)) | ||
goto error; | ||
|
||
p->renderer = gl_video_init(p->ra, vo->log, vo->global); | ||
gl_video_set_osd_source(p->renderer, vo->osd); | ||
gl_video_configure_queue(p->renderer, vo); | ||
|
||
return 0; | ||
|
||
error: | ||
uninit(vo); | ||
return -1; | ||
} | ||
|
||
static int control(struct vo *vo, uint32_t request, void *data) | ||
{ | ||
struct vk_priv *p = vo->priv; | ||
|
||
switch (request) { | ||
case VOCTRL_SET_PANSCAN: | ||
return resize(vo) ? VO_TRUE : VO_ERROR; | ||
case VOCTRL_SET_EQUALIZER: | ||
vo->want_redraw = true; | ||
return VO_TRUE; | ||
case VOCTRL_UPDATE_RENDER_OPTS: { | ||
gl_video_update_options(p->renderer); | ||
gl_video_configure_queue(p->renderer, p->vo); | ||
p->vo->want_redraw = true; | ||
return true; | ||
} | ||
case VOCTRL_RESET: | ||
gl_video_reset(p->renderer); | ||
return true; | ||
case VOCTRL_PAUSE: | ||
if (gl_video_showing_interpolated_frame(p->renderer)) | ||
vo->want_redraw = true; | ||
return true; | ||
case VOCTRL_PERFORMANCE_DATA: | ||
gl_video_perfdata(p->renderer, (struct voctrl_performance_data *)data); | ||
return true; | ||
} | ||
|
||
int events = 0, r = 0; | ||
|
||
if (vo->x11) | ||
r |= vo_x11_control(vo, &events, request, data); | ||
|
||
if (events & VO_EVENT_RESIZE) | ||
r |= resize(vo) ? 0 : VO_ERROR; | ||
|
||
if (events & VO_EVENT_EXPOSE) | ||
vo->want_redraw = true; | ||
|
||
vo_event(vo, events); | ||
return r; | ||
} | ||
|
||
static void draw_frame(struct vo *vo, struct vo_frame *frame) | ||
{ | ||
struct vk_priv *p = vo->priv; | ||
struct vk_swimg swimg; | ||
if (!vk_swchain_get(&p->swchain, &swimg)) | ||
goto error; | ||
|
||
struct fbodst target = { | ||
.tex = swimg.image, | ||
.flip = false, | ||
}; | ||
|
||
gl_video_render_frame(p->renderer, frame, target); | ||
if (!ra_vk_present_frame(p->ra, &swimg, &p->frames_in_flight)) { | ||
MP_ERR(vo, "Failed presenting frame!\n"); | ||
goto error; | ||
} | ||
|
||
error: | ||
return; | ||
} | ||
|
||
static void flip_page(struct vo *vo) | ||
{ | ||
struct vk_priv *p = vo->priv; | ||
while (p->frames_in_flight >= p->opts.swdepth) | ||
mpvk_poll_cmds(&p->vk, p->vk.pool, UINT64_MAX); | ||
} | ||
|
||
static int query_format(struct vo *vo, int format) | ||
{ | ||
struct vk_priv *p = vo->priv; | ||
if (!gl_video_check_format(p->renderer, format)) | ||
return 0; | ||
return 1; | ||
} | ||
|
||
static void wakeup(struct vo *vo) | ||
{ | ||
if (vo->x11) | ||
vo_x11_wakeup(vo); | ||
} | ||
|
||
static void wait_events(struct vo *vo, int64_t until_time_us) | ||
{ | ||
if (vo->x11) { | ||
vo_x11_wait_events(vo, until_time_us); | ||
} else { | ||
vo_wait_default(vo, until_time_us); | ||
} | ||
} | ||
|
||
static struct mp_image *get_image(struct vo *vo, int imgfmt, int w, int h, | ||
int stride_align) | ||
{ | ||
struct vk_priv *p = vo->priv; | ||
return gl_video_get_image(p->renderer, imgfmt, w, h, stride_align); | ||
} | ||
|
||
static int vk_validate_dev(struct mp_log *log, const struct m_option *opt, | ||
struct bstr name, struct bstr param) | ||
{ | ||
int ret = M_OPT_INVALID; | ||
VkResult res; | ||
|
||
// Create a dummy instance to validate/list the devices | ||
VkInstanceCreateInfo info = { | ||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, | ||
}; | ||
|
||
VkInstance inst; | ||
VkPhysicalDevice *devices = NULL; | ||
uint32_t num = 0; | ||
|
||
res = vkCreateInstance(&info, MPVK_ALLOCATOR, &inst); | ||
if (res != VK_SUCCESS) | ||
goto error; | ||
|
||
res = vkEnumeratePhysicalDevices(inst, &num, NULL); | ||
if (res != VK_SUCCESS) | ||
goto error; | ||
|
||
devices = talloc_array(NULL, VkPhysicalDevice, num); | ||
vkEnumeratePhysicalDevices(inst, &num, devices); | ||
if (res != VK_SUCCESS) | ||
goto error; | ||
|
||
bool help = bstr_equals0(param, "help"); | ||
if (help) { | ||
mp_info(log, "Available vulkan devices:\n"); | ||
ret = M_OPT_EXIT; | ||
} | ||
|
||
for (int i = 0; i < num; i++) { | ||
VkPhysicalDeviceProperties prop; | ||
vkGetPhysicalDeviceProperties(devices[i], &prop); | ||
|
||
if (help) { | ||
mp_info(log, " '%s' (GPU %d, ID %x:%x)\n", prop.deviceName, i, | ||
prop.vendorID, prop.deviceID); | ||
} else if (bstr_equals0(param, prop.deviceName)) { | ||
ret = 0; | ||
break; | ||
} | ||
} | ||
|
||
if (!help) | ||
mp_err(log, "No device with name '%.*s'!\n", BSTR_P(param)); | ||
|
||
error: | ||
talloc_free(devices); | ||
return ret; | ||
} | ||
|
||
#define OPT_BASE_STRUCT struct vk_priv | ||
|
||
const struct vo_driver video_out_vulkan = { | ||
.description = "Vulkan Renderer", | ||
.name = "vulkan", | ||
.preinit = preinit, | ||
.query_format = query_format, | ||
.reconfig = reconfig, | ||
.control = control, | ||
.get_image = get_image, | ||
.draw_frame = draw_frame, | ||
.flip_page = flip_page, | ||
.wait_events = wait_events, | ||
.wakeup = wakeup, | ||
.uninit = uninit, | ||
.priv_size = sizeof(struct vk_priv), | ||
.options = (const m_option_t[]) { | ||
OPT_FLAG("vulkan-debug", opts.debug, 0), | ||
OPT_FLAG("vulkan-sw", opts.allow_sw, 0), | ||
OPT_STRING_VALIDATE("vulkan-device", opts.device, 0, vk_validate_dev), | ||
OPT_INTRANGE("vulkan-swapchain-size", opts.swsize, 0, 1, | ||
MPVK_MAX_STREAMING_DEPTH), | ||
OPT_INTRANGE("vulkan-swapchain-depth", opts.swdepth, 0, 1, | ||
MPVK_MAX_STREAMING_DEPTH), | ||
{0} | ||
}, | ||
.priv_defaults = &(const struct vk_priv) { | ||
.opts = { | ||
.swsize = 8, | ||
.swdepth = 1, | ||
}, | ||
}, | ||
}; |
Oops, something went wrong.