1 /*
2 * Copyright (c) 2009-2018, Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 */
22 //!
23 //! \file vphal_mdf_wrapper.cpp
24 //! \brief Abstraction for MDF related operations.
25 //! \details It is a thin wrapper layer based on MDF APIs.
26 //!
27 #include "vphal_mdf_wrapper.h"
28 #include <algorithm>
29 #include <cstdio>
30
31
OnEventAvailable(CmEvent * event,const std::string & name)32 void EventManager::OnEventAvailable(CmEvent *event, const std::string &name)
33 {
34 AddEvent(name, event);
35 }
36
AddEvent(const std::string & name,CmEvent * event)37 void EventManager::AddEvent(const std::string &name, CmEvent *event)
38 {
39 if (mEventCount >= (128 * 1024) / sizeof(CmEvent))
40 {
41 if (mReport)
42 {
43 Profiling();
44 }
45
46 Clear();
47 }
48
49 mEventMap[name].push_back(event);
50 mLastEvent = event;
51 mEventCount++;
52 }
53
Clear()54 void EventManager::Clear()
55 {
56 VPHAL_RENDER_CHK_NULL_NO_STATUS_RETURN(m_cmContext);
57 CmQueue *queue = m_cmContext->GetCmQueue();
58 VPHAL_RENDER_CHK_NULL_NO_STATUS_RETURN(queue);
59 for (auto it : mEventMap)
60 {
61 for (CmEvent *event : it.second)
62 {
63 queue->DestroyEvent(event);
64 }
65 }
66
67 mEventMap.clear();
68 mEventCount = 0;
69 mLastEvent = nullptr;
70 }
71
Profiling() const72 void EventManager::Profiling() const
73 {
74 VPHAL_RENDER_NORMALMESSAGE("------------------------%s Profiling Report------------------------\n", mOwner.c_str());
75 for (auto it : mEventMap)
76 {
77 int count = 0;
78 double totalTimeInMS = 0.0;
79 for (CmEvent *event : it.second)
80 {
81 uint64_t executionTimeInNS = 0;
82 int result = event->GetExecutionTime(executionTimeInNS);
83 if (result != CM_SUCCESS)
84 {
85 VPHAL_RENDER_ASSERTMESSAGE("[%s]: CM GetExecutionTime error: %d\n", it.first.c_str(), result);
86 continue;
87 }
88 totalTimeInMS += executionTimeInNS / 1000000.0;
89 count++;
90 }
91 VPHAL_RENDER_NORMALMESSAGE("[%s]: execution count %llu, average time %f ms.\n", it.first.c_str(), it.second.size(), totalTimeInMS / count);
92 }
93 VPHAL_RENDER_NORMALMESSAGE("------------------------%s Profiling Report End------------------------\n", mOwner.c_str());
94 }
95
GetLastEvent() const96 CmEvent* EventManager::GetLastEvent() const
97 {
98 return mLastEvent;
99 }
100
CmContext(PMOS_INTERFACE osInterface)101 CmContext::CmContext(PMOS_INTERFACE osInterface) :
102 mRefCount(0),
103 mCmDevice(nullptr),
104 mCmQueue(nullptr),
105 mCmVebox(nullptr),
106 m_osInterface(osInterface),
107 mBatchTask(nullptr),
108 mHasBatchedTask(false),
109 mConditionalBatchBuffer(nullptr),
110 mCondParam({ 0 }),
111 mEventListener(nullptr)
112 {
113 VPHAL_RENDER_CHK_NULL_NO_STATUS_RETURN(osInterface);
114
115 const unsigned int MDF_DEVICE_CREATE_OPTION =
116 ((CM_DEVICE_CREATE_OPTION_SCRATCH_SPACE_DISABLE) |
117 (CM_DEVICE_CONFIG_DSH_DISABLE_MASK) |
118 (CM_DEVICE_CONFIG_TASK_NUM_16 << CM_DEVICE_CONFIG_TASK_NUM_OFFSET) |
119 (CM_DEVICE_CONFIG_MEDIA_RESET_ENABLE) |
120 (CM_DEVICE_CONFIG_EXTRA_TASK_NUM_4 << CM_DEVICE_CONFIG_EXTRA_TASK_NUM_OFFSET) |
121 (CM_DEVICE_CONFIG_GPUCONTEXT_ENABLE) |
122 (32 << CM_DEVICE_CONFIG_KERNELBINARYGSH_OFFSET));
123
124 int result = osInterface->pfnCreateCmDevice(osInterface->pOsContext, mCmDevice, MDF_DEVICE_CREATE_OPTION, CM_DEVICE_CREATE_PRIORITY_DEFAULT);
125 if (result != CM_SUCCESS)
126 {
127 VPHAL_RENDER_ASSERTMESSAGE("CmDevice creation error %d\n", result);
128 return;
129 }
130
131 result = mCmDevice->CreateQueue(mCmQueue);
132 if (result != CM_SUCCESS)
133 {
134 VPHAL_RENDER_ASSERTMESSAGE("CmQueue creation error %d\n", result);
135 return;
136 }
137
138 result = mCmDevice->CreateVebox(mCmVebox);
139 if (result != CM_SUCCESS)
140 {
141 VPHAL_RENDER_ASSERTMESSAGE("CmVebox creation error %d\n", result);
142 return;
143 }
144
145 #if (_DEBUG || _RELEASE_INTERNAL)
146 result = mCmDevice->InitPrintBuffer(32768);
147 if (result != CM_SUCCESS)
148 {
149 VPHAL_RENDER_ASSERTMESSAGE("Init printf error: %d\n", result);
150 return;
151 }
152 #endif
153
154 result = mCmDevice->CreateTask(mBatchTask);
155 if (result != CM_SUCCESS)
156 {
157 VPHAL_RENDER_ASSERTMESSAGE("Create batch task error: %d\n", result);
158 return;
159 }
160
161 }
162
CloneKernel(CmKernel * kernel)163 CmKernel* CmContext::CloneKernel(CmKernel *kernel)
164 {
165 auto it = std::find(mAddedKernels.begin(), mAddedKernels.end(), kernel);
166 if (it != mAddedKernels.end())
167 {
168 CmKernel *newKernel = nullptr;
169 int result = mCmDevice->CloneKernel(newKernel, kernel);
170 if (result != CM_SUCCESS)
171 {
172 // Clone kernel failed, try to use the old one.
173 VPHAL_RENDER_ASSERTMESSAGE("Clone kernel failed: %d\n", result);
174 return kernel;
175 }
176 mKernelsToPurge.push_back(newKernel);
177 return newKernel;
178 }
179 else
180 {
181 return kernel;
182 }
183 }
184
BatchKernel(CmKernel * kernel,CmThreadSpace * threadSpace,bool bFence)185 void CmContext::BatchKernel(CmKernel *kernel, CmThreadSpace *threadSpace, bool bFence)
186 {
187 int result;
188
189 if (mConditionalBatchBuffer && mAddedKernels.empty())
190 {
191 result = mBatchTask->AddConditionalEnd(mConditionalBatchBuffer->GetCmSurfaceIndex(), 0, &mCondParam);
192 if (result != CM_SUCCESS)
193 {
194 VPHAL_RENDER_ASSERTMESSAGE("Batch task AddConditionalEnd error: %d\n", result);
195 return;
196 }
197 }
198
199 if (bFence)
200 {
201 result = mBatchTask->AddSync();
202 if (result != CM_SUCCESS)
203 {
204 VPHAL_RENDER_ASSERTMESSAGE("Batch task add sync error: %d\n", result);
205 return;
206 }
207 }
208
209 result = mBatchTask->AddKernel(kernel);
210 if (result == CM_EXCEED_MAX_KERNEL_PER_ENQUEUE)
211 {
212 // Reach max kernels per task, flush and try again.
213 bool needAddBack = false;
214 if (mKernelsToPurge.back() == kernel)
215 {
216 mKernelsToPurge.pop_back();
217 needAddBack = true;
218 }
219
220 FlushBatchTask(false);
221 BatchKernel(kernel, threadSpace, false);
222
223 if (needAddBack)
224 {
225 mKernelsToPurge.push_back(kernel);
226 }
227
228 return;
229 }
230 else if (result != CM_SUCCESS)
231 {
232 VPHAL_RENDER_ASSERTMESSAGE("Batch task add sync error: %d\n", result);
233 return;
234 }
235
236 mAddedKernels.push_back(kernel);
237 mThreadSpacesToPurge.push_back(threadSpace);
238 mHasBatchedTask = true;
239 }
240
FlushBatchTask(bool waitForFinish)241 void CmContext::FlushBatchTask(bool waitForFinish)
242 {
243 int result = CM_SUCCESS;
244
245 if (mAddedKernels.empty())
246 {
247 return;
248 }
249
250 EnqueueTask(mBatchTask, nullptr, "BatchTask", waitForFinish);
251
252 for(auto it : mThreadSpacesToPurge)
253 {
254 result = mCmDevice->DestroyThreadSpace(it);
255 if (result != CM_SUCCESS)
256 {
257 VPHAL_RENDER_ASSERTMESSAGE("CM DestroyThreadSpace Fail %d", result);
258 }
259 }
260
261 for(auto it : mKernelsToPurge)
262 {
263 result = mCmDevice->DestroyKernel(it);
264 if (result != CM_SUCCESS)
265 {
266 VPHAL_RENDER_ASSERTMESSAGE("CM DestroyKernel Fail %d", result);
267 }
268 }
269
270 mThreadSpacesToPurge.clear();
271 mKernelsToPurge.clear();
272 mAddedKernels.clear();
273 result = mBatchTask->Reset();
274 if (result != CM_SUCCESS)
275 {
276 VPHAL_RENDER_ASSERTMESSAGE("CM Batch Task Reset Fail %d", result);
277 }
278 }
279
RunSingleKernel(CmKernel * kernel,CmThreadSpace * threadSpace,const std::string & name,bool waitForFinish)280 void CmContext::RunSingleKernel(
281 CmKernel *kernel,
282 CmThreadSpace *threadSpace,
283 const std::string &name,
284 bool waitForFinish)
285 {
286 FlushBatchTask(false);
287
288 CmTask *task = nullptr;
289 int result = mCmDevice->CreateTask(task);
290 if (result != CM_SUCCESS)
291 {
292 VPHAL_RENDER_ASSERTMESSAGE("[%s]: CmDevice CreateTask error: %d\n", name.c_str(), result);
293 return;
294 }
295
296 if (mConditionalBatchBuffer)
297 {
298 result = task->AddConditionalEnd(mConditionalBatchBuffer->GetCmSurfaceIndex(), 0, &mCondParam);
299 if (result != CM_SUCCESS)
300 {
301 VPHAL_RENDER_ASSERTMESSAGE("[%s]: AddConditionalEnd error: %d\n", name.c_str(), result);
302 mCmDevice->DestroyTask(task);
303 return;
304 }
305 }
306
307 result = task->AddKernel(kernel);
308 if (result != CM_SUCCESS)
309 {
310 VPHAL_RENDER_ASSERTMESSAGE("[%s]: CmDevice AddKernel error: %d\n", name.c_str(), result);
311 mCmDevice->DestroyTask(task);
312 return;
313 }
314
315 EnqueueTask(task, threadSpace, name, waitForFinish);
316 }
317
EnqueueTask(CmTask * task,CmThreadSpace * threadSpace,const std::string & name,bool waitForFinish)318 void CmContext::EnqueueTask(CmTask *task, CmThreadSpace *threadSpace, const std::string &name, bool waitForFinish)
319 {
320 CmEvent *event = nullptr;
321 int result = mCmQueue->Enqueue(task, event, threadSpace);
322 if (result != CM_SUCCESS)
323 {
324 VPHAL_RENDER_ASSERTMESSAGE("[%s]: CmDevice enqueue error: %d\n", name.c_str(), result);
325 return;
326 }
327
328 if (waitForFinish)
329 {
330 result = event->WaitForTaskFinished(-1);
331 if (result != CM_SUCCESS)
332 {
333 VPHAL_RENDER_ASSERTMESSAGE("WaitForTaskFinished Failed %d", result);
334 }
335
336 #if (_DEBUG || _RELEASE_INTERNAL)
337 result = mCmDevice->FlushPrintBuffer();
338 if (result != CM_SUCCESS)
339 {
340 VPHAL_RENDER_ASSERTMESSAGE("[%s]: Flush printf buffer error: %d", name.c_str(), result);
341 }
342 std::fflush(stdout);
343 #endif
344 }
345
346 if (mEventListener)
347 {
348 mEventListener->OnEventAvailable(event, name);
349 }
350 else
351 {
352 if (event != nullptr)
353 {
354 result = mCmQueue->DestroyEvent(event);
355 if (result != CM_SUCCESS)
356 {
357 VPHAL_RENDER_ASSERTMESSAGE("DestroyEvent Failed %d", result);
358 }
359 }
360 }
361 }
362
Destroy()363 void CmContext::Destroy()
364 {
365 int result = CM_SUCCESS;
366 FlushBatchTask(false);
367
368 if (mBatchTask)
369 {
370 result = mCmDevice->DestroyTask(mBatchTask);
371 if (result != CM_SUCCESS)
372 {
373 VPHAL_RENDER_ASSERTMESSAGE("CM DestroyTask Fail %d", result);
374 }
375 }
376
377 if (mCmVebox)
378 {
379 result = mCmDevice->DestroyVebox(mCmVebox);
380 if (result != CM_SUCCESS)
381 {
382 VPHAL_RENDER_ASSERTMESSAGE("CM DestroyVebox Fail %d", result);
383 }
384 }
385
386 if (mCmDevice && m_osInterface)
387 {
388 m_osInterface->pfnDestroyCmDevice(mCmDevice);
389 }
390
391 mBatchTask = nullptr;
392 mCmVebox = nullptr;
393 mCmDevice = nullptr;
394 }
395
~CmContext()396 CmContext::~CmContext()
397 {
398 Destroy();
399 }
400
VPCmRenderer(const std::string & name,CmContext * cmContext)401 VPCmRenderer::VPCmRenderer(const std::string &name, CmContext *cmContext) :
402 mName(name),
403 m_cmContext(cmContext),
404 mBatchDispatch(true),
405 mBlockingMode(false),
406 mEnableDump(false)
407 {
408 }
409
~VPCmRenderer()410 VPCmRenderer::~VPCmRenderer()
411 {
412 }
413
LoadProgram(const std::string & binaryFileName)414 CmProgram* VPCmRenderer::LoadProgram(const std::string& binaryFileName)
415 {
416 std::ifstream isa(binaryFileName, std::ifstream::ate | std::ifstream::binary);
417
418 if (!isa.is_open())
419 {
420 VPHAL_RENDER_ASSERTMESSAGE("Error in opening ISA file: %s.\n", binaryFileName.c_str());
421 return nullptr;
422 }
423
424 int size = static_cast<int>(isa.tellg());
425 if (size == 0)
426 {
427 VPHAL_RENDER_ASSERTMESSAGE("Code size is 0.\n");
428 return nullptr;
429 }
430
431 isa.seekg(0, isa.beg);
432 std::vector<char> code(size);
433 isa.read(code.data(), size);
434
435 CmProgram *program = nullptr;
436 if (!m_cmContext)
437 {
438 return nullptr;
439 }
440 CmDevice *dev = m_cmContext->GetCmDevice();
441 if (!dev)
442 {
443 return nullptr;
444 }
445 int result = dev->LoadProgram(code.data(), size, program, "-nojitter");
446 if (result != CM_SUCCESS)
447 {
448 VPHAL_RENDER_ASSERTMESSAGE("[%s]: CM LoadProgram error %d\n", mName.c_str(), result);
449 return nullptr;
450 }
451
452 return program;
453 }
454
LoadProgram(const void * binary,int size)455 CmProgram* VPCmRenderer::LoadProgram(const void *binary, int size)
456 {
457 if (!binary || size == 0)
458 {
459 VPHAL_RENDER_ASSERTMESSAGE("Invalid program to load.\n");
460 return nullptr;
461 }
462
463 CmProgram *program = nullptr;
464 if (!m_cmContext)
465 {
466 return nullptr;
467 }
468 CmDevice *dev = m_cmContext->GetCmDevice();
469 if (!dev)
470 {
471 return nullptr;
472 }
473 int result = dev->LoadProgram(const_cast<void *>(binary), size, program, "-nojitter");
474 if (result != CM_SUCCESS)
475 {
476 VPHAL_RENDER_ASSERTMESSAGE("[%s]: CM LoadProgram error %d\n", mName.c_str(), result);
477 return nullptr;
478 }
479
480 return program;
481 }
482
Render(void * payload)483 void VPCmRenderer::Render(void *payload)
484 {
485 AttachPayload(payload);
486
487 std::string kernelName;
488 CmKernel *kernel = GetKernelToRun(kernelName);
489 if (!kernel)
490 {
491 VPHAL_RENDER_ASSERTMESSAGE("[%s]: Did not find proper kernel to run\n", mName.c_str());
492 return;
493 }
494
495 int tsWidth, tsHeight, tsColor;
496 GetThreadSpaceDimension(tsWidth, tsHeight, tsColor);
497 if (!tsWidth || !tsHeight || !tsColor)
498 {
499 VPHAL_RENDER_ASSERTMESSAGE("[%s]: Degenerate thread space, return immediately.\n", mName.c_str());
500 return;
501 }
502
503 VPHAL_RENDER_CHK_NULL_NO_STATUS_RETURN(m_cmContext);
504 CmThreadSpace *threadSpace = nullptr;
505 CmDevice *dev = m_cmContext->GetCmDevice();
506 VPHAL_RENDER_CHK_NULL_NO_STATUS_RETURN(dev);
507 int result = dev->CreateThreadSpace(tsWidth, tsHeight, threadSpace);
508 if (result != CM_SUCCESS)
509 {
510 VPHAL_RENDER_ASSERTMESSAGE("[%s]: CM Create ThreadSpace error: %d\n", mName.c_str(), result);
511 return;
512 }
513
514 SetupThreadSpace(threadSpace, tsWidth, tsHeight, tsColor);
515
516 // We need to use CloneKernel API to add the same kernel multiple times into one task.
517 bool bBatch = mBatchDispatch && !mBlockingMode && !mEnableDump && !CannotAssociateThreadSpace();
518 if (bBatch)
519 {
520 kernel = m_cmContext->CloneKernel(kernel);
521 }
522
523 result = kernel->SetThreadCount(tsWidth * tsHeight * tsColor);
524 if (result != CM_SUCCESS)
525 {
526 VPHAL_RENDER_ASSERTMESSAGE("[%s]: CM Set ThreadCount error: %d\n", mName.c_str(), result);
527 }
528
529 if (!CannotAssociateThreadSpace())
530 {
531 kernel->AssociateThreadSpace(threadSpace);
532 }
533
534 PrepareKernel(kernel);
535
536 if (bBatch)
537 {
538 m_cmContext->BatchKernel(kernel, threadSpace, NeedAddSync());
539 }
540 else
541 {
542 m_cmContext->RunSingleKernel(kernel, CannotAssociateThreadSpace() ? threadSpace : nullptr, kernelName, mBlockingMode);
543 dev->DestroyThreadSpace(threadSpace);
544 }
545
546 if (mEnableDump)
547 {
548 Dump();
549 }
550
551 AttachPayload(nullptr);
552 }