Branch data Line data Source code
1 : : /*
2 : : * This file is part of libplacebo.
3 : : *
4 : : * libplacebo is free software; you can redistribute it and/or
5 : : * modify it under the terms of the GNU Lesser General Public
6 : : * License as published by the Free Software Foundation; either
7 : : * version 2.1 of the License, or (at your option) any later version.
8 : : *
9 : : * libplacebo is distributed in the hope that it will be useful,
10 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : : * GNU Lesser General Public License for more details.
13 : : *
14 : : * You should have received a copy of the GNU Lesser General Public
15 : : * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
16 : : */
17 : :
18 : : #include "gpu.h"
19 : :
20 : 2333 : VK_CB_FUNC_DEF(vk_tex_deref);
21 : :
22 : 2333 : void vk_tex_barrier(pl_gpu gpu, struct vk_cmd *cmd, pl_tex tex,
23 : : VkPipelineStageFlags2 stage, VkAccessFlags2 access,
24 : : VkImageLayout layout, uint32_t qf)
25 : : {
26 : 2333 : struct pl_vk *p = PL_PRIV(gpu);
27 : 2333 : struct vk_ctx *vk = p->vk;
28 : 2333 : struct pl_tex_vk *tex_vk = PL_PRIV(tex);
29 : 2333 : pl_rc_ref(&tex_vk->rc);
30 [ - + ]: 2333 : pl_assert(!tex_vk->held);
31 [ - + ]: 2333 : pl_assert(!tex_vk->num_planes);
32 : :
33 : : // CONCURRENT images require transitioning to/from IGNORED, EXCLUSIVE
34 : : // images require transitioning to/from the concrete QF index
35 [ - + ]: 2333 : if (vk->pools.num == 1) {
36 [ + + ]: 2333 : if (tex_vk->qf == VK_QUEUE_FAMILY_IGNORED)
37 : 469 : tex_vk->qf = cmd->pool->qf;
38 [ + + ]: 2333 : if (qf == VK_QUEUE_FAMILY_IGNORED)
39 : 2329 : qf = cmd->pool->qf;
40 : : }
41 : :
42 : : struct vk_sync_scope last;
43 : 2333 : bool is_trans = layout != tex_vk->layout, is_xfer = qf != tex_vk->qf;
44 : 2333 : last = vk_sem_barrier(cmd, &tex_vk->sem, stage, access, is_trans || is_xfer);
45 : :
46 : 2333 : VkImageMemoryBarrier2 barr = {
47 : : .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
48 : 2333 : .srcStageMask = last.stage,
49 : : .srcAccessMask = last.access,
50 : : .dstStageMask = stage,
51 : : .dstAccessMask = access,
52 : 2333 : .oldLayout = tex_vk->layout,
53 : : .newLayout = layout,
54 : 2333 : .srcQueueFamilyIndex = tex_vk->qf,
55 : : .dstQueueFamilyIndex = qf,
56 : 2333 : .image = tex_vk->img,
57 : : .subresourceRange = {
58 : 2333 : .aspectMask = tex_vk->aspect,
59 : : .levelCount = 1,
60 : : .layerCount = 1,
61 : : },
62 : : };
63 : :
64 [ + + ]: 2333 : if (tex_vk->may_invalidate) {
65 : 807 : tex_vk->may_invalidate = false;
66 : 807 : barr.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
67 : : }
68 : :
69 [ + + ]: 2333 : if (tex_vk->ext_deps.num) {
70 : : // We need to guarantee that all external dependencies are satisfied
71 : : // before the barrier begins executing. The easiest way to ensure this
72 : : // is to add the stage mask at which we wait for the external dependency
73 : : // to the source stage mask of the image barrier.
74 : 14 : barr.srcStageMask |= stage;
75 : : }
76 : :
77 [ + + + + ]: 2333 : if (last.access || is_trans || is_xfer) {
78 : 1831 : vk_cmd_barrier(cmd, &(VkDependencyInfo) {
79 : : .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
80 : : .imageMemoryBarrierCount = 1,
81 : : .pImageMemoryBarriers = &barr,
82 : : });
83 : : }
84 : :
85 : 2333 : tex_vk->qf = qf;
86 : 2333 : tex_vk->layout = layout;
87 : 2333 : vk_cmd_callback(cmd, VK_CB_FUNC(vk_tex_deref), gpu, tex);
88 : :
89 [ + + ]: 2347 : for (int i = 0; i < tex_vk->ext_deps.num; i++)
90 : 14 : vk_cmd_dep(cmd, stage, tex_vk->ext_deps.elem[i]);
91 : 2333 : tex_vk->ext_deps.num = 0;
92 : 2333 : }
93 : :
94 : 604 : static void vk_tex_destroy(pl_gpu gpu, struct pl_tex_t *tex)
95 : : {
96 [ + - ]: 604 : if (!tex)
97 : : return;
98 : :
99 : 604 : struct pl_vk *p = PL_PRIV(gpu);
100 : 604 : struct vk_ctx *vk = p->vk;
101 : 604 : struct pl_tex_vk *tex_vk = PL_PRIV(tex);
102 : :
103 : 604 : vk->DestroyFramebuffer(vk->dev, tex_vk->framebuffer, PL_VK_ALLOC);
104 : 604 : vk->DestroyImageView(vk->dev, tex_vk->view, PL_VK_ALLOC);
105 [ + + ]: 607 : for (int i = 0; i < tex_vk->num_planes; i++)
106 : 3 : vk_tex_deref(gpu, tex->planes[i]);
107 [ + + ]: 604 : if (!tex_vk->external_img) {
108 : 596 : vk->DestroyImage(vk->dev, tex_vk->img, PL_VK_ALLOC);
109 : 596 : vk_malloc_free(vk->ma, &tex_vk->mem);
110 : : }
111 : :
112 : 604 : pl_free(tex);
113 : : }
114 : :
115 : 2831 : void vk_tex_deref(pl_gpu gpu, pl_tex tex)
116 : : {
117 [ + + ]: 2831 : if (!tex)
118 : : return;
119 : :
120 : 2828 : struct pl_tex_vk *tex_vk = PL_PRIV(tex);
121 [ + + ]: 2828 : if (pl_rc_deref(&tex_vk->rc))
122 : 495 : vk_tex_destroy(gpu, (struct pl_tex_t *) tex);
123 : : }
124 : :
125 : :
126 : : // Initializes non-VkImage values like the image view, framebuffers, etc.
127 : 495 : static bool vk_init_image(pl_gpu gpu, pl_tex tex, pl_debug_tag debug_tag)
128 : : {
129 : 495 : struct pl_vk *p = PL_PRIV(gpu);
130 : 495 : struct vk_ctx *vk = p->vk;
131 : :
132 : : const struct pl_tex_params *params = &tex->params;
133 : 495 : struct pl_tex_vk *tex_vk = PL_PRIV(tex);
134 [ - + ]: 495 : pl_assert(tex_vk->img);
135 [ + - ]: 495 : PL_VK_NAME(IMAGE, tex_vk->img, debug_tag);
136 : 495 : pl_rc_init(&tex_vk->rc);
137 [ + - ]: 495 : if (tex_vk->num_planes)
138 : : return true;
139 : 495 : tex_vk->layout = VK_IMAGE_LAYOUT_UNDEFINED;
140 : 495 : tex_vk->transfer_queue = GRAPHICS;
141 : 495 : tex_vk->qf = VK_QUEUE_FAMILY_IGNORED; // will be set on first use, if needed
142 : :
143 : : // Always use the transfer pool if available, for efficiency
144 [ + + + - ]: 495 : if ((params->host_writable || params->host_readable) && vk->pool_transfer)
145 : 388 : tex_vk->transfer_queue = TRANSFER;
146 : :
147 : : // For emulated formats: force usage of the compute queue, because we
148 : : // can't properly track cross-queue dependencies for buffers (yet?)
149 [ + + ]: 495 : if (params->format->emulated)
150 : 18 : tex_vk->transfer_queue = COMPUTE;
151 : :
152 : : bool ret = false;
153 : 495 : VkRenderPass dummyPass = VK_NULL_HANDLE;
154 : :
155 [ + + ]: 495 : if (params->sampleable || params->renderable || params->storable) {
156 : : static const VkImageViewType viewType[] = {
157 : : [VK_IMAGE_TYPE_1D] = VK_IMAGE_VIEW_TYPE_1D,
158 : : [VK_IMAGE_TYPE_2D] = VK_IMAGE_VIEW_TYPE_2D,
159 : : [VK_IMAGE_TYPE_3D] = VK_IMAGE_VIEW_TYPE_3D,
160 : : };
161 : :
162 : 145 : const VkImageViewCreateInfo vinfo = {
163 : : .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
164 : 145 : .image = tex_vk->img,
165 : 145 : .viewType = viewType[tex_vk->type],
166 : 145 : .format = tex_vk->img_fmt,
167 : : .subresourceRange = {
168 : 145 : .aspectMask = tex_vk->aspect,
169 : : .levelCount = 1,
170 : : .layerCount = 1,
171 : : },
172 : : };
173 : :
174 [ - + ]: 145 : VK(vk->CreateImageView(vk->dev, &vinfo, PL_VK_ALLOC, &tex_vk->view));
175 [ + - ]: 145 : PL_VK_NAME(IMAGE_VIEW, tex_vk->view, debug_tag);
176 : : }
177 : :
178 [ + + ]: 495 : if (params->renderable) {
179 : : // Framebuffers need to be created against a specific render pass
180 : : // layout, so we need to temporarily create a skeleton/dummy render
181 : : // pass for vulkan to figure out the compatibility
182 : 27 : VkRenderPassCreateInfo rinfo = {
183 : : .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
184 : : .attachmentCount = 1,
185 : 27 : .pAttachments = &(VkAttachmentDescription) {
186 : 27 : .format = tex_vk->img_fmt,
187 : : .samples = VK_SAMPLE_COUNT_1_BIT,
188 : : .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
189 : : .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
190 : : .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
191 : : .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
192 : : },
193 : : .subpassCount = 1,
194 : 27 : .pSubpasses = &(VkSubpassDescription) {
195 : : .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
196 : : .colorAttachmentCount = 1,
197 : 27 : .pColorAttachments = &(VkAttachmentReference) {
198 : : .attachment = 0,
199 : : .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
200 : : },
201 : : },
202 : : };
203 : :
204 [ - + ]: 27 : VK(vk->CreateRenderPass(vk->dev, &rinfo, PL_VK_ALLOC, &dummyPass));
205 : :
206 : 27 : VkFramebufferCreateInfo finfo = {
207 : : .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
208 : : .renderPass = dummyPass,
209 : : .attachmentCount = 1,
210 : 27 : .pAttachments = &tex_vk->view,
211 : 27 : .width = tex->params.w,
212 : 27 : .height = tex->params.h,
213 : : .layers = 1,
214 : : };
215 : :
216 [ + - ]: 27 : if (finfo.width > vk->props.limits.maxFramebufferWidth ||
217 [ - + ]: 27 : finfo.height > vk->props.limits.maxFramebufferHeight)
218 : : {
219 : 0 : PL_ERR(gpu, "Framebuffer of size %dx%d exceeds the maximum allowed "
220 : : "dimensions: %dx%d", finfo.width, finfo.height,
221 : : vk->props.limits.maxFramebufferWidth,
222 : : vk->props.limits.maxFramebufferHeight);
223 : 0 : goto error;
224 : : }
225 : :
226 [ - + ]: 27 : VK(vk->CreateFramebuffer(vk->dev, &finfo, PL_VK_ALLOC,
227 : : &tex_vk->framebuffer));
228 [ + - ]: 27 : PL_VK_NAME(FRAMEBUFFER, tex_vk->framebuffer, debug_tag);
229 : : }
230 : :
231 : : ret = true;
232 : :
233 : 495 : error:
234 : 495 : vk->DestroyRenderPass(vk->dev, dummyPass, PL_VK_ALLOC);
235 : 495 : return ret;
236 : : }
237 : :
238 : 596 : pl_tex vk_tex_create(pl_gpu gpu, const struct pl_tex_params *params)
239 : : {
240 : 596 : struct pl_vk *p = PL_PRIV(gpu);
241 : 596 : struct vk_ctx *vk = p->vk;
242 : :
243 : 596 : enum pl_handle_type handle_type = params->export_handle |
244 : 596 : params->import_handle;
245 : 596 : VkExternalMemoryHandleTypeFlagBitsKHR vk_handle_type = vk_mem_handle_type(handle_type);
246 : :
247 : 596 : struct pl_tex_t *tex = pl_zalloc_obj(NULL, tex, struct pl_tex_vk);
248 : 596 : pl_fmt fmt = params->format;
249 : 596 : tex->params = *params;
250 : 596 : tex->params.initial_data = NULL;
251 : 596 : tex->sampler_type = PL_SAMPLER_NORMAL;
252 : :
253 : 596 : struct pl_tex_vk *tex_vk = PL_PRIV(tex);
254 : 596 : struct pl_fmt_vk *fmtp = PL_PRIV(fmt);
255 : 596 : tex_vk->img_fmt = fmtp->vk_fmt->tfmt;
256 : 596 : tex_vk->num_planes = fmt->num_planes;
257 [ + + ]: 599 : for (int i = 0; i < tex_vk->num_planes; i++)
258 : 3 : tex_vk->aspect |= VK_IMAGE_ASPECT_PLANE_0_BIT << i;
259 [ + + ]: 596 : tex_vk->aspect = PL_DEF(tex_vk->aspect, VK_IMAGE_ASPECT_COLOR_BIT);
260 : :
261 : : switch (pl_tex_params_dimension(*params)) {
262 : 183 : case 1: tex_vk->type = VK_IMAGE_TYPE_1D; break;
263 : 248 : case 2: tex_vk->type = VK_IMAGE_TYPE_2D; break;
264 : 165 : case 3: tex_vk->type = VK_IMAGE_TYPE_3D; break;
265 : : }
266 : :
267 [ + + ]: 596 : if (fmt->emulated) {
268 : 48 : tex_vk->texel_fmt = pl_find_fmt(gpu, fmt->type, 1, 0,
269 : 24 : fmt->host_bits[0],
270 : : PL_FMT_CAP_TEXEL_UNIFORM);
271 [ - + ]: 24 : if (!tex_vk->texel_fmt) {
272 : 0 : PL_ERR(gpu, "Failed picking texel format for emulated texture!");
273 : 0 : goto error;
274 : : }
275 : :
276 : : // Our format emulation requires storage image support. In order to
277 : : // make a bunch of checks happy, just mark it off as storable (and also
278 : : // enable VK_IMAGE_USAGE_STORAGE_BIT, which we do below)
279 : 24 : tex->params.storable = true;
280 : : }
281 : :
282 [ - + ]: 596 : if (fmtp->blit_emulated) {
283 : : // Enable what's required for sampling
284 : 0 : tex->params.sampleable = fmt->caps & PL_FMT_CAP_SAMPLEABLE;
285 : 0 : tex->params.storable = true;
286 : : }
287 : :
288 : : // Blit emulation on planar textures requires storage
289 [ + + + + ]: 596 : if ((params->blit_src || params->blit_dst) && tex_vk->num_planes)
290 : 1 : tex->params.storable = true;
291 : :
292 : : VkImageUsageFlags usage = 0;
293 : : VkImageCreateFlags flags = 0;
294 [ + + ]: 596 : if (tex->params.sampleable)
295 : : usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
296 [ + + ]: 596 : if (tex->params.renderable)
297 : 19 : usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
298 [ + + ]: 596 : if (tex->params.storable)
299 : 46 : usage |= VK_IMAGE_USAGE_STORAGE_BIT;
300 [ + + ]: 596 : if (tex->params.host_readable || tex->params.blit_src)
301 : 488 : usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
302 [ + + + + ]: 596 : if (tex->params.host_writable || tex->params.blit_dst || params->initial_data)
303 : 581 : usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
304 : :
305 [ + + ]: 15 : if (!usage) {
306 : : // Vulkan requires images have at least *some* image usage set, but our
307 : : // API is perfectly happy with a (useless) image. So just put
308 : : // VK_IMAGE_USAGE_TRANSFER_DST_BIT since this harmless.
309 : : usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
310 : : }
311 : :
312 [ + + ]: 596 : if (tex_vk->num_planes) {
313 : : flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT |
314 : : VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
315 : : }
316 : :
317 : : // FIXME: Since we can't keep track of queue family ownership properly,
318 : : // and we don't know in advance what types of queue families this image
319 : : // will belong to, we're forced to share all of our images between all
320 : : // command pools.
321 : 596 : uint32_t qfs[3] = {0};
322 [ + - ]: 596 : pl_assert(vk->pools.num <= PL_ARRAY_SIZE(qfs));
323 [ + + ]: 1192 : for (int i = 0; i < vk->pools.num; i++)
324 : 596 : qfs[i] = vk->pools.elem[i]->qf;
325 : :
326 : 1192 : VkImageDrmFormatModifierExplicitCreateInfoEXT drm_explicit = {
327 : : .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT,
328 : 596 : .drmFormatModifier = params->shared_mem.drm_format_mod,
329 : : .drmFormatModifierPlaneCount = 1,
330 : 1788 : .pPlaneLayouts = &(VkSubresourceLayout) {
331 : 594 : .rowPitch = PL_DEF(params->shared_mem.stride_w, params->w),
332 [ + + + - ]: 596 : .depthPitch = params->d ? PL_DEF(params->shared_mem.stride_h, params->h) : 0,
333 [ + + ]: 596 : .offset = params->shared_mem.offset,
334 : : },
335 : : };
336 : :
337 : : #ifdef VK_EXT_metal_objects
338 : : VkImportMetalTextureInfoEXT import_metal_tex = {
339 : : .sType = VK_STRUCTURE_TYPE_IMPORT_METAL_TEXTURE_INFO_EXT,
340 : : .plane = VK_IMAGE_ASPECT_PLANE_0_BIT << params->shared_mem.plane,
341 : : };
342 : :
343 : : VkImportMetalIOSurfaceInfoEXT import_iosurface = {
344 : : .sType = VK_STRUCTURE_TYPE_IMPORT_METAL_IO_SURFACE_INFO_EXT,
345 : : };
346 : : #endif
347 : :
348 : 596 : VkImageDrmFormatModifierListCreateInfoEXT drm_list = {
349 : : .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT,
350 : 596 : .drmFormatModifierCount = fmt->num_modifiers,
351 : 596 : .pDrmFormatModifiers = fmt->modifiers,
352 : : };
353 : :
354 : 596 : VkExternalMemoryImageCreateInfoKHR ext_info = {
355 : : .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR,
356 : : .handleTypes = vk_handle_type,
357 : : };
358 : :
359 : 1192 : VkImageCreateInfo iinfo = {
360 : : .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
361 [ + + ]: 596 : .pNext = vk_handle_type ? &ext_info : NULL,
362 : 596 : .imageType = tex_vk->type,
363 : 596 : .format = tex_vk->img_fmt,
364 : : .extent = (VkExtent3D) {
365 : 596 : .width = params->w,
366 : 596 : .height = PL_MAX(1, params->h),
367 : 596 : .depth = PL_MAX(1, params->d)
368 : : },
369 : : .mipLevels = 1,
370 : : .arrayLayers = 1,
371 : : .samples = VK_SAMPLE_COUNT_1_BIT,
372 : : .tiling = VK_IMAGE_TILING_OPTIMAL,
373 : : .usage = usage,
374 : : .flags = flags,
375 : : .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
376 : : .sharingMode = vk->pools.num > 1 ? VK_SHARING_MODE_CONCURRENT
377 : 596 : : VK_SHARING_MODE_EXCLUSIVE,
378 : : .queueFamilyIndexCount = vk->pools.num,
379 : : .pQueueFamilyIndices = qfs,
380 : : };
381 : :
382 : 596 : struct vk_malloc_params mparams = {
383 : : .optimal = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
384 : 596 : .export_handle = params->export_handle,
385 : 596 : .import_handle = params->import_handle,
386 : : .shared_mem = params->shared_mem,
387 : 596 : .debug_tag = params->debug_tag,
388 : : };
389 : :
390 [ + + ]: 596 : if (params->import_handle == PL_HANDLE_DMA_BUF) {
391 : 2 : vk_link_struct(&iinfo, &drm_explicit);
392 : 2 : iinfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
393 : 2 : mparams.shared_mem.offset = 0x0; // handled via plane offsets
394 : : }
395 : :
396 : : #ifdef VK_EXT_metal_objects
397 : : if (params->import_handle == PL_HANDLE_MTL_TEX) {
398 : : vk_link_struct(&iinfo, &import_metal_tex);
399 : : import_metal_tex.mtlTexture = params->shared_mem.handle.handle;
400 : : }
401 : :
402 : : if (params->import_handle == PL_HANDLE_IOSURFACE) {
403 : : vk_link_struct(&iinfo, &import_iosurface);
404 : : import_iosurface.ioSurface = params->shared_mem.handle.handle;
405 : : }
406 : : #endif
407 : :
408 [ + + ]: 596 : if (params->export_handle == PL_HANDLE_DMA_BUF) {
409 [ - + ]: 4 : pl_assert(drm_list.drmFormatModifierCount > 0);
410 : 4 : vk_link_struct(&iinfo, &drm_list);
411 : 4 : iinfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
412 : : }
413 : :
414 : : // Double-check physical image format limits and fail if invalid
415 : 596 : VkPhysicalDeviceImageDrmFormatModifierInfoEXT drm_pinfo = {
416 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT,
417 : 596 : .sharingMode = iinfo.sharingMode,
418 : 596 : .queueFamilyIndexCount = iinfo.queueFamilyIndexCount,
419 : 596 : .pQueueFamilyIndices = iinfo.pQueueFamilyIndices,
420 : : };
421 : :
422 : 596 : VkPhysicalDeviceExternalImageFormatInfoKHR ext_pinfo = {
423 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR,
424 : 596 : .handleType = ext_info.handleTypes,
425 : : };
426 : :
427 [ + + ]: 596 : if (handle_type == PL_HANDLE_DMA_BUF) {
428 [ + + ]: 6 : if (params->import_handle) {
429 : : // On import, we know exactly which format modifier to test
430 : 2 : drm_pinfo.drmFormatModifier = drm_explicit.drmFormatModifier;
431 : : } else {
432 : : // On export, the choice of format modifier is ambiguous, because
433 : : // we offer the implementation a whole list to choose from. In
434 : : // principle, we must check *all* supported drm format modifiers,
435 : : // but in practice it should hopefully suffice to just check one
436 : 4 : drm_pinfo.drmFormatModifier = drm_list.pDrmFormatModifiers[0];
437 : : }
438 : 6 : vk_link_struct(&ext_pinfo, &drm_pinfo);
439 : : }
440 : :
441 : 1192 : VkPhysicalDeviceImageFormatInfo2KHR pinfo = {
442 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR,
443 [ + + ]: 596 : .pNext = vk_handle_type ? &ext_pinfo : NULL,
444 : 596 : .format = iinfo.format,
445 : 596 : .type = iinfo.imageType,
446 : 596 : .tiling = iinfo.tiling,
447 : 596 : .usage = iinfo.usage,
448 : 596 : .flags = iinfo.flags,
449 : : };
450 : :
451 : 596 : VkExternalImageFormatPropertiesKHR ext_props = {
452 : : .sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR,
453 : : };
454 : :
455 : 1192 : VkImageFormatProperties2KHR props = {
456 : : .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR,
457 [ + + ]: 596 : .pNext = vk_handle_type ? &ext_props : NULL,
458 : : };
459 : :
460 : : VkResult res;
461 : 596 : res = vk->GetPhysicalDeviceImageFormatProperties2KHR(vk->physd, &pinfo, &props);
462 [ + + ]: 596 : if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) {
463 : 109 : PL_DEBUG(gpu, "Texture creation failed: not supported");
464 : 109 : goto error;
465 : : } else {
466 [ - + ]: 487 : PL_VK_ASSERT(res, "Querying image format properties");
467 : : }
468 : :
469 : 487 : VkExtent3D max = props.imageFormatProperties.maxExtent;
470 [ + - + - : 487 : if (params->w > max.width || params->h > max.height || params->d > max.depth)
- + ]
471 : : {
472 : 0 : PL_ERR(gpu, "Requested image size %dx%dx%d exceeds the maximum allowed "
473 : : "dimensions %dx%dx%d for vulkan image format %x",
474 : : params->w, params->h, params->d, max.width, max.height, max.depth,
475 : : (unsigned) iinfo.format);
476 : 0 : goto error;
477 : : }
478 : :
479 : : // Ensure the handle type is supported
480 [ + + ]: 487 : if (vk_handle_type) {
481 : 8 : bool ok = vk_external_mem_check(vk, &ext_props.externalMemoryProperties,
482 : 8 : handle_type, params->import_handle);
483 [ - + ]: 8 : if (!ok) {
484 : 0 : PL_ERR(gpu, "Requested handle type is not compatible with the "
485 : : "specified combination of image parameters. Possibly the "
486 : : "handle type is unsupported altogether?");
487 : 0 : goto error;
488 : : }
489 : : }
490 : :
491 [ - + ]: 487 : VK(vk->CreateImage(vk->dev, &iinfo, PL_VK_ALLOC, &tex_vk->img));
492 : 487 : tex_vk->usage_flags = iinfo.usage;
493 : :
494 : 487 : VkMemoryDedicatedRequirements ded_reqs = {
495 : : .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR,
496 : : };
497 : :
498 : 487 : VkMemoryRequirements2 reqs = {
499 : : .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR,
500 : : .pNext = &ded_reqs,
501 : : };
502 : :
503 : 487 : VkImageMemoryRequirementsInfo2 req_info = {
504 : : .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR,
505 : 487 : .image = tex_vk->img,
506 : : };
507 : :
508 : 487 : vk->GetImageMemoryRequirements2(vk->dev, &req_info, &reqs);
509 : 487 : mparams.reqs = reqs.memoryRequirements;
510 [ - + ]: 487 : if (ded_reqs.prefersDedicatedAllocation) {
511 : 0 : mparams.ded_image = tex_vk->img;
512 [ # # ]: 0 : if (vk_mem_handle_type(params->import_handle))
513 : 0 : mparams.shared_mem.size = reqs.memoryRequirements.size;
514 : : }
515 : :
516 [ + + ]: 487 : const char *debug_tag = params->debug_tag ? params->debug_tag :
517 [ + + ]: 11 : params->import_handle ? "imported" : "created";
518 : :
519 [ + + + - ]: 487 : if (!params->import_handle || vk_mem_handle_type(params->import_handle)) {
520 : 487 : struct vk_memslice *mem = &tex_vk->mem;
521 [ - + ]: 487 : if (!vk_malloc_slice(vk->ma, mem, &mparams))
522 : 0 : goto error;
523 : :
524 [ - + ]: 487 : VK(vk->BindImageMemory(vk->dev, tex_vk->img, mem->vkmem, mem->offset));
525 : : }
526 : :
527 : : static const char * const plane_names[4] = {
528 : : "plane 0", "plane 1", "plane 2", "plane 3",
529 : : };
530 : :
531 [ - + ]: 487 : if (tex_vk->num_planes) {
532 [ # # ]: 0 : for (int i = 0; i < tex_vk->num_planes; i++) {
533 : : struct pl_tex_t *plane;
534 : :
535 [ # # ]: 0 : pl_assert(tex_vk->type == VK_IMAGE_TYPE_2D);
536 [ # # ]: 0 : plane = (struct pl_tex_t *) pl_vulkan_wrap(gpu, pl_vulkan_wrap_params(
537 : : .image = tex_vk->img,
538 : : .aspect = VK_IMAGE_ASPECT_PLANE_0_BIT << i,
539 : : .width = PL_RSHIFT_UP(tex->params.w, fmt->planes[i].shift_x),
540 : : .height = PL_RSHIFT_UP(tex->params.h, fmt->planes[i].shift_y),
541 : : .format = fmtp->vk_fmt->pfmt[i].fmt,
542 : : .usage = usage,
543 : : .user_data = params->user_data,
544 : : .debug_tag = PL_DEF(params->debug_tag, plane_names[i]),
545 : : ));
546 [ # # ]: 0 : if (!plane)
547 : 0 : goto error;
548 : 0 : plane->parent = tex;
549 : 0 : tex->planes[i] = plane;
550 : 0 : tex_vk->planes[i] = PL_PRIV(plane);
551 : 0 : tex_vk->planes[i]->held = false;
552 : 0 : tex_vk->planes[i]->layout = tex_vk->layout;
553 : : }
554 : :
555 : : // Explicitly mask out all usage flags from planar parent images
556 [ # # ]: 0 : pl_assert(!fmt->caps);
557 : 0 : tex->params.sampleable = false;
558 : 0 : tex->params.renderable = false;
559 : 0 : tex->params.storable = false;
560 : 0 : tex->params.blit_src = false;
561 : 0 : tex->params.blit_dst = false;
562 : 0 : tex->params.host_writable = false;
563 : 0 : tex->params.host_readable = false;
564 : : }
565 : :
566 [ - + ]: 487 : if (!vk_init_image(gpu, tex, debug_tag))
567 : 0 : goto error;
568 : :
569 [ + + ]: 487 : if (params->export_handle)
570 : 6 : tex->shared_mem = tex_vk->mem.shared_mem;
571 : :
572 [ + + ]: 487 : if (params->export_handle == PL_HANDLE_DMA_BUF) {
573 [ + - ]: 4 : if (vk->GetImageDrmFormatModifierPropertiesEXT) {
574 : :
575 : : // Query the DRM format modifier and plane layout from the driver
576 : 4 : VkImageDrmFormatModifierPropertiesEXT mod_props = {
577 : : .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT,
578 : : };
579 : :
580 [ - + ]: 4 : VK(vk->GetImageDrmFormatModifierPropertiesEXT(vk->dev, tex_vk->img, &mod_props));
581 : 4 : tex->shared_mem.drm_format_mod = mod_props.drmFormatModifier;
582 : :
583 : 4 : VkSubresourceLayout layout = {0};
584 : 4 : VkImageSubresource plane = {
585 : : .aspectMask = VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT,
586 : : };
587 : :
588 : 4 : vk->GetImageSubresourceLayout(vk->dev, tex_vk->img, &plane, &layout);
589 [ - + ]: 4 : if (layout.offset != 0) {
590 : 0 : PL_ERR(gpu, "Exported DRM plane 0 has nonzero offset %zu, "
591 : : "this should never happen! Erroring for safety...",
592 : : (size_t) layout.offset);
593 : 0 : goto error;
594 : : }
595 : 4 : tex->shared_mem.stride_w = layout.rowPitch;
596 : 4 : tex->shared_mem.stride_h = layout.depthPitch;
597 : :
598 : : } else {
599 : :
600 : : // Fallback for no modifiers, just do something stupid.
601 : 0 : tex->shared_mem.drm_format_mod = DRM_FORMAT_MOD_INVALID;
602 : 0 : tex->shared_mem.stride_w = params->w;
603 : 0 : tex->shared_mem.stride_h = params->h;
604 : :
605 : : }
606 : : }
607 : :
608 [ + + ]: 487 : if (params->initial_data) {
609 : 85 : struct pl_tex_transfer_params ul_params = {
610 : : .tex = tex,
611 : : .ptr = (void *) params->initial_data,
612 : 85 : .rc = { 0, 0, 0, params->w, params->h, params->d },
613 : : };
614 : :
615 : : // Since we re-use GPU helpers which require writable images, just fake it
616 : 85 : bool writable = tex->params.host_writable;
617 : 85 : tex->params.host_writable = true;
618 [ - + ]: 85 : if (!pl_tex_upload(gpu, &ul_params))
619 : 0 : goto error;
620 : 85 : tex->params.host_writable = writable;
621 : : }
622 : :
623 : : return tex;
624 : :
625 : 109 : error:
626 : 109 : vk_tex_destroy(gpu, tex);
627 : 109 : return NULL;
628 : : }
629 : :
630 : 1482 : void vk_tex_invalidate(pl_gpu gpu, pl_tex tex)
631 : : {
632 : 1482 : struct pl_tex_vk *tex_vk = PL_PRIV(tex);
633 : 1482 : tex_vk->may_invalidate = true;
634 [ - + ]: 1482 : for (int i = 0; i < tex_vk->num_planes; i++)
635 : 0 : tex_vk->planes[i]->may_invalidate = true;
636 : 1482 : }
637 : :
638 : 0 : static bool tex_clear_fallback(pl_gpu gpu, pl_tex tex,
639 : : const union pl_clear_color color)
640 : : {
641 : 0 : pl_tex pixel = pl_tex_create(gpu, pl_tex_params(
642 : : .w = 1,
643 : : .h = 1,
644 : : .format = tex->params.format,
645 : : .storable = true,
646 : : .blit_src = true,
647 : : .blit_dst = true,
648 : : ));
649 [ # # ]: 0 : if (!pixel)
650 : : return false;
651 : :
652 : 0 : pl_tex_clear_ex(gpu, pixel, color);
653 : :
654 [ # # ]: 0 : pl_assert(tex->params.storable);
655 : 0 : pl_tex_blit(gpu, pl_tex_blit_params(
656 : : .src = pixel,
657 : : .dst = tex,
658 : : .sample_mode = PL_TEX_SAMPLE_NEAREST,
659 : : ));
660 : :
661 : 0 : pl_tex_destroy(gpu, &pixel);
662 : 0 : return true;
663 : : }
664 : :
665 : 202 : void vk_tex_clear_ex(pl_gpu gpu, pl_tex tex, const union pl_clear_color color)
666 : : {
667 : 202 : struct pl_vk *p = PL_PRIV(gpu);
668 : 202 : struct vk_ctx *vk = p->vk;
669 : 202 : struct pl_tex_vk *tex_vk = PL_PRIV(tex);
670 : :
671 [ - + ]: 202 : if (tex_vk->aspect != VK_IMAGE_ASPECT_COLOR_BIT) {
672 [ # # ]: 0 : if (!tex_clear_fallback(gpu, tex, color)) {
673 : 0 : PL_ERR(gpu, "Failed clearing imported planar image: color aspect "
674 : : "clears disallowed by spec and no shader fallback "
675 : : "available");
676 : : }
677 : 0 : return;
678 : : }
679 : :
680 : 202 : struct vk_cmd *cmd = CMD_BEGIN(GRAPHICS);
681 [ + - ]: 202 : if (!cmd)
682 : : return;
683 : :
684 : 202 : vk_tex_barrier(gpu, cmd, tex, VK_PIPELINE_STAGE_2_CLEAR_BIT,
685 : : VK_ACCESS_2_TRANSFER_WRITE_BIT,
686 : : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
687 : : VK_QUEUE_FAMILY_IGNORED);
688 : :
689 : : pl_static_assert(sizeof(VkClearColorValue) == sizeof(union pl_clear_color));
690 : : const VkClearColorValue *clearColor = (const VkClearColorValue *) &color;
691 : :
692 [ - + ]: 202 : pl_assert(tex_vk->aspect == VK_IMAGE_ASPECT_COLOR_BIT);
693 : : static const VkImageSubresourceRange range = {
694 : : .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
695 : : .levelCount = 1,
696 : : .layerCount = 1,
697 : : };
698 : :
699 : 202 : vk->CmdClearColorImage(cmd->buf, tex_vk->img, tex_vk->layout,
700 : : clearColor, 1, &range);
701 : :
702 : 202 : CMD_FINISH(&cmd);
703 : : }
704 : :
705 : 156 : void vk_tex_blit(pl_gpu gpu, const struct pl_tex_blit_params *params)
706 : : {
707 : 156 : struct pl_vk *p = PL_PRIV(gpu);
708 : 156 : struct vk_ctx *vk = p->vk;
709 : 156 : struct pl_tex_vk *src_vk = PL_PRIV(params->src);
710 : 156 : struct pl_tex_vk *dst_vk = PL_PRIV(params->dst);
711 : 156 : struct pl_fmt_vk *src_fmtp = PL_PRIV(params->src->params.format);
712 : 156 : struct pl_fmt_vk *dst_fmtp = PL_PRIV(params->dst->params.format);
713 [ + - - + ]: 156 : bool blit_emulated = src_fmtp->blit_emulated || dst_fmtp->blit_emulated;
714 [ + - ]: 156 : bool planar_fallback = src_vk->aspect != VK_IMAGE_ASPECT_COLOR_BIT ||
715 [ - + ]: 156 : dst_vk->aspect != VK_IMAGE_ASPECT_COLOR_BIT;
716 : :
717 : 156 : pl_rect3d src_rc = params->src_rc, dst_rc = params->dst_rc;
718 [ + - - + : 156 : bool requires_scaling = !pl_rect3d_eq(src_rc, dst_rc);
- + - + -
+ - + ]
719 [ + - - + ]: 156 : if ((requires_scaling && blit_emulated) || planar_fallback) {
720 [ # # ]: 0 : if (!pl_tex_blit_compute(gpu, params))
721 : 0 : PL_ERR(gpu, "Failed emulating texture blit, incompatible textures?");
722 : 0 : return;
723 : : }
724 : :
725 : 156 : struct vk_cmd *cmd = CMD_BEGIN(GRAPHICS);
726 [ + - ]: 156 : if (!cmd)
727 : : return;
728 : :
729 : : // When the blit operation doesn't require scaling, we can use the more
730 : : // efficient vkCmdCopyImage instead of vkCmdBlitImage
731 [ + - ]: 156 : if (!requires_scaling) {
732 : 156 : vk_tex_barrier(gpu, cmd, params->src, VK_PIPELINE_STAGE_2_COPY_BIT,
733 : : VK_ACCESS_2_TRANSFER_READ_BIT,
734 : : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
735 : : VK_QUEUE_FAMILY_IGNORED);
736 : :
737 : 156 : vk_tex_barrier(gpu, cmd, params->dst, VK_PIPELINE_STAGE_2_COPY_BIT,
738 : : VK_ACCESS_2_TRANSFER_WRITE_BIT,
739 : : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
740 : : VK_QUEUE_FAMILY_IGNORED);
741 : :
742 : 156 : pl_rect3d_normalize(&src_rc);
743 : :
744 : 156 : VkImageCopy region = {
745 : : .srcSubresource = {
746 : 156 : .aspectMask = src_vk->aspect,
747 : : .layerCount = 1,
748 : : },
749 : : .dstSubresource = {
750 : 156 : .aspectMask = dst_vk->aspect,
751 : : .layerCount = 1,
752 : : },
753 : 156 : .srcOffset = {src_rc.x0, src_rc.y0, src_rc.z0},
754 : : .dstOffset = {src_rc.x0, src_rc.y0, src_rc.z0},
755 : : .extent = {
756 : 156 : pl_rect_w(src_rc),
757 : 156 : pl_rect_h(src_rc),
758 : 156 : pl_rect_d(src_rc),
759 : : },
760 : : };
761 : :
762 : 156 : vk->CmdCopyImage(cmd->buf, src_vk->img, src_vk->layout,
763 : : dst_vk->img, dst_vk->layout, 1, ®ion);
764 : : } else {
765 : 0 : vk_tex_barrier(gpu, cmd, params->src, VK_PIPELINE_STAGE_2_BLIT_BIT,
766 : : VK_ACCESS_2_TRANSFER_READ_BIT,
767 : : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
768 : : VK_QUEUE_FAMILY_IGNORED);
769 : :
770 : 0 : vk_tex_barrier(gpu, cmd, params->dst, VK_PIPELINE_STAGE_2_BLIT_BIT,
771 : : VK_ACCESS_2_TRANSFER_WRITE_BIT,
772 : : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
773 : : VK_QUEUE_FAMILY_IGNORED);
774 : :
775 : 0 : VkImageBlit region = {
776 : : .srcSubresource = {
777 : 0 : .aspectMask = src_vk->aspect,
778 : : .layerCount = 1,
779 : : },
780 : : .dstSubresource = {
781 : 0 : .aspectMask = dst_vk->aspect,
782 : : .layerCount = 1,
783 : : },
784 : 0 : .srcOffsets = {{src_rc.x0, src_rc.y0, src_rc.z0},
785 : 0 : {src_rc.x1, src_rc.y1, src_rc.z1}},
786 : : .dstOffsets = {{dst_rc.x0, dst_rc.y0, dst_rc.z0},
787 : : {dst_rc.x1, dst_rc.y1, dst_rc.z1}},
788 : : };
789 : :
790 : : static const VkFilter filters[PL_TEX_SAMPLE_MODE_COUNT] = {
791 : : [PL_TEX_SAMPLE_NEAREST] = VK_FILTER_NEAREST,
792 : : [PL_TEX_SAMPLE_LINEAR] = VK_FILTER_LINEAR,
793 : : };
794 : :
795 : 0 : vk->CmdBlitImage(cmd->buf, src_vk->img, src_vk->layout,
796 : : dst_vk->img, dst_vk->layout, 1, ®ion,
797 : 0 : filters[params->sample_mode]);
798 : : }
799 : :
800 : 156 : CMD_FINISH(&cmd);
801 : : }
802 : :
803 : : // Determine the best queue type to perform a buffer<->image copy on
804 : 490 : static enum queue_type vk_img_copy_queue(pl_gpu gpu, pl_tex tex,
805 : : const struct VkBufferImageCopy *region)
806 : : {
807 : 490 : struct pl_vk *p = PL_PRIV(gpu);
808 : 490 : struct vk_ctx *vk = p->vk;
809 : :
810 : 490 : const struct pl_tex_vk *tex_vk = PL_PRIV(tex);
811 : 490 : enum queue_type queue = tex_vk->transfer_queue;
812 [ + + ]: 490 : if (queue != TRANSFER)
813 : : return queue;
814 : :
815 : 405 : VkExtent3D alignment = vk->pool_transfer->props.minImageTransferGranularity;
816 : :
817 : : enum queue_type fallback = GRAPHICS;
818 [ - + ]: 405 : if (gpu->limits.compute_queues > gpu->limits.fragment_queues)
819 : : fallback = COMPUTE; // prefer async compute queue
820 : :
821 [ + - ]: 405 : int tex_w = PL_DEF(tex->params.w, 1),
822 [ + + ]: 405 : tex_h = PL_DEF(tex->params.h, 1),
823 [ + + ]: 405 : tex_d = PL_DEF(tex->params.d, 1);
824 : :
825 : 405 : bool full_w = region->imageOffset.x + region->imageExtent.width == tex_w,
826 : 405 : full_h = region->imageOffset.y + region->imageExtent.height == tex_h,
827 : 405 : full_d = region->imageOffset.z + region->imageExtent.depth == tex_d;
828 : :
829 [ + - ]: 405 : if (alignment.width) {
830 : :
831 : : bool unaligned = false;
832 : 405 : unaligned |= region->imageOffset.x % alignment.width;
833 : 405 : unaligned |= region->imageOffset.y % alignment.height;
834 : 405 : unaligned |= region->imageOffset.z % alignment.depth;
835 [ - + - - ]: 405 : unaligned |= (region->imageExtent.width % alignment.width) && !full_w;
836 [ - + - - ]: 405 : unaligned |= (region->imageExtent.height % alignment.height) && !full_h;
837 [ - + - - ]: 405 : unaligned |= (region->imageExtent.depth % alignment.depth) && !full_d;
838 : :
839 [ - + ]: 405 : return unaligned ? fallback : queue;
840 : :
841 : : } else {
842 : :
843 : : // an alignment of {0} means the copy must span the entire image
844 : : bool unaligned = false;
845 [ # # # # ]: 0 : unaligned |= region->imageOffset.x || !full_w;
846 [ # # # # ]: 0 : unaligned |= region->imageOffset.y || !full_h;
847 [ # # # # ]: 0 : unaligned |= region->imageOffset.z || !full_d;
848 : :
849 [ # # ]: 0 : return unaligned ? fallback : queue;
850 : :
851 : : }
852 : : }
853 : :
854 : 187 : static void tex_xfer_cb(void *ctx, void *arg)
855 : : {
856 : 187 : void (*fun)(void *priv) = ctx;
857 : 187 : fun(arg);
858 : 187 : }
859 : :
860 : 569 : bool vk_tex_upload(pl_gpu gpu, const struct pl_tex_transfer_params *params)
861 : : {
862 : 569 : struct pl_vk *p = PL_PRIV(gpu);
863 : 569 : struct vk_ctx *vk = p->vk;
864 : 569 : pl_tex tex = params->tex;
865 : 569 : pl_fmt fmt = tex->params.format;
866 : 569 : struct pl_tex_vk *tex_vk = PL_PRIV(tex);
867 : 569 : struct pl_tex_transfer_params *slices = NULL;
868 : : int num_slices = 0;
869 : :
870 [ + + ]: 569 : if (!params->buf)
871 : 283 : return pl_tex_upload_pbo(gpu, params);
872 : :
873 : : pl_buf buf = params->buf;
874 : 286 : struct pl_buf_vk *buf_vk = PL_PRIV(buf);
875 : 286 : pl_rect3d rc = params->rc;
876 : 286 : const size_t size = pl_tex_transfer_size(params);
877 : 286 : const size_t buf_offset = buf_vk->mem.offset + params->buf_offset;
878 : 286 : bool unaligned = buf_offset % fmt->texel_size;
879 [ + + ]: 286 : if (unaligned)
880 : 3 : PL_TRACE(gpu, "vk_tex_upload: unaligned transfer (slow path)");
881 : :
882 [ + + + + ]: 286 : if (fmt->emulated || unaligned) {
883 : :
884 : : // Create all slice buffers first, to early-fail if OOM, and to avoid
885 : : // blocking unnecessarily on waiting for these buffers to get read from
886 : 12 : num_slices = pl_tex_transfer_slices(gpu, tex_vk->texel_fmt, params, &slices);
887 [ + + ]: 24 : for (int i = 0; i < num_slices; i++) {
888 : 12 : slices[i].buf = pl_buf_create(gpu, pl_buf_params(
889 : : .memory_type = PL_BUF_MEM_DEVICE,
890 : : .format = tex_vk->texel_fmt,
891 : : .size = pl_tex_transfer_size(&slices[i]),
892 : : .storable = fmt->emulated,
893 : : ));
894 : :
895 [ - + ]: 12 : if (!slices[i].buf) {
896 : 0 : PL_ERR(gpu, "Failed creating buffer for tex upload fallback!");
897 : : num_slices = i; // only clean up buffers up to here
898 : 0 : goto error;
899 : : }
900 : : }
901 : :
902 : : // All temporary buffers successfully created, begin copying source data
903 : 12 : struct vk_cmd *cmd = CMD_BEGIN_TIMED(tex_vk->transfer_queue,
904 : : params->timer);
905 [ - + ]: 12 : if (!cmd)
906 : 0 : goto error;
907 : :
908 : 12 : vk_buf_barrier(gpu, cmd, buf, VK_PIPELINE_STAGE_2_COPY_BIT,
909 : 12 : VK_ACCESS_2_TRANSFER_READ_BIT, params->buf_offset, size,
910 : : false);
911 : :
912 [ + + ]: 24 : for (int i = 0; i < num_slices; i++) {
913 : 12 : pl_buf slice = slices[i].buf;
914 : 12 : struct pl_buf_vk *slice_vk = PL_PRIV(slice);
915 : 12 : vk_buf_barrier(gpu, cmd, slice, VK_PIPELINE_STAGE_2_COPY_BIT,
916 : 12 : VK_ACCESS_2_TRANSFER_WRITE_BIT, 0, slice->params.size,
917 : : false);
918 : :
919 : 12 : vk->CmdCopyBuffer(cmd->buf, buf_vk->mem.buf, slice_vk->mem.buf, 1, &(VkBufferCopy) {
920 : 12 : .srcOffset = buf_vk->mem.offset + slices[i].buf_offset,
921 : 12 : .dstOffset = slice_vk->mem.offset,
922 : 12 : .size = slice->params.size,
923 : : });
924 : : }
925 : :
926 [ + + ]: 12 : if (params->callback)
927 : 4 : vk_cmd_callback(cmd, tex_xfer_cb, params->callback, params->priv);
928 : :
929 : 12 : bool ok = CMD_FINISH(&cmd);
930 : :
931 : : // Finally, dispatch the (texel) upload asynchronously. We can fire
932 : : // the callback already at the completion of previous command because
933 : : // these temporary buffers already hold persistent copies of the data
934 [ + + ]: 24 : for (int i = 0; i < num_slices; i++) {
935 [ + - ]: 12 : if (ok) {
936 : 12 : slices[i].buf_offset = 0;
937 : 9 : ok = fmt->emulated ? pl_tex_upload_texel(gpu, &slices[i])
938 [ + + ]: 12 : : pl_tex_upload(gpu, &slices[i]);
939 : : }
940 : 12 : pl_buf_destroy(gpu, &slices[i].buf);
941 : : }
942 : :
943 : 12 : pl_free(slices);
944 : 12 : return ok;
945 : :
946 : : } else {
947 : :
948 [ - + ]: 274 : pl_assert(fmt->texel_align == fmt->texel_size);
949 : 274 : const VkBufferImageCopy region = {
950 : : .bufferOffset = buf_offset,
951 : 274 : .bufferRowLength = params->row_pitch / fmt->texel_size,
952 : 274 : .bufferImageHeight = params->depth_pitch / params->row_pitch,
953 : : .imageOffset = { rc.x0, rc.y0, rc.z0 },
954 : 274 : .imageExtent = { rc.x1, rc.y1, rc.z1 },
955 : : .imageSubresource = {
956 : 274 : .aspectMask = tex_vk->aspect,
957 : : .layerCount = 1,
958 : : },
959 : : };
960 : :
961 : 274 : enum queue_type queue = vk_img_copy_queue(gpu, tex, ®ion);
962 : 274 : struct vk_cmd *cmd = CMD_BEGIN_TIMED(queue, params->timer);
963 [ - + ]: 274 : if (!cmd)
964 : 0 : goto error;
965 : :
966 : 274 : vk_buf_barrier(gpu, cmd, buf, VK_PIPELINE_STAGE_2_COPY_BIT,
967 : 274 : VK_ACCESS_2_TRANSFER_READ_BIT, params->buf_offset, size,
968 : : false);
969 : 274 : vk_tex_barrier(gpu, cmd, tex, VK_PIPELINE_STAGE_2_COPY_BIT,
970 : : VK_ACCESS_2_TRANSFER_WRITE_BIT,
971 : : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
972 : : VK_QUEUE_FAMILY_IGNORED);
973 : 274 : vk->CmdCopyBufferToImage(cmd->buf, buf_vk->mem.buf, tex_vk->img,
974 : : tex_vk->layout, 1, ®ion);
975 : :
976 [ + + ]: 274 : if (params->callback)
977 : 3 : vk_cmd_callback(cmd, tex_xfer_cb, params->callback, params->priv);
978 : :
979 : 274 : return CMD_FINISH(&cmd);
980 : : }
981 : :
982 : : pl_unreachable();
983 : :
984 : 0 : error:
985 [ # # ]: 0 : for (int i = 0; i < num_slices; i++)
986 : 0 : pl_buf_destroy(gpu, &slices[i].buf);
987 : 0 : pl_free(slices);
988 : 0 : return false;
989 : : }
990 : :
991 : 453 : bool vk_tex_download(pl_gpu gpu, const struct pl_tex_transfer_params *params)
992 : : {
993 : 453 : struct pl_vk *p = PL_PRIV(gpu);
994 : 453 : struct vk_ctx *vk = p->vk;
995 : 453 : pl_tex tex = params->tex;
996 : 453 : pl_fmt fmt = tex->params.format;
997 : 453 : struct pl_tex_vk *tex_vk = PL_PRIV(tex);
998 : 453 : struct pl_tex_transfer_params *slices = NULL;
999 : : int num_slices = 0;
1000 : :
1001 [ + + ]: 453 : if (!params->buf)
1002 : 225 : return pl_tex_download_pbo(gpu, params);
1003 : :
1004 : : pl_buf buf = params->buf;
1005 : 228 : struct pl_buf_vk *buf_vk = PL_PRIV(buf);
1006 : 228 : pl_rect3d rc = params->rc;
1007 : 228 : const size_t size = pl_tex_transfer_size(params);
1008 : 228 : const size_t buf_offset = buf_vk->mem.offset + params->buf_offset;
1009 : 228 : bool unaligned = buf_offset % fmt->texel_size;
1010 [ + + ]: 228 : if (unaligned)
1011 : 3 : PL_TRACE(gpu, "vk_tex_download: unaligned transfer (slow path)");
1012 : :
1013 [ + + + + ]: 228 : if (fmt->emulated || unaligned) {
1014 : :
1015 : 12 : num_slices = pl_tex_transfer_slices(gpu, tex_vk->texel_fmt, params, &slices);
1016 [ + + ]: 24 : for (int i = 0; i < num_slices; i++) {
1017 : 12 : slices[i].buf = pl_buf_create(gpu, pl_buf_params(
1018 : : .memory_type = PL_BUF_MEM_DEVICE,
1019 : : .format = tex_vk->texel_fmt,
1020 : : .size = pl_tex_transfer_size(&slices[i]),
1021 : : .storable = fmt->emulated,
1022 : : ));
1023 : :
1024 [ - + ]: 12 : if (!slices[i].buf) {
1025 : 0 : PL_ERR(gpu, "Failed creating buffer for tex download fallback!");
1026 : : num_slices = i;
1027 : 0 : goto error;
1028 : : }
1029 : : }
1030 : :
1031 [ + + ]: 24 : for (int i = 0; i < num_slices; i++) {
1032 : : // Restore buffer offset after downloading into temporary buffer,
1033 : : // because we still need to copy the data from the temporary buffer
1034 : : // into this offset in the original buffer
1035 : 12 : const size_t tmp_offset = slices[i].buf_offset;
1036 : 12 : slices[i].buf_offset = 0;
1037 : 9 : bool ok = fmt->emulated ? pl_tex_download_texel(gpu, &slices[i])
1038 [ + + ]: 12 : : pl_tex_download(gpu, &slices[i]);
1039 : 12 : slices[i].buf_offset = tmp_offset;
1040 [ - + ]: 12 : if (!ok)
1041 : 0 : goto error;
1042 : : }
1043 : :
1044 : : // Finally, download into the user buffer
1045 : 12 : struct vk_cmd *cmd = CMD_BEGIN_TIMED(tex_vk->transfer_queue, params->timer);
1046 [ - + ]: 12 : if (!cmd)
1047 : 0 : goto error;
1048 : :
1049 : 12 : vk_buf_barrier(gpu, cmd, buf, VK_PIPELINE_STAGE_2_COPY_BIT,
1050 : 12 : VK_ACCESS_2_TRANSFER_WRITE_BIT, params->buf_offset, size,
1051 : : false);
1052 : :
1053 [ + + ]: 24 : for (int i = 0; i < num_slices; i++) {
1054 : 12 : pl_buf slice = slices[i].buf;
1055 : 12 : struct pl_buf_vk *slice_vk = PL_PRIV(slice);
1056 : 12 : vk_buf_barrier(gpu, cmd, slice, VK_PIPELINE_STAGE_2_COPY_BIT,
1057 : 12 : VK_ACCESS_2_TRANSFER_READ_BIT, 0, slice->params.size,
1058 : : false);
1059 : :
1060 : 12 : vk->CmdCopyBuffer(cmd->buf, slice_vk->mem.buf, buf_vk->mem.buf, 1, &(VkBufferCopy) {
1061 : 12 : .srcOffset = slice_vk->mem.offset,
1062 : 12 : .dstOffset = buf_vk->mem.offset + slices[i].buf_offset,
1063 : 12 : .size = slice->params.size,
1064 : : });
1065 : :
1066 : 12 : pl_buf_destroy(gpu, &slices[i].buf);
1067 : : }
1068 : :
1069 : 12 : vk_buf_flush(gpu, cmd, buf, params->buf_offset, size);
1070 : :
1071 [ + - ]: 12 : if (params->callback)
1072 : 12 : vk_cmd_callback(cmd, tex_xfer_cb, params->callback, params->priv);
1073 : :
1074 : 12 : pl_free(slices);
1075 : 12 : return CMD_FINISH(&cmd);
1076 : :
1077 : : } else {
1078 : :
1079 [ - + ]: 216 : pl_assert(params->row_pitch % fmt->texel_size == 0);
1080 [ - + ]: 216 : pl_assert(params->depth_pitch % params->row_pitch == 0);
1081 : 216 : const VkBufferImageCopy region = {
1082 : : .bufferOffset = buf_offset,
1083 : 216 : .bufferRowLength = params->row_pitch / fmt->texel_size,
1084 : 216 : .bufferImageHeight = params->depth_pitch / params->row_pitch,
1085 : : .imageOffset = { rc.x0, rc.y0, rc.z0 },
1086 : 216 : .imageExtent = { rc.x1, rc.y1, rc.z1 },
1087 : : .imageSubresource = {
1088 : 216 : .aspectMask = tex_vk->aspect,
1089 : : .layerCount = 1,
1090 : : },
1091 : : };
1092 : :
1093 : 216 : enum queue_type queue = vk_img_copy_queue(gpu, tex, ®ion);
1094 : :
1095 : 216 : struct vk_cmd *cmd = CMD_BEGIN_TIMED(queue, params->timer);
1096 [ - + ]: 216 : if (!cmd)
1097 : 0 : goto error;
1098 : :
1099 : 216 : vk_buf_barrier(gpu, cmd, buf, VK_PIPELINE_STAGE_2_COPY_BIT,
1100 : 216 : VK_ACCESS_2_TRANSFER_WRITE_BIT, params->buf_offset, size,
1101 : : false);
1102 : 216 : vk_tex_barrier(gpu, cmd, tex, VK_PIPELINE_STAGE_2_COPY_BIT,
1103 : : VK_ACCESS_2_TRANSFER_READ_BIT,
1104 : : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1105 : : VK_QUEUE_FAMILY_IGNORED);
1106 : 216 : vk->CmdCopyImageToBuffer(cmd->buf, tex_vk->img, tex_vk->layout,
1107 : : buf_vk->mem.buf, 1, ®ion);
1108 : 216 : vk_buf_flush(gpu, cmd, buf, params->buf_offset, size);
1109 : :
1110 [ + + ]: 216 : if (params->callback)
1111 : 168 : vk_cmd_callback(cmd, tex_xfer_cb, params->callback, params->priv);
1112 : :
1113 : 216 : return CMD_FINISH(&cmd);
1114 : : }
1115 : :
1116 : : pl_unreachable();
1117 : :
1118 : 0 : error:
1119 [ # # ]: 0 : for (int i = 0; i < num_slices; i++)
1120 : 0 : pl_buf_destroy(gpu, &slices[i].buf);
1121 : 0 : pl_free(slices);
1122 : 0 : return false;
1123 : : }
1124 : :
1125 : 4 : bool vk_tex_poll(pl_gpu gpu, pl_tex tex, uint64_t timeout)
1126 : : {
1127 : 4 : struct pl_vk *p = PL_PRIV(gpu);
1128 : 4 : struct vk_ctx *vk = p->vk;
1129 : 4 : struct pl_tex_vk *tex_vk = PL_PRIV(tex);
1130 : :
1131 : : // Opportunistically check if we can re-use this texture without flush
1132 : 4 : vk_poll_commands(vk, 0);
1133 [ + - ]: 4 : if (pl_rc_count(&tex_vk->rc) == 1)
1134 : 4 : goto skip_blocking;
1135 : :
1136 : : // Otherwise, we're force to submit any queued command so that the user is
1137 : : // guaranteed to see progress eventually, even if they call this in a loop
1138 : 0 : CMD_SUBMIT(NULL);
1139 : 0 : vk_poll_commands(vk, timeout);
1140 [ # # ]: 0 : if (pl_rc_count(&tex_vk->rc) > 1)
1141 : : return true;
1142 : :
1143 : : // fall through
1144 : 0 : skip_blocking:
1145 [ - + ]: 4 : for (int i = 0; i < tex_vk->num_planes; i++) {
1146 [ # # ]: 0 : if (vk_tex_poll(gpu, tex->planes[i], timeout))
1147 : : return true;
1148 : : }
1149 : :
1150 : : return false;
1151 : : }
1152 : :
1153 : 8 : pl_tex pl_vulkan_wrap(pl_gpu gpu, const struct pl_vulkan_wrap_params *params)
1154 : : {
1155 : : pl_fmt fmt = NULL;
1156 [ + - ]: 40 : for (int i = 0; i < gpu->num_formats; i++) {
1157 : 40 : const struct vk_format **vkfmt = PL_PRIV(gpu->formats[i]);
1158 [ + + ]: 40 : if ((*vkfmt)->tfmt == params->format) {
1159 : : fmt = gpu->formats[i];
1160 : : break;
1161 : : }
1162 : : }
1163 : :
1164 [ - + ]: 8 : if (!fmt) {
1165 : 0 : PL_ERR(gpu, "Could not find pl_fmt suitable for wrapped image "
1166 : : "with format %s", vk_fmt_name(params->format));
1167 : 0 : return NULL;
1168 : : }
1169 : :
1170 : 8 : VkImageUsageFlags usage = params->usage;
1171 [ - + ]: 8 : if (fmt->num_planes)
1172 : : usage = 0; // mask capabilities from the base texture
1173 : :
1174 : 8 : struct pl_tex_t *tex = pl_zalloc_obj(NULL, tex, struct pl_tex_vk);
1175 : 8 : tex->params = (struct pl_tex_params) {
1176 : : .format = fmt,
1177 : 8 : .w = params->width,
1178 : 8 : .h = params->height,
1179 : 8 : .d = params->depth,
1180 : 8 : .sampleable = !!(usage & VK_IMAGE_USAGE_SAMPLED_BIT),
1181 : 8 : .renderable = !!(usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT),
1182 : 8 : .storable = !!(usage & VK_IMAGE_USAGE_STORAGE_BIT),
1183 : 8 : .blit_src = !!(usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT),
1184 : 8 : .blit_dst = !!(usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT),
1185 : : .host_writable = !!(usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT),
1186 : : .host_readable = !!(usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT),
1187 : 8 : .user_data = params->user_data,
1188 : 8 : .debug_tag = params->debug_tag,
1189 : : };
1190 : :
1191 : : // Mask out capabilities not permitted by the `pl_fmt`
1192 : : #define MASK(field, cap) \
1193 : : do { \
1194 : : if (tex->params.field && !(fmt->caps & cap)) { \
1195 : : PL_WARN(gpu, "Masking `" #field "` from wrapped texture because " \
1196 : : "the corresponding format '%s' does not support " #cap, \
1197 : : fmt->name); \
1198 : : tex->params.field = false; \
1199 : : } \
1200 : : } while (0)
1201 : :
1202 [ - + - - ]: 8 : MASK(sampleable, PL_FMT_CAP_SAMPLEABLE);
1203 [ + - - + ]: 8 : MASK(renderable, PL_FMT_CAP_RENDERABLE);
1204 [ + - - + ]: 8 : MASK(storable, PL_FMT_CAP_STORABLE);
1205 [ - + - - ]: 8 : MASK(blit_src, PL_FMT_CAP_BLITTABLE);
1206 [ + - - + ]: 8 : MASK(blit_dst, PL_FMT_CAP_BLITTABLE);
1207 [ - + - - ]: 8 : MASK(host_readable, PL_FMT_CAP_HOST_READABLE);
1208 : : #undef MASK
1209 : :
1210 : : // For simplicity, explicitly mask out blit emulation for wrapped textures
1211 : 8 : struct pl_fmt_vk *fmtp = PL_PRIV(fmt);
1212 [ - + ]: 8 : if (fmtp->blit_emulated) {
1213 : 0 : tex->params.blit_src = false;
1214 : 0 : tex->params.blit_dst = false;
1215 : : }
1216 : :
1217 [ - + ]: 8 : struct pl_tex_vk *tex_vk = PL_PRIV(tex);
1218 : : switch (pl_tex_params_dimension(tex->params)) {
1219 : 0 : case 1: tex_vk->type = VK_IMAGE_TYPE_1D; break;
1220 : 8 : case 2: tex_vk->type = VK_IMAGE_TYPE_2D; break;
1221 : 0 : case 3: tex_vk->type = VK_IMAGE_TYPE_3D; break;
1222 : : }
1223 : 8 : tex_vk->external_img = true;
1224 : 8 : tex_vk->held = !fmt->num_planes;
1225 : 8 : tex_vk->img = params->image;
1226 : 8 : tex_vk->img_fmt = params->format;
1227 : 8 : tex_vk->num_planes = fmt->num_planes;
1228 : 8 : tex_vk->usage_flags = usage;
1229 : 8 : tex_vk->aspect = params->aspect;
1230 : :
1231 [ + - ]: 8 : if (!tex_vk->aspect) {
1232 [ - + ]: 8 : for (int i = 0; i < tex_vk->num_planes; i++)
1233 : 0 : tex_vk->aspect |= VK_IMAGE_ASPECT_PLANE_0_BIT << i;
1234 : 8 : tex_vk->aspect = PL_DEF(tex_vk->aspect, VK_IMAGE_ASPECT_COLOR_BIT);
1235 : : }
1236 : :
1237 : : // Blitting to planar images requires fallback via compute shaders
1238 [ - + ]: 8 : if (tex_vk->aspect != VK_IMAGE_ASPECT_COLOR_BIT) {
1239 : 0 : tex->params.blit_src &= tex->params.storable;
1240 : 0 : tex->params.blit_dst &= tex->params.storable;
1241 : : }
1242 : :
1243 : : static const char * const wrapped_plane_names[4] = {
1244 : : "wrapped plane 0", "wrapped plane 1", "wrapped plane 2", "wrapped plane 3",
1245 : : };
1246 : :
1247 [ - + ]: 8 : for (int i = 0; i < tex_vk->num_planes; i++) {
1248 : : struct pl_tex_t *plane;
1249 : 0 : VkImageAspectFlags aspect = VK_IMAGE_ASPECT_PLANE_0_BIT << i;
1250 [ # # ]: 0 : if (!(aspect & tex_vk->aspect)) {
1251 : 0 : PL_INFO(gpu, "Not wrapping plane %d due to aspect bit 0x%x not "
1252 : : "being contained in supplied params->aspect 0x%x!",
1253 : : i, (unsigned) aspect, (unsigned) tex_vk->aspect);
1254 : 0 : continue;
1255 : : }
1256 : :
1257 [ # # ]: 0 : pl_assert(tex_vk->type == VK_IMAGE_TYPE_2D);
1258 [ # # ]: 0 : plane = (struct pl_tex_t *) pl_vulkan_wrap(gpu, pl_vulkan_wrap_params(
1259 : : .image = tex_vk->img,
1260 : : .aspect = aspect,
1261 : : .width = PL_RSHIFT_UP(tex->params.w, fmt->planes[i].shift_x),
1262 : : .height = PL_RSHIFT_UP(tex->params.h, fmt->planes[i].shift_y),
1263 : : .format = fmtp->vk_fmt->pfmt[i].fmt,
1264 : : .usage = params->usage,
1265 : : .user_data = params->user_data,
1266 : : .debug_tag = PL_DEF(params->debug_tag, wrapped_plane_names[i]),
1267 : : ));
1268 [ # # ]: 0 : if (!plane)
1269 : 0 : goto error;
1270 : 0 : plane->parent = tex;
1271 : 0 : tex->planes[i] = plane;
1272 : 0 : tex_vk->planes[i] = PL_PRIV(plane);
1273 : : }
1274 : :
1275 [ + - - + ]: 16 : if (!vk_init_image(gpu, tex, PL_DEF(params->debug_tag, "wrapped")))
1276 : 0 : goto error;
1277 : :
1278 : : return tex;
1279 : :
1280 : 0 : error:
1281 : 0 : vk_tex_destroy(gpu, tex);
1282 : 0 : return NULL;
1283 : : }
1284 : :
1285 : 0 : VkImage pl_vulkan_unwrap(pl_gpu gpu, pl_tex tex, VkFormat *out_format,
1286 : : VkImageUsageFlags *out_flags)
1287 : : {
1288 : 0 : struct pl_tex_vk *tex_vk = PL_PRIV(tex);
1289 : :
1290 [ # # ]: 0 : if (out_format)
1291 : 0 : *out_format = tex_vk->img_fmt;
1292 [ # # ]: 0 : if (out_flags)
1293 : 0 : *out_flags = tex_vk->usage_flags;
1294 : :
1295 : 0 : return tex_vk->img;
1296 : : }
1297 : :
1298 : 14 : bool pl_vulkan_hold_ex(pl_gpu gpu, const struct pl_vulkan_hold_params *params)
1299 : : {
1300 : 14 : struct pl_tex_vk *tex_vk = PL_PRIV(params->tex);
1301 [ - + ]: 14 : pl_assert(params->semaphore.sem);
1302 : :
1303 : 14 : bool held = tex_vk->held;
1304 [ - + ]: 14 : for (int i = 0; i < tex_vk->num_planes; i++)
1305 : 0 : held |= tex_vk->planes[i]->held;
1306 : :
1307 [ - + ]: 14 : if (held) {
1308 : 0 : PL_ERR(gpu, "Attempting to hold an already held image!");
1309 : 0 : return false;
1310 : : }
1311 : :
1312 : 14 : struct vk_cmd *cmd = CMD_BEGIN(GRAPHICS);
1313 [ - + ]: 14 : if (!cmd) {
1314 : 0 : PL_ERR(gpu, "Failed holding external image!");
1315 : 0 : return false;
1316 : : }
1317 : :
1318 : 14 : VkImageLayout layout = params->layout;
1319 [ - + ]: 14 : if (params->out_layout) {
1320 : : // For planar images, arbitrarily pick the current image layout of the
1321 : : // first plane. This should be fine in practice, since all planes will
1322 : : // share the same usage capabilities.
1323 [ # # ]: 0 : if (tex_vk->num_planes) {
1324 : 0 : layout = tex_vk->planes[0]->layout;
1325 : : } else {
1326 : 0 : layout = tex_vk->layout;
1327 : : }
1328 : : }
1329 : :
1330 : : bool may_invalidate = true;
1331 [ + - ]: 14 : if (!tex_vk->num_planes) {
1332 : 14 : may_invalidate &= tex_vk->may_invalidate;
1333 : 14 : vk_tex_barrier(gpu, cmd, params->tex, VK_PIPELINE_STAGE_2_NONE,
1334 : 14 : 0, layout, params->qf);
1335 : : }
1336 : :
1337 [ - + ]: 14 : for (int i = 0; i < tex_vk->num_planes; i++) {
1338 : 0 : may_invalidate &= tex_vk->planes[i]->may_invalidate;
1339 : 0 : vk_tex_barrier(gpu, cmd, params->tex->planes[i],
1340 : 0 : VK_PIPELINE_STAGE_2_NONE, 0, layout, params->qf);
1341 : : }
1342 : :
1343 : 14 : vk_cmd_sig(cmd, VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, params->semaphore);
1344 : 14 : bool ok = CMD_SUBMIT(&cmd);
1345 : :
1346 [ + - ]: 14 : if (!tex_vk->num_planes) {
1347 : 14 : tex_vk->sem.write.queue = tex_vk->sem.read.queue = NULL;
1348 : 14 : tex_vk->held = ok;
1349 : : }
1350 : :
1351 [ - + ]: 14 : for (int i = 0; i < tex_vk->num_planes; i++) {
1352 : 0 : struct pl_tex_vk *plane_vk = tex_vk->planes[i];
1353 : 0 : plane_vk->sem.write.queue = plane_vk->sem.read.queue = NULL;
1354 : 0 : plane_vk->held = ok;
1355 : : }
1356 : :
1357 [ + - + - ]: 14 : if (ok && params->out_layout)
1358 [ # # ]: 0 : *params->out_layout = may_invalidate ? VK_IMAGE_LAYOUT_UNDEFINED : layout;
1359 : :
1360 : : return ok;
1361 : : }
1362 : :
1363 : 14 : void pl_vulkan_release_ex(pl_gpu gpu, const struct pl_vulkan_release_params *params)
1364 : : {
1365 : 14 : struct pl_tex_vk *tex_vk = PL_PRIV(params->tex);
1366 [ - + ]: 14 : if (tex_vk->num_planes) {
1367 : 0 : struct pl_vulkan_release_params plane_pars = *params;
1368 [ # # ]: 0 : for (int i = 0; i < tex_vk->num_planes; i++) {
1369 : 0 : plane_pars.tex = params->tex->planes[i];
1370 : 0 : pl_vulkan_release_ex(gpu, &plane_pars);
1371 : : }
1372 : : return;
1373 : : }
1374 : :
1375 [ - + ]: 14 : if (!tex_vk->held) {
1376 : 0 : PL_ERR(gpu, "Attempting to release an unheld image?");
1377 : 0 : return;
1378 : : }
1379 : :
1380 [ + - ]: 14 : if (params->semaphore.sem)
1381 [ + + - + : 14 : PL_ARRAY_APPEND(params->tex, tex_vk->ext_deps, params->semaphore);
- + ]
1382 : :
1383 : 14 : tex_vk->qf = params->qf;
1384 : 14 : tex_vk->layout = params->layout;
1385 : 14 : tex_vk->held = false;
1386 : : }
|