Skip to content

Commit 0358f63

Browse files
committed
make vulkan rendering work
- fix UNREACHABLE macro - document vk_renderer API - cast _SRGB to _UNORM vulkan formats to work around flutter bug - specify proper vulkan image usage flags - close gbm bo fd on error - remove vk backing store ref logging
1 parent 3ef128e commit 0358f63

File tree

4 files changed

+136
-18
lines changed

4 files changed

+136
-18
lines changed

include/collection.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ static const char *__attribute__((unused)) __file_logging_name = _logging_name;
467467
#endif
468468

469469
#if defined(__GNUC__) || __has_builtin(__builtin_unreachable)
470-
#define UNREACHABLE() __builtin_unreachable
470+
#define UNREACHABLE() __builtin_unreachable()
471471
#else
472472
#define UNREACHABLE() assert(0 && "Unreachable")
473473
#endif

include/vk_renderer.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,83 @@
2323

2424
struct vk_renderer;
2525

26+
/**
27+
* @brief Create a new vulkan renderer with some reasonable defaults.
28+
*
29+
* Creates a vulkan instance with:
30+
* - app name `flutter-pi`, version 1.0.0
31+
* - engine `flutter-pi`, version 1.0.0
32+
* - vulkan version 1.1.0
33+
* - khronos validation layers and debug utils enabled, if supported and VULKAN_DEBUG is defined
34+
*
35+
* Selects a good physical device (dedicated GPU > integrated GPU > software) that has a
36+
* graphics queue family and supports the following device extensions:
37+
* - `VK_KHR_external_memory`
38+
* - `VK_KHR_external_memory_fd`
39+
* - `VK_KHR_external_semaphore`
40+
* - `VK_KHR_external_semaphore_fd`
41+
* - `VK_EXT_external_memory_dma_buf`
42+
* - `VK_KHR_image_format_list`
43+
* - `VK_EXT_image_drm_format_modifier`
44+
*
45+
* Those extensions will also be enabled when create the logical device of course.
46+
*
47+
* Will also create a graphics queue.
48+
*
49+
* @return New vulkan renderer instance.
50+
*/
2651
ATTR_MALLOC struct vk_renderer *vk_renderer_new();
2752

2853
void vk_renderer_destroy();
2954

3055
DECLARE_REF_OPS(vk_renderer)
3156

57+
/**
58+
* @brief Get the vulkan version of this renderer. This is unconditionally VK_MAKE_VERSION(1, 1, 0) for now.
59+
*
60+
* @param renderer renderer instance
61+
* @return VK_MAKE_VERSION(1, 1, 0)
62+
*/
3263
ATTR_CONST uint32_t vk_renderer_get_vk_version(struct vk_renderer *renderer);
3364

65+
/**
66+
* @brief Get the vulkan instance of this renderer. See @ref vk_renderer_new for details on this instance.
67+
*
68+
* @param renderer renderer instance
69+
* @return vulkan instance
70+
*/
3471
ATTR_PURE VkInstance vk_renderer_get_instance(struct vk_renderer *renderer);
3572

73+
/**
74+
* @brief Get the physical device that's used by this renderer. See @ref vk_renderer_new for details.
75+
*
76+
* @param renderer renderer instance
77+
* @return vulkan physical device
78+
*/
3679
ATTR_PURE VkPhysicalDevice vk_renderer_get_physical_device(struct vk_renderer *renderer);
3780

81+
/**
82+
* @brief Get the logical device that's used by this renderer. See @ref vk_renderer_new for details.
83+
*
84+
* @param renderer renderer instance
85+
* @return vulkan logical device
86+
*/
3887
ATTR_PURE VkDevice vk_renderer_get_device(struct vk_renderer *renderer);
3988

89+
/**
90+
* @brief Get the index of the graphics queue family.
91+
*
92+
* @param renderer renderer instance
93+
* @return instance of the graphics queue family.
94+
*/
4095
ATTR_PURE uint32_t vk_renderer_get_queue_family_index(struct vk_renderer *renderer);
4196

97+
/**
98+
* @brief Get the graphics queue of this renderer.
99+
*
100+
* @param renderer renderer instance
101+
* @return graphics queue
102+
*/
42103
ATTR_PURE VkQueue vk_renderer_get_queue(struct vk_renderer *renderer);
43104

44105
ATTR_PURE int vk_renderer_get_enabled_instance_extension_count(struct vk_renderer *renderer);
@@ -49,6 +110,16 @@ ATTR_PURE int vk_renderer_get_enabled_device_extension_count(struct vk_renderer
49110

50111
ATTR_PURE const char **vk_renderer_get_enabled_device_extensions(struct vk_renderer *renderer);
51112

113+
/**
114+
* @brief Find the index of a memory type for which the following conditions are true:
115+
* - (1 < 32) & @param req_bits is not 0
116+
* - the memory types property flags support the flags that are given in @param flags
117+
*
118+
* @param renderer renderer instance
119+
* @param flags Which property flags the memory type should support.
120+
* @param req_bits Which memory types are allowed to choose from.
121+
* @return index of the found memory type or -1 if none was found.
122+
*/
52123
ATTR_PURE int vk_renderer_find_mem_type(struct vk_renderer *renderer, VkMemoryPropertyFlags flags, uint32_t req_bits);
53124

54125
#endif // _FLUTTERPI_INCLUDE_VK_RENDERER_H

src/vk_gbm_backing_store.c

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ struct vk_gbm_backing_store {
8181
* flutter using @ref vk_gbm_backing_store_fill_vulkan. Even when @ref vk_gbm_backing_store_present_kms is called,
8282
* we don't set this to NULL.
8383
*
84+
* This is the framebuffer that will be presented on screen when @ref vk_gbm_backing_store_present_kms or
85+
* @ref vk_gbm_backing_store_present_fbdev is called.
86+
*
8487
*/
8588
struct locked_fb *front_fb;
8689

@@ -104,6 +107,7 @@ static void locked_fb_destroy(struct locked_fb *fb) {
104107

105108
store = fb->store;
106109
fb->store = NULL;
110+
107111
#ifdef DEBUG
108112
atomic_fetch_sub(&store->n_locked_fbs, 1);
109113
#endif
@@ -137,11 +141,26 @@ static int vk_gbm_backing_store_present_fbdev(struct surface *s, const struct fl
137141
static int vk_gbm_backing_store_fill(struct backing_store *store, FlutterBackingStore *fl_store);
138142
static int vk_gbm_backing_store_queue_present(struct backing_store *store, const FlutterBackingStore *fl_store);
139143

144+
static bool is_srgb_format(VkFormat vk_format) {
145+
return vk_format == VK_FORMAT_R8G8B8A8_SRGB || vk_format == VK_FORMAT_B8G8R8A8_SRGB;
146+
}
147+
148+
static VkFormat srgb_to_unorm_format(VkFormat vk_format) {
149+
if (vk_format == VK_FORMAT_R8G8B8A8_SRGB) {
150+
return VK_FORMAT_R8G8B8A8_UNORM;
151+
} else if (vk_format == VK_FORMAT_B8G8R8A8_SRGB) {
152+
return VK_FORMAT_B8G8R8A8_UNORM;
153+
} else {
154+
UNREACHABLE();
155+
}
156+
}
157+
140158
static int fb_init(struct fb *fb, struct gbm_device *gbm_device, struct vk_renderer *renderer, int width, int height, enum pixfmt pixel_format, uint64_t drm_modifier) {
141159
PFN_vkGetMemoryFdPropertiesKHR get_memory_fd_props;
142160
VkSubresourceLayout layout;
143161
VkDeviceMemory img_device_memory;
144162
struct gbm_bo *bo;
163+
VkFormat vk_format;
145164
VkDevice device;
146165
VkResult ok;
147166
VkImage vkimg;
@@ -151,19 +170,34 @@ static int fb_init(struct fb *fb, struct gbm_device *gbm_device, struct vk_rende
151170

152171
device = vk_renderer_get_device(renderer);
153172

173+
/// FIXME: Right now, using any _SRGB format (for example VK_FORMAT_B8G8R8A8_SRGB) will not work because
174+
/// that'll break some assertions inside flutter / skia. (VK_FORMAT_B8G8R8A8_SRGB maps to GrColorType::kRGBA_8888_SRGB,
175+
/// but some other part of flutter will use GrColorType::kRGBA_8888 so you'll get a mismatch at some point)
176+
/// We're just converting the _SRGB to a _UNORM here, but I'm not really sure that's guaranteed to work.
177+
/// (_UNORM can mean anything basically)
178+
179+
vk_format = get_pixfmt_info(pixel_format)->vk_format;
180+
if (is_srgb_format(vk_format)) {
181+
vk_format = srgb_to_unorm_format(vk_format);
182+
}
183+
154184
ok = vkCreateImage(
155185
device,
156186
&(VkImageCreateInfo){
157187
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
158188
.flags = 0,
159189
.imageType = VK_IMAGE_TYPE_2D,
160-
.format = get_pixfmt_info(pixel_format)->vk_format,
190+
.format = vk_format,
161191
.extent = { .width = width, .height = height, .depth = 1 },
162192
.mipLevels = 1,
163193
.arrayLayers = 1,
164194
.samples = VK_SAMPLE_COUNT_1_BIT,
195+
// Tell vulkan that the tiling we want to use is determined by a DRM format modifier
196+
// (in our case DRM_FORMAT_MOD_LINEAR, but could be something else as well, using a device-supported
197+
// modifier is probably faster if that's possible)
165198
.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT,
166-
.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
199+
// These are the usage flags flutter will use too internally
200+
.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
167201
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
168202
.queueFamilyIndexCount = 0,
169203
.pQueueFamilyIndices = 0,
@@ -180,6 +214,7 @@ static int fb_init(struct fb *fb, struct gbm_device *gbm_device, struct vk_rende
180214
.pPlaneLayouts =
181215
(VkSubresourceLayout[1]){
182216
{
217+
/// These are just dummy values, but they need to be there AFAIK
183218
.offset = 0,
184219
.size = 0,
185220
.rowPitch = 0,
@@ -198,6 +233,8 @@ static int fb_init(struct fb *fb, struct gbm_device *gbm_device, struct vk_rende
198233
return EIO;
199234
}
200235

236+
// We _should_ only have one plane in the linear case.
237+
// Query the layout of that plane to check if it matches the GBM BOs layout.
201238
vkGetImageSubresourceLayout(
202239
device,
203240
vkimg,
@@ -208,7 +245,8 @@ static int fb_init(struct fb *fb, struct gbm_device *gbm_device, struct vk_rende
208245
},
209246
&layout
210247
);
211-
248+
249+
// Create a GBM BO with the actual memory we're going to use
212250
bo = gbm_bo_create_with_modifiers(
213251
gbm_device,
214252
width,
@@ -222,6 +260,7 @@ static int fb_init(struct fb *fb, struct gbm_device *gbm_device, struct vk_rende
222260
goto fail_destroy_image;
223261
}
224262

263+
// Just some paranoid checks that the layout matches (had some issues with that initially)
225264
if (gbm_bo_get_offset(bo, 0) != layout.offset) {
226265
LOG_ERROR("GBM BO layout doesn't match image layout. This is probably a driver / kernel bug.\n");
227266
goto fail_destroy_bo;
@@ -232,6 +271,8 @@ static int fb_init(struct fb *fb, struct gbm_device *gbm_device, struct vk_rende
232271
goto fail_destroy_bo;
233272
}
234273

274+
// gbm_bo_get_fd will dup us a new dmabuf fd.
275+
// So if we don't use it, we need to close it.
235276
fd = gbm_bo_get_fd(bo);
236277
if (fd < 0) {
237278
LOG_ERROR("Couldn't get dmabuf fd for GBM buffer. gbm_bo_get_fd: %s\n", strerror(errno));
@@ -248,7 +289,7 @@ static int fb_init(struct fb *fb, struct gbm_device *gbm_device, struct vk_rende
248289
get_memory_fd_props = (PFN_vkGetMemoryFdPropertiesKHR) vkGetDeviceProcAddr(device, "vkGetMemoryFdPropertiesKHR");
249290
if (get_memory_fd_props == NULL) {
250291
LOG_ERROR("Couldn't resolve vkGetMemoryFdPropertiesKHR.\n");
251-
goto fail_destroy_bo;
292+
goto fail_close_fd;
252293
}
253294

254295
ok = get_memory_fd_props(
@@ -259,7 +300,7 @@ static int fb_init(struct fb *fb, struct gbm_device *gbm_device, struct vk_rende
259300
);
260301
if (ok != VK_SUCCESS) {
261302
LOG_VK_ERROR(ok, "Couldn't get dmabuf memory properties. vkGetMemoryFdPropertiesKHR");
262-
goto fail_destroy_bo;
303+
goto fail_close_fd;
263304
}
264305

265306
// Find out the memory requirements for our image (the supported memory types for import)
@@ -287,10 +328,12 @@ static int fb_init(struct fb *fb, struct gbm_device *gbm_device, struct vk_rende
287328
);
288329
if (mem < 0) {
289330
LOG_ERROR("Couldn't find a memory type that's both supported by the image and the dmabuffer.\n");
290-
goto fail_destroy_bo;
331+
goto fail_close_fd;
291332
}
292333

293334
// now, create a VkDeviceMemory instance from our dmabuf.
335+
// after successful import, the fd is owned by the device memory object
336+
// and we don't need to close it.
294337
ok = vkAllocateMemory(
295338
device,
296339
&(VkMemoryAllocateInfo) {
@@ -314,7 +357,7 @@ static int fb_init(struct fb *fb, struct gbm_device *gbm_device, struct vk_rende
314357
);
315358
if (ok != VK_SUCCESS) {
316359
LOG_VK_ERROR(ok, "Couldn't import dmabuf as vulkan device memory. vkAllocateMemory");
317-
goto fail_destroy_bo;
360+
goto fail_close_fd;
318361
}
319362

320363
ok = vkBindImageMemory2(
@@ -341,14 +384,18 @@ static int fb_init(struct fb *fb, struct gbm_device *gbm_device, struct vk_rende
341384
fb->fl_image = (FlutterVulkanImage) {
342385
.struct_size = sizeof(FlutterVulkanImage),
343386
.image = fb->image,
344-
.format = get_pixfmt_info(pixel_format)->vk_format,
387+
.format = vk_format,
345388
};
346389

347390
return 0;
348391

349392

350393
fail_free_device_memory:
351394
vkFreeMemory(device, img_device_memory, NULL);
395+
goto fail_destroy_bo;
396+
397+
fail_close_fd:
398+
close(fd);
352399

353400
fail_destroy_bo:
354401
gbm_bo_destroy(bo);
@@ -641,8 +688,6 @@ static int vk_gbm_backing_store_present_kms(struct surface *s, const struct fl_l
641688
pixel_format = store->pixel_format;
642689
}
643690

644-
LOG_DEBUG("presenting fb %d\n", store->front_fb - store->locked_fbs);
645-
646691
TRACER_BEGIN(store->surface.tracer, "kms_req_builder_push_fb_layer");
647692
ok = kms_req_builder_push_fb_layer(
648693
builder,
@@ -736,9 +781,7 @@ static int vk_gbm_backing_store_fill(struct backing_store *s, FlutterBackingStor
736781
locked: ;
737782
/// TODO: Remove this once we're using triple buffering
738783
#ifdef DEBUG
739-
int before = atomic_fetch_add(&store->n_locked_fbs, 1);
740-
LOG_DEBUG("filling with fb %d\n", i);
741-
LOG_DEBUG("locked fbs: before: %d, now: %d\n", before, before+1);
784+
atomic_fetch_add(&store->n_locked_fbs, 1);
742785
//DEBUG_ASSERT_MSG(before + 1 <= 3, "sanity check failed: too many locked fbs for double-buffered vsync");
743786
#endif
744787
store->locked_fbs[i].store = CAST_VK_GBM_BACKING_STORE(surface_ref(CAST_SURFACE_UNCHECKED(s)));
@@ -779,7 +822,6 @@ static int vk_gbm_backing_store_queue_present(struct backing_store *s, const Flu
779822
for (int i = 0; i < ARRAY_SIZE(store->locked_fbs); i++) {
780823
if (store->locked_fbs[i].fb->fl_image.image == fl_store->vulkan.image->image) {
781824
fb = store->locked_fbs + i;
782-
LOG_DEBUG("queueing present fb %d\n", i);
783825
break;
784826
}
785827
}
@@ -790,8 +832,13 @@ static int vk_gbm_backing_store_queue_present(struct backing_store *s, const Flu
790832
return EINVAL;
791833
}
792834

835+
// Replace the front fb with the new one
836+
// (will unref the old one if not NULL internally)
793837
locked_fb_swap_ptrs(&store->front_fb, fb);
794838

839+
// Since flutter no longer uses this fb for rendering, we need to unref it
840+
locked_fb_unref(fb);
841+
795842
surface_unlock(CAST_SURFACE_UNCHECKED(s));
796843
return 0;
797844
}

src/vk_renderer.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
FILE_DESCR("vulkan renderer")
1919

20-
static VkLayerProperties *get_layer_props(int n_layers, VkLayerProperties *layers, const char *layer_name) {
20+
MAYBE_UNUSED static VkLayerProperties *get_layer_props(int n_layers, VkLayerProperties *layers, const char *layer_name) {
2121
for (int i = 0; i < n_layers; i++) {
2222
if (strcmp(layers[i].layerName, layer_name) == 0) {
2323
return layers + i;
@@ -26,7 +26,7 @@ static VkLayerProperties *get_layer_props(int n_layers, VkLayerProperties *layer
2626
return NULL;
2727
}
2828

29-
static bool supports_layer(int n_layers, VkLayerProperties *layers, const char *layer_name) {
29+
MAYBE_UNUSED static bool supports_layer(int n_layers, VkLayerProperties *layers, const char *layer_name) {
3030
return get_layer_props(n_layers, layers, layer_name) != NULL;
3131
}
3232

@@ -39,7 +39,7 @@ static VkExtensionProperties *get_extension_props(int n_extensions, VkExtensionP
3939
return NULL;
4040
}
4141

42-
static bool supports_extension(int n_extensions, VkExtensionProperties *extensions, const char *extension_name) {
42+
MAYBE_UNUSED static bool supports_extension(int n_extensions, VkExtensionProperties *extensions, const char *extension_name) {
4343
return get_extension_props(n_extensions, extensions, extension_name) != NULL;
4444
}
4545

0 commit comments

Comments
 (0)