Compare commits
42 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
296e68fd43 | ||
|
|
924c473bb3 | ||
|
|
c337272ca9 | ||
|
|
f2f368014e | ||
|
|
3d1e8f750c | ||
|
|
e3da9fc367 | ||
|
|
24a55bba42 | ||
|
|
b87a71b3fe | ||
|
|
c65a8fafa0 | ||
|
|
0aebe6b3d5 | ||
|
|
7c3cc08957 | ||
|
|
3575d367a4 | ||
|
|
f4b98a857b | ||
|
|
c8f3fc9a4b | ||
|
|
f4c69149f9 | ||
|
|
6a03badcbc | ||
|
|
0f148548f3 | ||
|
|
c5803e30d3 | ||
|
|
e3fadb9616 | ||
|
|
4cd52a34b9 | ||
|
|
03c2d049d4 | ||
|
|
49e5de9f03 | ||
|
|
0d1a99edf6 | ||
|
|
c3dd456d51 | ||
|
|
8e28af6f89 | ||
|
|
3d3b10adc7 | ||
|
|
69bfe075b5 | ||
|
|
88a3140c9b | ||
|
|
519035db3d | ||
|
|
5f119bed56 | ||
|
|
170e19d4ea | ||
|
|
3b885691a1 | ||
|
|
56cc1c11ec | ||
|
|
f00ca69a81 | ||
|
|
0722adb471 | ||
|
|
068668780c | ||
|
|
04b9cde4f5 | ||
|
|
7d209b3c9f | ||
|
|
497b81558e | ||
|
|
98762e9601 | ||
|
|
3a96670f2d | ||
|
|
6daebaaa57 |
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: f7d11baa1c...dfdec797e3
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
@@ -83,8 +84,10 @@ private:
|
||||
}
|
||||
};
|
||||
while (true) {
|
||||
std::unique_lock<std::mutex> lock(message_mutex);
|
||||
message_cv.wait(lock, [&] { return !running || message_queue.Pop(entry); });
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(message_mutex);
|
||||
message_cv.wait(lock, [&] { return !running || message_queue.Pop(entry); });
|
||||
}
|
||||
if (!running) {
|
||||
break;
|
||||
}
|
||||
@@ -92,7 +95,7 @@ private:
|
||||
}
|
||||
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case
|
||||
// where a system is repeatedly spamming logs even on close.
|
||||
constexpr int MAX_LOGS_TO_WRITE = 100;
|
||||
const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100;
|
||||
int logs_written = 0;
|
||||
while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
|
||||
write_logs(entry);
|
||||
@@ -282,4 +285,4 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
||||
|
||||
Impl::Instance().PushEntry(std::move(entry));
|
||||
}
|
||||
} // namespace Log
|
||||
} // namespace Log
|
||||
|
||||
@@ -94,4 +94,11 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
|
||||
bool Filter::CheckMessage(Class log_class, Level level) const {
|
||||
return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]);
|
||||
}
|
||||
|
||||
bool Filter::IsDebug() const {
|
||||
return std::any_of(class_levels.begin(), class_levels.end(), [](const Level& l) {
|
||||
return static_cast<u8>(l) <= static_cast<u8>(Level::Debug);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Log
|
||||
|
||||
@@ -47,6 +47,9 @@ public:
|
||||
/// Matches class/level combination against the filter, returning true if it passed.
|
||||
bool CheckMessage(Class log_class, Level level) const;
|
||||
|
||||
/// Returns true if any logging classes are set to debug
|
||||
bool IsDebug() const;
|
||||
|
||||
private:
|
||||
std::array<Level, (size_t)Class::Count> class_levels;
|
||||
};
|
||||
|
||||
@@ -52,27 +52,14 @@ public:
|
||||
template <typename T>
|
||||
class Field : public FieldInterface {
|
||||
public:
|
||||
Field(FieldType type, std::string name, const T& value)
|
||||
: name(std::move(name)), type(type), value(value) {}
|
||||
|
||||
Field(FieldType type, std::string name, T&& value)
|
||||
Field(FieldType type, std::string name, T value)
|
||||
: name(std::move(name)), type(type), value(std::move(value)) {}
|
||||
|
||||
Field(const Field& other) : Field(other.type, other.name, other.value) {}
|
||||
Field(const Field&) = default;
|
||||
Field& operator=(const Field&) = default;
|
||||
|
||||
Field& operator=(const Field& other) {
|
||||
type = other.type;
|
||||
name = other.name;
|
||||
value = other.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Field& operator=(Field&& other) {
|
||||
type = other.type;
|
||||
name = std::move(other.name);
|
||||
value = std::move(other.value);
|
||||
return *this;
|
||||
}
|
||||
Field(Field&&) = default;
|
||||
Field& operator=(Field&& other) = default;
|
||||
|
||||
void Accept(VisitorInterface& visitor) const override;
|
||||
|
||||
@@ -94,11 +81,11 @@ public:
|
||||
return value;
|
||||
}
|
||||
|
||||
inline bool operator==(const Field<T>& other) {
|
||||
bool operator==(const Field& other) const {
|
||||
return (type == other.type) && (name == other.name) && (value == other.value);
|
||||
}
|
||||
|
||||
inline bool operator!=(const Field<T>& other) {
|
||||
bool operator!=(const Field& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,8 @@ public:
|
||||
*/
|
||||
virtual void LoadContext(const ThreadContext& ctx) = 0;
|
||||
|
||||
virtual void ClearExclusiveState() = 0;
|
||||
|
||||
/// Prepare core for thread reschedule (if needed to correctly handle state)
|
||||
virtual void PrepareReschedule() = 0;
|
||||
};
|
||||
|
||||
@@ -226,6 +226,10 @@ void ARM_Dynarmic::ClearInstructionCache() {
|
||||
jit->ClearCache();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::ClearExclusiveState() {
|
||||
jit->ClearExclusiveState();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::PageTableChanged() {
|
||||
jit = MakeJit(cb);
|
||||
current_page_table = Memory::GetCurrentPageTable();
|
||||
|
||||
@@ -39,6 +39,7 @@ public:
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
|
||||
void PrepareReschedule() override;
|
||||
void ClearExclusiveState() override;
|
||||
|
||||
void ClearInstructionCache() override;
|
||||
void PageTableChanged() override;
|
||||
|
||||
@@ -263,6 +263,8 @@ void ARM_Unicorn::PrepareReschedule() {
|
||||
CHECKED(uc_emu_stop(uc));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::ClearExclusiveState() {}
|
||||
|
||||
void ARM_Unicorn::ClearInstructionCache() {}
|
||||
|
||||
void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
|
||||
|
||||
@@ -31,6 +31,7 @@ public:
|
||||
void SaveContext(ThreadContext& ctx) override;
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
void PrepareReschedule() override;
|
||||
void ClearExclusiveState() override;
|
||||
void ExecuteInstructions(int num_instructions);
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
|
||||
@@ -115,7 +115,7 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu
|
||||
s32 updated_value;
|
||||
if (waiting_threads.size() == 0) {
|
||||
updated_value = value - 1;
|
||||
} else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) {
|
||||
} else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
|
||||
updated_value = value + 1;
|
||||
} else {
|
||||
updated_value = value;
|
||||
@@ -140,7 +140,9 @@ ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool
|
||||
|
||||
s32 cur_value = static_cast<s32>(Memory::Read32(address));
|
||||
if (cur_value < value) {
|
||||
Memory::Write32(address, static_cast<u32>(cur_value - 1));
|
||||
if (should_decrement) {
|
||||
Memory::Write32(address, static_cast<u32>(cur_value - 1));
|
||||
}
|
||||
} else {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
|
||||
|
||||
SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
|
||||
const std::string& reason, u64 timeout,
|
||||
WakeupCallback&& callback) {
|
||||
WakeupCallback&& callback,
|
||||
Kernel::SharedPtr<Kernel::Event> event) {
|
||||
|
||||
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
|
||||
thread->wakeup_callback =
|
||||
@@ -41,7 +42,12 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
|
||||
return true;
|
||||
};
|
||||
|
||||
auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
|
||||
if (!event) {
|
||||
// Create event if not provided
|
||||
event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
|
||||
}
|
||||
|
||||
event->Clear();
|
||||
thread->status = THREADSTATUS_WAIT_HLE_EVENT;
|
||||
thread->wait_objects = {event};
|
||||
event->AddWaitingThread(thread);
|
||||
|
||||
@@ -118,10 +118,12 @@ public:
|
||||
* @param callback Callback to be invoked when the thread is resumed. This callback must write
|
||||
* the entire command response once again, regardless of the state of it before this function
|
||||
* was called.
|
||||
* @param event Event to use to wake up the thread. If unspecified, an event will be created.
|
||||
* @returns Event that when signaled will resume the thread and call the callback function.
|
||||
*/
|
||||
SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
|
||||
u64 timeout, WakeupCallback&& callback);
|
||||
u64 timeout, WakeupCallback&& callback,
|
||||
Kernel::SharedPtr<Kernel::Event> event = nullptr);
|
||||
|
||||
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
|
||||
cpu_core->LoadContext(new_thread->context);
|
||||
cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
|
||||
cpu_core->ClearExclusiveState();
|
||||
} else {
|
||||
current_thread = nullptr;
|
||||
// Note: We do not reset the current process and current page table when idling because
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <atomic>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
@@ -18,9 +19,9 @@ namespace Service::HID {
|
||||
|
||||
// Updating period for each HID device.
|
||||
// TODO(shinyquagsire23): These need better values.
|
||||
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
|
||||
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
|
||||
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 10000;
|
||||
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
|
||||
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
|
||||
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
|
||||
|
||||
class IAppletResource final : public ServiceFramework<IAppletResource> {
|
||||
public:
|
||||
@@ -63,7 +64,8 @@ private:
|
||||
std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
|
||||
Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
|
||||
sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
|
||||
// TODO(shinyquagsire23): gyro, touch, mouse, keyboard
|
||||
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
|
||||
// TODO(shinyquagsire23): gyro, mouse, keyboard
|
||||
}
|
||||
|
||||
void UpdatePadCallback(u64 userdata, int cycles_late) {
|
||||
@@ -151,8 +153,6 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(bunnei): Properly implement the touch screen, the below will just write empty data
|
||||
|
||||
TouchScreen& touchscreen = mem.touchscreen;
|
||||
const u64 last_entry = touchscreen.header.latest_entry;
|
||||
const u64 curr_entry = (last_entry + 1) % touchscreen.entries.size();
|
||||
@@ -164,7 +164,26 @@ private:
|
||||
touchscreen.header.max_entry_index = touchscreen.entries.size();
|
||||
touchscreen.header.timestamp = timestamp;
|
||||
touchscreen.entries[curr_entry].header.timestamp = sample_counter;
|
||||
touchscreen.entries[curr_entry].header.num_touches = 0;
|
||||
|
||||
TouchScreenEntryTouch touch_entry{};
|
||||
auto [x, y, pressed] = touch_device->GetStatus();
|
||||
touch_entry.timestamp = timestamp;
|
||||
touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
|
||||
touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
|
||||
touch_entry.touch_index = 0;
|
||||
|
||||
// TODO(DarkLordZach): Maybe try to derive these from EmuWindow?
|
||||
touch_entry.diameter_x = 15;
|
||||
touch_entry.diameter_y = 15;
|
||||
touch_entry.angle = 0;
|
||||
|
||||
// TODO(DarkLordZach): Implement multi-touch support
|
||||
if (pressed) {
|
||||
touchscreen.entries[curr_entry].header.num_touches = 1;
|
||||
touchscreen.entries[curr_entry].touches[0] = touch_entry;
|
||||
} else {
|
||||
touchscreen.entries[curr_entry].header.num_touches = 0;
|
||||
}
|
||||
|
||||
// TODO(shinyquagsire23): Properly implement mouse
|
||||
Mouse& mouse = mem.mouse;
|
||||
@@ -250,6 +269,7 @@ private:
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
|
||||
buttons;
|
||||
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
|
||||
std::unique_ptr<Input::TouchDevice> touch_device;
|
||||
};
|
||||
|
||||
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
|
||||
|
||||
@@ -18,7 +18,8 @@ u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector
|
||||
}
|
||||
|
||||
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
|
||||
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform) {
|
||||
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform,
|
||||
const MathUtil::Rectangle<int>& crop_rect) {
|
||||
VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
|
||||
LOG_WARNING(Service,
|
||||
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
|
||||
@@ -26,7 +27,8 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
|
||||
|
||||
using PixelFormat = Tegra::FramebufferConfig::PixelFormat;
|
||||
const Tegra::FramebufferConfig framebuffer{
|
||||
addr, offset, width, height, stride, static_cast<PixelFormat>(format), transform};
|
||||
addr, offset, width, height, stride, static_cast<PixelFormat>(format),
|
||||
transform, crop_rect};
|
||||
|
||||
Core::System::GetInstance().perf_stats.EndGameFrame();
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
|
||||
@@ -23,7 +24,8 @@ public:
|
||||
|
||||
/// Performs a screen flip, drawing the buffer pointed to by the handle.
|
||||
void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
|
||||
NVFlinger::BufferQueue::BufferTransformFlags transform);
|
||||
NVFlinger::BufferQueue::BufferTransformFlags transform,
|
||||
const MathUtil::Rectangle<int>& crop_rect);
|
||||
|
||||
private:
|
||||
std::shared_ptr<nvmap> nvmap_dev;
|
||||
|
||||
@@ -13,8 +13,8 @@ namespace Service {
|
||||
namespace NVFlinger {
|
||||
|
||||
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
|
||||
native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle");
|
||||
native_handle->Signal();
|
||||
buffer_wait_event =
|
||||
Kernel::Event::Create(Kernel::ResetType::Sticky, "BufferQueue NativeHandle");
|
||||
}
|
||||
|
||||
void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
|
||||
@@ -26,10 +26,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
|
||||
LOG_WARNING(Service, "Adding graphics buffer {}", slot);
|
||||
|
||||
queue.emplace_back(buffer);
|
||||
|
||||
if (buffer_wait_event) {
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
|
||||
boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
|
||||
@@ -48,8 +45,6 @@ boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
buffer_wait_event = nullptr;
|
||||
|
||||
itr->status = Buffer::Status::Dequeued;
|
||||
return itr->slot;
|
||||
}
|
||||
@@ -62,13 +57,15 @@ const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
|
||||
return itr->igbp_buffer;
|
||||
}
|
||||
|
||||
void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform) {
|
||||
void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
|
||||
const MathUtil::Rectangle<int>& crop_rect) {
|
||||
auto itr = std::find_if(queue.begin(), queue.end(),
|
||||
[&](const Buffer& buffer) { return buffer.slot == slot; });
|
||||
ASSERT(itr != queue.end());
|
||||
ASSERT(itr->status == Buffer::Status::Dequeued);
|
||||
itr->status = Buffer::Status::Queued;
|
||||
itr->transform = transform;
|
||||
itr->crop_rect = crop_rect;
|
||||
}
|
||||
|
||||
boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() {
|
||||
@@ -88,9 +85,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
|
||||
ASSERT(itr->status == Buffer::Status::Acquired);
|
||||
itr->status = Buffer::Status::Free;
|
||||
|
||||
if (buffer_wait_event) {
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
|
||||
u32 BufferQueue::Query(QueryType type) {
|
||||
@@ -106,10 +101,5 @@ u32 BufferQueue::Query(QueryType type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event) {
|
||||
ASSERT_MSG(!buffer_wait_event, "buffer_wait_event only supports a single waiting thread!");
|
||||
buffer_wait_event = std::move(wait_event);
|
||||
}
|
||||
|
||||
} // namespace NVFlinger
|
||||
} // namespace Service
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/math_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
|
||||
@@ -68,23 +69,24 @@ public:
|
||||
Status status = Status::Free;
|
||||
IGBPBuffer igbp_buffer;
|
||||
BufferTransformFlags transform;
|
||||
MathUtil::Rectangle<int> crop_rect;
|
||||
};
|
||||
|
||||
void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
|
||||
boost::optional<u32> DequeueBuffer(u32 width, u32 height);
|
||||
const IGBPBuffer& RequestBuffer(u32 slot) const;
|
||||
void QueueBuffer(u32 slot, BufferTransformFlags transform);
|
||||
void QueueBuffer(u32 slot, BufferTransformFlags transform,
|
||||
const MathUtil::Rectangle<int>& crop_rect);
|
||||
boost::optional<const Buffer&> AcquireBuffer();
|
||||
void ReleaseBuffer(u32 slot);
|
||||
u32 Query(QueryType type);
|
||||
void SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event);
|
||||
|
||||
u32 GetId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> GetNativeHandle() const {
|
||||
return native_handle;
|
||||
Kernel::SharedPtr<Kernel::Event> GetBufferWaitEvent() const {
|
||||
return buffer_wait_event;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -92,9 +94,6 @@ private:
|
||||
u64 layer_id;
|
||||
|
||||
std::vector<Buffer> queue;
|
||||
Kernel::SharedPtr<Kernel::Event> native_handle;
|
||||
|
||||
/// Used to signal waiting thread when no buffers are available
|
||||
Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
|
||||
};
|
||||
|
||||
|
||||
@@ -149,12 +149,10 @@ void NVFlinger::Compose() {
|
||||
ASSERT(nvdisp);
|
||||
|
||||
nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
|
||||
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform);
|
||||
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform,
|
||||
buffer->crop_rect);
|
||||
|
||||
buffer_queue->ReleaseBuffer(buffer->slot);
|
||||
|
||||
// TODO(Subv): Figure out when we should actually signal this event.
|
||||
buffer_queue->GetNativeHandle()->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "core/hle/kernel/event.h"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/alignment.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
@@ -27,8 +28,8 @@ struct DisplayInfo {
|
||||
char display_name[0x40]{"Default"};
|
||||
u64 unknown_1{1};
|
||||
u64 unknown_2{1};
|
||||
u64 width{1920};
|
||||
u64 height{1080};
|
||||
u64 width{1280};
|
||||
u64 height{720};
|
||||
};
|
||||
static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
|
||||
|
||||
@@ -327,8 +328,8 @@ public:
|
||||
|
||||
protected:
|
||||
void SerializeData() override {
|
||||
// TODO(Subv): Figure out what this value means, writing non-zero here will make libnx try
|
||||
// to read an IGBPBuffer object from the parcel.
|
||||
// TODO(Subv): Figure out what this value means, writing non-zero here will make libnx
|
||||
// try to read an IGBPBuffer object from the parcel.
|
||||
Write<u32_le>(1);
|
||||
WriteObject(buffer);
|
||||
Write<u32_le>(0);
|
||||
@@ -360,8 +361,8 @@ public:
|
||||
INSERT_PADDING_WORDS(3);
|
||||
u32_le timestamp;
|
||||
s32_le is_auto_timestamp;
|
||||
s32_le crop_left;
|
||||
s32_le crop_top;
|
||||
s32_le crop_left;
|
||||
s32_le crop_right;
|
||||
s32_le crop_bottom;
|
||||
s32_le scaling_mode;
|
||||
@@ -370,6 +371,10 @@ public:
|
||||
INSERT_PADDING_WORDS(2);
|
||||
u32_le fence_is_valid;
|
||||
std::array<Fence, 2> fences;
|
||||
|
||||
MathUtil::Rectangle<int> GetCropRect() const {
|
||||
return {crop_left, crop_top, crop_right, crop_bottom};
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Data) == 80, "ParcelData has wrong size");
|
||||
|
||||
@@ -495,7 +500,7 @@ private:
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
} else {
|
||||
// Wait the current thread until a buffer becomes available
|
||||
auto wait_event = ctx.SleepClientThread(
|
||||
ctx.SleepClientThread(
|
||||
Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1,
|
||||
[=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
|
||||
ThreadWakeupReason reason) {
|
||||
@@ -506,8 +511,8 @@ private:
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
});
|
||||
buffer_queue->SetBufferWaitEvent(std::move(wait_event));
|
||||
},
|
||||
buffer_queue->GetBufferWaitEvent());
|
||||
}
|
||||
} else if (transaction == TransactionId::RequestBuffer) {
|
||||
IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
|
||||
@@ -519,7 +524,8 @@ private:
|
||||
} else if (transaction == TransactionId::QueueBuffer) {
|
||||
IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
|
||||
|
||||
buffer_queue->QueueBuffer(request.data.slot, request.data.transform);
|
||||
buffer_queue->QueueBuffer(request.data.slot, request.data.transform,
|
||||
request.data.GetCropRect());
|
||||
|
||||
IGBPQueueBufferResponseParcel response{1280, 720};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
@@ -532,7 +538,7 @@ private:
|
||||
IGBPQueryResponseParcel response{value};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
} else if (transaction == TransactionId::CancelBuffer) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
|
||||
LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
|
||||
} else {
|
||||
ASSERT_MSG(false, "Unimplemented");
|
||||
}
|
||||
@@ -565,7 +571,7 @@ private:
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(buffer_queue->GetNativeHandle());
|
||||
rb.PushCopyObjects(buffer_queue->GetBufferWaitEvent());
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
|
||||
|
||||
@@ -520,7 +520,18 @@ public:
|
||||
u32 enable[NumRenderTargets];
|
||||
} blend;
|
||||
|
||||
INSERT_PADDING_WORDS(0xB);
|
||||
struct {
|
||||
u32 enable;
|
||||
u32 front_op_fail;
|
||||
u32 front_op_zfail;
|
||||
u32 front_op_zpass;
|
||||
u32 front_func_func;
|
||||
u32 front_func_ref;
|
||||
u32 front_func_mask;
|
||||
u32 front_mask;
|
||||
} stencil;
|
||||
|
||||
INSERT_PADDING_WORDS(0x3);
|
||||
|
||||
union {
|
||||
BitField<4, 1, u32> triangle_rast_flip;
|
||||
@@ -556,7 +567,17 @@ public:
|
||||
}
|
||||
} tic;
|
||||
|
||||
INSERT_PADDING_WORDS(0x21);
|
||||
INSERT_PADDING_WORDS(0x5);
|
||||
|
||||
struct {
|
||||
u32 enable;
|
||||
u32 back_op_fail;
|
||||
u32 back_op_zfail;
|
||||
u32 back_op_zpass;
|
||||
u32 back_func_func;
|
||||
} stencil_two_side;
|
||||
|
||||
INSERT_PADDING_WORDS(0x17);
|
||||
|
||||
union {
|
||||
BitField<2, 1, u32> coord_origin;
|
||||
@@ -851,10 +872,12 @@ ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
|
||||
ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2);
|
||||
ASSERT_REG_POSITION(depth_test_func, 0x4C3);
|
||||
ASSERT_REG_POSITION(blend, 0x4CF);
|
||||
ASSERT_REG_POSITION(stencil, 0x4E0);
|
||||
ASSERT_REG_POSITION(screen_y_control, 0x4EB);
|
||||
ASSERT_REG_POSITION(vb_element_base, 0x50D);
|
||||
ASSERT_REG_POSITION(tsc, 0x557);
|
||||
ASSERT_REG_POSITION(tic, 0x55D);
|
||||
ASSERT_REG_POSITION(stencil_two_side, 0x565);
|
||||
ASSERT_REG_POSITION(point_coord_replace, 0x581);
|
||||
ASSERT_REG_POSITION(code_address, 0x582);
|
||||
ASSERT_REG_POSITION(draw, 0x585);
|
||||
|
||||
@@ -67,6 +67,7 @@ struct FramebufferConfig {
|
||||
|
||||
using TransformFlags = Service::NVFlinger::BufferQueue::BufferTransformFlags;
|
||||
TransformFlags transform_flags;
|
||||
MathUtil::Rectangle<int> crop_rect;
|
||||
};
|
||||
|
||||
namespace Engines {
|
||||
|
||||
@@ -105,6 +105,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
|
||||
{GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
|
||||
true}, // BC7U
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
|
||||
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8
|
||||
|
||||
// DepthStencil formats
|
||||
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm,
|
||||
@@ -196,8 +197,9 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
|
||||
MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>,
|
||||
MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>,
|
||||
MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
|
||||
MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>,
|
||||
MortonCopy<true, PixelFormat::Z32F>, MortonCopy<true, PixelFormat::Z16>,
|
||||
MortonCopy<true, PixelFormat::G8R8>, MortonCopy<true, PixelFormat::Z24S8>,
|
||||
MortonCopy<true, PixelFormat::S8Z24>, MortonCopy<true, PixelFormat::Z32F>,
|
||||
MortonCopy<true, PixelFormat::Z16>,
|
||||
};
|
||||
|
||||
static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
|
||||
@@ -217,7 +219,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
MortonCopy<false, PixelFormat::ABGR8>,
|
||||
nullptr,
|
||||
MortonCopy<false, PixelFormat::G8R8>,
|
||||
MortonCopy<false, PixelFormat::Z24S8>,
|
||||
MortonCopy<false, PixelFormat::S8Z24>,
|
||||
MortonCopy<false, PixelFormat::Z32F>,
|
||||
@@ -274,10 +277,10 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
|
||||
|
||||
S8Z24 input_pixel{};
|
||||
Z24S8 output_pixel{};
|
||||
|
||||
const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)};
|
||||
for (size_t y = 0; y < height; ++y) {
|
||||
for (size_t x = 0; x < width; ++x) {
|
||||
const size_t offset{4 * (y * width + x)};
|
||||
const size_t offset{bpp * (y * width + x)};
|
||||
std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24));
|
||||
output_pixel.s8.Assign(input_pixel.s8);
|
||||
output_pixel.z24.Assign(input_pixel.z24);
|
||||
@@ -285,6 +288,19 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
|
||||
const auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8)};
|
||||
for (size_t y = 0; y < height; ++y) {
|
||||
for (size_t x = 0; x < width; ++x) {
|
||||
const size_t offset{bpp * (y * width + x)};
|
||||
const u8 temp{data[offset]};
|
||||
data[offset] = data[offset + 1];
|
||||
data[offset + 1] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to perform software conversion (as needed) when loading a buffer from Switch
|
||||
* memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with
|
||||
@@ -305,6 +321,11 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
|
||||
// Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
|
||||
ConvertS8Z24ToZ24S8(data, width, height);
|
||||
break;
|
||||
|
||||
case PixelFormat::G8R8:
|
||||
// Convert the G8R8 color format to R8G8, as OpenGL does not support G8R8.
|
||||
ConvertG8R8ToR8G8(data, width, height);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,14 +37,15 @@ struct SurfaceParams {
|
||||
DXN1 = 11, // This is also known as BC4
|
||||
BC7U = 12,
|
||||
ASTC_2D_4X4 = 13,
|
||||
G8R8 = 14,
|
||||
|
||||
MaxColorFormat,
|
||||
|
||||
// DepthStencil formats
|
||||
Z24S8 = 14,
|
||||
S8Z24 = 15,
|
||||
Z32F = 16,
|
||||
Z16 = 17,
|
||||
Z24S8 = 15,
|
||||
S8Z24 = 16,
|
||||
Z32F = 17,
|
||||
Z16 = 18,
|
||||
|
||||
MaxDepthStencilFormat,
|
||||
|
||||
@@ -96,6 +97,7 @@ struct SurfaceParams {
|
||||
4, // DXN1
|
||||
4, // BC7U
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8
|
||||
1, // Z24S8
|
||||
1, // S8Z24
|
||||
1, // Z32F
|
||||
@@ -125,6 +127,7 @@ struct SurfaceParams {
|
||||
64, // DXN1
|
||||
128, // BC7U
|
||||
32, // ASTC_2D_4X4
|
||||
16, // G8R8
|
||||
32, // Z24S8
|
||||
32, // S8Z24
|
||||
32, // Z32F
|
||||
@@ -186,6 +189,8 @@ struct SurfaceParams {
|
||||
return PixelFormat::A1B5G5R5;
|
||||
case Tegra::Texture::TextureFormat::R8:
|
||||
return PixelFormat::R8;
|
||||
case Tegra::Texture::TextureFormat::G8R8:
|
||||
return PixelFormat::G8R8;
|
||||
case Tegra::Texture::TextureFormat::R16_G16_B16_A16:
|
||||
return PixelFormat::RGBA16F;
|
||||
case Tegra::Texture::TextureFormat::BF10GF11RF11:
|
||||
@@ -223,6 +228,8 @@ struct SurfaceParams {
|
||||
return Tegra::Texture::TextureFormat::A1B5G5R5;
|
||||
case PixelFormat::R8:
|
||||
return Tegra::Texture::TextureFormat::R8;
|
||||
case PixelFormat::G8R8:
|
||||
return Tegra::Texture::TextureFormat::G8R8;
|
||||
case PixelFormat::RGBA16F:
|
||||
return Tegra::Texture::TextureFormat::R16_G16_B16_A16;
|
||||
case PixelFormat::R11FG11FB10F:
|
||||
|
||||
@@ -154,6 +154,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
|
||||
|
||||
// Framebuffer orientation handling
|
||||
framebuffer_transform_flags = framebuffer.transform_flags;
|
||||
framebuffer_crop_rect = framebuffer.crop_rect;
|
||||
|
||||
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default
|
||||
// only allows rows to have a memory alignement of 4.
|
||||
@@ -320,11 +321,24 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_MSG(framebuffer_crop_rect.top == 0, "Unimplemented");
|
||||
ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented");
|
||||
|
||||
// Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
|
||||
// (e.g. handheld mode) on a 1920x1080 framebuffer.
|
||||
f32 scale_u = 1.f, scale_v = 1.f;
|
||||
if (framebuffer_crop_rect.GetWidth() > 0) {
|
||||
scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / screen_info.texture.width;
|
||||
}
|
||||
if (framebuffer_crop_rect.GetHeight() > 0) {
|
||||
scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) / screen_info.texture.height;
|
||||
}
|
||||
|
||||
std::array<ScreenRectVertex, 4> vertices = {{
|
||||
ScreenRectVertex(x, y, texcoords.top, left),
|
||||
ScreenRectVertex(x + w, y, texcoords.bottom, left),
|
||||
ScreenRectVertex(x, y + h, texcoords.top, right),
|
||||
ScreenRectVertex(x + w, y + h, texcoords.bottom, right),
|
||||
ScreenRectVertex(x, y, texcoords.top * scale_u, left * scale_v),
|
||||
ScreenRectVertex(x + w, y, texcoords.bottom * scale_u, left * scale_v),
|
||||
ScreenRectVertex(x, y + h, texcoords.top * scale_u, right * scale_v),
|
||||
ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v),
|
||||
}};
|
||||
|
||||
state.texture_units[0].texture_2d = screen_info.display_texture;
|
||||
|
||||
@@ -97,4 +97,5 @@ private:
|
||||
|
||||
/// Used for transforming the framebuffer orientation
|
||||
Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags;
|
||||
MathUtil::Rectangle<int> framebuffer_crop_rect;
|
||||
};
|
||||
|
||||
@@ -25,16 +25,15 @@
|
||||
|
||||
class BitStream {
|
||||
public:
|
||||
BitStream(unsigned char* ptr, int nBits = 0, int start_offset = 0)
|
||||
: m_BitsWritten(0), m_BitsRead(0), m_NumBits(nBits), m_CurByte(ptr),
|
||||
m_NextBit(start_offset % 8), done(false) {}
|
||||
explicit BitStream(unsigned char* ptr, int nBits = 0, int start_offset = 0)
|
||||
: m_NumBits(nBits), m_CurByte(ptr), m_NextBit(start_offset % 8) {}
|
||||
|
||||
~BitStream() = default;
|
||||
|
||||
int GetBitsWritten() const {
|
||||
return m_BitsWritten;
|
||||
}
|
||||
|
||||
~BitStream() {}
|
||||
|
||||
void WriteBitsR(unsigned int val, unsigned int nBits) {
|
||||
for (unsigned int i = 0; i < nBits; i++) {
|
||||
WriteBit((val >> (nBits - i - 1)) & 1);
|
||||
@@ -95,33 +94,28 @@ private:
|
||||
done = done || ++m_BitsWritten >= m_NumBits;
|
||||
}
|
||||
|
||||
int m_BitsWritten;
|
||||
int m_BitsWritten = 0;
|
||||
const int m_NumBits;
|
||||
unsigned char* m_CurByte;
|
||||
int m_NextBit;
|
||||
int m_BitsRead;
|
||||
int m_NextBit = 0;
|
||||
int m_BitsRead = 0;
|
||||
|
||||
bool done;
|
||||
bool done = false;
|
||||
};
|
||||
|
||||
template <typename IntType>
|
||||
class Bits {
|
||||
private:
|
||||
const IntType& m_Bits;
|
||||
|
||||
// Don't copy
|
||||
Bits() {}
|
||||
Bits(const Bits&) {}
|
||||
Bits& operator=(const Bits&) {}
|
||||
|
||||
public:
|
||||
explicit Bits(IntType& v) : m_Bits(v) {}
|
||||
explicit Bits(const IntType& v) : m_Bits(v) {}
|
||||
|
||||
uint8_t operator[](uint32_t bitPos) {
|
||||
Bits(const Bits&) = delete;
|
||||
Bits& operator=(const Bits&) = delete;
|
||||
|
||||
uint8_t operator[](uint32_t bitPos) const {
|
||||
return static_cast<uint8_t>((m_Bits >> bitPos) & 1);
|
||||
}
|
||||
|
||||
IntType operator()(uint32_t start, uint32_t end) {
|
||||
IntType operator()(uint32_t start, uint32_t end) const {
|
||||
if (start == end) {
|
||||
return (*this)[start];
|
||||
} else if (start > end) {
|
||||
@@ -133,6 +127,9 @@ public:
|
||||
uint64_t mask = (1 << (end - start + 1)) - 1;
|
||||
return (m_Bits >> start) & mask;
|
||||
}
|
||||
|
||||
private:
|
||||
const IntType& m_Bits;
|
||||
};
|
||||
|
||||
enum EIntegerEncoding { eIntegerEncoding_JustBits, eIntegerEncoding_Quint, eIntegerEncoding_Trit };
|
||||
@@ -186,12 +183,12 @@ public:
|
||||
m_QuintValue = val;
|
||||
}
|
||||
|
||||
bool MatchesEncoding(const IntegerEncodedValue& other) {
|
||||
bool MatchesEncoding(const IntegerEncodedValue& other) const {
|
||||
return m_Encoding == other.m_Encoding && m_NumBits == other.m_NumBits;
|
||||
}
|
||||
|
||||
// Returns the number of bits required to encode nVals values.
|
||||
uint32_t GetBitLength(uint32_t nVals) {
|
||||
uint32_t GetBitLength(uint32_t nVals) const {
|
||||
uint32_t totalBits = m_NumBits * nVals;
|
||||
if (m_Encoding == eIntegerEncoding_Trit) {
|
||||
totalBits += (nVals * 8 + 4) / 5;
|
||||
@@ -382,19 +379,15 @@ private:
|
||||
namespace ASTCC {
|
||||
|
||||
struct TexelWeightParams {
|
||||
uint32_t m_Width;
|
||||
uint32_t m_Height;
|
||||
bool m_bDualPlane;
|
||||
uint32_t m_MaxWeight;
|
||||
bool m_bError;
|
||||
bool m_bVoidExtentLDR;
|
||||
bool m_bVoidExtentHDR;
|
||||
uint32_t m_Width = 0;
|
||||
uint32_t m_Height = 0;
|
||||
bool m_bDualPlane = false;
|
||||
uint32_t m_MaxWeight = 0;
|
||||
bool m_bError = false;
|
||||
bool m_bVoidExtentLDR = false;
|
||||
bool m_bVoidExtentHDR = false;
|
||||
|
||||
TexelWeightParams() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
|
||||
uint32_t GetPackedBitSize() {
|
||||
uint32_t GetPackedBitSize() const {
|
||||
// How many indices do we have?
|
||||
uint32_t nIdxs = m_Height * m_Width;
|
||||
if (m_bDualPlane) {
|
||||
@@ -413,7 +406,7 @@ struct TexelWeightParams {
|
||||
}
|
||||
};
|
||||
|
||||
TexelWeightParams DecodeBlockInfo(BitStream& strm) {
|
||||
static TexelWeightParams DecodeBlockInfo(BitStream& strm) {
|
||||
TexelWeightParams params;
|
||||
|
||||
// Read the entire block mode all at once
|
||||
@@ -612,8 +605,8 @@ TexelWeightParams DecodeBlockInfo(BitStream& strm) {
|
||||
return params;
|
||||
}
|
||||
|
||||
void FillVoidExtentLDR(BitStream& strm, uint32_t* const outBuf, uint32_t blockWidth,
|
||||
uint32_t blockHeight) {
|
||||
static void FillVoidExtentLDR(BitStream& strm, uint32_t* const outBuf, uint32_t blockWidth,
|
||||
uint32_t blockHeight) {
|
||||
// Don't actually care about the void extent, just read the bits...
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
strm.ReadBits(13);
|
||||
@@ -628,23 +621,25 @@ void FillVoidExtentLDR(BitStream& strm, uint32_t* const outBuf, uint32_t blockWi
|
||||
uint32_t rgba = (r >> 8) | (g & 0xFF00) | (static_cast<uint32_t>(b) & 0xFF00) << 8 |
|
||||
(static_cast<uint32_t>(a) & 0xFF00) << 16;
|
||||
|
||||
for (uint32_t j = 0; j < blockHeight; j++)
|
||||
for (uint32_t j = 0; j < blockHeight; j++) {
|
||||
for (uint32_t i = 0; i < blockWidth; i++) {
|
||||
outBuf[j * blockWidth + i] = rgba;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillError(uint32_t* outBuf, uint32_t blockWidth, uint32_t blockHeight) {
|
||||
for (uint32_t j = 0; j < blockHeight; j++)
|
||||
static void FillError(uint32_t* outBuf, uint32_t blockWidth, uint32_t blockHeight) {
|
||||
for (uint32_t j = 0; j < blockHeight; j++) {
|
||||
for (uint32_t i = 0; i < blockWidth; i++) {
|
||||
outBuf[j * blockWidth + i] = 0xFFFF00FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replicates low numBits such that [(toBit - 1):(toBit - 1 - fromBit)]
|
||||
// is the same as [(numBits - 1):0] and repeats all the way down.
|
||||
template <typename IntType>
|
||||
IntType Replicate(const IntType& val, uint32_t numBits, uint32_t toBit) {
|
||||
static IntType Replicate(const IntType& val, uint32_t numBits, uint32_t toBit) {
|
||||
if (numBits == 0)
|
||||
return 0;
|
||||
if (toBit == 0)
|
||||
@@ -668,27 +663,15 @@ IntType Replicate(const IntType& val, uint32_t numBits, uint32_t toBit) {
|
||||
|
||||
class Pixel {
|
||||
protected:
|
||||
typedef int16_t ChannelType;
|
||||
uint8_t m_BitDepth[4];
|
||||
int16_t color[4];
|
||||
using ChannelType = int16_t;
|
||||
uint8_t m_BitDepth[4] = {8, 8, 8, 8};
|
||||
int16_t color[4] = {};
|
||||
|
||||
public:
|
||||
Pixel() {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
m_BitDepth[i] = 8;
|
||||
color[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Pixel(ChannelType a, ChannelType r, ChannelType g, ChannelType b, unsigned bitDepth = 8) {
|
||||
for (int i = 0; i < 4; i++)
|
||||
m_BitDepth[i] = bitDepth;
|
||||
|
||||
color[0] = a;
|
||||
color[1] = r;
|
||||
color[2] = g;
|
||||
color[3] = b;
|
||||
}
|
||||
Pixel() = default;
|
||||
Pixel(ChannelType a, ChannelType r, ChannelType g, ChannelType b, unsigned bitDepth = 8)
|
||||
: m_BitDepth{uint8_t(bitDepth), uint8_t(bitDepth), uint8_t(bitDepth), uint8_t(bitDepth)},
|
||||
color{a, r, g, b} {}
|
||||
|
||||
// Changes the depth of each pixel. This scales the values to
|
||||
// the appropriate bit depth by either truncating the least
|
||||
@@ -807,8 +790,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void DecodeColorValues(uint32_t* out, uint8_t* data, uint32_t* modes, const uint32_t nPartitions,
|
||||
const uint32_t nBitsForColorData) {
|
||||
static void DecodeColorValues(uint32_t* out, uint8_t* data, const uint32_t* modes,
|
||||
const uint32_t nPartitions, const uint32_t nBitsForColorData) {
|
||||
// First figure out how many color values we have
|
||||
uint32_t nValues = 0;
|
||||
for (uint32_t i = 0; i < nPartitions; i++) {
|
||||
@@ -844,8 +827,7 @@ void DecodeColorValues(uint32_t* out, uint8_t* data, uint32_t* modes, const uint
|
||||
// Once we have the decoded values, we need to dequantize them to the 0-255 range
|
||||
// This procedure is outlined in ASTC spec C.2.13
|
||||
uint32_t outIdx = 0;
|
||||
std::vector<IntegerEncodedValue>::const_iterator itr;
|
||||
for (itr = decodedColorValues.begin(); itr != decodedColorValues.end(); itr++) {
|
||||
for (auto itr = decodedColorValues.begin(); itr != decodedColorValues.end(); ++itr) {
|
||||
// Have we already decoded all that we need?
|
||||
if (outIdx >= nValues) {
|
||||
break;
|
||||
@@ -978,7 +960,7 @@ void DecodeColorValues(uint32_t* out, uint8_t* data, uint32_t* modes, const uint
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t UnquantizeTexelWeight(const IntegerEncodedValue& val) {
|
||||
static uint32_t UnquantizeTexelWeight(const IntegerEncodedValue& val) {
|
||||
uint32_t bitval = val.GetBitValue();
|
||||
uint32_t bitlen = val.BaseBitLength();
|
||||
|
||||
@@ -1067,17 +1049,18 @@ uint32_t UnquantizeTexelWeight(const IntegerEncodedValue& val) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void UnquantizeTexelWeights(uint32_t out[2][144], std::vector<IntegerEncodedValue>& weights,
|
||||
const TexelWeightParams& params, const uint32_t blockWidth,
|
||||
const uint32_t blockHeight) {
|
||||
static void UnquantizeTexelWeights(uint32_t out[2][144],
|
||||
const std::vector<IntegerEncodedValue>& weights,
|
||||
const TexelWeightParams& params, const uint32_t blockWidth,
|
||||
const uint32_t blockHeight) {
|
||||
uint32_t weightIdx = 0;
|
||||
uint32_t unquantized[2][144];
|
||||
std::vector<IntegerEncodedValue>::const_iterator itr;
|
||||
for (itr = weights.begin(); itr != weights.end(); itr++) {
|
||||
|
||||
for (auto itr = weights.begin(); itr != weights.end(); ++itr) {
|
||||
unquantized[0][weightIdx] = UnquantizeTexelWeight(*itr);
|
||||
|
||||
if (params.m_bDualPlane) {
|
||||
itr++;
|
||||
++itr;
|
||||
unquantized[1][weightIdx] = UnquantizeTexelWeight(*itr);
|
||||
if (itr == weights.end()) {
|
||||
break;
|
||||
@@ -1261,8 +1244,8 @@ static inline uint32_t Select2DPartition(int32_t seed, int32_t x, int32_t y, int
|
||||
}
|
||||
|
||||
// Section C.2.14
|
||||
void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const uint32_t*& colorValues,
|
||||
uint32_t colorEndpointMode) {
|
||||
static void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const uint32_t*& colorValues,
|
||||
uint32_t colorEndpointMode) {
|
||||
#define READ_UINT_VALUES(N) \
|
||||
uint32_t v[N]; \
|
||||
for (uint32_t i = 0; i < N; i++) { \
|
||||
@@ -1382,8 +1365,8 @@ void ComputeEndpoints(Pixel& ep1, Pixel& ep2, const uint32_t*& colorValues,
|
||||
#undef READ_INT_VALUES
|
||||
}
|
||||
|
||||
void DecompressBlock(uint8_t inBuf[16], const uint32_t blockWidth, const uint32_t blockHeight,
|
||||
uint32_t* outBuf) {
|
||||
static void DecompressBlock(uint8_t inBuf[16], const uint32_t blockWidth,
|
||||
const uint32_t blockHeight, uint32_t* outBuf) {
|
||||
BitStream strm(inBuf);
|
||||
TexelWeightParams weightParams = DecodeBlockInfo(strm);
|
||||
|
||||
@@ -1617,8 +1600,7 @@ namespace Tegra::Texture::ASTC {
|
||||
std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height,
|
||||
uint32_t block_width, uint32_t block_height) {
|
||||
uint32_t blockIdx = 0;
|
||||
std::vector<uint8_t> outData;
|
||||
outData.resize(height * width * 4);
|
||||
std::vector<uint8_t> outData(height * width * 4);
|
||||
for (uint32_t j = 0; j < height; j += block_height) {
|
||||
for (uint32_t i = 0; i < width; i += block_width) {
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ u32 BytesPerPixel(TextureFormat format) {
|
||||
return 4;
|
||||
case TextureFormat::A1B5G5R5:
|
||||
case TextureFormat::B5G6R5:
|
||||
case TextureFormat::G8R8:
|
||||
return 2;
|
||||
case TextureFormat::R8:
|
||||
return 1;
|
||||
@@ -112,6 +113,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width,
|
||||
case TextureFormat::A1B5G5R5:
|
||||
case TextureFormat::B5G6R5:
|
||||
case TextureFormat::R8:
|
||||
case TextureFormat::G8R8:
|
||||
case TextureFormat::R16_G16_B16_A16:
|
||||
case TextureFormat::R32_G32_B32_A32:
|
||||
case TextureFormat::BF10GF11RF11:
|
||||
@@ -167,6 +169,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
|
||||
case TextureFormat::A1B5G5R5:
|
||||
case TextureFormat::B5G6R5:
|
||||
case TextureFormat::R8:
|
||||
case TextureFormat::G8R8:
|
||||
case TextureFormat::BF10GF11RF11:
|
||||
case TextureFormat::R32_G32_B32_A32:
|
||||
// TODO(Subv): For the time being just forward the same data without any decoding.
|
||||
|
||||
@@ -97,7 +97,7 @@ void Config::ReadValues() {
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("System");
|
||||
Settings::values.use_docked_mode = qt_config->value("use_docked_mode", true).toBool();
|
||||
Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Miscellaneous");
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
#include "game_list_p.h"
|
||||
#include "ui_settings.h"
|
||||
|
||||
GameList::SearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) {
|
||||
this->gamelist = gamelist;
|
||||
edit_filter_text_old = "";
|
||||
}
|
||||
GameList::SearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {}
|
||||
|
||||
// EventFilter in order to process systemkeys while editing the searchfield
|
||||
bool GameList::SearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* event) {
|
||||
@@ -141,10 +138,12 @@ GameList::SearchField::SearchField(GameList* parent) : QWidget{parent} {
|
||||
* @param userinput String containing all words getting checked
|
||||
* @return true if the haystack contains all words of userinput
|
||||
*/
|
||||
bool GameList::containsAllWords(QString haystack, QString userinput) {
|
||||
QStringList userinput_split = userinput.split(" ", QString::SplitBehavior::SkipEmptyParts);
|
||||
bool GameList::ContainsAllWords(const QString& haystack, const QString& userinput) const {
|
||||
const QStringList userinput_split =
|
||||
userinput.split(' ', QString::SplitBehavior::SkipEmptyParts);
|
||||
|
||||
return std::all_of(userinput_split.begin(), userinput_split.end(),
|
||||
[haystack](QString s) { return haystack.contains(s); });
|
||||
[&haystack](const QString& s) { return haystack.contains(s); });
|
||||
}
|
||||
|
||||
// Event in order to filter the gamelist after editing the searchfield
|
||||
@@ -178,7 +177,7 @@ void GameList::onTextChanged(const QString& newText) {
|
||||
// The search is case insensitive because of toLower()
|
||||
// I decided not to use Qt::CaseInsensitive in containsAllWords to prevent
|
||||
// multiple conversions of edit_filter_text for each game in the gamelist
|
||||
if (containsAllWords(file_name.append(" ").append(file_title), edit_filter_text) ||
|
||||
if (ContainsAllWords(file_name.append(' ').append(file_title), edit_filter_text) ||
|
||||
(file_programmid.count() == 16 && edit_filter_text.contains(file_programmid))) {
|
||||
tree_view->setRowHidden(i, root_index, false);
|
||||
++result_count;
|
||||
|
||||
@@ -89,7 +89,7 @@ private:
|
||||
|
||||
void PopupContextMenu(const QPoint& menu_location);
|
||||
void RefreshGameDirectory();
|
||||
bool containsAllWords(QString haystack, QString userinput);
|
||||
bool ContainsAllWords(const QString& haystack, const QString& userinput) const;
|
||||
|
||||
SearchField* search_field;
|
||||
GMainWindow* main_window = nullptr;
|
||||
|
||||
@@ -110,7 +110,7 @@ void Config::ReadValues() {
|
||||
sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
|
||||
|
||||
// System
|
||||
Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", true);
|
||||
Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
|
||||
|
||||
// Miscellaneous
|
||||
Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
|
||||
|
||||
@@ -163,7 +163,7 @@ use_virtual_sd =
|
||||
|
||||
[System]
|
||||
# Whether the system is docked
|
||||
# 1 (default): Yes, 0: No
|
||||
# 1: Yes, 0 (default): No
|
||||
use_docked_mode =
|
||||
|
||||
# The system region that yuzu will use during emulation
|
||||
|
||||
Reference in New Issue
Block a user