summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Yuri Kunde Schlesner2015-05-21 23:27:48 -0300
committerGravatar Yuri Kunde Schlesner2015-05-22 17:57:21 -0300
commit9108482888ae66644e253fb104411746dc82b635 (patch)
treeb7d6cb64c69e069e89616e58c0e5ea6ba6a9cfc1 /src
parentMerge pull request #794 from linkmauve/resource-limit (diff)
downloadyuzu-9108482888ae66644e253fb104411746dc82b635.tar.gz
yuzu-9108482888ae66644e253fb104411746dc82b635.tar.xz
yuzu-9108482888ae66644e253fb104411746dc82b635.zip
Service::Y2R: Support for grayscale decoding of specific formats
Implements unrotated planar YUV 4:2:0 -> RGB24 conversions in Y2R. Currently only the Y (luma) channel is used, so the results don't contain color. This will be added in a later PR at some point. This is enough to get all currently know Moflex videos to decode. (Some don't display on-screen due to seemingly unrelated reasons.) Thanks to @archshift for doing the initial implementation which I cleaned up and then fixed the 8x8 block mode.
Diffstat (limited to 'src')
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/core/hle/service/y2r_u.cpp300
3 files changed, 267 insertions, 35 deletions
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index bd2c6a153..6ca8cb78d 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -45,6 +45,7 @@ namespace Log {
45 SUB(Service, DSP) \ 45 SUB(Service, DSP) \
46 SUB(Service, HID) \ 46 SUB(Service, HID) \
47 SUB(Service, SOC) \ 47 SUB(Service, SOC) \
48 SUB(Service, Y2R) \
48 CLS(HW) \ 49 CLS(HW) \
49 SUB(HW, Memory) \ 50 SUB(HW, Memory) \
50 SUB(HW, LCD) \ 51 SUB(HW, LCD) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index fd87ddbe6..d720d7fe0 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -65,6 +65,7 @@ enum class Class : ClassType {
65 Service_DSP, ///< The DSP (DSP control) service 65 Service_DSP, ///< The DSP (DSP control) service
66 Service_HID, ///< The HID (User input) service 66 Service_HID, ///< The HID (User input) service
67 Service_SOC, ///< The SOC (Socket) service 67 Service_SOC, ///< The SOC (Socket) service
68 Service_Y2R, ///< The Y2R (YUV to RGB conversion) service
68 HW, ///< Low-level hardware emulation 69 HW, ///< Low-level hardware emulation
69 HW_Memory, ///< Memory-map and address translation 70 HW_Memory, ///< Memory-map and address translation
70 HW_LCD, ///< LCD register emulation 71 HW_LCD, ///< LCD register emulation
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp
index edc443611..ce822e990 100644
--- a/src/core/hle/service/y2r_u.cpp
+++ b/src/core/hle/service/y2r_u.cpp
@@ -1,85 +1,314 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring>
6
5#include "common/logging/log.h" 7#include "common/logging/log.h"
6 8
7#include "core/hle/hle.h" 9#include "core/hle/hle.h"
8#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
9#include "core/hle/service/y2r_u.h" 11#include "core/hle/service/y2r_u.h"
12#include "video_core/utils.h"
10 13
11//////////////////////////////////////////////////////////////////////////////////////////////////// 14////////////////////////////////////////////////////////////////////////////////////////////////////
12// Namespace Y2R_U 15// Namespace Y2R_U
13 16
14namespace Y2R_U { 17namespace Y2R_U {
15 18
19enum class InputFormat {
20 /// 8-bit input, with YUV components in separate planes and using 4:2:2 subsampling.
21 YUV422_Indiv8 = 0,
22 /// 8-bit input, with YUV components in separate planes and using 4:2:0 subsampling.
23 YUV420_Indiv8 = 1,
24
25 YUV422_INDIV_16 = 2,
26 YUV420_INDIV_16 = 3,
27 YUV422_BATCH = 4,
28};
29
30enum class OutputFormat {
31 Rgb32 = 0,
32 Rgb24 = 1,
33 Rgb16_555 = 2,
34 Rgb16_565 = 3,
35};
36
37enum class Rotation {
38 None = 0,
39 Clockwise_90 = 1,
40 Clockwise_180 = 2,
41 Clockwise_270 = 3,
42};
43
44enum class BlockAlignment {
45 /// Image is output in linear format suitable for use as a framebuffer.
46 Linear = 0,
47 /// Image is output in tiled PICA format, suitable for use as a texture.
48 Block8x8 = 1,
49};
50
51enum class StandardCoefficient {
52 ITU_Rec601 = 0,
53 ITU_Rec709 = 1,
54 ITU_Rec601_Scaling = 2,
55 ITU_Rec709_Scaling = 3,
56};
57
16static Kernel::SharedPtr<Kernel::Event> completion_event; 58static Kernel::SharedPtr<Kernel::Event> completion_event;
17 59
18/** 60struct ConversionParameters {
19 * Y2R_U::IsBusyConversion service function 61 InputFormat input_format;
20 * Outputs: 62 OutputFormat output_format;
21 * 1 : Result of function, 0 on success, otherwise error code 63 Rotation rotation;
22 * 2 : Whether the current conversion is of type busy conversion (?) 64 BlockAlignment alignment;
23 */ 65 u16 input_line_width;
24static void IsBusyConversion(Service::Interface* self) { 66 u16 input_lines;
67
68 // Input parameters for the Y (luma) plane
69 VAddr srcY_address;
70 u32 srcY_image_size;
71 u16 srcY_transfer_unit;
72 u16 srcY_stride;
73
74 // Output parameters for the conversion results
75 VAddr dst_address;
76 u32 dst_image_size;
77 u16 dst_transfer_unit;
78 u16 dst_stride;
79};
80
81static ConversionParameters conversion_params;
82
83static void SetInputFormat(Service::Interface* self) {
25 u32* cmd_buff = Kernel::GetCommandBuffer(); 84 u32* cmd_buff = Kernel::GetCommandBuffer();
26 85
27 cmd_buff[1] = RESULT_SUCCESS.raw;; 86 conversion_params.input_format = static_cast<InputFormat>(cmd_buff[1]);
28 cmd_buff[2] = 0; 87 LOG_DEBUG(Service_Y2R, "called input_format=%u", conversion_params.input_format);
29 88
30 LOG_WARNING(Service, "(STUBBED) called"); 89 cmd_buff[1] = RESULT_SUCCESS.raw;
90}
91
92static void SetOutputFormat(Service::Interface* self) {
93 u32* cmd_buff = Kernel::GetCommandBuffer();
94
95 conversion_params.output_format = static_cast<OutputFormat>(cmd_buff[1]);
96 LOG_DEBUG(Service_Y2R, "called output_format=%u", conversion_params.output_format);
97
98 cmd_buff[1] = RESULT_SUCCESS.raw;
99}
100
101static void SetRotation(Service::Interface* self) {
102 u32* cmd_buff = Kernel::GetCommandBuffer();
103
104 conversion_params.rotation = static_cast<Rotation>(cmd_buff[1]);
105 LOG_DEBUG(Service_Y2R, "called rotation=%u", conversion_params.rotation);
106
107 cmd_buff[1] = RESULT_SUCCESS.raw;
108}
109
110static void SetBlockAlignment(Service::Interface* self) {
111 u32* cmd_buff = Kernel::GetCommandBuffer();
112
113 conversion_params.alignment = static_cast<BlockAlignment>(cmd_buff[1]);
114 LOG_DEBUG(Service_Y2R, "called alignment=%u", conversion_params.alignment);
115
116 cmd_buff[1] = RESULT_SUCCESS.raw;
31} 117}
32 118
33/** 119/**
34 * Y2R_U::GetTransferEndEvent service function 120* Y2R_U::GetTransferEndEvent service function
35 * Outputs: 121* Outputs:
36 * 1 : Result of function, 0 on success, otherwise error code 122* 1 : Result of function, 0 on success, otherwise error code
37 * 3 : The handle of the completion event 123* 3 : The handle of the completion event
38 */ 124*/
39static void GetTransferEndEvent(Service::Interface* self) { 125static void GetTransferEndEvent(Service::Interface* self) {
40 u32* cmd_buff = Kernel::GetCommandBuffer(); 126 u32* cmd_buff = Kernel::GetCommandBuffer();
41 127
42 cmd_buff[1] = RESULT_SUCCESS.raw; 128 cmd_buff[1] = RESULT_SUCCESS.raw;
43 cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); 129 cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom();
130 LOG_DEBUG(Service_Y2R, "called");
131}
132
133static void SetSendingY(Service::Interface* self) {
134 u32* cmd_buff = Kernel::GetCommandBuffer();
135
136 conversion_params.srcY_address = cmd_buff[1];
137 conversion_params.srcY_image_size = cmd_buff[2];
138 conversion_params.srcY_transfer_unit = cmd_buff[3];
139 conversion_params.srcY_stride = cmd_buff[4];
140 u32 src_process_handle = cmd_buff[6];
141 LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, "
142 "src_process_handle=0x%08X", conversion_params.srcY_image_size,
143 conversion_params.srcY_transfer_unit, conversion_params.srcY_stride, src_process_handle);
144
145 cmd_buff[1] = RESULT_SUCCESS.raw;
146}
147
148static void SetReceiving(Service::Interface* self) {
149 u32* cmd_buff = Kernel::GetCommandBuffer();
150
151 conversion_params.dst_address = cmd_buff[1];
152 conversion_params.dst_image_size = cmd_buff[2];
153 conversion_params.dst_transfer_unit = cmd_buff[3];
154 conversion_params.dst_stride = cmd_buff[4];
155 u32 dst_process_handle = cmd_buff[6];
156 LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, "
157 "dst_process_handle=0x%08X", conversion_params.dst_image_size,
158 conversion_params.dst_transfer_unit, conversion_params.dst_stride,
159 dst_process_handle);
160
161 cmd_buff[1] = RESULT_SUCCESS.raw;
162}
163
164static void SetInputLineWidth(Service::Interface* self) {
165 u32* cmd_buff = Kernel::GetCommandBuffer();
166
167 conversion_params.input_line_width = cmd_buff[1];
168 LOG_DEBUG(Service_Y2R, "input_line_width=%u", conversion_params.input_line_width);
169
170 cmd_buff[1] = RESULT_SUCCESS.raw;
171}
172
173static void SetInputLines(Service::Interface* self) {
174 u32* cmd_buff = Kernel::GetCommandBuffer();
175
176 conversion_params.input_lines = cmd_buff[1];
177 LOG_DEBUG(Service_Y2R, "input_line_number=%u", conversion_params.input_lines);
178
179 cmd_buff[1] = RESULT_SUCCESS.raw;
44} 180}
45 181
46/**
47 * Starts a YUV -> RGB conversion
48 */
49static void StartConversion(Service::Interface* self) { 182static void StartConversion(Service::Interface* self) {
50 u32* cmd_buff = Kernel::GetCommandBuffer(); 183 u32* cmd_buff = Kernel::GetCommandBuffer();
51 184
52 // TODO(bunnei): This is hack to indicate to the game that the conversion has immediately 185 const u8* srcY_buffer = Memory::GetPointer(conversion_params.srcY_address);
53 // completed, even though it's not actually implemented yet. This fixes games that would 186 u8* dst_buffer = Memory::GetPointer(conversion_params.dst_address);
54 // otherwise hang on trying to play moflex videos, which uses the Y2R service. 187
188 // TODO: support color and other kinds of conversions
189 ASSERT(conversion_params.input_format == InputFormat::YUV422_Indiv8
190 || conversion_params.input_format == InputFormat::YUV420_Indiv8);
191 ASSERT(conversion_params.output_format == OutputFormat::Rgb24);
192 ASSERT(conversion_params.rotation == Rotation::None);
193 const int bpp = 3;
194
195 switch (conversion_params.alignment) {
196 case BlockAlignment::Linear:
197 {
198 const size_t input_lines = conversion_params.input_lines;
199 const size_t input_line_width = conversion_params.input_line_width;
200 const size_t srcY_stride = conversion_params.srcY_stride;
201 const size_t dst_stride = conversion_params.dst_stride;
202
203 size_t srcY_offset = 0;
204 size_t dst_offset = 0;
205
206 for (size_t line = 0; line < input_lines; ++line) {
207 for (size_t i = 0; i < input_line_width; ++i) {
208 u8 Y = srcY_buffer[srcY_offset];
209 dst_buffer[dst_offset + 0] = Y;
210 dst_buffer[dst_offset + 1] = Y;
211 dst_buffer[dst_offset + 2] = Y;
212
213 srcY_offset += 1;
214 dst_offset += bpp;
215 }
216 srcY_offset += srcY_stride;
217 dst_offset += dst_stride;
218 }
219 break;
220 }
221 case BlockAlignment::Block8x8:
222 {
223 const size_t input_lines = conversion_params.input_lines;
224 const size_t input_line_width = conversion_params.input_line_width;
225 const size_t srcY_stride = conversion_params.srcY_stride;
226 const size_t dst_transfer_unit = conversion_params.dst_transfer_unit;
227 const size_t dst_stride = conversion_params.dst_stride;
228
229 size_t srcY_offset = 0;
230 size_t dst_tile_line_offs = 0;
231
232 const size_t tile_size = 8 * 8 * bpp;
233
234 for (size_t line = 0; line < input_lines;) {
235 size_t tile_y = line / 8;
236 size_t max_line = line + 8;
237
238 for (; line < max_line; ++line) {
239 for (size_t x = 0; x < input_line_width; ++x) {
240 size_t tile_x = x / 8;
241
242 size_t dst_tile_offs = dst_tile_line_offs + tile_x * tile_size;
243 size_t tile_i = VideoCore::MortonInterleave((u32)x, (u32)line);
244
245 size_t dst_offset = dst_tile_offs + tile_i * bpp;
246
247 u8 Y = srcY_buffer[srcY_offset];
248 dst_buffer[dst_offset + 0] = Y;
249 dst_buffer[dst_offset + 1] = Y;
250 dst_buffer[dst_offset + 2] = Y;
251
252 srcY_offset += 1;
253 }
254
255 srcY_offset += srcY_stride;
256 }
257
258 dst_tile_line_offs += dst_transfer_unit + dst_stride;
259 }
260 break;
261 }
262 }
263 LOG_DEBUG(Service_Y2R, "called");
55 completion_event->Signal(); 264 completion_event->Signal();
56 265
57 LOG_WARNING(Service, "(STUBBED) called, expect blank video (MOFLEX) output!"); 266 cmd_buff[1] = RESULT_SUCCESS.raw;
267}
268
269/**
270* Y2R_U::IsBusyConversion service function
271* Outputs:
272* 1 : Result of function, 0 on success, otherwise error code
273* 2 : 1 if there's a conversion running, otherwise 0.
274*/
275static void IsBusyConversion(Service::Interface* self) {
276 u32* cmd_buff = Kernel::GetCommandBuffer();
58 277
59 cmd_buff[1] = RESULT_SUCCESS.raw; 278 cmd_buff[1] = RESULT_SUCCESS.raw;
279 cmd_buff[2] = 0; // StartConversion always finishes immediately
280 LOG_DEBUG(Service_Y2R, "called");
281}
282
283static void PingProcess(Service::Interface* self) {
284 u32* cmd_buff = Kernel::GetCommandBuffer();
285
286 cmd_buff[1] = RESULT_SUCCESS.raw;
287 cmd_buff[2] = 0;
288 LOG_WARNING(Service_Y2R, "(STUBBED) called");
60} 289}
61 290
62const Interface::FunctionInfo FunctionTable[] = { 291const Interface::FunctionInfo FunctionTable[] = {
63 {0x00010040, nullptr, "SetInputFormat"}, 292 {0x00010040, SetInputFormat, "SetInputFormat"},
64 {0x00030040, nullptr, "SetOutputFormat"}, 293 {0x00030040, SetOutputFormat, "SetOutputFormat"},
65 {0x00050040, nullptr, "SetRotation"}, 294 {0x00050040, SetRotation, "SetRotation"},
66 {0x00070040, nullptr, "SetBlockAlignment"}, 295 {0x00070040, SetBlockAlignment, "SetBlockAlignment"},
67 {0x000D0040, nullptr, "SetTransferEndInterrupt"}, 296 {0x000D0040, nullptr, "SetTransferEndInterrupt"},
68 {0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"}, 297 {0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"},
69 {0x00100102, nullptr, "SetSendingY"}, 298 {0x00100102, SetSendingY, "SetSendingY"},
70 {0x00110102, nullptr, "SetSendingU"}, 299 {0x00110102, nullptr, "SetSendingU"},
71 {0x00120102, nullptr, "SetSendingV"}, 300 {0x00120102, nullptr, "SetSendingV"},
72 {0x00180102, nullptr, "SetReceiving"}, 301 {0x00180102, SetReceiving, "SetReceiving"},
73 {0x001A0040, nullptr, "SetInputLineWidth"}, 302 {0x001A0040, SetInputLineWidth, "SetInputLineWidth"},
74 {0x001C0040, nullptr, "SetInputLines"}, 303 {0x001C0040, SetInputLines, "SetInputLines"},
75 {0x00200040, nullptr, "SetStandardCoefficient"}, 304 {0x00200040, nullptr, "SetStandardCoefficient"},
76 {0x00220040, nullptr, "SetAlpha"}, 305 {0x00220040, nullptr, "SetAlpha"},
77 {0x00260000, StartConversion, "StartConversion"}, 306 {0x00260000, StartConversion, "StartConversion"},
78 {0x00270000, nullptr, "StopConversion"}, 307 {0x00270000, nullptr, "StopConversion"},
79 {0x00280000, IsBusyConversion, "IsBusyConversion"}, 308 {0x00280000, IsBusyConversion, "IsBusyConversion"},
80 {0x002A0000, nullptr, "PingProcess"}, 309 {0x002A0000, PingProcess, "PingProcess"},
81 {0x002B0000, nullptr, "DriverInitialize"}, 310 {0x002B0000, nullptr, "DriverInitialize"},
82 {0x002C0000, nullptr, "DriverFinalize"} 311 {0x002C0000, nullptr, "DriverFinalize"},
83}; 312};
84 313
85//////////////////////////////////////////////////////////////////////////////////////////////////// 314////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -87,8 +316,9 @@ const Interface::FunctionInfo FunctionTable[] = {
87 316
88Interface::Interface() { 317Interface::Interface() {
89 completion_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "Y2R:Completed"); 318 completion_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "Y2R:Completed");
319 std::memset(&conversion_params, 0, sizeof(conversion_params));
90 320
91 Register(FunctionTable); 321 Register(FunctionTable);
92} 322}
93 323
94} // namespace 324} // namespace