diff --git a/include/services/y2r.hpp b/include/services/y2r.hpp index 17ceaafa9..0a1cae2fc 100644 --- a/include/services/y2r.hpp +++ b/include/services/y2r.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include "helpers.hpp" #include "kernel_types.hpp" @@ -50,6 +51,17 @@ class Y2RService { Block8x8 = 1, // Output buffer's pixels are morton swizzled. Used when outputting to a GPU texture. }; + // https://github.com/citra-emu/citra/blob/ac9d72a95ca9a60de8d39484a14aecf489d6d016/src/core/hle/service/cam/y2r_u.cpp#L33 + using CoefficientSet = std::array; + static constexpr std::array standardCoefficients{{ + {{0x100, 0x166, 0xB6, 0x58, 0x1C5, -0x166F, 0x10EE, -0x1C5B}}, // ITU_Rec601 + {{0x100, 0x193, 0x77, 0x2F, 0x1DB, -0x1933, 0xA7C, -0x1D51}}, // ITU_Rec709 + {{0x12A, 0x198, 0xD0, 0x64, 0x204, -0x1BDE, 0x10F2, -0x229B}}, // ITU_Rec601_Scaling + {{0x12A, 0x1CA, 0x88, 0x36, 0x21C, -0x1F04, 0x99C, -0x2421}}, // ITU_Rec709_Scaling + }}; + + CoefficientSet conversionCoefficients; // Current conversion coefficients + InputFormat inputFmt; OutputFormat outputFmt; Rotation rotation; @@ -75,6 +87,7 @@ class Y2RService { void setAlpha(u32 messagePointer); void setBlockAlignment(u32 messagePointer); + void setCoefficientParams(u32 messagePointer); void setInputFormat(u32 messagePointer); void setInputLineWidth(u32 messagePointer); void setInputLines(u32 messagePointer); @@ -88,6 +101,8 @@ class Y2RService { void setSpacialDithering(u32 messagePointer); void setStandardCoeff(u32 messagePointer); void setTemporalDithering(u32 messagePointer); + void getCoefficientParams(u32 messagePointer); + void getStandardCoefficientParams(u32 messagePointer); void startConversion(u32 messagePointer); void stopConversion(u32 messagePointer); diff --git a/src/core/services/y2r.cpp b/src/core/services/y2r.cpp index 40a6a04d6..b5daf6bb7 100644 --- a/src/core/services/y2r.cpp +++ b/src/core/services/y2r.cpp @@ -23,7 +23,10 @@ namespace Y2RCommands { GetInputLineWidth = 0x001B0000, SetInputLines = 0x001C0040, GetInputLines = 0x001D0000, + SetCoefficientParams = 0x001E0100, + GetCoefficientParams = 0x001F0000, SetStandardCoeff = 0x00200040, + GetStandardCoefficientParams = 0x00210040, SetAlpha = 0x00220040, StartConversion = 0x00260000, StopConversion = 0x00270000, @@ -50,6 +53,8 @@ void Y2RService::reset() { alpha = 0xFFFF; inputLines = 69; inputLineWidth = 420; + + conversionCoefficients.fill(0); } void Y2RService::handleSyncRequest(u32 messagePointer) { @@ -62,6 +67,7 @@ void Y2RService::handleSyncRequest(u32 messagePointer) { case Y2RCommands::GetInputLineWidth: getInputLineWidth(messagePointer); break; case Y2RCommands::GetOutputFormat: getOutputFormat(messagePointer); break; case Y2RCommands::GetTransferEndEvent: getTransferEndEvent(messagePointer); break; + case Y2RCommands::GetStandardCoefficientParams: getStandardCoefficientParams(messagePointer); break; case Y2RCommands::IsBusyConversion: isBusyConversion(messagePointer); break; case Y2RCommands::PingProcess: pingProcess(messagePointer); break; case Y2RCommands::SetAlpha: setAlpha(messagePointer); break; @@ -82,6 +88,10 @@ void Y2RService::handleSyncRequest(u32 messagePointer) { case Y2RCommands::SetTransferEndInterrupt: setTransferEndInterrupt(messagePointer); break; case Y2RCommands::StartConversion: [[likely]] startConversion(messagePointer); break; case Y2RCommands::StopConversion: stopConversion(messagePointer); break; + + // Intentionally break ordering a bit for less-used Y2R functions + case Y2RCommands::SetCoefficientParams: setCoefficientParams(messagePointer); break; + case Y2RCommands::GetCoefficientParams: getCoefficientParams(messagePointer); break; default: Helpers::panic("Y2R service requested. Command: %08X\n", command); } } @@ -97,6 +107,8 @@ void Y2RService::driverInitialize(u32 messagePointer) { log("Y2R::DriverInitialize\n"); mem.write32(messagePointer, IPC::responseHeader(0x2B, 1, 0)); mem.write32(messagePointer + 4, Result::Success); + + conversionCoefficients.fill(0); } void Y2RService::driverFinalize(u32 messagePointer) { @@ -276,6 +288,7 @@ void Y2RService::getInputLineWidth(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, inputLineWidth); } + void Y2RService::setInputLines(u32 messagePointer) { const u16 lines = mem.read16(messagePointer + 4); log("Y2R::SetInputLines (lines = %d)\n", lines); @@ -306,7 +319,7 @@ void Y2RService::setStandardCoeff(u32 messagePointer) { log("Y2R::SetStandardCoeff (coefficient = %d)\n", coeff); mem.write32(messagePointer, IPC::responseHeader(0x20, 1, 0)); - if (coeff > 3) { + if (coeff > 3) { // Invalid coefficient, should have an error code Helpers::panic("Y2R: Invalid standard coefficient (coefficient = %d)\n", coeff); } @@ -316,6 +329,52 @@ void Y2RService::setStandardCoeff(u32 messagePointer) { } } +void Y2RService::getStandardCoefficientParams(u32 messagePointer) { + const u32 coefficientIndex = mem.read32(messagePointer + 4); + log("Y2R::GetStandardCoefficientParams (coefficient = %d)\n", coefficientIndex); + + if (coefficientIndex > 3) { // Invalid coefficient, should have an error code + Helpers::panic("Y2R: Invalid standard coefficient (coefficient = %d)\n", coefficientIndex); + } else { + mem.write32(messagePointer, IPC::responseHeader(0x21, 5, 0)); + mem.write32(messagePointer + 4, Result::Success); + const auto& coeff = standardCoefficients[coefficientIndex]; + + // Write standard coefficient parameters to output buffer + for (int i = 0; i < 8; i++) { + const u32 pointer = messagePointer + 8 + i * sizeof(u16); // Pointer to write parameter to + mem.write16(pointer, coeff[i]); + } + } +} + +void Y2RService::setCoefficientParams(u32 messagePointer) { + log("Y2R::SetCoefficientParams\n"); + auto& coeff = conversionCoefficients; + + // Write coefficient parameters to output buffer + for (int i = 0; i < 8; i++) { + const u32 pointer = messagePointer + 4 + i * sizeof(u16); // Pointer to write parameter to + coeff[i] = mem.read16(pointer); + } + + mem.write32(messagePointer, IPC::responseHeader(0x1E, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + +void Y2RService::getCoefficientParams(u32 messagePointer) { + log("Y2R::GetCoefficientParams\n"); + mem.write32(messagePointer, IPC::responseHeader(0x1F, 5, 0)); + mem.write32(messagePointer + 4, Result::Success); + const auto& coeff = conversionCoefficients; + + // Write coefficient parameters to output buffer + for (int i = 0; i < 8; i++) { + const u32 pointer = messagePointer + 8 + i * sizeof(u16); // Pointer to write parameter to + mem.write16(pointer, coeff[i]); + } +} + void Y2RService::setSendingY(u32 messagePointer) { log("Y2R::SetSendingY\n"); Helpers::warn("Unimplemented Y2R::SetSendingY");