gl: Implement basic MSAA rendertarget support

- Enough to get some popular titles working.
- Some depth resolvers still need work
This commit is contained in:
kd-11 2025-02-08 18:19:44 +03:00 committed by kd-11
parent 26d85e53a4
commit 042be7d7d1
12 changed files with 309 additions and 55 deletions

View file

@ -44,6 +44,21 @@ void GLFragmentDecompilerThread::insertHeader(std::stringstream & OS)
}
}
if (properties.multisampled_sampler_mask)
{
// Requires this extension or GLSL 450
const auto driver_caps = gl::get_driver_caps();
if (driver_caps.glsl_version.version >= 450)
{
gl_version = 450;
}
else
{
ensure(driver_caps.ARB_shader_texture_image_samples, "MSAA support on OpenGL requires a driver running OpenGL 4.5 or supporting GL_ARB_shader_texture_image_samples.");
required_extensions.push_back("GL_ARB_shader_texture_image_samples");
}
}
if (m_prog.ctrl & RSX_SHADER_CONTROL_ATTRIBUTE_INTERPOLATION)
{
gl_version = std::max(gl_version, 450);
@ -110,10 +125,14 @@ void GLFragmentDecompilerThread::insertConstants(std::stringstream & OS)
const auto mask = (1 << index);
if (properties.redirected_sampler_mask & mask)
if (properties.multisampled_sampler_mask & mask)
{
// Provide a stencil view of the main resource for the S channel
OS << "uniform u" << samplerType << " " << PI.name << "_stencil;\n";
if (samplerType != "sampler1D" && samplerType != "sampler2D")
{
rsx_log.error("Unexpected multisampled image type '%s'", samplerType);
}
samplerType = "sampler2DMS";
}
else if (properties.shadow_sampler_mask & mask)
{
@ -127,6 +146,12 @@ void GLFragmentDecompilerThread::insertConstants(std::stringstream & OS)
}
}
if (properties.redirected_sampler_mask & mask)
{
// Provide a stencil view of the main resource for the S channel
OS << "uniform u" << samplerType << " " << PI.name << "_stencil;\n";
}
OS << "uniform " << samplerType << " " << PI.name << ";\n";
}
}
@ -188,6 +213,7 @@ void GLFragmentDecompilerThread::insertGlobalFunctions(std::stringstream &OS)
m_shader_props.require_wpos = !!(properties.in_register_mask & in_wpos);
m_shader_props.require_texture_ops = properties.has_tex_op;
m_shader_props.require_tex_shadow_ops = properties.shadow_sampler_mask != 0;
m_shader_props.require_msaa_ops = properties.multisampled_sampler_mask != 0;
m_shader_props.require_texture_expand = properties.has_exp_tex_op;
m_shader_props.require_srgb_to_linear = properties.has_upg;
m_shader_props.require_linear_to_srgb = properties.has_pkg;

View file

@ -50,6 +50,12 @@ GLGSRender::GLGSRender(utils::serial* ar) noexcept : GSRender(ar)
backend_config.supports_hw_a2one = false;
backend_config.supports_multidraw = true;
backend_config.supports_normalized_barycentrics = true;
if (g_cfg.video.antialiasing_level != msaa_level::none)
{
backend_config.supports_hw_msaa = true;
backend_config.supports_hw_a2c = true;
}
}
GLGSRender::~GLGSRender()

View file

@ -578,6 +578,8 @@ void gl::render_target::memory_barrier(gl::command_context& cmd, rsx::surface_ac
const auto src_bpp = src_texture->get_bpp();
rsx::typeless_xfer typeless_info{};
src_texture->memory_barrier(cmd, rsx::surface_access::transfer_read);
if (get_internal_format() == src_texture->get_internal_format())
{
// Copy data from old contents onto this one

View file

@ -171,6 +171,7 @@ struct gl_render_target_traits
result->memory_usage_flags = rsx::surface_usage_flags::attachment;
result->state_flags = rsx::surface_state_flags::erase_bkgnd;
result->sample_layout = sample_layout;
result->queue_tag(address);
result->add_ref();
return result;
@ -213,6 +214,7 @@ struct gl_render_target_traits
result->memory_usage_flags = rsx::surface_usage_flags::attachment;
result->state_flags = rsx::surface_state_flags::erase_bkgnd;
result->sample_layout = sample_layout;
result->queue_tag(address);
result->add_ref();
return result;

View file

@ -7,6 +7,8 @@ namespace gl
{
std::unordered_map<texture::internal_format, std::unique_ptr<cs_resolve_task>> g_resolve_helpers;
std::unordered_map<texture::internal_format, std::unique_ptr<cs_unresolve_task>> g_unresolve_helpers;
std::unordered_map<GLuint, std::unique_ptr<ds_resolve_pass_base>> g_depth_resolvers;
std::unordered_map<GLuint, std::unique_ptr<ds_resolve_pass_base>> g_depth_unresolvers;
static const char* get_format_string(gl::texture::internal_format format)
{
@ -36,6 +38,8 @@ namespace gl
void resolve_image(gl::command_context& cmd, gl::viewable_image* dst, gl::viewable_image* src)
{
ensure(src->samples() > 1 && dst->samples() == 1);
ensure(src->format_class() == RSX_FORMAT_CLASS_COLOR); // TODO
{
auto& job = g_resolve_helpers[src->get_internal_format()];
@ -51,7 +55,9 @@ namespace gl
void unresolve_image(gl::command_context& cmd, gl::viewable_image* dst, gl::viewable_image* src)
{
ensure(src->format_class() == RSX_FORMAT_CLASS_COLOR); // TODO
ensure(dst->samples() > 1 && src->samples() == 1);
if (src->aspect() == gl::image_aspect::color) [[ likely ]]
{
auto& job = g_unresolve_helpers[src->get_internal_format()];
if (!job)
@ -61,12 +67,53 @@ namespace gl
}
job->run(cmd, dst, src);
return;
}
auto get_unresolver_pass = [](GLuint aspect_bits) -> std::unique_ptr<ds_resolve_pass_base>&
{
auto& pass = g_depth_unresolvers[aspect_bits];
if (!pass)
{
ds_resolve_pass_base* ptr = nullptr;
switch (aspect_bits)
{
case gl::image_aspect::depth:
ptr = new depth_only_unresolver();
break;
case gl::image_aspect::stencil:
ptr = new stencil_only_unresolver();
break;
case (gl::image_aspect::depth | gl::image_aspect::stencil):
ptr = new depth_stencil_unresolver();
break;
default:
fmt::throw_exception("Unreachable");
}
pass.reset(ptr);
}
return pass;
};
if (src->aspect() == (gl::image_aspect::depth | gl::image_aspect::stencil) &&
!gl::get_driver_caps().ARB_shader_stencil_export_supported)
{
// Special handling
rsx_log.error("Unsupported.");
return;
}
auto& pass = get_unresolver_pass(src->aspect());
pass->run(cmd, dst, src);
}
// Implementation
void cs_resolve_base::build(const std::string& format_prefix, bool unresolve)
{
create();
is_unresolve = unresolve;
switch (optimal_group_size)
{
@ -86,7 +133,7 @@ namespace gl
;
static const char* unresolve_kernel =
#include "Emu/RSX/Program/MSAA/ColorResolvePass.glsl"
#include "Emu/RSX/Program/MSAA/ColorUnresolvePass.glsl"
;
const std::pair<std::string_view, std::string> syntax_replace[] =
@ -101,5 +148,98 @@ namespace gl
m_src = fmt::replace_all(m_src, syntax_replace);
rsx_log.notice("Resolve shader:\n%s", m_src);
create();
}
void cs_resolve_base::bind_resources()
{
auto msaa_view = multisampled->get_view(rsx::default_remap_vector.with_encoding(GL_REMAP_VIEW_MULTISAMPLED));
auto resolved_view = resolve->get_view(rsx::default_remap_vector.with_encoding(GL_REMAP_IDENTITY));
glBindImageTexture(GL_COMPUTE_IMAGE_SLOT(0), msaa_view->id(), 0, GL_FALSE, 0, is_unresolve ? GL_WRITE_ONLY : GL_READ_ONLY, msaa_view->view_format());
glBindImageTexture(GL_COMPUTE_IMAGE_SLOT(1), resolved_view->id(), 0, GL_FALSE, 0, is_unresolve ? GL_READ_ONLY : GL_WRITE_ONLY, resolved_view->view_format());
}
void cs_resolve_base::run(gl::command_context& cmd, gl::viewable_image* msaa_image, gl::viewable_image* resolve_image)
{
ensure(msaa_image->samples() > 1);
ensure(resolve_image->samples() == 1);
multisampled = msaa_image;
resolve = resolve_image;
const u32 invocations_x = utils::align(resolve_image->width(), cs_wave_x) / cs_wave_x;
const u32 invocations_y = utils::align(resolve_image->height(), cs_wave_y) / cs_wave_y;
compute_task::run(cmd, invocations_x, invocations_y);
}
void ds_resolve_pass_base::build(bool depth, bool stencil, bool unresolve)
{
m_config.resolve_depth = depth;
m_config.resolve_stencil = stencil;
m_config.is_unresolve = unresolve;
vs_src =
#include "Emu/RSX/Program/GLSLSnippets/GenericVSPassthrough.glsl"
;
static const char* depth_resolver =
#include "Emu/RSX/Program/MSAA/DepthResolvePass.glsl"
;
static const char* depth_unresolver =
#include "Emu/RSX/Program/MSAA/DepthUnresolvePass.glsl"
;
static const char* stencil_resolver =
#include "Emu/RSX/Program/MSAA/StencilResolvePass.glsl"
;
static const char* stencil_unresolver =
#include "Emu/RSX/Program/MSAA/StencilUnresolvePass.glsl"
;
static const char* depth_stencil_resolver =
#include "Emu/RSX/Program/MSAA/DepthStencilResolvePass.glsl"
;
static const char* depth_stencil_unresolver =
#include "Emu/RSX/Program/MSAA/DepthStencilUnresolvePass.glsl"
;
if (m_config.resolve_depth && m_config.resolve_stencil)
{
fs_src = m_config.is_unresolve ? depth_stencil_unresolver : depth_stencil_resolver;
}
else if (m_config.resolve_depth)
{
fs_src = m_config.is_unresolve ? depth_unresolver : depth_resolver;
}
else if (m_config.resolve_stencil)
{
fs_src = m_config.is_unresolve ? stencil_unresolver : stencil_resolver;
}
create();
rsx_log.notice("Resolve shader:\n%s", fs_src);
}
void ds_resolve_pass_base::run(gl::command_context& cmd, gl::viewable_image* msaa_image, gl::viewable_image* resolve_image)
{
const auto read_resource = m_config.is_unresolve ? resolve_image : msaa_image;
saved_sampler_state saved(GL_TEMP_IMAGE_SLOT(0), m_sampler);
cmd->bind_texture(GL_TEMP_IMAGE_SLOT(0), GL_TEXTURE_2D, read_resource->id());
GLuint image_aspect_bits = 0;
if (m_config.resolve_depth) image_aspect_bits |= gl::image_aspect::depth;
if (m_config.resolve_stencil) image_aspect_bits |= gl::image_aspect::stencil;
areau viewport{};
viewport.x2 = msaa_image->width();
viewport.y2 = msaa_image->height();
overlay_pass::run(cmd, viewport, GL_NONE, image_aspect_bits, false);
}
}

View file

@ -12,6 +12,7 @@ namespace gl
{
gl::viewable_image* multisampled = nullptr;
gl::viewable_image* resolve = nullptr;
bool is_unresolve = false;
u32 cs_wave_x = 1;
u32 cs_wave_y = 1;
@ -24,28 +25,9 @@ namespace gl
void build(const std::string& format_prefix, bool unresolve);
void bind_resources() override
{
auto msaa_view = multisampled->get_view(rsx::default_remap_vector.with_encoding(GL_REMAP_VIEW_MULTISAMPLED));
auto resolved_view = resolve->get_view(rsx::default_remap_vector.with_encoding(GL_REMAP_IDENTITY));
void bind_resources() override;
glBindImageTexture(GL_COMPUTE_IMAGE_SLOT(0), msaa_view->id(), 0, GL_FALSE, 0, GL_WRITE_ONLY, msaa_view->view_format());
glBindImageTexture(GL_COMPUTE_IMAGE_SLOT(1), resolved_view->id(), 0, GL_FALSE, 0, GL_WRITE_ONLY, resolved_view->view_format());
}
void run(gl::command_context& cmd, gl::viewable_image* msaa_image, gl::viewable_image* resolve_image)
{
ensure(msaa_image->samples() > 1);
ensure(resolve_image->samples() == 1);
multisampled = msaa_image;
resolve = resolve_image;
const u32 invocations_x = utils::align(resolve_image->width(), cs_wave_x) / cs_wave_x;
const u32 invocations_y = utils::align(resolve_image->height(), cs_wave_y) / cs_wave_y;
compute_task::run(cmd, invocations_x, invocations_y);
}
void run(gl::command_context& cmd, gl::viewable_image* msaa_image, gl::viewable_image* resolve_image);
};
struct cs_resolve_task : cs_resolve_base
@ -63,4 +45,69 @@ namespace gl
build(format_prefix, true);
}
};
struct ds_resolve_pass_base : overlay_pass
{
gl::viewable_image* multisampled = nullptr;
gl::viewable_image* resolve = nullptr;
struct
{
bool resolve_depth = false;
bool resolve_stencil = false;
bool is_unresolve = false;
} m_config;
void build(bool depth, bool stencil, bool unresolve);
void run(gl::command_context& cmd, gl::viewable_image* msaa_image, gl::viewable_image* resolve_image);
};
struct depth_only_resolver : ds_resolve_pass_base
{
depth_only_resolver()
{
build(true, false, false);
}
};
struct depth_only_unresolver : ds_resolve_pass_base
{
depth_only_unresolver()
{
build(true, false, true);
}
};
struct stencil_only_resolver : ds_resolve_pass_base
{
stencil_only_resolver()
{
build(false, true, false);
}
};
struct stencil_only_unresolver : ds_resolve_pass_base
{
stencil_only_unresolver()
{
build(false, true, true);
}
};
struct depth_stencil_resolver : ds_resolve_pass_base
{
depth_stencil_resolver()
{
build(true, true, false);
}
};
struct depth_stencil_unresolver : ds_resolve_pass_base
{
depth_stencil_unresolver()
{
build(true, true, true);
}
};
}

View file

@ -262,28 +262,24 @@ namespace gl
baseclass::on_miss();
}
gl::texture* target_texture = vram_texture;
u32 transfer_width = width;
u32 transfer_height = height;
if (context == rsx::texture_upload_context::framebuffer_storage)
{
auto as_rtt = static_cast<gl::render_target*>(vram_texture);
if (as_rtt->dirty()) as_rtt->read_barrier(cmd);
auto surface = gl::as_rtt(vram_texture);
surface->memory_barrier(cmd, rsx::surface_access::transfer_read);
target_texture = surface->get_surface(rsx::surface_access::transfer_read);
transfer_width *= surface->samples_x;
transfer_height *= surface->samples_y;
}
gl::texture* target_texture = vram_texture;
if ((rsx::get_resolution_scale_percent() != 100 && context == rsx::texture_upload_context::framebuffer_storage) ||
(vram_texture->pitch() != rsx_pitch))
{
u32 real_width = width;
u32 real_height = height;
if (context == rsx::texture_upload_context::framebuffer_storage)
{
auto surface = gl::as_rtt(vram_texture);
real_width *= surface->samples_x;
real_height *= surface->samples_y;
}
areai src_area = { 0, 0, 0, 0 };
const areai dst_area = { 0, 0, static_cast<s32>(real_width), static_cast<s32>(real_height) };
const areai dst_area = { 0, 0, static_cast<s32>(transfer_width), static_cast<s32>(transfer_height) };
auto ifmt = vram_texture->get_internal_format();
src_area.x2 = vram_texture->width();
@ -294,18 +290,18 @@ namespace gl
if (scaled_texture)
{
auto sfmt = scaled_texture->get_internal_format();
if (scaled_texture->width() != real_width ||
scaled_texture->height() != real_height ||
if (scaled_texture->width() != transfer_width ||
scaled_texture->height() != transfer_height ||
sfmt != ifmt)
{
//Discard current scaled texture
// Discard current scaled texture
scaled_texture.reset();
}
}
if (!scaled_texture)
{
scaled_texture = std::make_unique<gl::texture>(GL_TEXTURE_2D, real_width, real_height, 1, 1, 1, static_cast<GLenum>(ifmt), vram_texture->format_class());
scaled_texture = std::make_unique<gl::texture>(GL_TEXTURE_2D, transfer_width, transfer_height, 1, 1, 1, static_cast<GLenum>(ifmt), vram_texture->format_class());
}
const bool linear_interp = is_depth_texture() ? false : true;

View file

@ -73,7 +73,31 @@ void GLVertexDecompilerThread::insertConstants(std::stringstream& OS, const std:
continue;
}
OS << "uniform " << PT.type << " " << PI.name << ";\n";
auto type = PT.type;
if (PT.type == "sampler2D" ||
PT.type == "samplerCube" ||
PT.type == "sampler1D" ||
PT.type == "sampler3D")
{
if (m_prog.texture_state.multisampled_textures) [[ unlikely ]]
{
ensure(PI.name.length() > 3);
int index = atoi(&PI.name[3]);
if (m_prog.texture_state.multisampled_textures & (1 << index))
{
if (type != "sampler1D" && type != "sampler2D")
{
rsx_log.error("Unexpected multisampled sampler type '%s'", type);
}
type = "sampler2DMS";
}
}
}
OS << "uniform " << type << " " << PI.name << ";\n";
}
}
}

View file

@ -33,7 +33,7 @@ namespace gl
void capabilities::initialize()
{
int find_count = 17;
int find_count = 18;
int ext_count = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &ext_count);
@ -171,6 +171,13 @@ namespace gl
find_count--;
continue;
}
if (check(ext_name, "GL_ARB_shader_texture_image_samples"))
{
ARB_shader_texture_image_samples = true;
find_count--;
continue;
}
}
// Set GLSL version

View file

@ -40,6 +40,7 @@ namespace gl
bool ARB_compute_shader_supported = false;
bool NV_depth_buffer_float_supported = false;
bool NV_fragment_shader_barycentric_supported = false;
bool ARB_shader_texture_image_samples = false;
bool vendor_INTEL = false; // has broken GLSL compiler
bool vendor_AMD = false; // has broken ARB_multidraw

View file

@ -75,13 +75,16 @@ namespace gl
if (target != GL_TEXTURE_BUFFER)
{
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT);
glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, mipmaps - 1);
if (samples == 1)
{
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT);
glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, mipmaps - 1);
}
m_width = width;
m_height = height;

View file

@ -7,7 +7,7 @@ layout(local_size_x=%WORKGROUP_SIZE_X, local_size_y=%WORKGROUP_SIZE_Y, local_siz
layout(set=0, binding=0) uniform writeonly restrict image2DMS multisampled;
layout(set=0, binding=1, %IMAGE_FORMAT) uniform readonly restrict image2D resolve;
#else
layout(binding=0) uniform readonly restrict image2DMS multisampled;
layout(binding=0) uniform writeonly restrict image2DMS multisampled;
layout(binding=1, %IMAGE_FORMAT) uniform readonly restrict image2D resolve;
#endif