diff --git a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp index 7980670ca4..af51240c88 100644 --- a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp @@ -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; diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 5026fda9b3..0e352e3c9c 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -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() diff --git a/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp b/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp index 78fa7e0b82..09521bf28d 100644 --- a/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp +++ b/rpcs3/Emu/RSX/GL/GLRenderTargets.cpp @@ -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 diff --git a/rpcs3/Emu/RSX/GL/GLRenderTargets.h b/rpcs3/Emu/RSX/GL/GLRenderTargets.h index 3f4414fba3..3d573db52c 100644 --- a/rpcs3/Emu/RSX/GL/GLRenderTargets.h +++ b/rpcs3/Emu/RSX/GL/GLRenderTargets.h @@ -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; diff --git a/rpcs3/Emu/RSX/GL/GLResolveHelper.cpp b/rpcs3/Emu/RSX/GL/GLResolveHelper.cpp index fc2349429f..a8997519af 100644 --- a/rpcs3/Emu/RSX/GL/GLResolveHelper.cpp +++ b/rpcs3/Emu/RSX/GL/GLResolveHelper.cpp @@ -7,6 +7,8 @@ namespace gl { std::unordered_map> g_resolve_helpers; std::unordered_map> g_unresolve_helpers; + std::unordered_map> g_depth_resolvers; + std::unordered_map> 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& + { + 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 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); } } diff --git a/rpcs3/Emu/RSX/GL/GLResolveHelper.h b/rpcs3/Emu/RSX/GL/GLResolveHelper.h index 6190e4ee7f..d86c25d406 100644 --- a/rpcs3/Emu/RSX/GL/GLResolveHelper.h +++ b/rpcs3/Emu/RSX/GL/GLResolveHelper.h @@ -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); + } + }; } diff --git a/rpcs3/Emu/RSX/GL/GLTextureCache.h b/rpcs3/Emu/RSX/GL/GLTextureCache.h index 6bc2182de5..5d0bed44b6 100644 --- a/rpcs3/Emu/RSX/GL/GLTextureCache.h +++ b/rpcs3/Emu/RSX/GL/GLTextureCache.h @@ -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(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(real_width), static_cast(real_height) }; + const areai dst_area = { 0, 0, static_cast(transfer_width), static_cast(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_2D, real_width, real_height, 1, 1, 1, static_cast(ifmt), vram_texture->format_class()); + scaled_texture = std::make_unique(GL_TEXTURE_2D, transfer_width, transfer_height, 1, 1, 1, static_cast(ifmt), vram_texture->format_class()); } const bool linear_interp = is_depth_texture() ? false : true; diff --git a/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp b/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp index fce7786b30..23156c87e8 100644 --- a/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp +++ b/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp @@ -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"; } } } diff --git a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp index f19ab3332b..0c8de71786 100644 --- a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp @@ -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 diff --git a/rpcs3/Emu/RSX/GL/glutils/capabilities.h b/rpcs3/Emu/RSX/GL/glutils/capabilities.h index c6abfaf3b6..801a426d80 100644 --- a/rpcs3/Emu/RSX/GL/glutils/capabilities.h +++ b/rpcs3/Emu/RSX/GL/glutils/capabilities.h @@ -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 diff --git a/rpcs3/Emu/RSX/GL/glutils/image.cpp b/rpcs3/Emu/RSX/GL/glutils/image.cpp index bbba4b6de2..0e25301afe 100644 --- a/rpcs3/Emu/RSX/GL/glutils/image.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/image.cpp @@ -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; diff --git a/rpcs3/Emu/RSX/Program/MSAA/ColorUnresolvePass.glsl b/rpcs3/Emu/RSX/Program/MSAA/ColorUnresolvePass.glsl index 373d06ce0a..56ed52b402 100644 --- a/rpcs3/Emu/RSX/Program/MSAA/ColorUnresolvePass.glsl +++ b/rpcs3/Emu/RSX/Program/MSAA/ColorUnresolvePass.glsl @@ -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