diff --git a/src/citra_qt/configuration/configure_enhancements.cpp b/src/citra_qt/configuration/configure_enhancements.cpp index 4d2d383f6..5075ddf94 100644 --- a/src/citra_qt/configuration/configure_enhancements.cpp +++ b/src/citra_qt/configuration/configure_enhancements.cpp @@ -19,10 +19,9 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent) SetConfiguration(); const auto graphics_api = Settings::values.graphics_api.GetValue(); - const bool res_scale_enabled = graphics_api != Settings::GraphicsAPI::Software; - ui->resolution_factor_combobox->setEnabled(res_scale_enabled); - const bool msaa_enabled = graphics_api == Settings::GraphicsAPI::Vulkan; - ui->antialiasing_combobox->setEnabled(msaa_enabled); + const bool hardware_graphics = graphics_api != Settings::GraphicsAPI::Software; + ui->resolution_factor_combobox->setEnabled(hardware_graphics); + ui->antialiasing_combobox->setEnabled(hardware_graphics); connect(ui->render_3d_combobox, qOverload(&QComboBox::currentIndexChanged), this, [this](int currentIndex) { diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index d81a031e9..916102e1f 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp @@ -30,13 +30,6 @@ u32 RendererBase::GetResolutionScaleFactor() { } u8 RendererBase::GetSampleCount() const { - const auto graphics_api = Settings::values.graphics_api.GetValue(); - - // Enabled for vulkan only for now - if (graphics_api != Settings::GraphicsAPI::Vulkan) { - return 1; - } - return Settings::GetAntiAliasingSampleCount(Settings::values.antialiasing.GetValue()); } diff --git a/src/video_core/renderer_opengl/gl_blit_helper.cpp b/src/video_core/renderer_opengl/gl_blit_helper.cpp index 30a502316..1d1a66150 100644 --- a/src/video_core/renderer_opengl/gl_blit_helper.cpp +++ b/src/video_core/renderer_opengl/gl_blit_helper.cpp @@ -67,6 +67,7 @@ BlitHelper::BlitHelper(const Driver& driver_) d24s8_to_rgba8{CreateProgram(HostShaders::D24S8_TO_RGBA8_FRAG, "D24S8_TO_RGBA8_FRAG")}, rgba4_to_rgb5a1{CreateProgram(HostShaders::RGBA4_TO_RGB5A1_FRAG, "RGBA4_TO_RGB5A1_FRAG")} { vao.Create(); + read_fbo.Create(); draw_fbo.Create(); state.draw.vertex_array = vao.handle; for (u32 i = 0; i < 3; i++) { @@ -156,6 +157,22 @@ bool BlitHelper::ConvertRGBA4ToRGB5A1(Surface& source, Surface& dest, return true; } +void BlitHelper::ResolveTexture(Surface& surface) { + + state.draw.read_framebuffer = read_fbo.handle; + state.draw.draw_framebuffer = draw_fbo.handle; + state.Apply(); + + surface.Attach(GL_READ_FRAMEBUFFER, 0, 0, 3); + surface.Attach(GL_DRAW_FRAMEBUFFER, 0, 0, 1); + const GLbitfield buffer_mask = surface.type == SurfaceType::Depth ? GL_DEPTH_BUFFER_BIT + : surface.type == SurfaceType::DepthStencil + ? (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) + : GL_COLOR_BUFFER_BIT; + glBlitFramebuffer(0, 0, surface.GetScaledWidth(), surface.GetScaledHeight(), 0, 0, + surface.GetScaledWidth(), surface.GetScaledHeight(), buffer_mask, GL_NEAREST); +} + bool BlitHelper::Filter(Surface& surface, const VideoCore::TextureBlit& blit) { const auto filter = Settings::values.texture_filter.GetValue(); const bool is_depth = diff --git a/src/video_core/renderer_opengl/gl_blit_helper.h b/src/video_core/renderer_opengl/gl_blit_helper.h index 054fdef84..bc4047456 100644 --- a/src/video_core/renderer_opengl/gl_blit_helper.h +++ b/src/video_core/renderer_opengl/gl_blit_helper.h @@ -1,4 +1,4 @@ -// Copyright 2023 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -31,6 +31,8 @@ public: bool ConvertRGBA4ToRGB5A1(Surface& source, Surface& dest, const VideoCore::TextureCopy& copy); + void ResolveTexture(Surface& surface); + private: void FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& blit); void FilterBicubic(Surface& surface, const VideoCore::TextureBlit& blit); @@ -47,6 +49,7 @@ private: const Driver& driver; OGLVertexArray vao; OpenGLState state; + OGLFramebuffer read_fbo; OGLFramebuffer draw_fbo; OGLSampler linear_sampler; OGLSampler nearest_sampler; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 407e2a5c0..50536da02 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -164,6 +164,10 @@ RasterizerOpenGL::RasterizerOpenGL(Memory::MemorySystem& memory, Pica::PicaCore& glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer.GetHandle()); glEnable(GL_BLEND); + + glEnable(GL_MULTISAMPLE); + glEnable(GL_SAMPLE_SHADING); + glMinSampleShading(1.0f); } RasterizerOpenGL::~RasterizerOpenGL() = default; diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.cpp b/src/video_core/renderer_opengl/gl_texture_runtime.cpp index 4ce41a0fc..863f00f82 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.cpp +++ b/src/video_core/renderer_opengl/gl_texture_runtime.cpp @@ -92,13 +92,20 @@ static constexpr std::array CUSTOM_TUPLES = {{ return 0; } -[[nodiscard]] OGLTexture MakeHandle(GLenum target, u32 width, u32 height, u32 levels, +[[nodiscard]] OGLTexture MakeHandle(GLenum target, u32 width, u32 height, u32 levels, u32 samples, const FormatTuple& tuple, std::string_view debug_name = "") { OGLTexture texture{}; texture.Create(); - glBindTexture(target, texture.handle); - glTexStorage2D(target, levels, tuple.internal_format, width, height); + if (samples > 1) { + ASSERT(target == GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture.handle); + glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, tuple.internal_format, width, + height, false); + } else { + glBindTexture(target, texture.handle); + glTexStorage2D(target, levels, tuple.internal_format, width, height); + } glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -215,6 +222,14 @@ bool TextureRuntime::ClearTextureWithoutFbo(Surface& surface, glClearTexSubImage(surface.Handle(), clear.texture_level, clear.texture_rect.left, clear.texture_rect.bottom, 0, clear.texture_rect.GetWidth(), clear.texture_rect.GetHeight(), 1, format, type, &clear.value); + + if (surface.sample_count > 1) { + // Clear MSAA too + glClearTexSubImage(surface.Handle(3), clear.texture_level, clear.texture_rect.left, + clear.texture_rect.bottom, 0, clear.texture_rect.GetWidth(), + clear.texture_rect.GetHeight(), 1, format, type, &clear.value); + } + return true; } @@ -279,6 +294,15 @@ bool TextureRuntime::CopyTextures(Surface& source, Surface& dest, bool TextureRuntime::BlitTextures(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit) { + // Must resolve images first + // Todo(wunk): Add a "dirty" flag for msaa resolves to avoid redundant image resolves + if (source.sample_count > 1) { + blit_helper.ResolveTexture(source); + } + if (dest.sample_count > 1) { + blit_helper.ResolveTexture(dest); + } + OpenGLState state = OpenGLState::GetCurState(); state.scissor.enabled = false; state.draw.read_framebuffer = read_fbos[FboIndex(source.type)].handle; @@ -329,11 +353,16 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param const GLenum target = texture_type == VideoCore::TextureType::CubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; - textures[0] = MakeHandle(target, width, height, levels, tuple, DebugName(false)); + textures[0] = MakeHandle(target, width, height, levels, 1, tuple, DebugName(false)); if (res_scale != 1) { - textures[1] = MakeHandle(target, GetScaledWidth(), GetScaledHeight(), levels, tuple, + textures[1] = MakeHandle(target, GetScaledWidth(), GetScaledHeight(), levels, 1, tuple, DebugName(true, false)); } + + if (sample_count > 1) { + textures[3] = MakeHandle(target, GetScaledWidth(), GetScaledHeight(), levels, sample_count, + tuple, DebugName(true, false, sample_count)); + } } Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface, @@ -351,15 +380,19 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface custom_format = mat->format; material = mat; - textures[0] = MakeHandle(target, mat->width, mat->height, levels, tuple, DebugName(false)); + textures[0] = MakeHandle(target, mat->width, mat->height, levels, 1, tuple, DebugName(false)); if (res_scale != 1) { - textures[1] = MakeHandle(target, mat->width, mat->height, levels, DEFAULT_TUPLE, + textures[1] = MakeHandle(target, mat->width, mat->height, levels, 1, DEFAULT_TUPLE, DebugName(true, true)); } const bool has_normal = mat->Map(MapType::Normal); if (has_normal) { textures[2] = - MakeHandle(target, mat->width, mat->height, levels, tuple, DebugName(true, true)); + MakeHandle(target, mat->width, mat->height, levels, 1, tuple, DebugName(true, true)); + } + if (sample_count > 1) { + textures[3] = MakeHandle(target, mat->width, mat->height, sample_count, levels, + DEFAULT_TUPLE, DebugName(true, true, sample_count)); } } @@ -374,8 +407,8 @@ GLuint Surface::Handle(u32 index) const noexcept { GLuint Surface::CopyHandle() noexcept { if (!copy_texture.handle) { - copy_texture = MakeHandle(GL_TEXTURE_2D, GetScaledWidth(), GetScaledHeight(), levels, tuple, - DebugName(true)); + copy_texture = MakeHandle(GL_TEXTURE_2D, GetScaledWidth(), GetScaledHeight(), levels, 1, + tuple, DebugName(true)); } for (u32 level = 0; level < levels; level++) { @@ -534,22 +567,26 @@ bool Surface::DownloadWithoutFbo(const VideoCore::BufferTextureCopy& download, return false; } -void Surface::Attach(GLenum target, u32 level, u32 layer, bool scaled) { - const GLuint handle = Handle(static_cast(scaled)); - const GLenum textarget = texture_type == TextureType::CubeMap - ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer - : GL_TEXTURE_2D; +void Surface::Attach(GLenum target, u32 level, u32 layer, u32 handle) { + const GLuint gl_handle = Handle(handle); + GLenum textarget = texture_type == TextureType::CubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer + : GL_TEXTURE_2D; + + if (handle == 3 && sample_count > 1) { + ASSERT(texture_type == TextureType::Texture2D); + textarget = GL_TEXTURE_2D_MULTISAMPLE; + } switch (type) { case SurfaceType::Color: case SurfaceType::Texture: - glFramebufferTexture2D(target, GL_COLOR_ATTACHMENT0, textarget, handle, level); + glFramebufferTexture2D(target, GL_COLOR_ATTACHMENT0, textarget, gl_handle, level); break; case SurfaceType::Depth: - glFramebufferTexture2D(target, GL_DEPTH_ATTACHMENT, textarget, handle, level); + glFramebufferTexture2D(target, GL_DEPTH_ATTACHMENT, textarget, gl_handle, level); break; case SurfaceType::DepthStencil: - glFramebufferTexture2D(target, GL_DEPTH_STENCIL_ATTACHMENT, textarget, handle, level); + glFramebufferTexture2D(target, GL_DEPTH_STENCIL_ATTACHMENT, textarget, gl_handle, level); break; default: UNREACHABLE_MSG("Invalid surface type!"); @@ -557,17 +594,11 @@ void Surface::Attach(GLenum target, u32 level, u32 layer, bool scaled) { } void Surface::ScaleUp(u32 new_scale, u8 new_sample_count) { - if (res_scale == new_scale && sample_count == new_sample_count) { - return; - } + const bool res_scale_modified = res_scale != new_scale; + if (res_scale_modified && new_scale > 1) { - res_scale = new_scale; - sample_count = new_sample_count; - - if (res_scale > 1) { - - textures[1] = MakeHandle(GL_TEXTURE_2D, GetScaledWidth(), GetScaledHeight(), levels, tuple, - DebugName(true)); + textures[1] = MakeHandle(GL_TEXTURE_2D, GetScaledWidth(), GetScaledHeight(), levels, 1, + tuple, DebugName(true)); for (u32 level = 0; level < levels; level++) { const VideoCore::TextureBlit blit = { .src_level = level, @@ -579,8 +610,10 @@ void Surface::ScaleUp(u32 new_scale, u8 new_sample_count) { } } - if (new_sample_count > 1) { + if ((res_scale_modified || sample_count != new_sample_count) && new_sample_count > 1) { // Todo(wunk): OpenGL MSAA + textures[3] = MakeHandle(GL_TEXTURE_2D, GetScaledWidth(), GetScaledHeight(), levels, + sample_count, tuple, DebugName(true)); } } @@ -614,7 +647,8 @@ void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) { Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferParams& params, const Surface* color, const Surface* depth) : VideoCore::FramebufferParams{params}, - res_scale{color ? color->res_scale : (depth ? depth->res_scale : 1u)} { + res_scale{color ? color->res_scale : (depth ? depth->res_scale : 1u)}, + sample_count{color ? color->sample_count : (depth ? depth->sample_count : 1u)} { if (shadow_rendering && !color) { return; @@ -627,6 +661,15 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferPa attachments[1] = depth->Handle(); } + if (sample_count > 1) { + if (color) { + attachments[2] = color->Handle(3); + } + if (depth) { + attachments[3] = depth->Handle(3); + } + } + framebuffer.Create(); OpenGLState state = OpenGLState::GetCurState(); @@ -658,6 +701,27 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferPa glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); } + + if (sample_count > 1) { + if (color) { + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D_MULTISAMPLE, color ? color->Handle(3) : 0, + color_level); + } + if (depth) { + if (depth->pixel_format == PixelFormat::D24S8) { + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D_MULTISAMPLE, depth->Handle(3), + depth_level); + } else { + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D_MULTISAMPLE, depth->Handle(3), + depth_level); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_TEXTURE_2D_MULTISAMPLE, 0, 0); + } + } + } } } diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.h b/src/video_core/renderer_opengl/gl_texture_runtime.h index 434a3c464..c5120a392 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.h +++ b/src/video_core/renderer_opengl/gl_texture_runtime.h @@ -130,7 +130,7 @@ public: const VideoCore::StagingData& staging); /// Attaches a handle of surface to the specified framebuffer target - void Attach(GLenum target, u32 level, u32 layer, bool scaled = true); + void Attach(GLenum target, u32 level, u32 layer, u32 handle = 1); /// Scales up the surface to match the new resolution scale and sample-count. void ScaleUp(u32 new_scale, u8 new_sample_count); @@ -149,7 +149,7 @@ private: private: const Driver* driver; TextureRuntime* runtime; - std::array textures; + std::array textures; OGLTexture copy_texture; FormatTuple tuple; }; @@ -170,6 +170,10 @@ public: return res_scale; } + [[nodiscard]] u32 Samples() const noexcept { + return sample_count; + } + [[nodiscard]] GLuint Handle() const noexcept { return framebuffer.handle; } @@ -184,7 +188,8 @@ public: private: u32 res_scale{1}; - std::array attachments{}; + u32 sample_count{1}; + std::array attachments{}; OGLFramebuffer framebuffer; };