diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.h b/rpcs3/Emu/RSX/Common/TextureUtils.h index 00a150499e..cf48823b75 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.h +++ b/rpcs3/Emu/RSX/Common/TextureUtils.h @@ -15,6 +15,12 @@ namespace rsx framebuffer_storage = 3 }; + enum texture_colorspace + { + rgb_linear = 0, + srgb_nonlinear = 1 + }; + //Sampled image descriptor struct sampled_image_descriptor_base { diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index a46f7ee979..fbad66f7ed 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -378,9 +378,9 @@ namespace rsx virtual image_view_type create_temporary_subresource_view(commandbuffer_type&, image_resource_type* src, u32 gcm_format, u16 x, u16 y, u16 w, u16 h) = 0; virtual image_view_type create_temporary_subresource_view(commandbuffer_type&, image_storage_type* src, u32 gcm_format, u16 x, u16 y, u16 w, u16 h) = 0; virtual section_storage_type* create_new_texture(commandbuffer_type&, u32 rsx_address, u32 rsx_size, u16 width, u16 height, u16 depth, u16 mipmaps, u32 gcm_format, - rsx::texture_upload_context context, rsx::texture_dimension_extended type, texture_create_flags flags, const std::pair, std::array>& remap_vector) = 0; + rsx::texture_upload_context context, rsx::texture_dimension_extended type, texture_create_flags flags, rsx::texture_colorspace colorspace, const std::pair, std::array>& remap_vector) = 0; virtual section_storage_type* upload_image_from_cpu(commandbuffer_type&, u32 rsx_address, u16 width, u16 height, u16 depth, u16 mipmaps, u16 pitch, u32 gcm_format, texture_upload_context context, - const std::vector& subresource_layout, rsx::texture_dimension_extended type, bool swizzled, const std::pair, std::array>& remap_vector) = 0; + const std::vector& subresource_layout, rsx::texture_dimension_extended type, rsx::texture_colorspace colorspace, bool swizzled, const std::pair, std::array>& remap_vector) = 0; virtual void enforce_surface_creation_type(section_storage_type& section, u32 gcm_format, texture_create_flags expected) = 0; virtual void set_up_remap_vector(section_storage_type& section, const std::pair, std::array>& remap_vector) = 0; virtual void insert_texture_barrier(commandbuffer_type&, image_storage_type* tex) = 0; @@ -1708,9 +1708,10 @@ namespace rsx //Invalidate with writing=false, discard=false, rebuild=false, native_flush=true invalidate_range_impl_base(texaddr, tex_size, false, false, false, true, std::forward(extras)...); + const auto colorspace = tex.gamma() ? rsx::texture_colorspace::srgb_nonlinear : rsx::texture_colorspace::rgb_linear; m_texture_memory_in_use += (tex_pitch * tex_height); return{ upload_image_from_cpu(cmd, texaddr, tex_width, tex_height, depth, tex.get_exact_mipmap_count(), tex_pitch, format, - texture_upload_context::shader_read, subresources_layout, extended_dimension, is_swizzled, remap_vector)->get_raw_view(), + texture_upload_context::shader_read, subresources_layout, extended_dimension, colorspace, is_swizzled, remap_vector)->get_raw_view(), texture_upload_context::shader_read, is_depth_format, scale_x, scale_y, extended_dimension }; } @@ -1940,7 +1941,7 @@ namespace rsx const u32 gcm_format = src_is_argb8 ? CELL_GCM_TEXTURE_A8R8G8B8 : CELL_GCM_TEXTURE_R5G6B5; vram_texture = upload_image_from_cpu(cmd, src_address, src.width, src.slice_h, 1, 1, src.pitch, gcm_format, texture_upload_context::blit_engine_src, - subresource_layout, rsx::texture_dimension_extended::texture_dimension_2d, dst.swizzled, default_remap_vector)->get_raw_texture(); + subresource_layout, rsx::texture_dimension_extended::texture_dimension_2d, rsx::texture_colorspace::rgb_linear, dst.swizzled, default_remap_vector)->get_raw_texture(); m_texture_memory_in_use += src.pitch * src.slice_h; } @@ -2054,7 +2055,7 @@ namespace rsx dest_texture = create_new_texture(cmd, dst.rsx_address, dst.pitch * dst_dimensions.height, dst_dimensions.width, dst_dimensions.height, 1, 1, gcm_format, rsx::texture_upload_context::blit_engine_dst, rsx::texture_dimension_extended::texture_dimension_2d, - channel_order, default_remap_vector)->get_raw_texture(); + channel_order, rsx::texture_colorspace::rgb_linear, default_remap_vector)->get_raw_texture(); m_texture_memory_in_use += dst.pitch * dst_dimensions.height; } diff --git a/rpcs3/Emu/RSX/GL/GLTexture.cpp b/rpcs3/Emu/RSX/GL/GLTexture.cpp index 4e6fa01d72..604c321246 100644 --- a/rpcs3/Emu/RSX/GL/GLTexture.cpp +++ b/rpcs3/Emu/RSX/GL/GLTexture.cpp @@ -90,6 +90,24 @@ namespace gl fmt::throw_exception("Compressed or unknown texture format 0x%x" HERE, texture_format); } + GLenum get_srgb_format(GLenum in_format) + { + switch (in_format) + { + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + case GL_RGBA8: + return GL_SRGB8_ALPHA8; + default: + //LOG_ERROR(RSX, "No gamma coversion for format 0x%X", in_format); + return in_format; + } + } + GLenum wrap_mode(rsx::texture_wrap_mode wrap) { switch (wrap) @@ -288,7 +306,8 @@ namespace gl return{ GL_ONE, GL_RED, GL_RED, GL_RED }; case CELL_GCM_TEXTURE_X16: - return{ GL_RED, GL_ONE, GL_RED, GL_ONE }; + //Blue component is also R (Mass Effect 3) + return{ GL_RED, GL_ONE, GL_RED, GL_RED }; case CELL_GCM_TEXTURE_X32_FLOAT: return{ GL_RED, GL_RED, GL_RED, GL_RED }; @@ -317,7 +336,8 @@ namespace gl fmt::throw_exception("Unknown format 0x%x" HERE, texture_format); } - GLuint create_texture(u32 gcm_format, u16 width, u16 height, u16 depth, u16 mipmaps, rsx::texture_dimension_extended type) + GLuint create_texture(u32 gcm_format, u16 width, u16 height, u16 depth, u16 mipmaps, + rsx::texture_dimension_extended type, rsx::texture_colorspace colorspace) { if (is_compressed_format(gcm_format)) { @@ -331,6 +351,9 @@ namespace gl GLenum target; GLenum internal_format = get_sized_internal_format(gcm_format); + if (colorspace != rsx::texture_colorspace::rgb_linear) + internal_format = get_srgb_format(internal_format); + glGenTextures(1, &id); switch (type) @@ -507,7 +530,8 @@ namespace gl } void upload_texture(GLuint id, u32 texaddr, u32 gcm_format, u16 width, u16 height, u16 depth, u16 mipmaps, bool is_swizzled, rsx::texture_dimension_extended type, - const std::vector& subresources_layout, const std::pair, std::array>& decoded_remap, bool static_state) + const std::vector& subresources_layout, const std::pair, std::array>& decoded_remap, bool static_state, + rsx::texture_colorspace colorspace) { const bool is_cubemap = type == rsx::texture_dimension_extended::texture_dimension_cubemap; @@ -564,7 +588,7 @@ namespace gl //The rest of sampler state is now handled by sampler state objects const auto format_type = get_format_type(gcm_format); - const GLenum gl_format = std::get<0>(format_type); + const GLenum gl_format = (colorspace == rsx::texture_colorspace::rgb_linear)? std::get<0>(format_type) : get_srgb_format(std::get<0>(format_type)); const GLenum gl_type = std::get<1>(format_type); fill_texture(type, mipmaps, gcm_format, width, height, depth, subresources_layout, is_swizzled, gl_format, gl_type, data_upload_buf); } diff --git a/rpcs3/Emu/RSX/GL/GLTexture.h b/rpcs3/Emu/RSX/GL/GLTexture.h index 494886ad73..3c685e4a80 100644 --- a/rpcs3/Emu/RSX/GL/GLTexture.h +++ b/rpcs3/Emu/RSX/GL/GLTexture.h @@ -17,7 +17,7 @@ namespace gl float max_aniso(rsx::texture_max_anisotropy aniso); std::array get_swizzle_remap(u32 texture_format); - GLuint create_texture(u32 gcm_format, u16 width, u16 height, u16 depth, u16 mipmaps, rsx::texture_dimension_extended type); + GLuint create_texture(u32 gcm_format, u16 width, u16 height, u16 depth, u16 mipmaps, rsx::texture_dimension_extended type, rsx::texture_colorspace colorspace); /** * is_swizzled - determines whether input bytes are in morton order @@ -28,7 +28,8 @@ namespace gl * static_state - set up the texture without consideration for sampler state (useful for vertex textures which have no real sampler state on RSX) */ void upload_texture(GLuint id, u32 texaddr, u32 gcm_format, u16 width, u16 height, u16 depth, u16 mipmaps, bool is_swizzled, rsx::texture_dimension_extended type, - const std::vector& subresources_layout, const std::pair, std::array>& decoded_remap, bool static_state); + const std::vector& subresources_layout, const std::pair, std::array>& decoded_remap, bool static_state, + rsx::texture_colorspace colorspace); void apply_swizzle_remap(GLenum target, const std::array& swizzle_remap, const std::pair, std::array>& decoded_remap); diff --git a/rpcs3/Emu/RSX/GL/GLTextureCache.h b/rpcs3/Emu/RSX/GL/GLTextureCache.h index c4cf33cb62..01e8b25e61 100644 --- a/rpcs3/Emu/RSX/GL/GLTextureCache.h +++ b/rpcs3/Emu/RSX/GL/GLTextureCache.h @@ -847,9 +847,9 @@ namespace gl cached_texture_section* create_new_texture(void*&, u32 rsx_address, u32 rsx_size, u16 width, u16 height, u16 depth, u16 mipmaps, u32 gcm_format, rsx::texture_upload_context context, rsx::texture_dimension_extended type, rsx::texture_create_flags flags, - const std::pair, std::array>& /*remap_vector*/) override + rsx::texture_colorspace colorspace, const std::pair, std::array>& /*remap_vector*/) override { - u32 vram_texture = gl::create_texture(gcm_format, width, height, depth, mipmaps, type); + u32 vram_texture = gl::create_texture(gcm_format, width, height, depth, mipmaps, type, colorspace); bool depth_flag = false; switch (gcm_format) @@ -918,12 +918,12 @@ namespace gl } cached_texture_section* upload_image_from_cpu(void*&, u32 rsx_address, u16 width, u16 height, u16 depth, u16 mipmaps, u16 pitch, u32 gcm_format, - rsx::texture_upload_context context, const std::vector& subresource_layout, rsx::texture_dimension_extended type, bool swizzled, - const std::pair, std::array>& remap_vector) override + rsx::texture_upload_context context, const std::vector& subresource_layout, rsx::texture_dimension_extended type, + rsx::texture_colorspace colorspace, bool swizzled, const std::pair, std::array>& remap_vector) override { void* unused = nullptr; auto section = create_new_texture(unused, rsx_address, pitch * height, width, height, depth, mipmaps, gcm_format, context, type, - rsx::texture_create_flags::default_component_order, remap_vector); + rsx::texture_create_flags::default_component_order, colorspace, remap_vector); bool input_swizzled = swizzled; if (context == rsx::texture_upload_context::blit_engine_src) @@ -938,7 +938,8 @@ namespace gl section->set_sampler_status(rsx::texture_sampler_status::status_ready); } - gl::upload_texture(section->get_raw_texture(), rsx_address, gcm_format, width, height, depth, mipmaps, input_swizzled, type, subresource_layout, remap_vector, false); + gl::upload_texture(section->get_raw_texture(), rsx_address, gcm_format, width, height, depth, mipmaps, + input_swizzled, type, subresource_layout, remap_vector, false, colorspace); return section; } diff --git a/rpcs3/Emu/RSX/RSXTexture.cpp b/rpcs3/Emu/RSX/RSXTexture.cpp index 9c26e6bb60..27ee22185d 100644 --- a/rpcs3/Emu/RSX/RSXTexture.cpp +++ b/rpcs3/Emu/RSX/RSXTexture.cpp @@ -193,10 +193,19 @@ namespace rsx case CELL_GCM_TEXTURE_Y16_X16_FLOAT: case CELL_GCM_TEXTURE_COMPRESSED_HILO8: case CELL_GCM_TEXTURE_COMPRESSED_HILO_S8: - //Low bit in remap control affects whether the G component should read from first or second component + { + //Low bit in remap control affects whether the G component should match R and B components //Components are usually interleaved R-G-R-G unless flag is set, then its R-R-R-G (Virtua Fighter 5) - remap_ctl = (remap_override) ?(0b01010110 | (remap_ctl & 0xFF00)): (0b01100110 | (remap_ctl & 0xFF00)); + //NOTE: The remap vector can also read from B-A-B-A in some cases (Mass Effect 3) + if (remap_override) + { + auto r_component = (remap_ctl >> 2) & 3; + remap_ctl = remap_ctl & ~(3 << 4) | r_component << 4; + } + + remap_ctl &= 0xFFFF; break; + } default: break; } diff --git a/rpcs3/Emu/RSX/VK/VKFormats.cpp b/rpcs3/Emu/RSX/VK/VKFormats.cpp index 11b4aa47e3..c15181c655 100644 --- a/rpcs3/Emu/RSX/VK/VKFormats.cpp +++ b/rpcs3/Emu/RSX/VK/VKFormats.cpp @@ -144,7 +144,8 @@ std::array get_component_mapping(u32 format) mapping = { VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R }; break; case CELL_GCM_TEXTURE_X16: - mapping = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ONE }; break; + //Blue component is also R (Mass Effect 3) + mapping = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R }; break; case CELL_GCM_TEXTURE_X32_FLOAT: mapping = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R }; break; diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.cpp b/rpcs3/Emu/RSX/VK/VKHelpers.cpp index a424604fb7..1f186c806d 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.cpp +++ b/rpcs3/Emu/RSX/VK/VKHelpers.cpp @@ -144,6 +144,23 @@ namespace vk fmt::throw_exception("Invalid or unsupported sampler format for texture format (0x%x)" HERE, format); } + VkFormat get_compatible_srgb_format(VkFormat rgb_format) + { + switch (rgb_format) + { + case VK_FORMAT_B8G8R8A8_UNORM: + return VK_FORMAT_B8G8R8A8_SRGB; + case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: + return VK_FORMAT_BC1_RGBA_SRGB_BLOCK; + case VK_FORMAT_BC2_UNORM_BLOCK: + return VK_FORMAT_BC2_SRGB_BLOCK; + case VK_FORMAT_BC3_UNORM_BLOCK: + return VK_FORMAT_BC3_SRGB_BLOCK; + default: + return rgb_format; + } + } + u8 get_format_texel_width(const VkFormat format) { switch (format) @@ -167,9 +184,13 @@ namespace vk case VK_FORMAT_A8B8G8R8_UNORM_PACK32: case VK_FORMAT_R8G8B8A8_UNORM: case VK_FORMAT_B8G8R8A8_UNORM: + case VK_FORMAT_B8G8R8A8_SRGB: case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: case VK_FORMAT_BC2_UNORM_BLOCK: case VK_FORMAT_BC3_UNORM_BLOCK: + case VK_FORMAT_BC1_RGBA_SRGB_BLOCK: + case VK_FORMAT_BC2_SRGB_BLOCK: + case VK_FORMAT_BC3_SRGB_BLOCK: return 4; case VK_FORMAT_R16G16B16A16_SFLOAT: return 8; diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.h b/rpcs3/Emu/RSX/VK/VKHelpers.h index bb50cd478f..53702c5612 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.h +++ b/rpcs3/Emu/RSX/VK/VKHelpers.h @@ -94,6 +94,7 @@ namespace vk void copy_scaled_image(VkCommandBuffer cmd, VkImage &src, VkImage &dst, VkImageLayout srcLayout, VkImageLayout dstLayout, u32 src_x_offset, u32 src_y_offset, u32 src_width, u32 src_height, u32 dst_x_offset, u32 dst_y_offset, u32 dst_width, u32 dst_height, u32 mipmaps, VkImageAspectFlagBits aspect, bool compatible_formats); VkFormat get_compatible_sampler_format(u32 format); + VkFormat get_compatible_srgb_format(VkFormat rgb_format); u8 get_format_texel_width(const VkFormat format); std::pair get_compatible_surface_format(rsx::surface_color_format color_format); size_t get_render_pass_location(VkFormat color_surface_format, VkFormat depth_stencil_format, u8 color_surface_count); diff --git a/rpcs3/Emu/RSX/VK/VKTextureCache.h b/rpcs3/Emu/RSX/VK/VKTextureCache.h index f1c9135b82..6ed540fee4 100644 --- a/rpcs3/Emu/RSX/VK/VKTextureCache.h +++ b/rpcs3/Emu/RSX/VK/VKTextureCache.h @@ -800,7 +800,7 @@ namespace vk cached_texture_section* create_new_texture(vk::command_buffer& cmd, u32 rsx_address, u32 rsx_size, u16 width, u16 height, u16 depth, u16 mipmaps, u32 gcm_format, rsx::texture_upload_context context, rsx::texture_dimension_extended type, rsx::texture_create_flags flags, - const std::pair, std::array>& remap_vector) override + rsx::texture_colorspace colorspace, const std::pair, std::array>& remap_vector) override { const u16 section_depth = depth; const bool is_cubemap = type == rsx::texture_dimension_extended::texture_dimension_cubemap; @@ -855,6 +855,9 @@ namespace vk default: aspect_flags = VK_IMAGE_ASPECT_COLOR_BIT; vk_format = get_compatible_sampler_format(gcm_format); + + if (colorspace != rsx::texture_colorspace::rgb_linear) + vk_format = get_compatible_srgb_format(vk_format); break; } @@ -898,11 +901,11 @@ namespace vk } cached_texture_section* upload_image_from_cpu(vk::command_buffer& cmd, u32 rsx_address, u16 width, u16 height, u16 depth, u16 mipmaps, u16 pitch, u32 gcm_format, - rsx::texture_upload_context context, const std::vector& subresource_layout, rsx::texture_dimension_extended type, bool swizzled, - const std::pair, std::array>& remap_vector) override + rsx::texture_upload_context context, const std::vector& subresource_layout, rsx::texture_dimension_extended type, + rsx::texture_colorspace colorspace, bool swizzled, const std::pair, std::array>& remap_vector) override { auto section = create_new_texture(cmd, rsx_address, pitch * height, width, height, depth, mipmaps, gcm_format, context, type, - rsx::texture_create_flags::default_component_order, remap_vector); + rsx::texture_create_flags::default_component_order, colorspace, remap_vector); auto image = section->get_raw_texture(); auto subres_range = section->get_raw_view()->info.subresourceRange;