Split out screen rotation from the projection matrix

This commit is contained in:
Henrik Rydgård committed 2026-05-17 10:58:54 +02:00
1 parent 9afe66219a
commit 065a68ef4f
6 files changed
+24 -28

No files matched your search

+2 -7
View File
@@ -128,11 +128,7 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView
memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float));
FlipProjMatrix(flippedMatrix);
ConvertProjMatrixToZeroToOneDepth(flippedMatrix);
if (!useBufferedRendering && g_display.rotation != DisplayRotation::ROTATE_0) {
flippedMatrix = flippedMatrix * g_display.rot_matrix;
}
CopyMatrix4x4(ub->proj, flippedMatrix.getReadPtr());
ub->rotation = useBufferedRendering ? 0 : (float)g_display.rotation;
@@ -141,9 +137,6 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView
if (dirtyUniforms & DIRTY_PROJTHROUGHMATRIX) {
Matrix4x4 proj_through;
proj_through.setOrthoVulkan(0.0f, gstate_c.curRTWidth, 0, gstate_c.curRTHeight, 0, 1);
if (!useBufferedRendering && g_display.rotation != DisplayRotation::ROTATE_0) {
proj_through = proj_through * g_display.rot_matrix;
}
// Negative RT offsets come from split framebuffers (Killzone)
if (gstate_c.curRTOffsetX < 0 || gstate_c.curRTOffsetY < 0) {
@@ -152,6 +145,8 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView
}
CopyMatrix4x4(ub->proj_through, proj_through.getReadPtr());
ub->rotation = useBufferedRendering ? 0 : (float)g_display.rotation;
}
// Transform
+4 -1
View File
@@ -17,7 +17,7 @@ enum : uint64_t {
DIRTY_MATDIFFUSE | DIRTY_MATSPECULAR | DIRTY_MATEMISSIVE | DIRTY_AMBIENT,
};
// Currently 496 bytes.
// Currently 480 bytes.
// Every line here is a 4-float.
struct alignas(16) UB_VS_FS_Base {
float proj[16];
@@ -43,6 +43,7 @@ struct alignas(16) UB_VS_FS_Base {
// VR stuff is to go here, later. For normal drawing, we can then get away
// with just uploading the first 448 bytes of the struct (up to and including fogCoef).
};
static_assert(sizeof(UB_VS_FS_Base) == 480, "UB_VS_FS_Base should be 496 bytes");
static const char * const ub_baseStr =
R"( mat4 u_proj;
@@ -84,6 +85,7 @@ struct alignas(16) UB_VS_Lights {
float lightDiffuse[4][4];
float lightSpecular[4][4];
};
static_assert(sizeof(UB_VS_Lights) == 512); // it's ok to optimize this, it's just an assumption check.
static const char * const ub_vs_lightsStr =
R"( vec4 u_ambient;
@@ -105,6 +107,7 @@ R"( vec4 u_ambient;
struct alignas(16) UB_VS_Bones {
float bones[8][12];
};
static_assert(sizeof(UB_VS_Bones) == 384); // No way to optimize this further.
static const char * const ub_vs_bonesStr =
R"( mat3x4 u_bone0; mat3x4 u_bone1; mat3x4 u_bone2; mat3x4 u_bone3; mat3x4 u_bone4; mat3x4 u_bone5; mat3x4 u_bone6; mat3x4 u_bone7; mat3x4 u_bone8;
+13 -17
View File
@@ -363,6 +363,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
}
WRITE(p, "};\n");
} else {
// Non-Vulkan GLSL.
if (enableBones) {
const char * const * boneWeightDecl = boneWeightAttrDecl;
if (!strcmp(compat.attribute, "in")) {
@@ -479,8 +480,6 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
WRITE(p, "uniform lowp vec3 u_matemissive;\n");
*uniformMask |= DIRTY_MATSPECULAR | DIRTY_MATEMISSIVE;
}
} else {
WRITE(p, "uniform lowp float u_rotation;\n");
}
if (gstate_c.Use(GPU_USE_VIRTUAL_REALITY)) {
@@ -749,24 +748,11 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
// The proj_through matrix already has the rotation, if needed.
WRITE(p, " vec4 outPos = mul(u_proj_through, vec4(position.xyz, 1.0));\n");
} else {
if (compat.shaderLanguage == GLSL_VULKAN) {
// Apply rotation from the uniform.
WRITE(p, " mat2 displayRotation = mat2(\n");
WRITE(p, " u_rotation == 0.0 ? 1.0 : (u_rotation == 2.0 ? -1.0 : 0.0), u_rotation == 1.0 ? 1.0 : (u_rotation == 3.0 ? -1.0 : 0.0),\n");
WRITE(p, " u_rotation == 3.0 ? 1.0 : (u_rotation == 1.0 ? -1.0 : 0.0), u_rotation == 0.0 ? 1.0 : (u_rotation == 2.0 ? -1.0 : 0.0)\n");
WRITE(p, " );\n");
WRITE(p, " vec4 pos = position;\n");
WRITE(p, " pos.xy = mul(displayRotation, pos.xy);\n");
} else {
WRITE(p, " vec4 pos = position;\n");
}
// The viewport is used in this case, so need to compensate for that.
if (gstate_c.Use(GPU_ROUND_DEPTH_TO_16BIT)) {
WRITE(p, " vec4 outPos = depthRoundZVP(pos);\n");
WRITE(p, " vec4 outPos = depthRoundZVP(position);\n");
} else {
WRITE(p, " vec4 outPos = pos;\n");
WRITE(p, " vec4 outPos = position;\n");
}
}
} else {
@@ -1276,6 +1262,16 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
}
}
if (compat.shaderLanguage == GLSL_VULKAN) {
// Apply rotation from the uniform.
WRITE(p, " mat2 displayRotation = mat2(\n");
WRITE(p, " u_rotation == 0.0 ? 1.0 : (u_rotation == 2.0 ? -1.0 : 0.0), u_rotation == 1.0 ? 1.0 : (u_rotation == 3.0 ? -1.0 : 0.0),\n");
WRITE(p, " u_rotation == 3.0 ? 1.0 : (u_rotation == 1.0 ? -1.0 : 0.0), u_rotation == 0.0 ? 1.0 : (u_rotation == 2.0 ? -1.0 : 0.0)\n");
WRITE(p, " );\n");
WRITE(p, " outPos.xy = mul(displayRotation, outPos.xy);\n");
}
bool flipY = strlen(compat.viewportYSign) > 0;
if (gstate_c.Use(GPU_USE_NONBUFFERED_FLIP)) {
flipY = !flipY;
-2
View File
@@ -129,7 +129,6 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs,
queries.push_back({ &u_depthRange, "u_depthRange" });
queries.push_back({ &u_cullRangeMin, "u_cullRangeMin" });
queries.push_back({ &u_cullRangeMax, "u_cullRangeMax" });
queries.push_back({ &u_rotation, "u_rotation" });
// These two are only used for VR, but let's always query them for simplicity.
queries.push_back({ &u_scaleX, "u_scaleX" });
@@ -442,7 +441,6 @@ void LinkedShader::UpdateUniforms(const ShaderID &vsid, bool useBufferedRenderin
ConvertProjMatrixToGL(flippedMatrix);
render_->SetUniformM4x4(&u_proj, flippedMatrix.m);
render_->SetUniformF1(&u_rotation, useBufferedRendering ? 0 : (float)g_display.rotation);
}
if (dirty & DIRTY_PROJTHROUGHMATRIX) {
Matrix4x4 proj_through;
-1
View File
@@ -68,7 +68,6 @@ public:
int u_depthRange; // x,y = viewport xscale/xcenter. z,w=clipping minz/maxz (?)
int u_cullRangeMin;
int u_cullRangeMax;
int u_rotation;
int u_mipBias;
int u_scaleX;
int u_scaleY;
+5
View File
@@ -504,10 +504,15 @@ void SystemInfoScreen::CreateVulkanExtsTab(UI::LinearLayout *gpuExtensions) {
auto si = GetI18NCategory(I18NCat::SYSINFO);
auto di = GetI18NCategory(I18NCat::DIALOG);
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
Draw::DrawContext *draw = screenManager()->getDrawContext();
CollapsibleSection *vulkanFeatures = gpuExtensions->Add(new CollapsibleSection(si->T("Vulkan Features")));
// TODO: This one belongs under its own header. And this is Vulkan "pre-rotation" really.
vulkanFeatures->Add(new InfoItem(gr->T("Display rotation"), StringFromFormat("%d°", (int)g_display.rotation * 90)));
std::vector<std::string> features = draw->GetFeatureList();
for (const auto &feature : features) {
vulkanFeatures->Add(new TextView(feature, FLAG_DYNAMIC_ASCII, true, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);