mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
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:
1 parent
85d075db53
commit
e7097ca95a
8 files changed
+91
-57
No files matched your search
@@ -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
@@ -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;
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
Reference in new issue
Block a user