Fall back to software transform when hardware transform fails. Should help Mali devices.

This is done per shader so the speed hit should not be as bad as turning hw transform off entirely.
Displays a red error at the top of the screen so that we don't trigger this accidentally.
This commit is contained in:
Henrik Rydgard committed 2013-06-06 10:09:37 +02:00
1 parent 85d075db53
commit e7097ca95a
8 files changed
+91 -57

No files matched your search

+1
View File
@@ -594,6 +594,7 @@ void FramebufferManager::SetDisplayFramebuffer(u32 framebuf, u32 stride, int for
displayStride_ = stride;
displayFormat_ = format;
} else {
ramDisplayFramebufPtr_ = 0;
displayFramebufPtr_ = framebuf;
displayStride_ = stride;
displayFormat_ = format;
+45 -26
View File
@@ -29,12 +29,13 @@
#include "math/lin/matrix4x4.h"
#include "Core/Reporting.h"
#include "../GPUState.h"
#include "../ge_constants.h"
#include "ShaderManager.h"
#include "TransformPipeline.h"
#include "GPU/GPUState.h"
#include "GPU/ge_constants.h"
#include "GPU/GLES/ShaderManager.h"
#include "GPU/GLES/TransformPipeline.h"
#include "UI/OnScreenDisplay.h"
Shader::Shader(const char *code, uint32_t shaderType) {
Shader::Shader(const char *code, uint32_t shaderType, bool useHWTransform) : failed_(false), useHWTransform_(useHWTransform) {
source_ = code;
#ifdef SHADERLOG
OutputDebugString(code);
@@ -57,13 +58,20 @@ Shader::Shader(const char *code, uint32_t shaderType) {
#ifdef SHADERLOG
OutputDebugString(infoLog);
#endif
failed_ = true;
shader = 0;
} else {
DEBUG_LOG(G3D, "Compiled shader:\n%s\n", (const char *)code);
}
}
LinkedShader::LinkedShader(Shader *vs, Shader *fs)
: program(0), dirtyUniforms(0) {
Shader::~Shader() {
if (shader)
glDeleteShader(shader);
}
LinkedShader::LinkedShader(Shader *vs, Shader *fs, bool useHWTransform)
: program(0), dirtyUniforms(0), useHWTransform_(useHWTransform) {
program = glCreateProgram();
glAttachShader(program, vs->shader);
glAttachShader(program, fs->shader);
@@ -166,8 +174,7 @@ LinkedShader::~LinkedShader() {
}
// Utility
static void SetColorUniform3(int uniform, u32 color)
{
static void SetColorUniform3(int uniform, u32 color) {
const float col[3] = {
((color & 0xFF)) / 255.0f,
((color & 0xFF00) >> 8) / 255.0f,
@@ -176,8 +183,7 @@ static void SetColorUniform3(int uniform, u32 color)
glUniform3fv(uniform, 1, col);
}
static void SetColorUniform3Alpha(int uniform, u32 color, u8 alpha)
{
static void SetColorUniform3Alpha(int uniform, u32 color, u8 alpha) {
const float col[4] = {
((color & 0xFF)) / 255.0f,
((color & 0xFF00) >> 8) / 255.0f,
@@ -188,8 +194,7 @@ static void SetColorUniform3Alpha(int uniform, u32 color, u8 alpha)
}
// This passes colors unscaled (e.g. 0 - 255 not 0 - 1.)
static void SetColorUniform3Alpha255(int uniform, u32 color, u8 alpha)
{
static void SetColorUniform3Alpha255(int uniform, u32 color, u8 alpha) {
const float col[4] = {
(float)((color & 0xFF)),
(float)((color & 0xFF00) >> 8),
@@ -199,8 +204,7 @@ static void SetColorUniform3Alpha255(int uniform, u32 color, u8 alpha)
glUniform4fv(uniform, 1, col);
}
static void SetColorUniform3ExtraFloat(int uniform, u32 color, float extra)
{
static void SetColorUniform3ExtraFloat(int uniform, u32 color, float extra) {
const float col[4] = {
((color & 0xFF)) / 255.0f,
((color & 0xFF00) >> 8) / 255.0f,
@@ -430,22 +434,19 @@ void ShaderManager::Clear() {
DirtyShader();
}
void ShaderManager::ClearCache(bool deleteThem)
{
void ShaderManager::ClearCache(bool deleteThem) {
Clear();
}
void ShaderManager::DirtyShader()
{
void ShaderManager::DirtyShader() {
// Forget the last shader ID
lastFSID.clear();
lastVSID.clear();
lastShader = 0;
}
void ShaderManager::EndFrame() // disables vertex arrays
{
void ShaderManager::EndFrame() { // disables vertex arrays
if (lastShader)
lastShader->stop();
lastShader = 0;
@@ -460,9 +461,11 @@ LinkedShader *ShaderManager::ApplyShader(int prim) {
globalDirty = 0;
}
bool useHWTransform = CanUseHardwareTransform(prim);
VertexShaderID VSID;
FragmentShaderID FSID;
ComputeVertexShaderID(&VSID, prim);
ComputeVertexShaderID(&VSID, prim, useHWTransform);
ComputeFragmentShaderID(&FSID);
// Just update uniforms if this is the same shader as last time.
@@ -489,8 +492,23 @@ LinkedShader *ShaderManager::ApplyShader(int prim) {
Shader *vs;
if (vsIter == vsCache.end()) {
// Vertex shader not in cache. Let's compile it.
GenerateVertexShader(prim, codeBuffer_);
vs = new Shader(codeBuffer_, GL_VERTEX_SHADER);
GenerateVertexShader(prim, codeBuffer_, useHWTransform);
vs = new Shader(codeBuffer_, GL_VERTEX_SHADER, useHWTransform);
if (vs->Failed()) {
ERROR_LOG(HLE, "Shader compilation failed, falling back to software transform");
osm.Show("hardware transform error - falling back to software", 2.5f, 0xFF3030FF, -1, true);
delete vs;
// TODO: Look for existing shader with the appropriate ID, use that instead of generating a new one - however, need to make sure
// that that shader ID is not used when computing the linked shader ID below, because then IDs won't match
// next time and we'll do this over and over...
// Can still work with software transform.
GenerateVertexShader(prim, codeBuffer_, false);
vs = new Shader(codeBuffer_, GL_VERTEX_SHADER, false);
}
vsCache[VSID] = vs;
} else {
vs = vsIter->second;
@@ -501,18 +519,19 @@ LinkedShader *ShaderManager::ApplyShader(int prim) {
if (fsIter == fsCache.end()) {
// Fragment shader not in cache. Let's compile it.
GenerateFragmentShader(codeBuffer_);
fs = new Shader(codeBuffer_, GL_FRAGMENT_SHADER);
fs = new Shader(codeBuffer_, GL_FRAGMENT_SHADER, useHWTransform);
fsCache[FSID] = fs;
} else {
fs = fsIter->second;
}
// Okay, we have both shaders. Let's see if there's a linked one.
std::pair<Shader*, Shader*> linkedID(vs, fs);
LinkedShaderCache::iterator iter = linkedShaderCache.find(linkedID);
LinkedShader *ls;
if (iter == linkedShaderCache.end()) {
ls = new LinkedShader(vs, fs); // This does "use" automatically
ls = new LinkedShader(vs, fs, vs->UseHWTransform()); // This does "use" automatically
linkedShaderCache[linkedID] = ls;
} else {
ls = iter->second;
+11 -2
View File
@@ -28,13 +28,16 @@ class Shader;
class LinkedShader
{
public:
LinkedShader(Shader *vs, Shader *fs);
LinkedShader(Shader *vs, Shader *fs, bool useHWTransform);
~LinkedShader();
void use();
void stop();
void updateUniforms();
// Set to false if the VS failed, happens on Mali-400 a lot for complex shaders.
bool useHWTransform_;
uint32_t program;
u32 dirtyUniforms;
@@ -129,12 +132,18 @@ enum
class Shader {
public:
Shader(const char *code, uint32_t shaderType);
Shader(const char *code, uint32_t shaderType, bool useHWTransform);
~Shader();
uint32_t shader;
const std::string &source() const { return source_; }
bool Failed() const { return failed_; }
bool UseHWTransform() const { return useHWTransform_; }
private:
std::string source_;
bool failed_;
bool useHWTransform_;
};
+1 -1
View File
@@ -1112,7 +1112,7 @@ void TransformDrawEngine::Flush() {
LinkedShader *program = shaderManager_->ApplyShader(prim);
if (CanUseHardwareTransform(prim)) {
if (program->useHWTransform_) {
GLuint vbo = 0, ebo = 0;
int vertexCount = 0;
bool useElements = true;
+16 -16
View File
@@ -45,7 +45,7 @@ bool CanUseHardwareTransform(int prim) {
}
// prim so we can special case for RECTANGLES :(
void ComputeVertexShaderID(VertexShaderID *id, int prim) {
void ComputeVertexShaderID(VertexShaderID *id, int prim, bool useHWTransform) {
const u32 vertType = gstate.vertType;
int doTexture = gstate.isTextureMapEnabled() && !gstate.isModeClear();
bool doTextureProjection = gstate.getUVGenMode() == 1;
@@ -62,13 +62,12 @@ void ComputeVertexShaderID(VertexShaderID *id, int prim) {
id->d[0] |= ((int)enableFog) << 2;
id->d[0] |= doTexture << 3;
id->d[0] |= (hasColor & 1) << 4;
if (doTexture)
{
if (doTexture) {
id->d[0] |= (gstate_c.flipTexture & 1) << 5;
id->d[0] |= (doTextureProjection & 1) << 6;
}
if (CanUseHardwareTransform(prim)) {
if (useHWTransform) {
id->d[0] |= 1 << 8;
id->d[0] |= (hasNormal & 1) << 9;
id->d[0] |= (hasBones & 1) << 10;
@@ -122,7 +121,7 @@ enum DoLightComputation {
LIGHT_FULL,
};
void GenerateVertexShader(int prim, char *buffer) {
void GenerateVertexShader(int prim, char *buffer, bool useHWTransform) {
char *p = buffer;
// #define USE_FOR_LOOP
@@ -146,16 +145,15 @@ void GenerateVertexShader(int prim, char *buffer) {
int lmode = (gstate.lmode & 1) && gstate.isLightingEnabled();
int doTexture = gstate.isTextureMapEnabled() && !gstate.isModeClear();
bool hwXForm = CanUseHardwareTransform(prim);
bool hasColor = (vertType & GE_VTYPE_COL_MASK) != 0 || !hwXForm;
bool hasNormal = (vertType & GE_VTYPE_NRM_MASK) != 0 && hwXForm;
bool hasColor = (vertType & GE_VTYPE_COL_MASK) != 0 || !useHWTransform;
bool hasNormal = (vertType & GE_VTYPE_NRM_MASK) != 0 && useHWTransform;
bool enableFog = gstate.isFogEnabled() && !gstate.isModeThrough() && !gstate.isModeClear();
bool throughmode = (vertType & GE_VTYPE_THROUGH_MASK) != 0;
bool flipV = gstate_c.flipTexture;
bool doTextureProjection = gstate.getUVGenMode() == 1;
DoLightComputation doLight[4] = {LIGHT_OFF, LIGHT_OFF, LIGHT_OFF, LIGHT_OFF};
if (hwXForm) {
if (useHWTransform) {
int shadeLight0 = gstate.getUVGenMode() == 2 ? gstate.getUVLS0() : -1;
int shadeLight1 = gstate.getUVGenMode() == 2 ? gstate.getUVLS1() : -1;
for (int i = 0; i < 4; i++) {
@@ -170,24 +168,24 @@ void GenerateVertexShader(int prim, char *buffer) {
WRITE(p, "%s", boneWeightAttrDecl[gstate.getNumBoneWeights() - 1]);
}
if (hwXForm)
if (useHWTransform)
WRITE(p, "attribute vec3 a_position;\n");
else
WRITE(p, "attribute vec4 a_position;\n"); // need to pass the fog coord in w
if (doTexture) {
if (!hwXForm && doTextureProjection)
if (!useHWTransform && doTextureProjection)
WRITE(p, "attribute vec3 a_texcoord;\n");
else
WRITE(p, "attribute vec2 a_texcoord;\n");
}
if (hasColor) {
WRITE(p, "attribute lowp vec4 a_color0;\n");
if (lmode && !hwXForm) // only software transform supplies color1 as vertex data
if (lmode && !useHWTransform) // only software transform supplies color1 as vertex data
WRITE(p, "attribute lowp vec3 a_color1;\n");
}
if (hwXForm && hasNormal)
if (useHWTransform && hasNormal)
WRITE(p, "attribute mediump vec3 a_normal;\n");
if (gstate.isModeThrough()) {
@@ -197,14 +195,14 @@ void GenerateVertexShader(int prim, char *buffer) {
// Add all the uniforms we'll need to transform properly.
}
if (hwXForm || !hasColor)
if (useHWTransform || !hasColor)
WRITE(p, "uniform lowp vec4 u_matambientalpha;\n"); // matambient + matalpha
if (enableFog) {
WRITE(p, "uniform vec2 u_fogcoef;\n");
}
if (hwXForm) {
if (useHWTransform) {
// When transforming by hardware, we need a great deal more uniforms...
WRITE(p, "uniform mat4 u_world;\n");
WRITE(p, "uniform mat4 u_view;\n");
@@ -269,7 +267,7 @@ void GenerateVertexShader(int prim, char *buffer) {
WRITE(p, "void main() {\n");
if (!hwXForm) {
if (!useHWTransform) {
// Simple pass-through of vertex data to fragment shader
if (doTexture)
WRITE(p, " v_texcoord = a_texcoord;\n");
@@ -347,6 +345,8 @@ void GenerateVertexShader(int prim, char *buffer) {
WRITE(p, " + %s * u_bone[%i]", weightAttr, i);
}
#else
// Uncomment this to screw up bone shaders to check the vertex shader software fallback
// WRITE(p, "THIS SHOULD ERROR! #error");
if (numWeights == 1)
WRITE(p, " mat4 skinMatrix = a_w1 * u_bone0");
else
+2 -3
View File
@@ -50,6 +50,5 @@ struct VertexShaderID
bool CanUseHardwareTransform(int prim);
void ComputeVertexShaderID(VertexShaderID *id, int prim);
void GenerateVertexShader(int prim, char *buffer);
void ComputeVertexShaderID(VertexShaderID *id, int prim, bool useHWTransform);
void GenerateVertexShader(int prim, char *buffer, bool useHWTransform);
+7 -1
View File
@@ -36,8 +36,14 @@ restart:
}
}
void OnScreenMessages::Show(const std::string &message, float duration_s, uint32_t color, int icon) {
void OnScreenMessages::Show(const std::string &message, float duration_s, uint32_t color, int icon, bool checkUnique) {
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (checkUnique) {
for (auto iter = messages_.begin(); iter != messages_.end(); ++iter) {
if (iter->text == message)
return;
}
}
double now = time_now_d();
Message msg;
msg.text = message;
+8 -8
View File
@@ -1,16 +1,16 @@
#pragma once
#include <string>
#include <list>
#include "base/basictypes.h"
#include "Common/StdMutex.h"
#pragma once
#include <string>
#include <list>
#include "base/basictypes.h"
#include "Common/StdMutex.h"
class DrawBuffer;
class OnScreenMessages {
public:
void Show(const std::string &message, float duration_s = 1.0f, uint32_t color = 0xFFFFFF, int icon = -1);
void Show(const std::string &message, float duration_s = 1.0f, uint32_t color = 0xFFFFFF, int icon = -1, bool checkUnique = false);
void ShowOnOff(const std::string &message, bool b, float duration_s = 1.0f, uint32_t color = 0xFFFFFF, int icon = -1);
void Draw(DrawBuffer &draw);
bool IsEmpty() const { return messages_.empty(); }