code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/settings.h"
#include "video_core/fsr.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/renderer_opengl/gl_shader_manager.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
#include "video_core/renderer_opengl/present/fsr.h"
#include "video_core/renderer_opengl/present/util.h"

namespace OpenGL {
using namespace FSR;

using FsrConstants = std::array<u32, 4 * 4>;

FSR::FSR(u32 output_width_, u32 output_height_) : width(output_width_), height(output_height_) {
    std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG};
    ReplaceInclude(fsr_source, "ffx_a.h", HostShaders::FFX_A_H);
    ReplaceInclude(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H);

    std::string fsr_easu_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG};
    std::string fsr_rcas_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG};
    ReplaceInclude(fsr_easu_source, "opengl_fidelityfx_fsr.frag", fsr_source);
    ReplaceInclude(fsr_rcas_source, "opengl_fidelityfx_fsr.frag", fsr_source);

    vert = CreateProgram(HostShaders::FULL_SCREEN_TRIANGLE_VERT, GL_VERTEX_SHADER);
    easu_frag = CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER);
    rcas_frag = CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER);

    glProgramUniform2f(vert.handle, 0, 1.0f, -1.0f);
    glProgramUniform2f(vert.handle, 1, 0.0f, 1.0f);

    sampler = CreateBilinearSampler();
    framebuffer.Create();

    easu_tex.Create(GL_TEXTURE_2D);
    glTextureStorage2D(easu_tex.handle, 1, GL_RGBA16F, width, height);

    rcas_tex.Create(GL_TEXTURE_2D);
    glTextureStorage2D(rcas_tex.handle, 1, GL_RGBA16F, width, height);
}

FSR::~FSR() = default;

GLuint FSR::Draw(ProgramManager& program_manager, GLuint texture, u32 input_image_width,
                 u32 input_image_height, const Common::Rectangle<f32>& crop_rect) {
    const f32 input_width = static_cast<f32>(input_image_width);
    const f32 input_height = static_cast<f32>(input_image_height);
    const f32 output_width = static_cast<f32>(width);
    const f32 output_height = static_cast<f32>(height);
    const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_width;
    const f32 viewport_x = crop_rect.left * input_width;
    const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_height;
    const f32 viewport_y = crop_rect.top * input_height;

    FsrConstants easu_con{};
    FsrConstants rcas_con{};

    FsrEasuConOffset(easu_con.data() + 0, easu_con.data() + 4, easu_con.data() + 8,
                     easu_con.data() + 12, viewport_width, viewport_height, input_width,
                     input_height, output_width, output_height, viewport_x, viewport_y);

    const float sharpening =
        static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;

    FsrRcasCon(rcas_con.data(), sharpening);

    glProgramUniform4uiv(easu_frag.handle, 0, sizeof(easu_con), easu_con.data());
    glProgramUniform4uiv(rcas_frag.handle, 0, sizeof(rcas_con), rcas_con.data());

    glFrontFace(GL_CW);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
    glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, easu_tex.handle, 0);
    glViewportIndexedf(0, 0.0f, 0.0f, output_width, output_height);
    program_manager.BindPresentPrograms(vert.handle, easu_frag.handle);
    glBindTextureUnit(0, texture);
    glBindSampler(0, sampler.handle);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, rcas_tex.handle, 0);
    program_manager.BindPresentPrograms(vert.handle, rcas_frag.handle);
    glBindTextureUnit(0, easu_tex.handle);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    return rcas_tex.handle;
}

bool FSR::NeedsRecreation(const Common::Rectangle<u32>& screen) {
    return screen.GetWidth() != width || screen.GetHeight() != height;
}

} // namespace OpenGL


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <string_view>

#include "common/common_types.h"
#include "common/math_util.h"
#include "video_core/fsr.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"

namespace OpenGL {

class ProgramManager;

class FSR {
public:
    explicit FSR(u32 output_width, u32 output_height);
    ~FSR();

    GLuint Draw(ProgramManager& program_manager, GLuint texture, u32 input_image_width,
                u32 input_image_height, const Common::Rectangle<f32>& crop_rect);

    bool NeedsRecreation(const Common::Rectangle<u32>& screen);

private:
    const u32 width;
    const u32 height;
    OGLFramebuffer framebuffer;
    OGLSampler sampler;
    OGLProgram vert;
    OGLProgram easu_frag;
    OGLProgram rcas_frag;
    OGLTexture easu_tex;
    OGLTexture rcas_tex;
};

} // namespace OpenGL

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "video_core/host_shaders/fxaa_frag.h"
#include "video_core/host_shaders/fxaa_vert.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/fxaa.h"
#include "video_core/renderer_opengl/present/util.h"

namespace OpenGL {

FXAA::FXAA(u32 width, u32 height) {
    vert_shader = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER);
    frag_shader = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER);

    sampler = CreateBilinearSampler();

    framebuffer.Create();

    texture.Create(GL_TEXTURE_2D);
    glTextureStorage2D(texture.handle, 1, GL_RGBA16F, width, height);
    glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, texture.handle, 0);
}

FXAA::~FXAA() = default;

GLuint FXAA::Draw(ProgramManager& program_manager, GLuint input_texture) {
    glFrontFace(GL_CCW);

    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);

    return texture.handle;
}

} // namespace OpenGL


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "video_core/renderer_opengl/gl_resource_manager.h"

namespace OpenGL {

class ProgramManager;

class FXAA {
public:
    explicit FXAA(u32 width, u32 height);
    ~FXAA();

    GLuint Draw(ProgramManager& program_manager, GLuint input_texture);

private:
    OGLProgram vert_shader;
    OGLProgram frag_shader;
    OGLSampler sampler;
    OGLFramebuffer framebuffer;
    OGLTexture texture;
};

} // namespace OpenGL


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "video_core/framebuffer_config.h"
#include "video_core/present.h"
#include "video_core/renderer_opengl/gl_blit_screen.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/present/fsr.h"
#include "video_core/renderer_opengl/present/fxaa.h"
#include "video_core/renderer_opengl/present/layer.h"
#include "video_core/renderer_opengl/present/present_uniforms.h"
#include "video_core/renderer_opengl/present/smaa.h"
#include "video_core/surface.h"
#include "video_core/textures/decoders.h"

namespace OpenGL {

Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_,
             const PresentFilters& filters_)
    : rasterizer(rasterizer_), device_memory(device_memory_), filters(filters_) {
    // Allocate textures for the screen
    framebuffer_texture.resource.Create(GL_TEXTURE_2D);

    const GLuint texture = framebuffer_texture.resource.handle;
    glTextureStorage2D(texture, 1, GL_RGBA8, 1, 1);

    // Clear screen to black
    const u8 framebuffer_data[4] = {0, 0, 0, 0};
    glClearTexImage(framebuffer_texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                    framebuffer_data);
}

Layer::~Layer() = default;

GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
                            std::array<ScreenRectVertex, 4>& out_vertices,
                            ProgramManager& program_manager,
                            const Tegra::FramebufferConfig& framebuffer,
                            const Layout::FramebufferLayout& layout, bool invert_y) {
    FramebufferTextureInfo info = PrepareRenderTarget(framebuffer);
    auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height);
    GLuint texture = info.display_texture;

    auto anti_aliasing = filters.get_anti_aliasing();
    if (anti_aliasing != Settings::AntiAliasing::None) {
        glEnablei(GL_SCISSOR_TEST, 0);
        auto viewport_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width);
        auto viewport_height = Settings::values.resolution_info.ScaleUp(framebuffer_texture.height);

        glScissorIndexed(0, 0, 0, viewport_width, viewport_height);
        glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(viewport_width),
                           static_cast<GLfloat>(viewport_height));

        switch (anti_aliasing) {
        case Settings::AntiAliasing::Fxaa:
            CreateFXAA();
            texture = fxaa->Draw(program_manager, info.display_texture);
            break;
        case Settings::AntiAliasing::Smaa:
        default:
            CreateSMAA();
            texture = smaa->Draw(program_manager, info.display_texture);
            break;
        }
    }

    glDisablei(GL_SCISSOR_TEST, 0);

    if (filters.get_scaling_filter() == Settings::ScalingFilter::Fsr) {
        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};
    }

    out_matrix =
        MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));

    // 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 auto left = crop.left;
    const auto right = crop.right;
    const auto top = invert_y ? crop.bottom : crop.top;
    const auto bottom = invert_y ? crop.top : crop.bottom;

    out_vertices[0] = ScreenRectVertex(x, y, left, top);
    out_vertices[1] = ScreenRectVertex(x + w, y, right, top);
    out_vertices[2] = ScreenRectVertex(x, y + h, left, bottom);
    out_vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom);

    return texture;
}

FramebufferTextureInfo Layer::PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer) {
    // If framebuffer is provided, reload it from memory to a texture
    if (framebuffer_texture.width != static_cast<GLsizei>(framebuffer.width) ||
        framebuffer_texture.height != static_cast<GLsizei>(framebuffer.height) ||
        framebuffer_texture.pixel_format != framebuffer.pixel_format ||
        gl_framebuffer_data.empty()) {
        // Reallocate texture if the framebuffer size has changed.
        // This is expected to not happen very often and hence should not be a
        // performance problem.
        ConfigureFramebufferTexture(framebuffer);
    }

    // Load the framebuffer from memory if needed
    return LoadFBToScreenInfo(framebuffer);
}

FramebufferTextureInfo Layer::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
    const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset};
    const auto accelerated_info =
        rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride);
    if (accelerated_info) {
        return *accelerated_info;
    }

    // Reset the screen info's display texture to its own permanent texture
    FramebufferTextureInfo info{};
    info.display_texture = framebuffer_texture.resource.handle;
    info.width = framebuffer.width;
    info.height = framebuffer.height;
    info.scaled_width = framebuffer.width;
    info.scaled_height = framebuffer.height;

    // TODO(Rodrigo): Read this from HLE
    constexpr u32 block_height_log2 = 4;
    const auto pixel_format{
        VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
    const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)};
    const u64 size_in_bytes{Tegra::Texture::CalculateSize(
        true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
    const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)};
    if (host_ptr) {
        const std::span<const u8> input_data(host_ptr, size_in_bytes);
        Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel,
                                         framebuffer.width, framebuffer.height, 1,
                                         block_height_log2, 0);
    }

    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
    glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));

    // Update existing texture
    // TODO: Test what happens on hardware when you change the framebuffer dimensions so that
    //       they differ from the LCD resolution.
    // TODO: Applications could theoretically crash yuzu here by specifying too large
    //       framebuffer sizes. We should make sure that this cannot happen.
    glTextureSubImage2D(framebuffer_texture.resource.handle, 0, 0, 0, framebuffer.width,
                        framebuffer.height, framebuffer_texture.gl_format,
                        framebuffer_texture.gl_type, gl_framebuffer_data.data());

    glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);

    return info;
}

void Layer::ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer) {
    framebuffer_texture.width = framebuffer.width;
    framebuffer_texture.height = framebuffer.height;
    framebuffer_texture.pixel_format = framebuffer.pixel_format;

    const auto pixel_format{
        VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
    const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)};
    gl_framebuffer_data.resize(framebuffer_texture.width * framebuffer_texture.height *
                               bytes_per_pixel);

    GLint internal_format;
    switch (framebuffer.pixel_format) {
    case Service::android::PixelFormat::Rgba8888:
        internal_format = GL_RGBA8;
        framebuffer_texture.gl_format = GL_RGBA;
        framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
        break;
    case Service::android::PixelFormat::Rgb565:
        internal_format = GL_RGB565;
        framebuffer_texture.gl_format = GL_RGB;
        framebuffer_texture.gl_type = GL_UNSIGNED_SHORT_5_6_5;
        break;
    default:
        internal_format = GL_RGBA8;
        framebuffer_texture.gl_format = GL_RGBA;
        framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
        // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
        //                   static_cast<u32>(framebuffer.pixel_format));
        break;
    }

    framebuffer_texture.resource.Release();
    framebuffer_texture.resource.Create(GL_TEXTURE_2D);
    glTextureStorage2D(framebuffer_texture.resource.handle, 1, internal_format,
                       framebuffer_texture.width, framebuffer_texture.height);

    fxaa.reset();
    smaa.reset();
}

void Layer::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 Layer::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));
    }
}

} // namespace OpenGL


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>
#include <vector>

#include "video_core/host1x/gpu_device_memory_manager.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"

namespace Layout {
struct FramebufferLayout;
}

struct PresentFilters;

namespace Service::android {
enum class PixelFormat : u32;
};

namespace Tegra {
struct FramebufferConfig;
}

namespace OpenGL {

struct FramebufferTextureInfo;
class FSR;
class FXAA;
class ProgramManager;
class RasterizerOpenGL;
class SMAA;

/// Structure used for storing information about the textures for the Switch screen
struct TextureInfo {
    OGLTexture resource;
    GLsizei width;
    GLsizei height;
    GLenum gl_format;
    GLenum gl_type;
    Service::android::PixelFormat pixel_format;
};

struct ScreenRectVertex;

class Layer {
public:
    explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory,
                   const PresentFilters& filters);
    ~Layer();

    GLuint ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix,
                         std::array<ScreenRectVertex, 4>& out_vertices,
                         ProgramManager& program_manager,
                         const Tegra::FramebufferConfig& framebuffer,
                         const Layout::FramebufferLayout& layout, bool invert_y);

private:
    /// Loads framebuffer from emulated memory into the active OpenGL texture.
    FramebufferTextureInfo LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
    FramebufferTextureInfo PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer);
    void ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer);

    void CreateFXAA();
    void CreateSMAA();

private:
    RasterizerOpenGL& rasterizer;
    Tegra::MaxwellDeviceMemoryManager& device_memory;
    const PresentFilters& filters;

    /// OpenGL framebuffer data
    std::vector<u8> gl_framebuffer_data;

    /// Display information for Switch screen
    TextureInfo framebuffer_texture;

    std::unique_ptr<FSR> fsr;
    std::unique_ptr<FXAA> fxaa;
    std::unique_ptr<SMAA> smaa;
};

} // namespace OpenGL


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "video_core/renderer_opengl/gl_resource_manager.h"

namespace OpenGL {

constexpr GLint PositionLocation = 0;
constexpr GLint TexCoordLocation = 1;
constexpr GLint ModelViewMatrixLocation = 0;

struct ScreenRectVertex {
    constexpr ScreenRectVertex() = default;

    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.
 */
static inline 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 OpenGL


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "video_core/host_shaders/opengl_smaa_glsl.h"
#include "video_core/host_shaders/smaa_blending_weight_calculation_frag.h"
#include "video_core/host_shaders/smaa_blending_weight_calculation_vert.h"
#include "video_core/host_shaders/smaa_edge_detection_frag.h"
#include "video_core/host_shaders/smaa_edge_detection_vert.h"
#include "video_core/host_shaders/smaa_neighborhood_blending_frag.h"
#include "video_core/host_shaders/smaa_neighborhood_blending_vert.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/smaa.h"
#include "video_core/renderer_opengl/present/util.h"
#include "video_core/smaa_area_tex.h"
#include "video_core/smaa_search_tex.h"

namespace OpenGL {

SMAA::SMAA(u32 width, u32 height) {
    const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) {
        std::string shader_source{specialized_source};
        ReplaceInclude(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL);
        return CreateProgram(shader_source, stage);
    };

    edge_detection_vert = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_VERT, GL_VERTEX_SHADER);
    edge_detection_frag = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_FRAG, GL_FRAGMENT_SHADER);
    blending_weight_calculation_vert =
        SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_VERT, GL_VERTEX_SHADER);
    blending_weight_calculation_frag =
        SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_FRAG, GL_FRAGMENT_SHADER);
    neighborhood_blending_vert =
        SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_VERT, GL_VERTEX_SHADER);
    neighborhood_blending_frag =
        SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER);

    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
    glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);

    area_tex.Create(GL_TEXTURE_2D);
    glTextureStorage2D(area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT);
    glTextureSubImage2D(area_tex.handle, 0, 0, 0, AREATEX_WIDTH, AREATEX_HEIGHT, GL_RG,
                        GL_UNSIGNED_BYTE, areaTexBytes);
    search_tex.Create(GL_TEXTURE_2D);
    glTextureStorage2D(search_tex.handle, 1, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT);
    glTextureSubImage2D(search_tex.handle, 0, 0, 0, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, GL_RED,
                        GL_UNSIGNED_BYTE, searchTexBytes);

    edges_tex.Create(GL_TEXTURE_2D);
    glTextureStorage2D(edges_tex.handle, 1, GL_RG16F, width, height);

    blend_tex.Create(GL_TEXTURE_2D);
    glTextureStorage2D(blend_tex.handle, 1, GL_RGBA16F, width, height);

    sampler = CreateBilinearSampler();

    framebuffer.Create();

    texture.Create(GL_TEXTURE_2D);
    glTextureStorage2D(texture.handle, 1, GL_RGBA16F, width, height);
    glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, texture.handle, 0);
}

SMAA::~SMAA() = default;

GLuint SMAA::Draw(ProgramManager& program_manager, GLuint input_texture) {
    glClearColor(0, 0, 0, 0);
    glFrontFace(GL_CCW);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
    glBindSampler(0, sampler.handle);
    glBindSampler(1, sampler.handle);
    glBindSampler(2, sampler.handle);

    glBindTextureUnit(0, input_texture);
    glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, edges_tex.handle, 0);
    glClear(GL_COLOR_BUFFER_BIT);
    program_manager.BindPresentPrograms(edge_detection_vert.handle, edge_detection_frag.handle);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glBindTextureUnit(0, edges_tex.handle);
    glBindTextureUnit(1, area_tex.handle);
    glBindTextureUnit(2, search_tex.handle);
    glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, blend_tex.handle, 0);
    glClear(GL_COLOR_BUFFER_BIT);
    program_manager.BindPresentPrograms(blending_weight_calculation_vert.handle,
                                        blending_weight_calculation_frag.handle);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glBindTextureUnit(0, input_texture);
    glBindTextureUnit(1, blend_tex.handle);
    glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, texture.handle, 0);
    program_manager.BindPresentPrograms(neighborhood_blending_vert.handle,
                                        neighborhood_blending_frag.handle);
    glClear(GL_COLOR_BUFFER_BIT);
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glFrontFace(GL_CW);

    return texture.handle;
}

} // namespace OpenGL


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "video_core/renderer_opengl/gl_resource_manager.h"

namespace OpenGL {

class ProgramManager;

class SMAA {
public:
    explicit SMAA(u32 width, u32 height);
    ~SMAA();

    GLuint Draw(ProgramManager& program_manager, GLuint input_texture);

private:
    OGLProgram edge_detection_vert;
    OGLProgram blending_weight_calculation_vert;
    OGLProgram neighborhood_blending_vert;
    OGLProgram edge_detection_frag;
    OGLProgram blending_weight_calculation_frag;
    OGLProgram neighborhood_blending_frag;
    OGLTexture area_tex;
    OGLTexture search_tex;
    OGLTexture edges_tex;
    OGLTexture blend_tex;
    OGLSampler sampler;
    OGLFramebuffer framebuffer;
    OGLTexture texture;
};

} // namespace OpenGL


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <string>

#include "common/assert.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"

namespace OpenGL {

static inline void ReplaceInclude(std::string& shader_source, std::string_view include_name,
                                  std::string_view include_content) {
    const std::string include_string = fmt::format("#include \"{}\"", include_name);
    const std::size_t pos = shader_source.find(include_string);
    ASSERT(pos != std::string::npos);
    shader_source.replace(pos, include_string.size(), include_content);
};

static inline OGLSampler CreateBilinearSampler() {
    OGLSampler sampler;
    sampler.Create();
    glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glSamplerParameteri(sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    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;
}

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


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/settings.h"
#include "video_core/framebuffer_config.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/layer.h"
#include "video_core/renderer_opengl/present/present_uniforms.h"
#include "video_core/renderer_opengl/present/window_adapt_pass.h"

namespace OpenGL {

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, std::list<Layer>& layers,
                                        std::span<const Tegra::FramebufferConfig> framebuffers,
                                        const Layout::FramebufferLayout& layout, bool invert_y) {
    GLint old_read_fb;
    GLint old_draw_fb;
    glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);

    const size_t layer_count = framebuffers.size();
    std::vector<GLuint> textures(layer_count);
    std::vector<std::array<GLfloat, 3 * 2>> matrices(layer_count);
    std::vector<std::array<ScreenRectVertex, 4>> vertices(layer_count);

    auto layer_it = layers.begin();
    for (size_t i = 0; i < layer_count; i++) {
        textures[i] = layer_it->ConfigureDraw(matrices[i], vertices[i], program_manager,
                                              framebuffers[i], layout, invert_y);
        layer_it++;
    }

    glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);

    program_manager.BindPresentPrograms(vert.handle, frag.handle);

    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(decltype(vertices)::value_type));
    } 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);

    for (size_t i = 0; i < layer_count; i++) {
        switch (framebuffers[i].blending) {
        case Tegra::BlendMode::Opaque:
        default:
            glDisablei(GL_BLEND, 0);
            break;
        case Tegra::BlendMode::Premultiplied:
            glEnablei(GL_BLEND, 0);
            glBlendFuncSeparatei(0, GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
            break;
        case Tegra::BlendMode::Coverage:
            glEnablei(GL_BLEND, 0);
            glBlendFuncSeparatei(0, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
            break;
        }

        glBindTextureUnit(0, textures[i]);
        glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE,
                                    matrices[i].data());
        glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices[i]), std::data(vertices[i]));
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    }
}

} // namespace OpenGL


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <list>
#include <span>

#include "common/math_util.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"

namespace Layout {
struct FramebufferLayout;
}

namespace Tegra {
struct FramebufferConfig;
}

namespace OpenGL {

class Device;
class Layer;
class ProgramManager;

class WindowAdaptPass final {
public:
    explicit WindowAdaptPass(const Device& device, OGLSampler&& sampler,
                             std::string_view frag_source);
    ~WindowAdaptPass();

    void DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers,
                           std::span<const Tegra::FramebufferConfig> framebuffers,
                           const Layout::FramebufferLayout& layout, bool invert_y);

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


code:
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <algorithm>
#include <cstddef>
#include <cstdlib>
#include <memory>

#include <glad/glad.h>

#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/settings.h"
#include "common/telemetry.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/telemetry_session.h"
#include "video_core/capture.h"
#include "video_core/present.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/renderer_opengl.h"
#include "video_core/textures/decoders.h"

namespace OpenGL {
namespace {
const char* GetSource(GLenum source) {
    switch (source) {
    case GL_DEBUG_SOURCE_API:
        return "API";
    case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
        return "WINDOW_SYSTEM";
    case GL_DEBUG_SOURCE_SHADER_COMPILER:
        return "SHADER_COMPILER";
    case GL_DEBUG_SOURCE_THIRD_PARTY:
        return "THIRD_PARTY";
    case GL_DEBUG_SOURCE_APPLICATION:
        return "APPLICATION";
    case GL_DEBUG_SOURCE_OTHER:
        return "OTHER";
    default:
        ASSERT(false);
        return "Unknown source";
    }
}

const char* GetType(GLenum type) {
    switch (type) {
    case GL_DEBUG_TYPE_ERROR:
        return "ERROR";
    case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
        return "DEPRECATED_BEHAVIOR";
    case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
        return "UNDEFINED_BEHAVIOR";
    case GL_DEBUG_TYPE_PORTABILITY:
        return "PORTABILITY";
    case GL_DEBUG_TYPE_PERFORMANCE:
        return "PERFORMANCE";
    case GL_DEBUG_TYPE_OTHER:
        return "OTHER";
    case GL_DEBUG_TYPE_MARKER:
        return "MARKER";
    default:
        ASSERT(false);
        return "Unknown type";
    }
}

void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
                           const GLchar* message, const void* user_param) {
    const char format[] = "{} {} {}: {}";
    const char* const str_source = GetSource(source);
    const char* const str_type = GetType(type);

    switch (severity) {
    case GL_DEBUG_SEVERITY_HIGH:
        LOG_CRITICAL(Render_OpenGL, format, str_source, str_type, id, message);
        break;
    case GL_DEBUG_SEVERITY_MEDIUM:
        LOG_WARNING(Render_OpenGL, format, str_source, str_type, id, message);
        break;
    case GL_DEBUG_SEVERITY_NOTIFICATION:
    case GL_DEBUG_SEVERITY_LOW:
        LOG_DEBUG(Render_OpenGL, format, str_source, str_type, id, message);
        break;
    }
}
} // Anonymous namespace

RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
                               Core::Frontend::EmuWindow& emu_window_,
                               Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_,
                               std::unique_ptr<Core::Frontend::GraphicsContext> context_)
    : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_},
      emu_window{emu_window_}, device_memory{device_memory_}, gpu{gpu_}, device{emu_window_},
      state_tracker{}, program_manager{device},
      rasterizer(emu_window, gpu, device_memory, device, program_manager, state_tracker) {
    if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) {
        glEnable(GL_DEBUG_OUTPUT);
        glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
        glDebugMessageCallback(DebugHandler, nullptr);
    }
    AddTelemetryFields();

    // Initialize default attributes to match hardware's disabled attributes
    GLint max_attribs{};
    glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_attribs);
    for (GLint attrib = 0; attrib < max_attribs; ++attrib) {
        glVertexAttrib4f(attrib, 0.0f, 0.0f, 0.0f, 1.0f);
    }
    // Enable seamless cubemaps when per texture parameters are not available
    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, PresentFiltersForDisplay);
    blit_applet =
        std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker, program_manager,
                                     device, PresentFiltersForAppletCapture);
    capture_framebuffer.Create();
    capture_renderbuffer.Create();
    glBindRenderbuffer(GL_RENDERBUFFER, capture_renderbuffer.handle);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, VideoCore::Capture::LinearWidth,
                          VideoCore::Capture::LinearHeight);
}

RendererOpenGL::~RendererOpenGL() = default;

void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
    if (framebuffers.empty()) {
        return;
    }

    RenderAppletCaptureLayer(framebuffers);
    RenderScreenshot(framebuffers);

    state_tracker.BindFramebuffer(0);
    blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout(), false);

    ++m_current_frame;

    gpu.RendererFrameEndNotify();
    rasterizer.TickFrame();

    context->SwapBuffers();
    render_window.OnFrameDisplayed();
}

void RendererOpenGL::AddTelemetryFields() {
    const char* const gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))};
    const char* const gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))};
    const char* const gpu_model{reinterpret_cast<char const*>(glGetString(GL_RENDERER))};

    LOG_INFO(Render_OpenGL, "GL_VERSION: {}", gl_version);
    LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor);
    LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model);

    constexpr auto user_system = Common::Telemetry::FieldType::UserSystem;
    telemetry_session.AddField(user_system, "GPU_Vendor", std::string(gpu_vendor));
    telemetry_session.AddField(user_system, "GPU_Model", std::string(gpu_model));
    telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version));
}

void RendererOpenGL::RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
                                    const Layout::FramebufferLayout& layout, void* dst) {
    GLint old_read_fb;
    GLint old_draw_fb;
    glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);

    // Draw the current frame to the screenshot framebuffer
    screenshot_framebuffer.Create();
    glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle);

    GLuint renderbuffer;
    glGenRenderbuffers(1, &renderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);

    blit_screen->DrawScreen(framebuffers, layout, false);

    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
    glPixelStorei(GL_PACK_ROW_LENGTH, 0);
    glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, dst);

    screenshot_framebuffer.Release();
    glDeleteRenderbuffers(1, &renderbuffer);

    glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
}

void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) {
    if (!renderer_settings.screenshot_requested) {
        return;
    }

    RenderToBuffer(framebuffers, renderer_settings.screenshot_framebuffer_layout,
                   renderer_settings.screenshot_bits);

    renderer_settings.screenshot_complete_callback(true);
    renderer_settings.screenshot_requested = false;
}

void RendererOpenGL::RenderAppletCaptureLayer(
    std::span<const Tegra::FramebufferConfig> framebuffers) {
    GLint old_read_fb;
    GLint old_draw_fb;
    glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);

    glBindFramebuffer(GL_FRAMEBUFFER, capture_framebuffer.handle);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
                              capture_renderbuffer.handle);

    blit_applet->DrawScreen(framebuffers, VideoCore::Capture::Layout, true);

    glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
}

std::vector<u8> RendererOpenGL::GetAppletCaptureBuffer() {
    using namespace VideoCore::Capture;

    std::vector<u8> linear(TiledSize);
    std::vector<u8> out(TiledSize);

    GLint old_read_fb;
    GLint old_draw_fb;
    GLint old_pixel_pack_buffer;
    GLint old_pack_row_length;
    glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
    glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &old_pixel_pack_buffer);
    glGetIntegerv(GL_PACK_ROW_LENGTH, &old_pack_row_length);

    glBindFramebuffer(GL_FRAMEBUFFER, capture_framebuffer.handle);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
                              capture_renderbuffer.handle);
    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
    glPixelStorei(GL_PACK_ROW_LENGTH, 0);
    glReadPixels(0, 0, LinearWidth, LinearHeight, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV,
                 linear.data());

    glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
    glBindBuffer(GL_PIXEL_PACK_BUFFER, old_pixel_pack_buffer);
    glPixelStorei(GL_PACK_ROW_LENGTH, old_pack_row_length);

    Tegra::Texture::SwizzleTexture(out, linear, BytesPerPixel, LinearWidth, LinearHeight,
                                   LinearDepth, BlockHeight, BlockDepth);

    return out;
}

} // namespace OpenGL


code:
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <vector>
#include <glad/glad.h>
#include "common/common_types.h"
#include "common/math_util.h"

#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
#include "video_core/renderer_opengl/gl_state_tracker.h"

namespace Core {
class System;
class TelemetrySession;
} // namespace Core

namespace Core::Frontend {
class EmuWindow;
}

namespace Tegra {
class GPU;
}

namespace OpenGL {

class BlitScreen;

class RendererOpenGL final : public VideoCore::RendererBase {
public:
    explicit RendererOpenGL(Core::TelemetrySession& telemetry_session_,
                            Core::Frontend::EmuWindow& emu_window_,
                            Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_,
                            std::unique_ptr<Core::Frontend::GraphicsContext> context_);
    ~RendererOpenGL() override;

    void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override;

    std::vector<u8> GetAppletCaptureBuffer() override;

    VideoCore::RasterizerInterface* ReadRasterizer() override {
        return &rasterizer;
    }

    [[nodiscard]] std::string GetDeviceVendor() const override {
        return device.GetVendorName();
    }

private:
    void AddTelemetryFields();

    void RenderToBuffer(std::span<const Tegra::FramebufferConfig> framebuffers,
                        const Layout::FramebufferLayout& layout, void* dst);
    void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
    void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);

    Core::TelemetrySession& telemetry_session;
    Core::Frontend::EmuWindow& emu_window;
    Tegra::MaxwellDeviceMemoryManager& device_memory;
    Tegra::GPU& gpu;

    Device device;
    StateTracker state_tracker;
    ProgramManager program_manager;
    RasterizerOpenGL rasterizer;
    OGLFramebuffer screenshot_framebuffer;
    OGLFramebuffer capture_framebuffer;
    OGLRenderbuffer capture_renderbuffer;

    std::unique_ptr<BlitScreen> blit_screen;
    std::unique_ptr<BlitScreen> blit_applet;
};

} // namespace OpenGL


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <span>
#include <string_view>

#include <glad/glad.h>

#include "common/assert.h"
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "video_core/host_shaders/astc_decoder_comp.h"
#include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h"
#include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h"
#include "video_core/host_shaders/convert_msaa_to_non_msaa_comp.h"
#include "video_core/host_shaders/convert_non_msaa_to_msaa_comp.h"
#include "video_core/host_shaders/opengl_convert_s8d24_comp.h"
#include "video_core/host_shaders/opengl_copy_bc4_comp.h"
#include "video_core/host_shaders/pitch_unswizzle_comp.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_staging_buffer_pool.h"
#include "video_core/renderer_opengl/gl_texture_cache.h"
#include "video_core/renderer_opengl/util_shaders.h"
#include "video_core/texture_cache/accelerated_swizzle.h"
#include "video_core/texture_cache/types.h"
#include "video_core/texture_cache/util.h"
#include "video_core/textures/astc.h"
#include "video_core/textures/decoders.h"

namespace OpenGL {

using namespace HostShaders;
using namespace Tegra::Texture::ASTC;

using VideoCommon::Extent2D;
using VideoCommon::Extent3D;
using VideoCommon::ImageCopy;
using VideoCommon::ImageType;
using VideoCommon::SwizzleParameters;
using VideoCommon::Accelerated::MakeBlockLinearSwizzle2DParams;
using VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams;
using VideoCore::Surface::BytesPerBlock;

namespace {
OGLProgram MakeProgram(std::string_view source) {
    return CreateProgram(source, GL_COMPUTE_SHADER);
}
} // Anonymous namespace

UtilShaders::UtilShaders(ProgramManager& program_manager_)
    : program_manager{program_manager_}, astc_decoder_program(MakeProgram(ASTC_DECODER_COMP)),
      block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)),
      block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)),
      pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)),
      copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)),
      convert_s8d24_program(MakeProgram(OPENGL_CONVERT_S8D24_COMP)),
      convert_ms_to_nonms_program(MakeProgram(CONVERT_MSAA_TO_NON_MSAA_COMP)),
      convert_nonms_to_ms_program(MakeProgram(CONVERT_NON_MSAA_TO_MSAA_COMP)) {
    const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
    swizzle_table_buffer.Create();
    glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0);
}

UtilShaders::~UtilShaders() = default;

void UtilShaders::ASTCDecode(Image& image, const StagingBufferMap& map,
                             std::span<const VideoCommon::SwizzleParameters> swizzles) {
    static constexpr GLuint BINDING_INPUT_BUFFER = 0;
    static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
    program_manager.LocalMemoryWarmup();

    const Extent2D tile_size{
        .width = VideoCore::Surface::DefaultBlockWidth(image.info.format),
        .height = VideoCore::Surface::DefaultBlockHeight(image.info.format),
    };
    program_manager.BindComputeProgram(astc_decoder_program.handle);
    glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
    glUniform2ui(1, tile_size.width, tile_size.height);

    // Ensure buffer data is valid before dispatching
    glFlush();
    for (const SwizzleParameters& swizzle : swizzles) {
        const size_t input_offset = swizzle.buffer_offset + map.offset;
        const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 8U);
        const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);

        const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
        ASSERT(params.origin == (std::array<u32, 3>{0, 0, 0}));
        ASSERT(params.destination == (std::array<s32, 3>{0, 0, 0}));
        ASSERT(params.bytes_per_block_log2 == 4);

        glUniform1ui(2, params.layer_stride);
        glUniform1ui(3, params.block_size);
        glUniform1ui(4, params.x_shift);
        glUniform1ui(5, params.block_height);
        glUniform1ui(6, params.block_height_mask);

        // ASTC texture data
        glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_INPUT_BUFFER, map.buffer, input_offset,
                          image.guest_size_bytes - swizzle.buffer_offset);
        glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), swizzle.level, GL_TRUE, 0,
                           GL_WRITE_ONLY, GL_RGBA8);

        glDispatchCompute(num_dispatches_x, num_dispatches_y, image.info.resources.layers);
    }
    // Precautionary barrier to ensure the compute shader is done decoding prior to texture access.
    // GL_TEXTURE_FETCH_BARRIER_BIT and GL_SHADER_IMAGE_ACCESS_BARRIER_BIT are used in a separate
    // glMemoryBarrier call by the texture cache runtime
    glMemoryBarrier(GL_UNIFORM_BARRIER_BIT | GL_COMMAND_BARRIER_BIT | GL_PIXEL_BUFFER_BARRIER_BIT |
                    GL_TEXTURE_UPDATE_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT |
                    GL_SHADER_STORAGE_BARRIER_BIT | GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
    program_manager.RestoreGuestCompute();
}

void UtilShaders::BlockLinearUpload2D(Image& image, const StagingBufferMap& map,
                                      std::span<const SwizzleParameters> swizzles) {
    static constexpr Extent3D WORKGROUP_SIZE{32, 32, 1};
    static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0;
    static constexpr GLuint BINDING_INPUT_BUFFER = 1;
    static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;

    program_manager.BindComputeProgram(block_linear_unswizzle_2d_program.handle);
    glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);

    const GLenum store_format = StoreFormat(BytesPerBlock(image.info.format));
    for (const SwizzleParameters& swizzle : swizzles) {
        const Extent3D num_tiles = swizzle.num_tiles;
        const size_t input_offset = swizzle.buffer_offset + map.offset;

        const u32 num_dispatches_x = Common::DivCeil(num_tiles.width, WORKGROUP_SIZE.width);
        const u32 num_dispatches_y = Common::DivCeil(num_tiles.height, WORKGROUP_SIZE.height);

        const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
        glUniform3uiv(0, 1, params.origin.data());
        glUniform3iv(1, 1, params.destination.data());
        glUniform1ui(2, params.bytes_per_block_log2);
        glUniform1ui(3, params.layer_stride);
        glUniform1ui(4, params.block_size);
        glUniform1ui(5, params.x_shift);
        glUniform1ui(6, params.block_height);
        glUniform1ui(7, params.block_height_mask);
        glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_INPUT_BUFFER, map.buffer, input_offset,
                          image.guest_size_bytes - swizzle.buffer_offset);
        glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), swizzle.level, GL_TRUE, 0,
                           GL_WRITE_ONLY, store_format);
        glDispatchCompute(num_dispatches_x, num_dispatches_y, image.info.resources.layers);
    }
    program_manager.RestoreGuestCompute();
}

void UtilShaders::BlockLinearUpload3D(Image& image, const StagingBufferMap& map,
                                      std::span<const SwizzleParameters> swizzles) {
    static constexpr Extent3D WORKGROUP_SIZE{16, 8, 8};

    static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0;
    static constexpr GLuint BINDING_INPUT_BUFFER = 1;
    static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;

    glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
    program_manager.BindComputeProgram(block_linear_unswizzle_3d_program.handle);
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);

    const GLenum store_format = StoreFormat(BytesPerBlock(image.info.format));
    for (const SwizzleParameters& swizzle : swizzles) {
        const Extent3D num_tiles = swizzle.num_tiles;
        const size_t input_offset = swizzle.buffer_offset + map.offset;

        const u32 num_dispatches_x = Common::DivCeil(num_tiles.width, WORKGROUP_SIZE.width);
        const u32 num_dispatches_y = Common::DivCeil(num_tiles.height, WORKGROUP_SIZE.height);
        const u32 num_dispatches_z = Common::DivCeil(num_tiles.depth, WORKGROUP_SIZE.depth);

        const auto params = MakeBlockLinearSwizzle3DParams(swizzle, image.info);
        glUniform3uiv(0, 1, params.origin.data());
        glUniform3iv(1, 1, params.destination.data());
        glUniform1ui(2, params.bytes_per_block_log2);
        glUniform1ui(3, params.slice_size);
        glUniform1ui(4, params.block_size);
        glUniform1ui(5, params.x_shift);
        glUniform1ui(6, params.block_height);
        glUniform1ui(7, params.block_height_mask);
        glUniform1ui(8, params.block_depth);
        glUniform1ui(9, params.block_depth_mask);
        glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_INPUT_BUFFER, map.buffer, input_offset,
                          image.guest_size_bytes - swizzle.buffer_offset);
        glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), swizzle.level, GL_TRUE, 0,
                           GL_WRITE_ONLY, store_format);
        glDispatchCompute(num_dispatches_x, num_dispatches_y, num_dispatches_z);
    }
    program_manager.RestoreGuestCompute();
}

void UtilShaders::PitchUpload(Image& image, const StagingBufferMap& map,
                              std::span<const SwizzleParameters> swizzles) {
    static constexpr Extent3D WORKGROUP_SIZE{32, 32, 1};
    static constexpr GLuint BINDING_INPUT_BUFFER = 0;
    static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
    static constexpr GLuint LOC_ORIGIN = 0;
    static constexpr GLuint LOC_DESTINATION = 1;
    static constexpr GLuint LOC_BYTES_PER_BLOCK = 2;
    static constexpr GLuint LOC_PITCH = 3;

    const u32 bytes_per_block = BytesPerBlock(image.info.format);
    const GLenum format = StoreFormat(bytes_per_block);
    const u32 pitch = image.info.pitch;

    UNIMPLEMENTED_IF_MSG(!std::has_single_bit(bytes_per_block),
                         "Non-power of two images are not implemented");

    program_manager.BindComputeProgram(pitch_unswizzle_program.handle);
    glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
    glUniform2ui(LOC_ORIGIN, 0, 0);
    glUniform2i(LOC_DESTINATION, 0, 0);
    glUniform1ui(LOC_BYTES_PER_BLOCK, bytes_per_block);
    glUniform1ui(LOC_PITCH, pitch);
    glBindImageTexture(BINDING_OUTPUT_IMAGE, image.StorageHandle(), 0, GL_FALSE, 0, GL_WRITE_ONLY,
                       format);
    for (const SwizzleParameters& swizzle : swizzles) {
        const Extent3D num_tiles = swizzle.num_tiles;
        const size_t input_offset = swizzle.buffer_offset + map.offset;

        const u32 num_dispatches_x = Common::DivCeil(num_tiles.width, WORKGROUP_SIZE.width);
        const u32 num_dispatches_y = Common::DivCeil(num_tiles.height, WORKGROUP_SIZE.height);

        glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BINDING_INPUT_BUFFER, map.buffer, input_offset,
                          image.guest_size_bytes - swizzle.buffer_offset);
        glDispatchCompute(num_dispatches_x, num_dispatches_y, 1);
    }
    program_manager.RestoreGuestCompute();
}

void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const ImageCopy> copies) {
    static constexpr GLuint BINDING_INPUT_IMAGE = 0;
    static constexpr GLuint BINDING_OUTPUT_IMAGE = 1;
    static constexpr GLuint LOC_SRC_OFFSET = 0;
    static constexpr GLuint LOC_DST_OFFSET = 1;

    program_manager.BindComputeProgram(copy_bc4_program.handle);

    for (const ImageCopy& copy : copies) {
        ASSERT(copy.src_subresource.base_layer == 0);
        ASSERT(copy.src_subresource.num_layers == 1);
        ASSERT(copy.dst_subresource.base_layer == 0);
        ASSERT(copy.dst_subresource.num_layers == 1);

        glUniform3ui(LOC_SRC_OFFSET, copy.src_offset.x, copy.src_offset.y, copy.src_offset.z);
        glUniform3ui(LOC_DST_OFFSET, copy.dst_offset.x, copy.dst_offset.y, copy.dst_offset.z);
        glBindImageTexture(BINDING_INPUT_IMAGE, src_image.StorageHandle(),
                           copy.src_subresource.base_level, GL_TRUE, 0, GL_READ_ONLY, GL_RG32UI);
        glBindImageTexture(BINDING_OUTPUT_IMAGE, dst_image.StorageHandle(),
                           copy.dst_subresource.base_level, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA8UI);
        glDispatchCompute(copy.extent.width, copy.extent.height, copy.extent.depth);
    }
    program_manager.RestoreGuestCompute();
}

void UtilShaders::ConvertS8D24(Image& dst_image, std::span<const ImageCopy> copies) {
    static constexpr GLuint BINDING_DESTINATION = 0;
    static constexpr GLuint LOC_SIZE = 0;

    program_manager.BindComputeProgram(convert_s8d24_program.handle);
    for (const ImageCopy& copy : copies) {
        ASSERT(copy.src_subresource.base_layer == 0);
        ASSERT(copy.src_subresource.num_layers == 1);
        ASSERT(copy.dst_subresource.base_layer == 0);
        ASSERT(copy.dst_subresource.num_layers == 1);

        glUniform3ui(LOC_SIZE, copy.extent.width, copy.extent.height, copy.extent.depth);
        glBindImageTexture(BINDING_DESTINATION, dst_image.StorageHandle(),
                           copy.dst_subresource.base_level, GL_TRUE, 0, GL_READ_WRITE, GL_RGBA8UI);
        glDispatchCompute(Common::DivCeil(copy.extent.width, 16u),
                          Common::DivCeil(copy.extent.height, 8u), copy.extent.depth);
    }
    program_manager.RestoreGuestCompute();
}

void UtilShaders::CopyMSAA(Image& dst_image, Image& src_image,
                           std::span<const VideoCommon::ImageCopy> copies) {
    const bool is_ms_to_non_ms = src_image.info.num_samples > 1 && dst_image.info.num_samples == 1;
    const auto program_handle =
        is_ms_to_non_ms ? convert_ms_to_nonms_program.handle : convert_nonms_to_ms_program.handle;
    program_manager.BindComputeProgram(program_handle);

    for (const ImageCopy& copy : copies) {
        ASSERT(copy.src_subresource.base_layer == 0);
        ASSERT(copy.src_subresource.num_layers == 1);
        ASSERT(copy.dst_subresource.base_layer == 0);
        ASSERT(copy.dst_subresource.num_layers == 1);

        glBindImageTexture(0, src_image.StorageHandle(), copy.src_subresource.base_level, GL_TRUE,
                           0, GL_READ_ONLY, GL_RGBA8);
        glBindImageTexture(1, dst_image.StorageHandle(), copy.dst_subresource.base_level, GL_TRUE,
                           0, GL_WRITE_ONLY, GL_RGBA8);

        const u32 num_dispatches_x = Common::DivCeil(copy.extent.width, 8U);
        const u32 num_dispatches_y = Common::DivCeil(copy.extent.height, 8U);
        const u32 num_dispatches_z = copy.extent.depth;

        glDispatchCompute(num_dispatches_x, num_dispatches_y, num_dispatches_z);
    }
    program_manager.RestoreGuestCompute();
}

GLenum StoreFormat(u32 bytes_per_block) {
    switch (bytes_per_block) {
    case 1:
        return GL_R8UI;
    case 2:
        return GL_R16UI;
    case 4:
        return GL_R32UI;
    case 8:
        return GL_RG32UI;
    case 16:
        return GL_RGBA32UI;
    }
    ASSERT(false);
    return GL_R8UI;
}

} // namespace OpenGL


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <span>

#include <glad/glad.h>

#include "common/common_types.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/texture_cache/types.h"

namespace OpenGL {

class Image;
class ProgramManager;

struct StagingBufferMap;

class UtilShaders {
public:
    explicit UtilShaders(ProgramManager& program_manager);
    ~UtilShaders();

    void ASTCDecode(Image& image, const StagingBufferMap& map,
                    std::span<const VideoCommon::SwizzleParameters> swizzles);

    void BlockLinearUpload2D(Image& image, const StagingBufferMap& map,
                             std::span<const VideoCommon::SwizzleParameters> swizzles);

    void BlockLinearUpload3D(Image& image, const StagingBufferMap& map,
                             std::span<const VideoCommon::SwizzleParameters> swizzles);

    void PitchUpload(Image& image, const StagingBufferMap& map,
                     std::span<const VideoCommon::SwizzleParameters> swizzles);

    void CopyBC4(Image& dst_image, Image& src_image,
                 std::span<const VideoCommon::ImageCopy> copies);

    void ConvertS8D24(Image& dst_image, std::span<const VideoCommon::ImageCopy> copies);

    void CopyMSAA(Image& dst_image, Image& src_image,
                  std::span<const VideoCommon::ImageCopy> copies);

private:
    ProgramManager& program_manager;

    OGLBuffer swizzle_table_buffer;

    OGLProgram astc_decoder_program;
    OGLProgram block_linear_unswizzle_2d_program;
    OGLProgram block_linear_unswizzle_3d_program;
    OGLProgram pitch_unswizzle_program;
    OGLProgram copy_bc4_program;
    OGLProgram convert_s8d24_program;
    OGLProgram convert_ms_to_nonms_program;
    OGLProgram convert_nonms_to_ms_program;
};

GLenum StoreFormat(u32 bytes_per_block);

} // namespace OpenGL