/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#undef NDEBUG
#include <cassert>
#include <condition_variable>
#include <cstdint>
#include <map>
#include <mutex>
#include <string>
#include <thread>
#include "GLESv1.h"
#include "GLESv3.h"
#include "RenderControl.h"
#include "Resource.h"
struct EglContext;
struct Context;
typedef void (*PFNSUBMITCMD)(Context*, char*, size_t, int);
struct Context {
static std::map<uint32_t, Context*> map;
Context(uint32_t handle_, const char* name_, uint32_t nlen_, PFNSUBMITCMD pfnProcessCmd_,
EGLDisplay dpy_)
: render_control(this, dpy_), worker(), name(std::string(name_, nlen_)), handle(handle_),
pfnProcessCmd(pfnProcessCmd_) {
map.emplace(handle, this);
reset();
}
~Context() {
{
std::lock_guard<std::mutex> lk(m);
killWorker = true;
}
cv.notify_one();
if (worker.joinable())
worker.join();
map.erase(handle);
}
Context* bind(EglContext* ctx_) {
for (auto const& it : Context::map) {
Context* ctx = it.second;
if (ctx == this)
continue;
if (ctx->ctx == ctx_)
return ctx;
}
ctx = ctx_;
return nullptr;
}
void unbind() {
ctx = nullptr;
}
void setPidTid(int pid_, int tid_) {
if (pid != pid_ && tid != tid_) {
assert(!worker.joinable() && "Changing pid/tid is not allowed");
worker = std::thread(&Context::worker_func, this);
}
pid = pid_;
tid = tid_;
}
void submitCommand(void* buf, size_t bufSize) {
char* cmdBufCopy = new char[bufSize];
memcpy(cmdBufCopy, buf, bufSize);
{
std::lock_guard<std::mutex> lk(m);
cmdBufSize = bufSize;
cmdBuf = cmdBufCopy;
}
cv.notify_one();
}
void setFence(int fence_) {
{
std::lock_guard<std::mutex> lk(m);
fence = fence_;
if (!worker.joinable())
processCmd();
}
cv.notify_one();
}
std::map<uint32_t, Resource*> resource_map;
ChecksumCalculator checksum_calc;
RenderControl render_control;
Resource* cmd_resp = nullptr;
EglContext* ctx = nullptr;
std::thread worker;
std::string name;
uint32_t handle;
GLESv1 gles1;
GLESv3 gles3;
int pid = 0;
int tid = 0;
private:
std::condition_variable cv;
PFNSUBMITCMD pfnProcessCmd;
bool killWorker = false;
size_t cmdBufSize;
char* cmdBuf;
std::mutex m;
int fence;
void reset() {
cmdBuf = nullptr;
cmdBufSize = 0U;
fence = 0;
}
void worker_func() {
while (!killWorker) {
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [this] { return killWorker || (cmdBuf && fence); });
if (!killWorker)
processCmd();
lk.unlock();
}
}
void processCmd() {
pfnProcessCmd(this, cmdBuf, cmdBufSize, fence);
delete cmdBuf;
reset();
}
};