mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-12 09:09:29 +01:00
Merge pull request #10911 from hrydgard/shader-depal
Vulkan: Depalettize in shaders
This commit is contained in:
28 files changed
+574
-58
No files matched your search
@@ -57,6 +57,8 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
|
||||
CheckSetting(iniFile, gameID, "RequireDefaultCPUClock", &flags_.RequireDefaultCPUClock);
|
||||
CheckSetting(iniFile, gameID, "DisableReadbacks", &flags_.DisableReadbacks);
|
||||
CheckSetting(iniFile, gameID, "DisableAccurateDepth", &flags_.DisableAccurateDepth);
|
||||
CheckSetting(iniFile, gameID, "MGS2AcidHack", &flags_.MGS2AcidHack);
|
||||
CheckSetting(iniFile, gameID, "SonicRivalsHack", &flags_.SonicRivalsHack);
|
||||
}
|
||||
|
||||
void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) {
|
||||
|
||||
@@ -57,6 +57,8 @@ struct CompatFlags {
|
||||
bool RequireDefaultCPUClock;
|
||||
bool DisableReadbacks;
|
||||
bool DisableAccurateDepth;
|
||||
bool MGS2AcidHack;
|
||||
bool SonicRivalsHack;
|
||||
};
|
||||
|
||||
class IniFile;
|
||||
|
||||
@@ -70,7 +70,7 @@ void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLang
|
||||
int mask = gstate.getClutIndexMask();
|
||||
int shift = gstate.getClutIndexShift();
|
||||
int offset = gstate.getClutIndexStartPos();
|
||||
const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat();
|
||||
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.
|
||||
|
||||
@@ -85,15 +85,16 @@ enum : uint64_t {
|
||||
DIRTY_BONEMATRIX6 = 1ULL << 30,
|
||||
DIRTY_BONEMATRIX7 = 1ULL << 31,
|
||||
|
||||
// These are for hardware tessellation
|
||||
DIRTY_BEZIERSPLINE = 1ULL << 32,
|
||||
DIRTY_TEXCLAMP = 1ULL << 33,
|
||||
|
||||
// space for 7 more uniforms.
|
||||
DIRTY_DEPAL = 1ULL << 34,
|
||||
|
||||
// space for 5 more uniform dirty flags. Remember to update DIRTY_ALL_UNIFORMS.
|
||||
|
||||
DIRTY_BONE_UNIFORMS = 0xFF000000ULL,
|
||||
|
||||
DIRTY_ALL_UNIFORMS = 0x3FFFFFFFFULL,
|
||||
DIRTY_ALL_UNIFORMS = 0x7FFFFFFFFULL,
|
||||
DIRTY_ALL_LIGHTS = DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3,
|
||||
|
||||
// Other dirty elements that aren't uniforms!
|
||||
|
||||
@@ -171,6 +171,7 @@ std::string FragmentShaderDesc(const ShaderID &id) {
|
||||
if (id.Bit(FS_BIT_COLOR_DOUBLE)) desc << "2x ";
|
||||
if (id.Bit(FS_BIT_FLATSHADE)) desc << "Flat ";
|
||||
if (id.Bit(FS_BIT_BGRA_TEXTURE)) desc << "BGRA ";
|
||||
if (id.Bit(FS_BIT_SHADER_DEPAL)) desc << "Depal ";
|
||||
if (id.Bit(FS_BIT_SHADER_TEX_CLAMP)) {
|
||||
desc << "TClamp";
|
||||
if (id.Bit(FS_BIT_CLAMP_S)) desc << "S";
|
||||
@@ -236,6 +237,7 @@ void ComputeFragmentShaderID(ShaderID *id_out) {
|
||||
bool doTextureProjection = (gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX && MatrixNeedsProjection(gstate.tgenMatrix));
|
||||
bool doTextureAlpha = gstate.isTextureAlphaUsed();
|
||||
bool doFlatShading = gstate.getShadeMode() == GE_SHADE_FLAT;
|
||||
bool useShaderDepal = gstate_c.useShaderDepal;
|
||||
|
||||
ReplaceBlendType replaceBlend = ReplaceBlendWithShader(gstate_c.allowShaderBlend, gstate.FrameBufFormat());
|
||||
ReplaceAlphaType stencilToAlpha = ReplaceAlphaWithStencil(replaceBlend);
|
||||
@@ -299,6 +301,8 @@ void ComputeFragmentShaderID(ShaderID *id_out) {
|
||||
id.SetBits(FS_BIT_BLENDFUNC_B, 4, gstate.getBlendFuncB());
|
||||
}
|
||||
id.SetBit(FS_BIT_FLATSHADE, doFlatShading);
|
||||
|
||||
id.SetBit(FS_BIT_SHADER_DEPAL, useShaderDepal);
|
||||
}
|
||||
|
||||
*id_out = id;
|
||||
|
||||
@@ -62,7 +62,7 @@ enum {
|
||||
FS_BIT_DO_TEXTURE = 1,
|
||||
FS_BIT_TEXFUNC = 2, // 3 bits
|
||||
FS_BIT_TEXALPHA = 5,
|
||||
// 6 is free.
|
||||
FS_BIT_SHADER_DEPAL = 6,
|
||||
FS_BIT_SHADER_TEX_CLAMP = 7,
|
||||
FS_BIT_CLAMP_S = 8,
|
||||
FS_BIT_CLAMP_T = 9,
|
||||
|
||||
@@ -200,6 +200,17 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView
|
||||
if (dirtyUniforms & DIRTY_BEZIERSPLINE) {
|
||||
ub->spline_counts = BytesToUint32(gstate_c.spline_count_u, gstate_c.spline_count_v, gstate_c.spline_type_u, gstate_c.spline_type_v);
|
||||
}
|
||||
|
||||
if (dirtyUniforms & DIRTY_DEPAL) {
|
||||
int indexMask = gstate.getClutIndexMask();
|
||||
int indexShift = gstate.getClutIndexShift();
|
||||
int indexOffset = gstate.getClutIndexStartPos() >> 4;
|
||||
int format = gstate_c.depalFramebufferFormat;
|
||||
uint32_t val = BytesToUint32(indexMask, indexShift, indexOffset, format);
|
||||
// Poke in a bilinear filter flag in the top bit.
|
||||
val |= gstate.isMagnifyFilteringEnabled() << 31;
|
||||
ub->depal_mask_shift_off_fmt = val;
|
||||
}
|
||||
}
|
||||
|
||||
void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms) {
|
||||
|
||||
@@ -11,7 +11,7 @@ enum : uint64_t {
|
||||
DIRTY_WORLDMATRIX | DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWMATRIX | DIRTY_TEXMATRIX | DIRTY_ALPHACOLORREF |
|
||||
DIRTY_PROJMATRIX | DIRTY_FOGCOLOR | DIRTY_FOGCOEF | DIRTY_TEXENV | DIRTY_STENCILREPLACEVALUE |
|
||||
DIRTY_ALPHACOLORMASK | DIRTY_SHADERBLEND | DIRTY_UVSCALEOFFSET | DIRTY_TEXCLAMP | DIRTY_DEPTHRANGE | DIRTY_MATAMBIENTALPHA |
|
||||
DIRTY_BEZIERSPLINE,
|
||||
DIRTY_BEZIERSPLINE | DIRTY_DEPAL,
|
||||
DIRTY_LIGHT_UNIFORMS =
|
||||
DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3 |
|
||||
DIRTY_MATDIFFUSE | DIRTY_MATSPECULAR | DIRTY_MATEMISSIVE | DIRTY_AMBIENT,
|
||||
@@ -30,7 +30,8 @@ struct UB_VS_FS_Base {
|
||||
float depthRange[4];
|
||||
float fogCoef[2]; float stencil; float pad0;
|
||||
float matAmbient[4];
|
||||
uint32_t spline_counts; int pad1; int pad2; int pad3;
|
||||
uint32_t spline_counts; uint32_t depal_mask_shift_off_fmt; // 4 params packed into one.
|
||||
int pad2; int pad3;
|
||||
// Fragment data
|
||||
float fogColor[4];
|
||||
float texEnvColor[4];
|
||||
@@ -54,7 +55,7 @@ R"( mat4 proj_mtx;
|
||||
float stencilReplace;
|
||||
vec4 matambientalpha;
|
||||
uint spline_counts;
|
||||
int pad1;
|
||||
uint depal_mask_shift_off_fmt;
|
||||
int pad2;
|
||||
int pad3;
|
||||
vec3 fogcolor;
|
||||
@@ -80,7 +81,7 @@ R"( float4x4 u_proj;
|
||||
float u_stencilReplaceValue;
|
||||
float4 u_matambientalpha;
|
||||
uint u_spline_counts;
|
||||
int pad1;
|
||||
uint u_depal_mask_shift_off_fmt;
|
||||
int pad2;
|
||||
int pad3;
|
||||
float3 u_fogcolor;
|
||||
|
||||
@@ -157,6 +157,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform
|
||||
bool doTextureProjection = id.Bit(FS_BIT_DO_TEXTURE_PROJ);
|
||||
bool doTextureAlpha = id.Bit(FS_BIT_TEXALPHA);
|
||||
bool doFlatShading = id.Bit(FS_BIT_FLATSHADE);
|
||||
bool shaderDepal = id.Bit(FS_BIT_SHADER_DEPAL);
|
||||
|
||||
GEComparison alphaTestFunc = (GEComparison)id.Bits(FS_BIT_ALPHA_TEST_FUNC, 3);
|
||||
GEComparison colorTestFunc = (GEComparison)id.Bits(FS_BIT_COLOR_TEST_FUNC, 2);
|
||||
@@ -217,6 +218,12 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform
|
||||
}
|
||||
}
|
||||
|
||||
if (shaderDepal) {
|
||||
WRITE(p, "uniform sampler2D pal;\n");
|
||||
WRITE(p, "uniform int u_depal;\n");
|
||||
*uniformMask |= DIRTY_DEPAL;
|
||||
}
|
||||
|
||||
StencilValueType replaceAlphaWithStencilType = (StencilValueType)id.Bits(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, 4);
|
||||
if (stencilToAlpha && replaceAlphaWithStencilType == STENCIL_VALUE_UNIFORM) {
|
||||
*uniformMask |= DIRTY_STENCILREPLACEVALUE;
|
||||
@@ -336,10 +343,95 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform
|
||||
|
||||
if (doTextureProjection) {
|
||||
WRITE(p, " vec4 t = %sProj(tex, %s);\n", texture, texcoord);
|
||||
if (shaderDepal) {
|
||||
WRITE(p, " vec4 t1 = %sProjOffset(tex, %s, ivec2(1, 0));\n", texture, texcoord);
|
||||
WRITE(p, " vec4 t2 = %sProjOffset(tex, %s, ivec2(0, 1));\n", texture, texcoord);
|
||||
WRITE(p, " vec4 t3 = %sProjOffset(tex, %s, ivec2(1, 1));\n", texture, texcoord);
|
||||
}
|
||||
} else {
|
||||
WRITE(p, " vec4 t = %s(tex, %s.xy);\n", texture, texcoord);
|
||||
if (shaderDepal) {
|
||||
WRITE(p, " vec4 t1 = %sOffset(tex, %s.xy, ivec2(1, 0));\n", texture, texcoord);
|
||||
WRITE(p, " vec4 t2 = %sOffset(tex, %s.xy, ivec2(0, 1));\n", texture, texcoord);
|
||||
WRITE(p, " vec4 t3 = %sOffset(tex, %s.xy, ivec2(1, 1));\n", texture, texcoord);
|
||||
}
|
||||
}
|
||||
WRITE(p, " vec4 p = v_color0;\n");
|
||||
|
||||
if (shaderDepal) {
|
||||
WRITE(p, " int depalMask = (u_depal & 0xFF);\n");
|
||||
WRITE(p, " int depalShift = ((u_depal >> 8) & 0xFF);\n");
|
||||
WRITE(p, " int depalOffset = (((u_depal >> 16) & 0xFF) << 4);\n");
|
||||
WRITE(p, " int depalFmt = ((u_depal >> 24) & 0x3);\n");
|
||||
WRITE(p, " bool bilinear = (u_depal >> 31) != 0;\n");
|
||||
WRITE(p, " vec2 fraction = fract(%s.xy * vec2(textureSize(tex, 0).xy));\n", texcoord);
|
||||
WRITE(p, " ivec4 col; int index0; int index1; int index2; int index3;\n");
|
||||
WRITE(p, " switch (depalFmt) {\n"); // We might want to include fmt in the shader ID if this is a performance issue.
|
||||
WRITE(p, " case 0:\n"); // 565
|
||||
WRITE(p, " col = ivec4(t.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
|
||||
WRITE(p, " index0 = (col.b << 11) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " if (bilinear) {\n");
|
||||
WRITE(p, " col = ivec4(t1.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
|
||||
WRITE(p, " index1 = (col.b << 11) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " col = ivec4(t2.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
|
||||
WRITE(p, " index2 = (col.b << 11) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " col = ivec4(t3.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
|
||||
WRITE(p, " index3 = (col.b << 11) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " break;\n");
|
||||
WRITE(p, " case 1:\n"); // 5551
|
||||
WRITE(p, " col = ivec4(t.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
|
||||
WRITE(p, " index0 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " if (bilinear) {\n");
|
||||
WRITE(p, " col = ivec4(t1.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
|
||||
WRITE(p, " index1 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " col = ivec4(t2.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
|
||||
WRITE(p, " index2 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " col = ivec4(t3.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
|
||||
WRITE(p, " index3 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " break;\n");
|
||||
WRITE(p, " case 2:\n"); // 4444
|
||||
WRITE(p, " col = ivec4(t.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n");
|
||||
WRITE(p, " index0 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n");
|
||||
WRITE(p, " if (bilinear) {\n");
|
||||
WRITE(p, " col = ivec4(t1.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n");
|
||||
WRITE(p, " index1 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n");
|
||||
WRITE(p, " col = ivec4(t2.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n");
|
||||
WRITE(p, " index2 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n");
|
||||
WRITE(p, " col = ivec4(t3.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n");
|
||||
WRITE(p, " index3 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n");
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " break;\n");
|
||||
WRITE(p, " case 3:\n"); // 8888
|
||||
WRITE(p, " col = ivec4(t.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n");
|
||||
WRITE(p, " index0 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n");
|
||||
WRITE(p, " if (bilinear) {\n");
|
||||
WRITE(p, " col = ivec4(t1.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n");
|
||||
WRITE(p, " index1 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n");
|
||||
WRITE(p, " col = ivec4(t2.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n");
|
||||
WRITE(p, " index2 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n");
|
||||
WRITE(p, " col = ivec4(t3.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n");
|
||||
WRITE(p, " index3 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n");
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " break;\n");
|
||||
WRITE(p, " };\n");
|
||||
WRITE(p, " index0 = ((index0 >> depalShift) & depalMask) | depalOffset;\n");
|
||||
WRITE(p, " t = texelFetch(pal, ivec2(index0, 0), 0);\n");
|
||||
WRITE(p, " if (bilinear) {\n");
|
||||
WRITE(p, " index1 = ((index1 >> depalShift) & depalMask) | depalOffset;\n");
|
||||
WRITE(p, " index2 = ((index2 >> depalShift) & depalMask) | depalOffset;\n");
|
||||
WRITE(p, " index3 = ((index3 >> depalShift) & depalMask) | depalOffset;\n");
|
||||
WRITE(p, " t1 = texelFetch(pal, ivec2(index1, 0), 0);\n");
|
||||
WRITE(p, " t2 = texelFetch(pal, ivec2(index2, 0), 0);\n");
|
||||
WRITE(p, " t3 = texelFetch(pal, ivec2(index3, 0), 0);\n");
|
||||
WRITE(p, " t = mix(t, t1, fraction.x);\n");
|
||||
WRITE(p, " t2 = mix(t2, t3, fraction.x);\n");
|
||||
WRITE(p, " t = mix(t, t2, fraction.y);\n");
|
||||
WRITE(p, " }\n");
|
||||
}
|
||||
|
||||
if (texFunc != GE_TEXFUNC_REPLACE || !doTextureAlpha)
|
||||
WRITE(p, " vec4 p = v_color0;\n");
|
||||
|
||||
if (doTextureAlpha) { // texfmt == RGBA
|
||||
switch (texFunc) {
|
||||
|
||||
@@ -103,6 +103,7 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs,
|
||||
queries.push_back({ &u_blendFixA, "u_blendFixA" });
|
||||
queries.push_back({ &u_blendFixB, "u_blendFixB" });
|
||||
queries.push_back({ &u_fbotexSize, "u_fbotexSize" });
|
||||
queries.push_back({ &u_pal, "pal" });
|
||||
|
||||
// Transform
|
||||
queries.push_back({ &u_view, "u_view" });
|
||||
@@ -161,6 +162,7 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs,
|
||||
queries.push_back({ &u_spline_count_v, "u_spline_count_v" });
|
||||
queries.push_back({ &u_spline_type_u, "u_spline_type_u" });
|
||||
queries.push_back({ &u_spline_type_v, "u_spline_type_v" });
|
||||
queries.push_back({ &u_depal, "u_depal" });
|
||||
|
||||
attrMask = vs->GetAttrMask();
|
||||
availableUniforms = vs->GetUniformMask() | fs->GetUniformMask();
|
||||
@@ -169,6 +171,7 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs,
|
||||
initialize.push_back({ &u_tex, 0, 0 });
|
||||
initialize.push_back({ &u_fbotex, 0, 1 });
|
||||
initialize.push_back({ &u_testtex, 0, 2 });
|
||||
initialize.push_back({ &u_pal, 0, 3 }); // CLUT
|
||||
initialize.push_back({ &u_tess_pos_tex, 0, 4 }); // Texture unit 4
|
||||
initialize.push_back({ &u_tess_tex_tex, 0, 5 }); // Texture unit 5
|
||||
initialize.push_back({ &u_tess_col_tex, 0, 6 }); // Texture unit 6
|
||||
@@ -283,6 +286,17 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid) {
|
||||
if (!dirty)
|
||||
return;
|
||||
|
||||
if (dirty & DIRTY_DEPAL) {
|
||||
int indexMask = gstate.getClutIndexMask();
|
||||
int indexShift = gstate.getClutIndexShift();
|
||||
int indexOffset = gstate.getClutIndexStartPos() >> 4;
|
||||
int format = gstate_c.depalFramebufferFormat;
|
||||
uint32_t val = BytesToUint32(indexMask, indexShift, indexOffset, format);
|
||||
// Poke in a bilinear filter flag in the top bit.
|
||||
val |= gstate.isMagnifyFilteringEnabled() << 31;
|
||||
render_->SetUniformI1(&u_depal, val);
|
||||
}
|
||||
|
||||
// Update any dirty uniforms before we draw
|
||||
if (dirty & DIRTY_PROJMATRIX) {
|
||||
Matrix4x4 flippedMatrix;
|
||||
@@ -810,7 +824,7 @@ std::string ShaderManagerGLES::DebugGetShaderString(std::string id, DebugShaderT
|
||||
// as sometimes these features might have an effect on the ID bits.
|
||||
|
||||
#define CACHE_HEADER_MAGIC 0x83277592
|
||||
#define CACHE_VERSION 11
|
||||
#define CACHE_VERSION 12
|
||||
struct CacheHeader {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
|
||||
@@ -85,6 +85,10 @@ public:
|
||||
int u_blendFixB;
|
||||
int u_fbotexSize;
|
||||
|
||||
// Shader depal
|
||||
int u_pal; // the texture
|
||||
int u_depal; // the params
|
||||
|
||||
// Fragment processing inputs
|
||||
int u_alphacolorref;
|
||||
int u_alphacolormask;
|
||||
|
||||
@@ -161,7 +161,7 @@ void TextureCacheGLES::UpdateSamplingParams(TexCacheEntry &entry, bool force) {
|
||||
render_->SetTextureSampler(0, sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT, tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT, MagFiltGL[magFilt], MinFiltGL[minFilt], aniso);
|
||||
}
|
||||
|
||||
void TextureCacheGLES::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight) {
|
||||
void TextureCacheGLES::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight, bool forcePoint) {
|
||||
int minFilt;
|
||||
int magFilt;
|
||||
bool sClamp;
|
||||
@@ -171,6 +171,10 @@ void TextureCacheGLES::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferH
|
||||
GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, 0, 0, mode);
|
||||
|
||||
minFilt &= 1; // framebuffers can't mipmap.
|
||||
if (forcePoint) {
|
||||
minFilt &= ~1;
|
||||
magFilt &= ~1;
|
||||
}
|
||||
|
||||
// Often the framebuffer will not match the texture size. We'll wrap/clamp in the shader in that case.
|
||||
// This happens whether we have OES_texture_npot or not.
|
||||
@@ -324,6 +328,7 @@ void TextureCacheGLES::BindTexture(TexCacheEntry *entry) {
|
||||
lastBoundTexture = entry->textureName;
|
||||
}
|
||||
UpdateSamplingParams(*entry, false);
|
||||
gstate_c.useShaderDepal = false;
|
||||
}
|
||||
|
||||
void TextureCacheGLES::Unbind() {
|
||||
@@ -434,7 +439,33 @@ protected:
|
||||
void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) {
|
||||
DepalShader *depal = nullptr;
|
||||
uint32_t clutMode = gstate.clutformat & 0xFFFFFF;
|
||||
|
||||
#if 0
|
||||
bool useShaderDepal = gstate_c.Supports(GPU_SUPPORTS_GLSL_ES_300);
|
||||
#else
|
||||
bool useShaderDepal = false;
|
||||
#endif
|
||||
|
||||
if ((entry->status & TexCacheEntry::STATUS_DEPALETTIZE) && !g_Config.bDisableSlowFramebufEffects) {
|
||||
if (useShaderDepal) {
|
||||
const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat();
|
||||
GLRTexture *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_);
|
||||
render_->BindTexture(TEX_SLOT_CLUT, clutTexture);
|
||||
framebufferManagerGL_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET);
|
||||
SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight, true);
|
||||
InvalidateLastTexture();
|
||||
|
||||
// Since we started/ended render passes, might need these.
|
||||
gstate_c.Dirty(DIRTY_DEPAL);
|
||||
gstate_c.useShaderDepal = true;
|
||||
gstate_c.depalFramebufferFormat = framebuffer->drawnFormat;
|
||||
const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16);
|
||||
const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor;
|
||||
TexCacheEntry::TexStatus alphaStatus = CheckAlpha((const uint8_t *)clutBuf_, getClutDestFormat(clutFormat), clutTotalColors, clutTotalColors, 1);
|
||||
gstate_c.SetTextureFullAlpha(alphaStatus == TexCacheEntry::STATUS_ALPHA_FULL);
|
||||
return;
|
||||
}
|
||||
|
||||
depal = depalShaderCache_->GetDepalettizeShader(clutMode, framebuffer->drawnFormat);
|
||||
}
|
||||
if (depal) {
|
||||
@@ -472,7 +503,7 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram
|
||||
}
|
||||
|
||||
framebufferManagerGL_->RebindFramebuffer();
|
||||
SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight);
|
||||
SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight, false);
|
||||
|
||||
InvalidateLastTexture();
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight);
|
||||
void SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight, bool forcePoint);
|
||||
bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) override;
|
||||
|
||||
void DeviceLost();
|
||||
|
||||
+1
-1
@@ -161,7 +161,7 @@ const CommonCommandTableEntry commonCommandTable[] = {
|
||||
// These must flush on change, so that LoadClut doesn't have to always flush.
|
||||
{ GE_CMD_CLUTADDR, FLAG_FLUSHBEFOREONCHANGE },
|
||||
{ GE_CMD_CLUTADDRUPPER, FLAG_FLUSHBEFOREONCHANGE },
|
||||
{ GE_CMD_CLUTFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS },
|
||||
{ GE_CMD_CLUTFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS | DIRTY_DEPAL },
|
||||
|
||||
// Morph weights. TODO: Remove precomputation?
|
||||
{ GE_CMD_MORPHWEIGHT0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight },
|
||||
|
||||
@@ -600,6 +600,9 @@ struct GPUStateCache {
|
||||
int spline_type_u;
|
||||
int spline_type_v;
|
||||
|
||||
bool useShaderDepal;
|
||||
GEBufferFormat depalFramebufferFormat;
|
||||
|
||||
u32 getRelativeAddress(u32 data) const;
|
||||
void Reset();
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
@@ -64,10 +64,11 @@ enum { VAI_KILL_AGE = 120, VAI_UNRELIABLE_KILL_AGE = 240, VAI_UNRELIABLE_KILL_MA
|
||||
enum {
|
||||
DRAW_BINDING_TEXTURE = 0,
|
||||
DRAW_BINDING_2ND_TEXTURE = 1,
|
||||
DRAW_BINDING_DYNUBO_BASE = 2,
|
||||
DRAW_BINDING_DYNUBO_LIGHT = 3,
|
||||
DRAW_BINDING_DYNUBO_BONE = 4,
|
||||
DRAW_BINDING_TESS_STORAGE_BUF = 5,
|
||||
DRAW_BINDING_DEPAL_TEXTURE = 2,
|
||||
DRAW_BINDING_DYNUBO_BASE = 3,
|
||||
DRAW_BINDING_DYNUBO_LIGHT = 4,
|
||||
DRAW_BINDING_DYNUBO_BONE = 5,
|
||||
DRAW_BINDING_TESS_STORAGE_BUF = 6,
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -95,7 +96,7 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *dra
|
||||
|
||||
void DrawEngineVulkan::InitDeviceObjects() {
|
||||
// All resources we need for PSP drawing. Usually only bindings 0 and 2-4 are populated.
|
||||
VkDescriptorSetLayoutBinding bindings[6]{};
|
||||
VkDescriptorSetLayoutBinding bindings[7]{};
|
||||
bindings[0].descriptorCount = 1;
|
||||
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
bindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
@@ -105,22 +106,26 @@ void DrawEngineVulkan::InitDeviceObjects() {
|
||||
bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
bindings[1].binding = DRAW_BINDING_2ND_TEXTURE;
|
||||
bindings[2].descriptorCount = 1;
|
||||
bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
bindings[2].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
bindings[2].binding = DRAW_BINDING_DYNUBO_BASE;
|
||||
bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; // sampler is ignored though.
|
||||
bindings[2].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
bindings[2].binding = DRAW_BINDING_DEPAL_TEXTURE;
|
||||
bindings[3].descriptorCount = 1;
|
||||
bindings[3].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
bindings[3].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
bindings[3].binding = DRAW_BINDING_DYNUBO_LIGHT;
|
||||
bindings[3].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
bindings[3].binding = DRAW_BINDING_DYNUBO_BASE;
|
||||
bindings[4].descriptorCount = 1;
|
||||
bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
bindings[4].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
bindings[4].binding = DRAW_BINDING_DYNUBO_BONE;
|
||||
// Used only for hardware tessellation.
|
||||
bindings[4].binding = DRAW_BINDING_DYNUBO_LIGHT;
|
||||
bindings[5].descriptorCount = 1;
|
||||
bindings[5].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
bindings[5].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
bindings[5].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
bindings[5].binding = DRAW_BINDING_TESS_STORAGE_BUF;
|
||||
bindings[5].binding = DRAW_BINDING_DYNUBO_BONE;
|
||||
// Used only for hardware tessellation.
|
||||
bindings[6].descriptorCount = 1;
|
||||
bindings[6].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
bindings[6].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
bindings[6].binding = DRAW_BINDING_TESS_STORAGE_BUF;
|
||||
|
||||
VkDevice device = vulkan_->GetDevice();
|
||||
|
||||
@@ -367,7 +372,7 @@ VkResult DrawEngineVulkan::RecreateDescriptorPool(FrameData &frame, int newSize)
|
||||
VkDescriptorPoolSize dpTypes[3];
|
||||
dpTypes[0].descriptorCount = frame.descPoolSize * 3;
|
||||
dpTypes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
dpTypes[1].descriptorCount = frame.descPoolSize * 2; // Don't use these for tess anymore, need max two per set.
|
||||
dpTypes[1].descriptorCount = frame.descPoolSize * 3; // Don't use these for tess anymore, need max three per set.
|
||||
dpTypes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
dpTypes[2].descriptorCount = frame.descPoolSize;
|
||||
dpTypes[2].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
@@ -388,6 +393,7 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView
|
||||
key.imageView_ = imageView;
|
||||
key.sampler_ = sampler;
|
||||
key.secondaryImageView_ = boundSecondary_;
|
||||
key.depalImageView_ = boundDepal_;
|
||||
key.base_ = base;
|
||||
key.light_ = light;
|
||||
key.bone_ = bone;
|
||||
@@ -433,12 +439,11 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView
|
||||
// Even in release mode, this is bad.
|
||||
_assert_msg_(G3D, result == VK_SUCCESS, "Ran out of descriptor space in pool. sz=%d res=%d", (int)frame.descSets.size(), (int)result);
|
||||
|
||||
// We just don't write to the slots we don't care about.
|
||||
// We need 8 now that we support secondary texture bindings.
|
||||
VkWriteDescriptorSet writes[8]{};
|
||||
// We just don't write to the slots we don't care about, which is fine.
|
||||
VkWriteDescriptorSet writes[7]{};
|
||||
// Main texture
|
||||
int n = 0;
|
||||
VkDescriptorImageInfo tex[2]{};
|
||||
VkDescriptorImageInfo tex[3]{};
|
||||
if (imageView) {
|
||||
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR
|
||||
tex[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
@@ -459,7 +464,7 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView
|
||||
|
||||
if (boundSecondary_) {
|
||||
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR
|
||||
tex[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
tex[1].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
#else
|
||||
tex[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
#endif
|
||||
@@ -475,7 +480,23 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView
|
||||
n++;
|
||||
}
|
||||
|
||||
// Skipping 2nd texture for now.
|
||||
if (boundDepal_) {
|
||||
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR
|
||||
tex[2].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
#else
|
||||
tex[2].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
#endif
|
||||
tex[2].imageView = boundDepal_;
|
||||
tex[2].sampler = samplerSecondary_; // doesn't matter, we use load
|
||||
writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
writes[n].pNext = nullptr;
|
||||
writes[n].dstBinding = DRAW_BINDING_DEPAL_TEXTURE;
|
||||
writes[n].pImageInfo = &tex[2];
|
||||
writes[n].descriptorCount = 1;
|
||||
writes[n].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
writes[n].dstSet = desc;
|
||||
n++;
|
||||
}
|
||||
|
||||
// Tessellation data buffer. Make sure this is declared outside the if to avoid optimizer
|
||||
// shenanigans.
|
||||
|
||||
@@ -20,10 +20,12 @@
|
||||
// The Descriptor Set used for the majority of PSP rendering looks like this:
|
||||
//
|
||||
// * binding 0: Texture/Sampler (the PSP texture)
|
||||
// * binding 1: Secondary texture sampler for shader blending or depal palettes
|
||||
// * binding 2: Base Uniform Buffer (includes fragment state)
|
||||
// * binding 3: Light uniform buffer
|
||||
// * binding 4: Bone uniform buffer
|
||||
// * binding 1: Secondary texture sampler for shader blending
|
||||
// * binding 2: Depal palette
|
||||
// * binding 3: Base Uniform Buffer (includes fragment state)
|
||||
// * binding 4: Light uniform buffer
|
||||
// * binding 5: Bone uniform buffer
|
||||
// * binding 6: Tess data storage buffer
|
||||
//
|
||||
// All shaders conform to this layout, so they are all compatible with the same descriptor set.
|
||||
// The format of the various uniform buffers may vary though - vertex shaders that don't skin
|
||||
@@ -177,6 +179,9 @@ public:
|
||||
}
|
||||
|
||||
void SetLineWidth(float lineWidth);
|
||||
void SetDepalTexture(VkImageView depal) {
|
||||
boundDepal_ = depal;
|
||||
}
|
||||
|
||||
private:
|
||||
struct FrameData;
|
||||
@@ -207,6 +212,7 @@ private:
|
||||
|
||||
// Secondary texture for shader blending
|
||||
VkImageView boundSecondary_ = VK_NULL_HANDLE;
|
||||
VkImageView boundDepal_ = VK_NULL_HANDLE;
|
||||
VkSampler samplerSecondary_ = VK_NULL_HANDLE; // This one is actually never used since we use fetch.
|
||||
|
||||
PrehashMap<VertexArrayInfoVulkan *, nullptr> vai_;
|
||||
@@ -217,6 +223,7 @@ private:
|
||||
struct DescriptorSetKey {
|
||||
VkImageView imageView_;
|
||||
VkImageView secondaryImageView_;
|
||||
VkImageView depalImageView_;
|
||||
VkSampler sampler_;
|
||||
VkBuffer base_, light_, bone_; // All three UBO slots will be set to this. This will usually be identical
|
||||
// for all draws in a frame, except when the buffer has to grow.
|
||||
|
||||
@@ -60,6 +60,7 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) {
|
||||
bool doTextureProjection = id.Bit(FS_BIT_DO_TEXTURE_PROJ);
|
||||
bool doTextureAlpha = id.Bit(FS_BIT_TEXALPHA);
|
||||
bool doFlatShading = id.Bit(FS_BIT_FLATSHADE);
|
||||
bool shaderDepal = id.Bit(FS_BIT_SHADER_DEPAL);
|
||||
|
||||
GEComparison alphaTestFunc = (GEComparison)id.Bits(FS_BIT_ALPHA_TEST_FUNC, 3);
|
||||
GEComparison colorTestFunc = (GEComparison)id.Bits(FS_BIT_COLOR_TEST_FUNC, 2);
|
||||
@@ -80,7 +81,7 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) {
|
||||
|
||||
const char *shading = doFlatShading ? "flat" : "";
|
||||
|
||||
WRITE(p, "layout (std140, set = 0, binding = 2) uniform baseUBO {\n%s} base;\n", ub_baseStr);
|
||||
WRITE(p, "layout (std140, set = 0, binding = 3) uniform baseUBO {\n%s} base;\n", ub_baseStr);
|
||||
if (doTexture) {
|
||||
WRITE(p, "layout (binding = 0) uniform sampler2D tex;\n");
|
||||
}
|
||||
@@ -91,6 +92,10 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
if (shaderDepal) {
|
||||
WRITE(p, "layout (binding = 2) uniform sampler2D pal;\n");
|
||||
}
|
||||
|
||||
WRITE(p, "layout (location = 1) %s in vec4 v_color0;\n", shading);
|
||||
if (lmode)
|
||||
WRITE(p, "layout (location = 2) %s in vec3 v_color1;\n", shading);
|
||||
@@ -172,10 +177,95 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) {
|
||||
|
||||
if (doTextureProjection) {
|
||||
WRITE(p, " vec4 t = textureProj(tex, %s);\n", texcoord);
|
||||
if (shaderDepal) {
|
||||
WRITE(p, " vec4 t1 = textureProjOffset(tex, %s, ivec2(1, 0));\n", texcoord);
|
||||
WRITE(p, " vec4 t2 = textureProjOffset(tex, %s, ivec2(0, 1));\n", texcoord);
|
||||
WRITE(p, " vec4 t3 = textureProjOffset(tex, %s, ivec2(1, 1));\n", texcoord);
|
||||
}
|
||||
} else {
|
||||
WRITE(p, " vec4 t = texture(tex, %s.xy);\n", texcoord);
|
||||
if (shaderDepal) {
|
||||
WRITE(p, " vec4 t1 = textureOffset(tex, %s.xy, ivec2(1, 0));\n", texcoord);
|
||||
WRITE(p, " vec4 t2 = textureOffset(tex, %s.xy, ivec2(0, 1));\n", texcoord);
|
||||
WRITE(p, " vec4 t3 = textureOffset(tex, %s.xy, ivec2(1, 1));\n", texcoord);
|
||||
}
|
||||
}
|
||||
WRITE(p, " vec4 p = v_color0;\n");
|
||||
|
||||
if (shaderDepal) {
|
||||
WRITE(p, " uint depalMask = (base.depal_mask_shift_off_fmt & 0xFF);\n");
|
||||
WRITE(p, " uint depalShift = (base.depal_mask_shift_off_fmt >> 8) & 0xFF;\n");
|
||||
WRITE(p, " uint depalOffset = ((base.depal_mask_shift_off_fmt >> 16) & 0xFF) << 4;\n");
|
||||
WRITE(p, " uint depalFmt = (base.depal_mask_shift_off_fmt >> 24) & 0x3;\n");
|
||||
WRITE(p, " bool bilinear = (base.depal_mask_shift_off_fmt >> 31) != 0;\n");
|
||||
WRITE(p, " vec2 fraction = fract(%s.xy * vec2(textureSize(tex, 0).xy));\n", texcoord);
|
||||
WRITE(p, " uvec4 col; uint index0; uint index1; uint index2; uint index3;\n");
|
||||
WRITE(p, " switch (depalFmt) {\n"); // We might want to include fmt in the shader ID if this is a performance issue.
|
||||
WRITE(p, " case 0:\n"); // 565
|
||||
WRITE(p, " col = uvec4(t.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
|
||||
WRITE(p, " index0 = (col.b << 11) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " if (bilinear) {\n");
|
||||
WRITE(p, " col = uvec4(t1.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
|
||||
WRITE(p, " index1 = (col.b << 11) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " col = uvec4(t2.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
|
||||
WRITE(p, " index2 = (col.b << 11) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " col = uvec4(t3.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
|
||||
WRITE(p, " index3 = (col.b << 11) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " break;\n");
|
||||
WRITE(p, " case 1:\n"); // 5551
|
||||
WRITE(p, " col = uvec4(t.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
|
||||
WRITE(p, " index0 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " if (bilinear) {\n");
|
||||
WRITE(p, " col = uvec4(t1.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
|
||||
WRITE(p, " index1 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " col = uvec4(t2.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
|
||||
WRITE(p, " index2 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " col = uvec4(t3.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
|
||||
WRITE(p, " index3 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n");
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " break;\n");
|
||||
WRITE(p, " case 2:\n"); // 4444
|
||||
WRITE(p, " col = uvec4(t.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n");
|
||||
WRITE(p, " index0 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n");
|
||||
WRITE(p, " if (bilinear) {\n");
|
||||
WRITE(p, " col = uvec4(t1.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n");
|
||||
WRITE(p, " index1 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n");
|
||||
WRITE(p, " col = uvec4(t2.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n");
|
||||
WRITE(p, " index2 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n");
|
||||
WRITE(p, " col = uvec4(t3.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n");
|
||||
WRITE(p, " index3 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n");
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " break;\n");
|
||||
WRITE(p, " case 3:\n"); // 8888
|
||||
WRITE(p, " col = uvec4(t.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n");
|
||||
WRITE(p, " index0 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n");
|
||||
WRITE(p, " if (bilinear) {\n");
|
||||
WRITE(p, " col = uvec4(t1.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n");
|
||||
WRITE(p, " index1 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n");
|
||||
WRITE(p, " col = uvec4(t2.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n");
|
||||
WRITE(p, " index2 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n");
|
||||
WRITE(p, " col = uvec4(t3.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n");
|
||||
WRITE(p, " index3 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n");
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " break;\n");
|
||||
WRITE(p, " };\n");
|
||||
WRITE(p, " index0 = ((index0 >> depalShift) & depalMask) | depalOffset;\n");
|
||||
WRITE(p, " t = texelFetch(pal, ivec2(index0, 0), 0);\n");
|
||||
WRITE(p, " if (bilinear) {\n");
|
||||
WRITE(p, " index1 = ((index1 >> depalShift) & depalMask) | depalOffset;\n");
|
||||
WRITE(p, " index2 = ((index2 >> depalShift) & depalMask) | depalOffset;\n");
|
||||
WRITE(p, " index3 = ((index3 >> depalShift) & depalMask) | depalOffset;\n");
|
||||
WRITE(p, " t1 = texelFetch(pal, ivec2(index1, 0), 0);\n");
|
||||
WRITE(p, " t2 = texelFetch(pal, ivec2(index2, 0), 0);\n");
|
||||
WRITE(p, " t3 = texelFetch(pal, ivec2(index3, 0), 0);\n");
|
||||
WRITE(p, " t = mix(t, t1, fraction.x);\n");
|
||||
WRITE(p, " t2 = mix(t2, t3, fraction.x);\n");
|
||||
WRITE(p, " t = mix(t, t2, fraction.y);\n");
|
||||
WRITE(p, " }\n");
|
||||
}
|
||||
|
||||
if (texFunc != GE_TEXFUNC_REPLACE || !doTextureAlpha)
|
||||
WRITE(p, " vec4 p = v_color0;\n");
|
||||
|
||||
if (doTextureAlpha) { // texfmt == RGBA
|
||||
switch (texFunc) {
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
#include "GPU/Vulkan/FramebufferVulkan.h"
|
||||
#include "GPU/Vulkan/DrawEngineVulkan.h"
|
||||
#include "GPU/Vulkan/TextureCacheVulkan.h"
|
||||
#include "thin3d/VulkanRenderManager.h"
|
||||
#include "thin3d/VulkanQueueRunner.h"
|
||||
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/HLE/sceKernelThread.h"
|
||||
@@ -456,6 +458,16 @@ void GPU_Vulkan::InitDeviceObjects() {
|
||||
assert(!frameData_[i].push_);
|
||||
frameData_[i].push_ = new VulkanPushBuffer(vulkan_, 64 * 1024);
|
||||
}
|
||||
|
||||
VulkanRenderManager *rm = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
uint32_t hacks = 0;
|
||||
if (PSP_CoreParameter().compat.flags().MGS2AcidHack)
|
||||
hacks |= QUEUE_HACK_MGS2_ACID;
|
||||
if (PSP_CoreParameter().compat.flags().SonicRivalsHack)
|
||||
hacks |= QUEUE_HACK_SONIC;
|
||||
if (hacks) {
|
||||
rm->GetQueueRunner()->EnableHacks(hacks);
|
||||
}
|
||||
}
|
||||
|
||||
void GPU_Vulkan::DestroyDeviceObjects() {
|
||||
@@ -467,6 +479,10 @@ void GPU_Vulkan::DestroyDeviceObjects() {
|
||||
frameData_[i].push_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Need to turn off hacks when shutting down the GPU. Don't want them running in the menu.
|
||||
VulkanRenderManager *rm = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
||||
rm->GetQueueRunner()->EnableHacks(0);
|
||||
}
|
||||
|
||||
void GPU_Vulkan::DeviceLost() {
|
||||
|
||||
@@ -62,6 +62,7 @@ VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id,
|
||||
ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str());
|
||||
ERROR_LOG(G3D, "Shader source:\n%s", code);
|
||||
#ifdef SHADERLOG
|
||||
OutputDebugStringA(LineNumberString(code).c_str());
|
||||
OutputDebugStringA("Messages:\n");
|
||||
OutputDebugStringA(errorMessage.c_str());
|
||||
#endif
|
||||
@@ -116,8 +117,11 @@ VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, VShaderID id, cons
|
||||
}
|
||||
ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str());
|
||||
ERROR_LOG(G3D, "Shader source:\n%s", code);
|
||||
#ifdef SHADERLOG
|
||||
OutputDebugStringA(LineNumberString(code).c_str());
|
||||
OutputDebugStringUTF8("Messages:\n");
|
||||
OutputDebugStringUTF8(errorMessage.c_str());
|
||||
#endif
|
||||
Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code);
|
||||
} else {
|
||||
success = vulkan_->CreateShaderModule(spirv, &module_);
|
||||
@@ -354,7 +358,7 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM
|
||||
// instantaneous.
|
||||
|
||||
#define CACHE_HEADER_MAGIC 0xff51f420
|
||||
#define CACHE_VERSION 12
|
||||
#define CACHE_VERSION 13
|
||||
struct VulkanCacheHeader {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
|
||||
@@ -327,6 +327,8 @@ void TextureCacheVulkan::BindTexture(TexCacheEntry *entry) {
|
||||
SamplerCacheKey key{};
|
||||
UpdateSamplingParams(*entry, key);
|
||||
curSampler_ = samplerCache_.GetOrCreateSampler(key);
|
||||
drawEngine_->SetDepalTexture(VK_NULL_HANDLE);
|
||||
gstate_c.useShaderDepal = false;
|
||||
}
|
||||
|
||||
void TextureCacheVulkan::Unbind() {
|
||||
@@ -336,10 +338,39 @@ void TextureCacheVulkan::Unbind() {
|
||||
}
|
||||
|
||||
void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) {
|
||||
SamplerCacheKey samplerKey{};
|
||||
SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight, samplerKey);
|
||||
|
||||
DepalShaderVulkan *depalShader = nullptr;
|
||||
uint32_t clutMode = gstate.clutformat & 0xFFFFFF;
|
||||
|
||||
bool useShaderDepal = true;
|
||||
|
||||
if ((entry->status & TexCacheEntry::STATUS_DEPALETTIZE) && !g_Config.bDisableSlowFramebufEffects) {
|
||||
depalShader = depalShaderCache_->GetDepalettizeShader(clutMode, framebuffer->drawnFormat);
|
||||
if (useShaderDepal) {
|
||||
depalShaderCache_->SetPushBuffer(drawEngine_->GetPushBufferForTextureData());
|
||||
const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat();
|
||||
VulkanTexture *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_);
|
||||
drawEngine_->SetDepalTexture(clutTexture->GetImageView());
|
||||
// Only point filtering enabled.
|
||||
samplerKey.magFilt = false;
|
||||
samplerKey.minFilt = false;
|
||||
samplerKey.mipFilt = false;
|
||||
// Make sure to update the uniforms.
|
||||
gstate_c.Dirty(DIRTY_DEPAL);
|
||||
gstate_c.useShaderDepal = true;
|
||||
gstate_c.depalFramebufferFormat = framebuffer->drawnFormat;
|
||||
const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16);
|
||||
const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor;
|
||||
TexCacheEntry::TexStatus alphaStatus = CheckAlpha(clutBuf_, getClutDestFormatVulkan(clutFormat), clutTotalColors, clutTotalColors, 1);
|
||||
gstate_c.SetTextureFullAlpha(alphaStatus == TexCacheEntry::STATUS_ALPHA_FULL);
|
||||
curSampler_ = samplerCache_.GetOrCreateSampler(samplerKey);
|
||||
InvalidateLastTexture(entry);
|
||||
imageView_ = framebufferManagerVulkan_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET);
|
||||
return;
|
||||
} else {
|
||||
depalShader = depalShaderCache_->GetDepalettizeShader(clutMode, framebuffer->drawnFormat);
|
||||
}
|
||||
}
|
||||
if (depalShader) {
|
||||
depalShaderCache_->SetPushBuffer(drawEngine_->GetPushBufferForTextureData());
|
||||
@@ -430,14 +461,11 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr
|
||||
} else {
|
||||
entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE;
|
||||
|
||||
framebufferManager_->RebindFramebuffer(); // TODO: This line should not be needed?
|
||||
framebufferManager_->RebindFramebuffer(); // TODO: This line should usually not be needed.
|
||||
imageView_ = framebufferManagerVulkan_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET);
|
||||
|
||||
gstate_c.SetTextureFullAlpha(gstate.getTextureFormat() == GE_TFMT_5650);
|
||||
}
|
||||
|
||||
SamplerCacheKey samplerKey{};
|
||||
SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight, samplerKey);
|
||||
curSampler_ = samplerCache_.GetOrCreateSampler(samplerKey);
|
||||
InvalidateLastTexture(entry);
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ public:
|
||||
lastBoundTexture = nullptr;
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
|
||||
}
|
||||
|
||||
void InvalidateLastTexture(TexCacheEntry *entry = nullptr) override {
|
||||
if (!entry || entry->vkTex == lastBoundTexture) {
|
||||
lastBoundTexture = nullptr;
|
||||
|
||||
@@ -137,11 +137,11 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) {
|
||||
bool flipNormalTess = id.Bit(VS_BIT_NORM_REVERSE_TESS);
|
||||
|
||||
WRITE(p, "\n");
|
||||
WRITE(p, "layout (std140, set = 0, binding = 2) uniform baseVars {\n%s} base;\n", ub_baseStr);
|
||||
WRITE(p, "layout (std140, set = 0, binding = 3) uniform baseVars {\n%s} base;\n", ub_baseStr);
|
||||
if (enableLighting || doShadeMapping)
|
||||
WRITE(p, "layout (std140, set = 0, binding = 3) uniform lightVars {\n%s} light;\n", ub_vs_lightsStr);
|
||||
WRITE(p, "layout (std140, set = 0, binding = 4) uniform lightVars {\n%s} light;\n", ub_vs_lightsStr);
|
||||
if (enableBones)
|
||||
WRITE(p, "layout (std140, set = 0, binding = 4) uniform boneVars {\n%s} bone;\n", ub_vs_bonesStr);
|
||||
WRITE(p, "layout (std140, set = 0, binding = 5) uniform boneVars {\n%s} bone;\n", ub_vs_bonesStr);
|
||||
|
||||
const char *shading = doFlatShading ? "flat " : "";
|
||||
|
||||
@@ -221,7 +221,7 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) {
|
||||
WRITE(p, " vec4 uv;\n");
|
||||
WRITE(p, " vec4 color;\n");
|
||||
WRITE(p, "};");
|
||||
WRITE(p, "layout (std430, set = 0, binding = 5) buffer s_tess_data {\n");
|
||||
WRITE(p, "layout (std430, set = 0, binding = 6) buffer s_tess_data {\n");
|
||||
WRITE(p, " TessData data[];");
|
||||
WRITE(p, "} tess_data;\n");
|
||||
|
||||
|
||||
+15
-1
@@ -325,4 +325,18 @@ NPHG00092 = true
|
||||
NPEG00044 = true
|
||||
NPJG00120 = true
|
||||
UCJS10114 = true
|
||||
UCES01401 = true
|
||||
UCES01401 = true
|
||||
|
||||
[MGS2AcidHack]
|
||||
ULES00008 = true
|
||||
ULJM08001 = true
|
||||
ULJM05001 = true
|
||||
ULAS42007 = true
|
||||
ULUS10006 = true
|
||||
ULUS10077 = true
|
||||
|
||||
[SonicRivalsHack]
|
||||
ULES00622 = true # SR1
|
||||
ULUS10195 = true # SR1
|
||||
ULUS10323 = true # SR2
|
||||
ULES00940 = true # SR2
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "GLRenderManager.h"
|
||||
#include "DataFormatGL.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/stringutil.h"
|
||||
#include "gfx/gl_common.h"
|
||||
#include "gfx/gl_debug_log.h"
|
||||
#include "gfx_es2/gpu_features.h"
|
||||
@@ -156,9 +157,10 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps) {
|
||||
|
||||
#ifdef _WIN32
|
||||
OutputDebugStringUTF8(buf);
|
||||
OutputDebugStringUTF8(vsCode);
|
||||
if (vsCode)
|
||||
OutputDebugStringUTF8(LineNumberString(vsCode).c_str());
|
||||
if (fsCode)
|
||||
OutputDebugStringUTF8(fsCode);
|
||||
OutputDebugStringUTF8(LineNumberString(fsCode).c_str());
|
||||
#endif
|
||||
delete[] buf;
|
||||
} else {
|
||||
|
||||
@@ -472,6 +472,7 @@ void GLRenderManager::Run(int frame) {
|
||||
auto &initStepsOnThread = frameData_[frame].initSteps;
|
||||
// queueRunner_.LogSteps(stepsOnThread);
|
||||
queueRunner_.RunInitSteps(initStepsOnThread);
|
||||
initStepsOnThread.clear();
|
||||
|
||||
// Run this after RunInitSteps so any fresh GLRBuffers for the pushbuffers can get created.
|
||||
for (auto iter : frameData.activePushBuffers) {
|
||||
@@ -481,7 +482,6 @@ void GLRenderManager::Run(int frame) {
|
||||
|
||||
queueRunner_.RunSteps(stepsOnThread);
|
||||
stepsOnThread.clear();
|
||||
initStepsOnThread.clear();
|
||||
|
||||
for (auto iter : frameData.activePushBuffers) {
|
||||
iter->MapDevice(bufferStrategy_);
|
||||
|
||||
@@ -347,7 +347,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) {
|
||||
return pass;
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, const std::vector<VKRStep *> &steps) {
|
||||
void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector<VKRStep *> &steps) {
|
||||
// Optimizes renderpasses, then sequences them.
|
||||
// Planned optimizations:
|
||||
// * Create copies of render target that are rendered to multiple times and textured from in sequence, and push those render passes
|
||||
@@ -397,6 +397,17 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, const std::vector<VKRStep
|
||||
}
|
||||
}
|
||||
|
||||
// Queue hacks.
|
||||
if (hacksEnabled_) {
|
||||
if (hacksEnabled_ & QUEUE_HACK_MGS2_ACID) {
|
||||
// Massive speedup.
|
||||
ApplyMGSHack(steps);
|
||||
}
|
||||
if (hacksEnabled_ & QUEUE_HACK_SONIC) {
|
||||
ApplySonicHack(steps);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < steps.size(); i++) {
|
||||
const VKRStep &step = *steps[i];
|
||||
switch (step.stepType) {
|
||||
@@ -422,6 +433,146 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, const std::vector<VKRStep
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::ApplyMGSHack(std::vector<VKRStep *> &steps) {
|
||||
// We want to turn a sequence of copy,render(1),copy,render(1),copy,render(1) to copy,copy,copy,render(n).
|
||||
|
||||
for (int i = 0; i < (int)steps.size() - 3; i++) {
|
||||
int last = -1;
|
||||
if (!(steps[i]->stepType == VKRStepType::COPY &&
|
||||
steps[i + 1]->stepType == VKRStepType::RENDER &&
|
||||
steps[i + 2]->stepType == VKRStepType::COPY &&
|
||||
steps[i + 1]->render.numDraws == 1 &&
|
||||
steps[i]->copy.dst == steps[i + 2]->copy.dst))
|
||||
continue;
|
||||
// Looks promising! Let's start by finding the last one.
|
||||
for (int j = i; j < (int)steps.size(); j++) {
|
||||
switch (steps[j]->stepType) {
|
||||
case VKRStepType::RENDER:
|
||||
if (steps[j]->render.numDraws > 1)
|
||||
last = j - 1;
|
||||
break;
|
||||
case VKRStepType::COPY:
|
||||
if (steps[j]->copy.dst != steps[i]->copy.dst)
|
||||
last = j - 1;
|
||||
break;
|
||||
}
|
||||
if (last != -1)
|
||||
break;
|
||||
}
|
||||
|
||||
if (last != -1) {
|
||||
// We've got a sequence from i to last that needs reordering.
|
||||
// First, let's sort it, keeping the same length.
|
||||
std::vector<VKRStep *> copies;
|
||||
std::vector<VKRStep *> renders;
|
||||
copies.reserve((last - i) / 2);
|
||||
renders.reserve((last - i) / 2);
|
||||
for (int n = i; n <= last; n++) {
|
||||
if (steps[n]->stepType == VKRStepType::COPY)
|
||||
copies.push_back(steps[n]);
|
||||
else if (steps[n]->stepType == VKRStepType::RENDER)
|
||||
renders.push_back(steps[n]);
|
||||
}
|
||||
// Write the copies back. TODO: Combine them too.
|
||||
for (int j = 0; j < (int)copies.size(); j++) {
|
||||
steps[i + j] = copies[j];
|
||||
}
|
||||
// Write the renders back (so they will be deleted properly).
|
||||
for (int j = 0; j < (int)renders.size(); j++) {
|
||||
steps[i + j + copies.size()] = renders[j];
|
||||
}
|
||||
assert(steps[i + copies.size()]->stepType == VKRStepType::RENDER);
|
||||
// Combine the renders.
|
||||
for (int j = 1; j < (int)renders.size(); j++) {
|
||||
for (int k = 0; k < renders[j]->commands.size(); k++) {
|
||||
steps[i + copies.size()]->commands.push_back(renders[j]->commands[k]);
|
||||
}
|
||||
steps[i + copies.size() + j]->stepType = VKRStepType::RENDER_SKIP;
|
||||
}
|
||||
// We're done.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::ApplySonicHack(std::vector<VKRStep *> &steps) {
|
||||
// We want to turn a sequence of render(3),render(1),render(6),render(1),render(6),render(1),render(3) to
|
||||
// render(1), render(1), render(1), render(6), render(6), render(6)
|
||||
|
||||
for (int i = 0; i < (int)steps.size() - 4; i++) {
|
||||
int last = -1;
|
||||
if (!(steps[i]->stepType == VKRStepType::RENDER &&
|
||||
steps[i + 1]->stepType == VKRStepType::RENDER &&
|
||||
steps[i + 2]->stepType == VKRStepType::RENDER &&
|
||||
steps[i + 3]->stepType == VKRStepType::RENDER &&
|
||||
steps[i]->render.numDraws == 3 &&
|
||||
steps[i + 1]->render.numDraws == 1 &&
|
||||
steps[i + 2]->render.numDraws == 6 &&
|
||||
steps[i + 3]->render.numDraws == 1 &&
|
||||
steps[i]->render.framebuffer == steps[i + 2]->render.framebuffer &&
|
||||
steps[i + 1]->render.framebuffer == steps[i + 3]->render.framebuffer))
|
||||
continue;
|
||||
// Looks promising! Let's start by finding the last one.
|
||||
for (int j = i; j < (int)steps.size(); j++) {
|
||||
switch (steps[j]->stepType) {
|
||||
case VKRStepType::RENDER:
|
||||
if ((j - i) & 1) {
|
||||
if (steps[j]->render.framebuffer != steps[i + 1]->render.framebuffer)
|
||||
last = j - 1;
|
||||
if (steps[j]->render.numDraws != 1)
|
||||
last = j - 1;
|
||||
} else {
|
||||
if (steps[j]->render.framebuffer != steps[i]->render.framebuffer)
|
||||
last = j - 1;
|
||||
if (steps[j]->render.numDraws != 3 && steps[j]->render.numDraws != 6)
|
||||
last = j - 1;
|
||||
}
|
||||
}
|
||||
if (last != -1)
|
||||
break;
|
||||
}
|
||||
|
||||
if (last != -1) {
|
||||
// We've got a sequence from i to last that needs reordering.
|
||||
// First, let's sort it, keeping the same length.
|
||||
std::vector<VKRStep *> type1;
|
||||
std::vector<VKRStep *> type2;
|
||||
type1.reserve((last - i) / 2);
|
||||
type2.reserve((last - i) / 2);
|
||||
for (int n = i; n <= last; n++) {
|
||||
if (steps[n]->render.framebuffer == steps[i]->render.framebuffer)
|
||||
type1.push_back(steps[n]);
|
||||
else
|
||||
type2.push_back(steps[n]);
|
||||
}
|
||||
|
||||
// Write the renders back in order. Same amount, so deletion will work fine.
|
||||
for (int j = 0; j < (int)type1.size(); j++) {
|
||||
steps[i + j] = type1[j];
|
||||
}
|
||||
for (int j = 0; j < (int)type2.size(); j++) {
|
||||
steps[i + j + type1.size()] = type2[j];
|
||||
}
|
||||
|
||||
// Combine the renders.
|
||||
for (int j = 1; j < (int)type1.size(); j++) {
|
||||
for (int k = 0; k < (int)type1[j]->commands.size(); k++) {
|
||||
steps[i]->commands.push_back(type1[j]->commands[k]);
|
||||
}
|
||||
steps[i + j]->stepType = VKRStepType::RENDER_SKIP;
|
||||
}
|
||||
for (int j = 1; j < (int)type2.size(); j++) {
|
||||
for (int k = 0; k < (int)type2[j]->commands.size(); k++) {
|
||||
steps[i + type1.size()]->commands.push_back(type2[j]->commands[k]);
|
||||
}
|
||||
steps[i + j + type1.size()]->stepType = VKRStepType::RENDER_SKIP;
|
||||
}
|
||||
// We're done.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::LogSteps(const std::vector<VKRStep *> &steps) {
|
||||
ILOG("=======================================");
|
||||
for (size_t i = 0; i < steps.size(); i++) {
|
||||
|
||||
@@ -10,6 +10,11 @@
|
||||
class VKRFramebuffer;
|
||||
struct VKRImage;
|
||||
|
||||
enum {
|
||||
QUEUE_HACK_MGS2_ACID = 1,
|
||||
QUEUE_HACK_SONIC = 2,
|
||||
};
|
||||
|
||||
enum class VKRRenderCommand : uint8_t {
|
||||
BIND_PIPELINE,
|
||||
STENCIL,
|
||||
@@ -152,7 +157,9 @@ public:
|
||||
backbuffer_ = fb;
|
||||
backbufferImage_ = img;
|
||||
}
|
||||
void RunSteps(VkCommandBuffer cmd, const std::vector<VKRStep *> &steps);
|
||||
|
||||
// RunSteps can modify steps but will leave it in a valid state.
|
||||
void RunSteps(VkCommandBuffer cmd, std::vector<VKRStep *> &steps);
|
||||
void LogSteps(const std::vector<VKRStep *> &steps);
|
||||
|
||||
void CreateDeviceObjects();
|
||||
@@ -205,6 +212,10 @@ public:
|
||||
return found;
|
||||
}
|
||||
|
||||
void EnableHacks(uint32_t hacks) {
|
||||
hacksEnabled_ = hacks;
|
||||
}
|
||||
|
||||
private:
|
||||
void InitBackbufferRenderPass();
|
||||
|
||||
@@ -223,6 +234,9 @@ private:
|
||||
|
||||
void ResizeReadbackBuffer(VkDeviceSize requiredSize);
|
||||
|
||||
void ApplyMGSHack(std::vector<VKRStep *> &steps);
|
||||
void ApplySonicHack(std::vector<VKRStep *> &steps);
|
||||
|
||||
static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect);
|
||||
static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect);
|
||||
|
||||
@@ -244,4 +258,7 @@ private:
|
||||
VkDeviceMemory readbackMemory_ = VK_NULL_HANDLE;
|
||||
VkBuffer readbackBuffer_ = VK_NULL_HANDLE;
|
||||
VkDeviceSize readbackBufferSize_ = 0;
|
||||
|
||||
// TODO: Enable based on compat.ini.
|
||||
uint32_t hacksEnabled_ = 0;
|
||||
};
|
||||
Reference in new issue
Block a user