mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 21:39:52 +00:00
Merge pull request #5767 from hrydgard/framebuffer-clut
Color-convert framebuffers that are bound as CLUT textures
This commit is contained in:
commit
1445d5ae31
@ -1243,6 +1243,8 @@ add_library(GPU OBJECT
|
||||
GPU/Debugger/Breakpoints.h
|
||||
GPU/Debugger/Stepping.cpp
|
||||
GPU/Debugger/Stepping.h
|
||||
GPU/GLES/DepalettizeShader.cpp
|
||||
GPU/GLES/DepalettizeShader.h
|
||||
GPU/GLES/GLES_GPU.cpp
|
||||
GPU/GLES/GLES_GPU.h
|
||||
GPU/GLES/FragmentShaderGenerator.cpp
|
||||
|
@ -739,7 +739,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(ME, "Unsupported video pixel format %d", videoPixelMode);
|
||||
ERROR_LOG_REPORT(ME, "Unsupported video pixel format %d", videoPixelMode);
|
||||
break;
|
||||
}
|
||||
return videoImageSize;
|
||||
|
@ -0,0 +1,418 @@
|
||||
// Copyright (c) 2014- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "DepalettizeShader.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/GLES/TextureCache.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define SHADERLOG
|
||||
#endif
|
||||
|
||||
static const char *depalVShader100 =
|
||||
#ifdef USING_GLES
|
||||
"#version 100\n"
|
||||
"precision highp float;\n"
|
||||
#endif
|
||||
"attribute vec4 a_position;\n"
|
||||
"attribute vec2 a_texcoord0;\n"
|
||||
"varying vec2 v_texcoord0;\n"
|
||||
"void main() {\n"
|
||||
" v_texcoord0 = a_texcoord0;\n"
|
||||
" gl_Position = a_position;\n"
|
||||
"}\n";
|
||||
|
||||
static const char *depalVShader300 =
|
||||
#ifdef USING_GLES
|
||||
"#version 300 es\n"
|
||||
"precision highp float;\n"
|
||||
#else
|
||||
"#version 330\n"
|
||||
#endif
|
||||
"in vec4 a_position;\n"
|
||||
"in vec2 a_texcoord0;\n"
|
||||
"out vec2 v_texcoord0;\n"
|
||||
"void main() {\n"
|
||||
" v_texcoord0 = a_texcoord0;\n"
|
||||
" gl_Position = a_position;\n"
|
||||
"}\n";
|
||||
|
||||
|
||||
static bool CheckShaderCompileSuccess(GLuint shader, const char *code) {
|
||||
GLint success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
#define MAX_INFO_LOG_SIZE 2048
|
||||
GLchar infoLog[MAX_INFO_LOG_SIZE];
|
||||
GLsizei len;
|
||||
glGetShaderInfoLog(shader, MAX_INFO_LOG_SIZE, &len, infoLog);
|
||||
infoLog[len] = '\0';
|
||||
#ifdef ANDROID
|
||||
ELOG("Error in shader compilation! %s\n", infoLog);
|
||||
ELOG("Shader source:\n%s\n", (const char *)code);
|
||||
#endif
|
||||
ERROR_LOG(G3D, "Error in shader compilation!\n");
|
||||
ERROR_LOG(G3D, "Info log: %s\n", infoLog);
|
||||
ERROR_LOG(G3D, "Shader source:\n%s\n", (const char *)code);
|
||||
#ifdef SHADERLOG
|
||||
OutputDebugStringUTF8(infoLog);
|
||||
#endif
|
||||
shader = 0;
|
||||
return false;
|
||||
} else {
|
||||
DEBUG_LOG(G3D, "Compiled shader:\n%s\n", (const char *)code);
|
||||
#ifdef SHADERLOG
|
||||
OutputDebugStringUTF8(code);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
DepalShaderCache::DepalShaderCache() {
|
||||
// Pre-build the vertex program
|
||||
bool useGL3 = gl_extensions.GLES3 || gl_extensions.VersionGEThan(3, 3);
|
||||
|
||||
vertexShader_ = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vertexShader_, 1, useGL3 ? &depalVShader300 : &depalVShader100, 0);
|
||||
glCompileShader(vertexShader_);
|
||||
|
||||
if (CheckShaderCompileSuccess(vertexShader_, depalVShader100)) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
DepalShaderCache::~DepalShaderCache() {
|
||||
Clear();
|
||||
glDeleteShader(vertexShader_);
|
||||
}
|
||||
|
||||
#define WRITE p+=sprintf
|
||||
|
||||
void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat) {
|
||||
char *p = buffer;
|
||||
#ifdef USING_GLES
|
||||
WRITE(p, "#version 300 es\n");
|
||||
WRITE(p, "precision mediump float;\n");
|
||||
#else
|
||||
WRITE(p, "#version 330\n");
|
||||
#endif
|
||||
WRITE(p, "in vec2 v_texcoord0;\n");
|
||||
WRITE(p, "out vec4 fragColor0;\n");
|
||||
WRITE(p, "uniform sampler2D tex;\n");
|
||||
WRITE(p, "uniform sampler2D pal;\n");
|
||||
|
||||
WRITE(p, "void main() {\n");
|
||||
WRITE(p, " vec4 index = texture2D(tex, v_texcoord0);\n");
|
||||
|
||||
int mask = gstate.getClutIndexMask();
|
||||
int shift = gstate.getClutIndexShift();
|
||||
int offset = gstate.getClutIndexStartPos();
|
||||
const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat();
|
||||
// Unfortunately sampling turned our texture into floating point. To avoid this, might be able
|
||||
// to declare them as isampler2D objects, but these require integer textures, which needs more work.
|
||||
// Anyhow, we simply work around this by converting back to integer. Hopefully there will be no loss of precision.
|
||||
// Use the mask to skip reading some components.
|
||||
int shiftedMask = mask << shift;
|
||||
switch (pixelFormat) {
|
||||
case GE_FORMAT_8888:
|
||||
if (shiftedMask & 0xFF) WRITE(p, " int r = int(index.r * 255.99);\n"); else WRITE(p, " int r = 0;\n");
|
||||
if (shiftedMask & 0xFF00) WRITE(p, " int g = int(index.g * 255.99);\n"); else WRITE(p, " int g = 0;\n");
|
||||
if (shiftedMask & 0xFF0000) WRITE(p, " int b = int(index.b * 255.99);\n"); else WRITE(p, " int b = 0;\n");
|
||||
if (shiftedMask & 0xFF000000) WRITE(p, " int a = int(index.a * 255.99);\n"); else WRITE(p, " int a = 0;\n");
|
||||
WRITE(p, " int color = (a << 24) | (b << 16) | (g << 8) | (r);\n");
|
||||
break;
|
||||
case GE_FORMAT_4444:
|
||||
if (shiftedMask & 0xF) WRITE(p, " int r = int(index.r * 15.99);\n"); else WRITE(p, " int r = 0;\n");
|
||||
if (shiftedMask & 0xF0) WRITE(p, " int g = int(index.g * 15.99);\n"); else WRITE(p, " int g = 0;\n");
|
||||
if (shiftedMask & 0xF00) WRITE(p, " int b = int(index.b * 15.99);\n"); else WRITE(p, " int b = 0;\n");
|
||||
if (shiftedMask & 0xF000) WRITE(p, " int a = int(index.a * 15.99);\n"); else WRITE(p, " int a = 0;\n");
|
||||
WRITE(p, " int color = (a << 12) | (b << 8) | (g << 4) | (r);\n");
|
||||
break;
|
||||
case GE_FORMAT_565:
|
||||
if (shiftedMask & 0x1F) WRITE(p, " int r = int(index.r * 31.99);\n"); else WRITE(p, " int r = 0;\n");
|
||||
if (shiftedMask & 0x7E0) WRITE(p, " int g = int(index.g * 63.99);\n"); else WRITE(p, " int g = 0;\n");
|
||||
if (shiftedMask & 0xF800) WRITE(p, " int b = int(index.b * 31.99);\n"); else WRITE(p, " int b = 0;\n");
|
||||
WRITE(p, " int color = (b << 11) | (g << 5) | (r);");
|
||||
break;
|
||||
case GE_FORMAT_5551:
|
||||
if (shiftedMask & 0x1F) WRITE(p, " int r = int(index.r * 31.99);\n"); else WRITE(p, " int r = 0;\n");
|
||||
if (shiftedMask & 0x3E0) WRITE(p, " int g = int(index.g * 31.99);\n"); else WRITE(p, " int g = 0;\n");
|
||||
if (shiftedMask & 0x7C00) WRITE(p, " int b = int(index.b * 31.99);\n"); else WRITE(p, " int b = 0;\n");
|
||||
if (shiftedMask & 0x8000) WRITE(p, " int a = int(index.a);\n"); else WRITE(p, " int a = 0;\n");
|
||||
WRITE(p, " int color = (a << 15) | (b << 10) | (g << 5) | (r);");
|
||||
break;
|
||||
}
|
||||
float texturePixels = 256;
|
||||
if (clutFormat != GE_CMODE_32BIT_ABGR8888)
|
||||
texturePixels = 512;
|
||||
|
||||
WRITE(p, " color = ((color >> %i) & 0x%02x) | %i;\n", shift, mask, offset); // '|' matches what we have in gstate.h
|
||||
WRITE(p, " fragColor0 = texture2D(pal, vec2((floor(float(color)) + 0.5) * (1.0 / %f), 0.0));\n", texturePixels);
|
||||
WRITE(p, "}\n");
|
||||
}
|
||||
|
||||
void GenerateDepalShader100(char *buffer, GEBufferFormat pixelFormat) {
|
||||
char *p = buffer;
|
||||
|
||||
char lookupMethod[128] = "index.r";
|
||||
char offset[128] = "";
|
||||
|
||||
const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat();
|
||||
const u32 clutBase = gstate.getClutIndexStartPos();
|
||||
|
||||
int shift = gstate.getClutIndexShift();
|
||||
int mask = gstate.getClutIndexMask();
|
||||
|
||||
float multiplier = 1.0f;
|
||||
// pixelformat is the format of the texture we are sampling.
|
||||
bool formatOK = true;
|
||||
switch (pixelFormat) {
|
||||
case GE_FORMAT_8888:
|
||||
if ((mask & 0xF) == 0xF) {
|
||||
switch (shift) { // bgra?
|
||||
case 0: strcpy(lookupMethod, "index.r"); break;
|
||||
case 4: strcpy(lookupMethod, "index.r"); multiplier = (1.0f / 16.0f); break;
|
||||
case 8: strcpy(lookupMethod, "index.g"); break;
|
||||
case 12: strcpy(lookupMethod, "index.g"); multiplier = (1.0f / 16.0f); break;
|
||||
case 16: strcpy(lookupMethod, "index.b"); break;
|
||||
case 20: strcpy(lookupMethod, "index.b"); multiplier = (1.0f / 16.0f); break;
|
||||
case 24: strcpy(lookupMethod, "index.a"); break;
|
||||
case 28: strcpy(lookupMethod, "index.a"); multiplier = (1.0f / 16.0f); break;
|
||||
default:
|
||||
formatOK = false;
|
||||
}
|
||||
} else {
|
||||
formatOK = false;
|
||||
}
|
||||
break;
|
||||
case GE_FORMAT_4444:
|
||||
if ((mask & 0xF) == 0xF) {
|
||||
switch (shift) { // bgra?
|
||||
case 0: strcpy(lookupMethod, "index.r"); break;
|
||||
case 4: strcpy(lookupMethod, "index.g"); break;
|
||||
case 8: strcpy(lookupMethod, "index.b"); break;
|
||||
case 12: strcpy(lookupMethod, "index.a"); break;
|
||||
default:
|
||||
formatOK = false;
|
||||
}
|
||||
multiplier = 1.0f / 16.0f;
|
||||
} else {
|
||||
formatOK = false;
|
||||
}
|
||||
break;
|
||||
case GE_FORMAT_565:
|
||||
if ((mask & 0x3f) == 0x3F) {
|
||||
switch (shift) { // bgra?
|
||||
case 0: strcpy(lookupMethod, "index.r"); multiplier = 1.0f / 32.0f; break;
|
||||
case 5: strcpy(lookupMethod, "index.g"); multiplier = 1.0f / 64.0f; break;
|
||||
case 11: strcpy(lookupMethod, "index.b"); multiplier = 1.0f / 32.0f; break;
|
||||
default:
|
||||
formatOK = false;
|
||||
}
|
||||
} else {
|
||||
formatOK = false;
|
||||
}
|
||||
break;
|
||||
case GE_FORMAT_5551:
|
||||
if ((mask & 0x1F) == 0x1F) {
|
||||
switch (shift) { // bgra?
|
||||
case 0: strcpy(lookupMethod, "index.r"); multiplier = 1.0f / 32.0f; break;
|
||||
case 5: strcpy(lookupMethod, "index.g"); multiplier = 1.0f / 32.0f; break;
|
||||
case 10: strcpy(lookupMethod, "index.b"); multiplier = 1.0f / 32.0f; break;
|
||||
case 15: strcpy(lookupMethod, "index.a"); multiplier = 1.0f / 256.0f; break;
|
||||
default:
|
||||
formatOK = false;
|
||||
}
|
||||
} else {
|
||||
formatOK = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
float texturePixels = 256.f;
|
||||
if (clutFormat != GE_CMODE_32BIT_ABGR8888) {
|
||||
texturePixels = 512.f;
|
||||
multiplier *= 0.5f;
|
||||
}
|
||||
|
||||
if (!formatOK) {
|
||||
ERROR_LOG_REPORT_ONCE(depal, G3D, "%i depal unsupported: shift=%i mask=%02x offset=%i", pixelFormat, shift, mask, offset);
|
||||
}
|
||||
|
||||
// Offset by half a texel (plus clutBase) to turn NEAREST filtering into FLOOR.
|
||||
sprintf(offset, " + %f", (float)clutBase / texturePixels - 0.5f / texturePixels);
|
||||
|
||||
#ifdef USING_GLES
|
||||
WRITE(p, "#version 100\n");
|
||||
WRITE(p, "precision mediump float;\n");
|
||||
#else
|
||||
WRITE(p, "#version 110\n");
|
||||
#endif
|
||||
WRITE(p, "varying vec2 v_texcoord0;\n");
|
||||
WRITE(p, "uniform sampler2D tex;\n");
|
||||
WRITE(p, "uniform sampler2D pal;\n");
|
||||
WRITE(p, "void main() {\n");
|
||||
WRITE(p, " vec4 index = texture2D(tex, v_texcoord0);\n");
|
||||
WRITE(p, " gl_FragColor = texture2D(pal, vec2((%s * %f)%s, 0.0));\n", lookupMethod, multiplier, offset);
|
||||
WRITE(p, "}\n");
|
||||
}
|
||||
|
||||
#undef WRITE
|
||||
|
||||
|
||||
u32 DepalShaderCache::GenerateShaderID(GEBufferFormat pixelFormat) {
|
||||
return (gstate.clutformat & 0xFFFFFF) | (pixelFormat << 24);
|
||||
}
|
||||
|
||||
GLuint DepalShaderCache::GetClutTexture(const u32 clutID, u32 *rawClut) {
|
||||
GEPaletteFormat palFormat = gstate.getClutPaletteFormat();
|
||||
const u32 realClutID = clutID ^ palFormat;
|
||||
|
||||
auto oldtex = texCache_.find(realClutID);
|
||||
if (oldtex != texCache_.end()) {
|
||||
return oldtex->second->texture;
|
||||
}
|
||||
|
||||
GLuint dstFmt = getClutDestFormat(palFormat);
|
||||
int texturePixels = palFormat == GE_CMODE_32BIT_ABGR8888 ? 256 : 512;
|
||||
|
||||
bool useBGRA = UseBGRA8888() && dstFmt == GL_UNSIGNED_BYTE;
|
||||
|
||||
DepalTexture *tex = new DepalTexture();
|
||||
glGenTextures(1, &tex->texture);
|
||||
glBindTexture(GL_TEXTURE_2D, tex->texture);
|
||||
GLuint components = dstFmt == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA;
|
||||
|
||||
GLuint components2 = components;
|
||||
#if defined(MAY_HAVE_GLES3)
|
||||
if (useBGRA) {
|
||||
components2 = GL_BGRA_EXT;
|
||||
}
|
||||
#endif
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, components, texturePixels, 1, 0, components2, dstFmt, (void *)rawClut);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
texCache_[realClutID] = tex;
|
||||
return tex->texture;
|
||||
}
|
||||
|
||||
void DepalShaderCache::Clear() {
|
||||
for (auto shader = cache_.begin(); shader != cache_.end(); ++shader) {
|
||||
glDeleteShader(shader->second->fragShader);
|
||||
glDeleteProgram(shader->second->program);
|
||||
delete shader->second;
|
||||
}
|
||||
cache_.clear();
|
||||
for (auto tex = texCache_.begin(); tex != texCache_.end(); ++tex) {
|
||||
glDeleteTextures(1, &tex->second->texture);
|
||||
delete tex->second;
|
||||
}
|
||||
texCache_.clear();
|
||||
}
|
||||
|
||||
void DepalShaderCache::Decimate() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
GLuint DepalShaderCache::GetDepalettizeShader(GEBufferFormat pixelFormat) {
|
||||
u32 id = GenerateShaderID(pixelFormat);
|
||||
|
||||
bool useGL3 = gl_extensions.GLES3 || gl_extensions.VersionGEThan(3, 3);
|
||||
|
||||
auto shader = cache_.find(id);
|
||||
if (shader != cache_.end()) {
|
||||
return shader->second->program;
|
||||
}
|
||||
|
||||
char *buffer = new char[2048];
|
||||
|
||||
if (useGL3) {
|
||||
GenerateDepalShader300(buffer, pixelFormat);
|
||||
} else {
|
||||
GenerateDepalShader100(buffer, pixelFormat);
|
||||
}
|
||||
|
||||
GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
|
||||
const char *buf = buffer;
|
||||
glShaderSource(fragShader, 1, &buf, 0);
|
||||
glCompileShader(fragShader);
|
||||
|
||||
CheckShaderCompileSuccess(fragShader, buffer);
|
||||
|
||||
GLuint program = glCreateProgram();
|
||||
glAttachShader(program, vertexShader_);
|
||||
glAttachShader(program, fragShader);
|
||||
|
||||
glBindAttribLocation(program, 0, "a_position");
|
||||
glBindAttribLocation(program, 1, "a_texcoord0");
|
||||
|
||||
if (useGL3) {
|
||||
// This call is not really necessary, I think.
|
||||
#ifndef MOBILE_DEVICE
|
||||
glBindFragDataLocation(program, 0, "fragColor0");
|
||||
#endif
|
||||
}
|
||||
|
||||
glLinkProgram(program);
|
||||
glUseProgram(program);
|
||||
|
||||
GLint u_tex = glGetUniformLocation(program, "tex");
|
||||
GLint u_pal = glGetUniformLocation(program, "pal");
|
||||
|
||||
glUniform1i(u_tex, 0);
|
||||
glUniform1i(u_pal, 1);
|
||||
|
||||
GLint linkStatus = GL_FALSE;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
|
||||
if (linkStatus != GL_TRUE) {
|
||||
GLint bufLength = 0;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
|
||||
if (bufLength) {
|
||||
char* errorbuf = new char[bufLength];
|
||||
glGetProgramInfoLog(program, bufLength, NULL, errorbuf);
|
||||
#ifdef SHADERLOG
|
||||
OutputDebugStringUTF8(buffer);
|
||||
OutputDebugStringUTF8(errorbuf);
|
||||
#endif
|
||||
ERROR_LOG(G3D, "Could not link program:\n %s \n\n %s", errorbuf, buf);
|
||||
delete[] errorbuf; // we're dead!
|
||||
}
|
||||
|
||||
delete[] buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DepalShader *depal = new DepalShader();
|
||||
depal->program = program;
|
||||
depal->fragShader = fragShader;
|
||||
|
||||
cache_[id] = depal;
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
return depal->program;
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2014- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "gfx_es2/gl_state.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
|
||||
class DepalShader {
|
||||
public:
|
||||
GLuint program;
|
||||
GLuint fragShader;
|
||||
};
|
||||
|
||||
class DepalTexture {
|
||||
public:
|
||||
GLuint texture;
|
||||
};
|
||||
|
||||
// Caches both shaders and palette textures.
|
||||
class DepalShaderCache {
|
||||
public:
|
||||
DepalShaderCache();
|
||||
~DepalShaderCache();
|
||||
|
||||
// This also uploads the palette and binds the correct texture.
|
||||
GLuint GetDepalettizeShader(GEBufferFormat pixelFormat);
|
||||
GLuint GetClutTexture(const u32 clutHash, u32 *rawClut);
|
||||
void Clear();
|
||||
void Decimate();
|
||||
|
||||
private:
|
||||
u32 GenerateShaderID(GEBufferFormat pixelFormat);
|
||||
|
||||
GLuint vertexShader_;
|
||||
std::map<u32, DepalShader *> cache_;
|
||||
std::map<u32, DepalTexture *> texCache_;
|
||||
};
|
||||
|
@ -610,6 +610,7 @@ void FramebufferManager::DrawActiveTexture(GLuint texture, float x, float y, flo
|
||||
shaderManager_->DirtyLastShader(); // dirty lastShader_
|
||||
}
|
||||
|
||||
|
||||
VirtualFramebuffer *FramebufferManager::GetVFBAt(u32 addr) {
|
||||
VirtualFramebuffer *match = NULL;
|
||||
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
||||
@ -684,6 +685,10 @@ void FramebufferManager::DestroyFramebuf(VirtualFramebuffer *v) {
|
||||
delete v;
|
||||
}
|
||||
|
||||
void FramebufferManager::RebindFramebuffer() {
|
||||
fbo_bind_as_render_target(currentRenderVfb_->fbo);
|
||||
}
|
||||
|
||||
void FramebufferManager::DoSetRenderFrameBuffer() {
|
||||
/*
|
||||
if (useBufferedRendering_ && currentRenderVfb_) {
|
||||
@ -1009,7 +1014,7 @@ void FramebufferManager::BindFramebufferDepth(VirtualFramebuffer *sourceframebuf
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManager::BindFramebufferColor(VirtualFramebuffer *framebuffer) {
|
||||
void FramebufferManager::BindFramebufferColor(VirtualFramebuffer *framebuffer, bool skipCopy) {
|
||||
if (framebuffer == NULL) {
|
||||
framebuffer = currentRenderVfb_;
|
||||
}
|
||||
@ -1022,7 +1027,7 @@ void FramebufferManager::BindFramebufferColor(VirtualFramebuffer *framebuffer) {
|
||||
|
||||
// currentRenderVfb_ will always be set when this is called, except from the GE debugger.
|
||||
// Let's just not bother with the copy in that case.
|
||||
if (currentRenderVfb_ && MaskedEqual(framebuffer->fb_address, gstate.getFrameBufRawAddress())) {
|
||||
if (!skipCopy && currentRenderVfb_ && MaskedEqual(framebuffer->fb_address, gstate.getFrameBufRawAddress())) {
|
||||
#ifndef USING_GLES2
|
||||
if (gl_extensions.FBO_ARB) {
|
||||
bool useNV = false;
|
||||
@ -1400,14 +1405,6 @@ void FramebufferManager::BlitFramebuffer_(VirtualFramebuffer *dst, int dstX, int
|
||||
fbo_unbind();
|
||||
}
|
||||
|
||||
static inline bool UseBGRA8888() {
|
||||
// TODO: Other platforms? May depend on vendor which is faster?
|
||||
#ifdef _WIN32
|
||||
return gl_extensions.EXT_bgra;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: SSE/NEON
|
||||
// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :)
|
||||
void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 stride, u32 height, GEBufferFormat format) {
|
||||
|
@ -86,6 +86,7 @@ struct VirtualFramebuffer {
|
||||
GEBufferFormat format; // virtual, right now they are all RGBA8888
|
||||
FBOColorDepth colorDepth;
|
||||
FBO *fbo;
|
||||
FBO *depalFBO;
|
||||
|
||||
bool dirtyAfterDisplay;
|
||||
bool reallyDirtyAfterDisplay; // takes frame skipping into account
|
||||
@ -161,7 +162,7 @@ public:
|
||||
void BindFramebufferDepth(VirtualFramebuffer *sourceframebuffer, VirtualFramebuffer *targetframebuffer);
|
||||
|
||||
// For use when texturing from a framebuffer. May create a duplicate if target.
|
||||
void BindFramebufferColor(VirtualFramebuffer *framebuffer);
|
||||
void BindFramebufferColor(VirtualFramebuffer *framebuffer, bool skipCopy = false);
|
||||
|
||||
// Returns true if it's sure this is a direct FBO->FBO transfer and it has already handle it.
|
||||
// In that case we hardly need to actually copy the bytes in VRAM, they will be wrong anyway (unless
|
||||
@ -219,6 +220,8 @@ public:
|
||||
bool GetCurrentDepthbuffer(GPUDebugBuffer &buffer);
|
||||
bool GetCurrentStencilbuffer(GPUDebugBuffer &buffer);
|
||||
|
||||
void RebindFramebuffer();
|
||||
|
||||
private:
|
||||
void CompileDraw2DProgram();
|
||||
void DestroyDraw2DProgram();
|
||||
|
@ -410,6 +410,7 @@ GLES_GPU::GLES_GPU()
|
||||
framebufferManager_.SetTextureCache(&textureCache_);
|
||||
framebufferManager_.SetShaderManager(shaderManager_);
|
||||
textureCache_.SetFramebufferManager(&framebufferManager_);
|
||||
textureCache_.SetDepalShaderCache(&depalShaderCache_);
|
||||
|
||||
// Sanity check gstate
|
||||
if ((int *)&gstate.transferstart - (int *)&gstate != 0xEA) {
|
||||
@ -459,6 +460,7 @@ GLES_GPU::GLES_GPU()
|
||||
GLES_GPU::~GLES_GPU() {
|
||||
framebufferManager_.DestroyAllFBOs();
|
||||
shaderManager_->ClearCache(true);
|
||||
depalShaderCache_.Clear();
|
||||
delete shaderManager_;
|
||||
}
|
||||
|
||||
@ -495,6 +497,7 @@ void GLES_GPU::DeviceLost() {
|
||||
// TransformDraw has registered as a GfxResourceHolder.
|
||||
shaderManager_->ClearCache(false);
|
||||
textureCache_.Clear(false);
|
||||
depalShaderCache_.Clear();
|
||||
framebufferManager_.DeviceLost();
|
||||
}
|
||||
|
||||
@ -2069,6 +2072,7 @@ void GLES_GPU::DoState(PointerWrap &p) {
|
||||
// In Freeze-Frame mode, we don't want to do any of this.
|
||||
if (p.mode == p.MODE_READ && !PSP_CoreParameter().frozen) {
|
||||
textureCache_.Clear(true);
|
||||
depalShaderCache_.Clear();
|
||||
transformDraw_.ClearTrackedVertexArrays();
|
||||
|
||||
gstate_c.textureChanged = TEXCHANGE_UPDATED;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "GPU/GLES/Framebuffer.h"
|
||||
#include "GPU/GLES/TransformPipeline.h"
|
||||
#include "GPU/GLES/TextureCache.h"
|
||||
#include "GPU/GLES/DepalettizeShader.h"
|
||||
|
||||
class ShaderManager;
|
||||
class LinkedShader;
|
||||
@ -162,6 +163,7 @@ private:
|
||||
|
||||
FramebufferManager framebufferManager_;
|
||||
TextureCache textureCache_;
|
||||
DepalShaderCache depalShaderCache_;
|
||||
TransformDrawEngine transformDraw_;
|
||||
ShaderManager *shaderManager_;
|
||||
|
||||
|
@ -684,10 +684,11 @@ Shader *ShaderManager::ApplyVertexShader(int prim, u32 vertType) {
|
||||
LinkedShader *ShaderManager::ApplyFragmentShader(Shader *vs, int prim, u32 vertType) {
|
||||
FragmentShaderID FSID;
|
||||
ComputeFragmentShaderID(&FSID);
|
||||
if (lastVShaderSame_ && FSID == lastFSID_) {
|
||||
if (lastVShaderSame_ && FSID == lastFSID_ && !gstate_c.shaderChanged) {
|
||||
lastShader_->UpdateUniforms(vertType);
|
||||
return lastShader_;
|
||||
}
|
||||
gstate_c.shaderChanged = false;
|
||||
|
||||
lastFSID_ = FSID;
|
||||
|
||||
|
@ -444,9 +444,6 @@ void TransformDrawEngine::ApplyDrawState(int prim) {
|
||||
amask = false;
|
||||
}
|
||||
}
|
||||
if (g_Config.bAlphaMaskHack) {
|
||||
amask = true; // Yes, this makes no sense, but it "fixes" the 3rd Birthday by popular demand.
|
||||
}
|
||||
|
||||
glstate.colorMask.set(rmask, gmask, bmask, amask);
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "GPU/GLES/TextureCache.h"
|
||||
#include "GPU/GLES/Framebuffer.h"
|
||||
#include "GPU/GLES/FragmentShaderGenerator.h"
|
||||
#include "GPU/GLES/DepalettizeShader.h"
|
||||
#include "GPU/Common/TextureDecoder.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Host.h"
|
||||
@ -57,14 +58,6 @@
|
||||
|
||||
extern int g_iNumVideos;
|
||||
|
||||
static inline bool UseBGRA8888() {
|
||||
// TODO: Other platforms? May depend on vendor which is faster?
|
||||
#ifdef _WIN32
|
||||
return gl_extensions.EXT_bgra;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
TextureCache::TextureCache() : clearCacheNextFrame_(false), lowMemoryMode_(false), clutBuf_(NULL) {
|
||||
lastBoundTexture = -1;
|
||||
decimationCounter_ = TEXCACHE_DECIMATION_INTERVAL;
|
||||
@ -72,8 +65,16 @@ TextureCache::TextureCache() : clearCacheNextFrame_(false), lowMemoryMode_(false
|
||||
tmpTexBuf32.resize(1024 * 512); // 2MB
|
||||
tmpTexBuf16.resize(1024 * 512); // 1MB
|
||||
tmpTexBufRearrange.resize(1024 * 512); // 2MB
|
||||
|
||||
// Aren't these way too big?
|
||||
clutBufConverted_ = (u32 *)AllocateAlignedMemory(4096 * sizeof(u32), 16); // 16KB
|
||||
clutBufRaw_ = (u32 *)AllocateAlignedMemory(4096 * sizeof(u32), 16); // 16KB
|
||||
|
||||
// Zap these so that reads from uninitialized parts of the CLUT look the same in
|
||||
// release and debug
|
||||
memset(clutBufConverted_, 0, 4096 * sizeof(u32));
|
||||
memset(clutBufRaw_, 0, 4096 * sizeof(u32));
|
||||
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyLevel);
|
||||
SetupTextureDecoder();
|
||||
}
|
||||
@ -117,6 +118,9 @@ void TextureCache::Decimate() {
|
||||
for (TexCache::iterator iter = cache.begin(); iter != cache.end(); ) {
|
||||
if (iter->second.lastFrame + killAge < gpuStats.numFlips) {
|
||||
glDeleteTextures(1, &iter->second.texture);
|
||||
if (iter->second.depalFBO) {
|
||||
fbo_destroy(iter->second.depalFBO);
|
||||
}
|
||||
cache.erase(iter++);
|
||||
} else {
|
||||
++iter;
|
||||
@ -128,6 +132,9 @@ void TextureCache::Decimate() {
|
||||
// In low memory mode, we kill them all.
|
||||
if (lowMemoryMode_ || iter->second.lastFrame + TEXTURE_SECOND_KILL_AGE < gpuStats.numFlips) {
|
||||
glDeleteTextures(1, &iter->second.texture);
|
||||
if (iter->second.depalFBO) {
|
||||
fbo_destroy(iter->second.depalFBO);
|
||||
}
|
||||
secondCache.erase(iter++);
|
||||
} else {
|
||||
++iter;
|
||||
@ -201,6 +208,7 @@ inline void AttachFramebufferValid(T &entry, VirtualFramebuffer *framebuffer) {
|
||||
if (hasInvalidFramebuffer || hasOlderFramebuffer) {
|
||||
entry->framebuffer = framebuffer;
|
||||
entry->invalidHint = 0;
|
||||
entry->status &= ~TextureCache::TexCacheEntry::STATUS_DEPALETTIZE;
|
||||
host->GPUNotifyTextureAttachment(entry->addr);
|
||||
}
|
||||
}
|
||||
@ -210,11 +218,12 @@ inline void AttachFramebufferInvalid(T &entry, VirtualFramebuffer *framebuffer)
|
||||
if (entry->framebuffer == 0 || entry->framebuffer == framebuffer) {
|
||||
entry->framebuffer = framebuffer;
|
||||
entry->invalidHint = -1;
|
||||
entry->status &= ~TextureCache::TexCacheEntry::STATUS_DEPALETTIZE;
|
||||
host->GPUNotifyTextureAttachment(entry->addr);
|
||||
}
|
||||
}
|
||||
|
||||
inline void TextureCache::AttachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, bool exactMatch) {
|
||||
void TextureCache::AttachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, bool exactMatch) {
|
||||
// If they match exactly, it's non-CLUT and from the top left.
|
||||
if (exactMatch) {
|
||||
// Apply to non-buffered and buffered mode only.
|
||||
@ -237,22 +246,41 @@ inline void TextureCache::AttachFramebuffer(TexCacheEntry *entry, u32 address, V
|
||||
if (!(g_Config.iRenderingMode == FB_BUFFERED_MODE))
|
||||
return;
|
||||
|
||||
// 3rd Birthday (and possibly other games) render to a 16 bit clut texture.
|
||||
const bool compatFormat = framebuffer->format == entry->format
|
||||
|| (framebuffer->format == GE_FORMAT_8888 && entry->format == GE_TFMT_CLUT32)
|
||||
|| (framebuffer->format != GE_FORMAT_8888 && entry->format == GE_TFMT_CLUT16);
|
||||
// Check for CLUT. The framebuffer is always RGB, but it can be interpreted as a CLUT texture.
|
||||
// 3rd Birthday (and a bunch of other games) render to a 16 bit clut texture.
|
||||
bool clutSuccess = false;
|
||||
if (((framebuffer->format == GE_FORMAT_8888 && entry->format == GE_TFMT_CLUT32) ||
|
||||
(framebuffer->format != GE_FORMAT_8888 && entry->format == GE_TFMT_CLUT16))) {
|
||||
AttachFramebufferValid(entry, framebuffer);
|
||||
entry->status |= TexCacheEntry::STATUS_DEPALETTIZE;
|
||||
// We'll validate it later.
|
||||
clutSuccess = true;
|
||||
} else if (entry->format == GE_TFMT_CLUT8 || entry->format == GE_TFMT_CLUT4) {
|
||||
ERROR_LOG_REPORT_ONCE(fourEightBit, G3D, "4 and 8-bit CLUT format not supported for framebuffers");
|
||||
}
|
||||
|
||||
// Is it at least the right stride?
|
||||
if (framebuffer->fb_stride == entry->bufw && compatFormat) {
|
||||
if (framebuffer->format != entry->format) {
|
||||
WARN_LOG_REPORT_ONCE(diffFormat2, G3D, "Render to texture with different formats %d != %d at %08x", entry->format, framebuffer->format, address);
|
||||
// TODO: Use an FBO to translate the palette?
|
||||
AttachFramebufferValid(entry, framebuffer);
|
||||
} else if ((entry->addr - address) / entry->bufw < framebuffer->height) {
|
||||
WARN_LOG_REPORT_ONCE(subarea, G3D, "Render to area containing texture at %08x", address);
|
||||
// TODO: Keep track of the y offset.
|
||||
// If "AttachFramebufferValid" , God of War Ghost of Sparta/Chains of Olympus will be missing special effect.
|
||||
AttachFramebufferInvalid(entry, framebuffer);
|
||||
if (!clutSuccess) {
|
||||
// This is either normal or we failed to generate a shader to depalettize
|
||||
const bool compatFormat = framebuffer->format == entry->format ||
|
||||
(framebuffer->format == GE_FORMAT_8888 && entry->format == GE_TFMT_CLUT32) ||
|
||||
(framebuffer->format != GE_FORMAT_8888 && entry->format == GE_TFMT_CLUT16);
|
||||
|
||||
// Is it at least the right stride?
|
||||
if (framebuffer->fb_stride == entry->bufw) {
|
||||
if (compatFormat) {
|
||||
if (framebuffer->format != entry->format) {
|
||||
WARN_LOG_REPORT_ONCE(diffFormat2, G3D, "Render to texture with different formats %d != %d at %08x", entry->format, framebuffer->format, address);
|
||||
// TODO: Use an FBO to translate the palette?
|
||||
AttachFramebufferValid(entry, framebuffer);
|
||||
} else if ((entry->addr - address) / entry->bufw < framebuffer->height) {
|
||||
WARN_LOG_REPORT_ONCE(subarea, G3D, "Render to area containing texture at %08x", address);
|
||||
// TODO: Keep track of the y offset.
|
||||
// If "AttachFramebufferValid" , God of War Ghost of Sparta/Chains of Olympus will be missing special effect.
|
||||
AttachFramebufferInvalid(entry, framebuffer);
|
||||
}
|
||||
} else {
|
||||
WARN_LOG_REPORT_ONCE(diffFormat2, G3D, "Render to texture with incompatible formats %d != %d at %08x", entry->format, framebuffer->format, address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -495,6 +523,11 @@ void TextureCache::UpdateSamplingParams(TexCacheEntry &entry, bool force) {
|
||||
bool sClamp = gstate.isTexCoordClampedS();
|
||||
bool tClamp = gstate.isTexCoordClampedT();
|
||||
|
||||
if (entry.status & TexCacheEntry::STATUS_TEXPARAM_DIRTY) {
|
||||
entry.status &= ~TexCacheEntry::STATUS_TEXPARAM_DIRTY;
|
||||
force = true;
|
||||
}
|
||||
|
||||
bool noMip = (gstate.texlevel & 0xFFFFFF) == 0x000001 || (gstate.texlevel & 0xFFFFFF) == 0x100001 ; // Fix texlevel at 0
|
||||
|
||||
if (entry.maxLevel == 0) {
|
||||
@ -578,7 +611,6 @@ void TextureCache::UpdateSamplingParams(TexCacheEntry &entry, bool force) {
|
||||
static void ConvertColors(void *dstBuf, const void *srcBuf, GLuint dstFmt, int numPixels) {
|
||||
const u32 *src = (const u32 *)srcBuf;
|
||||
u32 *dst = (u32 *)dstBuf;
|
||||
// TODO: NEON.
|
||||
switch (dstFmt) {
|
||||
case GL_UNSIGNED_SHORT_4_4_4_4:
|
||||
{
|
||||
@ -601,6 +633,7 @@ static void ConvertColors(void *dstBuf, const void *srcBuf, GLuint dstFmt, int n
|
||||
int i = sseChunks * 8 / 2;
|
||||
#else
|
||||
int i = 0;
|
||||
// TODO: NEON.
|
||||
#endif
|
||||
for (; i < (numPixels + 1) / 2; i++) {
|
||||
u32 c = src[i];
|
||||
@ -633,6 +666,7 @@ static void ConvertColors(void *dstBuf, const void *srcBuf, GLuint dstFmt, int n
|
||||
int i = sseChunks * 8 / 2;
|
||||
#else
|
||||
int i = 0;
|
||||
// TODO: NEON.
|
||||
#endif
|
||||
for (; i < (numPixels + 1) / 2; i++) {
|
||||
u32 c = src[i];
|
||||
@ -662,6 +696,7 @@ static void ConvertColors(void *dstBuf, const void *srcBuf, GLuint dstFmt, int n
|
||||
int i = sseChunks * 8 / 2;
|
||||
#else
|
||||
int i = 0;
|
||||
// TODO: NEON.
|
||||
#endif
|
||||
for (; i < (numPixels + 1) / 2; i++) {
|
||||
u32 c = src[i];
|
||||
@ -723,37 +758,6 @@ static inline u32 MiniHash(const u32 *ptr) {
|
||||
return ptr[0];
|
||||
}
|
||||
|
||||
static inline u32 QuickClutHash(const u8 *clut, u32 bytes) {
|
||||
// CLUTs always come in multiples of 32 bytes, can't load them any other way.
|
||||
_dbg_assert_msg_(G3D, (bytes & 31) == 0, "CLUT should always have a multiple of 32 bytes.");
|
||||
|
||||
const u32 prime = 2246822519U;
|
||||
u32 hash = 0;
|
||||
#ifdef _M_SSE
|
||||
if ((((u32)(intptr_t)clut) & 0xf) == 0) {
|
||||
__m128i cursor = _mm_set1_epi32(0);
|
||||
const __m128i mult = _mm_set1_epi32(prime);
|
||||
const __m128i *p = (const __m128i *)clut;
|
||||
for (u32 i = 0; i < bytes / 16; ++i) {
|
||||
cursor = _mm_add_epi32(cursor, _mm_mul_epu32(_mm_load_si128(&p[i]), mult));
|
||||
}
|
||||
// Add the four parts into the low i32.
|
||||
cursor = _mm_add_epi32(cursor, _mm_srli_si128(cursor, 8));
|
||||
cursor = _mm_add_epi32(cursor, _mm_srli_si128(cursor, 4));
|
||||
hash = _mm_cvtsi128_si32(cursor);
|
||||
} else {
|
||||
#else
|
||||
// TODO: ARM NEON implementation (using CPUDetect to be sure it has NEON.)
|
||||
{
|
||||
#endif
|
||||
for (const u32 *p = (u32 *)clut, *end = (u32 *)(clut + bytes); p < end; ) {
|
||||
hash += *p++ * prime;
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static inline u32 QuickTexHash(u32 addr, int bufw, int w, int h, GETextureFormat format) {
|
||||
const u32 sizeInRAM = (textureBitsPerPixel[format] * bufw * h) / 8;
|
||||
const u32 *checkp = (const u32 *) Memory::GetPointer(addr);
|
||||
@ -821,7 +825,8 @@ void TextureCache::UpdateCurrentClut() {
|
||||
clutAlphaLinear_ = false;
|
||||
break;
|
||||
}
|
||||
// Alpha 0 doesn't matter.
|
||||
// Alpha 0 doesn't matter.
|
||||
// TODO: Well, depending on blend mode etc, it can actually matter, although unlikely.
|
||||
if (i != 0 && (clut[i] & 0xFFF0) != clutAlphaLinearColor_) {
|
||||
clutAlphaLinear_ = false;
|
||||
break;
|
||||
@ -887,7 +892,73 @@ void TextureCache::SetTextureFramebuffer(TexCacheEntry *entry) {
|
||||
entry->framebuffer->usageFlags |= FB_USAGE_TEXTURE;
|
||||
bool useBufferedRendering = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE;
|
||||
if (useBufferedRendering) {
|
||||
framebufferManager_->BindFramebufferColor(entry->framebuffer);
|
||||
GLuint program = 0;
|
||||
if (entry->status & TexCacheEntry::STATUS_DEPALETTIZE) {
|
||||
program = depalShaderCache_->GetDepalettizeShader(entry->framebuffer->format);
|
||||
}
|
||||
if (program) {
|
||||
GLuint clutTexture = depalShaderCache_->GetClutTexture(clutHash_, clutBuf_);
|
||||
if (!entry->depalFBO) {
|
||||
entry->depalFBO = fbo_create(entry->framebuffer->renderWidth, entry->framebuffer->renderHeight, 1, false, FBO_8888);
|
||||
}
|
||||
fbo_bind_as_render_target(entry->depalFBO);
|
||||
static const float pos[12] = {
|
||||
-1, -1, -1,
|
||||
1, -1, -1,
|
||||
1, 1, -1,
|
||||
-1, 1, -1
|
||||
};
|
||||
static const float uv[8] = {
|
||||
0, 0,
|
||||
1, 0,
|
||||
1, 1,
|
||||
0, 1,
|
||||
};
|
||||
static const GLubyte indices[4] = { 0, 1, 3, 2 };
|
||||
|
||||
glUseProgram(program);
|
||||
gstate_c.shaderChanged = true;
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, clutTexture);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
framebufferManager_->BindFramebufferColor(entry->framebuffer, true);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
entry->status |= TexCacheEntry::STATUS_TEXPARAM_DIRTY;
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
#if !defined(USING_GLES2)
|
||||
glDisable(GL_LOGIC_OP);
|
||||
#endif
|
||||
glViewport(0, 0, entry->framebuffer->renderWidth, entry->framebuffer->renderHeight);
|
||||
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, pos);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 8, uv);
|
||||
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);
|
||||
|
||||
/*
|
||||
glDisableVertexAttribArray(0);
|
||||
glDisableVertexAttribArray(1);
|
||||
*/
|
||||
fbo_bind_color_as_texture(entry->depalFBO, 0);
|
||||
glstate.Restore();
|
||||
framebufferManager_->RebindFramebuffer();
|
||||
} else {
|
||||
entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE;
|
||||
framebufferManager_->BindFramebufferColor(entry->framebuffer);
|
||||
}
|
||||
|
||||
// Keep the framebuffer alive.
|
||||
entry->framebuffer->last_frame_used = gpuStats.numFlips;
|
||||
@ -897,7 +968,7 @@ void TextureCache::SetTextureFramebuffer(TexCacheEntry *entry) {
|
||||
gstate_c.curTextureHeight = entry->framebuffer->height;
|
||||
gstate_c.flipTexture = true;
|
||||
gstate_c.textureFullAlpha = entry->framebuffer->format == GE_FORMAT_565;
|
||||
gstate_c.textureSimpleAlpha = false;
|
||||
gstate_c.textureSimpleAlpha = gstate_c.textureFullAlpha;
|
||||
UpdateSamplingParams(*entry, true);
|
||||
} else {
|
||||
if (entry->framebuffer->fbo)
|
||||
@ -1149,6 +1220,7 @@ void TextureCache::SetTexture(bool force) {
|
||||
entry->framebuffer = 0;
|
||||
entry->maxLevel = maxLevel;
|
||||
entry->lodBias = 0.0f;
|
||||
entry->depalFBO = 0;
|
||||
|
||||
entry->dim = gstate.getTextureDimension(0);
|
||||
entry->bufw = bufw;
|
||||
|
@ -17,14 +17,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Globals.h"
|
||||
#include "gfx_es2/fbo.h"
|
||||
#include "gfx_es2/gpu_features.h"
|
||||
|
||||
#include "Globals.h"
|
||||
#include "GPU/GPUInterface.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "TextureScaler.h"
|
||||
#include "GPU/GLES/TextureScaler.h"
|
||||
|
||||
struct VirtualFramebuffer;
|
||||
class FramebufferManager;
|
||||
class DepalShaderCache;
|
||||
|
||||
enum TextureFiltering {
|
||||
AUTO = 1,
|
||||
@ -39,6 +42,14 @@ enum FramebufferNotification {
|
||||
NOTIFY_FB_DESTROYED,
|
||||
};
|
||||
|
||||
inline bool UseBGRA8888() {
|
||||
// TODO: Other platforms? May depend on vendor which is faster?
|
||||
#ifdef _WIN32
|
||||
return gl_extensions.EXT_bgra;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
class TextureCache {
|
||||
public:
|
||||
TextureCache();
|
||||
@ -60,6 +71,9 @@ public:
|
||||
void SetFramebufferManager(FramebufferManager *fbManager) {
|
||||
framebufferManager_ = fbManager;
|
||||
}
|
||||
void SetDepalShaderCache(DepalShaderCache *dpCache) {
|
||||
depalShaderCache_ = dpCache;
|
||||
}
|
||||
|
||||
size_t NumLoadedTextures() const {
|
||||
return cache.size();
|
||||
@ -73,7 +87,6 @@ public:
|
||||
// Only used by Qt UI?
|
||||
bool DecodeTexture(u8 *output, GPUgstate state);
|
||||
|
||||
private:
|
||||
// Wow this is starting to grow big. Soon need to start looking at resizing it.
|
||||
// Must stay a POD.
|
||||
struct TexCacheEntry {
|
||||
@ -93,6 +106,9 @@ private:
|
||||
|
||||
STATUS_CHANGE_FREQUENT = 0x10, // Changes often (less than 15 frames in between.)
|
||||
STATUS_CLUT_RECHECK = 0x20, // Another texture with same addr had a hashfail.
|
||||
STATUS_DEPALETTIZE = 0x40,
|
||||
STATUS_DEPALETTIZE_DIRTY = 0x80,
|
||||
STATUS_TEXPARAM_DIRTY = 0x100
|
||||
};
|
||||
|
||||
// Status, but int so we can zero initialize.
|
||||
@ -100,6 +116,7 @@ private:
|
||||
u32 addr;
|
||||
u32 hash;
|
||||
VirtualFramebuffer *framebuffer; // if null, not sourced from an FBO.
|
||||
FBO *depalFBO;
|
||||
u32 sizeInRAM;
|
||||
int lastFrame;
|
||||
int numFrames;
|
||||
@ -137,6 +154,7 @@ private:
|
||||
bool Matches(u16 dim2, u8 format2, int maxLevel2);
|
||||
};
|
||||
|
||||
private:
|
||||
void Decimate(); // Run this once per frame to get rid of old textures.
|
||||
void *UnswizzleFromMem(const u8 *texptr, u32 bufw, u32 bytesPerPixel, u32 level);
|
||||
void *ReadIndexedTex(int level, const u8 *texptr, int bytesPerIndex, GLuint dstFmt, int bufw);
|
||||
@ -184,5 +202,7 @@ private:
|
||||
|
||||
int decimationCounter_;
|
||||
FramebufferManager *framebufferManager_;
|
||||
DepalShaderCache *depalShaderCache_;
|
||||
};
|
||||
|
||||
GLenum getClutDestFormat(GEPaletteFormat format);
|
||||
|
@ -190,6 +190,7 @@
|
||||
<ClInclude Include="Directx9\VertexShaderGeneratorDX9.h" />
|
||||
<ClInclude Include="ge_constants.h" />
|
||||
<ClInclude Include="GeDisasm.h" />
|
||||
<ClInclude Include="GLES\DepalettizeShader.h" />
|
||||
<ClInclude Include="GLES\FragmentShaderGenerator.h" />
|
||||
<ClInclude Include="GLES\Framebuffer.h" />
|
||||
<ClInclude Include="GLES\GLES_GPU.h" />
|
||||
@ -242,6 +243,7 @@
|
||||
<ClCompile Include="Directx9\VertexDecoderDX9.cpp" />
|
||||
<ClCompile Include="Directx9\VertexShaderGeneratorDX9.cpp" />
|
||||
<ClCompile Include="GeDisasm.cpp" />
|
||||
<ClCompile Include="GLES\DepalettizeShader.cpp" />
|
||||
<ClCompile Include="GLES\FragmentShaderGenerator.cpp" />
|
||||
<ClCompile Include="GLES\Framebuffer.cpp" />
|
||||
<ClCompile Include="GLES\GLES_GPU.cpp" />
|
||||
|
@ -165,6 +165,9 @@
|
||||
<ClInclude Include="Common\TransformCommon.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GLES\DepalettizeShader.h">
|
||||
<Filter>GLES</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Math3D.cpp">
|
||||
@ -308,8 +311,11 @@
|
||||
<ClCompile Include="Common\TransformCommon.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GLES\DepalettizeShader.cpp">
|
||||
<Filter>GLES</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CMakeLists.txt" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -453,6 +453,8 @@ struct GPUStateCache
|
||||
bool textureSimpleAlpha;
|
||||
bool vertexFullAlpha;
|
||||
bool framebufChanged;
|
||||
// Doesn't need savestating.
|
||||
bool shaderChanged;
|
||||
|
||||
int skipDrawReason;
|
||||
|
||||
|
@ -46,6 +46,7 @@ SOURCES += $$P/Core/*.cpp \ # Core
|
||||
$$P/GPU/GPUState.cpp \
|
||||
$$P/GPU/Math3D.cpp \
|
||||
$$P/GPU/Null/NullGpu.cpp \
|
||||
$$P/GPU/GLES/DepalettizeShader.cpp \
|
||||
$$P/GPU/GLES/FragmentShaderGenerator.cpp \
|
||||
$$P/GPU/GLES/Framebuffer.cpp \
|
||||
$$P/GPU/GLES/GLES_GPU.cpp \
|
||||
|
@ -190,7 +190,6 @@ void GameSettingsScreen::CreateViews() {
|
||||
graphicsSettings->Add(new ItemHeader(gs->T("Hack Settings", "Hack Settings (these WILL cause glitches)")));
|
||||
graphicsSettings->Add(new CheckBox(&g_Config.bTimerHack, gs->T("Timer Hack")));
|
||||
graphicsSettings->Add(new CheckBox(&g_Config.bDisableStencilTest, gs->T("Disable Stencil Test")));
|
||||
graphicsSettings->Add(new CheckBox(&g_Config.bAlphaMaskHack, gs->T("Alpha Mask Hack (3rd Birthday)")));
|
||||
graphicsSettings->Add(new CheckBox(&g_Config.bAlwaysDepthWrite, gs->T("Always Depth Write")));
|
||||
CheckBox *prescale = graphicsSettings->Add(new CheckBox(&g_Config.bPrescaleUV, gs->T("Texture Coord Speedhack")));
|
||||
if (PSP_IsInited())
|
||||
|
@ -144,6 +144,7 @@ EXEC_AND_LIB_FILES := \
|
||||
$(SRC)/GPU/Debugger/Breakpoints.cpp \
|
||||
$(SRC)/GPU/Debugger/Stepping.cpp \
|
||||
$(SRC)/GPU/GLES/Framebuffer.cpp \
|
||||
$(SRC)/GPU/GLES/DepalettizeShader.cpp \
|
||||
$(SRC)/GPU/GLES/GLES_GPU.cpp.arm \
|
||||
$(SRC)/GPU/GLES/TextureCache.cpp.arm \
|
||||
$(SRC)/GPU/GLES/TransformPipeline.cpp.arm \
|
||||
|
Loading…
Reference in New Issue
Block a user