diff options
-rw-r--r-- | .ci/scripts/common/pre-upload.sh | 2 | ||||
-rwxr-xr-x | .ci/scripts/linux/docker.sh | 15 | ||||
-rwxr-xr-x | .ci/scripts/linux/upload.sh | 5 | ||||
-rw-r--r-- | dist/qt_themes/default/style.qss | 4 | ||||
-rw-r--r-- | dist/qt_themes/qdarkstyle/style.qss | 4 | ||||
-rw-r--r-- | dist/qt_themes/qdarkstyle_midnight_blue/style.qss | 4 | ||||
-rw-r--r-- | externals/nx_tzdb/CMakeLists.txt | 2 | ||||
m--------- | externals/nx_tzdb/tzdb_to_nx | 0 | ||||
-rw-r--r-- | src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp | 9 | ||||
-rw-r--r-- | src/audio_core/adsp/apps/audio_renderer/audio_renderer.h | 2 | ||||
-rw-r--r-- | src/video_core/buffer_cache/buffer_cache.h | 46 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_texture_cache.cpp | 14 | ||||
-rw-r--r-- | src/video_core/renderer_vulkan/vk_texture_cache.cpp | 16 | ||||
-rw-r--r-- | src/video_core/vulkan_common/vulkan_memory_allocator.cpp | 8 | ||||
-rw-r--r-- | src/yuzu/applets/qt_controller.cpp | 39 | ||||
-rw-r--r-- | src/yuzu/applets/qt_controller.h | 6 | ||||
-rw-r--r-- | src/yuzu/applets/qt_controller.ui | 54 | ||||
-rw-r--r-- | src/yuzu/util/util.cpp | 81 |
18 files changed, 233 insertions, 78 deletions
diff --git a/.ci/scripts/common/pre-upload.sh b/.ci/scripts/common/pre-upload.sh index 705362a3c..3583f9840 100644 --- a/.ci/scripts/common/pre-upload.sh +++ b/.ci/scripts/common/pre-upload.sh @@ -5,6 +5,6 @@ GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`" GITREV="`git show -s --format='%h'`" -ARTIFACTS_DIR="artifacts" +ARTIFACTS_DIR="$PWD/artifacts" mkdir -p "${ARTIFACTS_DIR}/" diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh index e5d83d4b9..a16577b27 100755 --- a/.ci/scripts/linux/docker.sh +++ b/.ci/scripts/linux/docker.sh @@ -11,7 +11,7 @@ ccache -s mkdir build || true && cd build cmake .. \ -DBoost_USE_STATIC_LIBS=ON \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_CXX_FLAGS="-march=x86-64-v2" \ -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \ -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \ @@ -31,6 +31,19 @@ ccache -s ctest -VV -C Release +# Separate debug symbols from specified executables +for EXE in yuzu; do + EXE_PATH="bin/$EXE" + # Copy debug symbols out + objcopy --only-keep-debug $EXE_PATH $EXE_PATH.debug + # Add debug link and strip debug symbols + objcopy -g --add-gnu-debuglink=$EXE_PATH.debug $EXE_PATH $EXE_PATH.out + # Overwrite original with stripped copy + mv $EXE_PATH.out $EXE_PATH +done +# Strip debug symbols from all executables +find bin/ -type f -not -regex '.*.debug' -exec strip -g {} ';' + DESTDIR="$PWD/AppDir" ninja install rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester diff --git a/.ci/scripts/linux/upload.sh b/.ci/scripts/linux/upload.sh index e0f336427..fbb2d9c1b 100755 --- a/.ci/scripts/linux/upload.sh +++ b/.ci/scripts/linux/upload.sh @@ -59,4 +59,9 @@ if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ]; cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/yuzu-${RELEASE_NAME}.AppImage" fi +# Copy debug symbols to artifacts +cd build/bin +tar $COMPRESSION_FLAGS "${ARTIFACTS_DIR}/${REV_NAME}-debug.tar.xz" *.debug +cd - + . .ci/scripts/common/post-upload.sh diff --git a/dist/qt_themes/default/style.qss b/dist/qt_themes/default/style.qss index 79960ee0c..921950c6c 100644 --- a/dist/qt_themes/default/style.qss +++ b/dist/qt_themes/default/style.qss @@ -120,6 +120,10 @@ QWidget#connectedControllers { background: transparent; } +QWidget#closeButtons { + background: transparent; +} + QWidget#playersSupported, QWidget#controllersSupported, QWidget#controllerSupported1, diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss index 63a636ae6..328ac942f 100644 --- a/dist/qt_themes/qdarkstyle/style.qss +++ b/dist/qt_themes/qdarkstyle/style.qss @@ -1380,6 +1380,10 @@ QWidget#connectedControllers { background: transparent; } +QWidget#closeButtons { + background: transparent; +} + QWidget#playersSupported, QWidget#controllersSupported, QWidget#controllerSupported1, diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss index 0c53115f6..eb0889b13 100644 --- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss +++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss @@ -2305,6 +2305,10 @@ QWidget#connectedControllers { background: transparent; } +QWidget#closeButtons { + background: transparent; +} + QWidget#playersSupported, QWidget#controllersSupported, QWidget#controllerSupported1, diff --git a/externals/nx_tzdb/CMakeLists.txt b/externals/nx_tzdb/CMakeLists.txt index 593786250..0fad24642 100644 --- a/externals/nx_tzdb/CMakeLists.txt +++ b/externals/nx_tzdb/CMakeLists.txt @@ -27,7 +27,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID) set(CAN_BUILD_NX_TZDB false) endif() -set(NX_TZDB_VERSION "220816") +set(NX_TZDB_VERSION "221202") set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip") set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb") diff --git a/externals/nx_tzdb/tzdb_to_nx b/externals/nx_tzdb/tzdb_to_nx -Subproject 212afa2394a74226dcf1b7996a570aae17debb6 +Subproject 0d17dd066d91f954a4c89d46dcb067eead6b1e4 diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp index 972d5e45b..ef301d8b4 100644 --- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp @@ -77,6 +77,7 @@ void AudioRenderer::Wait() { "{}, got {}", Message::RenderResponse, msg); } + PostDSPClearCommandBuffer(); } void AudioRenderer::Send(Direction dir, u32 message) { @@ -96,6 +97,14 @@ void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u command_buffers[session_id].reset_buffer = reset; } +void AudioRenderer::PostDSPClearCommandBuffer() noexcept { + for (auto& buffer : command_buffers) { + buffer.buffer = 0; + buffer.size = 0; + buffer.reset_buffer = false; + } +} + u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept { return command_buffers[session_id].remaining_command_count; } diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h index 85874d88a..57b89d9fe 100644 --- a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h +++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h @@ -85,6 +85,8 @@ private: */ void CreateSinkStreams(); + void PostDSPClearCommandBuffer() noexcept; + /// Core system Core::System& system; /// The output sink the AudioRenderer will send samples to diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 9e90c587c..9b2698fad 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -544,7 +544,7 @@ void BufferCache<P>::CommitAsyncFlushesHigh() { it++; } - boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads; + boost::container::small_vector<std::pair<BufferCopy, BufferId>, 16> downloads; u64 total_size_bytes = 0; u64 largest_copy = 0; for (const IntervalSet& intervals : committed_ranges) { @@ -914,6 +914,11 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) { const u32 offset = buffer.Offset(binding.cpu_addr); const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; + + if (is_written) { + MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); + } + if constexpr (NEEDS_BIND_STORAGE_INDEX) { runtime.BindStorageBuffer(stage, binding_index, buffer, offset, size, is_written); ++binding_index; @@ -931,6 +936,11 @@ void BufferCache<P>::BindHostGraphicsTextureBuffers(size_t stage) { const u32 size = binding.size; SynchronizeBuffer(buffer, binding.cpu_addr, size); + const bool is_written = ((channel_state->written_texture_buffers[stage] >> index) & 1) != 0; + if (is_written) { + MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); + } + const u32 offset = buffer.Offset(binding.cpu_addr); const PixelFormat format = binding.format; if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { @@ -962,6 +972,8 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() { const u32 size = binding.size; SynchronizeBuffer(buffer, binding.cpu_addr, size); + MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); + const u32 offset = buffer.Offset(binding.cpu_addr); host_bindings.buffers.push_back(&buffer); host_bindings.offsets.push_back(offset); @@ -1011,6 +1023,11 @@ void BufferCache<P>::BindHostComputeStorageBuffers() { const u32 offset = buffer.Offset(binding.cpu_addr); const bool is_written = ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; + + if (is_written) { + MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); + } + if constexpr (NEEDS_BIND_STORAGE_INDEX) { runtime.BindComputeStorageBuffer(binding_index, buffer, offset, size, is_written); ++binding_index; @@ -1028,6 +1045,12 @@ void BufferCache<P>::BindHostComputeTextureBuffers() { const u32 size = binding.size; SynchronizeBuffer(buffer, binding.cpu_addr, size); + const bool is_written = + ((channel_state->written_compute_texture_buffers >> index) & 1) != 0; + if (is_written) { + MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); + } + const u32 offset = buffer.Offset(binding.cpu_addr); const PixelFormat format = binding.format; if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { @@ -1201,16 +1224,11 @@ void BufferCache<P>::UpdateUniformBuffers(size_t stage) { template <class P> void BufferCache<P>::UpdateStorageBuffers(size_t stage) { - const u32 written_mask = channel_state->written_storage_buffers[stage]; ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) { // Resolve buffer Binding& binding = channel_state->storage_buffers[stage][index]; const BufferId buffer_id = FindBuffer(binding.cpu_addr, binding.size); binding.buffer_id = buffer_id; - // Mark buffer as written if needed - if (((written_mask >> index) & 1) != 0) { - MarkWrittenBuffer(buffer_id, binding.cpu_addr, binding.size); - } }); } @@ -1219,10 +1237,6 @@ void BufferCache<P>::UpdateTextureBuffers(size_t stage) { ForEachEnabledBit(channel_state->enabled_texture_buffers[stage], [&](u32 index) { Binding& binding = channel_state->texture_buffers[stage][index]; binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size); - // Mark buffer as written if needed - if (((channel_state->written_texture_buffers[stage] >> index) & 1) != 0) { - MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, binding.size); - } }); } @@ -1252,7 +1266,6 @@ void BufferCache<P>::UpdateTransformFeedbackBuffer(u32 index) { .size = size, .buffer_id = buffer_id, }; - MarkWrittenBuffer(buffer_id, *cpu_addr, size); } template <class P> @@ -1279,10 +1292,6 @@ void BufferCache<P>::UpdateComputeStorageBuffers() { // Resolve buffer Binding& binding = channel_state->compute_storage_buffers[index]; binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size); - // Mark as written if needed - if (((channel_state->written_compute_storage_buffers >> index) & 1) != 0) { - MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, binding.size); - } }); } @@ -1291,18 +1300,11 @@ void BufferCache<P>::UpdateComputeTextureBuffers() { ForEachEnabledBit(channel_state->enabled_compute_texture_buffers, [&](u32 index) { Binding& binding = channel_state->compute_texture_buffers[index]; binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size); - // Mark as written if needed - if (((channel_state->written_compute_texture_buffers >> index) & 1) != 0) { - MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, binding.size); - } }); } template <class P> void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 size) { - if (memory_tracker.IsRegionCpuModified(cpu_addr, size)) { - SynchronizeBuffer(slot_buffers[buffer_id], cpu_addr, size); - } memory_tracker.MarkRegionAsGpuModified(cpu_addr, size); const IntervalType base_interval{cpu_addr, cpu_addr + size}; diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 9cafd2983..512eef575 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -1048,6 +1048,10 @@ void Image::Scale(bool up_scale) { } bool Image::ScaleUp(bool ignore) { + const auto& resolution = runtime->resolution; + if (!resolution.active) { + return false; + } if (True(flags & ImageFlagBits::Rescaled)) { return false; } @@ -1060,9 +1064,6 @@ bool Image::ScaleUp(bool ignore) { return false; } flags |= ImageFlagBits::Rescaled; - if (!runtime->resolution.active) { - return false; - } has_scaled = true; if (ignore) { current_texture = upscaled_backup.handle; @@ -1073,13 +1074,14 @@ bool Image::ScaleUp(bool ignore) { } bool Image::ScaleDown(bool ignore) { - if (False(flags & ImageFlagBits::Rescaled)) { + const auto& resolution = runtime->resolution; + if (!resolution.active) { return false; } - flags &= ~ImageFlagBits::Rescaled; - if (!runtime->resolution.active) { + if (False(flags & ImageFlagBits::Rescaled)) { return false; } + flags &= ~ImageFlagBits::Rescaled; if (ignore) { current_texture = texture.handle; return true; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 54bfeccb6..93773a69f 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1530,15 +1530,15 @@ bool Image::IsRescaled() const noexcept { } bool Image::ScaleUp(bool ignore) { + const auto& resolution = runtime->resolution; + if (!resolution.active) { + return false; + } if (True(flags & ImageFlagBits::Rescaled)) { return false; } ASSERT(info.type != ImageType::Linear); flags |= ImageFlagBits::Rescaled; - const auto& resolution = runtime->resolution; - if (!resolution.active) { - return false; - } has_scaled = true; if (!scaled_image) { const bool is_2d = info.type == ImageType::e2D; @@ -1567,15 +1567,15 @@ bool Image::ScaleUp(bool ignore) { } bool Image::ScaleDown(bool ignore) { + const auto& resolution = runtime->resolution; + if (!resolution.active) { + return false; + } if (False(flags & ImageFlagBits::Rescaled)) { return false; } ASSERT(info.type != ImageType::Linear); flags &= ~ImageFlagBits::Rescaled; - const auto& resolution = runtime->resolution; - if (!resolution.active) { - return false; - } current_image = *original_image; if (ignore) { return true; diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 82767fdf0..8dd1667f3 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -66,9 +66,10 @@ struct Range { switch (usage) { case MemoryUsage::Upload: case MemoryUsage::Stream: - return VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + return VMA_ALLOCATION_CREATE_MAPPED_BIT | + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; case MemoryUsage::Download: - return VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; + return VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; case MemoryUsage::DeviceLocal: return {}; } @@ -252,8 +253,7 @@ vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const { vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const { const VmaAllocationCreateInfo alloc_ci = { - .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT | - MemoryUsageVmaFlags(usage), + .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage), .usage = MemoryUsageVma(usage), .requiredFlags = 0, .preferredFlags = MemoryUsagePreferedVmaFlags(usage), diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index d15559518..ca0e14fad 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp @@ -23,6 +23,7 @@ #include "yuzu/configuration/configure_vibration.h" #include "yuzu/configuration/input_profiles.h" #include "yuzu/main.h" +#include "yuzu/util/controller_navigation.h" namespace { @@ -132,6 +133,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected, }; + ui->labelError->setVisible(false); + // Setup/load everything prior to setting up connections. // This avoids unintentionally changing the states of elements while loading them in. SetSupportedControllers(); @@ -143,6 +146,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( LoadConfiguration(); + controller_navigation = new ControllerNavigation(system.HIDCore(), this); + for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { SetExplainText(i); UpdateControllerIcon(i); @@ -151,6 +156,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) { if (checked) { + // Hide eventual error message about number of controllers + ui->labelError->setVisible(false); for (std::size_t index = 0; index <= i; ++index) { connected_controller_checkboxes[index]->setChecked(checked); } @@ -199,6 +206,12 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QtControllerSelectorDialog::ApplyConfiguration); + connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, + [this](Qt::Key key) { + QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); + QCoreApplication::postEvent(this, event); + }); + // Enhancement: Check if the parameters have already been met before disconnecting controllers. // If all the parameters are met AND only allows a single player, // stop the constructor here as we do not need to continue. @@ -217,6 +230,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( } QtControllerSelectorDialog::~QtControllerSelectorDialog() { + controller_navigation->UnloadController(); system.HIDCore().DisableAllControllerConfiguration(); } @@ -291,6 +305,31 @@ void QtControllerSelectorDialog::CallConfigureInputProfileDialog() { dialog.exec(); } +void QtControllerSelectorDialog::keyPressEvent(QKeyEvent* evt) { + const auto num_connected_players = static_cast<int>( + std::count_if(player_groupboxes.begin(), player_groupboxes.end(), + [](const QGroupBox* player) { return player->isChecked(); })); + + const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players; + const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players; + + if ((evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) && !parameters_met) { + // Display error message when trying to validate using "Enter" and "OK" button is disabled + ui->labelError->setVisible(true); + return; + } else if (evt->key() == Qt::Key_Left && num_connected_players > min_supported_players) { + // Remove a player if possible + connected_controller_checkboxes[num_connected_players - 1]->setChecked(false); + return; + } else if (evt->key() == Qt::Key_Right && num_connected_players < max_supported_players) { + // Add a player, if possible + ui->labelError->setVisible(false); + connected_controller_checkboxes[num_connected_players]->setChecked(true); + return; + } + QDialog::keyPressEvent(evt); +} + bool QtControllerSelectorDialog::CheckIfParametersMet() { // Here, we check and validate the current configuration against all applicable parameters. const auto num_connected_players = static_cast<int>( diff --git a/src/yuzu/applets/qt_controller.h b/src/yuzu/applets/qt_controller.h index 2fdc35857..7f0673d06 100644 --- a/src/yuzu/applets/qt_controller.h +++ b/src/yuzu/applets/qt_controller.h @@ -34,6 +34,8 @@ class HIDCore; enum class NpadStyleIndex : u8; } // namespace Core::HID +class ControllerNavigation; + class QtControllerSelectorDialog final : public QDialog { Q_OBJECT @@ -46,6 +48,8 @@ public: int exec() override; + void keyPressEvent(QKeyEvent* evt) override; + private: // Applies the current configuration. void ApplyConfiguration(); @@ -110,6 +114,8 @@ private: Core::System& system; + ControllerNavigation* controller_navigation = nullptr; + // This is true if and only if all parameters are met. Otherwise, this is false. // This determines whether the "OK" button can be clicked to exit the applet. bool parameters_met{false}; diff --git a/src/yuzu/applets/qt_controller.ui b/src/yuzu/applets/qt_controller.ui index 729e921ee..6f7cb3c13 100644 --- a/src/yuzu/applets/qt_controller.ui +++ b/src/yuzu/applets/qt_controller.ui @@ -2624,13 +2624,53 @@ </spacer> </item> <item alignment="Qt::AlignBottom"> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> - </property> + <widget class="QWidget" name="closeButtons" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_46"> + <property name="spacing"> + <number>7</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="labelError"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="styleSheet"> + <string notr="true">QLabel { color : red; }</string> + </property> + <property name="text"> + <string>Not enough controllers</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="margin"> + <number>0</number> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> </widget> </item> </layout> diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index 61cf00176..f2854c8ec 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp @@ -63,25 +63,15 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) { }; #pragma pack(pop) - QImage source_image = image.convertToFormat(QImage::Format_RGB32); + const QImage source_image = image.convertToFormat(QImage::Format_RGB32); + constexpr std::array<int, 7> scale_sizes{256, 128, 64, 48, 32, 24, 16}; constexpr int bytes_per_pixel = 4; - const int image_size = source_image.width() * source_image.height() * bytes_per_pixel; - - BITMAPINFOHEADER info_header{}; - info_header.biSize = sizeof(BITMAPINFOHEADER), info_header.biWidth = source_image.width(), - info_header.biHeight = source_image.height() * 2, info_header.biPlanes = 1, - info_header.biBitCount = bytes_per_pixel * 8, info_header.biCompression = BI_RGB; - - const IconDir icon_dir{.id_reserved = 0, .id_type = 1, .id_count = 1}; - const IconDirEntry icon_entry{.width = static_cast<BYTE>(source_image.width()), - .height = static_cast<BYTE>(source_image.height() * 2), - .color_count = 0, - .reserved = 0, - .planes = 1, - .bit_count = bytes_per_pixel * 8, - .bytes_in_res = - static_cast<DWORD>(sizeof(BITMAPINFOHEADER) + image_size), - .image_offset = sizeof(IconDir) + sizeof(IconDirEntry)}; + + const IconDir icon_dir{ + .id_reserved = 0, + .id_type = 1, + .id_count = static_cast<WORD>(scale_sizes.size()), + }; Common::FS::IOFile icon_file(path, Common::FS::FileAccessMode::Write, Common::FS::FileType::BinaryFile); @@ -92,20 +82,55 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) { if (!icon_file.Write(icon_dir)) { return false; } - if (!icon_file.Write(icon_entry)) { - return false; - } - if (!icon_file.Write(info_header)) { - return false; + + std::size_t image_offset = sizeof(IconDir) + (sizeof(IconDirEntry) * scale_sizes.size()); + for (std::size_t i = 0; i < scale_sizes.size(); i++) { + const int image_size = scale_sizes[i] * scale_sizes[i] * bytes_per_pixel; + const IconDirEntry icon_entry{ + .width = static_cast<BYTE>(scale_sizes[i]), + .height = static_cast<BYTE>(scale_sizes[i]), + .color_count = 0, + .reserved = 0, + .planes = 1, + .bit_count = bytes_per_pixel * 8, + .bytes_in_res = static_cast<DWORD>(sizeof(BITMAPINFOHEADER) + image_size), + .image_offset = static_cast<DWORD>(image_offset), + }; + image_offset += icon_entry.bytes_in_res; + if (!icon_file.Write(icon_entry)) { + return false; + } } - for (int y = 0; y < image.height(); y++) { - const auto* line = source_image.scanLine(source_image.height() - 1 - y); - std::vector<u8> line_data(source_image.width() * bytes_per_pixel); - std::memcpy(line_data.data(), line, line_data.size()); - if (!icon_file.Write(line_data)) { + for (std::size_t i = 0; i < scale_sizes.size(); i++) { + const QImage scaled_image = source_image.scaled( + scale_sizes[i], scale_sizes[i], Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + const BITMAPINFOHEADER info_header{ + .biSize = sizeof(BITMAPINFOHEADER), + .biWidth = scaled_image.width(), + .biHeight = scaled_image.height() * 2, + .biPlanes = 1, + .biBitCount = bytes_per_pixel * 8, + .biCompression = BI_RGB, + .biSizeImage{}, + .biXPelsPerMeter{}, + .biYPelsPerMeter{}, + .biClrUsed{}, + .biClrImportant{}, + }; + + if (!icon_file.Write(info_header)) { return false; } + + for (int y = 0; y < scaled_image.height(); y++) { + const auto* line = scaled_image.scanLine(scaled_image.height() - 1 - y); + std::vector<u8> line_data(scaled_image.width() * bytes_per_pixel); + std::memcpy(line_data.data(), line, line_data.size()); + if (!icon_file.Write(line_data)) { + return false; + } + } } icon_file.Close(); |