Implement framebuffer vertical flip flag (#699)

* Implement framebuffer vertical flip flag

* Make VerticalMirror const
This commit is contained in:
PabloMK7 2025-03-17 20:39:55 +01:00 committed by OpenSauce04
parent 411abde5d1
commit 643f53f5f2
15 changed files with 88 additions and 28 deletions

View File

@ -1,4 +1,8 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -23,6 +27,12 @@ struct Rectangle {
constexpr Rectangle(T left, T top, T right, T bottom)
: left(left), top(top), right(right), bottom(bottom) {}
template <typename U>
operator Rectangle<U>() const {
return Rectangle<U>{static_cast<U>(left), static_cast<U>(top), static_cast<U>(right),
static_cast<U>(bottom)};
}
[[nodiscard]] constexpr bool operator==(const Rectangle<T>& rhs) const {
return (left == rhs.left) && (top == rhs.top) && (right == rhs.right) &&
(bottom == rhs.bottom);
@ -55,6 +65,9 @@ struct Rectangle {
return Rectangle{left, top, static_cast<T>(left + GetWidth() * s),
static_cast<T>(top + GetHeight() * s)};
}
[[nodiscard]] Rectangle<T> VerticalMirror(T ref_height) const {
return Rectangle{left, ref_height - bottom, right, ref_height - top};
}
};
template <typename T>

View File

@ -1,4 +1,4 @@
// Copyright 2017 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -233,6 +233,7 @@ struct FramebufferRegs {
// GetWidth() and GetHeight() instead.
BitField<0, 11, u32> width;
BitField<12, 10, u32> height;
BitField<24, 1, u32> flip;
};
INSERT_PADDING_WORDS(0x1);
@ -251,6 +252,10 @@ struct FramebufferRegs {
inline u32 GetHeight() const {
return height + 1;
}
inline bool IsFlipped() const {
return !flip.Value();
}
} framebuffer;
// Returns the number of bytes in the specified depth format

View File

@ -1,4 +1,4 @@
// Copyright 2017 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -29,11 +29,13 @@ struct RasterizerRegs {
BitField<0, 24, u32> viewport_size_x;
INSERT_PADDING_WORDS(0x1);
BitField<1, 31, u32> viewport_size_x_inv;
BitField<0, 24, u32> viewport_size_y;
INSERT_PADDING_WORDS(0x3);
BitField<1, 31, u32> viewport_size_y_inv;
INSERT_PADDING_WORDS(0x2);
BitField<0, 1, u32> clip_enable;
BitField<0, 24, u32> clip_coef[4]; // float24

View File

@ -1,4 +1,4 @@
// Copyright 2015 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -138,7 +138,7 @@ public:
}
private:
static constexpr u32 MASK = (1 << (M + E + 1)) - 1;
static constexpr u32 MASK = static_cast<u32>(1 << (M + E + 1)) - 1;
static constexpr u32 MANTISSA_MASK = (1 << M) - 1;
static constexpr u32 EXPONENT_MASK = (1 << E) - 1;
@ -153,6 +153,7 @@ private:
}
};
using f31 = Pica::Float<23, 7>;
using f24 = Pica::Float<16, 7>;
using f20 = Pica::Float<12, 7>;
using f16 = Pica::Float<10, 5>;

View File

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -846,7 +846,7 @@ void RasterizerAccelerated::SyncTextureBorderColor(int tex_index) {
}
void RasterizerAccelerated::SyncClipPlane() {
const u32 enable_clip1 = regs.rasterizer.clip_enable != 0;
const bool enable_clip1 = regs.rasterizer.clip_enable != 0;
const auto raw_clip_coef = regs.rasterizer.GetClipCoef();
const Common::Vec4f new_clip_coef = {raw_clip_coef.x.ToFloat32(), raw_clip_coef.y.ToFloat32(),
raw_clip_coef.z.ToFloat32(), raw_clip_coef.w.ToFloat32()};

View File

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -65,13 +65,18 @@ template <class T>
class FramebufferHelper {
public:
explicit FramebufferHelper(RasterizerCache<T>* res_cache_, typename T::Framebuffer* fb_,
const Pica::RasterizerRegs& regs,
bool flip_rect, const Pica::RasterizerRegs& regs,
Common::Rectangle<u32> surfaces_rect)
: res_cache{res_cache_}, fb{fb_} {
const u32 res_scale = fb->Scale();
const u32 height = surfaces_rect.GetHeight() / res_scale;
// Determine the draw rectangle (render area + scissor)
const Common::Rectangle viewport_rect = regs.GetViewportRect();
Common::Rectangle viewport_rect = regs.GetViewportRect();
if (flip_rect) {
viewport_rect = viewport_rect.VerticalMirror(height);
}
draw_rect.left =
std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left * res_scale,
surfaces_rect.left, surfaces_rect.right);
@ -103,6 +108,10 @@ public:
static_cast<s32>(surfaces_rect.left + (regs.scissor_test.x2 + 1) * res_scale);
scissor_rect.top =
static_cast<s32>(surfaces_rect.bottom + (regs.scissor_test.y2 + 1) * res_scale);
if (flip_rect) {
scissor_rect = scissor_rect.VerticalMirror(height);
}
}
~FramebufferHelper() {

View File

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Lime3DS Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -765,7 +765,8 @@ FramebufferHelper<T> RasterizerCache<T>::GetFramebufferSurfaces(bool using_color
it->second = slot_framebuffers.insert(runtime, fb_params, color_surface, depth_surface);
}
return FramebufferHelper<T>{this, &slot_framebuffers[it->second], regs.rasterizer, fb_rect};
return FramebufferHelper<T>{this, &slot_framebuffers[it->second],
regs.framebuffer.framebuffer.IsFlipped(), regs.rasterizer, fb_rect};
}
template <class T>

View File

@ -1,4 +1,4 @@
// Copyright 2022 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -409,6 +409,12 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
state.viewport.width = static_cast<GLsizei>(viewport.width);
state.viewport.height = static_cast<GLsizei>(viewport.height);
// If the framebuffer is flipped, request vertex shader to flip vertex y
const bool is_flipped = regs.framebuffer.framebuffer.IsFlipped();
vs_uniform_block_data.dirty |= vs_uniform_block_data.data.flip_viewport != is_flipped;
vs_uniform_block_data.data.flip_viewport = is_flipped;
state.cull.mode = is_flipped && state.cull.enabled ? GL_FRONT : GL_BACK;
// Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect.
// Enable scissor test to prevent drawing outside of the framebuffer region
const auto draw_rect = fb_helper.DrawRect();

View File

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -177,13 +177,13 @@ inline vk::PrimitiveTopology PrimitiveTopology(Pica::PipelineRegs::TriangleTopol
return vk::PrimitiveTopology::eTriangleList;
}
inline vk::CullModeFlags CullMode(Pica::RasterizerRegs::CullMode mode) {
inline vk::CullModeFlags CullMode(Pica::RasterizerRegs::CullMode mode, bool flip_viewport) {
switch (mode) {
case Pica::RasterizerRegs::CullMode::KeepAll:
return vk::CullModeFlagBits::eNone;
case Pica::RasterizerRegs::CullMode::KeepClockWise:
case Pica::RasterizerRegs::CullMode::KeepCounterClockWise:
return vk::CullModeFlagBits::eBack;
return flip_viewport ? vk::CullModeFlagBits::eFront : vk::CullModeFlagBits::eBack;
default:
UNREACHABLE_MSG("Unknown cull mode {}", mode);
}

View File

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -147,7 +147,8 @@ bool GraphicsPipeline::Build(bool fail_on_compile_required) {
const vk::PipelineRasterizationStateCreateInfo raster_state = {
.depthClampEnable = false,
.rasterizerDiscardEnable = false,
.cullMode = PicaToVK::CullMode(info.rasterization.cull_mode),
.cullMode =
PicaToVK::CullMode(info.rasterization.cull_mode, info.rasterization.flip_viewport),
.frontFace = PicaToVK::FrontFace(info.rasterization.cull_mode),
.depthBiasEnable = false,
.lineWidth = 1.0f,

View File

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -54,6 +54,7 @@ union RasterizationState {
u8 value = 0;
BitField<0, 2, Pica::PipelineRegs::TriangleTopology> topology;
BitField<4, 2, Pica::RasterizerRegs::CullMode> cull_mode;
BitField<6, 1, u8> flip_viewport;
};
union DepthStencilState {

View File

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -271,8 +271,12 @@ bool PipelineCache::BindPipeline(const PipelineInfo& info, bool wait_built) {
}
if (instance.IsExtendedDynamicStateSupported()) {
if (rasterization.cull_mode != current_rasterization.cull_mode || is_dirty) {
cmdbuf.setCullModeEXT(PicaToVK::CullMode(rasterization.cull_mode));
const bool needs_flip =
rasterization.flip_viewport != current_rasterization.flip_viewport;
if (rasterization.cull_mode != current_rasterization.cull_mode || needs_flip ||
is_dirty) {
cmdbuf.setCullModeEXT(
PicaToVK::CullMode(rasterization.cull_mode, rasterization.flip_viewport));
cmdbuf.setFrontFaceEXT(PicaToVK::FrontFace(rasterization.cull_mode));
}

View File

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -509,6 +509,12 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
shader_dirty = false;
}
// If the framebuffer is flipped, request to also flip vulkan viewport
const bool is_flipped = regs.framebuffer.framebuffer.IsFlipped();
vs_uniform_block_data.dirty |= vs_uniform_block_data.data.flip_viewport != is_flipped;
vs_uniform_block_data.data.flip_viewport = is_flipped;
pipeline_info.rasterization.flip_viewport.Assign(is_flipped);
// Sync the LUTs within the texture buffer
SyncAndUploadLUTs();
SyncAndUploadLUTsLF();

View File

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -37,6 +37,7 @@ layout (set = 0, binding = 1, std140) uniform vs_data {
layout (binding = 1, std140) uniform vs_data {
#endif
bool enable_clip1;
bool flip_viewport;
vec4 clip_coef;
};
@ -123,6 +124,9 @@ void main() {
normquat = vert_normquat;
view = vert_view;
vec4 vtx_pos = SanitizeVertex(vert_position);
if (flip_viewport) {
vtx_pos.y = -vtx_pos.y;
}
gl_Position = vec4(vtx_pos.x, vtx_pos.y, -vtx_pos.z, vtx_pos.w);
)";
if (use_clip_planes) {
@ -238,6 +242,9 @@ std::string GenerateVertexShader(const ShaderSetup& setup, const PicaVSConfig& c
semantic(VSOutputAttributes::POSITION_Z) + ", " +
semantic(VSOutputAttributes::POSITION_W) + ");\n";
out += " vtx_pos = SanitizeVertex(vtx_pos);\n";
out += " if (flip_viewport) {\n";
out += " vtx_pos.y = -vtx_pos.y;\n";
out += " }\n";
out += " gl_Position = vec4(vtx_pos.x, vtx_pos.y, -vtx_pos.z, vtx_pos.w);\n";
if (config.state.use_clip_planes) {
out += " gl_ClipDistance[0] = -vtx_pos.z;\n"; // fixed PICA clipping plane z <= 0
@ -332,6 +339,9 @@ struct Vertex {
semantic(VSOutputAttributes::POSITION_Z) + ", " +
semantic(VSOutputAttributes::POSITION_W) + ");\n";
out += " vtx_pos = SanitizeVertex(vtx_pos);\n";
out += " if (flip_viewport) {\n";
out += " vtx_pos.y = -vtx_pos.y;\n";
out += " }\n";
out += " gl_Position = vec4(vtx_pos.x, vtx_pos.y, -vtx_pos.z, vtx_pos.w);\n";
if (state.use_clip_planes) {
out += " gl_ClipDistance[0] = -vtx_pos.z;\n"; // fixed PICA clipping plane z <= 0

View File

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -86,7 +86,8 @@ struct PicaUniformsData {
};
struct VSUniformData {
u32 enable_clip1;
alignas(4) bool enable_clip1;
alignas(4) bool flip_viewport;
alignas(16) Common::Vec4f clip_coef;
};
static_assert(sizeof(VSUniformData) == 32,