summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.travis/macos/build.sh2
-rw-r--r--src/audio_core/cubeb_sink.cpp3
-rw-r--r--src/core/crypto/key_manager.cpp2
-rw-r--r--src/core/file_sys/patch_manager.cpp13
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp28
-rw-r--r--src/core/loader/nsp.cpp37
-rw-r--r--src/video_core/engines/maxwell_3d.h17
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp21
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h3
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp95
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h140
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp28
-rw-r--r--src/video_core/renderer_opengl/gl_state.h19
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp14
-rw-r--r--src/video_core/textures/texture.h5
-rw-r--r--src/yuzu/configuration/configure_system.cpp121
16 files changed, 426 insertions, 122 deletions
diff --git a/.travis/macos/build.sh b/.travis/macos/build.sh
index dce12099b..19fb5d445 100755
--- a/.travis/macos/build.sh
+++ b/.travis/macos/build.sh
@@ -2,7 +2,7 @@
2 2
3set -o pipefail 3set -o pipefail
4 4
5export MACOSX_DEPLOYMENT_TARGET=10.13 5export MACOSX_DEPLOYMENT_TARGET=10.14
6export Qt5_DIR=$(brew --prefix)/opt/qt5 6export Qt5_DIR=$(brew --prefix)/opt/qt5
7export UNICORNDIR=$(pwd)/externals/unicorn 7export UNICORNDIR=$(pwd)/externals/unicorn
8export PATH="/usr/local/opt/ccache/libexec:$PATH" 8export PATH="/usr/local/opt/ccache/libexec:$PATH"
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 392039688..d31a1c844 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -121,7 +121,8 @@ CubebSink::CubebSink(std::string target_device_name) {
121 const auto collection_end{collection.device + collection.count}; 121 const auto collection_end{collection.device + collection.count};
122 const auto device{ 122 const auto device{
123 std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) { 123 std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
124 return target_device_name == info.friendly_name; 124 return info.friendly_name != nullptr &&
125 target_device_name == info.friendly_name;
125 })}; 126 })};
126 if (device != collection_end) { 127 if (device != collection_end) {
127 output_device = device->devid; 128 output_device = device->devid;
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index fefc3c747..89ae79eb3 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -395,7 +395,7 @@ static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_
395 if (base.size() < begin + length) 395 if (base.size() < begin + length)
396 return false; 396 return false;
397 return std::all_of(base.begin() + begin, base.begin() + begin + length, 397 return std::all_of(base.begin() + begin, base.begin() + begin + length,
398 [](u8 c) { return std::isdigit(c); }); 398 [](u8 c) { return std::isxdigit(c); });
399} 399}
400 400
401void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { 401void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 1f4928562..cb457b987 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -61,13 +61,12 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
61 // Game Updates 61 // Game Updates
62 const auto update_tid = GetUpdateTitleID(title_id); 62 const auto update_tid = GetUpdateTitleID(title_id);
63 const auto update = installed->GetEntry(update_tid, ContentRecordType::Program); 63 const auto update = installed->GetEntry(update_tid, ContentRecordType::Program);
64 if (update != nullptr) { 64
65 if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && 65 if (update != nullptr && update->GetExeFS() != nullptr &&
66 update->GetExeFS() != nullptr) { 66 update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
67 LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", 67 LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
68 FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); 68 FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
69 exefs = update->GetExeFS(); 69 exefs = update->GetExeFS();
70 }
71 } 70 }
72 71
73 return exefs; 72 return exefs;
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index c87721c39..c1c83a11d 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -273,8 +273,8 @@ public:
273 {0, &IFileSystem::CreateFile, "CreateFile"}, 273 {0, &IFileSystem::CreateFile, "CreateFile"},
274 {1, &IFileSystem::DeleteFile, "DeleteFile"}, 274 {1, &IFileSystem::DeleteFile, "DeleteFile"},
275 {2, &IFileSystem::CreateDirectory, "CreateDirectory"}, 275 {2, &IFileSystem::CreateDirectory, "CreateDirectory"},
276 {3, nullptr, "DeleteDirectory"}, 276 {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"},
277 {4, nullptr, "DeleteDirectoryRecursively"}, 277 {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"},
278 {5, &IFileSystem::RenameFile, "RenameFile"}, 278 {5, &IFileSystem::RenameFile, "RenameFile"},
279 {6, nullptr, "RenameDirectory"}, 279 {6, nullptr, "RenameDirectory"},
280 {7, &IFileSystem::GetEntryType, "GetEntryType"}, 280 {7, &IFileSystem::GetEntryType, "GetEntryType"},
@@ -329,6 +329,30 @@ public:
329 rb.Push(backend.CreateDirectory(name)); 329 rb.Push(backend.CreateDirectory(name));
330 } 330 }
331 331
332 void DeleteDirectory(Kernel::HLERequestContext& ctx) {
333 const IPC::RequestParser rp{ctx};
334
335 const auto file_buffer = ctx.ReadBuffer();
336 std::string name = Common::StringFromBuffer(file_buffer);
337
338 LOG_DEBUG(Service_FS, "called directory {}", name);
339
340 IPC::ResponseBuilder rb{ctx, 2};
341 rb.Push(backend.DeleteDirectory(name));
342 }
343
344 void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) {
345 const IPC::RequestParser rp{ctx};
346
347 const auto file_buffer = ctx.ReadBuffer();
348 std::string name = Common::StringFromBuffer(file_buffer);
349
350 LOG_DEBUG(Service_FS, "called directory {}", name);
351
352 IPC::ResponseBuilder rb{ctx, 2};
353 rb.Push(backend.DeleteDirectoryRecursively(name));
354 }
355
332 void RenameFile(Kernel::HLERequestContext& ctx) { 356 void RenameFile(Kernel::HLERequestContext& ctx) {
333 IPC::RequestParser rp{ctx}; 357 IPC::RequestParser rp{ctx};
334 358
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 13e57848d..080d89904 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -36,6 +36,16 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
36 36
37 std::tie(nacp_file, icon_file) = 37 std::tie(nacp_file, icon_file) =
38 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca); 38 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
39
40 if (nsp->IsExtractedType()) {
41 secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
42 } else {
43 if (title_id == 0)
44 return;
45
46 secondary_loader = std::make_unique<AppLoader_NCA>(
47 nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
48 }
39} 49}
40 50
41AppLoader_NSP::~AppLoader_NSP() = default; 51AppLoader_NSP::~AppLoader_NSP() = default;
@@ -67,26 +77,19 @@ ResultStatus AppLoader_NSP::Load(Kernel::Process& process) {
67 return ResultStatus::ErrorAlreadyLoaded; 77 return ResultStatus::ErrorAlreadyLoaded;
68 } 78 }
69 79
70 if (nsp->IsExtractedType()) { 80 if (title_id == 0)
71 secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS()); 81 return ResultStatus::ErrorNSPMissingProgramNCA;
72 } else {
73 if (title_id == 0)
74 return ResultStatus::ErrorNSPMissingProgramNCA;
75
76 secondary_loader = std::make_unique<AppLoader_NCA>(
77 nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
78 82
79 if (nsp->GetStatus() != ResultStatus::Success) 83 if (nsp->GetStatus() != ResultStatus::Success)
80 return nsp->GetStatus(); 84 return nsp->GetStatus();
81 85
82 if (nsp->GetProgramStatus(title_id) != ResultStatus::Success) 86 if (nsp->GetProgramStatus(title_id) != ResultStatus::Success)
83 return nsp->GetProgramStatus(title_id); 87 return nsp->GetProgramStatus(title_id);
84 88
85 if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) { 89 if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
86 if (!Core::Crypto::KeyManager::KeyFileExists(false)) 90 if (!Core::Crypto::KeyManager::KeyFileExists(false))
87 return ResultStatus::ErrorMissingProductionKeyFile; 91 return ResultStatus::ErrorMissingProductionKeyFile;
88 return ResultStatus::ErrorNSPMissingProgramNCA; 92 return ResultStatus::ErrorNSPMissingProgramNCA;
89 }
90 } 93 }
91 94
92 const auto result = secondary_loader->Load(process); 95 const auto result = secondary_loader->Load(process);
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 754a149fa..443affc36 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -723,7 +723,11 @@ public:
723 StencilOp stencil_back_op_zpass; 723 StencilOp stencil_back_op_zpass;
724 ComparisonOp stencil_back_func_func; 724 ComparisonOp stencil_back_func_func;
725 725
726 INSERT_PADDING_WORDS(0x17); 726 INSERT_PADDING_WORDS(0x4);
727
728 u32 framebuffer_srgb;
729
730 INSERT_PADDING_WORDS(0x12);
727 731
728 union { 732 union {
729 BitField<2, 1, u32> coord_origin; 733 BitField<2, 1, u32> coord_origin;
@@ -751,7 +755,14 @@ public:
751 }; 755 };
752 } draw; 756 } draw;
753 757
754 INSERT_PADDING_WORDS(0x6B); 758 INSERT_PADDING_WORDS(0xA);
759
760 struct {
761 u32 enabled;
762 u32 index;
763 } primitive_restart;
764
765 INSERT_PADDING_WORDS(0x5F);
755 766
756 struct { 767 struct {
757 u32 start_addr_high; 768 u32 start_addr_high;
@@ -1079,9 +1090,11 @@ ASSERT_REG_POSITION(stencil_back_op_fail, 0x566);
1079ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567); 1090ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567);
1080ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568); 1091ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568);
1081ASSERT_REG_POSITION(stencil_back_func_func, 0x569); 1092ASSERT_REG_POSITION(stencil_back_func_func, 0x569);
1093ASSERT_REG_POSITION(framebuffer_srgb, 0x56E);
1082ASSERT_REG_POSITION(point_coord_replace, 0x581); 1094ASSERT_REG_POSITION(point_coord_replace, 0x581);
1083ASSERT_REG_POSITION(code_address, 0x582); 1095ASSERT_REG_POSITION(code_address, 0x582);
1084ASSERT_REG_POSITION(draw, 0x585); 1096ASSERT_REG_POSITION(draw, 0x585);
1097ASSERT_REG_POSITION(primitive_restart, 0x591);
1085ASSERT_REG_POSITION(index_array, 0x5F2); 1098ASSERT_REG_POSITION(index_array, 0x5F2);
1086ASSERT_REG_POSITION(instanced_arrays, 0x620); 1099ASSERT_REG_POSITION(instanced_arrays, 0x620);
1087ASSERT_REG_POSITION(cull, 0x646); 1100ASSERT_REG_POSITION(cull, 0x646);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index b472f421f..cb180b93c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -418,6 +418,7 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
418 // Bind the framebuffer surfaces 418 // Bind the framebuffer surfaces
419 state.draw.draw_framebuffer = framebuffer.handle; 419 state.draw.draw_framebuffer = framebuffer.handle;
420 state.Apply(); 420 state.Apply();
421 state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
421 422
422 if (using_color_fb) { 423 if (using_color_fb) {
423 if (single_color_target) { 424 if (single_color_target) {
@@ -429,6 +430,9 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
429 // Assume that a surface will be written to if it is used as a framebuffer, even if 430 // Assume that a surface will be written to if it is used as a framebuffer, even if
430 // the shader doesn't actually write to it. 431 // the shader doesn't actually write to it.
431 color_surface->MarkAsModified(true, res_cache); 432 color_surface->MarkAsModified(true, res_cache);
433 // Workaround for and issue in nvidia drivers
434 // https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
435 state.framebuffer_srgb.enabled |= color_surface->GetSurfaceParams().srgb_conversion;
432 } 436 }
433 437
434 glFramebufferTexture2D( 438 glFramebufferTexture2D(
@@ -446,6 +450,11 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
446 // Assume that a surface will be written to if it is used as a framebuffer, even 450 // Assume that a surface will be written to if it is used as a framebuffer, even
447 // if the shader doesn't actually write to it. 451 // if the shader doesn't actually write to it.
448 color_surface->MarkAsModified(true, res_cache); 452 color_surface->MarkAsModified(true, res_cache);
453 // Enable sRGB only for supported formats
454 // Workaround for and issue in nvidia drivers
455 // https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
456 state.framebuffer_srgb.enabled |=
457 color_surface->GetSurfaceParams().srgb_conversion;
449 } 458 }
450 459
451 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); 460 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
@@ -537,7 +546,9 @@ void RasterizerOpenGL::Clear() {
537 546
538 ConfigureFramebuffers(use_color, use_depth || use_stencil, false, 547 ConfigureFramebuffers(use_color, use_depth || use_stencil, false,
539 regs.clear_buffers.RT.Value()); 548 regs.clear_buffers.RT.Value());
540 549 // Copy the sRGB setting to the clear state to avoid problem with
550 // specific driver implementations
551 clear_state.framebuffer_srgb.enabled = state.framebuffer_srgb.enabled;
541 clear_state.Apply(); 552 clear_state.Apply();
542 553
543 if (use_color) { 554 if (use_color) {
@@ -570,6 +581,7 @@ void RasterizerOpenGL::DrawArrays() {
570 SyncBlendState(); 581 SyncBlendState();
571 SyncLogicOpState(); 582 SyncLogicOpState();
572 SyncCullMode(); 583 SyncCullMode();
584 SyncPrimitiveRestart();
573 SyncDepthRange(); 585 SyncDepthRange();
574 SyncScissorTest(); 586 SyncScissorTest();
575 // Alpha Testing is synced on shaders. 587 // Alpha Testing is synced on shaders.
@@ -924,6 +936,13 @@ void RasterizerOpenGL::SyncCullMode() {
924 } 936 }
925} 937}
926 938
939void RasterizerOpenGL::SyncPrimitiveRestart() {
940 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
941
942 state.primitive_restart.enabled = regs.primitive_restart.enabled;
943 state.primitive_restart.index = regs.primitive_restart.index;
944}
945
927void RasterizerOpenGL::SyncDepthRange() { 946void RasterizerOpenGL::SyncDepthRange() {
928 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 947 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
929 948
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 731a336d5..5020a5392 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -144,6 +144,9 @@ private:
144 /// Syncs the cull mode to match the guest state 144 /// Syncs the cull mode to match the guest state
145 void SyncCullMode(); 145 void SyncCullMode();
146 146
147 /// Syncs the primitve restart to match the guest state
148 void SyncPrimitiveRestart();
149
147 /// Syncs the depth range to match the guest state 150 /// Syncs the depth range to match the guest state
148 void SyncDepthRange(); 151 void SyncDepthRange();
149 152
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 591ec7998..b057e2efa 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -40,6 +40,10 @@ static bool IsPixelFormatASTC(PixelFormat format) {
40 case PixelFormat::ASTC_2D_5X4: 40 case PixelFormat::ASTC_2D_5X4:
41 case PixelFormat::ASTC_2D_8X8: 41 case PixelFormat::ASTC_2D_8X8:
42 case PixelFormat::ASTC_2D_8X5: 42 case PixelFormat::ASTC_2D_8X5:
43 case PixelFormat::ASTC_2D_4X4_SRGB:
44 case PixelFormat::ASTC_2D_5X4_SRGB:
45 case PixelFormat::ASTC_2D_8X8_SRGB:
46 case PixelFormat::ASTC_2D_8X5_SRGB:
43 return true; 47 return true;
44 default: 48 default:
45 return false; 49 return false;
@@ -56,6 +60,14 @@ static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
56 return {8, 8}; 60 return {8, 8};
57 case PixelFormat::ASTC_2D_8X5: 61 case PixelFormat::ASTC_2D_8X5:
58 return {8, 5}; 62 return {8, 5};
63 case PixelFormat::ASTC_2D_4X4_SRGB:
64 return {4, 4};
65 case PixelFormat::ASTC_2D_5X4_SRGB:
66 return {5, 4};
67 case PixelFormat::ASTC_2D_8X8_SRGB:
68 return {8, 8};
69 case PixelFormat::ASTC_2D_8X5_SRGB:
70 return {8, 5};
59 default: 71 default:
60 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format)); 72 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
61 UNREACHABLE(); 73 UNREACHABLE();
@@ -108,8 +120,9 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const {
108 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0, 120 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
109 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, 121 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
110 params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0, 122 params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0,
111 params.pixel_format = 123 params.srgb_conversion = config.tic.IsSrgbConversionEnabled();
112 PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value()); 124 params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(),
125 params.srgb_conversion);
113 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); 126 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
114 params.type = GetFormatType(params.pixel_format); 127 params.type = GetFormatType(params.pixel_format);
115 params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); 128 params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
@@ -166,6 +179,8 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const {
166 params.block_height = 1 << config.memory_layout.block_height; 179 params.block_height = 1 << config.memory_layout.block_height;
167 params.block_depth = 1 << config.memory_layout.block_depth; 180 params.block_depth = 1 << config.memory_layout.block_depth;
168 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); 181 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
182 params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB ||
183 config.format == Tegra::RenderTargetFormat::RGBA8_SRGB;
169 params.component_type = ComponentTypeFromRenderTarget(config.format); 184 params.component_type = ComponentTypeFromRenderTarget(config.format);
170 params.type = GetFormatType(params.pixel_format); 185 params.type = GetFormatType(params.pixel_format);
171 params.width = config.width; 186 params.width = config.width;
@@ -201,6 +216,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const {
201 params.pixel_format = PixelFormatFromDepthFormat(format); 216 params.pixel_format = PixelFormatFromDepthFormat(format);
202 params.component_type = ComponentTypeFromDepthFormat(format); 217 params.component_type = ComponentTypeFromDepthFormat(format);
203 params.type = GetFormatType(params.pixel_format); 218 params.type = GetFormatType(params.pixel_format);
219 params.srgb_conversion = false;
204 params.width = zeta_width; 220 params.width = zeta_width;
205 params.height = zeta_height; 221 params.height = zeta_height;
206 params.unaligned_height = zeta_height; 222 params.unaligned_height = zeta_height;
@@ -224,6 +240,8 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const {
224 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0, 240 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
225 params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0, 241 params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0,
226 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); 242 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
243 params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB ||
244 config.format == Tegra::RenderTargetFormat::RGBA8_SRGB;
227 params.component_type = ComponentTypeFromRenderTarget(config.format); 245 params.component_type = ComponentTypeFromRenderTarget(config.format);
228 params.type = GetFormatType(params.pixel_format); 246 params.type = GetFormatType(params.pixel_format);
229 params.width = config.width; 247 params.width = config.width;
@@ -289,14 +307,29 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
289 {GL_RG16I, GL_RG_INTEGER, GL_SHORT, ComponentType::SInt, false}, // RG16I 307 {GL_RG16I, GL_RG_INTEGER, GL_SHORT, ComponentType::SInt, false}, // RG16I
290 {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S 308 {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S
291 {GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F 309 {GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F
292 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // SRGBA8 310 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm,
293 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U 311 false}, // RGBA8_SRGB
294 {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S 312 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U
295 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI 313 {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S
296 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI 314 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI
297 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 315 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI
298 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5 316 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8
299 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4 317 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5
318 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4
319 {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8
320 // Compressed sRGB formats
321 {GL_COMPRESSED_SRGB_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
322 true}, // DXT1_SRGB
323 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
324 true}, // DXT23_SRGB
325 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
326 true}, // DXT45_SRGB
327 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8,
328 ComponentType::UNorm, true}, // BC7U_SRGB
329 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4_SRGB
330 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB
331 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB
332 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB
300 333
301 // Depth formats 334 // Depth formats
302 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F 335 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -361,6 +394,10 @@ static bool IsFormatBCn(PixelFormat format) {
361 case PixelFormat::BC7U: 394 case PixelFormat::BC7U:
362 case PixelFormat::BC6H_UF16: 395 case PixelFormat::BC6H_UF16:
363 case PixelFormat::BC6H_SF16: 396 case PixelFormat::BC6H_SF16:
397 case PixelFormat::DXT1_SRGB:
398 case PixelFormat::DXT23_SRGB:
399 case PixelFormat::DXT45_SRGB:
400 case PixelFormat::BC7U_SRGB:
364 return true; 401 return true;
365 } 402 }
366 return false; 403 return false;
@@ -432,7 +469,7 @@ static constexpr GLConversionArray morton_to_gl_fns = {
432 MortonCopy<true, PixelFormat::RG16I>, 469 MortonCopy<true, PixelFormat::RG16I>,
433 MortonCopy<true, PixelFormat::RG16S>, 470 MortonCopy<true, PixelFormat::RG16S>,
434 MortonCopy<true, PixelFormat::RGB32F>, 471 MortonCopy<true, PixelFormat::RGB32F>,
435 MortonCopy<true, PixelFormat::SRGBA8>, 472 MortonCopy<true, PixelFormat::RGBA8_SRGB>,
436 MortonCopy<true, PixelFormat::RG8U>, 473 MortonCopy<true, PixelFormat::RG8U>,
437 MortonCopy<true, PixelFormat::RG8S>, 474 MortonCopy<true, PixelFormat::RG8S>,
438 MortonCopy<true, PixelFormat::RG32UI>, 475 MortonCopy<true, PixelFormat::RG32UI>,
@@ -440,6 +477,15 @@ static constexpr GLConversionArray morton_to_gl_fns = {
440 MortonCopy<true, PixelFormat::ASTC_2D_8X8>, 477 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
441 MortonCopy<true, PixelFormat::ASTC_2D_8X5>, 478 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
442 MortonCopy<true, PixelFormat::ASTC_2D_5X4>, 479 MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
480 MortonCopy<true, PixelFormat::BGRA8_SRGB>,
481 MortonCopy<true, PixelFormat::DXT1_SRGB>,
482 MortonCopy<true, PixelFormat::DXT23_SRGB>,
483 MortonCopy<true, PixelFormat::DXT45_SRGB>,
484 MortonCopy<true, PixelFormat::BC7U_SRGB>,
485 MortonCopy<true, PixelFormat::ASTC_2D_4X4_SRGB>,
486 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>,
487 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>,
488 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
443 MortonCopy<true, PixelFormat::Z32F>, 489 MortonCopy<true, PixelFormat::Z32F>,
444 MortonCopy<true, PixelFormat::Z16>, 490 MortonCopy<true, PixelFormat::Z16>,
445 MortonCopy<true, PixelFormat::Z24S8>, 491 MortonCopy<true, PixelFormat::Z24S8>,
@@ -491,7 +537,7 @@ static constexpr GLConversionArray gl_to_morton_fns = {
491 MortonCopy<false, PixelFormat::RG16I>, 537 MortonCopy<false, PixelFormat::RG16I>,
492 MortonCopy<false, PixelFormat::RG16S>, 538 MortonCopy<false, PixelFormat::RG16S>,
493 MortonCopy<false, PixelFormat::RGB32F>, 539 MortonCopy<false, PixelFormat::RGB32F>,
494 MortonCopy<false, PixelFormat::SRGBA8>, 540 MortonCopy<false, PixelFormat::RGBA8_SRGB>,
495 MortonCopy<false, PixelFormat::RG8U>, 541 MortonCopy<false, PixelFormat::RG8U>,
496 MortonCopy<false, PixelFormat::RG8S>, 542 MortonCopy<false, PixelFormat::RG8S>,
497 MortonCopy<false, PixelFormat::RG32UI>, 543 MortonCopy<false, PixelFormat::RG32UI>,
@@ -499,6 +545,15 @@ static constexpr GLConversionArray gl_to_morton_fns = {
499 nullptr, 545 nullptr,
500 nullptr, 546 nullptr,
501 nullptr, 547 nullptr,
548 MortonCopy<false, PixelFormat::BGRA8_SRGB>,
549 MortonCopy<false, PixelFormat::DXT1_SRGB>,
550 MortonCopy<false, PixelFormat::DXT23_SRGB>,
551 MortonCopy<false, PixelFormat::DXT45_SRGB>,
552 MortonCopy<false, PixelFormat::BC7U_SRGB>,
553 nullptr,
554 nullptr,
555 nullptr,
556 nullptr,
502 MortonCopy<false, PixelFormat::Z32F>, 557 MortonCopy<false, PixelFormat::Z32F>,
503 MortonCopy<false, PixelFormat::Z16>, 558 MortonCopy<false, PixelFormat::Z16>,
504 MortonCopy<false, PixelFormat::Z24S8>, 559 MortonCopy<false, PixelFormat::Z24S8>,
@@ -546,6 +601,8 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
546 OpenGLState state; 601 OpenGLState state;
547 state.draw.read_framebuffer = read_fb_handle; 602 state.draw.read_framebuffer = read_fb_handle;
548 state.draw.draw_framebuffer = draw_fb_handle; 603 state.draw.draw_framebuffer = draw_fb_handle;
604 // Set sRGB enabled if the destination surfaces need it
605 state.framebuffer_srgb.enabled = dst_params.srgb_conversion;
549 state.Apply(); 606 state.Apply();
550 607
551 u32 buffers{}; 608 u32 buffers{};
@@ -881,7 +938,11 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
881 case PixelFormat::ASTC_2D_4X4: 938 case PixelFormat::ASTC_2D_4X4:
882 case PixelFormat::ASTC_2D_8X8: 939 case PixelFormat::ASTC_2D_8X8:
883 case PixelFormat::ASTC_2D_8X5: 940 case PixelFormat::ASTC_2D_8X5:
884 case PixelFormat::ASTC_2D_5X4: { 941 case PixelFormat::ASTC_2D_5X4:
942 case PixelFormat::ASTC_2D_4X4_SRGB:
943 case PixelFormat::ASTC_2D_8X8_SRGB:
944 case PixelFormat::ASTC_2D_8X5_SRGB:
945 case PixelFormat::ASTC_2D_5X4_SRGB: {
885 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. 946 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
886 u32 block_width{}; 947 u32 block_width{};
887 u32 block_height{}; 948 u32 block_height{};
@@ -913,7 +974,9 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm
913 case PixelFormat::G8R8U: 974 case PixelFormat::G8R8U:
914 case PixelFormat::G8R8S: 975 case PixelFormat::G8R8S:
915 case PixelFormat::ASTC_2D_4X4: 976 case PixelFormat::ASTC_2D_4X4:
916 case PixelFormat::ASTC_2D_8X8: { 977 case PixelFormat::ASTC_2D_8X8:
978 case PixelFormat::ASTC_2D_4X4_SRGB:
979 case PixelFormat::ASTC_2D_8X8_SRGB: {
917 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented", 980 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
918 static_cast<u32>(pixel_format)); 981 static_cast<u32>(pixel_format));
919 UNREACHABLE(); 982 UNREACHABLE();
@@ -960,8 +1023,8 @@ void CachedSurface::FlushGLBuffer() {
960 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width)); 1023 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
961 ASSERT(!tuple.compressed); 1024 ASSERT(!tuple.compressed);
962 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 1025 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
963 glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, gl_buffer.size(), 1026 glGetTextureImage(texture.handle, 0, tuple.format, tuple.type,
964 gl_buffer.data()); 1027 static_cast<GLsizei>(gl_buffer.size()), gl_buffer.data());
965 glPixelStorei(GL_PACK_ROW_LENGTH, 0); 1028 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
966 ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width, 1029 ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width,
967 params.height); 1030 params.height);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 50a7ab47d..b4701a616 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -69,7 +69,7 @@ struct SurfaceParams {
69 RG16I = 37, 69 RG16I = 37,
70 RG16S = 38, 70 RG16S = 38,
71 RGB32F = 39, 71 RGB32F = 39,
72 SRGBA8 = 40, 72 RGBA8_SRGB = 40,
73 RG8U = 41, 73 RG8U = 41,
74 RG8S = 42, 74 RG8S = 42,
75 RG32UI = 43, 75 RG32UI = 43,
@@ -77,19 +77,28 @@ struct SurfaceParams {
77 ASTC_2D_8X8 = 45, 77 ASTC_2D_8X8 = 45,
78 ASTC_2D_8X5 = 46, 78 ASTC_2D_8X5 = 46,
79 ASTC_2D_5X4 = 47, 79 ASTC_2D_5X4 = 47,
80 BGRA8_SRGB = 48,
81 DXT1_SRGB = 49,
82 DXT23_SRGB = 50,
83 DXT45_SRGB = 51,
84 BC7U_SRGB = 52,
85 ASTC_2D_4X4_SRGB = 53,
86 ASTC_2D_8X8_SRGB = 54,
87 ASTC_2D_8X5_SRGB = 55,
88 ASTC_2D_5X4_SRGB = 56,
80 89
81 MaxColorFormat, 90 MaxColorFormat,
82 91
83 // Depth formats 92 // Depth formats
84 Z32F = 48, 93 Z32F = 57,
85 Z16 = 49, 94 Z16 = 58,
86 95
87 MaxDepthFormat, 96 MaxDepthFormat,
88 97
89 // DepthStencil formats 98 // DepthStencil formats
90 Z24S8 = 50, 99 Z24S8 = 59,
91 S8Z24 = 51, 100 S8Z24 = 60,
92 Z32FS8 = 52, 101 Z32FS8 = 61,
93 102
94 MaxDepthStencilFormat, 103 MaxDepthStencilFormat,
95 104
@@ -236,7 +245,7 @@ struct SurfaceParams {
236 1, // RG16I 245 1, // RG16I
237 1, // RG16S 246 1, // RG16S
238 1, // RGB32F 247 1, // RGB32F
239 1, // SRGBA8 248 1, // RGBA8_SRGB
240 1, // RG8U 249 1, // RG8U
241 1, // RG8S 250 1, // RG8S
242 1, // RG32UI 251 1, // RG32UI
@@ -244,6 +253,15 @@ struct SurfaceParams {
244 4, // ASTC_2D_8X8 253 4, // ASTC_2D_8X8
245 4, // ASTC_2D_8X5 254 4, // ASTC_2D_8X5
246 4, // ASTC_2D_5X4 255 4, // ASTC_2D_5X4
256 1, // BGRA8_SRGB
257 4, // DXT1_SRGB
258 4, // DXT23_SRGB
259 4, // DXT45_SRGB
260 4, // BC7U_SRGB
261 4, // ASTC_2D_4X4_SRGB
262 4, // ASTC_2D_8X8_SRGB
263 4, // ASTC_2D_8X5_SRGB
264 4, // ASTC_2D_5X4_SRGB
247 1, // Z32F 265 1, // Z32F
248 1, // Z16 266 1, // Z16
249 1, // Z24S8 267 1, // Z24S8
@@ -255,6 +273,77 @@ struct SurfaceParams {
255 return compression_factor_table[static_cast<std::size_t>(format)]; 273 return compression_factor_table[static_cast<std::size_t>(format)];
256 } 274 }
257 275
276 static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
277 if (format == PixelFormat::Invalid)
278 return 0;
279 constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
280 1, // ABGR8U
281 1, // ABGR8S
282 1, // ABGR8UI
283 1, // B5G6R5U
284 1, // A2B10G10R10U
285 1, // A1B5G5R5U
286 1, // R8U
287 1, // R8UI
288 1, // RGBA16F
289 1, // RGBA16U
290 1, // RGBA16UI
291 1, // R11FG11FB10F
292 1, // RGBA32UI
293 4, // DXT1
294 4, // DXT23
295 4, // DXT45
296 4, // DXN1
297 4, // DXN2UNORM
298 4, // DXN2SNORM
299 4, // BC7U
300 4, // BC6H_UF16
301 4, // BC6H_SF16
302 4, // ASTC_2D_4X4
303 1, // G8R8U
304 1, // G8R8S
305 1, // BGRA8
306 1, // RGBA32F
307 1, // RG32F
308 1, // R32F
309 1, // R16F
310 1, // R16U
311 1, // R16S
312 1, // R16UI
313 1, // R16I
314 1, // RG16
315 1, // RG16F
316 1, // RG16UI
317 1, // RG16I
318 1, // RG16S
319 1, // RGB32F
320 1, // RGBA8_SRGB
321 1, // RG8U
322 1, // RG8S
323 1, // RG32UI
324 1, // R32UI
325 8, // ASTC_2D_8X8
326 5, // ASTC_2D_8X5
327 4, // ASTC_2D_5X4
328 1, // BGRA8_SRGB
329 4, // DXT1_SRGB
330 4, // DXT23_SRGB
331 4, // DXT45_SRGB
332 4, // BC7U_SRGB
333 4, // ASTC_2D_4X4_SRGB
334 8, // ASTC_2D_8X8_SRGB
335 5, // ASTC_2D_8X5_SRGB
336 4, // ASTC_2D_5X4_SRGB
337 1, // Z32F
338 1, // Z16
339 1, // Z24S8
340 1, // S8Z24
341 1, // Z32FS8
342 }};
343 ASSERT(static_cast<std::size_t>(format) < block_height_table.size());
344 return block_height_table[static_cast<std::size_t>(format)];
345 }
346
258 static constexpr u32 GetFormatBpp(PixelFormat format) { 347 static constexpr u32 GetFormatBpp(PixelFormat format) {
259 if (format == PixelFormat::Invalid) 348 if (format == PixelFormat::Invalid)
260 return 0; 349 return 0;
@@ -300,7 +389,7 @@ struct SurfaceParams {
300 32, // RG16I 389 32, // RG16I
301 32, // RG16S 390 32, // RG16S
302 96, // RGB32F 391 96, // RGB32F
303 32, // SRGBA8 392 32, // RGBA8_SRGB
304 16, // RG8U 393 16, // RG8U
305 16, // RG8S 394 16, // RG8S
306 64, // RG32UI 395 64, // RG32UI
@@ -308,6 +397,15 @@ struct SurfaceParams {
308 16, // ASTC_2D_8X8 397 16, // ASTC_2D_8X8
309 32, // ASTC_2D_8X5 398 32, // ASTC_2D_8X5
310 32, // ASTC_2D_5X4 399 32, // ASTC_2D_5X4
400 32, // BGRA8_SRGB
401 64, // DXT1_SRGB
402 128, // DXT23_SRGB
403 128, // DXT45_SRGB
404 128, // BC7U
405 32, // ASTC_2D_4X4_SRGB
406 16, // ASTC_2D_8X8_SRGB
407 32, // ASTC_2D_8X5_SRGB
408 32, // ASTC_2D_5X4_SRGB
311 32, // Z32F 409 32, // Z32F
312 16, // Z16 410 16, // Z16
313 32, // Z24S8 411 32, // Z24S8
@@ -346,6 +444,7 @@ struct SurfaceParams {
346 // TODO (Hexagon12): Converting SRGBA to RGBA is a hack and doesn't completely correct the 444 // TODO (Hexagon12): Converting SRGBA to RGBA is a hack and doesn't completely correct the
347 // gamma. 445 // gamma.
348 case Tegra::RenderTargetFormat::RGBA8_SRGB: 446 case Tegra::RenderTargetFormat::RGBA8_SRGB:
447 return PixelFormat::RGBA8_SRGB;
349 case Tegra::RenderTargetFormat::RGBA8_UNORM: 448 case Tegra::RenderTargetFormat::RGBA8_UNORM:
350 return PixelFormat::ABGR8U; 449 return PixelFormat::ABGR8U;
351 case Tegra::RenderTargetFormat::RGBA8_SNORM: 450 case Tegra::RenderTargetFormat::RGBA8_SNORM:
@@ -353,6 +452,7 @@ struct SurfaceParams {
353 case Tegra::RenderTargetFormat::RGBA8_UINT: 452 case Tegra::RenderTargetFormat::RGBA8_UINT:
354 return PixelFormat::ABGR8UI; 453 return PixelFormat::ABGR8UI;
355 case Tegra::RenderTargetFormat::BGRA8_SRGB: 454 case Tegra::RenderTargetFormat::BGRA8_SRGB:
455 return PixelFormat::BGRA8_SRGB;
356 case Tegra::RenderTargetFormat::BGRA8_UNORM: 456 case Tegra::RenderTargetFormat::BGRA8_UNORM:
357 return PixelFormat::BGRA8; 457 return PixelFormat::BGRA8;
358 case Tegra::RenderTargetFormat::RGB10_A2_UNORM: 458 case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
@@ -416,10 +516,14 @@ struct SurfaceParams {
416 } 516 }
417 517
418 static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, 518 static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
419 Tegra::Texture::ComponentType component_type) { 519 Tegra::Texture::ComponentType component_type,
520 bool is_srgb) {
420 // TODO(Subv): Properly implement this 521 // TODO(Subv): Properly implement this
421 switch (format) { 522 switch (format) {
422 case Tegra::Texture::TextureFormat::A8R8G8B8: 523 case Tegra::Texture::TextureFormat::A8R8G8B8:
524 if (is_srgb) {
525 return PixelFormat::RGBA8_SRGB;
526 }
423 switch (component_type) { 527 switch (component_type) {
424 case Tegra::Texture::ComponentType::UNORM: 528 case Tegra::Texture::ComponentType::UNORM:
425 return PixelFormat::ABGR8U; 529 return PixelFormat::ABGR8U;
@@ -554,11 +658,11 @@ struct SurfaceParams {
554 case Tegra::Texture::TextureFormat::Z24S8: 658 case Tegra::Texture::TextureFormat::Z24S8:
555 return PixelFormat::Z24S8; 659 return PixelFormat::Z24S8;
556 case Tegra::Texture::TextureFormat::DXT1: 660 case Tegra::Texture::TextureFormat::DXT1:
557 return PixelFormat::DXT1; 661 return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1;
558 case Tegra::Texture::TextureFormat::DXT23: 662 case Tegra::Texture::TextureFormat::DXT23:
559 return PixelFormat::DXT23; 663 return is_srgb ? PixelFormat::DXT23_SRGB : PixelFormat::DXT23;
560 case Tegra::Texture::TextureFormat::DXT45: 664 case Tegra::Texture::TextureFormat::DXT45:
561 return PixelFormat::DXT45; 665 return is_srgb ? PixelFormat::DXT45_SRGB : PixelFormat::DXT45;
562 case Tegra::Texture::TextureFormat::DXN1: 666 case Tegra::Texture::TextureFormat::DXN1:
563 return PixelFormat::DXN1; 667 return PixelFormat::DXN1;
564 case Tegra::Texture::TextureFormat::DXN2: 668 case Tegra::Texture::TextureFormat::DXN2:
@@ -572,19 +676,19 @@ struct SurfaceParams {
572 static_cast<u32>(component_type)); 676 static_cast<u32>(component_type));
573 UNREACHABLE(); 677 UNREACHABLE();
574 case Tegra::Texture::TextureFormat::BC7U: 678 case Tegra::Texture::TextureFormat::BC7U:
575 return PixelFormat::BC7U; 679 return is_srgb ? PixelFormat::BC7U_SRGB : PixelFormat::BC7U;
576 case Tegra::Texture::TextureFormat::BC6H_UF16: 680 case Tegra::Texture::TextureFormat::BC6H_UF16:
577 return PixelFormat::BC6H_UF16; 681 return PixelFormat::BC6H_UF16;
578 case Tegra::Texture::TextureFormat::BC6H_SF16: 682 case Tegra::Texture::TextureFormat::BC6H_SF16:
579 return PixelFormat::BC6H_SF16; 683 return PixelFormat::BC6H_SF16;
580 case Tegra::Texture::TextureFormat::ASTC_2D_4X4: 684 case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
581 return PixelFormat::ASTC_2D_4X4; 685 return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4;
582 case Tegra::Texture::TextureFormat::ASTC_2D_5X4: 686 case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
583 return PixelFormat::ASTC_2D_5X4; 687 return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::ASTC_2D_5X4;
584 case Tegra::Texture::TextureFormat::ASTC_2D_8X8: 688 case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
585 return PixelFormat::ASTC_2D_8X8; 689 return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8;
586 case Tegra::Texture::TextureFormat::ASTC_2D_8X5: 690 case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
587 return PixelFormat::ASTC_2D_8X5; 691 return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5;
588 case Tegra::Texture::TextureFormat::R16_G16: 692 case Tegra::Texture::TextureFormat::R16_G16:
589 switch (component_type) { 693 switch (component_type) {
590 case Tegra::Texture::ComponentType::FLOAT: 694 case Tegra::Texture::ComponentType::FLOAT:
@@ -819,7 +923,7 @@ struct SurfaceParams {
819 SurfaceTarget target; 923 SurfaceTarget target;
820 u32 max_mip_level; 924 u32 max_mip_level;
821 bool is_layered; 925 bool is_layered;
822 926 bool srgb_conversion;
823 // Parameters used for caching 927 // Parameters used for caching
824 VAddr addr; 928 VAddr addr;
825 Tegra::GPUVAddr gpu_addr; 929 Tegra::GPUVAddr gpu_addr;
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index ba6c6919a..d8a43cc94 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -11,9 +11,10 @@
11namespace OpenGL { 11namespace OpenGL {
12 12
13OpenGLState OpenGLState::cur_state; 13OpenGLState OpenGLState::cur_state;
14 14bool OpenGLState::s_rgb_used;
15OpenGLState::OpenGLState() { 15OpenGLState::OpenGLState() {
16 // These all match default OpenGL values 16 // These all match default OpenGL values
17 framebuffer_srgb.enabled = false;
17 cull.enabled = false; 18 cull.enabled = false;
18 cull.mode = GL_BACK; 19 cull.mode = GL_BACK;
19 cull.front_face = GL_CCW; 20 cull.front_face = GL_CCW;
@@ -24,6 +25,9 @@ OpenGLState::OpenGLState() {
24 depth.depth_range_near = 0.0f; 25 depth.depth_range_near = 0.0f;
25 depth.depth_range_far = 1.0f; 26 depth.depth_range_far = 1.0f;
26 27
28 primitive_restart.enabled = false;
29 primitive_restart.index = 0;
30
27 color_mask.red_enabled = GL_TRUE; 31 color_mask.red_enabled = GL_TRUE;
28 color_mask.green_enabled = GL_TRUE; 32 color_mask.green_enabled = GL_TRUE;
29 color_mask.blue_enabled = GL_TRUE; 33 color_mask.blue_enabled = GL_TRUE;
@@ -86,6 +90,16 @@ OpenGLState::OpenGLState() {
86} 90}
87 91
88void OpenGLState::Apply() const { 92void OpenGLState::Apply() const {
93 // sRGB
94 if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) {
95 if (framebuffer_srgb.enabled) {
96 // Track if sRGB is used
97 s_rgb_used = true;
98 glEnable(GL_FRAMEBUFFER_SRGB);
99 } else {
100 glDisable(GL_FRAMEBUFFER_SRGB);
101 }
102 }
89 // Culling 103 // Culling
90 if (cull.enabled != cur_state.cull.enabled) { 104 if (cull.enabled != cur_state.cull.enabled) {
91 if (cull.enabled) { 105 if (cull.enabled) {
@@ -127,6 +141,18 @@ void OpenGLState::Apply() const {
127 glDepthRange(depth.depth_range_near, depth.depth_range_far); 141 glDepthRange(depth.depth_range_near, depth.depth_range_far);
128 } 142 }
129 143
144 // Primitive restart
145 if (primitive_restart.enabled != cur_state.primitive_restart.enabled) {
146 if (primitive_restart.enabled) {
147 glEnable(GL_PRIMITIVE_RESTART);
148 } else {
149 glDisable(GL_PRIMITIVE_RESTART);
150 }
151 }
152 if (primitive_restart.index != cur_state.primitive_restart.index) {
153 glPrimitiveRestartIndex(primitive_restart.index);
154 }
155
130 // Color mask 156 // Color mask
131 if (color_mask.red_enabled != cur_state.color_mask.red_enabled || 157 if (color_mask.red_enabled != cur_state.color_mask.red_enabled ||
132 color_mask.green_enabled != cur_state.color_mask.green_enabled || 158 color_mask.green_enabled != cur_state.color_mask.green_enabled ||
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index daf7eb533..9e2c573b5 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -36,6 +36,10 @@ constexpr TextureUnit ProcTexDiffLUT{9};
36class OpenGLState { 36class OpenGLState {
37public: 37public:
38 struct { 38 struct {
39 bool enabled; // GL_FRAMEBUFFER_SRGB
40 } framebuffer_srgb;
41
42 struct {
39 bool enabled; // GL_CULL_FACE 43 bool enabled; // GL_CULL_FACE
40 GLenum mode; // GL_CULL_FACE_MODE 44 GLenum mode; // GL_CULL_FACE_MODE
41 GLenum front_face; // GL_FRONT_FACE 45 GLenum front_face; // GL_FRONT_FACE
@@ -50,6 +54,11 @@ public:
50 } depth; 54 } depth;
51 55
52 struct { 56 struct {
57 bool enabled;
58 GLuint index;
59 } primitive_restart; // GL_PRIMITIVE_RESTART
60
61 struct {
53 GLboolean red_enabled; 62 GLboolean red_enabled;
54 GLboolean green_enabled; 63 GLboolean green_enabled;
55 GLboolean blue_enabled; 64 GLboolean blue_enabled;
@@ -156,7 +165,12 @@ public:
156 static OpenGLState GetCurState() { 165 static OpenGLState GetCurState() {
157 return cur_state; 166 return cur_state;
158 } 167 }
159 168 static bool GetsRGBUsed() {
169 return s_rgb_used;
170 }
171 static void ClearsRGBUsed() {
172 s_rgb_used = false;
173 }
160 /// Apply this state as the current OpenGL state 174 /// Apply this state as the current OpenGL state
161 void Apply() const; 175 void Apply() const;
162 176
@@ -171,6 +185,9 @@ public:
171 185
172private: 186private:
173 static OpenGLState cur_state; 187 static OpenGLState cur_state;
188 // Workaround for sRGB problems caused by
189 // QT not supporting srgb output
190 static bool s_rgb_used;
174}; 191};
175 192
176} // namespace OpenGL 193} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 96d916b07..90b68943d 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -283,7 +283,8 @@ void RendererOpenGL::CreateRasterizer() {
283 if (rasterizer) { 283 if (rasterizer) {
284 return; 284 return;
285 } 285 }
286 286 // Initialize sRGB Usage
287 OpenGLState::ClearsRGBUsed();
287 rasterizer = std::make_unique<RasterizerOpenGL>(render_window, screen_info); 288 rasterizer = std::make_unique<RasterizerOpenGL>(render_window, screen_info);
288} 289}
289 290
@@ -356,13 +357,20 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
356 357
357 state.texture_units[0].texture = screen_info.display_texture; 358 state.texture_units[0].texture = screen_info.display_texture;
358 state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}; 359 state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
360 // Workaround brigthness problems in SMO by enabling sRGB in the final output
361 // if it has been used in the frame
362 // Needed because of this bug in QT
363 // QTBUG-50987
364 state.framebuffer_srgb.enabled = OpenGLState::GetsRGBUsed();
359 state.Apply(); 365 state.Apply();
360
361 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); 366 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
362 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 367 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
363 368 // restore default state
369 state.framebuffer_srgb.enabled = false;
364 state.texture_units[0].texture = 0; 370 state.texture_units[0].texture = 0;
365 state.Apply(); 371 state.Apply();
372 // Clear sRGB state for the next frame
373 OpenGLState::ClearsRGBUsed();
366} 374}
367 375
368/** 376/**
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 5947bd2b9..d12d2ecb8 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -173,6 +173,7 @@ struct TICEntry {
173 }; 173 };
174 union { 174 union {
175 BitField<0, 16, u32> width_minus_1; 175 BitField<0, 16, u32> width_minus_1;
176 BitField<22, 1, u32> srgb_conversion;
176 BitField<23, 4, TextureType> texture_type; 177 BitField<23, 4, TextureType> texture_type;
177 }; 178 };
178 union { 179 union {
@@ -227,6 +228,10 @@ struct TICEntry {
227 return header_version == TICHeaderVersion::BlockLinear || 228 return header_version == TICHeaderVersion::BlockLinear ||
228 header_version == TICHeaderVersion::BlockLinearColorKey; 229 header_version == TICHeaderVersion::BlockLinearColorKey;
229 } 230 }
231
232 bool IsSrgbConversionEnabled() const {
233 return srgb_conversion != 0;
234 }
230}; 235};
231static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size"); 236static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size");
232 237
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 20ffb0a9a..4b34c1e28 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -48,20 +48,40 @@ constexpr std::array<u8, 107> backup_jpeg{
48 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, 48 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
49}; 49};
50 50
51std::string GetImagePath(Service::Account::UUID uuid) { 51QString GetImagePath(Service::Account::UUID uuid) {
52 return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 52 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
53 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; 53 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
54 return QString::fromStdString(path);
54} 55}
55 56
56std::string GetAccountUsername(const Service::Account::ProfileManager& manager, 57QString GetAccountUsername(const Service::Account::ProfileManager& manager,
57 Service::Account::UUID uuid) { 58 Service::Account::UUID uuid) {
58 Service::Account::ProfileBase profile; 59 Service::Account::ProfileBase profile;
59 if (!manager.GetProfileBase(uuid, profile)) { 60 if (!manager.GetProfileBase(uuid, profile)) {
60 return ""; 61 return {};
61 } 62 }
62 63
63 return Common::StringFromFixedZeroTerminatedBuffer( 64 const auto text = Common::StringFromFixedZeroTerminatedBuffer(
64 reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); 65 reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
66 return QString::fromStdString(text);
67}
68
69QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) {
70 return ConfigureSystem::tr("%1\n%2",
71 "%1 is the profile username, %2 is the formatted UUID (e.g. "
72 "00112233-4455-6677-8899-AABBCCDDEEFF))")
73 .arg(username, QString::fromStdString(uuid.FormatSwitch()));
74}
75
76QPixmap GetIcon(Service::Account::UUID uuid) {
77 QPixmap icon{GetImagePath(uuid)};
78
79 if (!icon) {
80 icon.fill(Qt::black);
81 icon.loadFromData(backup_jpeg.data(), backup_jpeg.size());
82 }
83
84 return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
65} 85}
66} // Anonymous namespace 86} // Anonymous namespace
67 87
@@ -131,18 +151,6 @@ void ConfigureSystem::setConfiguration() {
131 UpdateCurrentUser(); 151 UpdateCurrentUser();
132} 152}
133 153
134static QPixmap GetIcon(Service::Account::UUID uuid) {
135 const auto icon_url = QString::fromStdString(GetImagePath(uuid));
136 QPixmap icon{icon_url};
137
138 if (!icon) {
139 icon.fill(Qt::black);
140 icon.loadFromData(backup_jpeg.data(), backup_jpeg.size());
141 }
142
143 return icon;
144}
145
146void ConfigureSystem::PopulateUserList() { 154void ConfigureSystem::PopulateUserList() {
147 const auto& profiles = profile_manager->GetAllUsers(); 155 const auto& profiles = profile_manager->GetAllUsers();
148 for (const auto& user : profiles) { 156 for (const auto& user : profiles) {
@@ -154,8 +162,7 @@ void ConfigureSystem::PopulateUserList() {
154 reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); 162 reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
155 163
156 list_items.push_back(QList<QStandardItem*>{new QStandardItem{ 164 list_items.push_back(QList<QStandardItem*>{new QStandardItem{
157 GetIcon(user).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), 165 GetIcon(user), FormatUserEntryText(QString::fromStdString(username), user)}});
158 QString::fromStdString(username + '\n' + user.FormatSwitch())}});
159 } 166 }
160 167
161 for (const auto& item : list_items) 168 for (const auto& item : list_items)
@@ -172,7 +179,7 @@ void ConfigureSystem::UpdateCurrentUser() {
172 scene->clear(); 179 scene->clear();
173 scene->addPixmap( 180 scene->addPixmap(
174 GetIcon(*current_user).scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); 181 GetIcon(*current_user).scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
175 ui->current_user_username->setText(QString::fromStdString(username)); 182 ui->current_user_username->setText(username);
176} 183}
177 184
178void ConfigureSystem::ReadSystemSettings() {} 185void ConfigureSystem::ReadSystemSettings() {}
@@ -248,25 +255,23 @@ void ConfigureSystem::AddUser() {
248 255
249 profile_manager->CreateNewUser(uuid, username.toStdString()); 256 profile_manager->CreateNewUser(uuid, username.toStdString());
250 257
251 item_model->appendRow(new QStandardItem{ 258 item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)});
252 GetIcon(uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
253 QString::fromStdString(username.toStdString() + '\n' + uuid.FormatSwitch())});
254} 259}
255 260
256void ConfigureSystem::RenameUser() { 261void ConfigureSystem::RenameUser() {
257 const auto user = tree_view->currentIndex().row(); 262 const auto user = tree_view->currentIndex().row();
258 const auto uuid = profile_manager->GetUser(user); 263 const auto uuid = profile_manager->GetUser(user);
259 ASSERT(uuid != std::nullopt); 264 ASSERT(uuid != std::nullopt);
260 const auto username = GetAccountUsername(*profile_manager, *uuid);
261 265
262 Service::Account::ProfileBase profile; 266 Service::Account::ProfileBase profile;
263 if (!profile_manager->GetProfileBase(*uuid, profile)) 267 if (!profile_manager->GetProfileBase(*uuid, profile))
264 return; 268 return;
265 269
266 bool ok = false; 270 bool ok = false;
271 const auto old_username = GetAccountUsername(*profile_manager, *uuid);
267 const auto new_username = 272 const auto new_username =
268 QInputDialog::getText(this, tr("Enter Username"), tr("Enter a new username:"), 273 QInputDialog::getText(this, tr("Enter Username"), tr("Enter a new username:"),
269 QLineEdit::Normal, QString::fromStdString(username), &ok); 274 QLineEdit::Normal, old_username, &ok);
270 275
271 if (!ok) 276 if (!ok)
272 return; 277 return;
@@ -284,12 +289,8 @@ void ConfigureSystem::RenameUser() {
284 289
285 item_model->setItem( 290 item_model->setItem(
286 user, 0, 291 user, 0,
287 new QStandardItem{ 292 new QStandardItem{GetIcon(*uuid),
288 GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), 293 FormatUserEntryText(QString::fromStdString(username_std), *uuid)});
289 tr("%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. "
290 "00112233-4455-6677-8899-AABBCCDDEEFF))")
291 .arg(QString::fromStdString(username_std),
292 QString::fromStdString(uuid->FormatSwitch()))});
293 UpdateCurrentUser(); 294 UpdateCurrentUser();
294} 295}
295 296
@@ -299,10 +300,9 @@ void ConfigureSystem::DeleteUser() {
299 ASSERT(uuid != std::nullopt); 300 ASSERT(uuid != std::nullopt);
300 const auto username = GetAccountUsername(*profile_manager, *uuid); 301 const auto username = GetAccountUsername(*profile_manager, *uuid);
301 302
302 const auto confirm = 303 const auto confirm = QMessageBox::question(
303 QMessageBox::question(this, tr("Confirm Delete"), 304 this, tr("Confirm Delete"),
304 tr("You are about to delete user with name %1. Are you sure?") 305 tr("You are about to delete user with name \"%1\". Are you sure?").arg(username));
305 .arg(QString::fromStdString(username)));
306 306
307 if (confirm == QMessageBox::No) 307 if (confirm == QMessageBox::No)
308 return; 308 return;
@@ -325,28 +325,47 @@ void ConfigureSystem::SetUserImage() {
325 const auto index = tree_view->currentIndex().row(); 325 const auto index = tree_view->currentIndex().row();
326 const auto uuid = profile_manager->GetUser(index); 326 const auto uuid = profile_manager->GetUser(index);
327 ASSERT(uuid != std::nullopt); 327 ASSERT(uuid != std::nullopt);
328 const auto username = GetAccountUsername(*profile_manager, *uuid);
329 328
330 const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(), 329 const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(),
331 tr("JPEG Images (*.jpg *.jpeg)")); 330 tr("JPEG Images (*.jpg *.jpeg)"));
332 331
333 if (file.isEmpty()) 332 if (file.isEmpty()) {
334 return; 333 return;
334 }
335
336 const auto image_path = GetImagePath(*uuid);
337 if (QFile::exists(image_path) && !QFile::remove(image_path)) {
338 QMessageBox::warning(
339 this, tr("Error deleting image"),
340 tr("Error occurred attempting to overwrite previous image at: %1.").arg(image_path));
341 return;
342 }
335 343
336 FileUtil::Delete(GetImagePath(*uuid)); 344 const auto raw_path = QString::fromStdString(
345 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010");
346 const QFileInfo raw_info{raw_path};
347 if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) {
348 QMessageBox::warning(this, tr("Error deleting file"),
349 tr("Unable to delete existing file: %1.").arg(raw_path));
350 return;
351 }
337 352
338 const auto raw_path = 353 const QString absolute_dst_path = QFileInfo{image_path}.absolutePath();
339 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; 354 if (!QDir{raw_path}.mkpath(absolute_dst_path)) {
340 if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) 355 QMessageBox::warning(
341 FileUtil::Delete(raw_path); 356 this, tr("Error creating user image directory"),
357 tr("Unable to create directory %1 for storing user images.").arg(absolute_dst_path));
358 return;
359 }
342 360
343 FileUtil::CreateFullPath(GetImagePath(*uuid)); 361 if (!QFile::copy(file, image_path)) {
344 FileUtil::Copy(file.toStdString(), GetImagePath(*uuid)); 362 QMessageBox::warning(this, tr("Error copying user image"),
363 tr("Unable to copy image from %1 to %2").arg(file, image_path));
364 return;
365 }
345 366
346 item_model->setItem( 367 const auto username = GetAccountUsername(*profile_manager, *uuid);
347 index, 0, 368 item_model->setItem(index, 0,
348 new QStandardItem{ 369 new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)});
349 GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
350 QString::fromStdString(username + '\n' + uuid->FormatSwitch())});
351 UpdateCurrentUser(); 370 UpdateCurrentUser();
352} 371}