Permalink
Browse files

GLES: Avoid hanging while precompiling shaders.

We want to continue updating the screen so it doesn't seem frozen.
  • Loading branch information...
unknownbrackets committed Dec 3, 2017
1 parent 60c4ac5 commit 9f000ddf35cf9c82960f1db35d589e892c122ddd
Showing with 107 additions and 25 deletions.
  1. +6 −1 GPU/GLES/GPU_GLES.cpp
  2. +2 −0 GPU/GLES/GPU_GLES.h
  3. +74 −23 GPU/GLES/ShaderManagerGLES.cpp
  4. +25 −1 GPU/GLES/ShaderManagerGLES.h
@@ -171,7 +171,8 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
if (discID.size()) {
File::CreateFullPath(GetSysDirectory(DIRECTORY_APP_CACHE));
shaderCachePath_ = GetSysDirectory(DIRECTORY_APP_CACHE) + "/" + discID + ".glshadercache";
shaderManagerGL_->LoadAndPrecompile(shaderCachePath_);
// Actually precompiled by IsReady() since we're single-threaded.
shaderManagerGL_->Load(shaderCachePath_);
}
if (g_Config.bHardwareTessellation) {
@@ -350,6 +351,10 @@ void GPU_GLES::CheckGPUFeatures() {
gstate_c.featureFlags = features;
}
bool GPU_GLES::IsReady() {
return shaderManagerGL_->ContinuePrecompile();
}
// Let's avoid passing nulls into snprintf().
static const char *GetGLStringAlways(GLenum name) {
const GLubyte *value = glGetString(name);
@@ -38,6 +38,8 @@ class GPU_GLES : public GPUCommon {
// This gets called on startup and when we get back from settings.
void CheckGPUFeatures();
bool IsReady() override;
void PreExecuteOp(u32 op, u32 diff) override;
void ExecuteOp(u32 op, u32 diff) override;
@@ -955,7 +955,7 @@ struct CacheHeader {
int numLinkedPrograms;
};
void ShaderManagerGLES::LoadAndPrecompile(const std::string &filename) {
void ShaderManagerGLES::Load(const std::string &filename) {
File::IOFile f(filename, "rb");
u64 sz = f.GetSize();
if (!f.IsOpen()) {
@@ -969,7 +969,8 @@ void ShaderManagerGLES::LoadAndPrecompile(const std::string &filename) {
return;
}
time_update();
double start = time_now_d();
diskCachePending_.start = time_now_d();
diskCachePending_.Clear();
// Sanity check the file contents
if (header.numFragmentShaders > 1000 || header.numVertexShaders > 1000 || header.numLinkedPrograms > 1000) {
@@ -987,16 +988,59 @@ void ShaderManagerGLES::LoadAndPrecompile(const std::string &filename) {
return;
}
for (int i = 0; i < header.numVertexShaders; i++) {
VShaderID id;
if (!f.ReadArray(&id, 1)) {
diskCachePending_.vert.resize(header.numVertexShaders);
if (!f.ReadArray(&diskCachePending_.vert[0], header.numVertexShaders)) {
diskCachePending_.vert.clear();
return;
}
diskCachePending_.frag.resize(header.numFragmentShaders);
if (!f.ReadArray(&diskCachePending_.frag[0], header.numFragmentShaders)) {
diskCachePending_.vert.clear();
diskCachePending_.frag.clear();
return;
}
for (int i = 0; i < header.numLinkedPrograms; i++) {
VShaderID vsid;
FShaderID fsid;
if (!f.ReadArray(&vsid, 1)) {
return;
}
if (!f.ReadArray(&fsid, 1)) {
return;
}
diskCachePending_.link.push_back(std::make_pair(vsid, fsid));
}
// Actual compilation happens in ContinuePrecompile(), called by GPU_GLES's IsReady.
NOTICE_LOG(G3D, "Precompiling the shader cache from '%s'", filename.c_str());
diskCacheDirty_ = false;
}
bool ShaderManagerGLES::ContinuePrecompile(float sliceTime) {
auto &pending = diskCachePending_;
if (pending.Done()) {
return true;
}
double start = real_time_now();
// Let's try to keep it under sliceTime if possible.
double end = start + sliceTime;
for (size_t &i = pending.vertPos; i < pending.vert.size(); i++) {
if (real_time_now() >= end) {
// We'll finish later.
return false;
}
const VShaderID &id = pending.vert[i];
if (!vsCache_.Get(id)) {
if (id.Bit(VS_BIT_IS_THROUGH) && id.Bit(VS_BIT_USE_HW_TRANSFORM)) {
// Clearly corrupt, bailing.
ERROR_LOG_REPORT(G3D, "Corrupt shader cache: Both IS_THROUGH and USE_HW_TRANSFORM set.");
return;
pending.Clear();
return false;
}
Shader *vs = CompileVertexShader(id);
@@ -1005,33 +1049,37 @@ void ShaderManagerGLES::LoadAndPrecompile(const std::string &filename) {
// without trying to deduce the vertType from the VSID.
ERROR_LOG(G3D, "Failed to compile a vertex shader loading from cache. Skipping rest of shader cache.");
delete vs;
return;
pending.Clear();
return false;
}
vsCache_.Insert(id, vs);
} else {
WARN_LOG(G3D, "Duplicate vertex shader found in GL shader cache, ignoring");
}
}
for (int i = 0; i < header.numFragmentShaders; i++) {
FShaderID id;
if (!f.ReadArray(&id, 1)) {
return;
for (size_t &i = pending.fragPos; i < pending.frag.size(); i++) {
if (real_time_now() >= end) {
// We'll finish later.
return false;
}
const FShaderID &id = pending.frag[i];
if (!fsCache_.Get(id)) {
fsCache_.Insert(id, CompileFragmentShader(id));
} else {
WARN_LOG(G3D, "Duplicate fragment shader found in GL shader cache, ignoring");
}
}
for (int i = 0; i < header.numLinkedPrograms; i++) {
VShaderID vsid;
FShaderID fsid;
if (!f.ReadArray(&vsid, 1)) {
return;
}
if (!f.ReadArray(&fsid, 1)) {
return;
for (size_t &i = pending.linkPos; i < pending.link.size(); i++) {
if (real_time_now() >= end) {
// We'll finish later.
return false;
}
const VShaderID &vsid = pending.link[i].first;
const FShaderID &fsid = pending.link[i].second;
Shader *vs = vsCache_.Get(vsid);
Shader *fs = fsCache_.Get(fsid);
if (vs && fs) {
@@ -1040,12 +1088,15 @@ void ShaderManagerGLES::LoadAndPrecompile(const std::string &filename) {
linkedShaderCache_.push_back(entry);
}
}
// Okay, finally done. Time to report status.
time_update();
double end = time_now_d();
double finish = time_now_d();
NOTICE_LOG(G3D, "Compiled and linked %d programs (%d vertex, %d fragment) in %0.1f milliseconds", header.numLinkedPrograms, header.numVertexShaders, header.numFragmentShaders, 1000 * (end - start));
NOTICE_LOG(G3D, "Loaded the shader cache from '%s'", filename.c_str());
diskCacheDirty_ = false;
NOTICE_LOG(G3D, "Compiled and linked %d programs (%d vertex, %d fragment) in %0.1f milliseconds", (int)pending.link.size(), (int)pending.vert.size(), (int)pending.frag.size(), 1000 * (finish - pending.start));
pending.Clear();
return true;
}
void ShaderManagerGLES::Save(const std::string &filename) {
@@ -167,7 +167,8 @@ class ShaderManagerGLES : public ShaderManagerCommon {
std::vector<std::string> DebugGetShaderIDs(DebugShaderType type);
std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType);
void LoadAndPrecompile(const std::string &filename);
void Load(const std::string &filename);
bool ContinuePrecompile(float sliceTime = 1.0f / 60.0f);
void Save(const std::string &filename);
private:
@@ -203,4 +204,27 @@ class ShaderManagerGLES : public ShaderManagerCommon {
VSCache vsCache_;
bool diskCacheDirty_;
struct {
std::vector<VShaderID> vert;
std::vector<FShaderID> frag;
std::vector<std::pair<VShaderID, FShaderID>> link;
size_t vertPos = 0;
size_t fragPos = 0;
size_t linkPos = 0;
double start;
void Clear() {
vert.clear();
frag.clear();
link.clear();
vertPos = 0;
fragPos = 0;
linkPos = 0;
}
bool Done() {
return vertPos >= vert.size() && fragPos >= frag.size() && linkPos >= link.size();
}
} diskCachePending_;
};

0 comments on commit 9f000dd

Please sign in to comment.