mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-22 13:50:04 +00:00
hw/y2r: Templatize input/output formats. (#6717)
This commit is contained in:
parent
19107cec4b
commit
8b21b902f2
@ -9,6 +9,7 @@
|
|||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/color.h"
|
#include "common/color.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/microprofileui.h"
|
||||||
#include "common/vector_math.h"
|
#include "common/vector_math.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/service/y2r_u.h"
|
#include "core/hle/service/y2r_u.h"
|
||||||
@ -24,33 +25,33 @@ static const std::size_t TILE_SIZE = 8 * 8;
|
|||||||
using ImageTile = std::array<u32, TILE_SIZE>;
|
using ImageTile = std::array<u32, TILE_SIZE>;
|
||||||
|
|
||||||
/// Converts a image strip from the source YUV format into individual 8x8 RGB32 tiles.
|
/// Converts a image strip from the source YUV format into individual 8x8 RGB32 tiles.
|
||||||
static void ConvertYUVToRGB(InputFormat input_format, const u8* input_Y, const u8* input_U,
|
template <InputFormat input_format>
|
||||||
const u8* input_V, ImageTile output[], unsigned int width,
|
static void ConvertYUVToRGB(const u8* input_Y, const u8* input_U, const u8* input_V,
|
||||||
unsigned int height, const CoefficientSet& coefficients) {
|
ImageTile output[], unsigned int width, unsigned int height,
|
||||||
|
const CoefficientSet& coefficients) {
|
||||||
|
|
||||||
for (unsigned int y = 0; y < height; ++y) {
|
for (unsigned int y = 0; y < height; ++y) {
|
||||||
for (unsigned int x = 0; x < width; ++x) {
|
for (unsigned int x = 0; x < width; ++x) {
|
||||||
s32 Y = 0;
|
s32 Y;
|
||||||
s32 U = 0;
|
s32 U;
|
||||||
s32 V = 0;
|
s32 V;
|
||||||
switch (input_format) {
|
if constexpr (input_format == InputFormat::YUV422_Indiv8 ||
|
||||||
case InputFormat::YUV422_Indiv8:
|
input_format == InputFormat::YUV422_Indiv16) {
|
||||||
case InputFormat::YUV422_Indiv16:
|
|
||||||
Y = input_Y[y * width + x];
|
Y = input_Y[y * width + x];
|
||||||
U = input_U[(y * width + x) / 2];
|
U = input_U[(y * width + x) / 2];
|
||||||
V = input_V[(y * width + x) / 2];
|
V = input_V[(y * width + x) / 2];
|
||||||
break;
|
} else if constexpr (input_format == InputFormat::YUV420_Indiv8 ||
|
||||||
case InputFormat::YUV420_Indiv8:
|
input_format == InputFormat::YUV420_Indiv16) {
|
||||||
case InputFormat::YUV420_Indiv16:
|
|
||||||
Y = input_Y[y * width + x];
|
Y = input_Y[y * width + x];
|
||||||
U = input_U[((y / 2) * width + x) / 2];
|
U = input_U[((y / 2) * width + x) / 2];
|
||||||
V = input_V[((y / 2) * width + x) / 2];
|
V = input_V[((y / 2) * width + x) / 2];
|
||||||
break;
|
} else if constexpr (input_format == InputFormat::YUYV422_Interleaved) {
|
||||||
case InputFormat::YUYV422_Interleaved:
|
|
||||||
Y = input_Y[(y * width + x) * 2];
|
Y = input_Y[(y * width + x) * 2];
|
||||||
U = input_Y[(y * width + (x / 2) * 2) * 2 + 1];
|
U = input_Y[(y * width + (x / 2) * 2) * 2 + 1];
|
||||||
V = input_Y[(y * width + (x / 2) * 2) * 2 + 3];
|
V = input_Y[(y * width + (x / 2) * 2) * 2 + 3];
|
||||||
break;
|
} else {
|
||||||
|
UNREACHABLE_MSG("Unknown Y2R input format {}", input_format);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This conversion process is bit-exact with hardware, as far as could be tested.
|
// This conversion process is bit-exact with hardware, as far as could be tested.
|
||||||
@ -102,8 +103,9 @@ static void ReceiveData(Memory::MemorySystem& memory, u8* output, ConversionBuff
|
|||||||
|
|
||||||
/// Convert intermediate RGB32 format to the final output format while simulating an outgoing CDMA
|
/// Convert intermediate RGB32 format to the final output format while simulating an outgoing CDMA
|
||||||
/// transfer.
|
/// transfer.
|
||||||
|
template <OutputFormat output_format>
|
||||||
static void SendData(Memory::MemorySystem& memory, const u32* input, ConversionBuffer& buf,
|
static void SendData(Memory::MemorySystem& memory, const u32* input, ConversionBuffer& buf,
|
||||||
int amount_of_data, OutputFormat output_format, u8 alpha) {
|
int amount_of_data, u8 alpha) {
|
||||||
|
|
||||||
u8* output = memory.GetPointer(buf.address);
|
u8* output = memory.GetPointer(buf.address);
|
||||||
|
|
||||||
@ -113,23 +115,20 @@ static void SendData(Memory::MemorySystem& memory, const u32* input, ConversionB
|
|||||||
u32 color = *input++;
|
u32 color = *input++;
|
||||||
Common::Vec4<u8> col_vec{(u8)(color >> 24), (u8)(color >> 16), (u8)(color >> 8), alpha};
|
Common::Vec4<u8> col_vec{(u8)(color >> 24), (u8)(color >> 16), (u8)(color >> 8), alpha};
|
||||||
|
|
||||||
switch (output_format) {
|
if constexpr (output_format == OutputFormat::RGBA8) {
|
||||||
case OutputFormat::RGBA8:
|
|
||||||
Common::Color::EncodeRGBA8(col_vec, output);
|
Common::Color::EncodeRGBA8(col_vec, output);
|
||||||
output += 4;
|
output += 4;
|
||||||
break;
|
} else if constexpr (output_format == OutputFormat::RGB8) {
|
||||||
case OutputFormat::RGB8:
|
|
||||||
Common::Color::EncodeRGB8(col_vec, output);
|
Common::Color::EncodeRGB8(col_vec, output);
|
||||||
output += 3;
|
output += 3;
|
||||||
break;
|
} else if constexpr (output_format == OutputFormat::RGB5A1) {
|
||||||
case OutputFormat::RGB5A1:
|
|
||||||
Common::Color::EncodeRGB5A1(col_vec, output);
|
Common::Color::EncodeRGB5A1(col_vec, output);
|
||||||
output += 2;
|
output += 2;
|
||||||
break;
|
} else if constexpr (output_format == OutputFormat::RGB565) {
|
||||||
case OutputFormat::RGB565:
|
|
||||||
Common::Color::EncodeRGB565(col_vec, output);
|
Common::Color::EncodeRGB565(col_vec, output);
|
||||||
output += 2;
|
output += 2;
|
||||||
break;
|
} else {
|
||||||
|
UNREACHABLE_MSG("Unknown Y2R output format {}", output_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
amount_of_data -= 1;
|
amount_of_data -= 1;
|
||||||
@ -210,6 +209,8 @@ static void WriteTileToOutput(u32* output, const ImageTile& tile, int height, in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MICROPROFILE_DEFINE(Y2R_PerformConversion, "Y2R", "PerformConversion", MP_RGB(185, 66, 245));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a Y2R colorspace conversion.
|
* Performs a Y2R colorspace conversion.
|
||||||
*
|
*
|
||||||
@ -261,6 +262,8 @@ static void WriteTileToOutput(u32* output, const ImageTile& tile, int height, in
|
|||||||
* so they are believed to be invalid configurations anyway.
|
* so they are believed to be invalid configurations anyway.
|
||||||
*/
|
*/
|
||||||
void PerformConversion(Memory::MemorySystem& memory, ConversionConfiguration cvt) {
|
void PerformConversion(Memory::MemorySystem& memory, ConversionConfiguration cvt) {
|
||||||
|
MICROPROFILE_SCOPE(Y2R_PerformConversion);
|
||||||
|
|
||||||
ASSERT(cvt.input_line_width % 8 == 0);
|
ASSERT(cvt.input_line_width % 8 == 0);
|
||||||
ASSERT(cvt.block_alignment != BlockAlignment::Block8x8 || cvt.input_lines % 8 == 0);
|
ASSERT(cvt.block_alignment != BlockAlignment::Block8x8 || cvt.input_lines % 8 == 0);
|
||||||
// Tiles per row
|
// Tiles per row
|
||||||
@ -300,34 +303,47 @@ void PerformConversion(Memory::MemorySystem& memory, ConversionConfiguration cvt
|
|||||||
ReceiveData<1>(memory, input_Y, cvt.src_Y, row_data_size);
|
ReceiveData<1>(memory, input_Y, cvt.src_Y, row_data_size);
|
||||||
ReceiveData<1>(memory, input_U, cvt.src_U, row_data_size / 2);
|
ReceiveData<1>(memory, input_U, cvt.src_U, row_data_size / 2);
|
||||||
ReceiveData<1>(memory, input_V, cvt.src_V, row_data_size / 2);
|
ReceiveData<1>(memory, input_V, cvt.src_V, row_data_size / 2);
|
||||||
|
ConvertYUVToRGB<InputFormat::YUV422_Indiv8>(input_Y, input_U, input_V, tiles.get(),
|
||||||
|
cvt.input_line_width, row_height,
|
||||||
|
cvt.coefficients);
|
||||||
break;
|
break;
|
||||||
case InputFormat::YUV420_Indiv8:
|
case InputFormat::YUV420_Indiv8:
|
||||||
ReceiveData<1>(memory, input_Y, cvt.src_Y, row_data_size);
|
ReceiveData<1>(memory, input_Y, cvt.src_Y, row_data_size);
|
||||||
ReceiveData<1>(memory, input_U, cvt.src_U, row_data_size / 4);
|
ReceiveData<1>(memory, input_U, cvt.src_U, row_data_size / 4);
|
||||||
ReceiveData<1>(memory, input_V, cvt.src_V, row_data_size / 4);
|
ReceiveData<1>(memory, input_V, cvt.src_V, row_data_size / 4);
|
||||||
|
ConvertYUVToRGB<InputFormat::YUV420_Indiv8>(input_Y, input_U, input_V, tiles.get(),
|
||||||
|
cvt.input_line_width, row_height,
|
||||||
|
cvt.coefficients);
|
||||||
break;
|
break;
|
||||||
case InputFormat::YUV422_Indiv16:
|
case InputFormat::YUV422_Indiv16:
|
||||||
ReceiveData<2>(memory, input_Y, cvt.src_Y, row_data_size);
|
ReceiveData<2>(memory, input_Y, cvt.src_Y, row_data_size);
|
||||||
ReceiveData<2>(memory, input_U, cvt.src_U, row_data_size / 2);
|
ReceiveData<2>(memory, input_U, cvt.src_U, row_data_size / 2);
|
||||||
ReceiveData<2>(memory, input_V, cvt.src_V, row_data_size / 2);
|
ReceiveData<2>(memory, input_V, cvt.src_V, row_data_size / 2);
|
||||||
|
ConvertYUVToRGB<InputFormat::YUV422_Indiv16>(input_Y, input_U, input_V, tiles.get(),
|
||||||
|
cvt.input_line_width, row_height,
|
||||||
|
cvt.coefficients);
|
||||||
break;
|
break;
|
||||||
case InputFormat::YUV420_Indiv16:
|
case InputFormat::YUV420_Indiv16:
|
||||||
ReceiveData<2>(memory, input_Y, cvt.src_Y, row_data_size);
|
ReceiveData<2>(memory, input_Y, cvt.src_Y, row_data_size);
|
||||||
ReceiveData<2>(memory, input_U, cvt.src_U, row_data_size / 4);
|
ReceiveData<2>(memory, input_U, cvt.src_U, row_data_size / 4);
|
||||||
ReceiveData<2>(memory, input_V, cvt.src_V, row_data_size / 4);
|
ReceiveData<2>(memory, input_V, cvt.src_V, row_data_size / 4);
|
||||||
|
ConvertYUVToRGB<InputFormat::YUV420_Indiv16>(input_Y, input_U, input_V, tiles.get(),
|
||||||
|
cvt.input_line_width, row_height,
|
||||||
|
cvt.coefficients);
|
||||||
break;
|
break;
|
||||||
case InputFormat::YUYV422_Interleaved:
|
case InputFormat::YUYV422_Interleaved:
|
||||||
input_U = nullptr;
|
input_U = nullptr;
|
||||||
input_V = nullptr;
|
input_V = nullptr;
|
||||||
ReceiveData<1>(memory, input_Y, cvt.src_YUYV, row_data_size * 2);
|
ReceiveData<1>(memory, input_Y, cvt.src_YUYV, row_data_size * 2);
|
||||||
|
ConvertYUVToRGB<InputFormat::YUYV422_Interleaved>(input_Y, input_U, input_V,
|
||||||
|
tiles.get(), cvt.input_line_width,
|
||||||
|
row_height, cvt.coefficients);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE_MSG("Unknown Y2R input format {}", cvt.input_format);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note(yuriks): If additional optimization is required, input_format can be moved to a
|
|
||||||
// template parameter, so that its dispatch can be moved to outside the inner loop.
|
|
||||||
ConvertYUVToRGB(cvt.input_format, input_Y, input_U, input_V, tiles.get(),
|
|
||||||
cvt.input_line_width, row_height, cvt.coefficients);
|
|
||||||
|
|
||||||
u32* output_buffer = reinterpret_cast<u32*>(data_buffer.get());
|
u32* output_buffer = reinterpret_cast<u32*>(data_buffer.get());
|
||||||
|
|
||||||
for (std::size_t i = 0; i < num_tiles; ++i) {
|
for (std::size_t i = 0; i < num_tiles; ++i) {
|
||||||
@ -371,10 +387,31 @@ void PerformConversion(Memory::MemorySystem& memory, ConversionConfiguration cvt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note(yuriks): If additional optimization is required, output_format can be moved to a
|
switch (cvt.output_format) {
|
||||||
// template parameter, so that its dispatch can be moved to outside the inner loop.
|
case OutputFormat::RGBA8:
|
||||||
SendData(memory, reinterpret_cast<u32*>(data_buffer.get()), cvt.dst, (int)row_data_size,
|
SendData<OutputFormat::RGBA8>(memory, reinterpret_cast<u32*>(data_buffer.get()),
|
||||||
cvt.output_format, (u8)cvt.alpha);
|
cvt.dst, static_cast<int>(row_data_size),
|
||||||
|
static_cast<u8>(cvt.alpha));
|
||||||
|
break;
|
||||||
|
case OutputFormat::RGB8:
|
||||||
|
SendData<OutputFormat::RGB8>(memory, reinterpret_cast<u32*>(data_buffer.get()), cvt.dst,
|
||||||
|
static_cast<int>(row_data_size),
|
||||||
|
static_cast<u8>(cvt.alpha));
|
||||||
|
break;
|
||||||
|
case OutputFormat::RGB5A1:
|
||||||
|
SendData<OutputFormat::RGB5A1>(memory, reinterpret_cast<u32*>(data_buffer.get()),
|
||||||
|
cvt.dst, static_cast<int>(row_data_size),
|
||||||
|
static_cast<u8>(cvt.alpha));
|
||||||
|
break;
|
||||||
|
case OutputFormat::RGB565:
|
||||||
|
SendData<OutputFormat::RGB565>(memory, reinterpret_cast<u32*>(data_buffer.get()),
|
||||||
|
cvt.dst, static_cast<int>(row_data_size),
|
||||||
|
static_cast<u8>(cvt.alpha));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE_MSG("Unknown Y2R output format {}", cvt.output_format);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace HW::Y2R
|
} // namespace HW::Y2R
|
||||||
|
Loading…
Reference in New Issue
Block a user