1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2023 The Khronos Group Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *	  http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19  *//*!
20 * \file
21 * \brief Video framebuffer
22  *//*--------------------------------------------------------------------*/
23 /*
24 * Copyright 2020 NVIDIA Corporation.
25 *
26 * Licensed under the Apache License, Version 2.0 (the "License");
27 * you may not use this file except in compliance with the License.
28 * You may obtain a copy of the License at
29 *
30 *    http://www.apache.org/licenses/LICENSE-2.0
31 *
32 * Unless required by applicable law or agreed to in writing, software
33 * distributed under the License is distributed on an "AS IS" BASIS,
34 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
35 * See the License for the specific language governing permissions and
36 * limitations under the License.
37  */
38 
39 #include "vktVideoFrameBuffer.hpp"
40 
41 #include <queue>
42 
43 namespace vkt
44 {
45 namespace video
46 {
47 static VkSharedBaseObj<VkImageResourceView> emptyImageView;
48 
49 class NvPerFrameDecodeResources : public vkPicBuffBase {
50 public:
NvPerFrameDecodeResources()51 	NvPerFrameDecodeResources()
52 		: m_picDispInfo()
53 		, m_frameCompleteFence(VK_NULL_HANDLE)
54 		, m_frameCompleteSemaphore(VK_NULL_HANDLE)
55 		, m_frameConsumerDoneFence(VK_NULL_HANDLE)
56 		, m_frameConsumerDoneSemaphore(VK_NULL_HANDLE)
57 		, m_hasFrameCompleteSignalFence(false)
58 		, m_hasFrameCompleteSignalSemaphore(false)
59 		, m_hasConsummerSignalFence(false)
60 		, m_hasConsummerSignalSemaphore(false)
61 		, m_recreateImage(false)
62 		, m_currentDpbImageLayerLayout(VK_IMAGE_LAYOUT_UNDEFINED)
63 		, m_currentOutputImageLayout(VK_IMAGE_LAYOUT_UNDEFINED)
64 		, m_vkDevCtx(nullptr)
65 		, m_frameDpbImageView(VK_NULL_HANDLE)
66 		, m_outImageView(VK_NULL_HANDLE)
67 	{
68 	}
69 
70 	VkResult CreateImage( DeviceContext& vkDevCtx,
71 						 const VkImageCreateInfo* pDpbImageCreateInfo,
72 						 const VkImageCreateInfo* pOutImageCreateInfo,
73 						 deUint32 imageIndex,
74 						 VkSharedBaseObj<VkImageResource>&  imageArrayParent,
75 						 VkSharedBaseObj<VkImageResourceView>& imageViewArrayParent,
76 						 bool useSeparateOutputImage = false,
77 						 bool useLinearOutput = false);
78 
79 	VkResult init(DeviceContext& vkDevCtx);
80 
81 	void Deinit();
82 
83 	NvPerFrameDecodeResources (const NvPerFrameDecodeResources &srcObj) = delete;
84 	NvPerFrameDecodeResources (NvPerFrameDecodeResources &&srcObj) = delete;
85 
86 	~NvPerFrameDecodeResources() override
87 	{
88 		Deinit();
89 	}
90 
GetFrameImageView()91 	VkSharedBaseObj<VkImageResourceView>& GetFrameImageView() {
92 		if (ImageExist()) {
93 			return m_frameDpbImageView;
94 		} else {
95 			return emptyImageView;
96 		}
97 	}
98 
GetDisplayImageView()99 	VkSharedBaseObj<VkImageResourceView>& GetDisplayImageView() {
100 		if (ImageExist()) {
101 			return m_outImageView;
102 		} else {
103 			return emptyImageView;
104 		}
105 	}
106 
ImageExist()107 	bool ImageExist() {
108 
109 		return (!!m_frameDpbImageView && (m_frameDpbImageView->GetImageView() != VK_NULL_HANDLE));
110 	}
111 
GetImageSetNewLayout(VkImageLayout newDpbImageLayout, VkVideoPictureResourceInfoKHR* pDpbPictureResource, VulkanVideoFrameBuffer::PictureResourceInfo* pDpbPictureResourceInfo, VkImageLayout newOutputImageLayout = VK_IMAGE_LAYOUT_MAX_ENUM, VkVideoPictureResourceInfoKHR* pOutputPictureResource = nullptr, VulkanVideoFrameBuffer::PictureResourceInfo* pOutputPictureResourceInfo = nullptr)112 	bool GetImageSetNewLayout(VkImageLayout newDpbImageLayout,
113 							  VkVideoPictureResourceInfoKHR* pDpbPictureResource,
114 							  VulkanVideoFrameBuffer::PictureResourceInfo* pDpbPictureResourceInfo,
115 							  VkImageLayout newOutputImageLayout = VK_IMAGE_LAYOUT_MAX_ENUM,
116 							  VkVideoPictureResourceInfoKHR* pOutputPictureResource = nullptr,
117 							  VulkanVideoFrameBuffer::PictureResourceInfo* pOutputPictureResourceInfo = nullptr) {
118 
119 
120 		if (m_recreateImage || !ImageExist()) {
121 			return false;
122 		}
123 
124 		if (pDpbPictureResourceInfo) {
125 			pDpbPictureResourceInfo->image = m_frameDpbImageView->GetImageResource()->GetImage();
126 			pDpbPictureResourceInfo->imageFormat = m_frameDpbImageView->GetImageResource()->GetImageCreateInfo().format;
127 			pDpbPictureResourceInfo->currentImageLayout = m_currentDpbImageLayerLayout;
128 		}
129 
130 		if (VK_IMAGE_LAYOUT_MAX_ENUM != newDpbImageLayout) {
131 			m_currentDpbImageLayerLayout = newDpbImageLayout;
132 		}
133 
134 		if (pDpbPictureResource) {
135 			pDpbPictureResource->imageViewBinding = m_frameDpbImageView->GetImageView();
136 		}
137 
138 		if (pOutputPictureResourceInfo) {
139 			pOutputPictureResourceInfo->image = m_outImageView->GetImageResource()->GetImage();
140 			pOutputPictureResourceInfo->imageFormat = m_outImageView->GetImageResource()->GetImageCreateInfo().format;
141 			pOutputPictureResourceInfo->currentImageLayout = m_currentOutputImageLayout;
142 		}
143 
144 		if (VK_IMAGE_LAYOUT_MAX_ENUM != newOutputImageLayout) {
145 			m_currentOutputImageLayout = newOutputImageLayout;
146 		}
147 
148 		if (pOutputPictureResource) {
149 			pOutputPictureResource->imageViewBinding = m_outImageView->GetImageView();
150 		}
151 
152 		return true;
153 	}
154 
155 	VkParserDecodePictureInfo m_picDispInfo;
156 	VkFence m_frameCompleteFence;
157 	VkSemaphore m_frameCompleteSemaphore;
158 	VkFence m_frameConsumerDoneFence;
159 	VkSemaphore m_frameConsumerDoneSemaphore;
160 	deUint32 m_hasFrameCompleteSignalFence : 1;
161 	deUint32 m_hasFrameCompleteSignalSemaphore : 1;
162 	deUint32 m_hasConsummerSignalFence : 1;
163 	deUint32 m_hasConsummerSignalSemaphore : 1;
164 	deUint32 m_recreateImage : 1;
165 	// VPS
166 	VkSharedBaseObj<VkVideoRefCountBase>  stdVps;
167 	// SPS
168 	VkSharedBaseObj<VkVideoRefCountBase>  stdSps;
169 	// PPS
170 	VkSharedBaseObj<VkVideoRefCountBase>  stdPps;
171 	// The bitstream Buffer
172 	VkSharedBaseObj<VkVideoRefCountBase>  bitstreamData;
173 
174 private:
175 	VkImageLayout                        m_currentDpbImageLayerLayout;
176 	VkImageLayout                        m_currentOutputImageLayout;
177 	DeviceContext*						 m_vkDevCtx;
178 	VkSharedBaseObj<VkImageResourceView> m_frameDpbImageView;
179 	VkSharedBaseObj<VkImageResourceView> m_outImageView;
180 };
181 
182 class NvPerFrameDecodeImageSet {
183 public:
184 
185 	static constexpr size_t maxImages = 32;
186 
NvPerFrameDecodeImageSet()187 	NvPerFrameDecodeImageSet()
188 		: m_queueFamilyIndex((deUint32)-1)
189 		, m_dpbImageCreateInfo()
190 		, m_outImageCreateInfo()
191 		, m_numImages(0)
192 		, m_usesImageArray(false)
193 		, m_usesImageViewArray(false)
194 		, m_usesSeparateOutputImage(false)
195 		, m_usesLinearOutput(false)
196 		, m_perFrameDecodeResources(maxImages)
197 		, m_imageArray()
198 		, m_imageViewArray()
199 	{
200 	}
201 
202 	int32_t init(DeviceContext& vkDevCtx,
203 				 const VkVideoProfileInfoKHR* pDecodeProfile,
204 				 deUint32              numImages,
205 				 VkFormat              dpbImageFormat,
206 				 VkFormat              outImageFormat,
207 				 const VkExtent2D&     maxImageExtent,
208 				 VkImageUsageFlags     dpbImageUsage,
209 				 VkImageUsageFlags     outImageUsage,
210 				 deUint32              queueFamilyIndex,
211 				 bool useImageArray = false,
212 				 bool useImageViewArray = false,
213 				 bool useSeparateOutputImages = false,
214 				 bool useLinearOutput = false);
215 
~NvPerFrameDecodeImageSet()216 	~NvPerFrameDecodeImageSet()
217 	{
218 		m_numImages = 0;
219 	}
220 
operator [](unsigned int index)221 	NvPerFrameDecodeResources& operator[](unsigned int index)
222 	{
223 		DE_ASSERT(index < m_perFrameDecodeResources.size());
224 		return m_perFrameDecodeResources[index];
225 	}
226 
227 	size_t size() const
228 	{
229 		return m_numImages;
230 	}
231 
232 	VkResult GetImageSetNewLayout(DeviceContext& vkDevCtx,
233 								  deUint32 imageIndex,
234 								  VkImageLayout newDpbImageLayout,
235 								  VkVideoPictureResourceInfoKHR* pDpbPictureResource = nullptr,
236 								  VulkanVideoFrameBuffer::PictureResourceInfo* pDpbPictureResourceInfo = nullptr,
237 								  VkImageLayout newOutputImageLayout = VK_IMAGE_LAYOUT_MAX_ENUM,
238 								  VkVideoPictureResourceInfoKHR* pOutputPictureResource = nullptr,
239 								  VulkanVideoFrameBuffer::PictureResourceInfo* pOutputPictureResourceInfo = nullptr) {
240 
241 		VkResult result = VK_SUCCESS;
242 		if (pDpbPictureResource) {
243 			if (m_imageViewArray) {
244 				// We have an image view that has the same number of layers as the image.
245 				// In that scenario, while specifying the resource, the API must specifically choose the image layer.
246 				pDpbPictureResource->baseArrayLayer = imageIndex;
247 			} else {
248 				// Let the image view sub-resource specify the image layer.
249 				pDpbPictureResource->baseArrayLayer = 0;
250 			}
251 		}
252 
253 		if(pOutputPictureResource) {
254 			// Output pictures currently are only allocated as discrete
255 			// Let the image view sub-resource specify the image layer.
256 			pOutputPictureResource->baseArrayLayer = 0;
257 		}
258 
259 		bool validImage = m_perFrameDecodeResources[imageIndex].GetImageSetNewLayout(
260 			newDpbImageLayout,
261 			pDpbPictureResource,
262 			pDpbPictureResourceInfo,
263 			newOutputImageLayout,
264 			pOutputPictureResource,
265 			pOutputPictureResourceInfo);
266 
267 		if (!validImage) {
268 			result = m_perFrameDecodeResources[imageIndex].CreateImage(
269 				vkDevCtx,
270 				&m_dpbImageCreateInfo,
271 				&m_outImageCreateInfo,
272 				imageIndex,
273 				m_imageArray,
274 				m_imageViewArray,
275 				m_usesSeparateOutputImage,
276 				m_usesLinearOutput);
277 
278 			if (result == VK_SUCCESS) {
279 				validImage = m_perFrameDecodeResources[imageIndex].GetImageSetNewLayout(
280 					newDpbImageLayout,
281 					pDpbPictureResource,
282 					pDpbPictureResourceInfo,
283 					newOutputImageLayout,
284 					pOutputPictureResource,
285 					pOutputPictureResourceInfo);
286 
287 				DE_ASSERT(validImage);
288 			}
289 		}
290 
291 		return result;
292 	}
293 
294 private:
295 	deUint32							   m_queueFamilyIndex;
296 	VkVideoCoreProfile					   m_videoProfile;
297 	VkImageCreateInfo					   m_dpbImageCreateInfo;
298 	VkImageCreateInfo					   m_outImageCreateInfo;
299 	deUint32							   m_numImages;
300 	// TODO: This code copied from the NVIDIA sample app has never been tested. Need to make sure the IHVs have a Radeon 5000 series GPU that uses this feature.
301 	deUint32							   m_usesImageArray : 1;
302 	deUint32							   m_usesImageViewArray : 1;
303 	deUint32							   m_usesSeparateOutputImage : 1;
304 	deUint32							   m_usesLinearOutput : 1;
305 	std::vector<NvPerFrameDecodeResources> m_perFrameDecodeResources;
306 	VkSharedBaseObj<VkImageResource>	   m_imageArray; // must be valid if m_usesImageArray is true
307 	VkSharedBaseObj<VkImageResourceView>   m_imageViewArray; // must be valid if m_usesImageViewArray is true
308 };
309 
310 class VkVideoFrameBuffer : public VulkanVideoFrameBuffer {
311 public:
312 	static constexpr size_t maxFramebufferImages = 32;
313 
314 	VkVideoFrameBuffer(DeviceContext& vkDevCtx, bool supportsQueries)
315 		: m_vkDevCtx(vkDevCtx),
316 		m_refCount(0),
317 		m_displayQueueMutex(),
318 		m_perFrameDecodeImageSet(),
319 		m_displayFrames(),
320 		m_supportsQueries(supportsQueries),
321 		m_queryPool(VK_NULL_HANDLE),
322 		m_ownedByDisplayMask(0),
323 		m_frameNumInDecodeOrder(0),
324 		m_frameNumInDisplayOrder(0),
325 		m_codedExtent{0, 0},
326 		m_numberParameterUpdates(0)
327 	{ }
328 
329 	int32_t AddRef() override;
330 	int32_t Release() override;
331 
332 	VkResult CreateVideoQueries(deUint32 numSlots, DeviceContext& vkDevCtx,
333 									   const VkVideoProfileInfoKHR* pDecodeProfile) {
334 		DE_ASSERT(numSlots <= maxFramebufferImages);
335 
336 		auto& vk = vkDevCtx.context->getDeviceInterface();
337 
338 		if (m_queryPool == VK_NULL_HANDLE) {
339 			// It would be difficult to resize a query pool, so allocate the maximum possible slot.
340 			numSlots = maxFramebufferImages;
341 			VkQueryPoolCreateInfo queryPoolCreateInfo{};
342 			queryPoolCreateInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
343 			queryPoolCreateInfo.pNext = pDecodeProfile;
344 			queryPoolCreateInfo.queryType = VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR;
345 			queryPoolCreateInfo.queryCount = numSlots;  // m_numDecodeSurfaces frames worth
346 
347 			return vk.createQueryPool(vkDevCtx.device, &queryPoolCreateInfo, nullptr, &m_queryPool);
348 		}
349 
350 		return VK_SUCCESS;
351 	}
352 
353 	void DestroyVideoQueries() {
354 		if (m_queryPool != VK_NULL_HANDLE) {
355 			m_vkDevCtx.getDeviceDriver().destroyQueryPool(m_vkDevCtx.device, m_queryPool, nullptr);
356 			m_queryPool = VK_NULL_HANDLE;
357 		}
358 	}
359 
360 	deUint32 FlushDisplayQueue() {
361 		std::lock_guard<std::mutex> lock(m_displayQueueMutex);
362 
363 		deUint32 flushedImages = 0;
364 		while (!m_displayFrames.empty()) {
365 			deUint8 pictureIndex = m_displayFrames.front();
366 			DE_ASSERT(pictureIndex < m_perFrameDecodeImageSet.size());
367 			m_displayFrames.pop();
368 			if (m_perFrameDecodeImageSet[(deUint32)pictureIndex].IsAvailable()) {
369 				// The frame is not released yet - force release it.
370 				m_perFrameDecodeImageSet[(deUint32)pictureIndex].Release();
371 			}
372 			flushedImages++;
373 		}
374 
375 		return flushedImages;
376 	}
377 
378 	int32_t InitImagePool(const VkVideoProfileInfoKHR* pDecodeProfile, deUint32 numImages, VkFormat dpbImageFormat,
379 								  VkFormat outImageFormat, const VkExtent2D& codedExtent, const VkExtent2D& maxImageExtent,
380 								  VkImageUsageFlags dpbImageUsage, VkImageUsageFlags outImageUsage, deUint32 queueFamilyIndex,
381 								  bool useImageArray, bool useImageViewArray,
382 								  bool useSeparateOutputImage, bool useLinearOutput) override;
383 
384 	void Deinitialize() {
385 		FlushDisplayQueue();
386 
387 		if (m_supportsQueries)
388 			DestroyVideoQueries();
389 
390 		m_ownedByDisplayMask = 0;
391 		m_frameNumInDecodeOrder = 0;
392 		m_frameNumInDisplayOrder = 0;
393 
394 		if (m_queryPool != VK_NULL_HANDLE) {
395 			m_vkDevCtx.getDeviceDriver().destroyQueryPool(m_vkDevCtx.device, m_queryPool, nullptr);
396 			m_queryPool = VK_NULL_HANDLE;
397 		}
398 	};
399 
400 	int32_t QueueDecodedPictureForDisplay(int8_t picId, VulkanVideoDisplayPictureInfo* pDispInfo) override {
401 		DE_ASSERT((deUint32)picId < m_perFrameDecodeImageSet.size());
402 
403 		std::lock_guard<std::mutex> lock(m_displayQueueMutex);
404 		m_perFrameDecodeImageSet[picId].m_displayOrder = m_frameNumInDisplayOrder++;
405 		m_perFrameDecodeImageSet[picId].m_timestamp = pDispInfo->timestamp;
406 		m_perFrameDecodeImageSet[picId].AddRef();
407 
408 		m_displayFrames.push((deUint8)picId);
409 
410 		if (videoLoggingEnabled()) {
411 			std::cout << "==> Queue Display Picture picIdx: " << (deUint32)picId
412 					  << "\t\tdisplayOrder: " << m_perFrameDecodeImageSet[picId].m_displayOrder
413 					  << "\tdecodeOrder: " << m_perFrameDecodeImageSet[picId].m_decodeOrder << "\ttimestamp "
414 					  << m_perFrameDecodeImageSet[picId].m_timestamp << std::endl;
415 		}
416 		return picId;
417 	}
418 
419 	int32_t QueuePictureForDecode(int8_t picId, VkParserDecodePictureInfo* pDecodePictureInfo,
420 										  ReferencedObjectsInfo* pReferencedObjectsInfo,
421 										  FrameSynchronizationInfo* pFrameSynchronizationInfo) override;
422 
423 	size_t GetDisplayedFrameCount() const override { return m_displayFrames.size(); }
424 
425 	// dequeue
426 	int32_t DequeueDecodedPicture(DecodedFrame* pDecodedFrame) override;
427 
428 	int32_t ReleaseDisplayedPicture(DecodedFrameRelease** pDecodedFramesRelease, deUint32 numFramesToRelease) override;
429 
430 	int32_t GetDpbImageResourcesByIndex(deUint32 numResources, const int8_t* referenceSlotIndexes,
431 												VkVideoPictureResourceInfoKHR* dpbPictureResources,
432 												PictureResourceInfo* dpbPictureResourcesInfo,
433 												VkImageLayout newDpbImageLayerLayout = VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR) override;
434 
435 	int32_t GetCurrentImageResourceByIndex(int8_t referenceSlotIndex, VkVideoPictureResourceInfoKHR* dpbPictureResource,
436 												   PictureResourceInfo* dpbPictureResourceInfo,
437 												   VkImageLayout newDpbImageLayerLayout = VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR,
438 												   VkVideoPictureResourceInfoKHR* outputPictureResource = nullptr,
439 												   PictureResourceInfo* outputPictureResourceInfo = nullptr,
440 												   VkImageLayout newOutputImageLayerLayout = VK_IMAGE_LAYOUT_MAX_ENUM) override;
441 
442 	int32_t ReleaseImageResources(deUint32 numResources, const deUint32* indexes) override {
443 		std::lock_guard<std::mutex> lock(m_displayQueueMutex);
444 		for (unsigned int resId = 0; resId < numResources; resId++) {
445 			if ((deUint32)indexes[resId] < m_perFrameDecodeImageSet.size()) {
446 				m_perFrameDecodeImageSet[indexes[resId]].Deinit();
447 			}
448 		}
449 		return (int32_t)m_perFrameDecodeImageSet.size();
450 	}
451 
452 	int32_t SetPicNumInDecodeOrder(int32_t picId, int32_t picNumInDecodeOrder) override {
453 		std::lock_guard<std::mutex> lock(m_displayQueueMutex);
454 		if ((deUint32)picId < m_perFrameDecodeImageSet.size()) {
455 			int32_t oldPicNumInDecodeOrder = m_perFrameDecodeImageSet[picId].m_decodeOrder;
456 			m_perFrameDecodeImageSet[picId].m_decodeOrder = picNumInDecodeOrder;
457 			return oldPicNumInDecodeOrder;
458 		}
459 		DE_ASSERT(false);
460 		return -1;
461 	}
462 
463 	int32_t SetPicNumInDisplayOrder(int32_t picId, int32_t picNumInDisplayOrder) override {
464 		std::lock_guard<std::mutex> lock(m_displayQueueMutex);
465 		if ((deUint32)picId < m_perFrameDecodeImageSet.size()) {
466 			int32_t oldPicNumInDisplayOrder = m_perFrameDecodeImageSet[picId].m_displayOrder;
467 			m_perFrameDecodeImageSet[picId].m_displayOrder = picNumInDisplayOrder;
468 			return oldPicNumInDisplayOrder;
469 		}
470 		DE_ASSERT(false);
471 		return -1;
472 	}
473 
474 	virtual const VkSharedBaseObj<VkImageResourceView>& GetImageResourceByIndex(int8_t picId) {
475 		std::lock_guard<std::mutex> lock(m_displayQueueMutex);
476 		if ((deUint32)picId < m_perFrameDecodeImageSet.size()) {
477 			return m_perFrameDecodeImageSet[picId].GetFrameImageView();
478 		}
479 		DE_ASSERT(false);
480 		return emptyImageView;
481 	}
482 
483 	vkPicBuffBase* ReservePictureBuffer() override {
484 		std::lock_guard<std::mutex> lock(m_displayQueueMutex);
485 		int foundPicId = -1;
486 		for (int picId = 0; picId < m_perFrameDecodeImageSet.size(); picId++) {
487 			if (m_perFrameDecodeImageSet[picId].IsAvailable()) {
488 				foundPicId = picId;
489 				break;
490 			}
491 		}
492 
493 		if (foundPicId >= 0) {
494 			m_perFrameDecodeImageSet[foundPicId].Reset();
495 			m_perFrameDecodeImageSet[foundPicId].AddRef();
496 			m_perFrameDecodeImageSet[foundPicId].m_picIdx = foundPicId;
497 			return &m_perFrameDecodeImageSet[foundPicId];
498 		}
499 
500 		DE_ASSERT(foundPicId >= 0);
501 		return nullptr;
502 	}
503 
504 	size_t GetSize() override {
505 		std::lock_guard<std::mutex> lock(m_displayQueueMutex);
506 		return m_perFrameDecodeImageSet.size();
507 	}
508 
509 	virtual ~VkVideoFrameBuffer() { Deinitialize(); }
510 
511 private:
512 	DeviceContext& m_vkDevCtx;
513 	std::atomic<int32_t> m_refCount;
514 	std::mutex m_displayQueueMutex;
515 	NvPerFrameDecodeImageSet m_perFrameDecodeImageSet;
516 	std::queue<deUint8> m_displayFrames;
517 	bool m_supportsQueries;
518 	VkQueryPool m_queryPool;
519 	deUint32 m_ownedByDisplayMask;
520 	int32_t m_frameNumInDecodeOrder;
521 	int32_t m_frameNumInDisplayOrder;
522 	VkExtent2D m_codedExtent;  // for the codedExtent, not the max image resolution
523 	deUint32 m_numberParameterUpdates;
524 };
525 
526 VkResult VulkanVideoFrameBuffer::Create(DeviceContext* vkDevCtx,
527 										bool supportsQueries,
528 										VkSharedBaseObj<VulkanVideoFrameBuffer>& vkVideoFrameBuffer)
529 {
530 	VkSharedBaseObj<VkVideoFrameBuffer> videoFrameBuffer(new VkVideoFrameBuffer(*vkDevCtx, supportsQueries));
531 	if (videoFrameBuffer) {
532 		vkVideoFrameBuffer = videoFrameBuffer;
533 		return VK_SUCCESS;
534 	}
535 	return VK_ERROR_OUT_OF_HOST_MEMORY;
536 }
537 
538 int32_t VkVideoFrameBuffer::AddRef()
539 {
540 	return ++m_refCount;
541 }
542 
543 int32_t VkVideoFrameBuffer::Release()
544 {
545 	deUint32 ret;
546 	ret = --m_refCount;
547 	// Destroy the device if refcount reaches zero
548 	if (ret == 0) {
549 		delete this;
550 	}
551 	return ret;
552 }
553 
554 int32_t VkVideoFrameBuffer::QueuePictureForDecode(int8_t picId, VkParserDecodePictureInfo* pDecodePictureInfo, VulkanVideoFrameBuffer::ReferencedObjectsInfo* pReferencedObjectsInfo, VulkanVideoFrameBuffer::FrameSynchronizationInfo* pFrameSynchronizationInfo)
555 {
556 	DE_ASSERT((deUint32)picId < m_perFrameDecodeImageSet.size());
557 
558 	std::lock_guard<std::mutex> lock(m_displayQueueMutex);
559 	m_perFrameDecodeImageSet[picId].m_picDispInfo = *pDecodePictureInfo;
560 	m_perFrameDecodeImageSet[picId].m_decodeOrder = m_frameNumInDecodeOrder++;
561 	m_perFrameDecodeImageSet[picId].stdPps = const_cast<VkVideoRefCountBase*>(pReferencedObjectsInfo->pStdPps);
562 	m_perFrameDecodeImageSet[picId].stdSps = const_cast<VkVideoRefCountBase*>(pReferencedObjectsInfo->pStdSps);
563 	m_perFrameDecodeImageSet[picId].stdVps = const_cast<VkVideoRefCountBase*>(pReferencedObjectsInfo->pStdVps);
564 	m_perFrameDecodeImageSet[picId].bitstreamData = const_cast<VkVideoRefCountBase*>(pReferencedObjectsInfo->pBitstreamData);
565 
566 	if (videoLoggingEnabled()) {
567 		std::cout << std::dec << "==> Queue Decode Picture picIdx: " << (deUint32)picId
568 				  << "\t\tdisplayOrder: " << m_perFrameDecodeImageSet[picId].m_displayOrder
569 				  << "\tdecodeOrder: " << m_perFrameDecodeImageSet[picId].m_decodeOrder << "\tFrameType "
570 				  << m_perFrameDecodeImageSet[picId].m_picDispInfo.videoFrameType << std::endl;
571 	}
572 
573 	if (pFrameSynchronizationInfo->hasFrameCompleteSignalFence) {
574 		pFrameSynchronizationInfo->frameCompleteFence = m_perFrameDecodeImageSet[picId].m_frameCompleteFence;
575 		if (!!pFrameSynchronizationInfo->frameCompleteFence) {
576 			m_perFrameDecodeImageSet[picId].m_hasFrameCompleteSignalFence = true;
577 		}
578 	}
579 
580 	if (m_perFrameDecodeImageSet[picId].m_hasConsummerSignalFence) {
581 		pFrameSynchronizationInfo->frameConsumerDoneFence = m_perFrameDecodeImageSet[picId].m_frameConsumerDoneFence;
582 		m_perFrameDecodeImageSet[picId].m_hasConsummerSignalFence = false;
583 	}
584 
585 	if (pFrameSynchronizationInfo->hasFrameCompleteSignalSemaphore) {
586 		pFrameSynchronizationInfo->frameCompleteSemaphore = m_perFrameDecodeImageSet[picId].m_frameCompleteSemaphore;
587 		if (!!pFrameSynchronizationInfo->frameCompleteSemaphore) {
588 			m_perFrameDecodeImageSet[picId].m_hasFrameCompleteSignalSemaphore = true;
589 		}
590 	}
591 
592 	if (m_perFrameDecodeImageSet[picId].m_hasConsummerSignalSemaphore) {
593 		pFrameSynchronizationInfo->frameConsumerDoneSemaphore = m_perFrameDecodeImageSet[picId].m_frameConsumerDoneSemaphore;
594 		m_perFrameDecodeImageSet[picId].m_hasConsummerSignalSemaphore = false;
595 	}
596 
597 	pFrameSynchronizationInfo->queryPool = m_queryPool;
598 	pFrameSynchronizationInfo->startQueryId = picId;
599 	pFrameSynchronizationInfo->numQueries = 1;
600 
601 	return picId;
602 }
603 
604 int32_t VkVideoFrameBuffer::DequeueDecodedPicture(DecodedFrame* pDecodedFrame)
605 {
606 	int numberofPendingFrames = 0;
607 	int pictureIndex = -1;
608 	std::lock_guard<std::mutex> lock(m_displayQueueMutex);
609 	if (!m_displayFrames.empty()) {
610 		numberofPendingFrames = (int)m_displayFrames.size();
611 		pictureIndex = m_displayFrames.front();
612 		DE_ASSERT((pictureIndex >= 0) && ((deUint32)pictureIndex < m_perFrameDecodeImageSet.size()));
613 		DE_ASSERT(!(m_ownedByDisplayMask & (1 << pictureIndex)));
614 		m_ownedByDisplayMask |= (1 << pictureIndex);
615 		m_displayFrames.pop();
616 	}
617 
618 	if ((deUint32)pictureIndex < m_perFrameDecodeImageSet.size()) {
619 		pDecodedFrame->pictureIndex = pictureIndex;
620 
621 		pDecodedFrame->imageLayerIndex = m_perFrameDecodeImageSet[pictureIndex].m_picDispInfo.imageLayerIndex;
622 
623 		pDecodedFrame->decodedImageView = m_perFrameDecodeImageSet[pictureIndex].GetFrameImageView();
624 		pDecodedFrame->outputImageView = m_perFrameDecodeImageSet[pictureIndex].GetDisplayImageView();
625 
626 		pDecodedFrame->displayWidth = m_perFrameDecodeImageSet[pictureIndex].m_picDispInfo.displayWidth;
627 		pDecodedFrame->displayHeight = m_perFrameDecodeImageSet[pictureIndex].m_picDispInfo.displayHeight;
628 
629 		if (m_perFrameDecodeImageSet[pictureIndex].m_hasFrameCompleteSignalFence) {
630 			pDecodedFrame->frameCompleteFence = m_perFrameDecodeImageSet[pictureIndex].m_frameCompleteFence;
631 			m_perFrameDecodeImageSet[pictureIndex].m_hasFrameCompleteSignalFence = false;
632 		} else {
633 			pDecodedFrame->frameCompleteFence = VK_NULL_HANDLE;
634 		}
635 
636 		if (m_perFrameDecodeImageSet[pictureIndex].m_hasFrameCompleteSignalSemaphore) {
637 			pDecodedFrame->frameCompleteSemaphore = m_perFrameDecodeImageSet[pictureIndex].m_frameCompleteSemaphore;
638 			m_perFrameDecodeImageSet[pictureIndex].m_hasFrameCompleteSignalSemaphore = false;
639 		} else {
640 			pDecodedFrame->frameCompleteSemaphore = VK_NULL_HANDLE;
641 		}
642 
643 		pDecodedFrame->frameConsumerDoneFence = m_perFrameDecodeImageSet[pictureIndex].m_frameConsumerDoneFence;
644 		pDecodedFrame->frameConsumerDoneSemaphore = m_perFrameDecodeImageSet[pictureIndex].m_frameConsumerDoneSemaphore;
645 
646 		pDecodedFrame->timestamp = m_perFrameDecodeImageSet[pictureIndex].m_timestamp;
647 		pDecodedFrame->decodeOrder = m_perFrameDecodeImageSet[pictureIndex].m_decodeOrder;
648 		pDecodedFrame->displayOrder = m_perFrameDecodeImageSet[pictureIndex].m_displayOrder;
649 
650 		pDecodedFrame->queryPool = m_queryPool;
651 		pDecodedFrame->startQueryId = pictureIndex;
652 		pDecodedFrame->numQueries = 1;
653 	}
654 
655 	if (videoLoggingEnabled()) {
656 		std::cout << "<<<<<<<<<<< Dequeue from Display: " << pictureIndex << " out of " << numberofPendingFrames
657 				  << " ===========" << std::endl;
658 	}
659 	return numberofPendingFrames;
660 }
661 
662 int32_t VkVideoFrameBuffer::ReleaseDisplayedPicture(DecodedFrameRelease** pDecodedFramesRelease, deUint32 numFramesToRelease)
663 {
664 	std::lock_guard<std::mutex> lock(m_displayQueueMutex);
665 	for (deUint32 i = 0; i < numFramesToRelease; i++) {
666 		const DecodedFrameRelease* pDecodedFrameRelease = pDecodedFramesRelease[i];
667 		int picId = pDecodedFrameRelease->pictureIndex;
668 		DE_ASSERT((picId >= 0) && ((deUint32)picId < m_perFrameDecodeImageSet.size()));
669 
670 		DE_ASSERT(m_perFrameDecodeImageSet[picId].m_decodeOrder == pDecodedFrameRelease->decodeOrder);
671 		DE_ASSERT(m_perFrameDecodeImageSet[picId].m_displayOrder == pDecodedFrameRelease->displayOrder);
672 
673 		DE_ASSERT(m_ownedByDisplayMask & (1 << picId));
674 		m_ownedByDisplayMask &= ~(1 << picId);
675 		m_perFrameDecodeImageSet[picId].bitstreamData = nullptr;
676 		m_perFrameDecodeImageSet[picId].stdPps = nullptr;
677 		m_perFrameDecodeImageSet[picId].stdSps = nullptr;
678 		m_perFrameDecodeImageSet[picId].stdVps = nullptr;
679 		m_perFrameDecodeImageSet[picId].Release();
680 
681 		m_perFrameDecodeImageSet[picId].m_hasConsummerSignalFence = pDecodedFrameRelease->hasConsummerSignalFence;
682 		m_perFrameDecodeImageSet[picId].m_hasConsummerSignalSemaphore = pDecodedFrameRelease->hasConsummerSignalSemaphore;
683 	}
684 	return 0;
685 }
686 
687 int32_t VkVideoFrameBuffer::GetDpbImageResourcesByIndex(deUint32 numResources, const int8_t* referenceSlotIndexes, VkVideoPictureResourceInfoKHR* dpbPictureResources, VulkanVideoFrameBuffer::PictureResourceInfo* dpbPictureResourcesInfo, VkImageLayout newDpbImageLayerLayout)
688 {
689 	DE_ASSERT(dpbPictureResources);
690 	std::lock_guard<std::mutex> lock(m_displayQueueMutex);
691 	for (unsigned int resId = 0; resId < numResources; resId++) {
692 		if ((deUint32)referenceSlotIndexes[resId] < m_perFrameDecodeImageSet.size()) {
693 			VkResult result =
694 				m_perFrameDecodeImageSet.GetImageSetNewLayout(m_vkDevCtx, referenceSlotIndexes[resId], newDpbImageLayerLayout,
695 															  &dpbPictureResources[resId], &dpbPictureResourcesInfo[resId]);
696 
697 			DE_ASSERT(result == VK_SUCCESS);
698 			if (result != VK_SUCCESS) {
699 				return -1;
700 			}
701 
702 			DE_ASSERT(dpbPictureResources[resId].sType == VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR);
703 			dpbPictureResources[resId].codedOffset = {
704 				0, 0};  // FIXME: This parameter must to be adjusted based on the interlaced mode.
705 			dpbPictureResources[resId].codedExtent = m_codedExtent;
706 		}
707 	}
708 	return numResources;
709 }
710 
711 int32_t VkVideoFrameBuffer::GetCurrentImageResourceByIndex(int8_t referenceSlotIndex, VkVideoPictureResourceInfoKHR* dpbPictureResource, VulkanVideoFrameBuffer::PictureResourceInfo* dpbPictureResourceInfo, VkImageLayout newDpbImageLayerLayout, VkVideoPictureResourceInfoKHR* outputPictureResource, VulkanVideoFrameBuffer::PictureResourceInfo* outputPictureResourceInfo, VkImageLayout newOutputImageLayerLayout)
712 {
713 	DE_ASSERT(dpbPictureResource);
714 	std::lock_guard<std::mutex> lock(m_displayQueueMutex);
715 	if ((deUint32)referenceSlotIndex < m_perFrameDecodeImageSet.size()) {
716 		VkResult result = m_perFrameDecodeImageSet.GetImageSetNewLayout(
717 			m_vkDevCtx, referenceSlotIndex, newDpbImageLayerLayout, dpbPictureResource, dpbPictureResourceInfo,
718 			newOutputImageLayerLayout, outputPictureResource, outputPictureResourceInfo);
719 		DE_ASSERT(result == VK_SUCCESS);
720 		if (result != VK_SUCCESS) {
721 			return -1;
722 		}
723 
724 		DE_ASSERT(dpbPictureResource->sType == VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR);
725 		dpbPictureResource->codedOffset = {0, 0};  // FIXME: This parameter must to be adjusted based on the interlaced mode.
726 		dpbPictureResource->codedExtent = m_codedExtent;
727 
728 		if (outputPictureResource) {
729 			DE_ASSERT(outputPictureResource->sType == VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR);
730 			outputPictureResource->codedOffset = {
731 				0, 0};  // FIXME: This parameter must to be adjusted based on the interlaced mode.
732 			outputPictureResource->codedExtent = m_codedExtent;
733 		}
734 	}
735 	return referenceSlotIndex;
736 }
737 
738 int32_t VkVideoFrameBuffer::InitImagePool(const VkVideoProfileInfoKHR* pDecodeProfile, deUint32 numImages, VkFormat dpbImageFormat, VkFormat outImageFormat, const VkExtent2D& codedExtent, const VkExtent2D& maxImageExtent, VkImageUsageFlags dpbImageUsage, VkImageUsageFlags outImageUsage, deUint32 queueFamilyIndex, bool useImageArray, bool useImageViewArray, bool useSeparateOutputImage, bool useLinearOutput)
739 {
740 	std::lock_guard<std::mutex> lock(m_displayQueueMutex);
741 
742 	DE_ASSERT(numImages && (numImages <= maxFramebufferImages) && pDecodeProfile);
743 
744 	if (m_supportsQueries)
745 		VK_CHECK(CreateVideoQueries(numImages, m_vkDevCtx, pDecodeProfile));
746 
747 	// m_extent is for the codedExtent, not the max image resolution
748 	m_codedExtent = codedExtent;
749 
750 	int32_t imageSetCreateResult = m_perFrameDecodeImageSet.init(
751 		m_vkDevCtx, pDecodeProfile, numImages, dpbImageFormat, outImageFormat, maxImageExtent, dpbImageUsage, outImageUsage,
752 		queueFamilyIndex,
753 		useImageArray, useImageViewArray, useSeparateOutputImage, useLinearOutput);
754 	m_numberParameterUpdates++;
755 
756 	return imageSetCreateResult;
757 }
758 
759 VkResult NvPerFrameDecodeResources::CreateImage( DeviceContext& vkDevCtx,
760 												const VkImageCreateInfo* pDpbImageCreateInfo,
761 												const VkImageCreateInfo* pOutImageCreateInfo,
762 												deUint32 imageIndex,
763 												VkSharedBaseObj<VkImageResource>& imageArrayParent,
764 												VkSharedBaseObj<VkImageResourceView>& imageViewArrayParent,
765 												bool useSeparateOutputImage,
766 												bool useLinearOutput)
767 {
768 	VkResult result = VK_SUCCESS;
769 
770 	if (!ImageExist() || m_recreateImage) {
771 
772 		DE_ASSERT(m_vkDevCtx != nullptr);
773 
774 		m_currentDpbImageLayerLayout = pDpbImageCreateInfo->initialLayout;
775 		m_currentOutputImageLayout   = pOutImageCreateInfo->initialLayout;
776 
777 		VkSharedBaseObj<VkImageResource> imageResource;
778 		if (!imageArrayParent) {
779 			result = VkImageResource::Create(vkDevCtx,
780 											 pDpbImageCreateInfo,
781 											 imageResource);
782 			if (result != VK_SUCCESS) {
783 				return result;
784 			}
785 		} else {
786 			// We are using a parent array image
787 			imageResource = imageArrayParent;
788 		}
789 
790 		if (!imageViewArrayParent) {
791 
792 			deUint32 baseArrayLayer = imageArrayParent ? imageIndex : 0;
793 			VkImageSubresourceRange subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, baseArrayLayer, 1 };
794 			result = VkImageResourceView::Create(vkDevCtx, imageResource,
795 												   subresourceRange,
796 												   m_frameDpbImageView);
797 
798 			if (result != VK_SUCCESS) {
799 				return result;
800 			}
801 
802 			if (!(useSeparateOutputImage || useLinearOutput)) {
803 				m_outImageView = m_frameDpbImageView;
804 			}
805 
806 		} else {
807 
808 			m_frameDpbImageView = imageViewArrayParent;
809 
810 			if (!(useSeparateOutputImage || useLinearOutput)) {
811 				VkImageSubresourceRange subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, imageIndex, 1 };
812 				result = VkImageResourceView::Create(vkDevCtx, imageResource,
813 													   subresourceRange,
814 													   m_outImageView);
815 				if (result != VK_SUCCESS) {
816 					return result;
817 				}
818 			}
819 		}
820 
821 		if (useSeparateOutputImage || useLinearOutput) {
822 
823 			VkSharedBaseObj<VkImageResource> displayImageResource;
824 			result = VkImageResource::Create(vkDevCtx,
825 											 pOutImageCreateInfo,
826 											 displayImageResource);
827 			if (result != VK_SUCCESS) {
828 				return result;
829 			}
830 
831 			VkImageSubresourceRange subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
832 			result = VkImageResourceView::Create(vkDevCtx, displayImageResource,
833 												   subresourceRange,
834 												   m_outImageView);
835 			if (result != VK_SUCCESS) {
836 				return result;
837 			}
838 		}
839 	}
840 
841 	m_currentDpbImageLayerLayout = VK_IMAGE_LAYOUT_UNDEFINED;
842 	m_currentOutputImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
843 	m_recreateImage = false;
844 
845 	return result;
846 }
847 
848 VkResult NvPerFrameDecodeResources::init(DeviceContext& vkDevCtx)
849 {
850 	m_vkDevCtx = &vkDevCtx;
851 	auto& vk = vkDevCtx.getDeviceDriver();
852 	auto device = vkDevCtx.device;
853 
854 	// The fence waited on for the first frame should be signaled.
855 	const VkFenceCreateInfo fenceFrameCompleteInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr,
856 													  VK_FENCE_CREATE_SIGNALED_BIT };
857 	VkResult result = vk.createFence(device, &fenceFrameCompleteInfo, nullptr, &m_frameCompleteFence);
858 
859 	VkFenceCreateInfo fenceInfo{};
860 	fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
861 	result = vk.createFence(device, &fenceInfo, nullptr, &m_frameConsumerDoneFence);
862 	DE_ASSERT(result == VK_SUCCESS);
863 
864 	VkSemaphoreCreateInfo semInfo{};
865 	semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
866 	result = vk.createSemaphore(device, &semInfo, nullptr, &m_frameCompleteSemaphore);
867 	DE_ASSERT(result == VK_SUCCESS);
868 	result = vk.createSemaphore(device, &semInfo, nullptr, &m_frameConsumerDoneSemaphore);
869 	DE_ASSERT(result == VK_SUCCESS);
870 
871 	Reset();
872 
873 	return result;
874 }
875 
876 void NvPerFrameDecodeResources::Deinit()
877 {
878 	bitstreamData = nullptr;
879 	stdPps = nullptr;
880 	stdSps = nullptr;
881 	stdVps = nullptr;
882 
883 	if (m_vkDevCtx == nullptr) {
884 		assert ((m_frameCompleteFence == VK_NULL_HANDLE) &&
885 			   (m_frameConsumerDoneFence == VK_NULL_HANDLE) &&
886 			   (m_frameCompleteSemaphore == VK_NULL_HANDLE) &&
887 			   (m_frameConsumerDoneSemaphore == VK_NULL_HANDLE) &&
888 			   !m_frameDpbImageView &&
889 			   !m_outImageView);
890 		return;
891 	}
892 
893 	DE_ASSERT(m_vkDevCtx);
894 	auto& vk = m_vkDevCtx->getDeviceDriver();
895 	auto device = m_vkDevCtx->device;
896 
897 	if (m_frameCompleteFence != VK_NULL_HANDLE) {
898 		vk.destroyFence(device, m_frameCompleteFence, nullptr);
899 		m_frameCompleteFence = VK_NULL_HANDLE;
900 	}
901 
902 	if (m_frameConsumerDoneFence != VK_NULL_HANDLE) {
903 		vk.destroyFence(device, m_frameConsumerDoneFence, nullptr);
904 		m_frameConsumerDoneFence = VK_NULL_HANDLE;
905 	}
906 
907 	if (m_frameCompleteSemaphore != VK_NULL_HANDLE) {
908 		vk.destroySemaphore(device, m_frameCompleteSemaphore, nullptr);
909 		m_frameCompleteSemaphore = VK_NULL_HANDLE;
910 	}
911 
912 	if (m_frameConsumerDoneSemaphore != VK_NULL_HANDLE) {
913 		vk.destroySemaphore(device, m_frameConsumerDoneSemaphore, nullptr);
914 		m_frameConsumerDoneSemaphore = VK_NULL_HANDLE;
915 	}
916 
917 	m_frameDpbImageView = nullptr;
918 	m_outImageView = nullptr;
919 
920 	m_vkDevCtx = nullptr;
921 
922 	Reset();
923 }
924 
925 int32_t NvPerFrameDecodeImageSet::init(DeviceContext& vkDevCtx,
926 									   const VkVideoProfileInfoKHR* pDecodeProfile,
927 									   deUint32                 numImages,
928 									   VkFormat                 dpbImageFormat,
929 									   VkFormat                 outImageFormat,
930 									   const VkExtent2D&        maxImageExtent,
931 									   VkImageUsageFlags        dpbImageUsage,
932 									   VkImageUsageFlags        outImageUsage,
933 									   deUint32                 queueFamilyIndex,
934 									   bool                     useImageArray,
935 									   bool                     useImageViewArray,
936 									   bool                     useSeparateOutputImage,
937 									   bool                     useLinearOutput)
938 {
939 	if (numImages > m_perFrameDecodeResources.size()) {
940 		DE_ASSERT(!"Number of requested images exceeds the max size of the image array");
941 		return -1;
942 	}
943 
944 	const bool reconfigureImages = (m_numImages &&
945 									(m_dpbImageCreateInfo.sType == VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO)) &&
946 								   ((m_dpbImageCreateInfo.format != dpbImageFormat) ||
947 									(m_dpbImageCreateInfo.extent.width < maxImageExtent.width) ||
948 									(m_dpbImageCreateInfo.extent.height < maxImageExtent.height));
949 
950 	for (deUint32 imageIndex = m_numImages; imageIndex < numImages; imageIndex++) {
951 		VkResult result = m_perFrameDecodeResources[imageIndex].init(vkDevCtx);
952 		DE_ASSERT(result == VK_SUCCESS);
953 		if (result != VK_SUCCESS) {
954 			return -1;
955 		}
956 	}
957 
958 	if (useImageViewArray) {
959 		useImageArray = true;
960 	}
961 
962 	m_videoProfile.InitFromProfile(pDecodeProfile);
963 
964 	m_queueFamilyIndex = queueFamilyIndex;
965 
966 	// Image create info for the DPBs
967 	m_dpbImageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
968 	// m_imageCreateInfo.pNext = m_videoProfile.GetProfile();
969 	m_dpbImageCreateInfo.pNext = m_videoProfile.GetProfileListInfo();
970 	m_dpbImageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
971 	m_dpbImageCreateInfo.format = dpbImageFormat;
972 	m_dpbImageCreateInfo.extent = { maxImageExtent.width, maxImageExtent.height, 1 };
973 	m_dpbImageCreateInfo.mipLevels = 1;
974 	m_dpbImageCreateInfo.arrayLayers = useImageArray ? numImages : 1;
975 	m_dpbImageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
976 	m_dpbImageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
977 	m_dpbImageCreateInfo.usage = dpbImageUsage;
978 	m_dpbImageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
979 	m_dpbImageCreateInfo.queueFamilyIndexCount = 1;
980 	m_dpbImageCreateInfo.pQueueFamilyIndices = &m_queueFamilyIndex;
981 	m_dpbImageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
982 	m_dpbImageCreateInfo.flags = 0;
983 
984 	// Image create info for the output
985 	if (useSeparateOutputImage || useLinearOutput) {
986 		m_outImageCreateInfo = m_dpbImageCreateInfo;
987 		m_outImageCreateInfo.format = outImageFormat;
988 		m_outImageCreateInfo.arrayLayers = 1;
989 		m_outImageCreateInfo.tiling = useLinearOutput ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL;
990 		m_outImageCreateInfo.usage = outImageUsage;
991 
992 		if ((outImageUsage & VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR) == 0) {
993 			// A simple output image not directly used by the decoder
994 			m_outImageCreateInfo.pNext = nullptr;
995 		}
996 	}
997 
998 	if (useImageArray) {
999 		// Create an image that has the same number of layers as the DPB images required.
1000 		VkResult result = VkImageResource::Create(vkDevCtx,
1001 												  &m_dpbImageCreateInfo,
1002 												  m_imageArray);
1003 		if (result != VK_SUCCESS) {
1004 			return -1;
1005 		}
1006 	} else {
1007 		m_imageArray = nullptr;
1008 	}
1009 
1010 	if (useImageViewArray) {
1011 		DE_ASSERT(m_imageArray);
1012 		// Create an image view that has the same number of layers as the image.
1013 		// In that scenario, while specifying the resource, the API must specifically choose the image layer.
1014 		VkImageSubresourceRange subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, numImages };
1015 		VkResult result = VkImageResourceView::Create(vkDevCtx, m_imageArray,
1016 													   subresourceRange,
1017 													   m_imageViewArray);
1018 
1019 		if (result != VK_SUCCESS) {
1020 			return -1;
1021 		}
1022 	}
1023 
1024 	deUint32 firstIndex = reconfigureImages ? 0 : m_numImages;
1025 	deUint32 maxNumImages = std::max(m_numImages, numImages);
1026 	for (deUint32 imageIndex = firstIndex; imageIndex < maxNumImages; imageIndex++) {
1027 
1028 		if (m_perFrameDecodeResources[imageIndex].ImageExist() && reconfigureImages) {
1029 
1030 			m_perFrameDecodeResources[imageIndex].m_recreateImage = true;
1031 
1032 		} else if (!m_perFrameDecodeResources[imageIndex].ImageExist()) {
1033 
1034 			VkResult result =
1035 				m_perFrameDecodeResources[imageIndex].CreateImage(vkDevCtx,
1036 																  &m_dpbImageCreateInfo,
1037 																  &m_outImageCreateInfo,
1038 																  imageIndex,
1039 																  m_imageArray,
1040 																  m_imageViewArray,
1041 																  useSeparateOutputImage,
1042 																  useLinearOutput);
1043 
1044 			DE_ASSERT(result == VK_SUCCESS);
1045 			if (result != VK_SUCCESS) {
1046 				return -1;
1047 			}
1048 		}
1049 	}
1050 
1051 	m_numImages               = numImages;
1052 	m_usesImageArray          = useImageArray;
1053 	m_usesImageViewArray      = useImageViewArray;
1054 	m_usesSeparateOutputImage = useSeparateOutputImage;
1055 	m_usesLinearOutput        = useLinearOutput;
1056 
1057 	return (int32_t)numImages;
1058 }
1059 
1060 } // namespace video
1061 } // namespace vkt
1062