diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 8018a9c55..939c06c9f 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -4,7 +4,9 @@ set(SHADER_FILES format_reinterpreter/d24s8_to_rgba8.frag + format_reinterpreter/d24s8_to_rgba8_ms.frag format_reinterpreter/rgba4_to_rgb5a1.frag + format_reinterpreter/rgba4_to_rgb5a1_ms.frag format_reinterpreter/vulkan_d24s8_to_rgba8.comp format_reinterpreter/vulkan_d24s8_to_rgba8_ms.comp texture_filtering/bicubic.frag diff --git a/src/video_core/host_shaders/format_reinterpreter/d24s8_to_rgba8_ms.frag b/src/video_core/host_shaders/format_reinterpreter/d24s8_to_rgba8_ms.frag new file mode 100644 index 000000000..ae193d93a --- /dev/null +++ b/src/video_core/host_shaders/format_reinterpreter/d24s8_to_rgba8_ms.frag @@ -0,0 +1,25 @@ +// Copyright Citra Emulator Project / Azahar Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +//? #version 430 core + +precision highp int; +precision highp float; + +layout(location = 0) in mediump vec2 tex_coord; +layout(location = 0) out lowp vec4 frag_color; + +layout(binding = 0) uniform highp sampler2DMS depth; +layout(binding = 1) uniform lowp usampler2DMS stencil; + +void main() { + mediump vec2 coord = tex_coord * vec2(textureSize(depth)); + mediump ivec2 tex_icoord = ivec2(coord); + highp uint depth_val = + uint(texelFetch(depth, tex_icoord, gl_SampleID).x * (exp2(32.0) - 1.0)); + lowp uint stencil_val = texelFetch(stencil, tex_icoord, gl_SampleID).x; + highp uvec4 components = + uvec4(stencil_val, (uvec3(depth_val) >> uvec3(24u, 16u, 8u)) & 0x000000FFu); + frag_color = vec4(components) / (exp2(8.0) - 1.0); +} diff --git a/src/video_core/host_shaders/format_reinterpreter/rgba4_to_rgb5a1_ms.frag b/src/video_core/host_shaders/format_reinterpreter/rgba4_to_rgb5a1_ms.frag new file mode 100644 index 000000000..ce2bef2c7 --- /dev/null +++ b/src/video_core/host_shaders/format_reinterpreter/rgba4_to_rgb5a1_ms.frag @@ -0,0 +1,22 @@ +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +//? #version 430 core + +precision highp int; +precision highp float; + +layout(location = 0) in mediump vec2 tex_coord; +layout(location = 0) out lowp vec4 frag_color; + +layout(binding = 0) uniform lowp sampler2D source; + +void main() { + mediump vec2 coord = tex_coord * vec2(textureSize(source, 0)); + mediump ivec2 tex_icoord = ivec2(coord); + lowp ivec4 rgba4 = ivec4(texelFetch(source, tex_icoord, 0) * (exp2(4.0) - 1.0)); + lowp ivec3 rgb5 = + ((rgba4.rgb << ivec3(1, 2, 3)) | (rgba4.gba >> ivec3(3, 2, 1))) & 0x1F; + frag_color = vec4(vec3(rgb5) / (exp2(5.0) - 1.0), rgba4.a & 0x01); +} diff --git a/src/video_core/renderer_opengl/gl_blit_helper.cpp b/src/video_core/renderer_opengl/gl_blit_helper.cpp index 1d1a66150..378d29fdf 100644 --- a/src/video_core/renderer_opengl/gl_blit_helper.cpp +++ b/src/video_core/renderer_opengl/gl_blit_helper.cpp @@ -11,7 +11,9 @@ #include "video_core/renderer_opengl/gl_texture_runtime.h" #include "video_core/host_shaders/format_reinterpreter/d24s8_to_rgba8_frag.h" +#include "video_core/host_shaders/format_reinterpreter/d24s8_to_rgba8_ms_frag.h" #include "video_core/host_shaders/format_reinterpreter/rgba4_to_rgb5a1_frag.h" +#include "video_core/host_shaders/format_reinterpreter/rgba4_to_rgb5a1_ms_frag.h" #include "video_core/host_shaders/full_screen_triangle_vert.h" #include "video_core/host_shaders/texture_filtering/bicubic_frag.h" #include "video_core/host_shaders/texture_filtering/mmpx_frag.h" @@ -65,7 +67,11 @@ BlitHelper::BlitHelper(const Driver& driver_) gradient_y_program{CreateProgram(HostShaders::Y_GRADIENT_FRAG, "Y_GRADIENT_FRAG")}, refine_program{CreateProgram(HostShaders::REFINE_FRAG, "REFINE_FRAG")}, 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")} { + d24s8_to_rgba8_ms{ + CreateProgram(HostShaders::D24S8_TO_RGBA8_MS_FRAG, "D24S8_TO_RGBA8_MS_FRAG")}, + rgba4_to_rgb5a1{CreateProgram(HostShaders::RGBA4_TO_RGB5A1_FRAG, "RGBA4_TO_RGB5A1_FRAG")}, + rgba4_to_rgb5a1_ms{ + CreateProgram(HostShaders::RGBA4_TO_RGB5A1_MS_FRAG, "RGBA4_TO_RGB5A1_MS_FRAG")} { vao.Create(); read_fbo.Create(); draw_fbo.Create(); @@ -88,46 +94,63 @@ bool BlitHelper::ConvertDS24S8ToRGBA8(Surface& source, Surface& dest, OpenGLState prev_state = OpenGLState::GetCurState(); SCOPE_EXIT({ prev_state.Apply(); }); - state.texture_units[0].texture_2d = source.Handle(); + const bool multisample = (source.sample_count > 1) && (dest.sample_count > 1); + const GLuint textarget = multisample ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; + + state.texture_units[0].texture_2d = source.Handle(multisample ? 3 : 1); + state.texture_units[0].target = textarget; state.texture_units[0].sampler = 0; state.texture_units[1].sampler = 0; if (use_texture_view) { temp_tex.Create(); glActiveTexture(GL_TEXTURE1); - glTextureView(temp_tex.handle, GL_TEXTURE_2D, source.Handle(), GL_DEPTH24_STENCIL8, 0, 1, 0, - 1); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTextureView(temp_tex.handle, textarget, source.Handle(multisample ? 3 : 1), + GL_DEPTH24_STENCIL8, 0, 1, 0, 1); + if (!multisample) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } } else if (copy.extent.width > temp_extent.width || copy.extent.height > temp_extent.height) { temp_extent = copy.extent; temp_tex.Release(); temp_tex.Create(); state.texture_units[1].texture_2d = temp_tex.handle; + state.texture_units[1].target = textarget; state.Apply(); glActiveTexture(GL_TEXTURE1); - glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, temp_extent.width, - temp_extent.height); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + if (multisample) { + glTexStorage2DMultisample(textarget, source.sample_count, GL_DEPTH24_STENCIL8, + temp_extent.width, temp_extent.height, true); + + } else { + glTexStorage2D(textarget, 1, GL_DEPTH24_STENCIL8, temp_extent.width, + temp_extent.height); + glTexParameteri(textarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(textarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } } state.texture_units[1].texture_2d = temp_tex.handle; + state.texture_units[1].target = textarget; state.Apply(); glActiveTexture(GL_TEXTURE1); if (!use_texture_view) { - glCopyImageSubData(source.Handle(), GL_TEXTURE_2D, 0, copy.src_offset.x, copy.src_offset.y, - 0, temp_tex.handle, GL_TEXTURE_2D, 0, copy.src_offset.x, + glCopyImageSubData(source.Handle(multisample ? 3 : 1), textarget, 0, copy.src_offset.x, + copy.src_offset.y, 0, temp_tex.handle, textarget, 0, copy.src_offset.x, copy.src_offset.y, 0, copy.extent.width, copy.extent.height, 1); } - glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX); + glTexParameteri(textarget, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX); const Common::Rectangle src_rect{copy.src_offset.x, copy.src_offset.y + copy.extent.height, copy.src_offset.x + copy.extent.width, copy.src_offset.x}; const Common::Rectangle dst_rect{copy.dst_offset.x, copy.dst_offset.y + copy.extent.height, copy.dst_offset.x + copy.extent.width, copy.dst_offset.x}; - SetParams(d24s8_to_rgba8, source.RealExtent(), src_rect); - Draw(d24s8_to_rgba8, dest.Handle(), draw_fbo.handle, 0, dst_rect); + + OGLProgram& blit_program = multisample ? d24s8_to_rgba8_ms : d24s8_to_rgba8; + SetParams(blit_program, source.RealExtent(), src_rect); + Draw(blit_program, dest.Handle(multisample ? 3 : 1), draw_fbo.handle, 0, dst_rect, multisample); if (use_texture_view) { temp_tex.Release(); @@ -137,6 +160,11 @@ bool BlitHelper::ConvertDS24S8ToRGBA8(Surface& source, Surface& dest, state.texture_units[0].sampler = linear_sampler.handle; state.texture_units[1].sampler = linear_sampler.handle; + if (multisample) { + // Resolve the destination image if needed + ResolveTexture(dest); + } + return true; } @@ -145,22 +173,36 @@ bool BlitHelper::ConvertRGBA4ToRGB5A1(Surface& source, Surface& dest, OpenGLState prev_state = OpenGLState::GetCurState(); SCOPE_EXIT({ prev_state.Apply(); }); - state.texture_units[0].texture_2d = source.Handle(); + const bool multisample = (source.sample_count > 1) && (dest.sample_count > 1); + + state.texture_units[0].texture_2d = source.Handle(multisample ? 3 : 1); const Common::Rectangle src_rect{copy.src_offset.x, copy.src_offset.y + copy.extent.height, copy.src_offset.x + copy.extent.width, copy.src_offset.x}; const Common::Rectangle dst_rect{copy.dst_offset.x, copy.dst_offset.y + copy.extent.height, copy.dst_offset.x + copy.extent.width, copy.dst_offset.x}; - SetParams(rgba4_to_rgb5a1, source.RealExtent(), src_rect); - Draw(rgba4_to_rgb5a1, dest.Handle(), draw_fbo.handle, 0, dst_rect); + + OGLProgram& blit_program = multisample ? rgba4_to_rgb5a1_ms : rgba4_to_rgb5a1; + SetParams(blit_program, source.RealExtent(), src_rect); + Draw(blit_program, dest.Handle(multisample ? 3 : 1), draw_fbo.handle, 0, dst_rect, multisample); + + if (multisample) { + // Resolve the destination image if needed + ResolveTexture(dest); + } return true; } void BlitHelper::ResolveTexture(Surface& surface) { + OpenGLState prev_state = OpenGLState::GetCurState(); + SCOPE_EXIT({ prev_state.Apply(); }); state.draw.read_framebuffer = read_fbo.handle; state.draw.draw_framebuffer = draw_fbo.handle; + state.texture_units[0].texture_2d = 0; + state.texture_units[1].texture_2d = 0; + state.texture_units[2].texture_2d = 0; state.Apply(); surface.Attach(GL_READ_FRAMEBUFFER, 0, 0, 3); @@ -307,7 +349,7 @@ void BlitHelper::SetParams(OGLProgram& program, const VideoCore::Extent& src_ext } void BlitHelper::Draw(OGLProgram& program, GLuint dst_tex, GLuint dst_fbo, u32 dst_level, - Common::Rectangle dst_rect) { + Common::Rectangle dst_rect, bool multisample) { state.draw.draw_framebuffer = dst_fbo; state.draw.shader_program = program.handle; state.viewport.x = dst_rect.left; @@ -316,9 +358,11 @@ void BlitHelper::Draw(OGLProgram& program, GLuint dst_tex, GLuint dst_fbo, u32 d state.viewport.height = dst_rect.GetHeight(); state.Apply(); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, + const GLuint textarget = multisample ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; + + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textarget, dst_tex, dst_level); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, textarget, 0, 0); glDrawArrays(GL_TRIANGLES, 0, 3); } diff --git a/src/video_core/renderer_opengl/gl_blit_helper.h b/src/video_core/renderer_opengl/gl_blit_helper.h index bc4047456..7eab60600 100644 --- a/src/video_core/renderer_opengl/gl_blit_helper.h +++ b/src/video_core/renderer_opengl/gl_blit_helper.h @@ -43,7 +43,7 @@ private: void SetParams(OGLProgram& program, const VideoCore::Extent& src_extent, Common::Rectangle src_rect); void Draw(OGLProgram& program, GLuint dst_tex, GLuint dst_fbo, u32 dst_level, - Common::Rectangle dst_rect); + Common::Rectangle dst_rect, bool multisample = false); private: const Driver& driver; @@ -62,7 +62,9 @@ private: OGLProgram gradient_y_program; OGLProgram refine_program; OGLProgram d24s8_to_rgba8; + OGLProgram d24s8_to_rgba8_ms; OGLProgram rgba4_to_rgb5a1; + OGLProgram rgba4_to_rgb5a1_ms; OGLTexture temp_tex; VideoCore::Extent temp_extent{}; diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.cpp b/src/video_core/renderer_opengl/gl_texture_runtime.cpp index c18e16609..75b04dab9 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.cpp +++ b/src/video_core/renderer_opengl/gl_texture_runtime.cpp @@ -101,7 +101,7 @@ static constexpr std::array CUSTOM_TUPLES = {{ ASSERT(target == GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture.handle); glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, tuple.internal_format, width, - height, false); + height, true); } else { glBindTexture(target, texture.handle); glTexStorage2D(target, levels, tuple.internal_format, width, height);