2023-01-21 20:37:04 +01:00
|
|
|
#include "retain_vars.hpp"
|
|
|
|
#include "shaders/ColorShader.h"
|
|
|
|
#include "shaders/Texture2DShader.h"
|
|
|
|
#include <function_patcher/fpatching_defines.h>
|
|
|
|
#include <gx2/state.h>
|
|
|
|
|
|
|
|
bool drawScreenshotSavedTexture(const GX2ColorBuffer *colorBuffer, GX2ScanTarget scan_target);
|
|
|
|
|
|
|
|
GX2ColorBuffer lastTVColorBuffer;
|
|
|
|
GX2ColorBuffer lastDRCColorBuffer;
|
|
|
|
DECL_FUNCTION(void, GX2GetCurrentScanBuffer, GX2ScanTarget scanTarget, GX2ColorBuffer *cb) {
|
|
|
|
real_GX2GetCurrentScanBuffer(scanTarget, cb);
|
|
|
|
if (scanTarget == GX2_SCAN_TARGET_TV) {
|
|
|
|
memcpy(&lastTVColorBuffer, cb, sizeof(GX2ColorBuffer));
|
|
|
|
} else {
|
|
|
|
memcpy(&lastDRCColorBuffer, cb, sizeof(GX2ColorBuffer));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DECL_FUNCTION(void, GX2SetContextState, GX2ContextState *curContext) {
|
|
|
|
real_GX2SetContextState(curContext);
|
|
|
|
|
|
|
|
gOriginalContextState = curContext;
|
|
|
|
}
|
|
|
|
|
|
|
|
DECL_FUNCTION(void, GX2SetupContextStateEx, GX2ContextState *state, BOOL unk1) {
|
|
|
|
real_GX2SetupContextStateEx(state, unk1);
|
|
|
|
gOriginalContextState = state;
|
|
|
|
DEBUG_FUNCTION_LINE_VERBOSE("gOriginalContextState = %08X", state);
|
|
|
|
}
|
|
|
|
|
|
|
|
DECL_FUNCTION(void, GX2SetTVBuffer, void *buffer, uint32_t buffer_size, int32_t tv_render_mode, GX2SurfaceFormat surface_format, GX2BufferingMode buffering_mode) {
|
|
|
|
DEBUG_FUNCTION_LINE_VERBOSE("Set TV Buffer format to 0x%08X", surface_format);
|
|
|
|
gTVSurfaceFormat = surface_format;
|
|
|
|
|
|
|
|
return real_GX2SetTVBuffer(buffer, buffer_size, tv_render_mode, surface_format, buffering_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
DECL_FUNCTION(void, GX2SetDRCBuffer, void *buffer, uint32_t buffer_size, uint32_t drc_mode, GX2SurfaceFormat surface_format, GX2BufferingMode buffering_mode) {
|
|
|
|
DEBUG_FUNCTION_LINE_VERBOSE("Set DRC Buffer format to 0x%08X", surface_format);
|
|
|
|
gDRCSurfaceFormat = surface_format;
|
|
|
|
|
|
|
|
return real_GX2SetDRCBuffer(buffer, buffer_size, drc_mode, surface_format, buffering_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
void drawIntoColorBuffer(const GX2ColorBuffer *colorBuffer, OverlayFrame *overlayFrame, GX2ScanTarget scan_target) {
|
|
|
|
real_GX2SetContextState(gContextState);
|
|
|
|
|
|
|
|
GX2SetDefaultState();
|
|
|
|
|
|
|
|
GX2SetColorBuffer((GX2ColorBuffer *) colorBuffer, GX2_RENDER_TARGET_0);
|
|
|
|
GX2SetViewport(0.0f, 0.0f, colorBuffer->surface.width, colorBuffer->surface.height, 0.0f, 1.0f);
|
|
|
|
GX2SetScissor(0, 0, colorBuffer->surface.width, colorBuffer->surface.height);
|
|
|
|
|
|
|
|
GX2SetDepthOnlyControl(GX2_FALSE, GX2_FALSE, GX2_COMPARE_FUNC_NEVER);
|
|
|
|
GX2SetAlphaTest(GX2_TRUE, GX2_COMPARE_FUNC_GREATER, 0.0f);
|
|
|
|
GX2SetColorControl(GX2_LOGIC_OP_COPY, GX2_ENABLE, GX2_DISABLE, GX2_ENABLE);
|
|
|
|
auto outputFormat = scan_target ? gTVSurfaceFormat : gDRCSurfaceFormat;
|
|
|
|
overlayFrame->draw(outputFormat & 0x400);
|
|
|
|
GX2Flush();
|
|
|
|
|
|
|
|
real_GX2SetContextState(gOriginalContextState);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void drawScreenshotSavedTexture2(GX2ColorBuffer *colorBuffer, GX2ScanTarget scan_target) {
|
|
|
|
if (gOverlayFrame->empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
drawIntoColorBuffer(colorBuffer, gOverlayFrame, scan_target);
|
|
|
|
}
|
|
|
|
|
|
|
|
DECL_FUNCTION(void, GX2CopyColorBufferToScanBuffer, const GX2ColorBuffer *colorBuffer, GX2ScanTarget scan_target) {
|
|
|
|
gDrawReady = true;
|
|
|
|
if (drawScreenshotSavedTexture(colorBuffer, scan_target)) {
|
|
|
|
// if it returns true we don't need to call GX2CopyColorBufferToScanBuffer
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
real_GX2CopyColorBufferToScanBuffer(colorBuffer, scan_target);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool drawScreenshotSavedTexture(const GX2ColorBuffer *colorBuffer, GX2ScanTarget scan_target) {
|
|
|
|
if (gOverlayFrame->empty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
GX2ColorBuffer cb;
|
|
|
|
GX2InitColorBuffer(&cb,
|
|
|
|
colorBuffer->surface.dim,
|
|
|
|
colorBuffer->surface.width,
|
|
|
|
colorBuffer->surface.height,
|
|
|
|
colorBuffer->surface.depth,
|
|
|
|
colorBuffer->surface.format,
|
|
|
|
colorBuffer->surface.aa,
|
|
|
|
colorBuffer->surface.tileMode,
|
|
|
|
colorBuffer->surface.swizzle,
|
|
|
|
colorBuffer->aaBuffer,
|
|
|
|
colorBuffer->aaSize);
|
|
|
|
|
|
|
|
cb.surface.image = colorBuffer->surface.image;
|
|
|
|
|
|
|
|
drawIntoColorBuffer(&cb, gOverlayFrame, scan_target);
|
|
|
|
|
|
|
|
real_GX2CopyColorBufferToScanBuffer(&cb, scan_target);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
DECL_FUNCTION(void, GX2Init, uint32_t attributes) {
|
|
|
|
real_GX2Init(attributes);
|
|
|
|
if (!gOverlayInitDone) {
|
2024-03-01 09:51:47 +01:00
|
|
|
std::lock_guard overlay_lock(gOverlayFrameMutex);
|
2023-01-21 20:37:04 +01:00
|
|
|
DEBUG_FUNCTION_LINE_VERBOSE("Init Overlay");
|
|
|
|
gOverlayFrame = new (std::nothrow) OverlayFrame(1280.0f, 720.0f);
|
|
|
|
if (!gOverlayFrame) {
|
2024-03-01 09:58:19 +01:00
|
|
|
OSFatal("NotificationModule: Failed to alloc gOverlayFrame");
|
2023-01-21 20:37:04 +01:00
|
|
|
}
|
|
|
|
|
2024-03-01 09:51:47 +01:00
|
|
|
// Add notification that had been called before the overlay was ready
|
|
|
|
for (const auto ¬ification : gOverlayQueueDuringStartup) {
|
|
|
|
gOverlayFrame->addNotification(notification);
|
|
|
|
}
|
|
|
|
gOverlayQueueDuringStartup.clear();
|
|
|
|
|
2023-01-21 20:37:04 +01:00
|
|
|
// Allocate shader.
|
2024-03-01 09:58:19 +01:00
|
|
|
if (ColorShader::instance() == nullptr) {
|
|
|
|
OSFatal("NotificationModule: Failed to alloc ColorShader");
|
|
|
|
}
|
|
|
|
if (Texture2DShader::instance() == nullptr) {
|
|
|
|
OSFatal("NotificationModule: Failed to alloc Texture2DShader");
|
|
|
|
}
|
2023-01-21 20:37:04 +01:00
|
|
|
|
|
|
|
// has been allocated in WUMS INIT
|
|
|
|
if (!gContextState) {
|
2024-03-01 09:58:19 +01:00
|
|
|
OSFatal("NotificationModule: Failed to alloc gContextState");
|
2023-01-21 20:37:04 +01:00
|
|
|
}
|
|
|
|
real_GX2SetupContextStateEx(gContextState, GX2_TRUE);
|
|
|
|
DCInvalidateRange(gContextState, sizeof(GX2ContextState)); // Important!
|
|
|
|
gOverlayInitDone = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DECL_FUNCTION(void, GX2MarkScanBufferCopied, GX2ScanTarget scan_target) {
|
|
|
|
gDrawReady = true;
|
|
|
|
if (scan_target == GX2_SCAN_TARGET_TV) {
|
|
|
|
drawScreenshotSavedTexture2(&lastTVColorBuffer, scan_target);
|
|
|
|
} else {
|
|
|
|
drawScreenshotSavedTexture2(&lastDRCColorBuffer, scan_target);
|
|
|
|
}
|
|
|
|
|
|
|
|
real_GX2MarkScanBufferCopied(scan_target);
|
|
|
|
}
|
|
|
|
|
|
|
|
DECL_FUNCTION(void, GX2SwapScanBuffers, void) {
|
|
|
|
if (gDrawReady && !gOverlayFrame->empty()) {
|
|
|
|
gOverlayFrame->process();
|
|
|
|
gOverlayFrame->updateEffects();
|
|
|
|
}
|
|
|
|
real_GX2SwapScanBuffers();
|
|
|
|
}
|
|
|
|
|
|
|
|
function_replacement_data_t function_replacements[] = {
|
|
|
|
REPLACE_FUNCTION(GX2GetCurrentScanBuffer, LIBRARY_GX2, GX2GetCurrentScanBuffer),
|
|
|
|
REPLACE_FUNCTION(GX2SetContextState, LIBRARY_GX2, GX2SetContextState),
|
|
|
|
REPLACE_FUNCTION(GX2SetupContextStateEx, LIBRARY_GX2, GX2SetupContextStateEx),
|
|
|
|
REPLACE_FUNCTION(GX2SetTVBuffer, LIBRARY_GX2, GX2SetTVBuffer),
|
|
|
|
REPLACE_FUNCTION(GX2SetDRCBuffer, LIBRARY_GX2, GX2SetDRCBuffer),
|
|
|
|
REPLACE_FUNCTION(GX2CopyColorBufferToScanBuffer, LIBRARY_GX2, GX2CopyColorBufferToScanBuffer),
|
|
|
|
REPLACE_FUNCTION(GX2Init, LIBRARY_GX2, GX2Init),
|
|
|
|
REPLACE_FUNCTION(GX2MarkScanBufferCopied, LIBRARY_GX2, GX2MarkScanBufferCopied),
|
|
|
|
REPLACE_FUNCTION(GX2SwapScanBuffers, LIBRARY_GX2, GX2SwapScanBuffers),
|
|
|
|
};
|
|
|
|
|
|
|
|
uint32_t function_replacements_size = sizeof(function_replacements) / sizeof(function_replacement_data_t);
|