History

hwcontext_vulkan: use host image copy

Lynne committed 1 year ago

Commit a9b2c101 parent f531c915 files · +165 −2Browse files

Unified · Split

5 files
  1. libavutil/hwcontext_vulkan.c +121 −2
  2. libavutil/vulkan.c +35 −0
  3. libavutil/vulkan.h +2 −0
  4. libavutil/vulkan_functions.h +6 −0
  5. libavutil/vulkan_loader.h +1 −0
libavutil/hwcontext_vulkan.c+121−2
@@ -80,6 +80,7 @@ typedef struct VulkanDeviceFeatures { · 79 unmodified lines
80 VkPhysicalDeviceVulkan13Features vulkan_1_3;80 VkPhysicalDeviceVulkan13Features vulkan_1_3;
81 VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore;81 VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore;
82 VkPhysicalDeviceShaderSubgroupRotateFeaturesKHR subgroup_rotate;82 VkPhysicalDeviceShaderSubgroupRotateFeaturesKHR subgroup_rotate;
83 VkPhysicalDeviceHostImageCopyFeaturesEXT host_image_copy;
83 84
84#ifdef VK_KHR_shader_expect_assume85#ifdef VK_KHR_shader_expect_assume
85 VkPhysicalDeviceShaderExpectAssumeFeaturesKHR expect_assume;86 VkPhysicalDeviceShaderExpectAssumeFeaturesKHR expect_assume;
@@ -209,6 +210,8 @@ static void device_features_init(AVHWDeviceContext *ctx, VulkanDeviceFeatures *f · 123 unmodified lines
209 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES);210 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES);
210 FF_VK_STRUCT_EXT(s, &feats->device, &feats->subgroup_rotate, FF_VK_EXT_SUBGROUP_ROTATE,211 FF_VK_STRUCT_EXT(s, &feats->device, &feats->subgroup_rotate, FF_VK_EXT_SUBGROUP_ROTATE,
211 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_ROTATE_FEATURES_KHR);212 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_ROTATE_FEATURES_KHR);
213 FF_VK_STRUCT_EXT(s, &feats->device, &feats->host_image_copy, FF_VK_EXT_HOST_IMAGE_COPY,
214 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES_EXT);
212 215
213#ifdef VK_KHR_shader_expect_assume216#ifdef VK_KHR_shader_expect_assume
214 FF_VK_STRUCT_EXT(s, &feats->device, &feats->expect_assume, FF_VK_EXT_EXPECT_ASSUME,217 FF_VK_STRUCT_EXT(s, &feats->device, &feats->expect_assume, FF_VK_EXT_EXPECT_ASSUME,
@@ -285,6 +288,7 @@ static void device_features_copy_needed(VulkanDeviceFeatures *dst, VulkanDeviceF · 70 unmodified lines
285 288
286 COPY_VAL(timeline_semaphore.timelineSemaphore);289 COPY_VAL(timeline_semaphore.timelineSemaphore);
287 COPY_VAL(subgroup_rotate.shaderSubgroupRotate);290 COPY_VAL(subgroup_rotate.shaderSubgroupRotate);
291 COPY_VAL(host_image_copy.hostImageCopy);
288 292
289 COPY_VAL(video_maintenance_1.videoMaintenance1);293 COPY_VAL(video_maintenance_1.videoMaintenance1);
290#ifdef VK_KHR_video_maintenance2294#ifdef VK_KHR_video_maintenance2
@@ -606,6 +610,7 @@ static const VulkanOptExtension optional_device_exts[] = { · 315 unmodified lines
606 { VK_KHR_COOPERATIVE_MATRIX_EXTENSION_NAME, FF_VK_EXT_COOP_MATRIX },610 { VK_KHR_COOPERATIVE_MATRIX_EXTENSION_NAME, FF_VK_EXT_COOP_MATRIX },
607 { VK_EXT_SHADER_OBJECT_EXTENSION_NAME, FF_VK_EXT_SHADER_OBJECT },611 { VK_EXT_SHADER_OBJECT_EXTENSION_NAME, FF_VK_EXT_SHADER_OBJECT },
608 { VK_KHR_SHADER_SUBGROUP_ROTATE_EXTENSION_NAME, FF_VK_EXT_SUBGROUP_ROTATE },612 { VK_KHR_SHADER_SUBGROUP_ROTATE_EXTENSION_NAME, FF_VK_EXT_SUBGROUP_ROTATE },
613 { VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME, FF_VK_EXT_HOST_IMAGE_COPY },
609#ifdef VK_KHR_shader_expect_assume614#ifdef VK_KHR_shader_expect_assume
610 { VK_KHR_SHADER_EXPECT_ASSUME_EXTENSION_NAME, FF_VK_EXT_EXPECT_ASSUME },615 { VK_KHR_SHADER_EXPECT_ASSUME_EXTENSION_NAME, FF_VK_EXT_EXPECT_ASSUME },
611#endif616#endif
@@ -2825,11 +2830,15 @@ static int vulkan_frames_init(AVHWFramesContext *hwfc) · 2213 unmodified lines
2825 2830
2826 /* Image usage flags */2831 /* Image usage flags */
2827 if (!hwctx->usage) {2832 if (!hwctx->usage) {
2828 hwctx->usage = supported_usage & (VK_IMAGE_USAGE_TRANSFER_DST_BIT |2833 hwctx->usage = supported_usage & (VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
2829 VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
2830 VK_IMAGE_USAGE_STORAGE_BIT |2834 VK_IMAGE_USAGE_STORAGE_BIT |
2831 VK_IMAGE_USAGE_SAMPLED_BIT);2835 VK_IMAGE_USAGE_SAMPLED_BIT);
2832 2836
2837 if (p->vkctx.extensions & FF_VK_EXT_HOST_IMAGE_COPY)
2838 hwctx->usage |= supported_usage & VK_IMAGE_USAGE_HOST_TRANSFER_BIT;
2839 else
2840 hwctx->usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
2841
2833 /* Enables encoding of images, if supported by format and extensions */2842 /* Enables encoding of images, if supported by format and extensions */
2834 if ((supported_usage & VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR) &&2843 if ((supported_usage & VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR) &&
2835 (p->vkctx.extensions & (FF_VK_EXT_VIDEO_ENCODE_QUEUE |2844 (p->vkctx.extensions & (FF_VK_EXT_VIDEO_ENCODE_QUEUE |
@@ -4148,6 +4157,113 @@ fail: · 1312 unmodified lines
4148 return err;4157 return err;
4149}4158}
4150 4159
4160static int vulkan_transfer_host(AVHWFramesContext *hwfc, AVFrame *hwf,
4161 AVFrame *swf, int upload)
4162{
4163 VulkanDevicePriv *p = hwfc->device_ctx->hwctx;
4164 AVVulkanFramesContext *hwfc_vk = hwfc->hwctx;
4165 AVVulkanDeviceContext *hwctx = &p->p;
4166 FFVulkanFunctions *vk = &p->vkctx.vkfn;
4167
4168 AVVkFrame *hwf_vk = (AVVkFrame *)hwf->data[0];
4169 const int planes = av_pix_fmt_count_planes(swf->format);
4170 const int nb_images = ff_vk_count_images(hwf_vk);
4171
4172 VkSemaphoreWaitInfo sem_wait;
4173 VkHostImageLayoutTransitionInfo layout_ch_info[];
4174 int nb_layout_ch = 0;
4175
4176 hwfc_vk->lock_frame(hwfc, hwf_vk);
4177
4178 for (int i = 0; i < nb_images; i++) {
4179 if (hwf_vk->layout[i])
4180 continue;
4181
4182 layout_ch_info[nb_layout_ch++] = (VkHostImageLayoutTransitionInfo) {
4183 .sType = VK_STRUCTURE_TYPE_HOST_IMAGE_LAYOUT_TRANSITION_INFO,
4184 .image = hwf_vk->img[i],
4185 .oldLayout = hwf_vk->layout[i],
4186 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
4187 .subresourceRange = {
4188 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
4189 .levelCount = 1,
4190 .layerCount = 1,
4191 };
4192 };
4193 }
4194
4195 if (nb_layout_ch)
4196 vk->TransitionImageLayoutEXT(hwctx->act_dev,
4197 nb_layout_ch, layout_ch_info);
4198
4199 sem_wait = (VkSemaphoreWaitInfo) {
4200 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
4201 .pSemaphores = hwf_vk->sem,
4202 .pValues = hwf_vk->sem_value,
4203 .semaphoreCount = nb_images,
4204 };
4205
4206 vk->WaitSemaphores(hwctx->act_dev, &sem_wait, UINT64_MAX);
4207
4208 if (upload) {
4209 VkMemoryToImageCopy region_info = {
4210 .sType = VK_STRUCTURE_TYPE_MEMORY_TO_IMAGE_COPY,
4211 .imageSubresource = {
4212 .layerCount = 1,
4213 },
4214 };
4215 VkCopyMemoryToImageInfo copy_info = {
4216 .sType = VK_STRUCTURE_TYPE_COPY_MEMORY_TO_IMAGE_INFO,
4217 .flags = VK_HOST_IMAGE_COPY_MEMCPY,
4218 .regionCount = 1,
4219 .pRegions = &region_info,
4220 };
4221 for (int i = 0; i < planes; i++) {
4222 int img_idx = FFMIN(i, (nb_images - 1));
4223 uint32_t p_w, p_h;
4224 get_plane_wh(&p_w, &p_h, swf->format, swf->width, swf->height, i);
4225
4226 region_info.pHostPointer = swf->data[i];
4227 region_info.imageSubresource.aspectMask = ff_vk_aspect_flag(hwf, i);
4228 region_info.imageExtent = (VkExtent3D){ p_w, p_h, 1 };
4229 copy_info.dstImage = hwf_vk->img[img_idx];
4230 copy_info.dstImageLayout = hwf_vk->layout[img_idx];
4231
4232 vk->CopyMemoryToImageEXT(hwctx->act_dev, &copy_info);
4233 }
4234 } else {
4235 VkImageToMemoryCopy region_info = {
4236 .sType = VK_STRUCTURE_TYPE_MEMORY_TO_IMAGE_COPY,
4237 .imageSubresource = {
4238 .layerCount = 1,
4239 },
4240 };
4241 VkCopyImageToMemoryInfo copy_info = {
4242 .sType = VK_STRUCTURE_TYPE_COPY_IMAGE_TO_MEMORY_INFO,
4243 .flags = VK_HOST_IMAGE_COPY_MEMCPY,
4244 .regionCount = 1,
4245 .pRegions = &region_info,
4246 };
4247 for (int i = 0; i < planes; i++) {
4248 int img_idx = FFMIN(i, (nb_images - 1));
4249 uint32_t p_w, p_h;
4250 get_plane_wh(&p_w, &p_h, swf->format, swf->width, swf->height, i);
4251
4252 region_info.pHostPointer = swf->data[i];
4253 region_info.imageSubresource.aspectMask = ff_vk_aspect_flag(hwf, i);
4254 region_info.imageExtent = (VkExtent3D){ p_w, p_h, 1 };
4255 copy_info.srcImage = hwf_vk->img[img_idx];
4256 copy_info.srcImageLayout = hwf_vk->layout[img_idx];
4257
4258 vk->CopyImageToMemoryEXT(hwctx->act_dev, &copy_info);
4259 }
4260 }
4261
4262 hwfc_vk->unlock_frame(hwfc, hwf_vk);
4263
4264 return 0;
4265}
4266
4151static int vulkan_transfer_frame(AVHWFramesContext *hwfc,4267static int vulkan_transfer_frame(AVHWFramesContext *hwfc,
4152 AVFrame *swf, AVFrame *hwf,4268 AVFrame *swf, AVFrame *hwf,
4153 int upload)4269 int upload)
@@ -4184,6 +4300,9 @@ static int vulkan_transfer_frame(AVHWFramesContext *hwfc, · 30 unmodified lines
4184 if (swf->width > hwfc->width || swf->height > hwfc->height)4300 if (swf->width > hwfc->width || swf->height > hwfc->height)
4185 return AVERROR(EINVAL);4301 return AVERROR(EINVAL);
4186 4302
4303 if (p->vkctx.extensions & FF_VK_EXT_HOST_IMAGE_COPY)
4304 return vulkan_transfer_host(hwfc, hwf, swf, upload);
4305
4187 for (int i = 0; i < av_pix_fmt_count_planes(swf->format); i++) {4306 for (int i = 0; i < av_pix_fmt_count_planes(swf->format); i++) {
4188 uint32_t p_w, p_h;4307 uint32_t p_w, p_h;
4189 get_plane_wh(&p_w, &p_h, swf->format, swf->width, swf->height, i);4308 get_plane_wh(&p_w, &p_h, swf->format, swf->width, swf->height, i);
libavutil/vulkan.c+35−0
@@ -162,6 +162,8 @@ int ff_vk_load_props(FFVulkanContext *s) · 161 unmodified lines
162 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT);162 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT);
163 FF_VK_STRUCT_EXT(s, &s->props, &s->optical_flow_props, FF_VK_EXT_OPTICAL_FLOW,163 FF_VK_STRUCT_EXT(s, &s->props, &s->optical_flow_props, FF_VK_EXT_OPTICAL_FLOW,
164 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_PROPERTIES_NV);164 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_PROPERTIES_NV);
165 FF_VK_STRUCT_EXT(s, &s->props, &s->host_image_props, FF_VK_EXT_HOST_IMAGE_COPY,
166 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_PROPERTIES);
165 167
166 s->feats = (VkPhysicalDeviceFeatures2) {168 s->feats = (VkPhysicalDeviceFeatures2) {
167 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,169 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
@@ -172,7 +174,39 @@ int ff_vk_load_props(FFVulkanContext *s) · 4 unmodified lines
172 FF_VK_STRUCT_EXT(s, &s->feats, &s->atomic_float_feats, FF_VK_EXT_ATOMIC_FLOAT,174 FF_VK_STRUCT_EXT(s, &s->feats, &s->atomic_float_feats, FF_VK_EXT_ATOMIC_FLOAT,
173 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT);175 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT);
174 176
177 /* Try allocating 1024 layouts */
178 s->host_image_copy_layouts = av_malloc(sizeof(*s->host_image_copy_layouts)*1024);
179 s->host_image_props.pCopySrcLayouts = s->host_image_copy_layouts;
180 s->host_image_props.copySrcLayoutCount = 512;
181 s->host_image_props.pCopyDstLayouts = s->host_image_copy_layouts + 512;
182 s->host_image_props.copyDstLayoutCount = 512;
183
175 vk->GetPhysicalDeviceProperties2(s->hwctx->phys_dev, &s->props);184 vk->GetPhysicalDeviceProperties2(s->hwctx->phys_dev, &s->props);
185
186 /* Check if we had enough memory for all layouts */
187 if (s->host_image_props.copySrcLayoutCount == 512 ||
188 s->host_image_props.copyDstLayoutCount == 512) {
189 VkImageLayout *new_array;
190 size_t new_size;
191 s->host_image_props.pCopySrcLayouts =
192 s->host_image_props.pCopyDstLayouts = NULL;
193 s->host_image_props.copySrcLayoutCount =
194 s->host_image_props.copyDstLayoutCount = NULL;
195 vk->GetPhysicalDeviceProperties2(s->hwctx->phys_dev, &s->props);
196
197 new_size = s->host_image_props.copySrcLayoutCount +
198 s->host_image_props.copyDstLayoutCount;
199 new_size *= sizeof(*s->host_image_copy_layouts);
200 new_array = av_realloc(s->host_image_copy_layouts, new_size);
201 if (!new_array)
202 return AVERROR(ENOMEM);
203
204 s->host_image_copy_layouts = new_array;
205 s->host_image_props.pCopySrcLayouts = new_array;
206 s->host_image_props.pCopyDstLayouts = new_array + s->host_image_props.copySrcLayoutCount;
207 vk->GetPhysicalDeviceProperties2(s->hwctx->phys_dev, &s->props);
208 }
209
176 vk->GetPhysicalDeviceMemoryProperties(s->hwctx->phys_dev, &s->mprops);210 vk->GetPhysicalDeviceMemoryProperties(s->hwctx->phys_dev, &s->mprops);
177 vk->GetPhysicalDeviceFeatures2(s->hwctx->phys_dev, &s->feats);211 vk->GetPhysicalDeviceFeatures2(s->hwctx->phys_dev, &s->feats);
178 212
@@ -2927,6 +2961,7 @@ void ff_vk_uninit(FFVulkanContext *s) · 2748 unmodified lines
2927 av_freep(&s->qf_props);2961 av_freep(&s->qf_props);
2928 av_freep(&s->video_props);2962 av_freep(&s->video_props);
2929 av_freep(&s->coop_mat_props);2963 av_freep(&s->coop_mat_props);
2964 av_freep(&s->host_image_copy_layouts);
2930 2965
2931 av_buffer_unref(&s->device_ref);2966 av_buffer_unref(&s->device_ref);
2932 av_buffer_unref(&s->frames_ref);2967 av_buffer_unref(&s->frames_ref);
libavutil/vulkan.h+2−0
@@ -291,6 +291,8 @@ typedef struct FFVulkanContext { · 290 unmodified lines
291 VkQueueFamilyVideoPropertiesKHR *video_props;291 VkQueueFamilyVideoPropertiesKHR *video_props;
292 VkQueueFamilyProperties2 *qf_props;292 VkQueueFamilyProperties2 *qf_props;
293 int tot_nb_qfs;293 int tot_nb_qfs;
294 VkPhysicalDeviceHostImageCopyPropertiesEXT host_image_props;
295 VkImageLayout *host_image_copy_layouts;
294 296
295 VkCooperativeMatrixPropertiesKHR *coop_mat_props;297 VkCooperativeMatrixPropertiesKHR *coop_mat_props;
296 uint32_t coop_mat_props_nb;298 uint32_t coop_mat_props_nb;
libavutil/vulkan_functions.h+6−0
@@ -49,6 +49,7 @@ typedef uint64_t FFVulkanExtensions; · 48 unmodified lines
49#define FF_VK_EXT_RELAXED_EXTENDED_INSTR (1ULL << 15) /* VK_KHR_shader_relaxed_extended_instruction */49#define FF_VK_EXT_RELAXED_EXTENDED_INSTR (1ULL << 15) /* VK_KHR_shader_relaxed_extended_instruction */
50#define FF_VK_EXT_EXPECT_ASSUME (1ULL << 16) /* VK_KHR_shader_expect_assume */50#define FF_VK_EXT_EXPECT_ASSUME (1ULL << 16) /* VK_KHR_shader_expect_assume */
51#define FF_VK_EXT_SUBGROUP_ROTATE (1ULL << 17) /* VK_KHR_shader_subgroup_rotate */51#define FF_VK_EXT_SUBGROUP_ROTATE (1ULL << 17) /* VK_KHR_shader_subgroup_rotate */
52#define FF_VK_EXT_HOST_IMAGE_COPY (1ULL << 18) /* VK_EXT_host_image_copy */
52 53
53/* Video extensions */54/* Video extensions */
54#define FF_VK_EXT_VIDEO_QUEUE (1ULL << 36) /* VK_KHR_video_queue */55#define FF_VK_EXT_VIDEO_QUEUE (1ULL << 36) /* VK_KHR_video_queue */
@@ -207,6 +208,11 @@ typedef uint64_t FFVulkanExtensions; · 152 unmodified lines
207 /* sync2 */ \208 /* sync2 */ \
208 MACRO(1, 1, FF_VK_EXT_NO_FLAG, CmdPipelineBarrier2) \209 MACRO(1, 1, FF_VK_EXT_NO_FLAG, CmdPipelineBarrier2) \
209 \210 \
211 /* Host image copy */ \
212 MACRO(1, 1, FF_VK_EXT_HOST_IMAGE_COPY, TransitionImageLayoutEXT) \
213 MACRO(1, 1, FF_VK_EXT_HOST_IMAGE_COPY, CopyMemoryToImageEXT) \
214 MACRO(1, 1, FF_VK_EXT_HOST_IMAGE_COPY, CopyImageToMemoryEXT) \
215 \
210 /* Video queue */ \216 /* Video queue */ \
211 MACRO(1, 1, FF_VK_EXT_VIDEO_QUEUE, CreateVideoSessionKHR) \217 MACRO(1, 1, FF_VK_EXT_VIDEO_QUEUE, CreateVideoSessionKHR) \
212 MACRO(1, 1, FF_VK_EXT_VIDEO_QUEUE, CreateVideoSessionParametersKHR) \218 MACRO(1, 1, FF_VK_EXT_VIDEO_QUEUE, CreateVideoSessionParametersKHR) \
libavutil/vulkan_loader.h+1−0
@@ -59,6 +59,7 @@ static inline uint64_t ff_vk_extensions_to_mask(const char * const *extensions, · 58 unmodified lines
59 { VK_NV_OPTICAL_FLOW_EXTENSION_NAME, FF_VK_EXT_OPTICAL_FLOW },59 { VK_NV_OPTICAL_FLOW_EXTENSION_NAME, FF_VK_EXT_OPTICAL_FLOW },
60 { VK_EXT_SHADER_OBJECT_EXTENSION_NAME, FF_VK_EXT_SHADER_OBJECT },60 { VK_EXT_SHADER_OBJECT_EXTENSION_NAME, FF_VK_EXT_SHADER_OBJECT },
61 { VK_KHR_SHADER_SUBGROUP_ROTATE_EXTENSION_NAME, FF_VK_EXT_SUBGROUP_ROTATE },61 { VK_KHR_SHADER_SUBGROUP_ROTATE_EXTENSION_NAME, FF_VK_EXT_SUBGROUP_ROTATE },
62 { VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME, FF_VK_EXT_HOST_IMAGE_COPY },
62 { VK_KHR_VIDEO_MAINTENANCE_1_EXTENSION_NAME, FF_VK_EXT_VIDEO_MAINTENANCE_1 },63 { VK_KHR_VIDEO_MAINTENANCE_1_EXTENSION_NAME, FF_VK_EXT_VIDEO_MAINTENANCE_1 },
63#ifdef VK_KHR_video_maintenance264#ifdef VK_KHR_video_maintenance2
64 { VK_KHR_VIDEO_MAINTENANCE_2_EXTENSION_NAME, FF_VK_EXT_VIDEO_MAINTENANCE_2 },65 { VK_KHR_VIDEO_MAINTENANCE_2_EXTENSION_NAME, FF_VK_EXT_VIDEO_MAINTENANCE_2 },

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t