Skip to content

Commit b090476

Browse files
committed
Add FPS overlay, clean-up rendering code and add meta sim fix
This does rework a lot of the AMD rendering fixes that was merged earlier, but I made sure that 3-4 people tested it to work on their AMD GPU still. Some upcoming changes will tackle a lot of the rendering features again, so I wanted to clean up the code since it was a bit too verbose, especially regarding the image layout. For our purposes, GPU rendering isn't really ever the bottleneck, and maintainability is of high importance due to the complications with interop and being a Vulkan hook. That's why I decided on using general image layouts, which most desktop drivers seem to ignore anyway judging by the very minor to no performance uplift when applied properly, at least on main GPU vendors on desktop. It also corrects some mistakes, such as that transitioning OpenXR's swapchains from general to present, despite them already being in that image layout according to the spec.
1 parent b73b0eb commit b090476

11 files changed

Lines changed: 349 additions & 495 deletions

File tree

src/hooking/camera.cpp

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -561,23 +561,10 @@ void CemuHooks::hook_GetEventName(PPCInterpreter_t* hCPU) {
561561
hCPU->instructionPointer = hCPU->sprNew.LR;
562562

563563
uint32_t isEventActive = hCPU->gpr[3];
564-
uint32_t ppcAddress = hCPU->gpr[4];
564+
uint32_t eventNamePtr = hCPU->gpr[4];
565565

566566
if (isEventActive) {
567-
// Validate PPC address is in valid game memory range
568-
if (ppcAddress < 0x10000000 || ppcAddress > 0x50000000) {
569-
return;
570-
}
571-
572-
const char* eventNamePtr = (const char*)(ppcAddress + s_memoryBaseAddress);
573-
574-
// Validate the string starts with a printable ASCII character
575-
unsigned char firstChar = static_cast<unsigned char>(eventNamePtr[0]);
576-
if (firstChar < 0x20 || firstChar > 0x7E) {
577-
return;
578-
}
579-
580-
std::string eventName = std::string(eventNamePtr);
567+
std::string eventName = std::string((char*)s_memoryBaseAddress + eventNamePtr);
581568
if (s_currentEvent == eventName) {
582569
return;
583570
}

src/hooking/framebuffer.cpp

Lines changed: 72 additions & 123 deletions
Large diffs are not rendered by default.

src/rendering/openxr.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#include "instance.h"
33

44
static XrBool32 XR_DebugUtilsMessengerCallback(XrDebugUtilsMessageSeverityFlagsEXT messageSeverity, XrDebugUtilsMessageTypeFlagsEXT messageType, const XrDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData) {
5-
//Log::print("[OpenXR Debug Utils] Function {}: {}", callbackData->functionName, callbackData->message);
5+
Log::print<XR_DEBUGUTILS>("[XR Debug Utils] Function {}: {}", callbackData->functionName, callbackData->message);
66
return XR_FALSE;
77
}
88

@@ -37,7 +37,7 @@ OpenXR::OpenXR() {
3737
}
3838
else if (strcmp(extensionProperties.extensionName, XR_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0) {
3939
#if defined(_DEBUG)
40-
debugUtilsSupported = Log::isLogTypeEnabled<VERBOSE>();
40+
debugUtilsSupported = Log::isLogTypeEnabled<XR_DEBUGUTILS>();
4141
#endif
4242
}
4343
}
@@ -53,10 +53,8 @@ OpenXR::OpenXR() {
5353
if (!timeConvSupported) {
5454
Log::print<WARNING>("OpenXR runtime doesn't support converting time from/to XrTime (XR_KHR_WIN32_CONVERT_PERFORMANCE_COUNTER_TIME). Not required, as of this version.");
5555
}
56-
if (!debugUtilsSupported) {
57-
#if defined(_DEBUG)
56+
if (!debugUtilsSupported && Log::isLogTypeEnabled<XR_DEBUGUTILS>()) {
5857
Log::print<INFO>("OpenXR runtime doesn't support debug utils (XR_EXT_DEBUG_UTILS)! Errors/debug information will no longer be able to be shown!");
59-
#endif
6058
}
6159

6260
std::vector<const char*> enabledExtensions = { XR_KHR_D3D12_ENABLE_EXTENSION_NAME, XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME, XR_KHR_WIN32_CONVERT_PERFORMANCE_COUNTER_TIME_EXTENSION_NAME };
@@ -131,7 +129,8 @@ OpenXR::OpenXR() {
131129

132130
m_capabilities.isOculusLinkRuntime = std::string(properties.runtimeName) == "Oculus";
133131
Log::print<INFO>(" - Using Meta Quest Link OpenXR runtime: {}", m_capabilities.isOculusLinkRuntime ? "Yes" : "No");
134-
132+
133+
m_capabilities.isMetaSimulator = std::string(properties.runtimeName).find("Meta XR Simulator") != std::string::npos;
135134
}
136135

137136
OpenXR::~OpenXR() {

src/rendering/openxr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class OpenXR {
2121
bool supportsPositional;
2222
bool supportsMutatableFOV;
2323
bool isOculusLinkRuntime;
24+
bool isMetaSimulator;
2425
} m_capabilities = {};
2526

2627
union InputState {

src/rendering/renderer.cpp

Lines changed: 53 additions & 95 deletions
Large diffs are not rendered by default.

src/rendering/renderer.h

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ class RND_Renderer {
8585
return ToMat4(middlePos, middleOri);
8686
};
8787

88+
double GetLastFrameWorkTimeMs() const { return m_lastFrameWorkTimeMs; }
89+
double GetLastWaitTimeMs() const { return m_lastWaitTimeMs; }
90+
double GetLastFrameTimeMs() const { return m_lastFrameTimeMs; }
91+
double GetPredictedDisplayPeriodMs() const { return m_predictedDisplayPeriodMs; }
92+
double GetLastOverheadMs() const { return m_lastOverheadMs; }
93+
8894
void On3DColorCopied(OpenXR::EyeSide side, long frameIdx) {
8995
m_renderFrames[frameIdx].copiedColor[side] = true;
9096
if (!m_renderFrames[frameIdx].views.has_value()) m_renderFrames[frameIdx].views = m_currViews;
@@ -104,11 +110,11 @@ class RND_Renderer {
104110

105111
class Layer3D {
106112
public:
107-
explicit Layer3D(VkExtent2D extent);
113+
explicit Layer3D(VkExtent2D inputRes, VkExtent2D outputRes);
108114
~Layer3D();
109115

110-
SharedTexture* CopyColorToLayer(OpenXR::EyeSide side, VkCommandBuffer copyCmdBuffer, VkImage image, long frameIdx, VkImageLayout srcImageLayout);
111-
SharedTexture* CopyDepthToLayer(OpenXR::EyeSide side, VkCommandBuffer copyCmdBuffer, VkImage image, long frameIdx, VkImageLayout srcImageLayout);
116+
SharedTexture* CopyColorToLayer(OpenXR::EyeSide side, VkCommandBuffer copyCmdBuffer, VkImage image, long frameIdx);
117+
SharedTexture* CopyDepthToLayer(OpenXR::EyeSide side, VkCommandBuffer copyCmdBuffer, VkImage image, long frameIdx);
112118
void PrepareRendering(OpenXR::EyeSide side);
113119
void StartRendering();
114120
void Render(OpenXR::EyeSide side, long frameIdx);
@@ -133,10 +139,10 @@ class RND_Renderer {
133139

134140
class Layer2D {
135141
public:
136-
explicit Layer2D(VkExtent2D extent);
142+
explicit Layer2D(VkExtent2D inputRes, VkExtent2D outputRes);
137143
~Layer2D();
138144

139-
SharedTexture* CopyColorToLayer(VkCommandBuffer copyCmdBuffer, VkImage image, long frameIdx, VkImageLayout srcImageLayout);
145+
SharedTexture* CopyColorToLayer(VkCommandBuffer copyCmdBuffer, VkImage image, long frameIdx);
140146
// AMD GPU FIX: With incrementing values, Vulkan signals odd values (1,3,5...), D3D12 signals even values (2,4,6...)
141147
// Texture is ready for D3D12 when Vulkan has signaled (odd value > 0)
142148
bool IsTextureReady(long frameIdx) const {
@@ -166,9 +172,8 @@ class RND_Renderer {
166172
bool ShouldBlockGameInput() { return ImGui::GetIO().WantCaptureKeyboard; }
167173

168174
void BeginFrame(long frameIdx, bool renderBackground);
169-
// AMD GPU FIX: Added srcLayout parameter to specify the actual source image layout
170-
static void Draw3DLayerAsBackground(VkCommandBuffer cb, VkImage srcImage, float aspectRatio, long frameIdx, VkImageLayout srcLayout);
171-
static void DrawHUDLayerAsBackground(VkCommandBuffer cb, VkImage srcImage, long frameIdx, VkImageLayout srcLayout);
175+
static void Draw3DLayerAsBackground(VkCommandBuffer cb, VkImage srcImage, float aspectRatio, long frameIdx);
176+
static void DrawHUDLayerAsBackground(VkCommandBuffer cb, VkImage srcImage, long frameIdx);
172177
void Update();
173178
void Render();
174179
void DrawAndCopyToImage(VkCommandBuffer cb, VkImage destImage, long frameIdx);
@@ -181,6 +186,9 @@ class RND_Renderer {
181186
HWND m_cemuRenderWindow = nullptr;
182187

183188
VkSampler m_sampler = VK_NULL_HANDLE;
189+
190+
bool m_showAppMS = false;
191+
bool m_wasF3Pressed = false;
184192
};
185193

186194
std::unique_ptr<Layer3D> m_layer3D;
@@ -205,4 +213,17 @@ class RND_Renderer {
205213

206214
std::atomic_bool m_isInitialized = false;
207215
std::atomic_bool m_presented2DLastFrame = false;
216+
217+
// Full-frame timing derived from OpenXR timestamps (XrTime is in nanoseconds)
218+
XrTime m_lastPredictedDisplayTime = 0;
219+
220+
std::chrono::high_resolution_clock::time_point m_frameStartTime;
221+
222+
double m_lastFrameWorkTimeMs = 0.0;
223+
double m_lastWaitTimeMs = 0.0;
224+
225+
// Derived from OpenXR timestamps
226+
double m_lastFrameTimeMs = 0.0;
227+
double m_predictedDisplayPeriodMs = 0.0;
228+
double m_lastOverheadMs = 0.0;
208229
};

src/rendering/swapchain.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ ID3D12Resource* Swapchain<T>::StartRendering() {
6060
if (XrResult waitResult = xrWaitSwapchainImage(m_swapchain, &waitSwapchainInfo); waitResult == XR_TIMEOUT_EXPIRED || XR_FAILED(waitResult)) {
6161
checkXRResult(waitResult, "Failed to wait for swapchain image!");
6262
}
63+
6364
return m_swapchainTextures[m_swapchainImageIdx].Get();
6465
}
6566

0 commit comments

Comments
 (0)