Files
jpolo1224 1eb63137e3 ARMSX3: Android port of RPCS3, proof of concept
Adds an Android build of the RPCS3 core plus a Compose UI, and fixes several
things that stopped it working on ARM64.

Renderer:
- Emit concrete bounds for runtime sized arrays in uniform blocks when
  VK_EXT_shader_uniform_buffer_unsized_array is missing. Adreno does not have
  the extension, so every game pipeline failed with VK_ERROR_UNKNOWN and only
  overlays drew.
- Probe and request that extension properly instead of chaining its feature
  struct unconditionally.
- Hand VMA the Vulkan function pointers it needs under VK_NO_PROTOTYPES.
- Rebuild the surface and swapchain when the window is lost instead of killing
  the RSX thread.
- Only create a GLES context when the GL renderer is actually selected.

SPU:
- Sum instead of taking an absolute difference in the ARM64 block verification
  checksum. The difference collides on the near identical job binaries an SPU
  job manager streams through one local store address, so a cached block could
  run against another job's code.

Threading:
- Implement thread affinity on Android using sched_setaffinity.
- Add an ARM big.LITTLE core arrangement so SPU and RSX threads land on the
  fast cores.

Misc:
- Detect the host CPU for the LLVM JIT instead of pinning cortex-a34.
- Fall back to the default audio device when cubeb cannot enumerate.
2026-08-06 08:59:52 -04:00

111 lines
2.5 KiB
C++

#pragma once
#include "Utilities/File.h"
#include <cstddef>
#include <utility>
class block_dev
{
std::size_t m_block_size = 0;
std::size_t m_block_count = 0;
public:
virtual ~block_dev() = default;
std::size_t block_size() const
{
return m_block_size;
}
std::size_t block_count() const
{
return m_block_count;
}
std::size_t size() const
{
return block_size() * block_count();
}
virtual std::size_t read(std::size_t blockIndex, void* data,
std::size_t blockCount) = 0;
virtual std::size_t write(std::size_t blockIndex, const void* data,
std::size_t blockCount) = 0;
protected:
void set_block_info(std::size_t size, std::size_t count)
{
m_block_size = size;
m_block_count = count;
}
};
class file_block_dev final : public block_dev
{
fs::file m_file;
public:
explicit file_block_dev(fs::file file, std::size_t blockSize = 2048)
: m_file(std::move(file))
{
set_block_info(blockSize, m_file.size() / blockSize);
}
std::size_t read(std::size_t blockIndex, void* data,
std::size_t blockCount) override
{
auto result = m_file.read_at(block_size() * blockIndex, data,
blockCount * block_size());
return result / block_size();
}
std::size_t write(std::size_t blockIndex, const void* data,
std::size_t blockCount) override
{
// Upstream fs::file has read_at() but no write_at() -- that was an
// RPCSX addition. Seek + write is the equivalent; ISO mounting is
// read-only in practice, so this path is not hot.
m_file.seek(block_size() * blockIndex);
auto result = m_file.write(data, blockCount * block_size());
return result / block_size();
}
fs::file& file()
{
return m_file;
}
fs::file release()
{
return std::exchange(m_file, {});
}
};
class file_view_block_dev final : public block_dev
{
const fs::file* m_file;
public:
explicit file_view_block_dev(const fs::file& file,
std::size_t blockSize = 2048)
: m_file(&file)
{
set_block_info(blockSize, m_file->size() / blockSize);
}
std::size_t read(std::size_t blockIndex, void* data,
std::size_t blockCount) override
{
auto result = m_file->read_at(block_size() * blockIndex, data,
blockCount * block_size());
return result / block_size();
}
std::size_t write(std::size_t blockIndex, const void* data,
std::size_t blockCount) override
{
// See the note in file_block_dev::write -- no fs::file::write_at upstream.
m_file->seek(block_size() * blockIndex);
auto result = m_file->write(data, blockCount * block_size());
return result / block_size();
}
};