distribution/packages/emulators/standalone/duckstation-sa/patches/legacy/004-add-rotation.patch
2023-04-25 19:57:37 +00:00

389 lines
15 KiB
Diff
Executable file

diff --git a/src/common/gl/program.h b/src/common/gl/program.h
index d877a462..3fa9a7a7 100644
--- a/src/common/gl/program.h
+++ b/src/common/gl/program.h
@@ -40,6 +40,7 @@ public:
void Destroy();
int RegisterUniform(const char* name);
+ GLint GetUniformLocation(int index) const { return m_uniform_locations[index]; }
void Uniform1ui(int index, u32 x) const;
void Uniform2ui(int index, u32 x, u32 y) const;
void Uniform3ui(int index, u32 x, u32 y, u32 z) const;
@@ -97,6 +98,6 @@ private:
GLuint m_fragment_shader_id = 0;
std::vector<GLint> m_uniform_locations;
-};
+}; // namespace GL
} // namespace GL
\ No newline at end of file
diff --git a/src/core/host_display.cpp b/src/core/host_display.cpp
index d0469fd3..790be30f 100644
--- a/src/core/host_display.cpp
+++ b/src/core/host_display.cpp
@@ -103,6 +103,11 @@ bool HostDisplay::GetHostRefreshRate(float* refresh_rate)
return WindowInfo::QueryRefreshRateForWindow(m_window_info, refresh_rate);
}
+bool HostDisplay::SetDisplayRotation(Rotation rotation)
+{
+ return false;
+}
+
void HostDisplay::SetSoftwareCursor(std::unique_ptr<HostDisplayTexture> texture, float scale /*= 1.0f*/)
{
m_cursor_texture = std::move(texture);
diff --git a/src/core/host_display.h b/src/core/host_display.h
index ea01f846..b5a3563e 100644
--- a/src/core/host_display.h
+++ b/src/core/host_display.h
@@ -60,6 +60,15 @@ public:
std::vector<std::string> fullscreen_modes;
};
+ enum class Rotation
+ {
+ None,
+ R90Degrees,
+ R180Degrees,
+ R270Degrees,
+ Count
+ };
+
virtual ~HostDisplay();
ALWAYS_INLINE const WindowInfo& GetWindowInfo() const { return m_window_info; }
@@ -201,6 +210,7 @@ public:
virtual bool SetDisplayPixels(HostDisplayPixelFormat format, u32 width, u32 height, const void* buffer, u32 pitch);
virtual bool GetHostRefreshRate(float* refresh_rate);
+ virtual bool SetDisplayRotation(Rotation rotation);
void SetDisplayLinearFiltering(bool enabled) { m_display_linear_filtering = enabled; }
void SetDisplayTopMargin(s32 height) { m_display_top_margin = height; }
@@ -283,6 +293,7 @@ protected:
s32 m_display_top_margin = 0;
Alignment m_display_alignment = Alignment::Center;
+ Rotation m_display_rotation = Rotation::None;
std::unique_ptr<HostDisplayTexture> m_cursor_texture;
float m_cursor_texture_scale = 1.0f;
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 833b6b9f..4381b848 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -230,6 +230,7 @@ void Settings::Load(SettingsInterface& si)
display_line_start_offset = static_cast<s8>(si.GetIntValue("Display", "LineStartOffset", 0));
display_line_end_offset = static_cast<s8>(si.GetIntValue("Display", "LineEndOffset", 0));
display_linear_filtering = si.GetBoolValue("Display", "LinearFiltering", true);
+ display_rotate = static_cast<s8>(si.GetIntValue("Display", "Rotate", 0));
display_integer_scaling = si.GetBoolValue("Display", "IntegerScaling", false);
display_stretch = si.GetBoolValue("Display", "Stretch", false);
display_post_processing = si.GetBoolValue("Display", "PostProcessing", false);
diff --git a/src/core/settings.h b/src/core/settings.h
index e837f8b2..199ecac7 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -136,6 +136,7 @@ struct Settings
s16 display_active_end_offset = 0;
s8 display_line_start_offset = 0;
s8 display_line_end_offset = 0;
+ s8 display_rotate = 0;
bool display_force_4_3_for_24bit = false;
bool gpu_24bit_chroma_smoothing = false;
bool display_linear_filtering = true;
diff --git a/src/duckstation-nogui/nogui_host_interface.cpp b/src/duckstation-nogui/nogui_host_interface.cpp
index 638af4ea..6cef355a 100644
--- a/src/duckstation-nogui/nogui_host_interface.cpp
+++ b/src/duckstation-nogui/nogui_host_interface.cpp
@@ -136,6 +136,15 @@ bool NoGUIHostInterface::CreateDisplay(bool fullscreen)
return false;
}
+ switch(g_settings.display_rotate & 3)
+ {
+ case 1: m_display->SetDisplayRotation(HostDisplay::Rotation::R90Degrees);
+ case 2: m_display->SetDisplayRotation(HostDisplay::Rotation::R180Degrees);
+ case 3: m_display->SetDisplayRotation(HostDisplay::Rotation::R270Degrees);
+ case 0:
+ default: break;
+ }
+
if (fullscreen)
SetFullscreen(true);
diff --git a/src/frontend-common/opengl_host_display.cpp b/src/frontend-common/opengl_host_display.cpp
index 7092b455..ff1c7939 100644
--- a/src/frontend-common/opengl_host_display.cpp
+++ b/src/frontend-common/opengl_host_display.cpp
@@ -203,6 +203,77 @@ void OpenGLHostDisplay::UpdateDisplayPixelsTextureFilter()
m_display_texture_is_linear_filtered = m_display_linear_filtering;
}
+bool OpenGLHostDisplay::SetDisplayRotation(Rotation rotation)
+{
+ m_display_rotation = rotation;
+ UpdateDisplayRotationFramebuffer();
+ return true;
+}
+
+void OpenGLHostDisplay::UpdateDisplayRotationFramebuffer()
+{
+ m_window_info.surface_width = m_gl_context->GetSurfaceWidth();
+ m_window_info.surface_height = m_gl_context->GetSurfaceHeight();
+
+ if (m_display_rotation_framebuffer_fbo != 0)
+ {
+ glDeleteFramebuffers(1, &m_display_rotation_framebuffer_fbo);
+ m_display_rotation_framebuffer_fbo = 0;
+ glDeleteTextures(1, &m_display_rotation_framebuffer_texture);
+ m_display_rotation_framebuffer_texture = 0;
+ }
+
+ if (m_display_rotation != Rotation::None)
+ {
+ if (m_display_rotation_framebuffer_texture == 0)
+ glGenTextures(1, &m_display_rotation_framebuffer_texture);
+ if (m_display_rotation_framebuffer_fbo == 0)
+ glGenFramebuffers(1, &m_display_rotation_framebuffer_fbo);
+
+ if (m_display_rotation == Rotation::R90Degrees || m_display_rotation == Rotation::R270Degrees)
+ std::swap(m_window_info.surface_width, m_window_info.surface_height);
+
+ GLint old_texture;
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture);
+
+ glBindTexture(GL_TEXTURE_2D, m_display_rotation_framebuffer_texture);
+
+ if (GLAD_GL_ARB_texture_storage || GLAD_GL_ES_VERSION_3_1)
+ {
+ glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, m_window_info.surface_width, m_window_info.surface_height);
+ }
+ else
+ {
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_window_info.surface_width, m_window_info.surface_height, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, nullptr);
+ }
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ 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_MAX_LEVEL, 1);
+
+ const GLenum framebuffer_binding = m_use_gles2_draw_path ? GL_FRAMEBUFFER : GL_DRAW_FRAMEBUFFER;
+ GLint old_framebuffer;
+ glGetIntegerv(m_use_gles2_draw_path ? GL_FRAMEBUFFER_BINDING : GL_DRAW_FRAMEBUFFER_BINDING, &old_framebuffer);
+
+ glBindFramebuffer(framebuffer_binding, m_display_rotation_framebuffer_fbo);
+ glFramebufferTexture2D(framebuffer_binding, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ m_display_rotation_framebuffer_texture, 0);
+ Assert(glCheckFramebufferStatus(framebuffer_binding) == GL_FRAMEBUFFER_COMPLETE);
+
+ glBindFramebuffer(framebuffer_binding, old_framebuffer);
+ glBindTexture(GL_TEXTURE_2D, old_texture);
+ }
+
+ if (ImGui::GetCurrentContext())
+ {
+ ImGui::GetIO().DisplaySize =
+ ImVec2(static_cast<float>(m_window_info.surface_width), static_cast<float>(m_window_info.surface_height));
+ }
+}
+
bool OpenGLHostDisplay::SupportsDisplayPixelFormat(HostDisplayPixelFormat format) const
{
const auto [gl_internal_format, gl_format, gl_type] = GetPixelFormatMapping(m_gl_context->IsGLES(), format);
@@ -435,7 +506,7 @@ bool OpenGLHostDisplay::InitializeRenderDevice(std::string_view shader_cache_dir
glDebugMessageCallback(GLDebugCallback, nullptr);
glEnable(GL_DEBUG_OUTPUT);
- // glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
+ glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
}
if (!CreateResources())
@@ -484,7 +555,17 @@ bool OpenGLHostDisplay::ChangeRenderWindow(const WindowInfo& new_wi)
return false;
}
- m_window_info = m_gl_context->GetWindowInfo();
+ m_window_info = new_wi;
+ m_window_info.surface_width = m_gl_context->GetSurfaceWidth();
+ m_window_info.surface_height = m_gl_context->GetSurfaceHeight();
+
+ if (ImGui::GetCurrentContext())
+ {
+ ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_info.surface_width);
+ ImGui::GetIO().DisplaySize.y = static_cast<float>(m_window_info.surface_height);
+ }
+
+ UpdateDisplayRotationFramebuffer();
return true;
}
@@ -494,7 +575,16 @@ void OpenGLHostDisplay::ResizeRenderWindow(s32 new_window_width, s32 new_window_
return;
m_gl_context->ResizeSurface(static_cast<u32>(new_window_width), static_cast<u32>(new_window_height));
- m_window_info = m_gl_context->GetWindowInfo();
+ m_window_info.surface_width = m_gl_context->GetSurfaceWidth();
+ m_window_info.surface_height = m_gl_context->GetSurfaceHeight();
+
+ if (ImGui::GetCurrentContext())
+ {
+ ImGui::GetIO().DisplaySize.x = static_cast<float>(m_window_info.surface_width);
+ ImGui::GetIO().DisplaySize.y = static_cast<float>(m_window_info.surface_height);
+ }
+
+ UpdateDisplayRotationFramebuffer();
}
bool OpenGLHostDisplay::SupportsFullscreen() const
@@ -560,12 +650,17 @@ bool OpenGLHostDisplay::CreateResources()
{
static constexpr char fullscreen_quad_vertex_shader[] = R"(
uniform vec4 u_src_rect;
+uniform mat2 u_rotation_matrix;
out vec2 v_tex0;
void main()
{
vec2 pos = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
- v_tex0 = u_src_rect.xy + pos * u_src_rect.zw;
+ v_tex0 = (u_src_rect.xy + pos * u_src_rect.zw);
+
+ vec2 center = vec2(0.5, 0.5);
+ v_tex0 = center + (u_rotation_matrix * (v_tex0 - center));
+
gl_Position = vec4(pos * vec2(2.0f, -2.0f) + vec2(-1.0f, 1.0f), 0.0f, 1.0f);
}
)";
@@ -617,12 +712,14 @@ void main()
m_display_program.Bind();
m_display_program.RegisterUniform("u_src_rect");
+ m_display_program.RegisterUniform("u_rotation_matrix");
m_display_program.RegisterUniform("samp0");
- m_display_program.Uniform1i(1, 0);
+ m_display_program.Uniform1i(2, 0);
m_cursor_program.Bind();
m_cursor_program.RegisterUniform("u_src_rect");
+ m_cursor_program.RegisterUniform("u_rotation_matrix");
m_cursor_program.RegisterUniform("samp0");
- m_cursor_program.Uniform1i(1, 0);
+ m_cursor_program.Uniform1i(2, 0);
glGenVertexArrays(1, &m_display_vao);
@@ -749,7 +846,7 @@ bool OpenGLHostDisplay::Render()
}
glDisable(GL_SCISSOR_TEST);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_display_rotation_framebuffer_fbo);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
@@ -760,6 +857,12 @@ bool OpenGLHostDisplay::Render()
RenderSoftwareCursor();
+ if (m_display_rotation_framebuffer_fbo != 0)
+ {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ RenderRotatedFramebuffer();
+ }
+
m_gl_context->SwapBuffers();
return true;
}
@@ -833,6 +936,13 @@ void OpenGLHostDisplay::RenderDisplay()
m_display_texture_view_width, m_display_texture_view_height, m_display_linear_filtering);
}
+static const std::array<std::array<float, 4>, static_cast<u32>(HostDisplay::Rotation::Count)> s_rotation_matrices = {{
+ {{1.0f, 0.0f, 0.0f, 1.0f}},
+ {{0.0f, 1.0f, 1.0f, 0.0f}},
+ {{-1.0f, 0.0f, 0.0f, 1.0f}},
+ {{0.0f, -1.0f, -1.0f, 0.0f}},
+}};
+
static void DrawFullscreenQuadES2(s32 tex_view_x, s32 tex_view_y, s32 tex_view_width, s32 tex_view_height,
s32 tex_width, s32 tex_height)
{
@@ -879,6 +989,8 @@ void OpenGLHostDisplay::RenderDisplay(s32 left, s32 bottom, s32 width, s32 heigh
(static_cast<float>(texture_view_y) + (position_adjust * flip_adjust)) / static_cast<float>(texture_height),
(static_cast<float>(texture_view_width) - size_adjust) / static_cast<float>(texture_width),
(static_cast<float>(texture_view_height) - (size_adjust * flip_adjust)) / static_cast<float>(texture_height));
+ glUniformMatrix2fv(m_display_program.GetUniformLocation(1), 1, GL_TRUE,
+ s_rotation_matrices[static_cast<u32>(HostDisplay::Rotation::None)].data());
glBindSampler(0, linear_filter ? m_display_linear_sampler : m_display_nearest_sampler);
glBindVertexArray(m_display_vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
@@ -894,6 +1006,30 @@ void OpenGLHostDisplay::RenderDisplay(s32 left, s32 bottom, s32 width, s32 heigh
}
}
+void OpenGLHostDisplay::RenderRotatedFramebuffer()
+{
+ const u32 width = m_gl_context->GetSurfaceWidth();
+ const u32 height = m_gl_context->GetSurfaceHeight();
+
+ glViewport(0, 0, width, height);
+ glDisable(GL_BLEND);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ glDepthMask(GL_FALSE);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ m_display_program.Bind();
+ m_display_program.Uniform4f(0, 0.0f, 0.0f, 1.0f, 1.0f);
+ glUniformMatrix2fv(m_display_program.GetUniformLocation(1), 1, GL_TRUE,
+ s_rotation_matrices[static_cast<u32>(m_display_rotation)].data());
+ glBindTexture(GL_TEXTURE_2D, m_display_rotation_framebuffer_texture);
+
+ glBindVertexArray(m_display_vao);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+}
+
void OpenGLHostDisplay::RenderSoftwareCursor()
{
if (!HasSoftwareCursor())
diff --git a/src/frontend-common/opengl_host_display.h b/src/frontend-common/opengl_host_display.h
index 23e8d700..585ab4b5 100644
--- a/src/frontend-common/opengl_host_display.h
+++ b/src/frontend-common/opengl_host_display.h
@@ -51,6 +51,8 @@ public:
bool SetPostProcessingChain(const std::string_view& config) override;
+ bool SetDisplayRotation(Rotation rotation) override;
+
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
HostDisplayPixelFormat format, const void* data, u32 data_stride,
bool dynamic = false) override;
@@ -83,6 +85,8 @@ protected:
void BindDisplayPixelsTexture();
void UpdateDisplayPixelsTextureFilter();
+ void UpdateDisplayRotationFramebuffer();
+ void RenderRotatedFramebuffer();
void RenderDisplay();
void RenderImGui();
@@ -121,6 +125,9 @@ protected:
u32 m_display_pixels_texture_pbo_map_size = 0;
std::vector<u8> m_gles_pixels_repack_buffer;
+ GLuint m_display_rotation_framebuffer_texture = 0;
+ GLuint m_display_rotation_framebuffer_fbo = 0;
+
PostProcessingChain m_post_processing_chain;
GL::Texture m_post_processing_input_texture;
std::unique_ptr<GL::StreamBuffer> m_post_processing_ubo;