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 PMOS_CONTEXT CmContext::sOsContext = nullptr;
32
OnEventAvailable(CmEvent * event,const std::string & name)33 void EventManager::OnEventAvailable(CmEvent *event, const std::string &name)
34 {
35 AddEvent(name, event);
36 }
37
AddEvent(const std::string & name,CmEvent * event)38 void EventManager::AddEvent(const std::string &name, CmEvent *event)
39 {
40 if (mEventCount >= (128 * 1024) / sizeof(CmEvent))
41 {
42 if (mReport)
43 {
44 Profiling();
45 }
46
47 Clear();
48 }
49
50 mEventMap[name].push_back(event);
51 mLastEvent = event;
52 mEventCount++;
53 }
54
Clear()55 void EventManager::Clear()
56 {
57 CmQueue *queue = CmContext::GetCmContext().GetCmQueue();
58
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()101 CmContext::CmContext():
102 mRefCount(0),
103 mCmDevice(nullptr),
104 mCmQueue(nullptr),
105 mCmVebox(nullptr),
106 mBatchTask(nullptr),
107 mHasBatchedTask(false),
108 mConditionalBatchBuffer(nullptr),
109 mCondParam({ 0 }),
110 mEventListener(nullptr)
111 {
112 VPHAL_RENDER_ASSERT(sOsContext);
113
114 const unsigned int MDF_DEVICE_CREATE_OPTION =
115 ((CM_DEVICE_CREATE_OPTION_SCRATCH_SPACE_DISABLE) |
116 (CM_DEVICE_CONFIG_DSH_DISABLE_MASK) |
117 (CM_DEVICE_CONFIG_TASK_NUM_16 << CM_DEVICE_CONFIG_TASK_NUM_OFFSET) |
118 (CM_DEVICE_CONFIG_MEDIA_RESET_ENABLE) |
119 (CM_DEVICE_CONFIG_EXTRA_TASK_NUM_4 << CM_DEVICE_CONFIG_EXTRA_TASK_NUM_OFFSET) |
120 (CM_DEVICE_CONFIG_GPUCONTEXT_ENABLE) |
121 (32 << CM_DEVICE_CONFIG_KERNELBINARYGSH_OFFSET));
122
123 int result = CreateCmDevice(sOsContext, mCmDevice, MDF_DEVICE_CREATE_OPTION);
124 if (result != CM_SUCCESS)
125 {
126 VPHAL_RENDER_ASSERTMESSAGE("CmDevice creation error %d\n", result);
127 return;
128 }
129
130 result = mCmDevice->CreateQueue(mCmQueue);
131 if (result != CM_SUCCESS)
132 {
133 VPHAL_RENDER_ASSERTMESSAGE("CmQueue creation error %d\n", result);
134 return;
135 }
136
137 result = mCmDevice->CreateVebox(mCmVebox);
138 if (result != CM_SUCCESS)
139 {
140 VPHAL_RENDER_ASSERTMESSAGE("CmVebox creation error %d\n", result);
141 return;
142 }
143
144 #if (_DEBUG || _RELEASE_INTERNAL)
145 result = mCmDevice->InitPrintBuffer(32768);
146 if (result != CM_SUCCESS)
147 {
148 VPHAL_RENDER_ASSERTMESSAGE("Init printf error: %d\n", result);
149 return;
150 }
151 #endif
152
153 result = mCmDevice->CreateTask(mBatchTask);
154 if (result != CM_SUCCESS)
155 {
156 VPHAL_RENDER_ASSERTMESSAGE("Create batch task error: %d\n", result);
157 return;
158 }
159
160 }
161
CloneKernel(CmKernel * kernel)162 CmKernel* CmContext::CloneKernel(CmKernel *kernel)
163 {
164 auto it = std::find(mAddedKernels.begin(), mAddedKernels.end(), kernel);
165 if (it != mAddedKernels.end())
166 {
167 CmKernel *newKernel = nullptr;
168 int result = mCmDevice->CloneKernel(newKernel, kernel);
169 if (result != CM_SUCCESS)
170 {
171 // Clone kernel failed, try to use the old one.
172 VPHAL_RENDER_ASSERTMESSAGE("Clone kernel failed: %d\n", result);
173 return kernel;
174 }
175 mKernelsToPurge.push_back(newKernel);
176 return newKernel;
177 }
178 else
179 {
180 return kernel;
181 }
182 }
183
BatchKernel(CmKernel * kernel,CmThreadSpace * threadSpace,bool bFence)184 void CmContext::BatchKernel(CmKernel *kernel, CmThreadSpace *threadSpace, bool bFence)
185 {
186 int result;
187
188 if (mConditionalBatchBuffer && mAddedKernels.empty())
189 {
190 result = mBatchTask->AddConditionalEnd(mConditionalBatchBuffer->GetCmSurfaceIndex(), 0, &mCondParam);
191 if (result != CM_SUCCESS)
192 {
193 VPHAL_RENDER_ASSERTMESSAGE("Batch task AddConditionalEnd error: %d\n", result);
194 return;
195 }
196 }
197
198 if (bFence)
199 {
200 result = mBatchTask->AddSync();
201 if (result != CM_SUCCESS)
202 {
203 VPHAL_RENDER_ASSERTMESSAGE("Batch task add sync error: %d\n", result);
204 return;
205 }
206 }
207
208 result = mBatchTask->AddKernel(kernel);
209 if (result == CM_EXCEED_MAX_KERNEL_PER_ENQUEUE)
210 {
211 // Reach max kernels per task, flush and try again.
212 bool needAddBack = false;
213 if (mKernelsToPurge.back() == kernel)
214 {
215 mKernelsToPurge.pop_back();
216 needAddBack = true;
217 }
218
219 FlushBatchTask(false);
220 BatchKernel(kernel, threadSpace, false);
221
222 if (needAddBack)
223 {
224 mKernelsToPurge.push_back(kernel);
225 }
226
227 return;
228 }
229 else if (result != CM_SUCCESS)
230 {
231 VPHAL_RENDER_ASSERTMESSAGE("Batch task add sync error: %d\n", result);
232 return;
233 }
234
235 mAddedKernels.push_back(kernel);
236 mThreadSpacesToPurge.push_back(threadSpace);
237 mHasBatchedTask = true;
238 }
239
FlushBatchTask(bool waitForFinish)240 void CmContext::FlushBatchTask(bool waitForFinish)
241 {
242 if (mAddedKernels.empty())
243 {
244 return;
245 }
246
247 EnqueueTask(mBatchTask, nullptr, "BatchTask", waitForFinish);
248
249 for(auto it : mThreadSpacesToPurge)
250 {
251 mCmDevice->DestroyThreadSpace(it);
252 }
253
254 for(auto it : mKernelsToPurge)
255 {
256 mCmDevice->DestroyKernel(it);
257 }
258
259 mThreadSpacesToPurge.clear();
260 mKernelsToPurge.clear();
261 mAddedKernels.clear();
262 mBatchTask->Reset();
263 }
264
RunSingleKernel(CmKernel * kernel,CmThreadSpace * threadSpace,const std::string & name,bool waitForFinish)265 void CmContext::RunSingleKernel(
266 CmKernel *kernel,
267 CmThreadSpace *threadSpace,
268 const std::string &name,
269 bool waitForFinish)
270 {
271 FlushBatchTask(false);
272
273 CmTask *task = nullptr;
274 int result = mCmDevice->CreateTask(task);
275 if (result != CM_SUCCESS)
276 {
277 VPHAL_RENDER_ASSERTMESSAGE("[%s]: CmDevice CreateTask error: %d\n", name.c_str(), result);
278 return;
279 }
280
281 if (mConditionalBatchBuffer)
282 {
283 result = task->AddConditionalEnd(mConditionalBatchBuffer->GetCmSurfaceIndex(), 0, &mCondParam);
284 if (result != CM_SUCCESS)
285 {
286 VPHAL_RENDER_ASSERTMESSAGE("[%s]: AddConditionalEnd error: %d\n", name.c_str(), result);
287 mCmDevice->DestroyTask(task);
288 return;
289 }
290 }
291
292 result = task->AddKernel(kernel);
293 if (result != CM_SUCCESS)
294 {
295 VPHAL_RENDER_ASSERTMESSAGE("[%s]: CmDevice AddKernel error: %d\n", name.c_str(), result);
296 mCmDevice->DestroyTask(task);
297 return;
298 }
299
300 EnqueueTask(task, threadSpace, name, waitForFinish);
301 }
302
EnqueueTask(CmTask * task,CmThreadSpace * threadSpace,const std::string & name,bool waitForFinish)303 void CmContext::EnqueueTask(CmTask *task, CmThreadSpace *threadSpace, const std::string &name, bool waitForFinish)
304 {
305 CmEvent *event = nullptr;
306 int result = mCmQueue->Enqueue(task, event, threadSpace);
307 if (result != CM_SUCCESS)
308 {
309 VPHAL_RENDER_ASSERTMESSAGE("[%s]: CmDevice enqueue error: %d\n", name.c_str(), result);
310 return;
311 }
312
313 if (waitForFinish)
314 {
315 event->WaitForTaskFinished(-1);
316
317 #if (_DEBUG || _RELEASE_INTERNAL)
318 result = mCmDevice->FlushPrintBuffer();
319 if (result != CM_SUCCESS)
320 {
321 VPHAL_RENDER_ASSERTMESSAGE("[%s]: Flush printf buffer error: %d", name.c_str(), result);
322 }
323 std::fflush(stdout);
324 #endif
325 }
326
327 if (mEventListener)
328 {
329 mEventListener->OnEventAvailable(event, name);
330 }
331 }
332
Destroy()333 void CmContext::Destroy()
334 {
335 FlushBatchTask(false);
336
337 if (mBatchTask)
338 {
339 mCmDevice->DestroyTask(mBatchTask);
340 }
341
342 if (mCmVebox)
343 {
344 mCmDevice->DestroyVebox(mCmVebox);
345 }
346
347 if (mCmDevice)
348 {
349 DestroyCmDevice(mCmDevice);
350 }
351
352 mBatchTask = nullptr;
353 mCmVebox = nullptr;
354 mCmDevice = nullptr;
355 }
356
~CmContext()357 CmContext::~CmContext()
358 {
359 Destroy();
360 }
361
VPCmRenderer(const std::string & name)362 VPCmRenderer::VPCmRenderer(const std::string &name):
363 mName(name),
364 mBatchDispatch(true),
365 mBlockingMode(false),
366 mEnableDump(false)
367 {
368 }
369
~VPCmRenderer()370 VPCmRenderer::~VPCmRenderer()
371 {
372 }
373
LoadProgram(const std::string & binaryFileName)374 CmProgram* VPCmRenderer::LoadProgram(const std::string& binaryFileName)
375 {
376 std::ifstream isa(binaryFileName, std::ifstream::ate | std::ifstream::binary);
377
378 if (!isa.is_open())
379 {
380 VPHAL_RENDER_ASSERTMESSAGE("Error in opening ISA file: %s.\n", binaryFileName.c_str());
381 return nullptr;
382 }
383
384 int size = static_cast<int>(isa.tellg());
385 if (size == 0)
386 {
387 VPHAL_RENDER_ASSERTMESSAGE("Code size is 0.\n");
388 return nullptr;
389 }
390
391 isa.seekg(0, isa.beg);
392 std::vector<char> code(size);
393 isa.read(code.data(), size);
394
395 CmProgram *program = nullptr;
396 CmDevice *dev = CmContext::GetCmContext().GetCmDevice();
397 int result = dev->LoadProgram(code.data(), size, program, "-nojitter");
398 if (result != CM_SUCCESS )
399 {
400 VPHAL_RENDER_ASSERTMESSAGE("[%s]: CM LoadProgram error %d\n", mName.c_str(), result);
401 return nullptr;
402 }
403
404 return program;
405 }
406
LoadProgram(const void * binary,int size)407 CmProgram* VPCmRenderer::LoadProgram(const void *binary, int size)
408 {
409 if (!binary || size == 0)
410 {
411 VPHAL_RENDER_ASSERTMESSAGE("Invalid program to load.\n");
412 return nullptr;
413 }
414
415 CmProgram *program = nullptr;
416 CmDevice *dev = CmContext::GetCmContext().GetCmDevice();
417 int result = dev->LoadProgram(const_cast<void *>(binary), size, program, "-nojitter");
418 if (result != CM_SUCCESS)
419 {
420 VPHAL_RENDER_ASSERTMESSAGE("[%s]: CM LoadProgram error %d\n", mName.c_str(), result);
421 return nullptr;
422 }
423
424 return program;
425 }
426
Render(void * payload)427 void VPCmRenderer::Render(void *payload)
428 {
429 AttachPayload(payload);
430
431 std::string kernelName;
432 CmKernel *kernel = GetKernelToRun(kernelName);
433 if (!kernel)
434 {
435 VPHAL_RENDER_ASSERTMESSAGE("[%s]: Did not find proper kernel to run\n", mName.c_str());
436 return;
437 }
438
439 int tsWidth, tsHeight, tsColor;
440 GetThreadSpaceDimension(tsWidth, tsHeight, tsColor);
441 if (!tsWidth || !tsHeight || !tsColor)
442 {
443 VPHAL_RENDER_ASSERTMESSAGE("[%s]: Degenerate thread space, return immediately.\n", mName.c_str());
444 return;
445 }
446
447 CmThreadSpace *threadSpace = nullptr;
448 CmDevice *dev = CmContext::GetCmContext().GetCmDevice();
449 int result = dev->CreateThreadSpace(tsWidth, tsHeight, threadSpace);
450 if (result != CM_SUCCESS)
451 {
452 VPHAL_RENDER_ASSERTMESSAGE("[%s]: CM Create ThreadSpace error: %d\n", mName.c_str(), result);
453 return;
454 }
455
456 SetupThreadSpace(threadSpace, tsWidth, tsHeight, tsColor);
457
458 // We need to use CloneKernel API to add the same kernel multiple times into one task.
459 bool bBatch = mBatchDispatch && !mBlockingMode && !mEnableDump && !CannotAssociateThreadSpace();
460 if (bBatch)
461 {
462 kernel = CmContext::GetCmContext().CloneKernel(kernel);
463 }
464
465 kernel->SetThreadCount(tsWidth * tsHeight * tsColor);
466
467 if (!CannotAssociateThreadSpace())
468 {
469 kernel->AssociateThreadSpace(threadSpace);
470 }
471
472 PrepareKernel(kernel);
473
474 if (bBatch)
475 {
476 CmContext::GetCmContext().BatchKernel(kernel, threadSpace, NeedAddSync());
477 }
478 else
479 {
480 CmContext::GetCmContext().RunSingleKernel(kernel, CannotAssociateThreadSpace()? threadSpace : nullptr, kernelName, mBlockingMode);
481 dev->DestroyThreadSpace(threadSpace);
482 }
483
484 if (mEnableDump)
485 {
486 Dump();
487 }
488
489 AttachPayload(nullptr);
490 }