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 }