mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-07-08 00:02:58 +12:00
Merge branch 'master' into FCRAM_Size_Stuff
This commit is contained in:
commit
5b42834f8e
66 changed files with 6250 additions and 3387 deletions
|
@ -170,6 +170,8 @@ public:
|
|||
return env.totalTicks;
|
||||
}
|
||||
|
||||
void clearCache() { jit->ClearCache(); }
|
||||
|
||||
void runFrame() {
|
||||
env.ticksLeft = ticksPerSec / 60;
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ enum class ROMType {
|
|||
ELF,
|
||||
NCSD,
|
||||
CXI,
|
||||
HB_3DSX,
|
||||
};
|
||||
|
||||
class Emulator {
|
||||
|
@ -99,6 +100,7 @@ class Emulator {
|
|||
|
||||
bool loadROM(const std::filesystem::path& path);
|
||||
bool loadNCSD(const std::filesystem::path& path, ROMType type);
|
||||
bool load3DSX(const std::filesystem::path& path);
|
||||
bool loadELF(const std::filesystem::path& path);
|
||||
bool loadELF(std::ifstream& file);
|
||||
void initGraphicsContext();
|
||||
|
|
|
@ -251,4 +251,11 @@ public:
|
|||
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
|
||||
|
||||
ArchiveBase(Memory& mem) : mem(mem) {}
|
||||
};
|
||||
|
||||
struct ArchiveResource {
|
||||
u32 sectorSize; // Size of a sector in bytes
|
||||
u32 clusterSize; // Size of a cluster in bytes
|
||||
u32 partitionCapacityInClusters;
|
||||
u32 freeSpaceInClusters;
|
||||
};
|
|
@ -9,6 +9,7 @@ public:
|
|||
u64 getFreeBytes() override { Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); return 0; }
|
||||
std::string name() override { return "ExtSaveData::" + backingFolder; }
|
||||
|
||||
HorizonResult createDirectory(const FSPath& path) override;
|
||||
HorizonResult createFile(const FSPath& path, u64 size) override;
|
||||
HorizonResult deleteFile(const FSPath& path) override;
|
||||
|
||||
|
|
|
@ -8,13 +8,16 @@ class SDMCArchive : public ArchiveBase {
|
|||
public:
|
||||
SDMCArchive(Memory& mem) : ArchiveBase(mem) {}
|
||||
|
||||
u64 getFreeBytes() override { Helpers::panic("SDMC::GetFreeBytes unimplemented"); return 0; }
|
||||
u64 getFreeBytes() override { return 1_GB; }
|
||||
std::string name() override { return "SDMC"; }
|
||||
|
||||
HorizonResult createFile(const FSPath& path, u64 size) override;
|
||||
HorizonResult deleteFile(const FSPath& path) override;
|
||||
HorizonResult createDirectory(const FSPath& path) override;
|
||||
|
||||
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
|
||||
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
|
||||
|
||||
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
||||
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
|
||||
};
|
|
@ -18,7 +18,8 @@ public:
|
|||
// Returns whether the cart has a RomFS
|
||||
bool hasRomFS() {
|
||||
auto cxi = mem.getCXI();
|
||||
return (cxi != nullptr && cxi->hasRomFS());
|
||||
auto hb3dsx = mem.get3DSX();
|
||||
return (cxi != nullptr && cxi->hasRomFS()) || (hb3dsx != nullptr && hb3dsx->hasRomFs());
|
||||
}
|
||||
|
||||
// Returns whether the cart has an ExeFS (All executable carts should have an ExeFS. This is just here to be safe)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -137,7 +137,7 @@ namespace Helpers {
|
|||
return getBits<offset, bits, ValueT, ValueT>(value);
|
||||
}
|
||||
|
||||
#if defined(HELPERS_APPLE_CLANG) || defined(__ANDROID__)
|
||||
#if defined(HELPERS_APPLE_CLANG) || defined(__ANDROID__) || !defined(__cpp_lib_bit_cast)
|
||||
template <class To, class From>
|
||||
constexpr To bit_cast(const From& from) noexcept {
|
||||
return *reinterpret_cast<const To*>(&from);
|
||||
|
|
|
@ -83,6 +83,9 @@ private:
|
|||
bool canThreadRun(const Thread& t);
|
||||
bool shouldWaitOnObject(KernelObject* object);
|
||||
void releaseMutex(Mutex* moo);
|
||||
void cancelTimer(Timer* timer);
|
||||
void signalTimer(Handle timerHandle, Timer* timer);
|
||||
void updateTimer(Handle timerHandle, Timer* timer);
|
||||
|
||||
// Wake up the thread with the highest priority out of all threads in the waitlist
|
||||
// Returns the index of the woken up thread
|
||||
|
@ -226,4 +229,5 @@ public:
|
|||
|
||||
void sendGPUInterrupt(GPUInterrupt type) { serviceManager.sendGPUInterrupt(type); }
|
||||
void signalDSPEvents() { serviceManager.signalDSPEvents(); }
|
||||
void clearInstructionCache();
|
||||
};
|
|
@ -173,6 +173,19 @@ struct Semaphore {
|
|||
Semaphore(s32 initialCount, s32 maximumCount) : availableCount(initialCount), maximumCount(maximumCount), waitlist(0) {}
|
||||
};
|
||||
|
||||
struct Timer {
|
||||
u64 waitlist; // Refer to the getWaitlist function below for documentation
|
||||
ResetType resetType = ResetType::OneShot;
|
||||
|
||||
u64 startTick; // CPU tick the timer started
|
||||
u64 currentDelay; // Number of ns until the timer fires next time
|
||||
u64 interval; // Number of ns until the timer fires for the second and future times
|
||||
bool fired; // Has this timer been signalled?
|
||||
bool running; // Is this timer running or stopped?
|
||||
|
||||
Timer(ResetType type) : resetType(type), startTick(0), currentDelay(0), interval(0), waitlist(0), fired(false), running(false) {}
|
||||
};
|
||||
|
||||
struct MemoryBlock {
|
||||
u32 addr = 0;
|
||||
u32 size = 0;
|
||||
|
@ -206,21 +219,23 @@ struct KernelObject {
|
|||
}
|
||||
|
||||
// Retrieves a reference to the waitlist for a specified object
|
||||
// We return a reference because this function is only called in the kernel threading internals
|
||||
// We want the kernel to be able to easily manage waitlists, by reading/parsing them or setting/clearing bits.
|
||||
// As we mention in the definition of the "Event" struct, the format for wailists is very simple and made to be efficient.
|
||||
// Each bit corresponds to a thread index and denotes whether the corresponding thread is waiting on this object
|
||||
// For example if bit 0 of the wait list is set, then the thread with index 0 is waiting on our object
|
||||
u64& getWaitlist() {
|
||||
// This code is actually kinda trash but eh good enough
|
||||
switch (type) {
|
||||
case KernelObjectType::Event: return getData<Event>()->waitlist;
|
||||
case KernelObjectType::Mutex: return getData<Mutex>()->waitlist;
|
||||
case KernelObjectType::Semaphore: return getData<Mutex>()->waitlist;
|
||||
case KernelObjectType::Thread: return getData<Thread>()->threadsWaitingForTermination;
|
||||
// This should be unreachable once we fully implement sync objects
|
||||
default: [[unlikely]]
|
||||
// We return a reference because this function is only called in the kernel threading internals
|
||||
// We want the kernel to be able to easily manage waitlists, by reading/parsing them or setting/clearing bits.
|
||||
// As we mention in the definition of the "Event" struct, the format for wailists is very simple and made to be efficient.
|
||||
// Each bit corresponds to a thread index and denotes whether the corresponding thread is waiting on this object
|
||||
// For example if bit 0 of the wait list is set, then the thread with index 0 is waiting on our object
|
||||
u64& getWaitlist() {
|
||||
// This code is actually kinda trash but eh good enough
|
||||
switch (type) {
|
||||
case KernelObjectType::Event: return getData<Event>()->waitlist;
|
||||
case KernelObjectType::Mutex: return getData<Mutex>()->waitlist;
|
||||
case KernelObjectType::Semaphore: return getData<Mutex>()->waitlist;
|
||||
case KernelObjectType::Thread: return getData<Thread>()->threadsWaitingForTermination;
|
||||
case KernelObjectType::Timer: return getData<Timer>()->waitlist;
|
||||
|
||||
// This should be unreachable once we fully implement sync objects
|
||||
default: [[unlikely]]
|
||||
Helpers::panic("Called GetWaitList on kernel object without a waitlist (Type: %s)", getTypeName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
80
include/loader/3dsx.hpp
Normal file
80
include/loader/3dsx.hpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include "helpers.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "loader/ncch.hpp"
|
||||
|
||||
struct HB3DSX {
|
||||
// File layout:
|
||||
// - File header
|
||||
// - Code, rodata and data relocation table headers
|
||||
// - Code segment
|
||||
// - Rodata segment
|
||||
// - Loadable (non-BSS) part of the data segment
|
||||
// - Code relocation table
|
||||
// - Rodata relocation table
|
||||
// - Data relocation table
|
||||
|
||||
// Memory layout before relocations are applied:
|
||||
// [0..codeSegSize) -> code segment
|
||||
// [codeSegSize..rodataSegSize) -> rodata segment
|
||||
// [rodataSegSize..dataSegSize) -> data segment
|
||||
|
||||
// Memory layout after relocations are applied: well, however the loader sets it up :)
|
||||
// The entrypoint is always the start of the code segment.
|
||||
// The BSS section must be cleared manually by the application.
|
||||
|
||||
// File header
|
||||
struct Header {
|
||||
// minus char magic[4]
|
||||
u16 headerSize;
|
||||
u16 relocHeaderSize;
|
||||
u32 formatVer;
|
||||
u32 flags;
|
||||
|
||||
// Sizes of the code, rodata and data segments +
|
||||
// size of the BSS section (uninitialized latter half of the data segment)
|
||||
u32 codeSegSize, rodataSegSize, dataSegSize, bssSize;
|
||||
};
|
||||
|
||||
// Relocation header: all fields (even extra unknown fields) are guaranteed to be relocation counts.
|
||||
struct RelocHeader {
|
||||
u32 absoluteCount; // # of absolute relocations (that is, fix address to post-relocation memory layout)
|
||||
u32 relativeCount; // # of cross-segment relative relocations (that is, 32bit signed offsets that need to be patched)
|
||||
// more?
|
||||
|
||||
// Relocations are written in this order:
|
||||
// - Absolute relocs
|
||||
// - Relative relocs
|
||||
};
|
||||
|
||||
enum class RelocType {
|
||||
Absolute,
|
||||
Relative,
|
||||
};
|
||||
|
||||
// Relocation entry: from the current pointer, skip X words and patch Y words
|
||||
struct Reloc {
|
||||
u16 skip, patch;
|
||||
};
|
||||
|
||||
// _prm structure
|
||||
static constexpr std::array<char, 4> PRM_MAGIC = {'_', 'P', 'R', 'M'};
|
||||
struct PrmStruct {
|
||||
char magic[4];
|
||||
u32 pSrvOverride;
|
||||
u32 aptAppId;
|
||||
u32 heapSize, linearHeapSize;
|
||||
u32 pArgList;
|
||||
u32 runFlags;
|
||||
};
|
||||
|
||||
IOFile file;
|
||||
|
||||
static constexpr u32 entrypoint = 0x00100000; // Initial ARM11 PC
|
||||
u32 romFSSize = 0;
|
||||
u32 romFSOffset = 0;
|
||||
|
||||
bool hasRomFs() const;
|
||||
std::pair<bool, std::size_t> readRomFSBytes(void *dst, std::size_t offset, std::size_t size);
|
||||
};
|
|
@ -11,6 +11,7 @@
|
|||
#include "handles.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "loader/ncsd.hpp"
|
||||
#include "loader/3dsx.hpp"
|
||||
#include "services/region_codes.hpp"
|
||||
|
||||
namespace PhysicalAddrs {
|
||||
|
@ -172,10 +173,12 @@ public:
|
|||
void* getReadPointer(u32 address);
|
||||
void* getWritePointer(u32 address);
|
||||
std::optional<u32> loadELF(std::ifstream& file);
|
||||
std::optional<u32> load3DSX(const std::filesystem::path& path);
|
||||
std::optional<NCSD> loadNCSD(Crypto::AESEngine& aesEngine, const std::filesystem::path& path);
|
||||
std::optional<NCSD> loadCXI(Crypto::AESEngine& aesEngine, const std::filesystem::path& path);
|
||||
|
||||
bool mapCXI(NCSD& ncsd, NCCH& cxi);
|
||||
bool map3DSX(HB3DSX& hb3dsx, const HB3DSX::Header& header);
|
||||
|
||||
u8 read8(u32 vaddr);
|
||||
u16 read16(u32 vaddr);
|
||||
|
@ -240,6 +243,14 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
HB3DSX* get3DSX() {
|
||||
if (loaded3DSX.has_value()) {
|
||||
return &loaded3DSX.value();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns whether "addr" is aligned to a page (4096 byte) boundary
|
||||
static constexpr bool isAligned(u32 addr) {
|
||||
return (addr & pageMask) == 0;
|
||||
|
@ -275,6 +286,7 @@ public:
|
|||
|
||||
// Backup of the game's CXI partition info, if any
|
||||
std::optional<NCCH> loadedCXI = std::nullopt;
|
||||
std::optional<HB3DSX> loaded3DSX = std::nullopt;
|
||||
// File handle for reading the loaded ncch
|
||||
IOFile CXIFile;
|
||||
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
#include "math_util.hpp"
|
||||
#include "renderer.hpp"
|
||||
#include "vulkan_api.hpp"
|
||||
#include "vk_api.hpp"
|
||||
#include "vk_descriptor_heap.hpp"
|
||||
#include "vk_descriptor_update_batch.hpp"
|
||||
#include "vk_sampler_cache.hpp"
|
||||
|
||||
class GPU;
|
||||
|
||||
|
@ -10,7 +17,7 @@ class RendererVK final : public Renderer {
|
|||
vk::UniqueInstance instance = {};
|
||||
vk::UniqueDebugUtilsMessengerEXT debugMessenger = {};
|
||||
|
||||
vk::UniqueSurfaceKHR surface = {};
|
||||
vk::SurfaceKHR swapchainSurface = {};
|
||||
|
||||
vk::PhysicalDevice physicalDevice = {};
|
||||
|
||||
|
@ -32,17 +39,74 @@ class RendererVK final : public Renderer {
|
|||
std::vector<vk::Image> swapchainImages = {};
|
||||
std::vector<vk::UniqueImageView> swapchainImageViews = {};
|
||||
|
||||
// Per-swapchain-image data
|
||||
// Each vector is `swapchainImageCount` in size
|
||||
std::vector<vk::UniqueCommandBuffer> presentCommandBuffers = {};
|
||||
// This value is the degree of parallelism to allow multiple frames to be in-flight
|
||||
// aka: "double-buffer"/"triple-buffering"
|
||||
// Todo: make this a configuration option
|
||||
static constexpr usize frameBufferingCount = 3;
|
||||
|
||||
// Frame-buffering data
|
||||
// Each vector is `frameBufferingCount` in size
|
||||
std::vector<vk::UniqueSemaphore> swapImageFreeSemaphore = {};
|
||||
std::vector<vk::UniqueSemaphore> renderFinishedSemaphore = {};
|
||||
std::vector<vk::UniqueFence> frameFinishedFences = {};
|
||||
std::vector<std::vector<vk::UniqueFramebuffer>> frameFramebuffers = {};
|
||||
std::vector<vk::UniqueCommandBuffer> frameCommandBuffers = {};
|
||||
|
||||
const vk::CommandBuffer& getCurrentCommandBuffer() const { return frameCommandBuffers[frameBufferingIndex].get(); }
|
||||
|
||||
// Todo:
|
||||
// Use `{colourBuffer,depthBuffer}Loc` to maintain an std::map-cache of framebuffers
|
||||
struct Texture {
|
||||
u32 loc = 0;
|
||||
u32 sizePerPixel = 0;
|
||||
std::array<u32, 2> size = {};
|
||||
|
||||
vk::Format format;
|
||||
vk::UniqueImage image;
|
||||
vk::UniqueDeviceMemory imageMemory;
|
||||
vk::UniqueImageView imageView;
|
||||
|
||||
Math::Rect<u32> getSubRect(u32 inputAddress, u32 width, u32 height) {
|
||||
// PICA textures have top-left origin, same as Vulkan
|
||||
const u32 startOffset = (inputAddress - loc) / sizePerPixel;
|
||||
const u32 x0 = (startOffset % (size[0] * 8)) / 8;
|
||||
const u32 y0 = (startOffset / (size[0] * 8)) * 8;
|
||||
return Math::Rect<u32>{x0, y0, x0 + width, y0 + height};
|
||||
}
|
||||
};
|
||||
// Hash(loc, size, format) -> Texture
|
||||
std::map<u64, Texture> textureCache;
|
||||
|
||||
Texture* findRenderTexture(u32 addr);
|
||||
Texture& getColorRenderTexture(u32 addr, PICA::ColorFmt format, u32 width, u32 height);
|
||||
Texture& getDepthRenderTexture(u32 addr, PICA::DepthFmt format, u32 width, u32 height);
|
||||
|
||||
// Framebuffer for the top/bottom image
|
||||
std::vector<vk::UniqueImage> screenTexture = {};
|
||||
std::vector<vk::UniqueImageView> screenTextureViews = {};
|
||||
std::vector<vk::UniqueFramebuffer> screenTextureFramebuffers = {};
|
||||
vk::UniqueDeviceMemory framebufferMemory = {};
|
||||
|
||||
std::map<u64, vk::UniqueRenderPass> renderPassCache;
|
||||
|
||||
vk::RenderPass getRenderPass(vk::Format colorFormat, std::optional<vk::Format> depthFormat);
|
||||
vk::RenderPass getRenderPass(PICA::ColorFmt colorFormat, std::optional<PICA::DepthFmt> depthFormat);
|
||||
|
||||
std::unique_ptr<Vulkan::DescriptorUpdateBatch> descriptorUpdateBatch;
|
||||
std::unique_ptr<Vulkan::SamplerCache> samplerCache;
|
||||
|
||||
// Display pipeline data
|
||||
std::unique_ptr<Vulkan::DescriptorHeap> displayDescriptorHeap;
|
||||
vk::UniquePipeline displayPipeline;
|
||||
vk::UniquePipelineLayout displayPipelineLayout;
|
||||
std::vector<vk::DescriptorSet> topDisplayPipelineDescriptorSet;
|
||||
std::vector<vk::DescriptorSet> bottomDisplayPipelineDescriptorSet;
|
||||
|
||||
// Recreate the swapchain, possibly re-using the old one in the case of a resize
|
||||
vk::Result recreateSwapchain(vk::SurfaceKHR surface, vk::Extent2D swapchainExtent);
|
||||
|
||||
u64 currentFrame = 0;
|
||||
u64 frameBufferingIndex = 0;
|
||||
|
||||
public:
|
||||
RendererVK(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
|
||||
~RendererVK() override;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "vulkan_api.hpp"
|
||||
#include "vk_api.hpp"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
|
|
49
include/renderer_vk/vk_descriptor_heap.hpp
Normal file
49
include/renderer_vk/vk_descriptor_heap.hpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <span>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "vk_api.hpp"
|
||||
|
||||
namespace Vulkan {
|
||||
// Implements a basic heap of descriptor sets given a layout of particular
|
||||
// bindings. Create a descriptor set by providing a list of bindings and it will
|
||||
// automatically create both the pool, layout, and maintail a heap of descriptor
|
||||
// sets. Descriptor sets will be reused and recycled. Assume that newly
|
||||
// allocated descriptor sets are in an undefined state.
|
||||
class DescriptorHeap {
|
||||
private:
|
||||
const vk::Device device;
|
||||
|
||||
vk::UniqueDescriptorPool descriptorPool;
|
||||
vk::UniqueDescriptorSetLayout descriptorSetLayout;
|
||||
std::vector<vk::UniqueDescriptorSet> descriptorSets;
|
||||
|
||||
std::vector<vk::DescriptorSetLayoutBinding> bindings;
|
||||
|
||||
std::vector<bool> allocationMap;
|
||||
|
||||
explicit DescriptorHeap(vk::Device device);
|
||||
|
||||
public:
|
||||
~DescriptorHeap() = default;
|
||||
|
||||
DescriptorHeap(DescriptorHeap&&) = default;
|
||||
|
||||
const vk::DescriptorPool& getDescriptorPool() const { return descriptorPool.get(); };
|
||||
|
||||
const vk::DescriptorSetLayout& getDescriptorSetLayout() const { return descriptorSetLayout.get(); };
|
||||
|
||||
const std::span<const vk::UniqueDescriptorSet> getDescriptorSets() const { return descriptorSets; };
|
||||
|
||||
std::span<const vk::DescriptorSetLayoutBinding> getBindings() const { return bindings; };
|
||||
|
||||
std::optional<vk::DescriptorSet> allocateDescriptorSet();
|
||||
bool freeDescriptorSet(vk::DescriptorSet set);
|
||||
|
||||
static std::optional<DescriptorHeap> create(
|
||||
vk::Device device, std::span<const vk::DescriptorSetLayoutBinding> bindings, u16 descriptorHeapCount = 1024
|
||||
);
|
||||
};
|
||||
} // namespace Vulkan
|
62
include/renderer_vk/vk_descriptor_update_batch.hpp
Normal file
62
include/renderer_vk/vk_descriptor_update_batch.hpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "vk_api.hpp"
|
||||
|
||||
namespace Vulkan {
|
||||
// Implements a re-usable structure for batching up descriptor writes with a
|
||||
// finite amount of space for both convenience and to reduce the overall amount
|
||||
// of API calls to `vkUpdateDescriptorSets`
|
||||
class DescriptorUpdateBatch {
|
||||
private:
|
||||
const vk::Device device;
|
||||
|
||||
const usize descriptorWriteMax;
|
||||
const usize descriptorCopyMax;
|
||||
|
||||
using DescriptorInfoUnion = std::variant<vk::DescriptorImageInfo, vk::DescriptorBufferInfo, vk::BufferView>;
|
||||
|
||||
// Todo: Maybe some kind of hash so that these structures can be re-used
|
||||
// among descriptor writes.
|
||||
std::unique_ptr<DescriptorInfoUnion[]> descriptorInfos;
|
||||
std::unique_ptr<vk::WriteDescriptorSet[]> descriptorWrites;
|
||||
std::unique_ptr<vk::CopyDescriptorSet[]> descriptorCopies;
|
||||
|
||||
usize descriptorWriteEnd = 0;
|
||||
usize descriptorCopyEnd = 0;
|
||||
|
||||
DescriptorUpdateBatch(vk::Device device, usize descriptorWriteMax, usize descriptorCopyMax)
|
||||
: device(device), descriptorWriteMax(descriptorWriteMax), descriptorCopyMax(descriptorCopyMax) {}
|
||||
|
||||
public:
|
||||
~DescriptorUpdateBatch() = default;
|
||||
|
||||
DescriptorUpdateBatch(DescriptorUpdateBatch&&) = default;
|
||||
|
||||
void flush();
|
||||
|
||||
void addImage(
|
||||
vk::DescriptorSet targetDescriptor, u8 targetBinding, vk::ImageView imageView, vk::ImageLayout imageLayout = vk::ImageLayout::eGeneral
|
||||
);
|
||||
void addSampler(vk::DescriptorSet targetDescriptor, u8 targetBinding, vk::Sampler sampler);
|
||||
|
||||
void addImageSampler(
|
||||
vk::DescriptorSet targetDescriptor, u8 targetBinding, vk::ImageView imageView, vk::Sampler sampler,
|
||||
vk::ImageLayout imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal
|
||||
);
|
||||
void addBuffer(
|
||||
vk::DescriptorSet targetDescriptor, u8 targetBinding, vk::Buffer buffer, vk::DeviceSize offset, vk::DeviceSize size = VK_WHOLE_SIZE
|
||||
);
|
||||
|
||||
void copyBinding(
|
||||
vk::DescriptorSet sourceDescriptor, vk::DescriptorSet targetDescriptor, u8 sourceBinding, u8 targetBinding, u8 sourceArrayElement = 0,
|
||||
u8 targetArrayElement = 0, u8 descriptorCount = 1
|
||||
);
|
||||
|
||||
static std::optional<DescriptorUpdateBatch> create(vk::Device device, usize descriptorWriteMax = 256, usize descriptorCopyMax = 256);
|
||||
};
|
||||
} // namespace Vulkan
|
36
include/renderer_vk/vk_memory.hpp
Normal file
36
include/renderer_vk/vk_memory.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "vk_api.hpp"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
// Will try to find a memory type that is suitable for the given requirements.
|
||||
// Returns -1 if no suitable memory type was found.
|
||||
s32 findMemoryTypeIndex(
|
||||
vk::PhysicalDevice physicalDevice, u32 memoryTypeMask, vk::MemoryPropertyFlags memoryProperties,
|
||||
vk::MemoryPropertyFlags memoryExcludeProperties = vk::MemoryPropertyFlagBits::eProtected
|
||||
);
|
||||
|
||||
// Given an array of valid Vulkan image-handles or buffer-handles, these
|
||||
// functions will allocate a single block of device-memory for all of them
|
||||
// and bind them consecutively.
|
||||
// There may be a case that all the buffers or images cannot be allocated
|
||||
// to the same device memory due to their required memory-type.
|
||||
std::tuple<vk::Result, vk::UniqueDeviceMemory> commitImageHeap(
|
||||
vk::Device device, vk::PhysicalDevice physicalDevice, const std::span<const vk::Image> images,
|
||||
vk::MemoryPropertyFlags memoryProperties = vk::MemoryPropertyFlagBits::eDeviceLocal,
|
||||
vk::MemoryPropertyFlags memoryExcludeProperties = vk::MemoryPropertyFlagBits::eProtected
|
||||
);
|
||||
|
||||
std::tuple<vk::Result, vk::UniqueDeviceMemory> commitBufferHeap(
|
||||
vk::Device device, vk::PhysicalDevice physicalDevice, const std::span<const vk::Buffer> buffers,
|
||||
vk::MemoryPropertyFlags memoryProperties = vk::MemoryPropertyFlagBits::eDeviceLocal,
|
||||
vk::MemoryPropertyFlags memoryExcludeProperties = vk::MemoryPropertyFlagBits::eProtected
|
||||
);
|
||||
|
||||
} // namespace Vulkan
|
12
include/renderer_vk/vk_pica.hpp
Normal file
12
include/renderer_vk/vk_pica.hpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "PICA/gpu.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "vk_api.hpp"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
vk::Format colorFormatToVulkan(PICA::ColorFmt colorFormat);
|
||||
vk::Format depthFormatToVulkan(PICA::DepthFmt depthFormat);
|
||||
|
||||
} // namespace Vulkan
|
28
include/renderer_vk/vk_sampler_cache.hpp
Normal file
28
include/renderer_vk/vk_sampler_cache.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "vk_api.hpp"
|
||||
|
||||
namespace Vulkan {
|
||||
// Implements a simple pool of reusable sampler objects
|
||||
class SamplerCache {
|
||||
private:
|
||||
const vk::Device device;
|
||||
|
||||
std::unordered_map<std::size_t, vk::UniqueSampler> samplerMap;
|
||||
|
||||
explicit SamplerCache(vk::Device device);
|
||||
|
||||
public:
|
||||
~SamplerCache() = default;
|
||||
|
||||
SamplerCache(SamplerCache&&) = default;
|
||||
|
||||
const vk::Sampler& getSampler(const vk::SamplerCreateInfo& samplerInfo);
|
||||
|
||||
static std::optional<SamplerCache> create(vk::Device device);
|
||||
};
|
||||
} // namespace Vulkan
|
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
#include "config.hpp"
|
||||
#include "fs/archive_ext_save_data.hpp"
|
||||
#include "fs/archive_ncch.hpp"
|
||||
#include "fs/archive_save_data.hpp"
|
||||
|
@ -9,7 +10,6 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
// Yay, more circular dependencies
|
||||
class Kernel;
|
||||
|
@ -40,7 +40,10 @@ class FSService {
|
|||
std::optional<Handle> openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms);
|
||||
FSPath readPath(u32 type, u32 pointer, u32 size);
|
||||
|
||||
const EmulatorConfig& config;
|
||||
|
||||
// Service commands
|
||||
void abnegateAccessRight(u32 messagePointer);
|
||||
void createDirectory(u32 messagePointer);
|
||||
void createExtSaveData(u32 messagePointer);
|
||||
void createFile(u32 messagePointer);
|
||||
|
@ -50,9 +53,12 @@ class FSService {
|
|||
void deleteFile(u32 messagePointer);
|
||||
void formatSaveData(u32 messagePointer);
|
||||
void formatThisUserSaveData(u32 messagePointer);
|
||||
void getArchiveResource(u32 messagePointer);
|
||||
void getFreeBytes(u32 messagePointer);
|
||||
void getFormatInfo(u32 messagePointer);
|
||||
void getPriority(u32 messagePointer);
|
||||
void getThisSaveDataSecureValue(u32 messagePointer);
|
||||
void theGameboyVCFunction(u32 messagePointer);
|
||||
void initialize(u32 messagePointer);
|
||||
void initializeWithSdkVersion(u32 messagePointer);
|
||||
void isSdmcDetected(u32 messagePointer);
|
||||
|
@ -61,15 +67,17 @@ class FSService {
|
|||
void openDirectory(u32 messagePointer);
|
||||
void openFile(u32 messagePointer);
|
||||
void openFileDirectly(u32 messagePointer);
|
||||
void setArchivePriority(u32 messagePointer);
|
||||
void setPriority(u32 messagePointer);
|
||||
void setThisSaveDataSecureValue(u32 messagePointer);
|
||||
|
||||
// Used for set/get priority: Not sure what sort of priority this is referring to
|
||||
u32 priority;
|
||||
|
||||
public:
|
||||
FSService(Memory& mem, Kernel& kernel)
|
||||
FSService(Memory& mem, Kernel& kernel, const EmulatorConfig& config)
|
||||
: mem(mem), saveData(mem), sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"), sdmc(mem), selfNcch(mem),
|
||||
ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1), userSaveData2(mem, ArchiveID::UserSaveData2), kernel(kernel) {}
|
||||
ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1), userSaveData2(mem, ArchiveID::UserSaveData2), kernel(kernel), config(config) {}
|
||||
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
|
|
|
@ -12,7 +12,9 @@ class HTTPService {
|
|||
bool initialized = false;
|
||||
|
||||
// Service commands
|
||||
void createRootCertChain(u32 messagePointer);
|
||||
void initialize(u32 messagePointer);
|
||||
void rootCertChainAddDefaultCert(u32 messagePointer);
|
||||
|
||||
public:
|
||||
HTTPService(Memory& mem) : mem(mem) {}
|
||||
|
|
|
@ -17,6 +17,7 @@ class MICService {
|
|||
// Service commands
|
||||
void getEventHandle(u32 messagePointer);
|
||||
void getGain(u32 messagePointer);
|
||||
void getPower(u32 messagePointer);
|
||||
void isSampling(u32 messagePointer);
|
||||
void mapSharedMem(u32 messagePointer);
|
||||
void setClamp(u32 messagePointer);
|
||||
|
|
|
@ -44,7 +44,9 @@ class NFCService {
|
|||
void getTagInRangeEvent(u32 messagePointer);
|
||||
void getTagOutOfRangeEvent(u32 messagePointer);
|
||||
void getTagState(u32 messagePointer);
|
||||
void shutdown(u32 messagePointer);
|
||||
void startCommunication(u32 messagePointer);
|
||||
void startTagScanning(u32 messagePointer);
|
||||
void stopCommunication(u32 messagePointer);
|
||||
|
||||
public:
|
||||
|
|
|
@ -16,6 +16,7 @@ class PTMService {
|
|||
// Service commands
|
||||
void configureNew3DSCPU(u32 messagePointer);
|
||||
void getAdapterState(u32 messagePointer);
|
||||
void getBatteryChargeState(u32 messagePointer);
|
||||
void getBatteryLevel(u32 messagePointer);
|
||||
void getStepHistory(u32 messagePointer);
|
||||
void getTotalStepCount(u32 messagePointer);
|
||||
|
|
|
@ -84,6 +84,7 @@ class ServiceManager {
|
|||
void receiveNotification(u32 messagePointer);
|
||||
void registerClient(u32 messagePointer);
|
||||
void subscribe(u32 messagePointer);
|
||||
void unsubscribe(u32 messagePointer);
|
||||
|
||||
public:
|
||||
ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config);
|
||||
|
|
|
@ -64,10 +64,14 @@ class Y2RService {
|
|||
// Service commands
|
||||
void driverInitialize(u32 messagePointer);
|
||||
void driverFinalize(u32 messagePointer);
|
||||
void getTransferEndEvent(u32 messagePointer);
|
||||
void getBlockAlignment(u32 messagePointer);
|
||||
void getInputLines(u32 messagePointer);
|
||||
void getInputLineWidth(u32 messagePointer);
|
||||
void getOutputFormat(u32 messagePointer);
|
||||
void isBusyConversion(u32 messagePointer);
|
||||
void pingProcess(u32 messagePointer);
|
||||
void setTransferEndInterrupt(u32 messagePointer);
|
||||
void getTransferEndEvent(u32 messagePointer);
|
||||
|
||||
void setAlpha(u32 messagePointer);
|
||||
void setBlockAlignment(u32 messagePointer);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue