Improve ENUM_STRING + Fix Host1X Syncpoints

`ENUM_STRING` now has a unified implementation in <common/macros.h> with a documented format and can be used throughout the codebase. 

A major performance regression was added in the Host1X Syncpoint revamp as it did a syscall if there were any waiters during `Increment` even if they would just be woken up and go back to sleep as the threshold wasn't hit. It has now been optimized to only do a wake if any waiting thread needs to be awoken. 

There was also a bug concerning increment where it would perform actions corresponding to the previous increment rather than the current one which has also been fixed.
This commit is contained in:
PixelyIon 2021-06-29 05:12:16 +05:30
parent 375ce58008
commit 879d01f78d
6 changed files with 118 additions and 127 deletions

View File

@ -0,0 +1,25 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
/**
* @brief A case statement for an enumerant value to use alongside ENUM_STRING
*/
#define ENUM_CASE(key) \
case ENUM_TYPE::key: \
return #key
/**
* @brief Creates a function to convert an enumerant to its string representation at runtime
* @example ENUM_STRING(Example, { ENUM_CASE(A); ENUM_CASE(B); })
*/
#define ENUM_STRING(name, cases) \
constexpr const char *ToString(name value) { \
using ENUM_TYPE = name; \
switch (value) { \
cases \
default: \
return "Unknown"; \
}; \
};

View File

@ -14,20 +14,6 @@ namespace skyline::gpu {
class Texture;
}
#define ENUM_CASE(key) \
case ENUM_TYPE::key: \
return #key
#define ENUM_STRING(name, cases) \
constexpr const char *ToString(name value) { \
using ENUM_TYPE = name; \
switch (value) { \
cases \
default: \
return "Unknown"; \
}; \
};
namespace skyline::service::hosbinder {
/**
* @url https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h;l=52-91
@ -39,8 +25,12 @@ namespace skyline::service::hosbinder {
Acquired,
};
ENUM_STRING(BufferState, ENUM_CASE(Free);ENUM_CASE(Dequeued);ENUM_CASE(Queued);ENUM_CASE(Acquired);
);
ENUM_STRING(BufferState, {
ENUM_CASE(Free);
ENUM_CASE(Dequeued);
ENUM_CASE(Queued);
ENUM_CASE(Acquired);
})
/**
* @url https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h;l=32-138
@ -203,5 +193,3 @@ namespace skyline::service::hosbinder {
extern std::weak_ptr<GraphicBufferProducer> producer; //!< A globally shared instance of the GraphicsBufferProducer
}
#undef ENUM_CASE

View File

@ -6,13 +6,10 @@
#pragma once
#include <common.h>
#include <common/macros.h>
#include <soc/host1x.h>
#include <services/common/fence.h>
#define ENUM_CASE(name, key) \
case name::key: \
return #key
namespace skyline::service::hosbinder {
/**
* @brief An enumeration of all status codes for Android including Binder IPC
@ -113,26 +110,22 @@ namespace skyline::service::hosbinder {
sRGBX8888 = 13, //! 4x8-bit sRGB + 0
};
constexpr const char *ToString(AndroidPixelFormat format) {
switch (format) {
ENUM_CASE(AndroidPixelFormat, None);
ENUM_CASE(AndroidPixelFormat, Custom);
ENUM_CASE(AndroidPixelFormat, Translucent);
ENUM_CASE(AndroidPixelFormat, Transparent);
ENUM_CASE(AndroidPixelFormat, Opaque);
ENUM_CASE(AndroidPixelFormat, RGBA8888);
ENUM_CASE(AndroidPixelFormat, RGBX8888);
ENUM_CASE(AndroidPixelFormat, RGB888);
ENUM_CASE(AndroidPixelFormat, RGB565);
ENUM_CASE(AndroidPixelFormat, BGRA8888);
ENUM_CASE(AndroidPixelFormat, RGBA5551);
ENUM_CASE(AndroidPixelFormat, RGBA4444);
ENUM_CASE(AndroidPixelFormat, sRGBA8888);
ENUM_CASE(AndroidPixelFormat, sRGBX8888);
default:
return "Unknown";
}
}
ENUM_STRING(AndroidPixelFormat, {
ENUM_CASE(None);
ENUM_CASE(Custom);
ENUM_CASE(Translucent);
ENUM_CASE(Transparent);
ENUM_CASE(Opaque);
ENUM_CASE(RGBA8888);
ENUM_CASE(RGBX8888);
ENUM_CASE(RGB888);
ENUM_CASE(RGB565);
ENUM_CASE(BGRA8888);
ENUM_CASE(RGBA5551);
ENUM_CASE(RGBA4444);
ENUM_CASE(sRGBA8888);
ENUM_CASE(sRGBX8888);
})
/**
* @brief The layout of the surface's pixels in GPU memory
@ -143,15 +136,11 @@ namespace skyline::service::hosbinder {
Blocklinear = 0x3, //!< A generic block layout which is further defined by it's kind
};
constexpr const char *ToString(NvSurfaceLayout layout) {
switch (layout) {
ENUM_CASE(NvSurfaceLayout, Pitch);
ENUM_CASE(NvSurfaceLayout, Tiled);
ENUM_CASE(NvSurfaceLayout, Blocklinear);
default:
return "Unknown";
}
}
ENUM_STRING(NvSurfaceLayout, {
ENUM_CASE(Pitch);
ENUM_CASE(Tiled);
ENUM_CASE(Blocklinear);
})
/**
* @brief The kind of tiling used to arrange pixels in a blocklinear surface
@ -170,14 +159,10 @@ namespace skyline::service::hosbinder {
Interlaced, //!< Odd and even rows are updated in an alternating pattern
};
constexpr const char *ToString(NvDisplayScanFormat format) {
switch (format) {
ENUM_CASE(NvDisplayScanFormat, Progressive);
ENUM_CASE(NvDisplayScanFormat, Interlaced);
default:
return "Unknown";
}
}
ENUM_STRING(NvDisplayScanFormat, {
ENUM_CASE(Progressive);
ENUM_CASE(Interlaced);
})
#pragma pack(push, 1)
@ -246,5 +231,3 @@ namespace skyline::service::hosbinder {
#pragma pack(pop)
}
#undef ENUM_CASE

View File

@ -4,9 +4,7 @@
#pragma once
#define ENUM_CASE(name, key) \
case name::key: \
return #key
#include <common/macros.h>
namespace skyline::service::hosbinder {
/**
@ -20,17 +18,13 @@ namespace skyline::service::hosbinder {
Camera = 4,
};
constexpr const char *ToString(NativeWindowApi api) {
switch (api) {
ENUM_CASE(NativeWindowApi, None);
ENUM_CASE(NativeWindowApi, EGL);
ENUM_CASE(NativeWindowApi, CPU);
ENUM_CASE(NativeWindowApi, Media);
ENUM_CASE(NativeWindowApi, Camera);
default:
return "Unknown";
}
}
ENUM_STRING(NativeWindowApi, {
ENUM_CASE(None);
ENUM_CASE(EGL);
ENUM_CASE(CPU);
ENUM_CASE(Media);
ENUM_CASE(Camera);
});
/**
* @note A few combinations of transforms that are not in the NATIVE_WINDOW_TRANSFORM enum were added to assist with conversion to/from Vulkan transforms
@ -48,21 +42,17 @@ namespace skyline::service::hosbinder {
InvertDisplay = 0b1000,
};
constexpr const char *ToString(NativeWindowTransform transform) {
switch (transform) {
ENUM_CASE(NativeWindowTransform, Identity);
ENUM_CASE(NativeWindowTransform, MirrorHorizontal);
ENUM_CASE(NativeWindowTransform, MirrorVertical);
ENUM_CASE(NativeWindowTransform, Rotate90);
ENUM_CASE(NativeWindowTransform, Rotate180);
ENUM_CASE(NativeWindowTransform, Rotate270);
ENUM_CASE(NativeWindowTransform, MirrorHorizontalRotate90);
ENUM_CASE(NativeWindowTransform, MirrorVerticalRotate90);
ENUM_CASE(NativeWindowTransform, InvertDisplay);
default:
return "Unknown";
}
}
ENUM_STRING(NativeWindowTransform, {
ENUM_CASE(Identity);
ENUM_CASE(MirrorHorizontal);
ENUM_CASE(MirrorVertical);
ENUM_CASE(Rotate90);
ENUM_CASE(Rotate180);
ENUM_CASE(Rotate270);
ENUM_CASE(MirrorHorizontalRotate90);
ENUM_CASE(MirrorVerticalRotate90);
ENUM_CASE(InvertDisplay);
});
/**
* @url https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:system/core/include/system/window.h;l=338-354
@ -74,16 +64,12 @@ namespace skyline::service::hosbinder {
NoScaleCrop = 3,
};
constexpr const char *ToString(NativeWindowScalingMode scalingMode) {
switch (scalingMode) {
ENUM_CASE(NativeWindowScalingMode, Freeze);
ENUM_CASE(NativeWindowScalingMode, ScaleToWindow);
ENUM_CASE(NativeWindowScalingMode, ScaleCrop);
ENUM_CASE(NativeWindowScalingMode, NoScaleCrop);
default:
return "Unknown";
}
}
ENUM_STRING(NativeWindowScalingMode, {
ENUM_CASE(Freeze);
ENUM_CASE(ScaleToWindow);
ENUM_CASE(ScaleCrop);
ENUM_CASE(NoScaleCrop);
});
/**
* @url https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:system/core/include/system/window.h;l=127-265
@ -104,24 +90,19 @@ namespace skyline::service::hosbinder {
MaxBufferCount = 12, //!< A custom query for HOS which returns the maximum number of buffers that can be allocated at once
};
constexpr const char *ToString(NativeWindowQuery query) {
switch (query) {
ENUM_CASE(NativeWindowQuery, Width);
ENUM_CASE(NativeWindowQuery, Height);
ENUM_CASE(NativeWindowQuery, Format);
ENUM_CASE(NativeWindowQuery, MinUndequeuedBuffers);
ENUM_CASE(NativeWindowQuery, QueuesToWindowComposer);
ENUM_CASE(NativeWindowQuery, ConcreteType);
ENUM_CASE(NativeWindowQuery, DefaultWidth);
ENUM_CASE(NativeWindowQuery, DefaultHeight);
ENUM_CASE(NativeWindowQuery, TransformHint);
ENUM_CASE(NativeWindowQuery, ConsumerRunningBehind);
ENUM_CASE(NativeWindowQuery, ConsumerUsageBits);
ENUM_CASE(NativeWindowQuery, StickyTransform);
default:
return "Unknown";
ENUM_STRING(NativeWindowQuery, {
ENUM_CASE(Width);
ENUM_CASE(Height);
ENUM_CASE(Format);
ENUM_CASE(MinUndequeuedBuffers);
ENUM_CASE(QueuesToWindowComposer);
ENUM_CASE(ConcreteType);
ENUM_CASE(DefaultWidth);
ENUM_CASE(DefaultHeight);
ENUM_CASE(TransformHint);
ENUM_CASE(ConsumerRunningBehind);
ENUM_CASE(ConsumerUsageBits);
ENUM_CASE(StickyTransform);
ENUM_CASE(MaxBufferCount);
});
}
}
}
#undef ENUM_CASE

View File

@ -35,14 +35,23 @@ namespace skyline::soc::host1x {
}
u32 Syncpoint::Increment() {
auto readValue{value.fetch_add(1, std::memory_order_acq_rel)}; // We don't want to constantly do redundant atomic loads
auto readValue{value.fetch_add(1, std::memory_order_acq_rel) + 1}; // We don't want to constantly do redundant atomic loads
std::lock_guard lock(mutex);
std::scoped_lock lock(mutex);
bool signalCondition{};
auto it{waiters.begin()};
while (it != waiters.end() && readValue >= it->threshold)
it++->callback();
while (it != waiters.end() && readValue >= it->threshold) {
auto &waiter{*it};
if (waiter.callback)
waiter.callback();
else
signalCondition = true;
it++;
}
waiters.erase(waiters.begin(), it);
if (signalCondition)
incrementCondition.notify_all();
return readValue;
@ -54,6 +63,11 @@ namespace skyline::soc::host1x {
return {};
std::unique_lock lock(mutex);
auto it{waiters.begin()};
while (it != waiters.end() && threshold >= it->threshold)
it++;
waiters.emplace(it, threshold, nullptr);
if (timeout == std::chrono::steady_clock::duration::max()) {
incrementCondition.wait(lock, [&] { return value.load(std::memory_order_relaxed) >= threshold; });
return true;

View File

@ -17,11 +17,11 @@ namespace skyline::soc::host1x {
std::atomic<u32> value{}; //!< An atomically-incrementing counter at the core of a syncpoint
std::mutex mutex; //!< Synchronizes insertions and deletions of waiters alongside locking the increment condition
std::condition_variable incrementCondition; //!< Signalled on every increment to the syncpoint
std::condition_variable incrementCondition; //!< Signalled on thresholds for waiters which are tied to Wait(...)
struct Waiter {
u32 threshold; //!< The syncpoint value to wait on to be reached
std::function<void()> callback; //!< The callback to do after the wait has ended
std::function<void()> callback; //!< The callback to do after the wait has ended, refers to cvar signal when nullptr
Waiter(u32 threshold, std::function<void()> callback) : threshold(threshold), callback(std::move(callback)) {}
};