Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c4ec76edba | |||
| 65d3300875 | |||
| 7b2ac196d2 | |||
| fe04a7523a | |||
| 45a76637f5 | |||
| bf7c45e560 | |||
| 6f4a080b98 | |||
| da6824d9fd | |||
| b36f45b239 | |||
| 07ae6659e7 | |||
| 880b004321 | |||
| 21ebe3e462 | |||
| 84b0e29b56 | |||
| 5ecdcfa334 | |||
| 5f4857691e | |||
| b50ce645ac | |||
| b6d19329ac | |||
| 8c769b71a1 | |||
| 9512992fe2 | |||
| 6b10f04322 | |||
| c206a04747 | |||
| ec6ddaf766 | |||
| 00b0938f10 | |||
| ed58445111 | |||
| a764f49910 | |||
| b881949b6d |
@@ -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}/"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Vendored
+4
@@ -120,6 +120,10 @@ QWidget#connectedControllers {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#closeButtons {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#playersSupported,
|
||||
QWidget#controllersSupported,
|
||||
QWidget#controllerSupported1,
|
||||
|
||||
Vendored
+4
@@ -1380,6 +1380,10 @@ QWidget#connectedControllers {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#closeButtons {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#playersSupported,
|
||||
QWidget#controllersSupported,
|
||||
QWidget#controllerSupported1,
|
||||
|
||||
@@ -2305,6 +2305,10 @@ QWidget#connectedControllers {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#closeButtons {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#playersSupported,
|
||||
QWidget#controllersSupported,
|
||||
QWidget#controllerSupported1,
|
||||
|
||||
Vendored
+1
-1
@@ -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")
|
||||
|
||||
Vendored
+1
-1
Submodule externals/nx_tzdb/tzdb_to_nx updated: 212afa2394...0d17dd066d
@@ -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;
|
||||
}
|
||||
|
||||
@@ -85,6 +85,8 @@ private:
|
||||
*/
|
||||
void CreateSinkStreams();
|
||||
|
||||
void PostDSPClearCommandBuffer() noexcept;
|
||||
|
||||
/// Core system
|
||||
Core::System& system;
|
||||
/// The output sink the AudioRenderer will send samples to
|
||||
|
||||
@@ -3405,6 +3405,11 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_K
|
||||
new_attr, KMemoryBlockDisableMergeAttribute::Locked,
|
||||
KMemoryBlockDisableMergeAttribute::None);
|
||||
|
||||
// If we have an output page group, open.
|
||||
if (out_pg) {
|
||||
out_pg->Open();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Service::Capture {
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
auto album_manager = std::make_shared<AlbumManager>();
|
||||
auto album_manager = std::make_shared<AlbumManager>(system);
|
||||
|
||||
server_manager->RegisterNamedService(
|
||||
"caps:a", std::make_shared<IAlbumAccessorService>(system, album_manager));
|
||||
|
||||
@@ -8,12 +8,15 @@
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/caps/caps_manager.h"
|
||||
#include "core/hle/service/caps/caps_result.h"
|
||||
#include "core/hle/service/time/time_manager.h"
|
||||
#include "core/hle/service/time/time_zone_content_manager.h"
|
||||
|
||||
namespace Service::Capture {
|
||||
|
||||
AlbumManager::AlbumManager() {}
|
||||
AlbumManager::AlbumManager(Core::System& system_) : system{system_} {}
|
||||
|
||||
AlbumManager::~AlbumManager() = default;
|
||||
|
||||
@@ -83,6 +86,34 @@ Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, Albu
|
||||
}
|
||||
|
||||
Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries,
|
||||
ContentType contex_type, s64 start_posix_time,
|
||||
s64 end_posix_time, u64 aruid) const {
|
||||
if (!is_mounted) {
|
||||
return ResultIsNotMounted;
|
||||
}
|
||||
|
||||
std::vector<ApplicationAlbumEntry> album_entries;
|
||||
const auto start_date = ConvertToAlbumDateTime(start_posix_time);
|
||||
const auto end_date = ConvertToAlbumDateTime(end_posix_time);
|
||||
const auto result = GetAlbumFileList(album_entries, contex_type, start_date, end_date, aruid);
|
||||
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (const auto& album_entry : album_entries) {
|
||||
ApplicationAlbumFileEntry entry{
|
||||
.entry = album_entry,
|
||||
.datetime = album_entry.datetime,
|
||||
.unknown = {},
|
||||
};
|
||||
out_entries.push_back(entry);
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries,
|
||||
ContentType contex_type, AlbumFileDateTime start_date,
|
||||
AlbumFileDateTime end_date, u64 aruid) const {
|
||||
if (!is_mounted) {
|
||||
@@ -93,31 +124,25 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& ou
|
||||
if (file_id.type != contex_type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file_id.date > start_date) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file_id.date < end_date) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (out_entries.size() >= SdAlbumFileLimit) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto entry_size = Common::FS::GetSize(path);
|
||||
ApplicationAlbumFileEntry entry{.entry =
|
||||
{
|
||||
.size = entry_size,
|
||||
.hash{},
|
||||
.datetime = file_id.date,
|
||||
.storage = file_id.storage,
|
||||
.content = contex_type,
|
||||
.unknown = 1,
|
||||
},
|
||||
.datetime = file_id.date,
|
||||
.unknown = {}};
|
||||
ApplicationAlbumEntry entry{
|
||||
.size = entry_size,
|
||||
.hash{},
|
||||
.datetime = file_id.date,
|
||||
.storage = file_id.storage,
|
||||
.content = contex_type,
|
||||
.unknown = 1,
|
||||
};
|
||||
out_entries.push_back(entry);
|
||||
}
|
||||
|
||||
@@ -274,12 +299,12 @@ Result AlbumManager::GetAlbumEntry(AlbumEntry& out_entry, const std::filesystem:
|
||||
.application_id = static_cast<u64>(std::stoll(application, 0, 16)),
|
||||
.date =
|
||||
{
|
||||
.year = static_cast<u16>(std::stoi(year)),
|
||||
.month = static_cast<u8>(std::stoi(month)),
|
||||
.day = static_cast<u8>(std::stoi(day)),
|
||||
.hour = static_cast<u8>(std::stoi(hour)),
|
||||
.minute = static_cast<u8>(std::stoi(minute)),
|
||||
.second = static_cast<u8>(std::stoi(second)),
|
||||
.year = static_cast<s16>(std::stoi(year)),
|
||||
.month = static_cast<s8>(std::stoi(month)),
|
||||
.day = static_cast<s8>(std::stoi(day)),
|
||||
.hour = static_cast<s8>(std::stoi(hour)),
|
||||
.minute = static_cast<s8>(std::stoi(minute)),
|
||||
.second = static_cast<s8>(std::stoi(second)),
|
||||
.unique_id = 0,
|
||||
},
|
||||
.storage = AlbumStorage::Sd,
|
||||
@@ -339,4 +364,23 @@ Result AlbumManager::LoadImage(std::span<u8> out_image, const std::filesystem::p
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
AlbumFileDateTime AlbumManager::ConvertToAlbumDateTime(u64 posix_time) const {
|
||||
Time::TimeZone::CalendarInfo calendar_date{};
|
||||
const auto& time_zone_manager =
|
||||
system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager();
|
||||
|
||||
time_zone_manager.ToCalendarTimeWithMyRules(posix_time, calendar_date);
|
||||
|
||||
return {
|
||||
.year = calendar_date.time.year,
|
||||
.month = calendar_date.time.month,
|
||||
.day = calendar_date.time.day,
|
||||
.hour = calendar_date.time.hour,
|
||||
.minute = calendar_date.time.minute,
|
||||
.second = calendar_date.time.second,
|
||||
.unique_id = 0,
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Service::Capture
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Service::Capture {
|
||||
|
||||
class AlbumManager {
|
||||
public:
|
||||
explicit AlbumManager();
|
||||
explicit AlbumManager(Core::System& system_);
|
||||
~AlbumManager();
|
||||
|
||||
Result DeleteAlbumFile(const AlbumFileId& file_id);
|
||||
@@ -45,6 +45,9 @@ public:
|
||||
Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage,
|
||||
u8 flags) const;
|
||||
Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries,
|
||||
ContentType contex_type, s64 start_posix_time, s64 end_posix_time,
|
||||
u64 aruid) const;
|
||||
Result GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries,
|
||||
ContentType contex_type, AlbumFileDateTime start_date,
|
||||
AlbumFileDateTime end_date, u64 aruid) const;
|
||||
Result GetAutoSavingStorage(bool& out_is_autosaving) const;
|
||||
@@ -65,8 +68,12 @@ private:
|
||||
Result LoadImage(std::span<u8> out_image, const std::filesystem::path& path, int width,
|
||||
int height, ScreenShotDecoderFlag flag) const;
|
||||
|
||||
AlbumFileDateTime ConvertToAlbumDateTime(u64 posix_time) const;
|
||||
|
||||
bool is_mounted{};
|
||||
std::unordered_map<AlbumFileId, std::filesystem::path> album_files;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace Service::Capture
|
||||
|
||||
@@ -41,13 +41,13 @@ enum class ScreenShotDecoderFlag : u64 {
|
||||
|
||||
// This is nn::capsrv::AlbumFileDateTime
|
||||
struct AlbumFileDateTime {
|
||||
u16 year{};
|
||||
u8 month{};
|
||||
u8 day{};
|
||||
u8 hour{};
|
||||
u8 minute{};
|
||||
u8 second{};
|
||||
u8 unique_id{};
|
||||
s16 year{};
|
||||
s8 month{};
|
||||
s8 day{};
|
||||
s8 hour{};
|
||||
s8 minute{};
|
||||
s8 second{};
|
||||
s8 unique_id{};
|
||||
|
||||
friend constexpr bool operator==(const AlbumFileDateTime&, const AlbumFileDateTime&) = default;
|
||||
friend constexpr bool operator>(const AlbumFileDateTime& a, const AlbumFileDateTime& b) {
|
||||
|
||||
@@ -50,22 +50,35 @@ void IAlbumApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) {
|
||||
|
||||
void IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto pid{rp.Pop<s32>()};
|
||||
const auto content_type{rp.PopEnum<ContentType>()};
|
||||
const auto start_posix_time{rp.Pop<s64>()};
|
||||
const auto end_posix_time{rp.Pop<s64>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
struct Parameters {
|
||||
ContentType content_type;
|
||||
INSERT_PADDING_BYTES(7);
|
||||
s64 start_posix_time;
|
||||
s64 end_posix_time;
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
LOG_WARNING(Service_Capture,
|
||||
"(STUBBED) called. pid={}, content_type={}, start_posix_time={}, "
|
||||
"end_posix_time={}, applet_resource_user_id={}",
|
||||
pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id);
|
||||
"(STUBBED) called. content_type={}, start_posix_time={}, end_posix_time={}, "
|
||||
"applet_resource_user_id={}",
|
||||
parameters.content_type, parameters.start_posix_time, parameters.end_posix_time,
|
||||
parameters.applet_resource_user_id);
|
||||
|
||||
// TODO: Translate posix to DateTime
|
||||
Result result = ResultSuccess;
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
result = manager->IsAlbumMounted(AlbumStorage::Sd);
|
||||
}
|
||||
|
||||
std::vector<ApplicationAlbumFileEntry> entries;
|
||||
const Result result =
|
||||
manager->GetAlbumFileList(entries, content_type, {}, {}, applet_resource_user_id);
|
||||
if (result.IsSuccess()) {
|
||||
result = manager->GetAlbumFileList(entries, parameters.content_type,
|
||||
parameters.start_posix_time, parameters.end_posix_time,
|
||||
parameters.applet_resource_user_id);
|
||||
}
|
||||
|
||||
if (!entries.empty()) {
|
||||
ctx.WriteBuffer(entries);
|
||||
@@ -78,19 +91,38 @@ void IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(HLERequestCo
|
||||
|
||||
void IAlbumApplicationService::GetAlbumFileList3AaeAruid(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto pid{rp.Pop<s32>()};
|
||||
const auto content_type{rp.PopEnum<ContentType>()};
|
||||
const auto start_date_time{rp.PopRaw<AlbumFileDateTime>()};
|
||||
const auto end_date_time{rp.PopRaw<AlbumFileDateTime>()};
|
||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||
struct Parameters {
|
||||
ContentType content_type;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
AlbumFileDateTime start_date_time;
|
||||
AlbumFileDateTime end_date_time;
|
||||
INSERT_PADDING_BYTES(6);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
LOG_WARNING(Service_Capture,
|
||||
"(STUBBED) called. pid={}, content_type={}, applet_resource_user_id={}", pid,
|
||||
content_type, applet_resource_user_id);
|
||||
"(STUBBED) called. content_type={}, start_date={}/{}/{}, "
|
||||
"end_date={}/{}/{}, applet_resource_user_id={}",
|
||||
parameters.content_type, parameters.start_date_time.year,
|
||||
parameters.start_date_time.month, parameters.start_date_time.day,
|
||||
parameters.end_date_time.year, parameters.end_date_time.month,
|
||||
parameters.end_date_time.day, parameters.applet_resource_user_id);
|
||||
|
||||
std::vector<ApplicationAlbumFileEntry> entries;
|
||||
const Result result = manager->GetAlbumFileList(entries, content_type, start_date_time,
|
||||
end_date_time, applet_resource_user_id);
|
||||
Result result = ResultSuccess;
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
result = manager->IsAlbumMounted(AlbumStorage::Sd);
|
||||
}
|
||||
|
||||
std::vector<ApplicationAlbumEntry> entries;
|
||||
if (result.IsSuccess()) {
|
||||
result =
|
||||
manager->GetAlbumFileList(entries, parameters.content_type, parameters.start_date_time,
|
||||
parameters.end_date_time, parameters.applet_resource_user_id);
|
||||
}
|
||||
|
||||
if (!entries.empty()) {
|
||||
ctx.WriteBuffer(entries);
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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) {
|
||||
const auto& resolution = runtime->resolution;
|
||||
if (!resolution.active) {
|
||||
return false;
|
||||
}
|
||||
if (False(flags & ImageFlagBits::Rescaled)) {
|
||||
return false;
|
||||
}
|
||||
flags &= ~ImageFlagBits::Rescaled;
|
||||
if (!runtime->resolution.active) {
|
||||
return false;
|
||||
}
|
||||
if (ignore) {
|
||||
current_texture = texture.handle;
|
||||
return true;
|
||||
|
||||
@@ -118,6 +118,8 @@ public:
|
||||
|
||||
void InsertUploadMemoryBarrier();
|
||||
|
||||
void TransitionImageLayout(Image& image) {}
|
||||
|
||||
FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const;
|
||||
|
||||
bool HasNativeBgr() const noexcept {
|
||||
|
||||
@@ -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;
|
||||
@@ -2013,4 +2013,32 @@ void TextureCacheRuntime::AccelerateImageUpload(
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
void TextureCacheRuntime::TransitionImageLayout(Image& image) {
|
||||
if (!image.ExchangeInitialization()) {
|
||||
VkImageMemoryBarrier barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_NONE,
|
||||
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = image.Handle(),
|
||||
.subresourceRange{
|
||||
.aspectMask = image.AspectMask(),
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([barrier = barrier](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, barrier);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -92,6 +92,8 @@ public:
|
||||
|
||||
void InsertUploadMemoryBarrier() {}
|
||||
|
||||
void TransitionImageLayout(Image& image);
|
||||
|
||||
bool HasBrokenTextureViewFormats() const noexcept {
|
||||
// No known Vulkan driver has broken image views
|
||||
return false;
|
||||
|
||||
@@ -1016,6 +1016,7 @@ void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) {
|
||||
|
||||
if (image.info.num_samples > 1 && !runtime.CanUploadMSAA()) {
|
||||
LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented");
|
||||
runtime.TransitionImageLayout(image);
|
||||
return;
|
||||
}
|
||||
if (True(image.flags & ImageFlagBits::AsynchronousDecode)) {
|
||||
|
||||
@@ -885,7 +885,7 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
|
||||
};
|
||||
const Extent3D num_tiles = AdjustTileSize(level_size, tile_size);
|
||||
const Extent3D block =
|
||||
AdjustMipBlockSize(num_tiles, info.block, level, level_info.num_levels);
|
||||
AdjustMipBlockSize(num_tiles, level_info.block, level, level_info.num_levels);
|
||||
const u32 stride_alignment = StrideAlignment(num_tiles, info.block, gob, bpp_log2);
|
||||
size_t guest_layer_offset = 0;
|
||||
|
||||
@@ -1062,7 +1062,7 @@ boost::container::small_vector<SwizzleParameters, 16> FullUploadSwizzles(const I
|
||||
const Extent3D level_size = AdjustMipSize(size, level);
|
||||
const Extent3D num_tiles = AdjustTileSize(level_size, tile_size);
|
||||
const Extent3D block =
|
||||
AdjustMipBlockSize(num_tiles, info.block, level, level_info.num_levels);
|
||||
AdjustMipBlockSize(num_tiles, level_info.block, level, level_info.num_levels);
|
||||
params[level] = SwizzleParameters{
|
||||
.num_tiles = num_tiles,
|
||||
.block = block,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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>
|
||||
|
||||
+52
-27
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user