mirror of
https://github.com/libretro/parallel-gx1.git
synced 2026-11-07 04:26:01 -10:00
CPU: the state machine drops its two mutexes and two condition variables. The state is one atomic word, which the JIT still reads in place; PowerDown sticks through a CAS. The CPU thread marks itself active and then rechecks for a pause or a state change, and a pausing thread stores its request and then waits for the flag to drop, a Dekker pair, so neither misses the other. Waits park on eventcounts. A step request and its Event travel as one exchanged pointer, pending jobs are an MPSC stack run in order, and SetStepping and PauseAndLock still run one at a time, through a claimed flag. Core: the recursive core mutex goes. Stop moves the state with a CAS, and PauseAndLock pairs are already serialized by the CPU's claim. Host jobs are an MPSC stack the host thread moves, in order, into its own queue, which keeps HostDispatchJobs re-entrant. RunOnCPUThread waits on a Common::Event instead of a binary semaphore, and OneShotEvent is removed. State: the transferable shared mutex that counted save tasks is a counter and an eventcount; the slot readers and the flush hook wait for it to reach zero. TransferableSharedMutex is removed. WaitableFlag parks on an eventcount. PixelEngine: the token, its interrupt flags and the raised-event flag are one atomic word; the video thread merges into it and schedules the event only on the edge, the CPU thread takes the flags with a CAS. Savestates keep the old fields. VertexLoaderManager: the loader map is a fixed set of buckets, each a list pushed with a CAS and read without a lock by the main and the preprocessing thread; two threads making the same loader keep one. AsyncShaderCompiler: the pending queue is the video thread's own. It hands the most urgent items to two slots per worker, and finished items come back on an MPSC stack, so neither side takes a lock. OSD messages are posted to an MPSC stack and taken in by the video thread when it draws; a screenshot request is one exchanged string. Also removed, being unreachable: the surface change and resize requests nothing sends (and the swap mutex guarding them), the ImGui keyboard and mouse input nothing feeds (and the ImGui mutex, as only the video thread touches the context now), ClearMessages, and the libretro Vulkan WaitForPresentation with its mutex and condition variable.
224 lines
5.2 KiB
C++
224 lines
5.2 KiB
C++
// Copyright 2019 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <mbedtls/md5.h>
|
|
|
|
#include <rthreads/rthreads.h>
|
|
|
|
#include "Common/CommonTypes.h"
|
|
#include "Common/Crypto/SHA1.h"
|
|
#include "Common/WorkQueueThread.h"
|
|
#include "Core/IOS/ES/Formats.h"
|
|
#include "DiscIO/DiscScrubber.h"
|
|
#include "DiscIO/Volume.h"
|
|
|
|
// To be used as follows:
|
|
//
|
|
// VolumeVerifier verifier(volume, redump_verification, hashes_to_calculate);
|
|
// verifier.Start();
|
|
// while (verifier.GetBytesProcessed() != verifier.GetTotalBytes())
|
|
// verifier.Process();
|
|
// verifier.Finish();
|
|
// auto result = verifier.GetResult();
|
|
//
|
|
// Start, Process and Finish may take some time to run.
|
|
//
|
|
// GetResult() can be called before the processing is finished, but the result will be incomplete.
|
|
|
|
namespace DiscIO
|
|
{
|
|
template <typename T>
|
|
struct Hashes
|
|
{
|
|
T crc32;
|
|
T md5;
|
|
T sha1;
|
|
};
|
|
|
|
class RedumpVerifier final
|
|
{
|
|
public:
|
|
enum class Status
|
|
{
|
|
Unknown,
|
|
GoodDump,
|
|
BadDump,
|
|
Error,
|
|
};
|
|
|
|
struct Result
|
|
{
|
|
Status status = Status::Unknown;
|
|
std::string message;
|
|
};
|
|
|
|
RedumpVerifier() = default;
|
|
~RedumpVerifier();
|
|
RedumpVerifier(const RedumpVerifier&) = delete;
|
|
RedumpVerifier& operator=(const RedumpVerifier&) = delete;
|
|
|
|
void Start(const Volume& volume);
|
|
Result Finish(const Hashes<std::vector<u8>>& hashes);
|
|
|
|
private:
|
|
enum class DownloadStatus
|
|
{
|
|
NotAttempted,
|
|
Success,
|
|
Fail,
|
|
FailButOldCacheAvailable,
|
|
};
|
|
|
|
struct DownloadState
|
|
{
|
|
std::mutex mutex;
|
|
DownloadStatus status = DownloadStatus::NotAttempted;
|
|
};
|
|
|
|
struct PotentialMatch
|
|
{
|
|
u64 size = 0;
|
|
Hashes<std::vector<u8>> hashes;
|
|
};
|
|
|
|
static DownloadStatus DownloadDatfile(const std::string& system, DownloadStatus old_status);
|
|
static std::vector<u8> ReadDatfile(const std::string& system);
|
|
std::vector<PotentialMatch> ScanDatfile(std::span<const u8> data, const std::string& system);
|
|
|
|
std::string m_game_id;
|
|
u16 m_revision = 0;
|
|
u8 m_disc_number = 0;
|
|
u64 m_size = 0;
|
|
|
|
void MatchThread();
|
|
std::vector<PotentialMatch> FindPotentialMatches(Platform platform);
|
|
|
|
// Looks the disc up in the Redump datfile while the disc is hashed.
|
|
sthread_t* m_thread = nullptr;
|
|
Platform m_platform{};
|
|
std::vector<PotentialMatch> m_potential_matches;
|
|
Result m_result;
|
|
|
|
static DownloadState m_gc_download_state;
|
|
static DownloadState m_wii_download_state;
|
|
};
|
|
|
|
class VolumeVerifier final
|
|
{
|
|
public:
|
|
enum class Severity
|
|
{
|
|
None, // Only used internally
|
|
Low,
|
|
Medium,
|
|
High,
|
|
};
|
|
|
|
struct Problem
|
|
{
|
|
Severity severity;
|
|
std::string text;
|
|
};
|
|
|
|
struct Result
|
|
{
|
|
Hashes<std::vector<u8>> hashes;
|
|
std::string summary_text;
|
|
std::vector<Problem> problems;
|
|
RedumpVerifier::Result redump;
|
|
};
|
|
|
|
VolumeVerifier(const Volume& volume, bool redump_verification, Hashes<bool> hashes_to_calculate);
|
|
~VolumeVerifier();
|
|
|
|
static Hashes<bool> GetDefaultHashesToCalculate();
|
|
void Start();
|
|
void Process();
|
|
u64 GetBytesProcessed() const;
|
|
u64 GetTotalBytes() const;
|
|
void Finish();
|
|
const Result& GetResult() const;
|
|
|
|
private:
|
|
struct GroupToVerify
|
|
{
|
|
Partition partition;
|
|
u64 offset;
|
|
size_t block_index_start;
|
|
size_t block_index_end;
|
|
};
|
|
|
|
std::vector<Partition> CheckPartitions();
|
|
bool CheckPartition(const Partition& partition); // Returns false if partition should be ignored
|
|
std::string GetPartitionName(std::optional<u32> type) const;
|
|
bool IsDebugSigned() const;
|
|
bool ShouldHaveChannelPartition() const;
|
|
bool ShouldHaveInstallPartition() const;
|
|
bool ShouldHaveMasterpiecePartitions() const;
|
|
bool ShouldBeDualLayer() const;
|
|
void CheckVolumeSize();
|
|
void CheckMisc();
|
|
void CheckSuperPaperMario();
|
|
void SetUpHashing();
|
|
void WaitForAsyncOperations();
|
|
static void RunAsyncTask(void* ctx, size_t index);
|
|
bool ReadChunkAndWaitForAsyncOperations(u64 bytes_to_read);
|
|
|
|
void AddProblem(Severity severity, std::string text);
|
|
|
|
const Volume& m_volume;
|
|
Result m_result;
|
|
bool m_is_tgc = false;
|
|
bool m_is_datel = false;
|
|
bool m_is_triforce = false;
|
|
bool m_is_not_retail = false;
|
|
|
|
bool m_redump_verification;
|
|
RedumpVerifier m_redump_verifier;
|
|
|
|
bool m_read_errors_occurred = false;
|
|
|
|
Hashes<bool> m_hashes_to_calculate{};
|
|
bool m_calculating_any_hash = false;
|
|
u32 m_crc32_context = 0;
|
|
mbedtls_md5_context m_md5_context{};
|
|
std::unique_ptr<Common::SHA1::Context> m_sha1_context;
|
|
|
|
u64 m_excess_bytes = 0;
|
|
std::vector<u8> m_data;
|
|
std::vector<std::function<void()>> m_async_tasks;
|
|
|
|
DiscScrubber m_scrubber;
|
|
IOS::ES::TicketReader m_ticket;
|
|
std::vector<u64> m_content_offsets;
|
|
u16 m_content_index = 0;
|
|
std::vector<GroupToVerify> m_groups;
|
|
size_t m_group_index = 0; // Index in m_groups, not index in a specific partition
|
|
std::map<Partition, size_t> m_block_errors;
|
|
std::map<Partition, size_t> m_unused_block_errors;
|
|
|
|
u64 m_biggest_referenced_offset = 0;
|
|
u64 m_biggest_verified_offset = 0;
|
|
|
|
bool m_started = false;
|
|
bool m_done = false;
|
|
u64 m_progress = 0;
|
|
u64 m_max_progress = 0;
|
|
DataSizeType m_data_size_type;
|
|
|
|
// Declared last so it stops before anything its tasks touch is destroyed.
|
|
Common::AsyncWorkThreadSP m_async_worker;
|
|
};
|
|
|
|
} // namespace DiscIO
|