1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #undef NDEBUG
20 
21 #include <cassert>
22 #include <condition_variable>
23 #include <cstdint>
24 #include <map>
25 #include <mutex>
26 #include <string>
27 #include <thread>
28 
29 #include "GLESv1.h"
30 #include "GLESv3.h"
31 #include "RenderControl.h"
32 #include "Resource.h"
33 
34 struct EglContext;
35 struct Context;
36 
37 typedef void (*PFNSUBMITCMD)(Context*, char*, size_t, int);
38 
39 struct Context {
40     static std::map<uint32_t, Context*> map;
41 
ContextContext42     Context(uint32_t handle_, const char* name_, uint32_t nlen_, PFNSUBMITCMD pfnProcessCmd_,
43             EGLDisplay dpy_)
44         : render_control(this, dpy_), worker(), name(std::string(name_, nlen_)), handle(handle_),
45           pfnProcessCmd(pfnProcessCmd_) {
46         map.emplace(handle, this);
47         reset();
48     }
49 
~ContextContext50     ~Context() {
51         {
52             std::lock_guard<std::mutex> lk(m);
53             killWorker = true;
54         }
55         cv.notify_one();
56         if (worker.joinable())
57             worker.join();
58         map.erase(handle);
59     }
60 
bindContext61     Context* bind(EglContext* ctx_) {
62         for (auto const& it : Context::map) {
63             Context* ctx = it.second;
64             if (ctx == this)
65                 continue;
66             if (ctx->ctx == ctx_)
67                 return ctx;
68         }
69         ctx = ctx_;
70         return nullptr;
71     }
72 
unbindContext73     void unbind() {
74         ctx = nullptr;
75     }
76 
setPidTidContext77     void setPidTid(int pid_, int tid_) {
78         if (pid != pid_ && tid != tid_) {
79             assert(!worker.joinable() && "Changing pid/tid is not allowed");
80             worker = std::thread(&Context::worker_func, this);
81         }
82         pid = pid_;
83         tid = tid_;
84     }
85 
submitCommandContext86     void submitCommand(void* buf, size_t bufSize) {
87         char* cmdBufCopy = new char[bufSize];
88         memcpy(cmdBufCopy, buf, bufSize);
89         {
90             std::lock_guard<std::mutex> lk(m);
91             cmdBufSize = bufSize;
92             cmdBuf = cmdBufCopy;
93         }
94         cv.notify_one();
95     }
96 
setFenceContext97     void setFence(int fence_) {
98         {
99             std::lock_guard<std::mutex> lk(m);
100             fence = fence_;
101             if (!worker.joinable())
102                 processCmd();
103         }
104         cv.notify_one();
105     }
106 
107     std::map<uint32_t, Resource*> resource_map;
108     ChecksumCalculator checksum_calc;
109     RenderControl render_control;
110     Resource* cmd_resp = nullptr;
111     EglContext* ctx = nullptr;
112     std::thread worker;
113     std::string name;
114     uint32_t handle;
115     GLESv1 gles1;
116     GLESv3 gles3;
117     int pid = 0;
118     int tid = 0;
119 
120   private:
121     std::condition_variable cv;
122     PFNSUBMITCMD pfnProcessCmd;
123     bool killWorker = false;
124     size_t cmdBufSize;
125     char* cmdBuf;
126     std::mutex m;
127     int fence;
128 
resetContext129     void reset() {
130         cmdBuf = nullptr;
131         cmdBufSize = 0U;
132         fence = 0;
133     }
134 
worker_funcContext135     void worker_func() {
136         while (!killWorker) {
137             std::unique_lock<std::mutex> lk(m);
138             cv.wait(lk, [this] { return killWorker || (cmdBuf && fence); });
139             if (!killWorker)
140                 processCmd();
141             lk.unlock();
142         }
143     }
144 
processCmdContext145     void processCmd() {
146         pfnProcessCmd(this, cmdBuf, cmdBufSize, fence);
147         delete cmdBuf;
148         reset();
149     }
150 };
151