1
0
Fork 1
forked from suyu/suyu

renderer_opengl: split up blit screen resources into antialias and window adapt passes

This commit is contained in:
Liam 2024-01-15 15:08:21 -05:00
parent dd2918efd8
commit d4de04584f
13 changed files with 332 additions and 232 deletions

View file

@ -116,6 +116,8 @@ add_library(video_core STATIC
renderer_null/null_rasterizer.h
renderer_null/renderer_null.cpp
renderer_null/renderer_null.h
renderer_opengl/present/filters.cpp
renderer_opengl/present/filters.h
renderer_opengl/present/fsr.cpp
renderer_opengl/present/fsr.h
renderer_opengl/present/fxaa.cpp
@ -123,6 +125,8 @@ add_library(video_core STATIC
renderer_opengl/present/smaa.cpp
renderer_opengl/present/smaa.h
renderer_opengl/present/util.h
renderer_opengl/present/window_adapt_pass.cpp
renderer_opengl/present/window_adapt_pass.h
renderer_opengl/blit_image.cpp
renderer_opengl/blit_image.h
renderer_opengl/gl_blit_screen.cpp

View file

@ -2,100 +2,26 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "video_core/framebuffer_config.h"
#include "video_core/host_shaders/ffx_a_h.h"
#include "video_core/host_shaders/ffx_fsr1_h.h"
#include "video_core/host_shaders/full_screen_triangle_vert.h"
#include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h"
#include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h"
#include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h"
#include "video_core/host_shaders/opengl_present_frag.h"
#include "video_core/host_shaders/opengl_present_scaleforce_frag.h"
#include "video_core/host_shaders/opengl_present_vert.h"
#include "video_core/host_shaders/present_bicubic_frag.h"
#include "video_core/host_shaders/present_gaussian_frag.h"
#include "video_core/renderer_opengl/gl_blit_screen.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
#include "video_core/renderer_opengl/gl_state_tracker.h"
#include "video_core/renderer_opengl/present/filters.h"
#include "video_core/renderer_opengl/present/fsr.h"
#include "video_core/renderer_opengl/present/fxaa.h"
#include "video_core/renderer_opengl/present/smaa.h"
#include "video_core/renderer_opengl/present/window_adapt_pass.h"
#include "video_core/textures/decoders.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<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {}
std::array<GLfloat, 2> position;
std::array<GLfloat, 2> tex_coord;
};
/**
* Defines a 1:1 pixel ortographic 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<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) {
std::array<GLfloat, 3 * 2> 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
BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_,
Tegra::MaxwellDeviceMemoryManager& device_memory_,
StateTracker& state_tracker_, ProgramManager& program_manager_,
Device& device_)
: rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_),
program_manager(program_manager_), device(device_) {
// Create shader programs
present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
present_bilinear_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER);
present_bicubic_fragment = CreateProgram(HostShaders::PRESENT_BICUBIC_FRAG, GL_FRAGMENT_SHADER);
present_gaussian_fragment =
CreateProgram(HostShaders::PRESENT_GAUSSIAN_FRAG, GL_FRAGMENT_SHADER);
present_scaleforce_fragment =
CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG),
GL_FRAGMENT_SHADER);
// Generate presentation sampler
present_sampler.Create();
glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
present_sampler_nn.Create();
glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
// Generate VBO handle for drawing
vertex_buffer.Create();
// Attach vertex data to VAO
glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
// Allocate textures for the screen
framebuffer_texture.resource.Create(GL_TEXTURE_2D);
@ -106,15 +32,6 @@ BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_,
const u8 framebuffer_data[4] = {0, 0, 0, 0};
glClearTexImage(framebuffer_texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE,
framebuffer_data);
// Enable unified vertex attributes and query vertex buffer address when the driver supports it
if (device.HasVertexBufferUnifiedMemory()) {
glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV);
glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV);
glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY);
glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV,
&vertex_buffer_address);
}
}
BlitScreen::~BlitScreen() = default;
@ -219,18 +136,14 @@ void BlitScreen::ConfigureFramebufferTexture(const Tegra::FramebufferConfig& fra
glTextureStorage2D(framebuffer_texture.resource.handle, 1, internal_format,
framebuffer_texture.width, framebuffer_texture.height);
fxaa = std::make_unique<FXAA>(
Settings::values.resolution_info.ScaleUp(framebuffer_texture.width),
Settings::values.resolution_info.ScaleUp(framebuffer_texture.height));
smaa = std::make_unique<SMAA>(
Settings::values.resolution_info.ScaleUp(framebuffer_texture.width),
Settings::values.resolution_info.ScaleUp(framebuffer_texture.height));
fxaa.reset();
smaa.reset();
}
void BlitScreen::DrawScreen(const Tegra::FramebufferConfig& framebuffer,
const Layout::FramebufferLayout& layout) {
FramebufferTextureInfo info = PrepareRenderTarget(framebuffer);
const auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height);
auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height);
// TODO: Signal state tracker about these changes
state_tracker.NotifyScreenDrawVertexArray();
@ -267,15 +180,14 @@ void BlitScreen::DrawScreen(const Tegra::FramebufferConfig& framebuffer,
glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthRangeIndexed(0, 0.0, 0.0);
GLint old_read_fb;
GLint old_draw_fb;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
GLuint texture = info.display_texture;
auto anti_aliasing = Settings::values.anti_aliasing.GetValue();
if (anti_aliasing >= Settings::AntiAliasing::MaxEnum) {
LOG_ERROR(Render_OpenGL, "Invalid antialiasing option selected {}", anti_aliasing);
anti_aliasing = Settings::AntiAliasing::None;
Settings::values.anti_aliasing.SetValue(anti_aliasing);
}
if (anti_aliasing != Settings::AntiAliasing::None) {
glEnablei(GL_SCISSOR_TEST, 0);
auto scissor_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width);
@ -286,137 +198,83 @@ void BlitScreen::DrawScreen(const Tegra::FramebufferConfig& framebuffer,
glScissorIndexed(0, 0, 0, scissor_width, scissor_height);
glViewportIndexedf(0, 0.0f, 0.0f, viewport_width, viewport_height);
glBindSampler(0, present_sampler.handle);
GLint old_read_fb;
GLint old_draw_fb;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
switch (anti_aliasing) {
case Settings::AntiAliasing::Fxaa: {
case Settings::AntiAliasing::Fxaa:
CreateFXAA();
texture = fxaa->Draw(program_manager, info.display_texture);
} break;
case Settings::AntiAliasing::Smaa: {
texture = smaa->Draw(program_manager, info.display_texture);
} break;
break;
case Settings::AntiAliasing::Smaa:
default:
UNREACHABLE();
CreateSMAA();
texture = smaa->Draw(program_manager, info.display_texture);
break;
}
}
glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
}
glDisablei(GL_SCISSOR_TEST, 0);
if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
GLint old_read_fb;
GLint old_draw_fb;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
if (!fsr || fsr->NeedsRecreation(layout.screen)) {
fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight());
}
texture = fsr->Draw(program_manager, texture, info.scaled_width, info.scaled_height, crop);
crop = {0, 0, 1, 1};
}
glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
}
glBindTextureUnit(0, texture);
const std::array ortho_matrix =
MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
const auto fragment_handle = [this]() {
switch (Settings::values.scaling_filter.GetValue()) {
case Settings::ScalingFilter::Bicubic:
return present_bicubic_fragment.handle;
case Settings::ScalingFilter::Gaussian:
return present_gaussian_fragment.handle;
case Settings::ScalingFilter::ScaleForce:
return present_scaleforce_fragment.handle;
case Settings::ScalingFilter::NearestNeighbor:
case Settings::ScalingFilter::Bilinear:
case Settings::ScalingFilter::Fsr:
default:
return present_bilinear_fragment.handle;
}
}();
program_manager.BindPresentPrograms(present_vertex.handle, fragment_handle);
glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE,
ortho_matrix.data());
f32 left, top, right, bottom;
if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
// FSR has already applied the crop, so we just want to render the image
// it has produced.
left = 0;
top = 0;
right = 1;
bottom = 1;
} else {
// Apply the precomputed crop.
left = crop.left;
top = crop.top;
right = crop.right;
bottom = crop.bottom;
}
// 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, left, top),
ScreenRectVertex(x + w, y, right, top),
ScreenRectVertex(x, y + h, left, bottom),
ScreenRectVertex(x + w, y + h, right, bottom),
};
glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
glDisable(GL_FRAMEBUFFER_SRGB);
glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width),
static_cast<GLfloat>(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));
}
if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) {
glBindSampler(0, present_sampler.handle);
} else {
glBindSampler(0, present_sampler_nn.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);
CreateWindowAdapt();
window_adapt->DrawToFramebuffer(program_manager, texture, layout, crop);
// TODO
// program_manager.RestoreGuestPipeline();
}
void BlitScreen::CreateFXAA() {
smaa.reset();
if (!fxaa) {
fxaa = std::make_unique<FXAA>(
Settings::values.resolution_info.ScaleUp(framebuffer_texture.width),
Settings::values.resolution_info.ScaleUp(framebuffer_texture.height));
}
}
void BlitScreen::CreateSMAA() {
fxaa.reset();
if (!smaa) {
smaa = std::make_unique<SMAA>(
Settings::values.resolution_info.ScaleUp(framebuffer_texture.width),
Settings::values.resolution_info.ScaleUp(framebuffer_texture.height));
}
}
void BlitScreen::CreateWindowAdapt() {
if (window_adapt && Settings::values.scaling_filter.GetValue() == current_window_adapt) {
return;
}
current_window_adapt = Settings::values.scaling_filter.GetValue();
switch (current_window_adapt) {
case Settings::ScalingFilter::NearestNeighbor:
window_adapt = MakeNearestNeighbor(device);
break;
case Settings::ScalingFilter::Bicubic:
window_adapt = MakeBicubic(device);
break;
case Settings::ScalingFilter::Gaussian:
window_adapt = MakeGaussian(device);
break;
case Settings::ScalingFilter::ScaleForce:
window_adapt = MakeScaleForce(device);
break;
case Settings::ScalingFilter::Fsr:
case Settings::ScalingFilter::Bilinear:
default:
window_adapt = MakeBilinear(device);
break;
}
}
} // namespace OpenGL

View file

@ -18,6 +18,10 @@ namespace Tegra {
struct FramebufferConfig;
}
namespace Settings {
enum class ScalingFilter : u32;
}
namespace OpenGL {
class Device;
@ -27,6 +31,7 @@ class ProgramManager;
class RasterizerOpenGL;
class SMAA;
class StateTracker;
class WindowAdaptPass;
/// Structure used for storing information about the textures for the Switch screen
struct TextureInfo {
@ -61,29 +66,22 @@ public:
void DrawScreen(const Tegra::FramebufferConfig& framebuffer,
const Layout::FramebufferLayout& layout);
void RenderScreenshot(const Tegra::FramebufferConfig& framebuffer);
/// Loads framebuffer from emulated memory into the active OpenGL texture.
FramebufferTextureInfo LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
FramebufferTextureInfo PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer);
private:
void CreateFXAA();
void CreateSMAA();
void CreateWindowAdapt();
RasterizerOpenGL& rasterizer;
Tegra::MaxwellDeviceMemoryManager& device_memory;
StateTracker& state_tracker;
ProgramManager& program_manager;
Device& device;
OGLSampler present_sampler;
OGLSampler present_sampler_nn;
OGLBuffer vertex_buffer;
OGLProgram present_vertex;
OGLProgram present_bilinear_fragment;
OGLProgram present_bicubic_fragment;
OGLProgram present_gaussian_fragment;
OGLProgram present_scaleforce_fragment;
/// Display information for Switch screen
TextureInfo framebuffer_texture;
@ -91,11 +89,11 @@ private:
std::unique_ptr<FXAA> fxaa;
std::unique_ptr<SMAA> smaa;
Settings::ScalingFilter current_window_adapt{};
std::unique_ptr<WindowAdaptPass> window_adapt;
/// OpenGL framebuffer data
std::vector<u8> gl_framebuffer_data;
// GPU address of the vertex buffer
GLuint64EXT vertex_buffer_address = 0;
};
} // namespace OpenGL

View file

@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "video_core/host_shaders/opengl_present_frag.h"
#include "video_core/host_shaders/opengl_present_scaleforce_frag.h"
#include "video_core/host_shaders/present_bicubic_frag.h"
#include "video_core/host_shaders/present_gaussian_frag.h"
#include "video_core/renderer_opengl/present/filters.h"
#include "video_core/renderer_opengl/present/util.h"
namespace OpenGL {
std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device) {
return std::make_unique<WindowAdaptPass>(device, CreateNearestNeighborSampler(),
HostShaders::OPENGL_PRESENT_FRAG);
}
std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device) {
return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(),
HostShaders::OPENGL_PRESENT_FRAG);
}
std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device) {
return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(),
HostShaders::PRESENT_BICUBIC_FRAG);
}
std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device) {
return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(),
HostShaders::PRESENT_GAUSSIAN_FRAG);
}
std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device) {
return std::make_unique<WindowAdaptPass>(
device, CreateBilinearSampler(),
fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG));
}
} // namespace OpenGL

View file

@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "video_core/renderer_opengl/present/window_adapt_pass.h"
namespace OpenGL {
std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device);
std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device);
std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device);
std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device);
std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device);
} // namespace OpenGL

View file

@ -31,6 +31,7 @@ GLuint FXAA::Draw(ProgramManager& program_manager, GLuint input_texture) {
program_manager.BindPresentPrograms(vert_shader.handle, frag_shader.handle);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
glBindTextureUnit(0, input_texture);
glBindSampler(0, sampler.handle);
glDrawArrays(GL_TRIANGLES, 0, 3);
glFrontFace(GL_CW);

View file

@ -36,13 +36,7 @@ SMAA::SMAA(u32 width, u32 height) {
SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
area_tex.Create(GL_TEXTURE_2D);
glTextureStorage2D(area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT);

View file

@ -29,4 +29,15 @@ static inline OGLSampler CreateBilinearSampler() {
return sampler;
}
static inline OGLSampler CreateNearestNeighborSampler() {
OGLSampler sampler;
sampler.Create();
glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glSamplerParameteri(sampler.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
return sampler;
}
} // namespace OpenGL

View file

@ -0,0 +1,128 @@
// 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<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {}
std::array<GLfloat, 2> position;
std::array<GLfloat, 2> 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<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) {
std::array<GLfloat, 3 * 2> 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<f32>& crop) {
glBindTextureUnit(0, texture);
const std::array ortho_matrix =
MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(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<GLfloat>(layout.width),
static_cast<GLfloat>(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

View file

@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/math_util.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
namespace Layout {
struct FramebufferLayout;
}
namespace OpenGL {
class Device;
class ProgramManager;
class WindowAdaptPass final {
public:
explicit WindowAdaptPass(const Device& device, OGLSampler&& sampler,
std::string_view frag_source);
~WindowAdaptPass();
void DrawToFramebuffer(ProgramManager& program_manager, GLuint texture,
const Layout::FramebufferLayout& layout,
const Common::Rectangle<f32>& crop);
private:
const Device& device;
OGLSampler sampler;
OGLProgram vert;
OGLProgram frag;
OGLBuffer vertex_buffer;
// GPU address of the vertex buffer
GLuint64EXT vertex_buffer_address = 0;
};
} // namespace OpenGL

View file

@ -113,6 +113,12 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) {
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
}
// Enable unified vertex attributes when the driver supports it
if (device.HasVertexBufferUnifiedMemory()) {
glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV);
glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV);
}
blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker,
program_manager, device);
}

View file

@ -92,7 +92,9 @@ void WindowAdaptPass::Draw(Scheduler& scheduler, size_t image_index, VkImageView
const VkFramebuffer host_framebuffer{*dst->framebuffer};
const VkRenderPass renderpass{*render_pass};
const VkPipeline graphics_pipeline{*pipeline};
const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout};
const VkDescriptorSet descriptor_set{descriptor_sets[image_index]};
const VkBuffer vertex_buffer{*buffer};
const VkExtent2D render_area{
.width = dst->width,
.height = dst->height,
@ -134,8 +136,8 @@ void WindowAdaptPass::Draw(Scheduler& scheduler, size_t image_index, VkImageView
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, scissor);
cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices));
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0,
cmdbuf.BindVertexBuffer(0, vertex_buffer, offsetof(BufferData, vertices));
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0,
descriptor_set, {});
cmdbuf.Draw(4, 1, 0, 0);
cmdbuf.EndRenderPass();

View file

@ -222,6 +222,9 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr
.image = std::move(staging_image),
.image_view = std::move(dst_view),
.framebuffer = std::move(screenshot_fb),
.cmdbuf{},
.render_ready{},
.present_done{},
};
}();