Commit Graph

5256 Commits

Author SHA1 Message Date
JMC47
0e2785a562
Merge pull request #13387 from jordan-woyak/frame-pacing
CoreTiming: Improve frame pacing
2025-03-13 14:23:18 -04:00
Jordan Woyak
5b36c13bfb VideoCommon: Rename Renderer to EFBInterface. 2025-03-12 21:09:16 -05:00
JMC47
de997d616f
Merge pull request #13416 from jordan-woyak/unused-viewport-correction
VertexShaderManager: Eliminate unnecessary m_viewport_correction member.
2025-03-11 19:00:55 -04:00
JMC47
b7bd0a0836
Merge pull request #13413 from jordan-woyak/vconfig-progressive
VideoConfig: Eliminate bForceProgressive.
2025-03-11 18:59:28 -04:00
JMC47
67c8d5640f
Merge pull request #13412 from jordan-woyak/netplay-vconfig
VideoConfig: Eliminate NetPlay related members.
2025-03-11 18:59:02 -04:00
JMC47
1698daef66
Merge pull request #13410 from jordan-woyak/movie-vconfig
Movie: Eliminate MovieManager::SetGraphicsConfig.
2025-03-11 18:58:45 -04:00
Jordan Woyak
c191ed5321 VertexShaderManager: Eliminate unnecessary m_viewport_correction member. 2025-03-11 03:55:57 -05:00
Jordan Woyak
de01a790e1 VideoConfig: Eliminate bForceProgressive. 2025-03-10 19:02:43 -05:00
Jordan Woyak
7f3d8a1ad4 VideoConfig: Eliminate NetPlay related members. 2025-03-10 18:53:44 -05:00
Jordan Woyak
44423a3656 Movie: Eliminate MovieManager::SetGraphicsConfig. 2025-03-10 18:16:49 -05:00
Jordan Woyak
7222188cde Core/VideoCommon: Push presentation time calculated from CPU thread to GPU thread. 2025-03-10 16:40:46 -05:00
Jordan Woyak
fe2d247acb VideoCommon: Don't merge EFBPoke AsyncRequests. 2025-03-10 16:37:24 -05:00
mitaclaw
433c6ce0f2 GCC: Remedy NRVO Fails
Using the `-Wnrvo` flag introduced by GCC 14, I identified a few places where NRVO was clearly intended, but is fumbled.
2025-03-10 12:38:03 -07:00
JMC47
5ed8b7bc9d
Merge pull request #13403 from jordan-woyak/backend_info
VideoCommon: Move backend_info out of VideoConfig struct.
2025-03-10 15:06:19 -04:00
mitaclaw
e4efe011d7 Modernize std::max_element with ranges and projections 2025-03-09 13:26:39 -07:00
mitaclaw
8b9f92a0af Modernize std::sort with ranges and projections
In PPCTables.cpp, the code is currently unused so I was unable to test it.

In CustomPipeline.cpp, a pointer to member function cannot be used due to 16.4.5.2.1 of the C++ Standard regarding "addressable functions". https://eel.is/c++draft/namespace.std#6

In Fs.cpp and DirectoryBlob.cpp, these examples used projections in a previous iteration of this commit, but no longer do. Still, they remain in this commit because the PR they would actually belong to is already merged.
2025-03-09 13:26:38 -07:00
mitaclaw
62b2b939b5 Simplify std::find_if with std::ranges::find and projections
In LabelMap.cpp, the code is currently unused so I was unable to test it.

In WiiUtils.cpp, the magic value `1u` was replaced by the constant value `DiscIO::PARTITION_UPDATE`.
2025-03-09 13:26:35 -07:00
Jordan Woyak
c18c039089 VideoCommon: Move backend_info out of VideoConfig struct. 2025-03-09 01:42:45 -06:00
Jordan Woyak
7925240107 Core/VideoBackendBase: Call ExitGpuLoop from Core to eliminate Video_ExitLoop. 2025-03-07 17:30:33 -06:00
iwubcode
6dedf11c96 VideoCommon: mark 'WriteTexCoordTransforms' as 'static' 2025-03-02 11:27:54 -06:00
Tillmann Karras
4e460a7968 UberShaderPixel: use unswapped texture samples for depth 2025-02-25 18:45:01 +00:00
Tillmann Karras
359fb51d64 PixelShaderGen: use unswapped texture samples for depth 2025-02-25 18:45:01 +00:00
Dentomologist
0645a267d9 PerformanceMetrics: Add clamping, resetting on resize, and setting
Clamp overlays to the render window (with some padding), reset their
positions when the render window changes sizes, and add a setting to
enable moving the overlays (off by default, .ini only for now).
2025-02-24 13:17:57 -08:00
JMC47
43a6ec5bbd
Merge pull request #13353 from iwubcode/custom_shader_overhaul_vertex_texcoord
VideoCommon: move texcoord calculations to accessible functions in VertexShaderGen
2025-02-22 02:32:47 -05:00
JosJuice
af064c57de
Merge pull request #13219 from Tilka/cp_cleanup
VideoCommon: drop CP MMIO registers that were probably added in the wrong place
2025-02-17 21:27:19 +01:00
iwubcode
1ce78dd9b4 VideoCommon: move texcoord calculations to accessible functions in VertexShaderGen 2025-02-14 22:44:49 -06:00
Tillmann Karras
5db6bd6054 VideoCommon: simplify dither calculation
This saves three instructions on AMD GPUs. Dunno about Nvidia.
2025-02-11 19:23:37 +00:00
Tillmann Karras
19d954eaea VideoCommon: remove CP readonly field, it's now always false 2025-02-08 11:46:49 +00:00
Tillmann Karras
84e72c185a VideoCommon: drop CP MMIO registers that were probably added in the wrong place
I think someone confused these with the actual token and bounding box
registers in PE, which were added later. In CP they never did anything
and it's suspicious that they have the same addresses as their PE
counterparts. On real hardware they always read as zero.
2025-02-08 11:46:49 +00:00
Admiral H. Curtiss
05f217bd0b
Merge pull request #13330 from Dentomologist/performancemetrics_allow_moving_overlays
PerformanceMetrics: Allow users to move overlays
2025-02-06 21:56:57 +01:00
JosJuice
db285b7c87
Merge pull request #13319 from hoogmin/infinite-style-fix
Refactor: Infinite loop in AsyncShaderCompiler according to style guidelines
2025-02-06 18:31:28 +01:00
Javier Martinez
7dec083af5 Refactor: Make loop non-infinite instead in AsyncShaderCompiler. 2025-02-05 20:06:59 -05:00
Dentomologist
4b8e36cba9 PerformanceMetrics: Allow users to move overlays
Allow users to move the FPS, VPS, Speed, and Performance Graph overlays.
2025-02-02 19:40:01 -08:00
Dentomologist
f94e6cb73e PerformanceMetrics: Call ImGui::End() unconditionally
Move ImGui::End() calls out of if(ImGui::Begin()) blocks.

Quoting from ImGui::Begin's function comment in imgui.cpp:
"You always need to call ImGui::End() even if false is returned."

In practice this didn't cause problems because the windows don't have
title bars and thus can't be collapsed, and so the block containing
::End would always run, but let's do it the right way.
2025-02-02 14:14:56 -08:00
OatmealDome
b5a0d293ae
Merge pull request #13305 from OatmealDome/vertexloader-config
VertexLoaderBase: Allow the vertex loader type to be set via config
2025-01-29 17:14:05 -05:00
JMC47
e16e3f9a61
Merge pull request #13291 from iwubcode/imgui_1_91_7
Externals / VideoCommon: update imgui to 1.91.7 and implot to v0.16
2025-01-28 20:57:28 -05:00
OatmealDome
bffaec9c5e VertexLoaderBase: Allow the vertex loader type to be set via config 2025-01-24 18:31:42 -05:00
iwubcode
25c805be99 Externals / VideoCommon: update imgui to 1.91.7 and implot to v0.16; imgui changed types for ImTextureId, which was addressed by using an implicit cast 2025-01-20 14:47:14 -06:00
iwubcode
ee906ae6e8 VideoCommon: add formatter for AbstractTextureType 2025-01-20 14:24:54 -06:00
Admiral H. Curtiss
3ea870ef8c
Merge pull request #13244 from dreamsyntax/bugfix-double-osd-texture-messages
Core/VideoCommon: Fix duplicate OSD Custom Textures messages
2025-01-12 16:16:09 +01:00
Admiral H. Curtiss
7133bfbb0e
Merge pull request #13180 from jordan-woyak/eof-logic
Core/VideoCommon: Fix some weird (!eof) logic.
2025-01-08 05:27:42 +01:00
JMC47
6b686be5f1
Merge pull request #13233 from TryTwo/PR_Codec
AdvancedWidget: Replace FFV1 codec with Ut Video
2025-01-04 22:26:36 -05:00
mitaclaw
110d32729e Simplify std::find with Common::Contains
In NandPaths.cpp, the `std::initializer_list<char>` of illegal characters has been turned into a `char[]` (similar to the one in GameList.cpp).

The reverse iteration in ResourcePack.cpp seemed to provide no benefits, and doing without it it seemed to have no ill effects.
2025-01-01 09:52:03 -08:00
Jordan Woyak
0938fca6e3 Core/VideoCommon: Fix some weird (!eof) logic. 2024-12-30 20:13:59 -06:00
TryTwo
3d248d000f Frame Dumping: Change lossless codec from FFV1 to Ut Video.
Ut Video is faster and more compatible with editing programs, but produces larger files.
2024-12-30 14:07:43 -07:00
dreamsyntax
07f712f8a0 Core/VideoCommon: Fix duplicate OSD Custom Textures messages
Resolves duplicate OSD messages for Loading and Found custom textures.
VideoBackend initialization results in HiresTexture::Init being called.
We already call HiresTexture::Update when OnNewTitleLoad is called.
Thus we can remove HiresTextures::Init completely as it is redundant.
2024-12-28 01:09:50 -07:00
mitaclaw
2b0cd16c8c Modernize std::none_of with ranges
In JitRegCache.cpp, the lambda predicate were replaced by a pointer to member function because ranges algorithms are able to invoke those.

In ConvertDialog.cpp, the `std::mem_fn` helper was removed because ranges algorithms are able to handle pointers to member functions as predicates.

In BoundingBox.cpp, the lambda predicate was returning the bool element unchanged, so `std::identity` was a better fit.
2024-12-15 19:54:17 -08:00
mitaclaw
140252ffc0 Modernize std::any_of with ranges
In WiimoteReal.cpp, JitRegCache.cpp, lambda predicates were replaced by pointers to member functions because ranges algorithms are able invoke those.

In ConvertDialog.cpp, the `std::mem_fn` helper was removed because ranges algorithms are able to handle pointers to member functions as predicates.
2024-12-15 19:54:16 -08:00
mitaclaw
860e6cf5cb Modernize std::all_of with ranges
In DITSpecification.cpp, MaterialAsset.cpp, and ShaderAsset.cpp, lambda predicates were replaced by pointers to member functions because ranges algorithms are able invoke those.

In NetPlayClient.cpp, the non-trivial `NetPlay::Player` elements were being passed by value in `NetPlayClient::DoAllPlayersHaveGame()`. This has been fixed.

In WIABlob.cpp, the second example's predicate was returning the `std::optional` by value instead of implicitly converting it to a bool. This has been fixed.
2024-12-15 19:50:34 -08:00
Dr. Dystopia
6d44afc7dd Replace 'reinterpret_cast' with 'static_cast' 2024-11-08 07:26:47 +01:00
Dentomologist
47a86d920f GeneralWidget: Recommend default video backend in tooltip
Recommend the platform's default video backend in the Backend tooltip
instead of always recommending OpenGL.
2024-11-03 12:28:30 -08:00
Dentomologist
7b8610f4ea VideoBackendBase: Clarify function name
Rename GetDefaultBackendName to GetDefaultBackendConfigName to
distinguish it from the display name.
2024-11-03 12:26:57 -08:00
TellowKrinkle
5ef4fcb005 VideoCommon: Implement primitive breaking for primitive lists 2024-10-29 22:38:22 -05:00
JMC47
05e3a52de0
Merge pull request #13135 from Tilka/warning
VideoCommon: fix -Wshadow-uncaptured-local warning and simplify
2024-10-19 15:12:25 -04:00
Tilka
fed8220c39
Merge pull request #12712 from Dentomologist/remove_defined_out_code
Remove defined-out code
2024-10-19 19:36:19 +01:00
JMC47
21f4319302
Merge pull request #13092 from mitaclaw/ranges-modernization-3-spaceship
Ranges Algorithms Modernization - Compare
2024-10-17 13:37:16 -04:00
Tillmann Karras
0e41adb7b7 VideoCommon: fix -Wshadow-uncaptured-local warning and simplify 2024-10-16 18:36:00 +01:00
JosJuice
07605bf67c
Merge pull request #13090 from mitaclaw/ranges-modernization-1-trivial
Ranges Algorithms Modernization - Trivial
2024-10-15 17:08:55 +02:00
JMC47
ef8b753cd7
Merge pull request #13115 from Tilka/syncfifo
VideoCommon: fix common opcode decoding errors
2024-10-14 21:26:08 -04:00
Pokechu22
937bb2aa2e Cache normals in addition to binormals and tangents
Fixes LIT (https://bugs.dolphin-emu.org/issues/13635). The text does not include normals, but has lighting enabled. With the previous default of (0, 0, 0), lighting was always black (as dot(X, (0, 0, 0)) is always 0). It seems like the normal from the map in the background (0, 0, 1) is re-used.

LIT also has the vertex color enabled while vertex color is not specified, the same as SMS's debug cubes; the default MissingColorValue GameINI value of solid white seems to work correctly in this case.
2024-10-12 10:32:41 -07:00
Tilka
c1832d17f6
Merge pull request #13117 from mitaclaw/ranges-modernization-9-trivial-find
Ranges Algorithms Modernization - Find
2024-10-11 20:27:18 +01:00
mitaclaw
8b1975ab18 GraphicsMod: Simplify std::sort using ranges and projections 2024-10-11 12:08:22 -07:00
mitaclaw
e8d5fb89e4 C++20: Synthesize operator!= From operator==
The inequality operator is automatically generated by the compiler if `operator==` is defined.
2024-10-10 20:23:55 -07:00
mitaclaw
e4fb837f4b Modernize std::find_if with ranges
In BTEmu.cpp, `std::mem_fn` was not necessary for the predicate to compile.
2024-10-10 15:28:11 -07:00
mitaclaw
6ca7e2856b Modernize std::find with ranges 2024-10-10 15:28:11 -07:00
mitaclaw
728663bdc0 Modernize std::binary_search with ranges
In VolumeVerifier.cpp, constructing a `std::string_view` of the volume's GameID is unnecessary, as `std::`(`ranges::`)`binary_search` supports heterogeneous lookup. The usage in GameFile.cpp is a perfect example.
2024-10-10 00:53:48 -07:00
mitaclaw
ebf7cebc32 Modernize std::sort with ranges 2024-10-10 00:53:48 -07:00
mitaclaw
9bd1dae41d Modernize std::fill with ranges
In DSPCore.cpp, there were two `std::fill` uses that could be simplified using `std::fill_n`. Due to their proximity with other `std::fill` algorithms being modernized with ranges, I chose to make these examples into the rare `std::ranges::fill_n`.
2024-10-10 00:53:48 -07:00
Tillmann Karras
e96960e2a6 VideoCommon: fix common opcode decoding errors
Many games call GXSetGPFifo() without first waiting for the GP to finish
consuming outstanding commands in the previous GP fifo. Normally,
Dolphin runs OpcodeDecoding in 1000-cycle time slices. In that time
frame, GXSetGPFifo() has probably completed and the GP read pointer now
points to entirely new memory. If the last GP fifo copy ended in an
incomplete command, the new GP fifo would most likely desync for a
while. To avoid all this, give the GP a time slice right now to copy the
remaining data from the previous GP fifo.
2024-10-10 04:07:44 +01:00
Pokechu22
929bcbdde9 Fifo analyzer: Fix indexed XF loads showing "bytes" instead of "words"
Indexed XF loads specify the number of 32-bit words (generally floats, but light data has some integers) to load, not the number of bytes. This was only a mistake in the fifo analyzer text; the actual implementation already loaded words.
2024-10-06 21:56:48 -07:00
JMC47
339f9311fb
Merge pull request #12918 from JosJuice/revert-revert-audit-get-state
Partially revert "Revert "Audit uses of IsRunning and GetState""
2024-10-06 11:26:06 -04:00
Jules Blok
b191c0f7d8
Merge pull request #12208 from PatrickFerry/add-vertex-depth-range-case
Use Vertex Depth Range when zRange Exceeds farZ
2024-10-05 14:14:11 +02:00
JosJuice
6ca2da53e8 Partially revert "Revert "Audit uses of IsRunning and GetState""
This reverts the revert commit bc67fc97c3,
except for the changes in BaseConfigLoader.cpp, which caused the bug
that made us revert 72cf2bdb87. PR 12917
contains an improved change to BaseConfigLoader.cpp, which can be merged
(or rejected) independently.

A few changes have also been made based on review comments.
2024-10-04 18:35:41 +02:00
JosJuice
2da3e49b1e
Merge pull request #13078 from Dentomologist/videobackendbase_only_populate_backend_info_when_uninitialized
VideoBackendBase: Only populate backend info when uninitialized
2024-10-04 18:31:06 +02:00
JMC47
e259831bcf
Merge pull request #13087 from TellowKrinkle/PresentSkip
VideoBackends:Vulkan: Prevent freezes during window resize on Linux
2024-10-02 21:09:25 -04:00
TellowKrinkle
7e1a9490c0 VideoBackends:Vulkan: Don't try to present if swapchain acquire failed 2024-10-02 19:22:16 -05:00
TellowKrinkle
fd94ff252d VideoCommon: Dirty pixel shader manager on efb scale changes 2024-09-30 01:12:55 -05:00
Dentomologist
dc1b961c09 VideoBackendBase: Only populate backend info when uninitialized
Prevent potential issues when creating the Graphics window (and thus
calling PopulateBackendInfo) while the core state is Stopping, like we
already do while it's Starting or Running.
2024-09-27 12:20:48 -07:00
Dentomologist
0a1084fad5 VideoBackendBase: Check Core state in PopulateBackendInfo
Remove the PopulateBackendInfoFromUI function, which had a single caller
(GraphicsWindow::OnBackendChanged) and checked that the core wasn't
running or starting before calling PopulateBackendInfo.

Move the core state check into PopulateBackendInfo and have
OnBackendChanged call that instead. This guarantees the check is
performed by all callers of PopulateBackendInfo, preventing
potential reintroduction of the crash fixed in 3d4ae63f if another call
to PopulateBackendInfo is added.

As of the previous commit the only other caller of PopulateBackendInfo
is Core::Init shortly before s_state is set to Starting, so it will
always pass the check and so maintain its current behavior.
2024-09-26 17:12:26 -07:00
Dentomologist
91f7b776ca GraphicsWindow: Fix crash when opening during emulation startup
Fix a crash when opening the Graphics window for the first time during
emulation startup when the backend is Vulkan, D3D11, or D3D12.

Don't call PopulateBackendInfo() from the Host thread when the core is
starting up. First, the function has already been called in Core::Init()
so we don't need to again. More importantly, PopulateBackendInfo() calls
g_video_backend->InitBackendInfo(), and the Vulkan and D3D
implementations of those functions load and then unload libraries (and
their associated function pointers) which are potentially in use by
other threads.

This crash was reliably reproducible with the following steps:
1) Select an affected backend.
2) Enable "Compile Shaders Before Starting"
3) Delete the cached shaders (but not the .uidcache file) for the game
   you're testing.
4) Close and reopen Dolphin.
5) Start the game.
6) While the game is still booting or compiling shaders, open the
   Graphics window for the first time in that Dolphin session.

Fixes https://bugs.dolphin-emu.org/issues/13634.
2024-09-26 17:12:26 -07:00
Dentomologist
c3d6d2189a Texture Dumping: Show OSD message on startup and when toggled
If texture dumping is enabled, notify the user on emulation startup
using an On Screen Display message.

Also notify the user when texture dumping is toggled.

Addresses https://bugs.dolphin-emu.org/issues/12445.
2024-09-06 13:00:25 -07:00
Admiral H. Curtiss
e1b1e4b4cf
Merge pull request #13031 from parona-source/libfmt-11
Add support for libfmt-11
2024-08-31 15:34:48 +02:00
Alfred Wingate
d7c93d87be
Add support for libfmt-11
fmt::join was moved into fmt/ranges.h

Signed-off-by: Alfred Wingate <parona@protonmail.com>
2024-08-22 16:54:35 +03:00
Bryan Jacobs
7ec6d116e8 Graphics: Adapt aspect ratio when SBS/TAB 3D is used
Adds support for choosing to present the full resolution
independently to each eye when using side-by-side or
top-and-bottom 3D.
2024-08-22 07:11:43 +10:00
Dr. Dystopia
9602f36248 Remove redundant semicolons 2024-08-20 14:59:54 +02:00
Tillmann Karras
20f4643c74 VideoCommon: force 32-byte alignment for display list address and size 2024-08-18 05:00:38 +01:00
mitaclaw
9fa4eb9aab Use 'contains' method 2024-08-15 14:20:16 -07:00
Dr. Dystopia
618b41a459 Use 'contains' method 2024-08-14 22:18:28 +02:00
Dentomologist
d627b78c46 Adjust order and spacing of various #includes
Move some #includes around to match the Contributing guidelines.
2024-07-26 14:28:34 -07:00
TellowKrinkle
cab6e7c12e Properly link against xxhash
Things using dolphin_find_optional_system_library need to link against the name used there or they won't work with both the system and bundled cases
2024-07-06 16:56:44 -05:00
iwubcode
36ccbbc06e VideoCommon: add ability to serialize a texture asset metadata to json 2024-06-29 14:50:40 -05:00
iwubcode
1c3402502b VideoCommon: set individual texture asset filter/wrap values when loading from json 2024-06-29 14:50:40 -05:00
iwubcode
75a0a1ed67 VideoCommon: update texture asset to properly set near sampler state value 2024-06-29 14:50:40 -05:00
Admiral H. Curtiss
ce2f4101f3
Core/VideoCommon: Revert change from #12828
This causes Dual Core to lock up during the boot sequence, because it tries to wait for a not-yet-running GPU thread.

Fixes https://bugs.dolphin-emu.org/issues/13559
2024-06-23 03:44:05 +02:00
Admiral H. Curtiss
9b33b777cf
Merge pull request #12828 from JosJuice/unify-state-variables-2
Clean up Core::GetState
2024-06-22 20:20:54 +02:00
JosJuice
72cf2bdb87 Audit uses of IsRunning and GetState
Some pieces of code are calling IsRunning because there's some
particular action that only makes sense when emulation is running, for
instance showing the state of the emulated CPU. IsRunning is appropriate
to use for this. Then there are pieces of code that are calling
IsRunning because there's some particular thing they must avoid doing
e.g. when the CPU thread is running or IOS is running. IsRunning isn't
quite appropriate for this. Such code should also be checking for the
states Starting and Stopping. Keep in mind that:

* When the state is Starting, the state can asynchronously change to
  Running at any time.
* When we try to stop the core, the state gets set to Stopping before we
  take any action to actually stop things.

This commit adds a new method Core::IsUninitialized, and changes all
callers of IsRunning and GetState that look to me like they should be
changed.
2024-06-21 20:52:55 +02:00
JosJuice
962230f91e Core: Store current state in less places
Core::GetState reads from four different pieces of state: s_is_stopping,
s_hardware_initialized, s_is_booting, and CPUManager::IsStepping.
I'm keeping that last one as is for now because there's code in Dolphin
that sets it directly, but we can unify the other three to make things
easier to reason about.

This commit also gets rid of s_is_started. This was previously used in
Core::IsRunningAndStarted to ensure true wouldn't be returned until the
CPU thread was started, but it wasn't used in Core::GetState, so
Core::GetState would happily return State::Running after we had
initialized the hardware but before we had initialized the CPU thread.
As far as I know, there are no callers that have any real need to know
whether the boot process is currently initializing the hardware or the
CPU thread. Perhaps once upon a time there was a desire to make the
apploader debuggable, but a long time has passed without anyone stepping
up to implement it, and the way CBoot::RunApploader is implemented makes
it rather difficult. So this commit makes all the functions in Core.cpp
consider the core to still be starting until the CPU thread is started.
2024-06-21 20:46:44 +02:00
Admiral H. Curtiss
5728ebf7ad
Merge pull request #12867 from AdmiralCurtiss/uicommon-dependency
CMake dependency fixes
2024-06-21 20:01:44 +02:00
LillyJadeKatrin
45563a872c Scale challenge icons based on screen height 2024-06-19 13:59:59 -04:00
JMC47
9886199cb1
Merge pull request #12859 from LillyJadeKatrin/retroachievements-challenge-hide
Hide Challenge Icons when OSD Messages Disabled
2024-06-19 02:29:08 -04:00