// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/settings.h" #include "video_core/host_shaders/opengl_present_vert.h" #include "video_core/renderer_opengl/gl_device.h" #include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/present/window_adapt_pass.h" namespace OpenGL { namespace { constexpr GLint PositionLocation = 0; constexpr GLint TexCoordLocation = 1; constexpr GLint ModelViewMatrixLocation = 0; struct ScreenRectVertex { constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v) : position{{static_cast(x), static_cast(y)}}, tex_coord{{u, v}} {} std::array position; std::array tex_coord; }; /** * Defines a 1:1 pixel orthographic projection matrix with (0,0) on the top-left * corner and (width, height) on the lower-bottom. * * The projection part of the matrix is trivial, hence these operations are represented * by a 3x2 matrix. */ std::array MakeOrthographicMatrix(float width, float height) { std::array matrix; // Laid out in column-major order // clang-format off matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f; // Last matrix row is implicitly assumed to be [0, 0, 1]. // clang-format on return matrix; } } // namespace WindowAdaptPass::WindowAdaptPass(const Device& device_, OGLSampler&& sampler_, std::string_view frag_source) : device(device_), sampler(std::move(sampler_)) { vert = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER); frag = CreateProgram(frag_source, GL_FRAGMENT_SHADER); // Generate VBO handle for drawing vertex_buffer.Create(); // Attach vertex data to VAO glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); // Query vertex buffer address when the driver supports unified vertex attributes if (device.HasVertexBufferUnifiedMemory()) { glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY); glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, &vertex_buffer_address); } } WindowAdaptPass::~WindowAdaptPass() = default; void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, GLuint texture, const Layout::FramebufferLayout& layout, const Common::Rectangle& crop) { glBindTextureUnit(0, texture); const std::array ortho_matrix = MakeOrthographicMatrix(static_cast(layout.width), static_cast(layout.height)); program_manager.BindPresentPrograms(vert.handle, frag.handle); glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE, ortho_matrix.data()); // Map the coordinates to the screen. const auto& screen = layout.screen; const auto x = screen.left; const auto y = screen.top; const auto w = screen.GetWidth(); const auto h = screen.GetHeight(); const std::array vertices = { ScreenRectVertex(x, y, crop.left, crop.top), ScreenRectVertex(x + w, y, crop.right, crop.top), ScreenRectVertex(x, y + h, crop.left, crop.bottom), ScreenRectVertex(x + w, y + h, crop.right, crop.bottom), }; glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices)); glDisable(GL_FRAMEBUFFER_SRGB); glViewportIndexedf(0, 0.0f, 0.0f, static_cast(layout.width), static_cast(layout.height)); glEnableVertexAttribArray(PositionLocation); glEnableVertexAttribArray(TexCoordLocation); glVertexAttribDivisor(PositionLocation, 0); glVertexAttribDivisor(TexCoordLocation, 0); glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE, offsetof(ScreenRectVertex, position)); glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE, offsetof(ScreenRectVertex, tex_coord)); glVertexAttribBinding(PositionLocation, 0); glVertexAttribBinding(TexCoordLocation, 0); if (device.HasVertexBufferUnifiedMemory()) { glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex)); glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address, sizeof(vertices)); } else { glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); } glBindSampler(0, sampler.handle); // Update background color before drawing glClearColor(Settings::values.bg_red.GetValue() / 255.0f, Settings::values.bg_green.GetValue() / 255.0f, Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } } // namespace OpenGL