mirror of
https://github.com/hyprwm/wlroots-hyprland.git
synced 2024-12-22 09:59:48 +01:00
render/vulkan: add Vulkan renderer
This new renderer is implemented with the existing wlr_renderer API (which is known to be sub-optimal for some operations). It's not used by default, but users can opt-in by setting WLR_RENDERER=vulkan. The renderer depends on VK_EXT_image_drm_format_modifier and VK_EXT_physical_device_drm. Co-authored-by: Simon Ser <contact@emersion.fr> Co-authored-by: Jan Beich <jbeich@FreeBSD.org>
This commit is contained in:
parent
2edf468aeb
commit
8e34692250
21 changed files with 3704 additions and 2 deletions
|
@ -2,11 +2,14 @@ image: alpine/edge
|
|||
packages:
|
||||
- eudev-dev
|
||||
- ffmpeg-dev
|
||||
- glslang
|
||||
- libinput-dev
|
||||
- libxkbcommon-dev
|
||||
- mesa-dev
|
||||
- meson
|
||||
- pixman-dev
|
||||
- vulkan-headers
|
||||
- vulkan-loader-dev
|
||||
- wayland-dev
|
||||
- wayland-protocols
|
||||
- xcb-util-image-dev
|
||||
|
|
|
@ -15,6 +15,9 @@ packages:
|
|||
- xcb-util-wm
|
||||
- xorg-xwayland
|
||||
- seatd
|
||||
- vulkan-icd-loader
|
||||
- vulkan-headers
|
||||
- glslang
|
||||
sources:
|
||||
- https://github.com/swaywm/wlroots
|
||||
tasks:
|
||||
|
|
|
@ -5,9 +5,12 @@ packages:
|
|||
- devel/libudev-devd
|
||||
- devel/meson # implies ninja
|
||||
- devel/pkgconf
|
||||
- graphics/glslang
|
||||
- graphics/libdrm
|
||||
- graphics/mesa-libs
|
||||
- graphics/png
|
||||
- graphics/vulkan-headers
|
||||
- graphics/vulkan-loader
|
||||
- graphics/wayland
|
||||
- graphics/wayland-protocols
|
||||
- multimedia/ffmpeg
|
||||
|
|
|
@ -18,6 +18,9 @@ endif
|
|||
if not features.get('gles2-renderer')
|
||||
exclude_files += ['render/egl.h', 'render/gles2.h']
|
||||
endif
|
||||
if not features.get('vulkan-renderer')
|
||||
exclude_files += 'render/vulkan.h'
|
||||
endif
|
||||
|
||||
install_subdir('wlr',
|
||||
install_dir: get_option('includedir'),
|
||||
|
|
312
include/render/vulkan.h
Normal file
312
include/render/vulkan.h
Normal file
|
@ -0,0 +1,312 @@
|
|||
#ifndef RENDER_VULKAN_H
|
||||
#define RENDER_VULKAN_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/render/wlr_texture.h>
|
||||
#include <wlr/render/drm_format_set.h>
|
||||
#include <wlr/render/interface.h>
|
||||
|
||||
struct wlr_vk_descriptor_pool;
|
||||
|
||||
// Central vulkan state that should only be needed once per compositor.
|
||||
struct wlr_vk_instance {
|
||||
VkInstance instance;
|
||||
VkDebugUtilsMessengerEXT messenger;
|
||||
|
||||
// enabled extensions
|
||||
size_t extension_count;
|
||||
const char **extensions;
|
||||
|
||||
struct {
|
||||
PFN_vkCreateDebugUtilsMessengerEXT createDebugUtilsMessengerEXT;
|
||||
PFN_vkDestroyDebugUtilsMessengerEXT destroyDebugUtilsMessengerEXT;
|
||||
} api;
|
||||
};
|
||||
|
||||
// Creates and initializes a vulkan instance.
|
||||
// Will try to enable the given extensions but not fail if they are not
|
||||
// available which can later be checked by the caller.
|
||||
// The debug parameter determines if validation layers are enabled and a
|
||||
// debug messenger created.
|
||||
// `compositor_name` and `compositor_version` are passed to the vulkan driver.
|
||||
struct wlr_vk_instance *vulkan_instance_create(size_t ext_count,
|
||||
const char **exts, bool debug);
|
||||
void vulkan_instance_destroy(struct wlr_vk_instance *ini);
|
||||
|
||||
// Logical vulkan device state.
|
||||
// Ownership can be shared by multiple renderers, reference counted
|
||||
// with `renderers`.
|
||||
struct wlr_vk_device {
|
||||
struct wlr_vk_instance *instance;
|
||||
|
||||
VkPhysicalDevice phdev;
|
||||
VkDevice dev;
|
||||
|
||||
int drm_fd;
|
||||
|
||||
// enabled extensions
|
||||
size_t extension_count;
|
||||
const char **extensions;
|
||||
|
||||
// we only ever need one queue for rendering and transfer commands
|
||||
uint32_t queue_family;
|
||||
VkQueue queue;
|
||||
|
||||
struct {
|
||||
PFN_vkGetMemoryFdPropertiesKHR getMemoryFdPropertiesKHR;
|
||||
} api;
|
||||
|
||||
uint32_t format_prop_count;
|
||||
struct wlr_vk_format_props *format_props;
|
||||
struct wlr_drm_format_set dmabuf_render_formats;
|
||||
struct wlr_drm_format_set dmabuf_texture_formats;
|
||||
|
||||
// supported formats for textures (contains only those formats
|
||||
// that support everything we need for textures)
|
||||
uint32_t shm_format_count;
|
||||
uint32_t *shm_formats; // to implement vulkan_get_shm_texture_formats
|
||||
};
|
||||
|
||||
// Tries to find the VkPhysicalDevice for the given drm fd.
|
||||
// Might find none and return VK_NULL_HANDLE.
|
||||
VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd);
|
||||
|
||||
// Creates a device for the given instance and physical device.
|
||||
// Will try to enable the given extensions but not fail if they are not
|
||||
// available which can later be checked by the caller.
|
||||
struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini,
|
||||
VkPhysicalDevice phdev, size_t ext_count, const char **exts);
|
||||
void vulkan_device_destroy(struct wlr_vk_device *dev);
|
||||
|
||||
// Tries to find any memory bit for the given vulkan device that
|
||||
// supports the given flags and is set in req_bits (e.g. if memory
|
||||
// type 2 is ok, (req_bits & (1 << 2)) must not be 0.
|
||||
// Set req_bits to 0xFFFFFFFF to allow all types.
|
||||
int vulkan_find_mem_type(struct wlr_vk_device *device,
|
||||
VkMemoryPropertyFlags flags, uint32_t req_bits);
|
||||
|
||||
struct wlr_vk_format {
|
||||
uint32_t drm_format;
|
||||
VkFormat vk_format;
|
||||
};
|
||||
|
||||
// Returns all known format mappings.
|
||||
// Might not be supported for gpu/usecase.
|
||||
const struct wlr_vk_format *vulkan_get_format_list(size_t *len);
|
||||
const struct wlr_vk_format *vulkan_get_format_from_drm(uint32_t drm_format);
|
||||
|
||||
struct wlr_vk_format_modifier_props {
|
||||
VkDrmFormatModifierPropertiesEXT props;
|
||||
VkExternalMemoryFeatureFlags dmabuf_flags;
|
||||
VkExtent2D max_extent;
|
||||
bool export_imported;
|
||||
};
|
||||
|
||||
struct wlr_vk_format_props {
|
||||
struct wlr_vk_format format;
|
||||
VkExtent2D max_extent; // relevant if not created as dma_buf
|
||||
VkFormatFeatureFlags features; // relevant if not created as dma_buf
|
||||
|
||||
uint32_t render_mod_count;
|
||||
struct wlr_vk_format_modifier_props *render_mods;
|
||||
|
||||
uint32_t texture_mod_count;
|
||||
struct wlr_vk_format_modifier_props *texture_mods;
|
||||
};
|
||||
|
||||
void vulkan_format_props_query(struct wlr_vk_device *dev,
|
||||
const struct wlr_vk_format *format);
|
||||
struct wlr_vk_format_modifier_props *vulkan_format_props_find_modifier(
|
||||
struct wlr_vk_format_props *props, uint64_t mod, bool render);
|
||||
void vulkan_format_props_finish(struct wlr_vk_format_props *props);
|
||||
|
||||
// For each format we want to render, we need a separate renderpass
|
||||
// and therefore also separate pipelines.
|
||||
struct wlr_vk_render_format_setup {
|
||||
struct wl_list link;
|
||||
VkFormat render_format; // used in renderpass
|
||||
VkRenderPass render_pass;
|
||||
|
||||
VkPipeline tex_pipe;
|
||||
VkPipeline quad_pipe;
|
||||
};
|
||||
|
||||
// Renderer-internal represenation of an wlr_buffer imported for rendering.
|
||||
struct wlr_vk_render_buffer {
|
||||
struct wlr_buffer *wlr_buffer;
|
||||
struct wlr_vk_renderer *renderer;
|
||||
struct wlr_vk_render_format_setup *render_setup;
|
||||
struct wl_list link; // wlr_vk_renderer.buffers
|
||||
|
||||
VkImage image;
|
||||
VkImageView image_view;
|
||||
VkFramebuffer framebuffer;
|
||||
uint32_t mem_count;
|
||||
VkDeviceMemory memories[WLR_DMABUF_MAX_PLANES];
|
||||
bool transitioned;
|
||||
|
||||
struct wl_listener buffer_destroy;
|
||||
};
|
||||
|
||||
// Vulkan wlr_renderer implementation on top of a wlr_vk_device.
|
||||
struct wlr_vk_renderer {
|
||||
struct wlr_renderer wlr_renderer;
|
||||
struct wlr_backend *backend;
|
||||
struct wlr_vk_device *dev;
|
||||
|
||||
VkCommandPool command_pool;
|
||||
|
||||
VkShaderModule vert_module;
|
||||
VkShaderModule tex_frag_module;
|
||||
VkShaderModule quad_frag_module;
|
||||
|
||||
VkDescriptorSetLayout ds_layout;
|
||||
VkPipelineLayout pipe_layout;
|
||||
VkSampler sampler;
|
||||
|
||||
VkFence fence;
|
||||
|
||||
struct wlr_vk_render_buffer *current_render_buffer;
|
||||
|
||||
// current frame id. Used in wlr_vk_texture.last_used
|
||||
// Increased every time a frame is ended for the renderer
|
||||
uint32_t frame;
|
||||
VkRect2D scissor; // needed for clearing
|
||||
|
||||
VkCommandBuffer cb;
|
||||
VkPipeline bound_pipe;
|
||||
|
||||
uint32_t render_width;
|
||||
uint32_t render_height;
|
||||
float projection[9];
|
||||
|
||||
size_t last_pool_size;
|
||||
struct wl_list descriptor_pools; // type wlr_vk_descriptor_pool
|
||||
struct wl_list render_format_setups;
|
||||
|
||||
struct wl_list textures; // wlr_gles2_texture.link
|
||||
struct wl_list destroy_textures; // wlr_vk_texture to destroy after frame
|
||||
struct wl_list foreign_textures; // wlr_vk_texture to return to foreign queue
|
||||
|
||||
struct wl_list render_buffers; // wlr_vk_render_buffer
|
||||
|
||||
struct {
|
||||
VkCommandBuffer cb;
|
||||
bool recording;
|
||||
struct wl_list buffers; // type wlr_vk_shared_buffer
|
||||
} stage;
|
||||
};
|
||||
|
||||
// Creates a vulkan renderer for the given device.
|
||||
struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev);
|
||||
|
||||
// stage utility - for uploading/retrieving data
|
||||
// Gets an command buffer in recording state which is guaranteed to be
|
||||
// executed before the next frame.
|
||||
VkCommandBuffer vulkan_record_stage_cb(struct wlr_vk_renderer *renderer);
|
||||
|
||||
// Submits the current stage command buffer and waits until it has
|
||||
// finished execution.
|
||||
bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer);
|
||||
|
||||
// Suballocates a buffer span with the given size that can be mapped
|
||||
// and used as staging buffer. The allocation is implicitly released when the
|
||||
// stage cb has finished execution.
|
||||
struct wlr_vk_buffer_span vulkan_get_stage_span(
|
||||
struct wlr_vk_renderer *renderer, VkDeviceSize size);
|
||||
|
||||
// Tries to allocate a texture descriptor set. Will additionally
|
||||
// return the pool it was allocated from when successful (for freeing it later).
|
||||
struct wlr_vk_descriptor_pool *vulkan_alloc_texture_ds(
|
||||
struct wlr_vk_renderer *renderer, VkDescriptorSet *ds);
|
||||
|
||||
// Frees the given descriptor set from the pool its pool.
|
||||
void vulkan_free_ds(struct wlr_vk_renderer *renderer,
|
||||
struct wlr_vk_descriptor_pool *pool, VkDescriptorSet ds);
|
||||
struct wlr_vk_format_props *vulkan_format_props_from_drm(
|
||||
struct wlr_vk_device *dev, uint32_t drm_format);
|
||||
struct wlr_vk_renderer *vulkan_get_renderer(struct wlr_renderer *r);
|
||||
|
||||
// State (e.g. image texture) associated with a surface.
|
||||
struct wlr_vk_texture {
|
||||
struct wlr_texture wlr_texture;
|
||||
struct wlr_vk_renderer *renderer;
|
||||
uint32_t mem_count;
|
||||
VkDeviceMemory memories[WLR_DMABUF_MAX_PLANES];
|
||||
VkImage image;
|
||||
VkImageView image_view;
|
||||
const struct wlr_vk_format *format;
|
||||
VkDescriptorSet ds;
|
||||
struct wlr_vk_descriptor_pool *ds_pool;
|
||||
uint32_t last_used; // to track when it can be destroyed
|
||||
bool dmabuf_imported;
|
||||
bool owned; // if dmabuf_imported: whether we have ownership of the image
|
||||
bool transitioned; // if dma_imported: whether we transitioned it away from preinit
|
||||
bool invert_y; // if dma_imported: whether we must flip y
|
||||
struct wl_list foreign_link;
|
||||
struct wl_list destroy_link;
|
||||
struct wl_list link; // wlr_gles2_renderer.textures
|
||||
|
||||
// If imported from a wlr_buffer
|
||||
struct wlr_buffer *buffer;
|
||||
struct wl_listener buffer_destroy;
|
||||
};
|
||||
|
||||
struct wlr_vk_texture *vulkan_get_texture(struct wlr_texture *wlr_texture);
|
||||
VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer,
|
||||
const struct wlr_dmabuf_attributes *attribs,
|
||||
VkDeviceMemory mems[static WLR_DMABUF_MAX_PLANES], uint32_t *n_mems,
|
||||
bool for_render);
|
||||
struct wlr_texture *vulkan_texture_from_buffer(
|
||||
struct wlr_renderer *wlr_renderer, struct wlr_buffer *buffer);
|
||||
void vulkan_texture_destroy(struct wlr_vk_texture *texture);
|
||||
|
||||
struct wlr_vk_descriptor_pool {
|
||||
VkDescriptorPool pool;
|
||||
uint32_t free; // number of textures that can be allocated
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
struct wlr_vk_allocation {
|
||||
VkDeviceSize start;
|
||||
VkDeviceSize size;
|
||||
};
|
||||
|
||||
// List of suballocated staging buffers.
|
||||
// Used to upload to/read from device local images.
|
||||
struct wlr_vk_shared_buffer {
|
||||
struct wl_list link;
|
||||
VkBuffer buffer;
|
||||
VkDeviceMemory memory;
|
||||
VkDeviceSize buf_size;
|
||||
|
||||
size_t allocs_size;
|
||||
size_t allocs_capacity;
|
||||
struct wlr_vk_allocation *allocs;
|
||||
};
|
||||
|
||||
// Suballocated range on a buffer.
|
||||
struct wlr_vk_buffer_span {
|
||||
struct wlr_vk_shared_buffer *buffer;
|
||||
struct wlr_vk_allocation alloc;
|
||||
};
|
||||
|
||||
// util
|
||||
bool vulkan_has_extension(size_t count, const char **exts, const char *find);
|
||||
const char *vulkan_strerror(VkResult err);
|
||||
void vulkan_change_layout(VkCommandBuffer cb, VkImage img,
|
||||
VkImageLayout ol, VkPipelineStageFlags srcs, VkAccessFlags srca,
|
||||
VkImageLayout nl, VkPipelineStageFlags dsts, VkAccessFlags dsta);
|
||||
void vulkan_change_layout_queue(VkCommandBuffer cb, VkImage img,
|
||||
VkImageLayout ol, VkPipelineStageFlags srcs, VkAccessFlags srca,
|
||||
VkImageLayout nl, VkPipelineStageFlags dsts, VkAccessFlags dsta,
|
||||
uint32_t src_family, uint32_t dst_family);
|
||||
|
||||
#define wlr_vk_error(fmt, res, ...) wlr_log(WLR_ERROR, fmt ": %s (%d)", \
|
||||
vulkan_strerror(res), res, ##__VA_ARGS__)
|
||||
|
||||
#endif // RENDER_VULKAN_H
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#mesondefine WLR_HAS_GLES2_RENDERER
|
||||
|
||||
#mesondefine WLR_HAS_VULKAN_RENDERER
|
||||
|
||||
#mesondefine WLR_HAS_XWAYLAND
|
||||
|
||||
#endif
|
||||
|
|
18
include/wlr/render/vulkan.h
Normal file
18
include/wlr/render/vulkan.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* This an unstable interface of wlroots. No guarantees are made regarding the
|
||||
* future consistency of this API.
|
||||
*/
|
||||
#ifndef WLR_USE_UNSTABLE
|
||||
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
|
||||
#endif
|
||||
|
||||
#ifndef WLR_RENDER_VULKAN_H
|
||||
#define WLR_RENDER_VULKAN_H
|
||||
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
|
||||
struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(int drm_fd);
|
||||
bool wlr_texture_is_vk(struct wlr_texture *texture);
|
||||
|
||||
#endif
|
||||
|
|
@ -91,6 +91,7 @@ features = {
|
|||
'libinput-backend': false,
|
||||
'xwayland': false,
|
||||
'gles2-renderer': false,
|
||||
'vulkan-renderer': false,
|
||||
}
|
||||
internal_features = {
|
||||
'xcb-errors': false,
|
||||
|
|
|
@ -2,5 +2,5 @@ option('xcb-errors', type: 'feature', value: 'auto', description: 'Use xcb-error
|
|||
option('xwayland', type: 'feature', value: 'auto', yield: true, description: 'Enable support for X11 applications')
|
||||
option('examples', type: 'boolean', value: true, description: 'Build example applications')
|
||||
option('icon_directory', description: 'Location used to look for cursors (default: ${datadir}/icons)', type: 'string', value: '')
|
||||
option('renderers', type: 'array', choices: ['auto', 'gles2'], value: ['auto'], description: 'Select built-in renderers')
|
||||
option('renderers', type: 'array', choices: ['auto', 'gles2', 'vulkan'], value: ['auto'], description: 'Select built-in renderers')
|
||||
option('backends', type: 'array', choices: ['auto', 'drm', 'libinput', 'x11'], value: ['auto'], description: 'Select built-in backends')
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
renderers = get_option('renderers')
|
||||
if 'auto' in renderers and get_option('auto_features').enabled()
|
||||
renderers = ['gles2']
|
||||
renderers = ['gles2', 'vulkan']
|
||||
elif 'auto' in renderers and get_option('auto_features').disabled()
|
||||
renderers = []
|
||||
endif
|
||||
|
@ -24,6 +24,10 @@ if 'gles2' in renderers or 'auto' in renderers
|
|||
subdir('gles2')
|
||||
endif
|
||||
|
||||
if 'vulkan' in renderers or 'auto' in renderers
|
||||
subdir('vulkan')
|
||||
endif
|
||||
|
||||
subdir('pixman')
|
||||
|
||||
subdir('allocator')
|
||||
|
|
38
render/vulkan/meson.build
Normal file
38
render/vulkan/meson.build
Normal file
|
@ -0,0 +1,38 @@
|
|||
msg = []
|
||||
if 'vulkan' in renderers
|
||||
msg += 'Install "@0@" or pass "-Dvulkan=disabled" to disable it.'
|
||||
else
|
||||
msg += 'Required for vulkan renderer support.'
|
||||
endif
|
||||
|
||||
dep_vulkan = dependency('vulkan',
|
||||
version: '>=1.2.182',
|
||||
required: 'vulkan' in renderers,
|
||||
not_found_message: '\n'.join(msg).format('vulkan')
|
||||
)
|
||||
|
||||
if not dep_vulkan.found()
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
glslang = find_program('glslangValidator', native: true, required: false)
|
||||
if not glslang.found()
|
||||
if 'vulkan' in renderers
|
||||
error('\n'.join(msg).format('glslang'))
|
||||
else
|
||||
subdir_done()
|
||||
endif
|
||||
endif
|
||||
|
||||
wlr_files += files(
|
||||
'renderer.c',
|
||||
'texture.c',
|
||||
'vulkan.c',
|
||||
'util.c',
|
||||
'pixel_format.c',
|
||||
)
|
||||
|
||||
wlr_deps += dep_vulkan
|
||||
features += { 'vulkan-renderer': true }
|
||||
|
||||
subdir('shaders')
|
325
render/vulkan/pixel_format.c
Normal file
325
render/vulkan/pixel_format.c
Normal file
|
@ -0,0 +1,325 @@
|
|||
#include <drm_fourcc.h>
|
||||
#include <stdlib.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "render/vulkan.h"
|
||||
|
||||
// Reversed endianess of shm and vulkan format names
|
||||
static const struct wlr_vk_format formats[] = {
|
||||
{
|
||||
.drm_format = DRM_FORMAT_ARGB8888,
|
||||
.vk_format = VK_FORMAT_B8G8R8A8_SRGB,
|
||||
},
|
||||
{
|
||||
.drm_format = DRM_FORMAT_XRGB8888,
|
||||
.vk_format = VK_FORMAT_B8G8R8A8_SRGB,
|
||||
},
|
||||
{
|
||||
.drm_format = DRM_FORMAT_XBGR8888,
|
||||
.vk_format = VK_FORMAT_R8G8B8A8_SRGB,
|
||||
},
|
||||
{
|
||||
.drm_format = DRM_FORMAT_ABGR8888,
|
||||
.vk_format = VK_FORMAT_R8G8B8A8_SRGB,
|
||||
},
|
||||
};
|
||||
|
||||
const struct wlr_vk_format *vulkan_get_format_list(size_t *len) {
|
||||
*len = sizeof(formats) / sizeof(formats[0]);
|
||||
return formats;
|
||||
}
|
||||
|
||||
const struct wlr_vk_format *vulkan_get_format_from_drm(uint32_t drm_format) {
|
||||
for (unsigned i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) {
|
||||
if (formats[i].drm_format == drm_format) {
|
||||
return &formats[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const VkImageUsageFlags render_usage =
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
static const VkImageUsageFlags tex_usage =
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
static const VkImageUsageFlags dma_tex_usage =
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
|
||||
static const VkFormatFeatureFlags tex_features =
|
||||
VK_FORMAT_FEATURE_TRANSFER_SRC_BIT |
|
||||
VK_FORMAT_FEATURE_TRANSFER_DST_BIT |
|
||||
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
|
||||
// NOTE: we don't strictly require this, we could create a NEAREST
|
||||
// sampler for formats that need it, in case this ever makes problems.
|
||||
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
|
||||
static const VkFormatFeatureFlags render_features =
|
||||
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT |
|
||||
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT;
|
||||
static const VkFormatFeatureFlags dma_tex_features =
|
||||
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
|
||||
// NOTE: we don't strictly require this, we could create a NEAREST
|
||||
// sampler for formats that need it, in case this ever makes problems.
|
||||
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
|
||||
|
||||
static bool query_modifier_support(struct wlr_vk_device *dev,
|
||||
struct wlr_vk_format_props *props, size_t modifier_count,
|
||||
VkPhysicalDeviceImageFormatInfo2 fmti) {
|
||||
VkResult res;
|
||||
|
||||
VkFormatProperties2 fmtp = {0};
|
||||
fmtp.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
|
||||
|
||||
VkDrmFormatModifierPropertiesListEXT modp = {0};
|
||||
modp.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT;
|
||||
modp.drmFormatModifierCount = modifier_count;
|
||||
fmtp.pNext = &modp;
|
||||
|
||||
// the first call to vkGetPhysicalDeviceFormatProperties2 did only
|
||||
// retrieve the number of modifiers, we now have to retrieve
|
||||
// the modifiers
|
||||
modp.pDrmFormatModifierProperties = calloc(modifier_count,
|
||||
sizeof(*modp.pDrmFormatModifierProperties));
|
||||
if (!modp.pDrmFormatModifierProperties) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
vkGetPhysicalDeviceFormatProperties2(dev->phdev,
|
||||
props->format.vk_format, &fmtp);
|
||||
|
||||
props->render_mods = calloc(modp.drmFormatModifierCount,
|
||||
sizeof(*props->render_mods));
|
||||
if (!props->render_mods) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
free(modp.pDrmFormatModifierProperties);
|
||||
return false;
|
||||
}
|
||||
|
||||
props->texture_mods = calloc(modp.drmFormatModifierCount,
|
||||
sizeof(*props->texture_mods));
|
||||
if (!props->texture_mods) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
free(modp.pDrmFormatModifierProperties);
|
||||
free(props->render_mods);
|
||||
return false;
|
||||
}
|
||||
|
||||
// detailed check
|
||||
// format info
|
||||
// only added if dmabuf/drm_fmt_ext supported
|
||||
VkPhysicalDeviceExternalImageFormatInfo efmti = {0};
|
||||
efmti.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO;
|
||||
efmti.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
|
||||
|
||||
fmti.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
|
||||
fmti.pNext = &efmti;
|
||||
|
||||
VkPhysicalDeviceImageDrmFormatModifierInfoEXT modi = {0};
|
||||
modi.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT;
|
||||
modi.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
efmti.pNext = &modi;
|
||||
|
||||
// format properties
|
||||
VkExternalImageFormatProperties efmtp = {0};
|
||||
efmtp.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES;
|
||||
|
||||
VkImageFormatProperties2 ifmtp = {0};
|
||||
ifmtp.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
|
||||
ifmtp.pNext = &efmtp;
|
||||
const VkExternalMemoryProperties *emp = &efmtp.externalMemoryProperties;
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (unsigned i = 0u; i < modp.drmFormatModifierCount; ++i) {
|
||||
VkDrmFormatModifierPropertiesEXT m =
|
||||
modp.pDrmFormatModifierProperties[i];
|
||||
wlr_log(WLR_DEBUG, " modifier: 0x%"PRIx64 ": features 0x%"PRIx32", %d planes",
|
||||
m.drmFormatModifier, m.drmFormatModifierTilingFeatures,
|
||||
m.drmFormatModifierPlaneCount);
|
||||
|
||||
// check that specific modifier for render usage
|
||||
if ((m.drmFormatModifierTilingFeatures & render_features) == render_features) {
|
||||
fmti.usage = render_usage;
|
||||
|
||||
modi.drmFormatModifier = m.drmFormatModifier;
|
||||
res = vkGetPhysicalDeviceImageFormatProperties2(
|
||||
dev->phdev, &fmti, &ifmtp);
|
||||
if (res != VK_SUCCESS) {
|
||||
if (res != VK_ERROR_FORMAT_NOT_SUPPORTED) {
|
||||
wlr_vk_error("vkGetPhysicalDeviceImageFormatProperties2",
|
||||
res);
|
||||
}
|
||||
|
||||
wlr_log(WLR_DEBUG, " >> rendering: format not supported");
|
||||
} else if (emp->externalMemoryFeatures &
|
||||
VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT) {
|
||||
unsigned c = props->render_mod_count;
|
||||
VkExtent3D me = ifmtp.imageFormatProperties.maxExtent;
|
||||
VkExternalMemoryProperties emp = efmtp.externalMemoryProperties;
|
||||
props->render_mods[c].props = m;
|
||||
props->render_mods[c].max_extent.width = me.width;
|
||||
props->render_mods[c].max_extent.height = me.height;
|
||||
props->render_mods[c].dmabuf_flags = emp.externalMemoryFeatures;
|
||||
props->render_mods[c].export_imported =
|
||||
(emp.exportFromImportedHandleTypes &
|
||||
VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT);
|
||||
++props->render_mod_count;
|
||||
|
||||
found = true;
|
||||
wlr_drm_format_set_add(&dev->dmabuf_render_formats,
|
||||
props->format.drm_format, m.drmFormatModifier);
|
||||
|
||||
wlr_log(WLR_DEBUG, " >> rendering: supported");
|
||||
} else {
|
||||
wlr_log(WLR_DEBUG, " >> rendering: importing not supported");
|
||||
}
|
||||
} else {
|
||||
wlr_log(WLR_DEBUG, " >> rendering: format features not supported");
|
||||
}
|
||||
|
||||
// check that specific modifier for texture usage
|
||||
if ((m.drmFormatModifierTilingFeatures & dma_tex_features) == dma_tex_features) {
|
||||
fmti.usage = dma_tex_usage;
|
||||
|
||||
modi.drmFormatModifier = m.drmFormatModifier;
|
||||
res = vkGetPhysicalDeviceImageFormatProperties2(
|
||||
dev->phdev, &fmti, &ifmtp);
|
||||
if (res != VK_SUCCESS) {
|
||||
if (res != VK_ERROR_FORMAT_NOT_SUPPORTED) {
|
||||
wlr_vk_error("vkGetPhysicalDeviceImageFormatProperties2",
|
||||
res);
|
||||
}
|
||||
|
||||
wlr_log(WLR_DEBUG, " >> dmatex: format not supported");
|
||||
} else if (emp->externalMemoryFeatures &
|
||||
VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT) {
|
||||
unsigned c = props->texture_mod_count;
|
||||
VkExtent3D me = ifmtp.imageFormatProperties.maxExtent;
|
||||
VkExternalMemoryProperties emp = efmtp.externalMemoryProperties;
|
||||
props->texture_mods[c].props = m;
|
||||
props->texture_mods[c].max_extent.width = me.width;
|
||||
props->texture_mods[c].max_extent.height = me.height;
|
||||
props->texture_mods[c].dmabuf_flags = emp.externalMemoryFeatures;
|
||||
props->texture_mods[c].export_imported =
|
||||
(emp.exportFromImportedHandleTypes &
|
||||
VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT);
|
||||
++props->texture_mod_count;
|
||||
|
||||
found = true;
|
||||
wlr_drm_format_set_add(&dev->dmabuf_texture_formats,
|
||||
props->format.drm_format, m.drmFormatModifier);
|
||||
|
||||
wlr_log(WLR_DEBUG, " >> dmatex: supported");
|
||||
} else {
|
||||
wlr_log(WLR_DEBUG, " >> dmatex: importing not supported");
|
||||
}
|
||||
} else {
|
||||
wlr_log(WLR_DEBUG, " >> dmatex: format features not supported");
|
||||
}
|
||||
}
|
||||
|
||||
free(modp.pDrmFormatModifierProperties);
|
||||
return found;
|
||||
}
|
||||
|
||||
void vulkan_format_props_query(struct wlr_vk_device *dev,
|
||||
const struct wlr_vk_format *format) {
|
||||
|
||||
wlr_log(WLR_DEBUG, "vulkan: Checking support for format %.4s (0x%" PRIx32 ")",
|
||||
(const char *)&format->drm_format, format->drm_format);
|
||||
VkResult res;
|
||||
|
||||
// get general features and modifiers
|
||||
VkFormatProperties2 fmtp = {0};
|
||||
fmtp.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
|
||||
|
||||
VkDrmFormatModifierPropertiesListEXT modp = {0};
|
||||
modp.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT;
|
||||
fmtp.pNext = &modp;
|
||||
|
||||
vkGetPhysicalDeviceFormatProperties2(dev->phdev,
|
||||
format->vk_format, &fmtp);
|
||||
|
||||
// detailed check
|
||||
VkPhysicalDeviceImageFormatInfo2 fmti = {0};
|
||||
fmti.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
|
||||
fmti.type = VK_IMAGE_TYPE_2D;
|
||||
fmti.format = format->vk_format;
|
||||
|
||||
VkImageFormatProperties2 ifmtp = {0};
|
||||
ifmtp.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
|
||||
|
||||
bool add_fmt_props = false;
|
||||
struct wlr_vk_format_props props = {0};
|
||||
props.format = *format;
|
||||
|
||||
wlr_log(WLR_DEBUG, " drmFormatModifierCount: %d", modp.drmFormatModifierCount);
|
||||
if (modp.drmFormatModifierCount > 0) {
|
||||
add_fmt_props |= query_modifier_support(dev, &props,
|
||||
modp.drmFormatModifierCount, fmti);
|
||||
}
|
||||
|
||||
// non-dmabuf texture properties
|
||||
if (fmtp.formatProperties.optimalTilingFeatures & tex_features) {
|
||||
fmti.pNext = NULL;
|
||||
ifmtp.pNext = NULL;
|
||||
fmti.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
fmti.usage = tex_usage;
|
||||
|
||||
res = vkGetPhysicalDeviceImageFormatProperties2(
|
||||
dev->phdev, &fmti, &ifmtp);
|
||||
if (res != VK_SUCCESS) {
|
||||
if (res != VK_ERROR_FORMAT_NOT_SUPPORTED) {
|
||||
wlr_vk_error("vkGetPhysicalDeviceImageFormatProperties2",
|
||||
res);
|
||||
}
|
||||
|
||||
wlr_log(WLR_DEBUG, " >> shmtex: format not supported");
|
||||
} else {
|
||||
VkExtent3D me = ifmtp.imageFormatProperties.maxExtent;
|
||||
props.max_extent.width = me.width;
|
||||
props.max_extent.height = me.height;
|
||||
props.features = fmtp.formatProperties.optimalTilingFeatures;
|
||||
|
||||
wlr_log(WLR_DEBUG, " >> shmtex: supported");
|
||||
|
||||
dev->shm_formats[dev->shm_format_count] = format->drm_format;
|
||||
++dev->shm_format_count;
|
||||
|
||||
add_fmt_props = true;
|
||||
}
|
||||
} else {
|
||||
wlr_log(WLR_DEBUG, " >> shmtex: format features not supported");
|
||||
}
|
||||
|
||||
if (add_fmt_props) {
|
||||
dev->format_props[dev->format_prop_count] = props;
|
||||
++dev->format_prop_count;
|
||||
}
|
||||
}
|
||||
|
||||
void vulkan_format_props_finish(struct wlr_vk_format_props *props) {
|
||||
free(props->texture_mods);
|
||||
free(props->render_mods);
|
||||
}
|
||||
|
||||
struct wlr_vk_format_modifier_props *vulkan_format_props_find_modifier(
|
||||
struct wlr_vk_format_props *props, uint64_t mod, bool render) {
|
||||
if (render) {
|
||||
for (unsigned i = 0u; i < props->render_mod_count; ++i) {
|
||||
if (props->render_mods[i].props.drmFormatModifier == mod) {
|
||||
return &props->render_mods[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (unsigned i = 0u; i < props->texture_mod_count; ++i) {
|
||||
if (props->texture_mods[i].props.drmFormatModifier == mod) {
|
||||
return &props->texture_mods[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
1540
render/vulkan/renderer.c
Normal file
1540
render/vulkan/renderer.c
Normal file
File diff suppressed because it is too large
Load diff
25
render/vulkan/shaders/common.vert
Normal file
25
render/vulkan/shaders/common.vert
Normal file
|
@ -0,0 +1,25 @@
|
|||
#version 450
|
||||
|
||||
// we use a mat4 since it uses the same size as mat3 due to
|
||||
// alignment. Easier to deal with (tighly-packed) mat4 though.
|
||||
layout(push_constant, row_major) uniform UBO {
|
||||
mat4 proj;
|
||||
vec2 uv_offset;
|
||||
vec2 uv_size;
|
||||
} data;
|
||||
|
||||
layout(location = 0) out vec2 uv;
|
||||
|
||||
// 4 outlining points and uv coords
|
||||
const vec2[] values = {
|
||||
{0, 0},
|
||||
{1, 0},
|
||||
{1, 1},
|
||||
{0, 1},
|
||||
};
|
||||
|
||||
void main() {
|
||||
vec2 pos = values[gl_VertexIndex % 4];
|
||||
uv = data.uv_offset + pos * data.uv_size;
|
||||
gl_Position = data.proj * vec4(pos, 0.0, 1.0);
|
||||
}
|
20
render/vulkan/shaders/meson.build
Normal file
20
render/vulkan/shaders/meson.build
Normal file
|
@ -0,0 +1,20 @@
|
|||
vulkan_shaders_src = [
|
||||
'common.vert',
|
||||
'texture.frag',
|
||||
'quad.frag',
|
||||
]
|
||||
|
||||
vulkan_shaders = []
|
||||
foreach shader : vulkan_shaders_src
|
||||
name = shader.underscorify() + '_data'
|
||||
args = [glslang, '-V', '@INPUT@', '-o', '@OUTPUT@', '--vn', name]
|
||||
header = custom_target(
|
||||
shader + '_spv',
|
||||
output: shader + '.h',
|
||||
input: shader,
|
||||
command: args)
|
||||
|
||||
vulkan_shaders += [header]
|
||||
endforeach
|
||||
|
||||
wlr_files += vulkan_shaders
|
10
render/vulkan/shaders/quad.frag
Normal file
10
render/vulkan/shaders/quad.frag
Normal file
|
@ -0,0 +1,10 @@
|
|||
#version 450
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(push_constant) uniform UBO {
|
||||
layout(offset = 80) vec4 color;
|
||||
} data;
|
||||
|
||||
void main() {
|
||||
out_color = data.color;
|
||||
}
|
25
render/vulkan/shaders/texture.frag
Normal file
25
render/vulkan/shaders/texture.frag
Normal file
|
@ -0,0 +1,25 @@
|
|||
#version 450
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D tex;
|
||||
|
||||
layout(location = 0) in vec2 uv;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
layout(push_constant) uniform UBO {
|
||||
layout(offset = 80) float alpha;
|
||||
} data;
|
||||
|
||||
void main() {
|
||||
out_color = textureLod(tex, uv, 0);
|
||||
|
||||
// We expect this shader to output pre-alpha-multiplied color values.
|
||||
// alpha < 0.0 means that this shader should ignore the texture's alpha
|
||||
// value.
|
||||
if (data.alpha < 0.0) {
|
||||
out_color.a = -data.alpha;
|
||||
out_color.rgb *= -data.alpha;
|
||||
} else {
|
||||
out_color *= data.alpha;
|
||||
}
|
||||
}
|
||||
|
718
render/vulkan/texture.c
Normal file
718
render/vulkan/texture.c
Normal file
|
@ -0,0 +1,718 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <wlr/render/wlr_texture.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "render/pixel_format.h"
|
||||
#include "render/vulkan.h"
|
||||
|
||||
static const struct wlr_texture_impl texture_impl;
|
||||
|
||||
struct wlr_vk_texture *vulkan_get_texture(struct wlr_texture *wlr_texture) {
|
||||
assert(wlr_texture->impl == &texture_impl);
|
||||
return (struct wlr_vk_texture *)wlr_texture;
|
||||
}
|
||||
|
||||
static VkImageAspectFlagBits mem_plane_aspect(unsigned i) {
|
||||
switch (i) {
|
||||
case 0: return VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT;
|
||||
case 1: return VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT;
|
||||
case 2: return VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT;
|
||||
case 3: return VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT;
|
||||
default: abort(); // unreachable
|
||||
}
|
||||
}
|
||||
|
||||
static bool vulkan_texture_is_opaque(struct wlr_texture *wlr_texture) {
|
||||
struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture);
|
||||
const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(
|
||||
texture->format->drm_format);
|
||||
assert(format_info);
|
||||
return !format_info->has_alpha;
|
||||
}
|
||||
|
||||
// Will transition the texture to shaderReadOnlyOptimal layout for reading
|
||||
// from fragment shader later on
|
||||
static bool write_pixels(struct wlr_texture *wlr_texture,
|
||||
uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x,
|
||||
uint32_t src_y, uint32_t dst_x, uint32_t dst_y, const void *vdata,
|
||||
VkImageLayout old_layout, VkPipelineStageFlags src_stage,
|
||||
VkAccessFlags src_access) {
|
||||
VkResult res;
|
||||
struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture);
|
||||
struct wlr_vk_renderer *renderer = texture->renderer;
|
||||
VkDevice dev = texture->renderer->dev->dev;
|
||||
|
||||
// make sure assumptions are met
|
||||
assert(src_x + width <= texture->wlr_texture.width);
|
||||
assert(src_y + height <= texture->wlr_texture.height);
|
||||
assert(dst_x + width <= texture->wlr_texture.width);
|
||||
assert(dst_y + height <= texture->wlr_texture.height);
|
||||
|
||||
const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(
|
||||
texture->format->drm_format);
|
||||
assert(format_info);
|
||||
|
||||
// deferred upload by transfer; using staging buffer
|
||||
// calculate maximum side needed
|
||||
uint32_t bsize = 0;
|
||||
unsigned bytespb = format_info->bpp / 8;
|
||||
bsize += height * bytespb * width;
|
||||
|
||||
// get staging buffer
|
||||
struct wlr_vk_buffer_span span = vulkan_get_stage_span(renderer, bsize);
|
||||
if (!span.buffer || span.alloc.size != bsize) {
|
||||
wlr_log(WLR_ERROR, "Failed to retrieve staging buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
void *vmap;
|
||||
res = vkMapMemory(dev, span.buffer->memory, span.alloc.start,
|
||||
bsize, 0, &vmap);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkMapMemory", res);
|
||||
return false;
|
||||
}
|
||||
char *map = (char *)vmap;
|
||||
|
||||
// record staging cb
|
||||
// will be executed before next frame
|
||||
VkCommandBuffer cb = vulkan_record_stage_cb(renderer);
|
||||
vulkan_change_layout(cb, texture->image,
|
||||
old_layout, src_stage, src_access,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
|
||||
// upload data
|
||||
const char *pdata = vdata; // data iterator
|
||||
|
||||
uint32_t packed_stride = bytespb * width;
|
||||
uint32_t buf_off = span.alloc.start + (map - (char *)vmap);
|
||||
|
||||
// write data into staging buffer span
|
||||
pdata += stride * src_y;
|
||||
pdata += bytespb * src_x;
|
||||
if (src_x == 0 && width == texture->wlr_texture.width &&
|
||||
stride == packed_stride) {
|
||||
memcpy(map, pdata, packed_stride * height);
|
||||
map += packed_stride * height;
|
||||
} else {
|
||||
for (unsigned i = 0u; i < height; ++i) {
|
||||
memcpy(map, pdata, packed_stride);
|
||||
pdata += stride;
|
||||
map += packed_stride;
|
||||
}
|
||||
}
|
||||
|
||||
VkBufferImageCopy copy;
|
||||
copy.imageExtent.width = width;
|
||||
copy.imageExtent.height = height;
|
||||
copy.imageExtent.depth = 1;
|
||||
copy.imageOffset.x = dst_x;
|
||||
copy.imageOffset.y = dst_y;
|
||||
copy.imageOffset.z = 0;
|
||||
copy.bufferOffset = buf_off;
|
||||
copy.bufferRowLength = width;
|
||||
copy.bufferImageHeight = height;
|
||||
copy.imageSubresource.mipLevel = 0;
|
||||
copy.imageSubresource.baseArrayLayer = 0;
|
||||
copy.imageSubresource.layerCount = 1;
|
||||
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
|
||||
assert((uint32_t)(map - (char *)vmap) == bsize);
|
||||
vkUnmapMemory(dev, span.buffer->memory);
|
||||
|
||||
vkCmdCopyBufferToImage(cb, span.buffer->buffer, texture->image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©);
|
||||
vulkan_change_layout(cb, texture->image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||
texture->last_used = renderer->frame;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool vulkan_texture_write_pixels(struct wlr_texture *wlr_texture,
|
||||
uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x,
|
||||
uint32_t src_y, uint32_t dst_x, uint32_t dst_y, const void *vdata) {
|
||||
return write_pixels(wlr_texture, stride, width, height, src_x, src_y,
|
||||
dst_x, dst_y, vdata, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||
}
|
||||
|
||||
void vulkan_texture_destroy(struct wlr_vk_texture *texture) {
|
||||
if (!texture->renderer) {
|
||||
free(texture);
|
||||
return;
|
||||
}
|
||||
|
||||
// when we recorded a command to fill this image _this_ frame,
|
||||
// it has to be executed before the texture can be destroyed.
|
||||
// Add it to the renderer->destroy_textures list, destroying
|
||||
// _after_ the stage command buffer has exectued
|
||||
if (texture->last_used == texture->renderer->frame) {
|
||||
assert(texture->destroy_link.next == NULL); // not already inserted
|
||||
wl_list_insert(&texture->renderer->destroy_textures,
|
||||
&texture->destroy_link);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_list_remove(&texture->link);
|
||||
wl_list_remove(&texture->buffer_destroy.link);
|
||||
|
||||
VkDevice dev = texture->renderer->dev->dev;
|
||||
if (texture->ds && texture->ds_pool) {
|
||||
vulkan_free_ds(texture->renderer, texture->ds_pool, texture->ds);
|
||||
}
|
||||
|
||||
vkDestroyImageView(dev, texture->image_view, NULL);
|
||||
vkDestroyImage(dev, texture->image, NULL);
|
||||
|
||||
for (unsigned i = 0u; i < texture->mem_count; ++i) {
|
||||
vkFreeMemory(dev, texture->memories[i], NULL);
|
||||
}
|
||||
|
||||
free(texture);
|
||||
}
|
||||
|
||||
static void vulkan_texture_unref(struct wlr_texture *wlr_texture) {
|
||||
struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture);
|
||||
if (texture->buffer != NULL) {
|
||||
// Keep the texture around, in case the buffer is re-used later. We're
|
||||
// still listening to the buffer's destroy event.
|
||||
wlr_buffer_unlock(texture->buffer);
|
||||
} else {
|
||||
vulkan_texture_destroy(texture);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wlr_texture_impl texture_impl = {
|
||||
.is_opaque = vulkan_texture_is_opaque,
|
||||
.write_pixels = vulkan_texture_write_pixels,
|
||||
.destroy = vulkan_texture_unref,
|
||||
};
|
||||
|
||||
static struct wlr_vk_texture *vulkan_texture_create(
|
||||
struct wlr_vk_renderer *renderer, uint32_t width, uint32_t height) {
|
||||
struct wlr_vk_texture *texture =
|
||||
calloc(1, sizeof(struct wlr_vk_texture));
|
||||
if (texture == NULL) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
wlr_texture_init(&texture->wlr_texture, &texture_impl, width, height);
|
||||
texture->renderer = renderer;
|
||||
wl_list_insert(&renderer->textures, &texture->link);
|
||||
wl_list_init(&texture->buffer_destroy.link);
|
||||
return texture;
|
||||
}
|
||||
|
||||
static struct wlr_texture *vulkan_texture_from_pixels(struct wlr_renderer *wlr_renderer,
|
||||
uint32_t drm_fmt, uint32_t stride, uint32_t width,
|
||||
uint32_t height, const void *data) {
|
||||
struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer);
|
||||
|
||||
VkResult res;
|
||||
VkDevice dev = renderer->dev->dev;
|
||||
|
||||
wlr_log(WLR_DEBUG, "vulkan_texture_from_pixels: %.4s, %dx%d",
|
||||
(const char*) &drm_fmt, width, height);
|
||||
|
||||
const struct wlr_vk_format_props *fmt =
|
||||
vulkan_format_props_from_drm(renderer->dev, drm_fmt);
|
||||
if (fmt == NULL) {
|
||||
wlr_log(WLR_ERROR, "Unsupported pixel format %"PRIx32 " (%.4s)",
|
||||
drm_fmt, (const char*) &drm_fmt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_vk_texture *texture = vulkan_texture_create(renderer, width, height);
|
||||
if (texture == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
texture->format = &fmt->format;
|
||||
|
||||
// create image
|
||||
unsigned mem_bits = 0xFFFFFFFF;
|
||||
|
||||
VkImageCreateInfo img_info = {0};
|
||||
img_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
img_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
img_info.format = texture->format->vk_format;
|
||||
img_info.mipLevels = 1;
|
||||
img_info.arrayLayers = 1;
|
||||
img_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
img_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
img_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
img_info.extent = (VkExtent3D) { width, height, 1 };
|
||||
img_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
|
||||
img_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
img_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
mem_bits = vulkan_find_mem_type(renderer->dev,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mem_bits);
|
||||
VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
res = vkCreateImage(dev, &img_info, NULL, &texture->image);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkCreateImage failed", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// memory
|
||||
VkMemoryRequirements mem_reqs;
|
||||
vkGetImageMemoryRequirements(dev, texture->image, &mem_reqs);
|
||||
|
||||
VkMemoryAllocateInfo mem_info = {0};
|
||||
mem_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
mem_info.allocationSize = mem_reqs.size;
|
||||
mem_info.memoryTypeIndex = mem_bits & mem_reqs.memoryTypeBits;
|
||||
res = vkAllocateMemory(dev, &mem_info, NULL, &texture->memories[0]);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkAllocatorMemory failed", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
texture->mem_count = 1;
|
||||
res = vkBindImageMemory(dev, texture->image, texture->memories[0], 0);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkBindMemory failed", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// view
|
||||
VkImageViewCreateInfo view_info = {0};
|
||||
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
view_info.format = texture->format->vk_format;
|
||||
view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
|
||||
view_info.subresourceRange = (VkImageSubresourceRange) {
|
||||
VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1
|
||||
};
|
||||
view_info.image = texture->image;
|
||||
|
||||
res = vkCreateImageView(dev, &view_info, NULL,
|
||||
&texture->image_view);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkCreateImageView failed", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// descriptor
|
||||
texture->ds_pool = vulkan_alloc_texture_ds(renderer, &texture->ds);
|
||||
if (!texture->ds_pool) {
|
||||
wlr_log(WLR_ERROR, "failed to allocate descriptor");
|
||||
goto error;
|
||||
}
|
||||
|
||||
VkDescriptorImageInfo ds_img_info = {0};
|
||||
ds_img_info.imageView = texture->image_view;
|
||||
ds_img_info.imageLayout = layout;
|
||||
|
||||
VkWriteDescriptorSet ds_write = {0};
|
||||
ds_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
ds_write.descriptorCount = 1;
|
||||
ds_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
ds_write.dstSet = texture->ds;
|
||||
ds_write.pImageInfo = &ds_img_info;
|
||||
|
||||
vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL);
|
||||
|
||||
// write data
|
||||
if (!write_pixels(&texture->wlr_texture, stride,
|
||||
width, height, 0, 0, 0, 0, data, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return &texture->wlr_texture;
|
||||
|
||||
error:
|
||||
vulkan_texture_destroy(texture);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool is_dmabuf_disjoint(const struct wlr_dmabuf_attributes *attribs) {
|
||||
if (attribs->n_planes == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat first_stat;
|
||||
if (fstat(attribs->fd[0], &first_stat) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "fstat failed");
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 1; i < attribs->n_planes; i++) {
|
||||
struct stat plane_stat;
|
||||
if (fstat(attribs->fd[i], &plane_stat) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "fstat failed");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (first_stat.st_ino != plane_stat.st_ino) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer,
|
||||
const struct wlr_dmabuf_attributes *attribs,
|
||||
VkDeviceMemory mems[static WLR_DMABUF_MAX_PLANES], uint32_t *n_mems,
|
||||
bool for_render) {
|
||||
VkResult res;
|
||||
VkDevice dev = renderer->dev->dev;
|
||||
*n_mems = 0u;
|
||||
|
||||
wlr_log(WLR_DEBUG, "vulkan_import_dmabuf: %.4s (mod %"PRIx64"), %dx%d, %d planes",
|
||||
(const char *)&attribs->format, attribs->modifier,
|
||||
attribs->width, attribs->height, attribs->n_planes);
|
||||
|
||||
struct wlr_vk_format_props *fmt = vulkan_format_props_from_drm(renderer->dev,
|
||||
attribs->format);
|
||||
if (fmt == NULL) {
|
||||
wlr_log(WLR_ERROR, "Unsupported pixel format %"PRIx32 " (%.4s)",
|
||||
attribs->format, (const char*) &attribs->format);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
uint32_t plane_count = attribs->n_planes;
|
||||
assert(plane_count < WLR_DMABUF_MAX_PLANES);
|
||||
struct wlr_vk_format_modifier_props *mod =
|
||||
vulkan_format_props_find_modifier(fmt, attribs->modifier, for_render);
|
||||
if (!mod || !(mod->dmabuf_flags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT)) {
|
||||
wlr_log(WLR_ERROR, "Format %"PRIx32" (%.4s) can't be used with modifier "
|
||||
"%"PRIx64, attribs->format, (const char*) &attribs->format,
|
||||
attribs->modifier);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if ((uint32_t) attribs->width > mod->max_extent.width ||
|
||||
(uint32_t) attribs->height > mod->max_extent.height) {
|
||||
wlr_log(WLR_ERROR, "dmabuf is too large to import");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (mod->props.drmFormatModifierPlaneCount != plane_count) {
|
||||
wlr_log(WLR_ERROR, "Number of planes (%d) does not match format (%d)",
|
||||
plane_count, mod->props.drmFormatModifierPlaneCount);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// check if we have to create the image disjoint
|
||||
bool disjoint = is_dmabuf_disjoint(attribs);
|
||||
if (disjoint && !(mod->props.drmFormatModifierTilingFeatures
|
||||
& VK_FORMAT_FEATURE_DISJOINT_BIT)) {
|
||||
wlr_log(WLR_ERROR, "Format/Modifier does not support disjoint images");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// image
|
||||
VkExternalMemoryHandleTypeFlagBits htype =
|
||||
VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
|
||||
|
||||
VkImageCreateInfo img_info = {0};
|
||||
img_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
img_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
img_info.format = fmt->format.vk_format;
|
||||
img_info.mipLevels = 1;
|
||||
img_info.arrayLayers = 1;
|
||||
img_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
img_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
img_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||||
img_info.extent = (VkExtent3D) { attribs->width, attribs->height, 1 };
|
||||
img_info.usage = for_render ?
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT :
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
if (disjoint) {
|
||||
img_info.flags = VK_IMAGE_CREATE_DISJOINT_BIT;
|
||||
}
|
||||
|
||||
VkExternalMemoryImageCreateInfo eimg = {0};
|
||||
eimg.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
|
||||
eimg.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
|
||||
img_info.pNext = &eimg;
|
||||
|
||||
VkSubresourceLayout plane_layouts[WLR_DMABUF_MAX_PLANES] = {0};
|
||||
VkImageDrmFormatModifierExplicitCreateInfoEXT mod_info = {0};
|
||||
|
||||
img_info.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
|
||||
for (unsigned i = 0u; i < plane_count; ++i) {
|
||||
plane_layouts[i].offset = attribs->offset[i];
|
||||
plane_layouts[i].rowPitch = attribs->stride[i];
|
||||
plane_layouts[i].size = 0;
|
||||
}
|
||||
|
||||
mod_info.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT;
|
||||
mod_info.drmFormatModifierPlaneCount = plane_count;
|
||||
mod_info.drmFormatModifier = mod->props.drmFormatModifier;
|
||||
mod_info.pPlaneLayouts = plane_layouts;
|
||||
eimg.pNext = &mod_info;
|
||||
|
||||
VkImage image;
|
||||
res = vkCreateImage(dev, &img_info, NULL, &image);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkCreateImage", res);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
unsigned mem_count = disjoint ? plane_count : 1u;
|
||||
VkBindImageMemoryInfo bindi[WLR_DMABUF_MAX_PLANES] = {0};
|
||||
VkBindImagePlaneMemoryInfo planei[WLR_DMABUF_MAX_PLANES] = {0};
|
||||
|
||||
for (unsigned i = 0u; i < mem_count; ++i) {
|
||||
struct VkMemoryFdPropertiesKHR fdp = {0};
|
||||
fdp.sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR;
|
||||
res = renderer->dev->api.getMemoryFdPropertiesKHR(dev, htype,
|
||||
attribs->fd[i], &fdp);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("getMemoryFdPropertiesKHR", res);
|
||||
goto error_image;
|
||||
}
|
||||
|
||||
VkImageMemoryRequirementsInfo2 memri = {0};
|
||||
memri.image = image;
|
||||
memri.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2;
|
||||
|
||||
VkImagePlaneMemoryRequirementsInfo planeri = {0};
|
||||
if (disjoint) {
|
||||
planeri.sType = VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO;
|
||||
planeri.planeAspect = mem_plane_aspect(i);
|
||||
memri.pNext = &planeri;
|
||||
}
|
||||
|
||||
VkMemoryRequirements2 memr = {0};
|
||||
memr.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
|
||||
|
||||
vkGetImageMemoryRequirements2(dev, &memri, &memr);
|
||||
int mem = vulkan_find_mem_type(renderer->dev, 0,
|
||||
memr.memoryRequirements.memoryTypeBits & fdp.memoryTypeBits);
|
||||
if (mem < 0) {
|
||||
wlr_log(WLR_ERROR, "no valid memory type index");
|
||||
goto error_image;
|
||||
}
|
||||
|
||||
// Since importing transfers ownership of the FD to Vulkan, we have
|
||||
// to duplicate it since this operation does not transfer ownership
|
||||
// of the attribs to this texture. Will be closed by Vulkan on
|
||||
// vkFreeMemory.
|
||||
int dfd = fcntl(attribs->fd[i], F_DUPFD_CLOEXEC, 0);
|
||||
if (dfd < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed");
|
||||
goto error_image;
|
||||
}
|
||||
|
||||
VkMemoryAllocateInfo memi = {0};
|
||||
memi.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
memi.allocationSize = memr.memoryRequirements.size;
|
||||
memi.memoryTypeIndex = mem;
|
||||
|
||||
VkImportMemoryFdInfoKHR importi = {0};
|
||||
importi.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;
|
||||
importi.fd = dfd;
|
||||
importi.handleType = htype;
|
||||
memi.pNext = &importi;
|
||||
|
||||
VkMemoryDedicatedAllocateInfo dedi = {0};
|
||||
dedi.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;
|
||||
dedi.image = image;
|
||||
importi.pNext = &dedi;
|
||||
|
||||
res = vkAllocateMemory(dev, &memi, NULL, &mems[i]);
|
||||
if (res != VK_SUCCESS) {
|
||||
close(dfd);
|
||||
wlr_vk_error("vkAllocateMemory failed", res);
|
||||
goto error_image;
|
||||
}
|
||||
|
||||
++(*n_mems);
|
||||
|
||||
// fill bind info
|
||||
bindi[i].image = image;
|
||||
bindi[i].memory = mems[i];
|
||||
bindi[i].memoryOffset = 0;
|
||||
bindi[i].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
|
||||
|
||||
if (disjoint) {
|
||||
planei[i].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO;
|
||||
planei[i].planeAspect = planeri.planeAspect;
|
||||
bindi[i].pNext = &planei[i];
|
||||
}
|
||||
}
|
||||
|
||||
res = vkBindImageMemory2(dev, mem_count, bindi);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkBindMemory failed", res);
|
||||
goto error_image;
|
||||
}
|
||||
|
||||
return image;
|
||||
|
||||
error_image:
|
||||
vkDestroyImage(dev, image, NULL);
|
||||
for (size_t i = 0u; i < *n_mems; ++i) {
|
||||
vkFreeMemory(dev, mems[i], NULL);
|
||||
mems[i] = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
static struct wlr_texture *vulkan_texture_from_dmabuf(struct wlr_renderer *wlr_renderer,
|
||||
struct wlr_dmabuf_attributes *attribs) {
|
||||
struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer);
|
||||
|
||||
VkResult res;
|
||||
VkDevice dev = renderer->dev->dev;
|
||||
|
||||
const struct wlr_vk_format_props *fmt = vulkan_format_props_from_drm(
|
||||
renderer->dev, attribs->format);
|
||||
if (fmt == NULL) {
|
||||
wlr_log(WLR_ERROR, "Unsupported pixel format %"PRIx32 " (%.4s)",
|
||||
attribs->format, (const char*) &attribs->format);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_vk_texture *texture = vulkan_texture_create(renderer,
|
||||
attribs->width, attribs->height);
|
||||
if (texture == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
texture->format = &fmt->format;
|
||||
texture->image = vulkan_import_dmabuf(renderer, attribs,
|
||||
texture->memories, &texture->mem_count, false);
|
||||
if (!texture->image) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
uint32_t flags = attribs->flags;
|
||||
if (flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT) {
|
||||
texture->invert_y = true;
|
||||
flags &= ~WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT;
|
||||
}
|
||||
|
||||
if (flags != 0) {
|
||||
wlr_log(WLR_ERROR, "dmabuf flags %x not supported/implemented on vulkan",
|
||||
attribs->flags);
|
||||
// NOTE: should probably make this a critical error in future
|
||||
// return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// view
|
||||
VkImageViewCreateInfo view_info = {0};
|
||||
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
view_info.format = texture->format->vk_format;
|
||||
view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
|
||||
view_info.subresourceRange = (VkImageSubresourceRange) {
|
||||
VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1
|
||||
};
|
||||
view_info.image = texture->image;
|
||||
|
||||
res = vkCreateImageView(dev, &view_info, NULL, &texture->image_view);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkCreateImageView failed", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// descriptor
|
||||
texture->ds_pool = vulkan_alloc_texture_ds(renderer, &texture->ds);
|
||||
if (!texture->ds_pool) {
|
||||
wlr_log(WLR_ERROR, "failed to allocate descriptor");
|
||||
goto error;
|
||||
}
|
||||
|
||||
VkDescriptorImageInfo ds_img_info = {0};
|
||||
ds_img_info.imageView = texture->image_view;
|
||||
ds_img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
VkWriteDescriptorSet ds_write = {0};
|
||||
ds_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
ds_write.descriptorCount = 1;
|
||||
ds_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
ds_write.dstSet = texture->ds;
|
||||
ds_write.pImageInfo = &ds_img_info;
|
||||
|
||||
vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL);
|
||||
texture->dmabuf_imported = true;
|
||||
|
||||
return &texture->wlr_texture;
|
||||
|
||||
error:
|
||||
vulkan_texture_destroy(texture);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void texture_handle_buffer_destroy(struct wl_listener *listener,
|
||||
void *data) {
|
||||
struct wlr_vk_texture *texture =
|
||||
wl_container_of(listener, texture, buffer_destroy);
|
||||
vulkan_texture_destroy(texture);
|
||||
}
|
||||
|
||||
static struct wlr_texture *vulkan_texture_from_dmabuf_buffer(
|
||||
struct wlr_vk_renderer *renderer, struct wlr_buffer *buffer,
|
||||
struct wlr_dmabuf_attributes *dmabuf) {
|
||||
struct wlr_vk_texture *texture;
|
||||
wl_list_for_each(texture, &renderer->textures, link) {
|
||||
if (texture->buffer == buffer) {
|
||||
wlr_buffer_lock(texture->buffer);
|
||||
return &texture->wlr_texture;
|
||||
}
|
||||
}
|
||||
|
||||
struct wlr_texture *wlr_texture =
|
||||
vulkan_texture_from_dmabuf(&renderer->wlr_renderer, dmabuf);
|
||||
if (wlr_texture == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
texture = vulkan_get_texture(wlr_texture);
|
||||
texture->buffer = wlr_buffer_lock(buffer);
|
||||
|
||||
texture->buffer_destroy.notify = texture_handle_buffer_destroy;
|
||||
wl_signal_add(&buffer->events.destroy, &texture->buffer_destroy);
|
||||
|
||||
return &texture->wlr_texture;
|
||||
}
|
||||
|
||||
struct wlr_texture *vulkan_texture_from_buffer(
|
||||
struct wlr_renderer *wlr_renderer,
|
||||
struct wlr_buffer *buffer) {
|
||||
struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer);
|
||||
|
||||
void *data;
|
||||
uint32_t format;
|
||||
size_t stride;
|
||||
struct wlr_dmabuf_attributes dmabuf;
|
||||
if (wlr_buffer_get_dmabuf(buffer, &dmabuf)) {
|
||||
return vulkan_texture_from_dmabuf_buffer(renderer, buffer, &dmabuf);
|
||||
} else if (wlr_buffer_begin_data_ptr_access(buffer,
|
||||
WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) {
|
||||
struct wlr_texture *tex = vulkan_texture_from_pixels(wlr_renderer,
|
||||
format, stride, buffer->width, buffer->height, data);
|
||||
wlr_buffer_end_data_ptr_access(buffer);
|
||||
return tex;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
93
render/vulkan/util.c
Normal file
93
render/vulkan/util.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
#include <vulkan/vulkan.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "render/vulkan.h"
|
||||
|
||||
int vulkan_find_mem_type(struct wlr_vk_device *dev,
|
||||
VkMemoryPropertyFlags flags, uint32_t req_bits) {
|
||||
|
||||
VkPhysicalDeviceMemoryProperties props;
|
||||
vkGetPhysicalDeviceMemoryProperties(dev->phdev, &props);
|
||||
|
||||
for (unsigned i = 0u; i < props.memoryTypeCount; ++i) {
|
||||
if (req_bits & (1 << i)) {
|
||||
if ((props.memoryTypes[i].propertyFlags & flags) == flags) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *vulkan_strerror(VkResult err) {
|
||||
#define ERR_STR(r) case VK_ ##r: return #r
|
||||
switch (err) {
|
||||
ERR_STR(SUCCESS);
|
||||
ERR_STR(NOT_READY);
|
||||
ERR_STR(TIMEOUT);
|
||||
ERR_STR(EVENT_SET);
|
||||
ERR_STR(EVENT_RESET);
|
||||
ERR_STR(INCOMPLETE);
|
||||
ERR_STR(SUBOPTIMAL_KHR);
|
||||
ERR_STR(ERROR_OUT_OF_HOST_MEMORY);
|
||||
ERR_STR(ERROR_OUT_OF_DEVICE_MEMORY);
|
||||
ERR_STR(ERROR_INITIALIZATION_FAILED);
|
||||
ERR_STR(ERROR_DEVICE_LOST);
|
||||
ERR_STR(ERROR_MEMORY_MAP_FAILED);
|
||||
ERR_STR(ERROR_LAYER_NOT_PRESENT);
|
||||
ERR_STR(ERROR_EXTENSION_NOT_PRESENT);
|
||||
ERR_STR(ERROR_FEATURE_NOT_PRESENT);
|
||||
ERR_STR(ERROR_INCOMPATIBLE_DRIVER);
|
||||
ERR_STR(ERROR_TOO_MANY_OBJECTS);
|
||||
ERR_STR(ERROR_FORMAT_NOT_SUPPORTED);
|
||||
ERR_STR(ERROR_SURFACE_LOST_KHR);
|
||||
ERR_STR(ERROR_NATIVE_WINDOW_IN_USE_KHR);
|
||||
ERR_STR(ERROR_OUT_OF_DATE_KHR);
|
||||
ERR_STR(ERROR_FRAGMENTED_POOL);
|
||||
ERR_STR(ERROR_INCOMPATIBLE_DISPLAY_KHR);
|
||||
ERR_STR(ERROR_VALIDATION_FAILED_EXT);
|
||||
ERR_STR(ERROR_INVALID_EXTERNAL_HANDLE);
|
||||
ERR_STR(ERROR_OUT_OF_POOL_MEMORY);
|
||||
ERR_STR(ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT);
|
||||
default:
|
||||
return "<unknown>";
|
||||
}
|
||||
#undef ERR_STR
|
||||
}
|
||||
|
||||
void vulkan_change_layout_queue(VkCommandBuffer cb, VkImage img,
|
||||
VkImageLayout ol, VkPipelineStageFlags srcs, VkAccessFlags srca,
|
||||
VkImageLayout nl, VkPipelineStageFlags dsts, VkAccessFlags dsta,
|
||||
uint32_t src_family, uint32_t dst_family) {
|
||||
VkImageMemoryBarrier barrier = {0};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
barrier.oldLayout = ol;
|
||||
barrier.newLayout = nl;
|
||||
barrier.image = img;
|
||||
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
barrier.subresourceRange.layerCount = 1;
|
||||
barrier.subresourceRange.levelCount = 1;
|
||||
barrier.srcAccessMask = srca;
|
||||
barrier.dstAccessMask = dsta;
|
||||
barrier.srcQueueFamilyIndex = src_family;
|
||||
barrier.dstQueueFamilyIndex = dst_family;
|
||||
|
||||
vkCmdPipelineBarrier(cb, srcs, dsts, 0, 0, NULL, 0, NULL, 1, &barrier);
|
||||
}
|
||||
|
||||
void vulkan_change_layout(VkCommandBuffer cb, VkImage img,
|
||||
VkImageLayout ol, VkPipelineStageFlags srcs, VkAccessFlags srca,
|
||||
VkImageLayout nl, VkPipelineStageFlags dsts, VkAccessFlags dsta) {
|
||||
vulkan_change_layout_queue(cb, img, ol, srcs, srca, nl, dsts, dsta,
|
||||
VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED);
|
||||
}
|
||||
|
||||
bool vulkan_has_extension(size_t count, const char **exts, const char *find) {
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
if (strcmp(exts[i], find) == 0u) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
550
render/vulkan/vulkan.c
Normal file
550
render/vulkan/vulkan.c
Normal file
|
@ -0,0 +1,550 @@
|
|||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <xf86drm.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <wlr/version.h>
|
||||
#include <wlr/config.h>
|
||||
#include "render/vulkan.h"
|
||||
|
||||
// Returns the name of the first extension that could not be found or NULL.
|
||||
static const char *find_extensions(const VkExtensionProperties *avail,
|
||||
unsigned availc, const char **req, unsigned reqc) {
|
||||
// check if all required extensions are supported
|
||||
for (size_t i = 0; i < reqc; ++i) {
|
||||
bool found = false;
|
||||
for (size_t j = 0; j < availc; ++j) {
|
||||
if (!strcmp(avail[j].extensionName, req[i])) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return req[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static VkBool32 debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT type,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT *debug_data,
|
||||
void *data) {
|
||||
// we ignore some of the non-helpful warnings
|
||||
static const char *const ignored[] = {
|
||||
// notifies us that shader output is not consumed since
|
||||
// we use the shared vertex buffer with uv output
|
||||
"UNASSIGNED-CoreValidation-Shader-OutputNotConsumed",
|
||||
};
|
||||
|
||||
if (debug_data->pMessageIdName) {
|
||||
for (unsigned i = 0; i < sizeof(ignored) / sizeof(ignored[0]); ++i) {
|
||||
if (strcmp(debug_data->pMessageIdName, ignored[i]) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum wlr_log_importance importance;
|
||||
switch (severity) {
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
|
||||
importance = WLR_ERROR;
|
||||
break;
|
||||
default:
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
|
||||
importance = WLR_INFO;
|
||||
break;
|
||||
}
|
||||
|
||||
wlr_log(importance, "%s (%s)", debug_data->pMessage,
|
||||
debug_data->pMessageIdName);
|
||||
if (debug_data->queueLabelCount > 0) {
|
||||
const char *name = debug_data->pQueueLabels[0].pLabelName;
|
||||
if (name) {
|
||||
wlr_log(importance, " last label '%s'", name);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < debug_data->objectCount; ++i) {
|
||||
if (debug_data->pObjects[i].pObjectName) {
|
||||
wlr_log(importance, " involving '%s'", debug_data->pMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// instance
|
||||
struct wlr_vk_instance *vulkan_instance_create(size_t ext_count,
|
||||
const char **exts, bool debug) {
|
||||
// we require vulkan 1.1
|
||||
PFN_vkEnumerateInstanceVersion pfEnumInstanceVersion =
|
||||
(PFN_vkEnumerateInstanceVersion)
|
||||
vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion");
|
||||
if (!pfEnumInstanceVersion) {
|
||||
wlr_log(WLR_ERROR, "wlroots requires vulkan 1.1 which is not available");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t ini_version;
|
||||
if (pfEnumInstanceVersion(&ini_version) != VK_SUCCESS ||
|
||||
ini_version < VK_API_VERSION_1_1) {
|
||||
wlr_log(WLR_ERROR, "wlroots requires vulkan 1.1 which is not available");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// query extension support
|
||||
uint32_t avail_extc = 0;
|
||||
VkResult res;
|
||||
res = vkEnumerateInstanceExtensionProperties(NULL, &avail_extc, NULL);
|
||||
if ((res != VK_SUCCESS) || (avail_extc == 0)) {
|
||||
wlr_vk_error("Could not enumerate instance extensions (1)", res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VkExtensionProperties avail_ext_props[avail_extc + 1];
|
||||
res = vkEnumerateInstanceExtensionProperties(NULL, &avail_extc,
|
||||
avail_ext_props);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("Could not enumerate instance extensions (2)", res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < avail_extc; ++j) {
|
||||
wlr_log(WLR_DEBUG, "Vulkan instance extension %s v%"PRIu32,
|
||||
avail_ext_props[j].extensionName, avail_ext_props[j].specVersion);
|
||||
}
|
||||
|
||||
// create instance
|
||||
struct wlr_vk_instance *ini = calloc(1, sizeof(*ini));
|
||||
if (!ini) {
|
||||
wlr_log_errno(WLR_ERROR, "allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool debug_utils_found = false;
|
||||
ini->extensions = calloc(1 + ext_count, sizeof(*ini->extensions));
|
||||
if (!ini->extensions) {
|
||||
wlr_log_errno(WLR_ERROR, "allocation failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// find extensions
|
||||
for (unsigned i = 0; i < ext_count; ++i) {
|
||||
if (find_extensions(avail_ext_props, avail_extc, &exts[i], 1)) {
|
||||
wlr_log(WLR_DEBUG, "vulkan instance extension %s not found",
|
||||
exts[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
ini->extensions[ini->extension_count++] = exts[i];
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
const char *name = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
|
||||
if (find_extensions(avail_ext_props, avail_extc, &name, 1) == NULL) {
|
||||
debug_utils_found = true;
|
||||
ini->extensions[ini->extension_count++] = name;
|
||||
}
|
||||
}
|
||||
|
||||
VkApplicationInfo application_info = {0};
|
||||
application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
application_info.pEngineName = "wlroots";
|
||||
application_info.engineVersion = WLR_VERSION_NUM;
|
||||
application_info.apiVersion = VK_API_VERSION_1_1;
|
||||
|
||||
const char *layers[] = {
|
||||
"VK_LAYER_KHRONOS_validation",
|
||||
// "VK_LAYER_RENDERDOC_Capture",
|
||||
// "VK_LAYER_live_introspection",
|
||||
};
|
||||
|
||||
unsigned layer_count = debug * (sizeof(layers) / sizeof(layers[0]));
|
||||
|
||||
VkInstanceCreateInfo instance_info = {0};
|
||||
instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
instance_info.pApplicationInfo = &application_info;
|
||||
instance_info.enabledExtensionCount = ini->extension_count;
|
||||
instance_info.ppEnabledExtensionNames = ini->extensions;
|
||||
instance_info.enabledLayerCount = layer_count;
|
||||
instance_info.ppEnabledLayerNames = layers;
|
||||
|
||||
VkDebugUtilsMessageSeverityFlagsEXT severity =
|
||||
// VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||
VkDebugUtilsMessageTypeFlagsEXT types =
|
||||
// VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||
|
||||
VkDebugUtilsMessengerCreateInfoEXT debug_info = {0};
|
||||
debug_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||
debug_info.messageSeverity = severity;
|
||||
debug_info.messageType = types;
|
||||
debug_info.pfnUserCallback = &debug_callback;
|
||||
debug_info.pUserData = ini;
|
||||
|
||||
if (debug_utils_found) {
|
||||
// already adding the debug utils messenger extension to
|
||||
// instance creation gives us additional information during
|
||||
// instance creation and destruction, can be useful for debugging
|
||||
// layers/extensions not being found.
|
||||
instance_info.pNext = &debug_info;
|
||||
}
|
||||
|
||||
res = vkCreateInstance(&instance_info, NULL, &ini->instance);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("Could not create instance", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// debug callback
|
||||
if (debug_utils_found) {
|
||||
ini->api.createDebugUtilsMessengerEXT =
|
||||
(PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(
|
||||
ini->instance, "vkCreateDebugUtilsMessengerEXT");
|
||||
ini->api.destroyDebugUtilsMessengerEXT =
|
||||
(PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(
|
||||
ini->instance, "vkDestroyDebugUtilsMessengerEXT");
|
||||
|
||||
if (ini->api.createDebugUtilsMessengerEXT) {
|
||||
ini->api.createDebugUtilsMessengerEXT(ini->instance,
|
||||
&debug_info, NULL, &ini->messenger);
|
||||
} else {
|
||||
wlr_log(WLR_ERROR, "vkCreateDebugUtilsMessengerEXT not found");
|
||||
}
|
||||
}
|
||||
|
||||
return ini;
|
||||
|
||||
error:
|
||||
vulkan_instance_destroy(ini);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void vulkan_instance_destroy(struct wlr_vk_instance *ini) {
|
||||
if (!ini) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ini->messenger && ini->api.destroyDebugUtilsMessengerEXT) {
|
||||
ini->api.destroyDebugUtilsMessengerEXT(ini->instance,
|
||||
ini->messenger, NULL);
|
||||
}
|
||||
|
||||
if (ini->instance) {
|
||||
vkDestroyInstance(ini->instance, NULL);
|
||||
}
|
||||
|
||||
free(ini->extensions);
|
||||
free(ini);
|
||||
}
|
||||
|
||||
// physical device matching
|
||||
static void log_phdev(const VkPhysicalDeviceProperties *props) {
|
||||
uint32_t vv_major = VK_VERSION_MAJOR(props->apiVersion);
|
||||
uint32_t vv_minor = VK_VERSION_MINOR(props->apiVersion);
|
||||
uint32_t vv_patch = VK_VERSION_PATCH(props->apiVersion);
|
||||
|
||||
uint32_t dv_major = VK_VERSION_MAJOR(props->driverVersion);
|
||||
uint32_t dv_minor = VK_VERSION_MINOR(props->driverVersion);
|
||||
uint32_t dv_patch = VK_VERSION_PATCH(props->driverVersion);
|
||||
|
||||
const char *dev_type = "unknown";
|
||||
switch (props->deviceType) {
|
||||
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
|
||||
dev_type = "integrated";
|
||||
break;
|
||||
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
|
||||
dev_type = "discrete";
|
||||
break;
|
||||
case VK_PHYSICAL_DEVICE_TYPE_CPU:
|
||||
dev_type = "cpu";
|
||||
break;
|
||||
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
|
||||
dev_type = "vgpu";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
wlr_log(WLR_INFO, "Vulkan device: '%s'", props->deviceName);
|
||||
wlr_log(WLR_INFO, " Device type: '%s'", dev_type);
|
||||
wlr_log(WLR_INFO, " Supported API version: %u.%u.%u", vv_major, vv_minor, vv_patch);
|
||||
wlr_log(WLR_INFO, " Driver version: %u.%u.%u", dv_major, dv_minor, dv_patch);
|
||||
}
|
||||
|
||||
VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) {
|
||||
VkResult res;
|
||||
uint32_t num_phdevs;
|
||||
|
||||
res = vkEnumeratePhysicalDevices(ini->instance, &num_phdevs, NULL);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("Could not retrieve physical devices", res);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkPhysicalDevice phdevs[1 + num_phdevs];
|
||||
res = vkEnumeratePhysicalDevices(ini->instance, &num_phdevs, phdevs);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("Could not retrieve physical devices", res);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
struct stat drm_stat = {0};
|
||||
if (fstat(drm_fd, &drm_stat) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "fstat failed");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < num_phdevs; ++i) {
|
||||
VkPhysicalDevice phdev = phdevs[i];
|
||||
|
||||
// check whether device supports vulkan 1.1, needed for
|
||||
// vkGetPhysicalDeviceProperties2
|
||||
VkPhysicalDeviceProperties phdev_props;
|
||||
vkGetPhysicalDeviceProperties(phdev, &phdev_props);
|
||||
|
||||
log_phdev(&phdev_props);
|
||||
|
||||
if (phdev_props.apiVersion < VK_API_VERSION_1_1) {
|
||||
// NOTE: we could additionaly check whether the
|
||||
// VkPhysicalDeviceProperties2KHR extension is supported but
|
||||
// implementations not supporting 1.1 are unlikely in future
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for extensions
|
||||
uint32_t avail_extc = 0;
|
||||
res = vkEnumerateDeviceExtensionProperties(phdev, NULL,
|
||||
&avail_extc, NULL);
|
||||
if ((res != VK_SUCCESS) || (avail_extc == 0)) {
|
||||
wlr_vk_error(" Could not enumerate device extensions", res);
|
||||
continue;
|
||||
}
|
||||
|
||||
VkExtensionProperties avail_ext_props[avail_extc + 1];
|
||||
res = vkEnumerateDeviceExtensionProperties(phdev, NULL,
|
||||
&avail_extc, avail_ext_props);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error(" Could not enumerate device extensions", res);
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *name = VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME;
|
||||
if (find_extensions(avail_ext_props, avail_extc, &name, 1) != NULL) {
|
||||
wlr_log(WLR_DEBUG, " Ignoring physical device \"%s\": "
|
||||
"VK_EXT_physical_device_drm not supported",
|
||||
phdev_props.deviceName);
|
||||
continue;
|
||||
}
|
||||
|
||||
VkPhysicalDeviceDrmPropertiesEXT drm_props = {0};
|
||||
drm_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT;
|
||||
|
||||
VkPhysicalDeviceProperties2 props = {0};
|
||||
props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||
props.pNext = &drm_props;
|
||||
|
||||
vkGetPhysicalDeviceProperties2(phdev, &props);
|
||||
|
||||
dev_t primary_devid = makedev(drm_props.primaryMajor, drm_props.primaryMinor);
|
||||
dev_t render_devid = makedev(drm_props.renderMajor, drm_props.renderMinor);
|
||||
if (primary_devid == drm_stat.st_rdev ||
|
||||
render_devid == drm_stat.st_rdev) {
|
||||
wlr_log(WLR_INFO, "Found matching Vulkan physical device: %s",
|
||||
phdev_props.deviceName);
|
||||
return phdev;
|
||||
}
|
||||
}
|
||||
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini,
|
||||
VkPhysicalDevice phdev, size_t ext_count, const char **exts) {
|
||||
VkResult res;
|
||||
|
||||
// check for extensions
|
||||
uint32_t avail_extc = 0;
|
||||
res = vkEnumerateDeviceExtensionProperties(phdev, NULL,
|
||||
&avail_extc, NULL);
|
||||
if (res != VK_SUCCESS || avail_extc == 0) {
|
||||
wlr_vk_error("Could not enumerate device extensions (1)", res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VkExtensionProperties avail_ext_props[avail_extc + 1];
|
||||
res = vkEnumerateDeviceExtensionProperties(phdev, NULL,
|
||||
&avail_extc, avail_ext_props);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("Could not enumerate device extensions (2)", res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < avail_extc; ++j) {
|
||||
wlr_log(WLR_DEBUG, "Vulkan device extension %s v%"PRIu32,
|
||||
avail_ext_props[j].extensionName, avail_ext_props[j].specVersion);
|
||||
}
|
||||
|
||||
// create device
|
||||
struct wlr_vk_device *dev = calloc(1, sizeof(*dev));
|
||||
if (!dev) {
|
||||
wlr_log_errno(WLR_ERROR, "allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->phdev = phdev;
|
||||
dev->instance = ini;
|
||||
dev->drm_fd = -1;
|
||||
dev->extensions = calloc(16 + ext_count, sizeof(*ini->extensions));
|
||||
if (!dev->extensions) {
|
||||
wlr_log_errno(WLR_ERROR, "allocation failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// find extensions
|
||||
for (unsigned i = 0; i < ext_count; ++i) {
|
||||
if (find_extensions(avail_ext_props, avail_extc, &exts[i], 1)) {
|
||||
wlr_log(WLR_DEBUG, "vulkan device extension %s not found",
|
||||
exts[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
dev->extensions[dev->extension_count++] = exts[i];
|
||||
}
|
||||
|
||||
// For dmabuf import we require at least the external_memory_fd,
|
||||
// external_memory_dma_buf, queue_family_foreign and
|
||||
// image_drm_format_modifier extensions.
|
||||
const char *names[] = {
|
||||
VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
|
||||
VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, // or vulkan 1.2
|
||||
VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME,
|
||||
VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
|
||||
VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
|
||||
};
|
||||
|
||||
unsigned nc = sizeof(names) / sizeof(names[0]);
|
||||
const char *not_found = find_extensions(avail_ext_props, avail_extc, names, nc);
|
||||
if (not_found) {
|
||||
wlr_log(WLR_ERROR, "vulkan: required device extension %s not found",
|
||||
not_found);
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (unsigned i = 0u; i < nc; ++i) {
|
||||
dev->extensions[dev->extension_count++] = names[i];
|
||||
}
|
||||
|
||||
// queue families
|
||||
{
|
||||
uint32_t qfam_count;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(phdev, &qfam_count, NULL);
|
||||
assert(qfam_count > 0);
|
||||
VkQueueFamilyProperties queue_props[qfam_count];
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(phdev, &qfam_count,
|
||||
queue_props);
|
||||
|
||||
bool graphics_found = false;
|
||||
for (unsigned i = 0u; i < qfam_count; ++i) {
|
||||
graphics_found = queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT;
|
||||
if (graphics_found) {
|
||||
dev->queue_family = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(graphics_found);
|
||||
}
|
||||
|
||||
const float prio = 1.f;
|
||||
VkDeviceQueueCreateInfo qinfo = {};
|
||||
qinfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||
qinfo.queueFamilyIndex = dev->queue_family;
|
||||
qinfo.queueCount = 1;
|
||||
qinfo.pQueuePriorities = &prio;
|
||||
|
||||
VkDeviceCreateInfo dev_info = {0};
|
||||
dev_info.pNext = NULL;
|
||||
dev_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||
dev_info.queueCreateInfoCount = 1u;
|
||||
dev_info.pQueueCreateInfos = &qinfo;
|
||||
dev_info.enabledExtensionCount = dev->extension_count;
|
||||
dev_info.ppEnabledExtensionNames = dev->extensions;
|
||||
|
||||
res = vkCreateDevice(phdev, &dev_info, NULL, &dev->dev);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("Failed to create vulkan device", res);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
vkGetDeviceQueue(dev->dev, dev->queue_family, 0, &dev->queue);
|
||||
|
||||
// load api
|
||||
dev->api.getMemoryFdPropertiesKHR = (PFN_vkGetMemoryFdPropertiesKHR)
|
||||
vkGetDeviceProcAddr(dev->dev, "vkGetMemoryFdPropertiesKHR");
|
||||
|
||||
if (!dev->api.getMemoryFdPropertiesKHR) {
|
||||
wlr_log(WLR_ERROR, "Failed to retrieve required dev function pointers");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// - check device format support -
|
||||
size_t max_fmts;
|
||||
const struct wlr_vk_format *fmts = vulkan_get_format_list(&max_fmts);
|
||||
dev->shm_formats = calloc(max_fmts, sizeof(*dev->shm_formats));
|
||||
dev->format_props = calloc(max_fmts, sizeof(*dev->format_props));
|
||||
if (!dev->shm_formats || !dev->format_props) {
|
||||
wlr_log_errno(WLR_ERROR, "allocation failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (unsigned i = 0u; i < max_fmts; ++i) {
|
||||
vulkan_format_props_query(dev, &fmts[i]);
|
||||
}
|
||||
|
||||
return dev;
|
||||
|
||||
error:
|
||||
vulkan_device_destroy(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void vulkan_device_destroy(struct wlr_vk_device *dev) {
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev->dev) {
|
||||
vkDestroyDevice(dev->dev, NULL);
|
||||
}
|
||||
|
||||
if (dev->drm_fd > 0) {
|
||||
close(dev->drm_fd);
|
||||
}
|
||||
|
||||
wlr_drm_format_set_finish(&dev->dmabuf_render_formats);
|
||||
wlr_drm_format_set_finish(&dev->dmabuf_texture_formats);
|
||||
|
||||
for (unsigned i = 0u; i < dev->format_prop_count; ++i) {
|
||||
vulkan_format_props_finish(&dev->format_props[i]);
|
||||
}
|
||||
|
||||
free(dev->extensions);
|
||||
free(dev->shm_formats);
|
||||
free(dev->format_props);
|
||||
free(dev);
|
||||
}
|
|
@ -17,6 +17,10 @@
|
|||
#include <wlr/render/gles2.h>
|
||||
#endif
|
||||
|
||||
#if WLR_HAS_VULKAN_RENDERER
|
||||
#include <wlr/render/vulkan.h>
|
||||
#endif // WLR_HAS_VULKAN_RENDERER
|
||||
|
||||
#include "util/signal.h"
|
||||
#include "render/pixel_format.h"
|
||||
#include "render/wlr_renderer.h"
|
||||
|
@ -257,6 +261,11 @@ struct wlr_renderer *renderer_autocreate_with_drm_fd(int drm_fd) {
|
|||
}
|
||||
return wlr_gles2_renderer_create_with_drm_fd(drm_fd);
|
||||
}
|
||||
#endif
|
||||
#if WLR_HAS_VULKAN_RENDERER
|
||||
if (strcmp(name, "vulkan") == 0) {
|
||||
return wlr_vk_renderer_create_with_drm_fd(drm_fd);
|
||||
}
|
||||
#endif
|
||||
if (strcmp(name, "pixman") == 0) {
|
||||
return wlr_pixman_renderer_create();
|
||||
|
|
Loading…
Reference in a new issue