Merge pull request #200 from Subv/bufferproducerfence

Make the fence handling in Vi a little less of a hack.
This commit is contained in:
bunnei 2018-02-18 14:11:04 -05:00 committed by GitHub
commit 5babad5de5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 68 additions and 28 deletions

View file

@ -17,6 +17,8 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<
switch (static_cast<IoctlCommand>(command.raw)) { switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocGetConfigCommand: case IoctlCommand::IocGetConfigCommand:
return NvOsGetConfigU32(input, output); return NvOsGetConfigU32(input, output);
case IoctlCommand::IocCtrlEventWaitCommand:
return IocCtrlEventWait(input, output);
} }
UNIMPLEMENTED(); UNIMPLEMENTED();
return 0; return 0;
@ -45,6 +47,18 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>&
return 0; return 0;
} }
u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
IocCtrlEventWaitParams params{};
std::memcpy(&params, input.data(), sizeof(params));
LOG_WARNING(Service_NVDRV, "(STUBBED) called, syncpt_id=%u threshold=%u timeout=%d",
params.syncpt_id, params.threshold, params.timeout);
// TODO(Subv): Implement actual syncpt waiting.
params.value = 0;
std::memcpy(output.data(), &params, sizeof(params));
return 0;
}
} // namespace Devices } // namespace Devices
} // namespace Nvidia } // namespace Nvidia
} // namespace Service } // namespace Service

View file

@ -31,6 +31,7 @@ private:
IocModuleRegRDWRCommand = 0xC008010E, IocModuleRegRDWRCommand = 0xC008010E,
IocSyncptWaitexCommand = 0xC0100019, IocSyncptWaitexCommand = 0xC0100019,
IocSyncptReadMaxCommand = 0xC008001A, IocSyncptReadMaxCommand = 0xC008001A,
IocCtrlEventWaitCommand = 0xC010001D,
IocGetConfigCommand = 0xC183001B, IocGetConfigCommand = 0xC183001B,
}; };
@ -41,7 +42,17 @@ private:
}; };
static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
struct IocCtrlEventWaitParams {
u32_le syncpt_id;
u32_le threshold;
s32_le timeout;
u32_le value;
};
static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output);
}; };
} // namespace Devices } // namespace Devices

View file

@ -103,11 +103,8 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
[&](const auto& entry) { return entry.second->id == params.id; }); [&](const auto& entry) { return entry.second->id == params.id; });
ASSERT(itr != handles.end()); ASSERT(itr != handles.end());
// Make a new handle for the object // Return the existing handle instead of creating a new one.
u32 handle = next_handle++; params.handle = itr->first;
handles[handle] = itr->second;
params.handle = handle;
std::memcpy(output.data(), &params, sizeof(params)); std::memcpy(output.data(), &params, sizeof(params));
return 0; return 0;

View file

@ -17,6 +17,13 @@ namespace Devices {
class nvdevice; class nvdevice;
} }
struct IoctlFence {
u32 id;
u32 value;
};
static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size");
class Module final { class Module final {
public: public:
Module(); Module();

View file

@ -8,6 +8,7 @@
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/ipc_helpers.h" #include "core/hle/ipc_helpers.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/buffer_queue.h" #include "core/hle/service/nvflinger/buffer_queue.h"
#include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_m.h" #include "core/hle/service/vi/vi_m.h"
@ -38,6 +39,7 @@ public:
template <typename T> template <typename T>
T Read() { T Read() {
ASSERT(read_index + sizeof(T) <= buffer.size());
T val; T val;
std::memcpy(&val, buffer.data() + read_index, sizeof(T)); std::memcpy(&val, buffer.data() + read_index, sizeof(T));
read_index += sizeof(T); read_index += sizeof(T);
@ -47,6 +49,7 @@ public:
template <typename T> template <typename T>
T ReadUnaligned() { T ReadUnaligned() {
ASSERT(read_index + sizeof(T) <= buffer.size());
T val; T val;
std::memcpy(&val, buffer.data() + read_index, sizeof(T)); std::memcpy(&val, buffer.data() + read_index, sizeof(T));
read_index += sizeof(T); read_index += sizeof(T);
@ -54,6 +57,7 @@ public:
} }
std::vector<u8> ReadBlock(size_t length) { std::vector<u8> ReadBlock(size_t length) {
ASSERT(read_index + length <= buffer.size());
const u8* const begin = buffer.data() + read_index; const u8* const begin = buffer.data() + read_index;
const u8* const end = begin + length; const u8* const end = begin + length;
std::vector<u8> data(begin, end); std::vector<u8> data(begin, end);
@ -86,7 +90,18 @@ public:
write_index = Common::AlignUp(write_index, 4); write_index = Common::AlignUp(write_index, 4);
} }
template <typename T>
void WriteObject(const T& val) {
u32_le size = static_cast<u32>(sizeof(val));
Write(size);
// TODO(Subv): Support file descriptors.
Write<u32_le>(0); // Fd count.
Write(val);
}
void Deserialize() { void Deserialize() {
ASSERT(buffer.size() > sizeof(Header));
Header header{}; Header header{};
std::memcpy(&header, buffer.data(), sizeof(Header)); std::memcpy(&header, buffer.data(), sizeof(Header));
@ -262,10 +277,11 @@ public:
Data data; Data data;
}; };
// TODO(bunnei): Remove this. When set to 1, games will think a fence is valid and boot further. struct BufferProducerFence {
// This will break libnx and potentially other apps that more stringently check this. This is here u32 is_valid;
// purely as a convenience, and should go away once we implement fences. std::array<Nvidia::IoctlFence, 4> fences;
static constexpr u32 FENCE_HACK = 0; };
static_assert(sizeof(BufferProducerFence) == 36, "BufferProducerFence has wrong size");
class IGBPDequeueBufferResponseParcel : public Parcel { class IGBPDequeueBufferResponseParcel : public Parcel {
public: public:
@ -274,20 +290,16 @@ public:
protected: protected:
void SerializeData() override { void SerializeData() override {
// TODO(bunnei): Find out what this all means. Writing anything non-zero here breaks libnx. // TODO(Subv): Find out how this Fence is used.
Write<u32>(0); BufferProducerFence fence = {};
Write<u32>(FENCE_HACK); fence.is_valid = 1;
Write<u32>(0); for (auto& fence_ : fence.fences)
Write<u32>(0); fence_.id = -1;
Write<u32>(0);
Write<u32>(0); Write(slot);
Write<u32>(0); Write<u32_le>(1);
Write<u32>(0); WriteObject(fence);
Write<u32>(0); Write<u32_le>(0);
Write<u32>(0);
Write<u32>(0);
Write<u32>(0);
Write<u32>(0);
} }
u32_le slot; u32_le slot;
@ -316,11 +328,10 @@ public:
protected: protected:
void SerializeData() override { void SerializeData() override {
// TODO(bunnei): Find out what this all means. Writing anything non-zero here breaks libnx. // TODO(Subv): Figure out what this value means, writing non-zero here will make libnx try
Write<u32_le>(0); // to read an IGBPBuffer object from the parcel.
Write<u32_le>(FENCE_HACK); Write<u32_le>(1);
Write<u32_le>(0); WriteObject(buffer);
Write(buffer);
Write<u32_le>(0); Write<u32_le>(0);
} }