diff options
Diffstat (limited to 'src')
| -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 |
10 files changed, 200 insertions, 75 deletions
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() { | |||
| 77 | "{}, got {}", | 77 | "{}, got {}", |
| 78 | Message::RenderResponse, msg); | 78 | Message::RenderResponse, msg); |
| 79 | } | 79 | } |
| 80 | PostDSPClearCommandBuffer(); | ||
| 80 | } | 81 | } |
| 81 | 82 | ||
| 82 | void AudioRenderer::Send(Direction dir, u32 message) { | 83 | void AudioRenderer::Send(Direction dir, u32 message) { |
| @@ -96,6 +97,14 @@ void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u | |||
| 96 | command_buffers[session_id].reset_buffer = reset; | 97 | command_buffers[session_id].reset_buffer = reset; |
| 97 | } | 98 | } |
| 98 | 99 | ||
| 100 | void AudioRenderer::PostDSPClearCommandBuffer() noexcept { | ||
| 101 | for (auto& buffer : command_buffers) { | ||
| 102 | buffer.buffer = 0; | ||
| 103 | buffer.size = 0; | ||
| 104 | buffer.reset_buffer = false; | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 99 | u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept { | 108 | u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept { |
| 100 | return command_buffers[session_id].remaining_command_count; | 109 | return command_buffers[session_id].remaining_command_count; |
| 101 | } | 110 | } |
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: | |||
| 85 | */ | 85 | */ |
| 86 | void CreateSinkStreams(); | 86 | void CreateSinkStreams(); |
| 87 | 87 | ||
| 88 | void PostDSPClearCommandBuffer() noexcept; | ||
| 89 | |||
| 88 | /// Core system | 90 | /// Core system |
| 89 | Core::System& system; | 91 | Core::System& system; |
| 90 | /// The output sink the AudioRenderer will send samples to | 92 | /// 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() { | |||
| 544 | it++; | 544 | it++; |
| 545 | } | 545 | } |
| 546 | 546 | ||
| 547 | boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads; | 547 | boost::container::small_vector<std::pair<BufferCopy, BufferId>, 16> downloads; |
| 548 | u64 total_size_bytes = 0; | 548 | u64 total_size_bytes = 0; |
| 549 | u64 largest_copy = 0; | 549 | u64 largest_copy = 0; |
| 550 | for (const IntervalSet& intervals : committed_ranges) { | 550 | for (const IntervalSet& intervals : committed_ranges) { |
| @@ -914,6 +914,11 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) { | |||
| 914 | 914 | ||
| 915 | const u32 offset = buffer.Offset(binding.cpu_addr); | 915 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 916 | const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; | 916 | const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; |
| 917 | |||
| 918 | if (is_written) { | ||
| 919 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); | ||
| 920 | } | ||
| 921 | |||
| 917 | if constexpr (NEEDS_BIND_STORAGE_INDEX) { | 922 | if constexpr (NEEDS_BIND_STORAGE_INDEX) { |
| 918 | runtime.BindStorageBuffer(stage, binding_index, buffer, offset, size, is_written); | 923 | runtime.BindStorageBuffer(stage, binding_index, buffer, offset, size, is_written); |
| 919 | ++binding_index; | 924 | ++binding_index; |
| @@ -931,6 +936,11 @@ void BufferCache<P>::BindHostGraphicsTextureBuffers(size_t stage) { | |||
| 931 | const u32 size = binding.size; | 936 | const u32 size = binding.size; |
| 932 | SynchronizeBuffer(buffer, binding.cpu_addr, size); | 937 | SynchronizeBuffer(buffer, binding.cpu_addr, size); |
| 933 | 938 | ||
| 939 | const bool is_written = ((channel_state->written_texture_buffers[stage] >> index) & 1) != 0; | ||
| 940 | if (is_written) { | ||
| 941 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); | ||
| 942 | } | ||
| 943 | |||
| 934 | const u32 offset = buffer.Offset(binding.cpu_addr); | 944 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 935 | const PixelFormat format = binding.format; | 945 | const PixelFormat format = binding.format; |
| 936 | if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { | 946 | if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { |
| @@ -962,6 +972,8 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() { | |||
| 962 | const u32 size = binding.size; | 972 | const u32 size = binding.size; |
| 963 | SynchronizeBuffer(buffer, binding.cpu_addr, size); | 973 | SynchronizeBuffer(buffer, binding.cpu_addr, size); |
| 964 | 974 | ||
| 975 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); | ||
| 976 | |||
| 965 | const u32 offset = buffer.Offset(binding.cpu_addr); | 977 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 966 | host_bindings.buffers.push_back(&buffer); | 978 | host_bindings.buffers.push_back(&buffer); |
| 967 | host_bindings.offsets.push_back(offset); | 979 | host_bindings.offsets.push_back(offset); |
| @@ -1011,6 +1023,11 @@ void BufferCache<P>::BindHostComputeStorageBuffers() { | |||
| 1011 | const u32 offset = buffer.Offset(binding.cpu_addr); | 1023 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 1012 | const bool is_written = | 1024 | const bool is_written = |
| 1013 | ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; | 1025 | ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; |
| 1026 | |||
| 1027 | if (is_written) { | ||
| 1028 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); | ||
| 1029 | } | ||
| 1030 | |||
| 1014 | if constexpr (NEEDS_BIND_STORAGE_INDEX) { | 1031 | if constexpr (NEEDS_BIND_STORAGE_INDEX) { |
| 1015 | runtime.BindComputeStorageBuffer(binding_index, buffer, offset, size, is_written); | 1032 | runtime.BindComputeStorageBuffer(binding_index, buffer, offset, size, is_written); |
| 1016 | ++binding_index; | 1033 | ++binding_index; |
| @@ -1028,6 +1045,12 @@ void BufferCache<P>::BindHostComputeTextureBuffers() { | |||
| 1028 | const u32 size = binding.size; | 1045 | const u32 size = binding.size; |
| 1029 | SynchronizeBuffer(buffer, binding.cpu_addr, size); | 1046 | SynchronizeBuffer(buffer, binding.cpu_addr, size); |
| 1030 | 1047 | ||
| 1048 | const bool is_written = | ||
| 1049 | ((channel_state->written_compute_texture_buffers >> index) & 1) != 0; | ||
| 1050 | if (is_written) { | ||
| 1051 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); | ||
| 1052 | } | ||
| 1053 | |||
| 1031 | const u32 offset = buffer.Offset(binding.cpu_addr); | 1054 | const u32 offset = buffer.Offset(binding.cpu_addr); |
| 1032 | const PixelFormat format = binding.format; | 1055 | const PixelFormat format = binding.format; |
| 1033 | if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { | 1056 | if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { |
| @@ -1201,16 +1224,11 @@ void BufferCache<P>::UpdateUniformBuffers(size_t stage) { | |||
| 1201 | 1224 | ||
| 1202 | template <class P> | 1225 | template <class P> |
| 1203 | void BufferCache<P>::UpdateStorageBuffers(size_t stage) { | 1226 | void BufferCache<P>::UpdateStorageBuffers(size_t stage) { |
| 1204 | const u32 written_mask = channel_state->written_storage_buffers[stage]; | ||
| 1205 | ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) { | 1227 | ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) { |
| 1206 | // Resolve buffer | 1228 | // Resolve buffer |
| 1207 | Binding& binding = channel_state->storage_buffers[stage][index]; | 1229 | Binding& binding = channel_state->storage_buffers[stage][index]; |
| 1208 | const BufferId buffer_id = FindBuffer(binding.cpu_addr, binding.size); | 1230 | const BufferId buffer_id = FindBuffer(binding.cpu_addr, binding.size); |
| 1209 | binding.buffer_id = buffer_id; | 1231 | binding.buffer_id = buffer_id; |
| 1210 | // Mark buffer as written if needed | ||
| 1211 | if (((written_mask >> index) & 1) != 0) { | ||
| 1212 | MarkWrittenBuffer(buffer_id, binding.cpu_addr, binding.size); | ||
| 1213 | } | ||
| 1214 | }); | 1232 | }); |
| 1215 | } | 1233 | } |
| 1216 | 1234 | ||
| @@ -1219,10 +1237,6 @@ void BufferCache<P>::UpdateTextureBuffers(size_t stage) { | |||
| 1219 | ForEachEnabledBit(channel_state->enabled_texture_buffers[stage], [&](u32 index) { | 1237 | ForEachEnabledBit(channel_state->enabled_texture_buffers[stage], [&](u32 index) { |
| 1220 | Binding& binding = channel_state->texture_buffers[stage][index]; | 1238 | Binding& binding = channel_state->texture_buffers[stage][index]; |
| 1221 | binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size); | 1239 | binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size); |
| 1222 | // Mark buffer as written if needed | ||
| 1223 | if (((channel_state->written_texture_buffers[stage] >> index) & 1) != 0) { | ||
| 1224 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, binding.size); | ||
| 1225 | } | ||
| 1226 | }); | 1240 | }); |
| 1227 | } | 1241 | } |
| 1228 | 1242 | ||
| @@ -1252,7 +1266,6 @@ void BufferCache<P>::UpdateTransformFeedbackBuffer(u32 index) { | |||
| 1252 | .size = size, | 1266 | .size = size, |
| 1253 | .buffer_id = buffer_id, | 1267 | .buffer_id = buffer_id, |
| 1254 | }; | 1268 | }; |
| 1255 | MarkWrittenBuffer(buffer_id, *cpu_addr, size); | ||
| 1256 | } | 1269 | } |
| 1257 | 1270 | ||
| 1258 | template <class P> | 1271 | template <class P> |
| @@ -1279,10 +1292,6 @@ void BufferCache<P>::UpdateComputeStorageBuffers() { | |||
| 1279 | // Resolve buffer | 1292 | // Resolve buffer |
| 1280 | Binding& binding = channel_state->compute_storage_buffers[index]; | 1293 | Binding& binding = channel_state->compute_storage_buffers[index]; |
| 1281 | binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size); | 1294 | binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size); |
| 1282 | // Mark as written if needed | ||
| 1283 | if (((channel_state->written_compute_storage_buffers >> index) & 1) != 0) { | ||
| 1284 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, binding.size); | ||
| 1285 | } | ||
| 1286 | }); | 1295 | }); |
| 1287 | } | 1296 | } |
| 1288 | 1297 | ||
| @@ -1291,18 +1300,11 @@ void BufferCache<P>::UpdateComputeTextureBuffers() { | |||
| 1291 | ForEachEnabledBit(channel_state->enabled_compute_texture_buffers, [&](u32 index) { | 1300 | ForEachEnabledBit(channel_state->enabled_compute_texture_buffers, [&](u32 index) { |
| 1292 | Binding& binding = channel_state->compute_texture_buffers[index]; | 1301 | Binding& binding = channel_state->compute_texture_buffers[index]; |
| 1293 | binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size); | 1302 | binding.buffer_id = FindBuffer(binding.cpu_addr, binding.size); |
| 1294 | // Mark as written if needed | ||
| 1295 | if (((channel_state->written_compute_texture_buffers >> index) & 1) != 0) { | ||
| 1296 | MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, binding.size); | ||
| 1297 | } | ||
| 1298 | }); | 1303 | }); |
| 1299 | } | 1304 | } |
| 1300 | 1305 | ||
| 1301 | template <class P> | 1306 | template <class P> |
| 1302 | void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 size) { | 1307 | void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 size) { |
| 1303 | if (memory_tracker.IsRegionCpuModified(cpu_addr, size)) { | ||
| 1304 | SynchronizeBuffer(slot_buffers[buffer_id], cpu_addr, size); | ||
| 1305 | } | ||
| 1306 | memory_tracker.MarkRegionAsGpuModified(cpu_addr, size); | 1308 | memory_tracker.MarkRegionAsGpuModified(cpu_addr, size); |
| 1307 | 1309 | ||
| 1308 | const IntervalType base_interval{cpu_addr, cpu_addr + size}; | 1310 | 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) { | |||
| 1048 | } | 1048 | } |
| 1049 | 1049 | ||
| 1050 | bool Image::ScaleUp(bool ignore) { | 1050 | bool Image::ScaleUp(bool ignore) { |
| 1051 | const auto& resolution = runtime->resolution; | ||
| 1052 | if (!resolution.active) { | ||
| 1053 | return false; | ||
| 1054 | } | ||
| 1051 | if (True(flags & ImageFlagBits::Rescaled)) { | 1055 | if (True(flags & ImageFlagBits::Rescaled)) { |
| 1052 | return false; | 1056 | return false; |
| 1053 | } | 1057 | } |
| @@ -1060,9 +1064,6 @@ bool Image::ScaleUp(bool ignore) { | |||
| 1060 | return false; | 1064 | return false; |
| 1061 | } | 1065 | } |
| 1062 | flags |= ImageFlagBits::Rescaled; | 1066 | flags |= ImageFlagBits::Rescaled; |
| 1063 | if (!runtime->resolution.active) { | ||
| 1064 | return false; | ||
| 1065 | } | ||
| 1066 | has_scaled = true; | 1067 | has_scaled = true; |
| 1067 | if (ignore) { | 1068 | if (ignore) { |
| 1068 | current_texture = upscaled_backup.handle; | 1069 | current_texture = upscaled_backup.handle; |
| @@ -1073,13 +1074,14 @@ bool Image::ScaleUp(bool ignore) { | |||
| 1073 | } | 1074 | } |
| 1074 | 1075 | ||
| 1075 | bool Image::ScaleDown(bool ignore) { | 1076 | bool Image::ScaleDown(bool ignore) { |
| 1076 | if (False(flags & ImageFlagBits::Rescaled)) { | 1077 | const auto& resolution = runtime->resolution; |
| 1078 | if (!resolution.active) { | ||
| 1077 | return false; | 1079 | return false; |
| 1078 | } | 1080 | } |
| 1079 | flags &= ~ImageFlagBits::Rescaled; | 1081 | if (False(flags & ImageFlagBits::Rescaled)) { |
| 1080 | if (!runtime->resolution.active) { | ||
| 1081 | return false; | 1082 | return false; |
| 1082 | } | 1083 | } |
| 1084 | flags &= ~ImageFlagBits::Rescaled; | ||
| 1083 | if (ignore) { | 1085 | if (ignore) { |
| 1084 | current_texture = texture.handle; | 1086 | current_texture = texture.handle; |
| 1085 | return true; | 1087 | 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 { | |||
| 1530 | } | 1530 | } |
| 1531 | 1531 | ||
| 1532 | bool Image::ScaleUp(bool ignore) { | 1532 | bool Image::ScaleUp(bool ignore) { |
| 1533 | const auto& resolution = runtime->resolution; | ||
| 1534 | if (!resolution.active) { | ||
| 1535 | return false; | ||
| 1536 | } | ||
| 1533 | if (True(flags & ImageFlagBits::Rescaled)) { | 1537 | if (True(flags & ImageFlagBits::Rescaled)) { |
| 1534 | return false; | 1538 | return false; |
| 1535 | } | 1539 | } |
| 1536 | ASSERT(info.type != ImageType::Linear); | 1540 | ASSERT(info.type != ImageType::Linear); |
| 1537 | flags |= ImageFlagBits::Rescaled; | 1541 | flags |= ImageFlagBits::Rescaled; |
| 1538 | const auto& resolution = runtime->resolution; | ||
| 1539 | if (!resolution.active) { | ||
| 1540 | return false; | ||
| 1541 | } | ||
| 1542 | has_scaled = true; | 1542 | has_scaled = true; |
| 1543 | if (!scaled_image) { | 1543 | if (!scaled_image) { |
| 1544 | const bool is_2d = info.type == ImageType::e2D; | 1544 | const bool is_2d = info.type == ImageType::e2D; |
| @@ -1567,15 +1567,15 @@ bool Image::ScaleUp(bool ignore) { | |||
| 1567 | } | 1567 | } |
| 1568 | 1568 | ||
| 1569 | bool Image::ScaleDown(bool ignore) { | 1569 | bool Image::ScaleDown(bool ignore) { |
| 1570 | const auto& resolution = runtime->resolution; | ||
| 1571 | if (!resolution.active) { | ||
| 1572 | return false; | ||
| 1573 | } | ||
| 1570 | if (False(flags & ImageFlagBits::Rescaled)) { | 1574 | if (False(flags & ImageFlagBits::Rescaled)) { |
| 1571 | return false; | 1575 | return false; |
| 1572 | } | 1576 | } |
| 1573 | ASSERT(info.type != ImageType::Linear); | 1577 | ASSERT(info.type != ImageType::Linear); |
| 1574 | flags &= ~ImageFlagBits::Rescaled; | 1578 | flags &= ~ImageFlagBits::Rescaled; |
| 1575 | const auto& resolution = runtime->resolution; | ||
| 1576 | if (!resolution.active) { | ||
| 1577 | return false; | ||
| 1578 | } | ||
| 1579 | current_image = *original_image; | 1579 | current_image = *original_image; |
| 1580 | if (ignore) { | 1580 | if (ignore) { |
| 1581 | return true; | 1581 | 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 { | |||
| 66 | switch (usage) { | 66 | switch (usage) { |
| 67 | case MemoryUsage::Upload: | 67 | case MemoryUsage::Upload: |
| 68 | case MemoryUsage::Stream: | 68 | case MemoryUsage::Stream: |
| 69 | return VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; | 69 | return VMA_ALLOCATION_CREATE_MAPPED_BIT | |
| 70 | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; | ||
| 70 | case MemoryUsage::Download: | 71 | case MemoryUsage::Download: |
| 71 | return VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; | 72 | return VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; |
| 72 | case MemoryUsage::DeviceLocal: | 73 | case MemoryUsage::DeviceLocal: |
| 73 | return {}; | 74 | return {}; |
| 74 | } | 75 | } |
| @@ -252,8 +253,7 @@ vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const { | |||
| 252 | 253 | ||
| 253 | vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const { | 254 | vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const { |
| 254 | const VmaAllocationCreateInfo alloc_ci = { | 255 | const VmaAllocationCreateInfo alloc_ci = { |
| 255 | .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT | | 256 | .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage), |
| 256 | MemoryUsageVmaFlags(usage), | ||
| 257 | .usage = MemoryUsageVma(usage), | 257 | .usage = MemoryUsageVma(usage), |
| 258 | .requiredFlags = 0, | 258 | .requiredFlags = 0, |
| 259 | .preferredFlags = MemoryUsagePreferedVmaFlags(usage), | 259 | .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 @@ | |||
| 23 | #include "yuzu/configuration/configure_vibration.h" | 23 | #include "yuzu/configuration/configure_vibration.h" |
| 24 | #include "yuzu/configuration/input_profiles.h" | 24 | #include "yuzu/configuration/input_profiles.h" |
| 25 | #include "yuzu/main.h" | 25 | #include "yuzu/main.h" |
| 26 | #include "yuzu/util/controller_navigation.h" | ||
| 26 | 27 | ||
| 27 | namespace { | 28 | namespace { |
| 28 | 29 | ||
| @@ -132,6 +133,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( | |||
| 132 | ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected, | 133 | ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected, |
| 133 | }; | 134 | }; |
| 134 | 135 | ||
| 136 | ui->labelError->setVisible(false); | ||
| 137 | |||
| 135 | // Setup/load everything prior to setting up connections. | 138 | // Setup/load everything prior to setting up connections. |
| 136 | // This avoids unintentionally changing the states of elements while loading them in. | 139 | // This avoids unintentionally changing the states of elements while loading them in. |
| 137 | SetSupportedControllers(); | 140 | SetSupportedControllers(); |
| @@ -143,6 +146,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( | |||
| 143 | 146 | ||
| 144 | LoadConfiguration(); | 147 | LoadConfiguration(); |
| 145 | 148 | ||
| 149 | controller_navigation = new ControllerNavigation(system.HIDCore(), this); | ||
| 150 | |||
| 146 | for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { | 151 | for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { |
| 147 | SetExplainText(i); | 152 | SetExplainText(i); |
| 148 | UpdateControllerIcon(i); | 153 | UpdateControllerIcon(i); |
| @@ -151,6 +156,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( | |||
| 151 | 156 | ||
| 152 | connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) { | 157 | connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) { |
| 153 | if (checked) { | 158 | if (checked) { |
| 159 | // Hide eventual error message about number of controllers | ||
| 160 | ui->labelError->setVisible(false); | ||
| 154 | for (std::size_t index = 0; index <= i; ++index) { | 161 | for (std::size_t index = 0; index <= i; ++index) { |
| 155 | connected_controller_checkboxes[index]->setChecked(checked); | 162 | connected_controller_checkboxes[index]->setChecked(checked); |
| 156 | } | 163 | } |
| @@ -199,6 +206,12 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( | |||
| 199 | connect(ui->buttonBox, &QDialogButtonBox::accepted, this, | 206 | connect(ui->buttonBox, &QDialogButtonBox::accepted, this, |
| 200 | &QtControllerSelectorDialog::ApplyConfiguration); | 207 | &QtControllerSelectorDialog::ApplyConfiguration); |
| 201 | 208 | ||
| 209 | connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, | ||
| 210 | [this](Qt::Key key) { | ||
| 211 | QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); | ||
| 212 | QCoreApplication::postEvent(this, event); | ||
| 213 | }); | ||
| 214 | |||
| 202 | // Enhancement: Check if the parameters have already been met before disconnecting controllers. | 215 | // Enhancement: Check if the parameters have already been met before disconnecting controllers. |
| 203 | // If all the parameters are met AND only allows a single player, | 216 | // If all the parameters are met AND only allows a single player, |
| 204 | // stop the constructor here as we do not need to continue. | 217 | // stop the constructor here as we do not need to continue. |
| @@ -217,6 +230,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( | |||
| 217 | } | 230 | } |
| 218 | 231 | ||
| 219 | QtControllerSelectorDialog::~QtControllerSelectorDialog() { | 232 | QtControllerSelectorDialog::~QtControllerSelectorDialog() { |
| 233 | controller_navigation->UnloadController(); | ||
| 220 | system.HIDCore().DisableAllControllerConfiguration(); | 234 | system.HIDCore().DisableAllControllerConfiguration(); |
| 221 | } | 235 | } |
| 222 | 236 | ||
| @@ -291,6 +305,31 @@ void QtControllerSelectorDialog::CallConfigureInputProfileDialog() { | |||
| 291 | dialog.exec(); | 305 | dialog.exec(); |
| 292 | } | 306 | } |
| 293 | 307 | ||
| 308 | void QtControllerSelectorDialog::keyPressEvent(QKeyEvent* evt) { | ||
| 309 | const auto num_connected_players = static_cast<int>( | ||
| 310 | std::count_if(player_groupboxes.begin(), player_groupboxes.end(), | ||
| 311 | [](const QGroupBox* player) { return player->isChecked(); })); | ||
| 312 | |||
| 313 | const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players; | ||
| 314 | const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players; | ||
| 315 | |||
| 316 | if ((evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) && !parameters_met) { | ||
| 317 | // Display error message when trying to validate using "Enter" and "OK" button is disabled | ||
| 318 | ui->labelError->setVisible(true); | ||
| 319 | return; | ||
| 320 | } else if (evt->key() == Qt::Key_Left && num_connected_players > min_supported_players) { | ||
| 321 | // Remove a player if possible | ||
| 322 | connected_controller_checkboxes[num_connected_players - 1]->setChecked(false); | ||
| 323 | return; | ||
| 324 | } else if (evt->key() == Qt::Key_Right && num_connected_players < max_supported_players) { | ||
| 325 | // Add a player, if possible | ||
| 326 | ui->labelError->setVisible(false); | ||
| 327 | connected_controller_checkboxes[num_connected_players]->setChecked(true); | ||
| 328 | return; | ||
| 329 | } | ||
| 330 | QDialog::keyPressEvent(evt); | ||
| 331 | } | ||
| 332 | |||
| 294 | bool QtControllerSelectorDialog::CheckIfParametersMet() { | 333 | bool QtControllerSelectorDialog::CheckIfParametersMet() { |
| 295 | // Here, we check and validate the current configuration against all applicable parameters. | 334 | // Here, we check and validate the current configuration against all applicable parameters. |
| 296 | const auto num_connected_players = static_cast<int>( | 335 | 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; | |||
| 34 | enum class NpadStyleIndex : u8; | 34 | enum class NpadStyleIndex : u8; |
| 35 | } // namespace Core::HID | 35 | } // namespace Core::HID |
| 36 | 36 | ||
| 37 | class ControllerNavigation; | ||
| 38 | |||
| 37 | class QtControllerSelectorDialog final : public QDialog { | 39 | class QtControllerSelectorDialog final : public QDialog { |
| 38 | Q_OBJECT | 40 | Q_OBJECT |
| 39 | 41 | ||
| @@ -46,6 +48,8 @@ public: | |||
| 46 | 48 | ||
| 47 | int exec() override; | 49 | int exec() override; |
| 48 | 50 | ||
| 51 | void keyPressEvent(QKeyEvent* evt) override; | ||
| 52 | |||
| 49 | private: | 53 | private: |
| 50 | // Applies the current configuration. | 54 | // Applies the current configuration. |
| 51 | void ApplyConfiguration(); | 55 | void ApplyConfiguration(); |
| @@ -110,6 +114,8 @@ private: | |||
| 110 | 114 | ||
| 111 | Core::System& system; | 115 | Core::System& system; |
| 112 | 116 | ||
| 117 | ControllerNavigation* controller_navigation = nullptr; | ||
| 118 | |||
| 113 | // This is true if and only if all parameters are met. Otherwise, this is false. | 119 | // This is true if and only if all parameters are met. Otherwise, this is false. |
| 114 | // This determines whether the "OK" button can be clicked to exit the applet. | 120 | // This determines whether the "OK" button can be clicked to exit the applet. |
| 115 | bool parameters_met{false}; | 121 | 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 @@ | |||
| 2624 | </spacer> | 2624 | </spacer> |
| 2625 | </item> | 2625 | </item> |
| 2626 | <item alignment="Qt::AlignBottom"> | 2626 | <item alignment="Qt::AlignBottom"> |
| 2627 | <widget class="QDialogButtonBox" name="buttonBox"> | 2627 | <widget class="QWidget" name="closeButtons" native="true"> |
| 2628 | <property name="enabled"> | 2628 | <layout class="QVBoxLayout" name="verticalLayout_46"> |
| 2629 | <bool>true</bool> | 2629 | <property name="spacing"> |
| 2630 | </property> | 2630 | <number>7</number> |
| 2631 | <property name="standardButtons"> | 2631 | </property> |
| 2632 | <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | 2632 | <property name="leftMargin"> |
| 2633 | </property> | 2633 | <number>0</number> |
| 2634 | </property> | ||
| 2635 | <property name="topMargin"> | ||
| 2636 | <number>0</number> | ||
| 2637 | </property> | ||
| 2638 | <property name="rightMargin"> | ||
| 2639 | <number>0</number> | ||
| 2640 | </property> | ||
| 2641 | <property name="bottomMargin"> | ||
| 2642 | <number>0</number> | ||
| 2643 | </property> | ||
| 2644 | <item> | ||
| 2645 | <widget class="QLabel" name="labelError"> | ||
| 2646 | <property name="enabled"> | ||
| 2647 | <bool>true</bool> | ||
| 2648 | </property> | ||
| 2649 | <property name="styleSheet"> | ||
| 2650 | <string notr="true">QLabel { color : red; }</string> | ||
| 2651 | </property> | ||
| 2652 | <property name="text"> | ||
| 2653 | <string>Not enough controllers</string> | ||
| 2654 | </property> | ||
| 2655 | <property name="alignment"> | ||
| 2656 | <set>Qt::AlignCenter</set> | ||
| 2657 | </property> | ||
| 2658 | <property name="margin"> | ||
| 2659 | <number>0</number> | ||
| 2660 | </property> | ||
| 2661 | </widget> | ||
| 2662 | </item> | ||
| 2663 | <item> | ||
| 2664 | <widget class="QDialogButtonBox" name="buttonBox"> | ||
| 2665 | <property name="enabled"> | ||
| 2666 | <bool>true</bool> | ||
| 2667 | </property> | ||
| 2668 | <property name="standardButtons"> | ||
| 2669 | <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||
| 2670 | </property> | ||
| 2671 | </widget> | ||
| 2672 | </item> | ||
| 2673 | </layout> | ||
| 2634 | </widget> | 2674 | </widget> |
| 2635 | </item> | 2675 | </item> |
| 2636 | </layout> | 2676 | </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) { | |||
| 63 | }; | 63 | }; |
| 64 | #pragma pack(pop) | 64 | #pragma pack(pop) |
| 65 | 65 | ||
| 66 | QImage source_image = image.convertToFormat(QImage::Format_RGB32); | 66 | const QImage source_image = image.convertToFormat(QImage::Format_RGB32); |
| 67 | constexpr std::array<int, 7> scale_sizes{256, 128, 64, 48, 32, 24, 16}; | ||
| 67 | constexpr int bytes_per_pixel = 4; | 68 | constexpr int bytes_per_pixel = 4; |
| 68 | const int image_size = source_image.width() * source_image.height() * bytes_per_pixel; | 69 | |
| 69 | 70 | const IconDir icon_dir{ | |
| 70 | BITMAPINFOHEADER info_header{}; | 71 | .id_reserved = 0, |
| 71 | info_header.biSize = sizeof(BITMAPINFOHEADER), info_header.biWidth = source_image.width(), | 72 | .id_type = 1, |
| 72 | info_header.biHeight = source_image.height() * 2, info_header.biPlanes = 1, | 73 | .id_count = static_cast<WORD>(scale_sizes.size()), |
| 73 | info_header.biBitCount = bytes_per_pixel * 8, info_header.biCompression = BI_RGB; | 74 | }; |
| 74 | |||
| 75 | const IconDir icon_dir{.id_reserved = 0, .id_type = 1, .id_count = 1}; | ||
| 76 | const IconDirEntry icon_entry{.width = static_cast<BYTE>(source_image.width()), | ||
| 77 | .height = static_cast<BYTE>(source_image.height() * 2), | ||
| 78 | .color_count = 0, | ||
| 79 | .reserved = 0, | ||
| 80 | .planes = 1, | ||
| 81 | .bit_count = bytes_per_pixel * 8, | ||
| 82 | .bytes_in_res = | ||
| 83 | static_cast<DWORD>(sizeof(BITMAPINFOHEADER) + image_size), | ||
| 84 | .image_offset = sizeof(IconDir) + sizeof(IconDirEntry)}; | ||
| 85 | 75 | ||
| 86 | Common::FS::IOFile icon_file(path, Common::FS::FileAccessMode::Write, | 76 | Common::FS::IOFile icon_file(path, Common::FS::FileAccessMode::Write, |
| 87 | Common::FS::FileType::BinaryFile); | 77 | Common::FS::FileType::BinaryFile); |
| @@ -92,20 +82,55 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) { | |||
| 92 | if (!icon_file.Write(icon_dir)) { | 82 | if (!icon_file.Write(icon_dir)) { |
| 93 | return false; | 83 | return false; |
| 94 | } | 84 | } |
| 95 | if (!icon_file.Write(icon_entry)) { | 85 | |
| 96 | return false; | 86 | std::size_t image_offset = sizeof(IconDir) + (sizeof(IconDirEntry) * scale_sizes.size()); |
| 97 | } | 87 | for (std::size_t i = 0; i < scale_sizes.size(); i++) { |
| 98 | if (!icon_file.Write(info_header)) { | 88 | const int image_size = scale_sizes[i] * scale_sizes[i] * bytes_per_pixel; |
| 99 | return false; | 89 | const IconDirEntry icon_entry{ |
| 90 | .width = static_cast<BYTE>(scale_sizes[i]), | ||
| 91 | .height = static_cast<BYTE>(scale_sizes[i]), | ||
| 92 | .color_count = 0, | ||
| 93 | .reserved = 0, | ||
| 94 | .planes = 1, | ||
| 95 | .bit_count = bytes_per_pixel * 8, | ||
| 96 | .bytes_in_res = static_cast<DWORD>(sizeof(BITMAPINFOHEADER) + image_size), | ||
| 97 | .image_offset = static_cast<DWORD>(image_offset), | ||
| 98 | }; | ||
| 99 | image_offset += icon_entry.bytes_in_res; | ||
| 100 | if (!icon_file.Write(icon_entry)) { | ||
| 101 | return false; | ||
| 102 | } | ||
| 100 | } | 103 | } |
| 101 | 104 | ||
| 102 | for (int y = 0; y < image.height(); y++) { | 105 | for (std::size_t i = 0; i < scale_sizes.size(); i++) { |
| 103 | const auto* line = source_image.scanLine(source_image.height() - 1 - y); | 106 | const QImage scaled_image = source_image.scaled( |
| 104 | std::vector<u8> line_data(source_image.width() * bytes_per_pixel); | 107 | scale_sizes[i], scale_sizes[i], Qt::IgnoreAspectRatio, Qt::SmoothTransformation); |
| 105 | std::memcpy(line_data.data(), line, line_data.size()); | 108 | const BITMAPINFOHEADER info_header{ |
| 106 | if (!icon_file.Write(line_data)) { | 109 | .biSize = sizeof(BITMAPINFOHEADER), |
| 110 | .biWidth = scaled_image.width(), | ||
| 111 | .biHeight = scaled_image.height() * 2, | ||
| 112 | .biPlanes = 1, | ||
| 113 | .biBitCount = bytes_per_pixel * 8, | ||
| 114 | .biCompression = BI_RGB, | ||
| 115 | .biSizeImage{}, | ||
| 116 | .biXPelsPerMeter{}, | ||
| 117 | .biYPelsPerMeter{}, | ||
| 118 | .biClrUsed{}, | ||
| 119 | .biClrImportant{}, | ||
| 120 | }; | ||
| 121 | |||
| 122 | if (!icon_file.Write(info_header)) { | ||
| 107 | return false; | 123 | return false; |
| 108 | } | 124 | } |
| 125 | |||
| 126 | for (int y = 0; y < scaled_image.height(); y++) { | ||
| 127 | const auto* line = scaled_image.scanLine(scaled_image.height() - 1 - y); | ||
| 128 | std::vector<u8> line_data(scaled_image.width() * bytes_per_pixel); | ||
| 129 | std::memcpy(line_data.data(), line, line_data.size()); | ||
| 130 | if (!icon_file.Write(line_data)) { | ||
| 131 | return false; | ||
| 132 | } | ||
| 133 | } | ||
| 109 | } | 134 | } |
| 110 | icon_file.Close(); | 135 | icon_file.Close(); |
| 111 | 136 | ||