From 0ad050f85d4f5d091b32dacac41b23e0b5b251e2 Mon Sep 17 00:00:00 2001 From: Jannik Vogel Date: Fri, 8 Apr 2016 23:16:51 +0200 Subject: [PATCH 01/39] OpenGL: Implement color combiner Operation::Dot3_RGB --- src/video_core/renderer_opengl/gl_shader_gen.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index ee4b54ab9..646b4eaaf 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -198,6 +198,9 @@ static void AppendColorCombiner(std::string& out, TevStageConfig::Operation oper case Operation::AddThenMultiply: out += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]"; break; + case Operation::Dot3_RGB: + out += "vec3(dot(" + variable_name + "[0] - vec3(0.5), " + variable_name + "[1] - vec3(0.5)) * 4.0)"; + break; default: out += "vec3(0.0)"; LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", operation); From 43b6cbd762aa9158e44484f728aa828f22b7811a Mon Sep 17 00:00:00 2001 From: wwylele Date: Fri, 15 Apr 2016 11:50:50 +0300 Subject: [PATCH 02/39] fix driver root identification on Windows --- src/common/file_util.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 9ada09f8a..687b7ae5a 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -69,9 +69,10 @@ static void StripTailDirSlashes(std::string &fname) { if (fname.length() > 1) { - size_t i = fname.length() - 1; - while (fname[i] == DIR_SEP_CHR) - fname[i--] = '\0'; + size_t i = fname.length(); + while (i > 0 && fname[i - 1] == DIR_SEP_CHR) + --i; + fname.resize(i); } return; } @@ -85,6 +86,10 @@ bool Exists(const std::string &filename) StripTailDirSlashes(copy); #ifdef _WIN32 + // Windows needs a slash to identify a driver root + if (copy.size() != 0 && copy.back() == ':') + copy += DIR_SEP_CHR; + int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info); #else int result = stat64(copy.c_str(), &file_info); @@ -102,6 +107,10 @@ bool IsDirectory(const std::string &filename) StripTailDirSlashes(copy); #ifdef _WIN32 + // Windows needs a slash to identify a driver root + if (copy.size() != 0 && copy.back() == ':') + copy += DIR_SEP_CHR; + int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info); #else int result = stat64(copy.c_str(), &file_info); From 164c15f9116dd0f0b1f0a64fd551d10f24024e96 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 19 Apr 2016 02:24:21 +0100 Subject: [PATCH 03/39] SDL2 Frontend: Use argv[0], add a --version, and reorder options. --- src/citra/citra.cpp | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index d6ad13f69..b4501eb2e 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -20,6 +20,7 @@ #include "common/logging/log.h" #include "common/logging/backend.h" #include "common/logging/filter.h" +#include "common/scm_rev.h" #include "common/scope_exit.h" #include "core/settings.h" @@ -34,11 +35,17 @@ #include "video_core/video_core.h" -static void PrintHelp() +static void PrintHelp(const char *argv0) { - std::cout << "Usage: citra [options] " << std::endl; - std::cout << "--help, -h Display this information" << std::endl; - std::cout << "--gdbport, -g number Enable gdb stub on port number" << std::endl; + std::cout << "Usage: " << argv0 << " [options] \n" + "-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n" + "-h, --help Display this help and exit\n" + "-v, --version Output version information and exit\n"; +} + +static void PrintVersion() +{ + std::cout << "Citra " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; } /// Application entry point @@ -51,18 +58,16 @@ int main(int argc, char **argv) { std::string boot_filename; static struct option long_options[] = { - { "help", no_argument, 0, 'h' }, { "gdbport", required_argument, 0, 'g' }, + { "help", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'v' }, { 0, 0, 0, 0 } }; while (optind < argc) { - char arg = getopt_long(argc, argv, ":hg:", long_options, &option_index); + char arg = getopt_long(argc, argv, "g:hv", long_options, &option_index); if (arg != -1) { switch (arg) { - case 'h': - PrintHelp(); - return 0; case 'g': errno = 0; gdb_port = strtoul(optarg, &endarg, 0); @@ -73,6 +78,12 @@ int main(int argc, char **argv) { exit(1); } break; + case 'h': + PrintHelp(argv[0]); + return 0; + case 'v': + PrintVersion(); + return 0; } } else { boot_filename = argv[optind]; From 854912ca5d166cab47f7a2a41f563135889e3c1a Mon Sep 17 00:00:00 2001 From: JamePeng Date: Wed, 20 Apr 2016 18:38:01 +0800 Subject: [PATCH 04/39] Update the code of service y2r! --- src/core/hle/service/y2r_u.cpp | 369 ++++++++++++++++++++++++++++++--- src/core/hle/service/y2r_u.h | 20 ++ 2 files changed, 357 insertions(+), 32 deletions(-) diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 22f373adf..2c6328a44 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -4,6 +4,7 @@ #include +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/logging/log.h" @@ -28,13 +29,17 @@ struct ConversionParameters { u16 input_line_width; u16 input_lines; StandardCoefficient standard_coefficient; - u8 reserved; + u8 padding; u16 alpha; }; static_assert(sizeof(ConversionParameters) == 12, "ConversionParameters struct has incorrect size"); static Kernel::SharedPtr completion_event; static ConversionConfiguration conversion; +static DitheringWeightParams dithering_weight_params; +static u32 temporal_dithering_enabled = 0; +static u32 transfer_end_interrupt_enabled = 0; +static u32 spacial_dithering_enabled = 0; static const CoefficientSet standard_coefficients[4] = { {{ 0x100, 0x166, 0xB6, 0x58, 0x1C5, -0x166F, 0x10EE, -0x1C5B }}, // ITU_Rec601 @@ -73,7 +78,7 @@ ResultCode ConversionConfiguration::SetInputLines(u16 lines) { ResultCode ConversionConfiguration::SetStandardCoefficient(StandardCoefficient standard_coefficient) { size_t index = static_cast(standard_coefficient); - if (index >= 4) { + if (index >= ARRAY_SIZE(standard_coefficients)) { return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::CAM, ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E053ED } @@ -88,42 +93,167 @@ static void SetInputFormat(Service::Interface* self) { conversion.input_format = static_cast(cmd_buff[1]); LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); + cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; } +static void GetInputFormat(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast(conversion.input_format); + LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); +} + static void SetOutputFormat(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.output_format = static_cast(cmd_buff[1]); LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); + cmd_buff[0] = IPC::MakeHeader(0x3, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; } +static void GetOutputFormat(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x4, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast(conversion.output_format); + LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); +} + static void SetRotation(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.rotation = static_cast(cmd_buff[1]); LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); + cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; } +static void GetRotation(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x6, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast(conversion.rotation); + LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); +} + static void SetBlockAlignment(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.block_alignment = static_cast(cmd_buff[1]); - LOG_DEBUG(Service_Y2R, "called alignment=%hhu", conversion.block_alignment); + LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); + cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; } +static void GetBlockAlignment(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x8, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast(conversion.block_alignment); + LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); +} + +/** + * Y2R_U::SetSpacialDithering service function + * Inputs: + * 1 : u8, 0 = Disabled, 1 = Enabled + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetSpacialDithering(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + spacial_dithering_enabled = cmd_buff[1] & 0xF; + + cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R_U::GetSpacialDithering service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : u8, 0 = Disabled, 1 = Enabled + */ +static void GetSpacialDithering(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = spacial_dithering_enabled; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R_U::SetTemporalDithering service function + * Inputs: + * 1 : u8, 0 = Disabled, 1 = Enabled + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetTemporalDithering(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + temporal_dithering_enabled = cmd_buff[1] & 0xF; + + cmd_buff[0] = IPC::MakeHeader(0xB, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R_U::GetTemporalDithering service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : u8, 0 = Disabled, 1 = Enabled + */ +static void GetTemporalDithering(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = temporal_dithering_enabled; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R_U::SetTransferEndInterrupt service function + * Inputs: + * 1 : u8, 0 = Disabled, 1 = Enabled + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ static void SetTransferEndInterrupt(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + transfer_end_interrupt_enabled = cmd_buff[1] & 0xf; cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_DEBUG(Service_Y2R, "(STUBBED) called"); + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R_U::GetTransferEndInterrupt service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : u8, 0 = Disabled, 1 = Enabled + */ +static void GetTransferEndInterrupt(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0xE, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = transfer_end_interrupt_enabled; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } /** @@ -135,6 +265,7 @@ static void SetTransferEndInterrupt(Service::Interface* self) { static void GetTransferEndEvent(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); LOG_DEBUG(Service_Y2R, "called"); @@ -152,6 +283,7 @@ static void SetSendingY(Service::Interface* self) { "src_process_handle=0x%08X", conversion.src_Y.image_size, conversion.src_Y.transfer_unit, conversion.src_Y.gap, src_process_handle); + cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; } @@ -167,6 +299,7 @@ static void SetSendingU(Service::Interface* self) { "src_process_handle=0x%08X", conversion.src_U.image_size, conversion.src_U.transfer_unit, conversion.src_U.gap, src_process_handle); + cmd_buff[0] = IPC::MakeHeader(0x11, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; } @@ -182,6 +315,7 @@ static void SetSendingV(Service::Interface* self) { "src_process_handle=0x%08X", conversion.src_V.image_size, conversion.src_V.transfer_unit, conversion.src_V.gap, src_process_handle); + cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; } @@ -197,9 +331,70 @@ static void SetSendingYUYV(Service::Interface* self) { "src_process_handle=0x%08X", conversion.src_YUYV.image_size, conversion.src_YUYV.transfer_unit, conversion.src_YUYV.gap, src_process_handle); + cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; } +/** + * Y2R::IsFinishedSendingYuv service function + * Output: + * 1 : Result of the function, 0 on success, otherwise error code + * 2 : u8, 0 = Not Finished, 1 = Finished + */ +static void IsFinishedSendingYuv(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x14, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R::IsFinishedSendingY service function + * Output: + * 1 : Result of the function, 0 on success, otherwise error code + * 2 : u8, 0 = Not Finished, 1 = Finished + */ +static void IsFinishedSendingY(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x15, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R::IsFinishedSendingU service function + * Output: + * 1 : Result of the function, 0 on success, otherwise error code + * 2 : u8, 0 = Not Finished, 1 = Finished + */ +static void IsFinishedSendingU(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R::IsFinishedSendingV service function + * Output: + * 1 : Result of the function, 0 on success, otherwise error code + * 2 : u8, 0 = Not Finished, 1 = Finished + */ +static void IsFinishedSendingV(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x17, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + static void SetReceiving(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -213,23 +408,59 @@ static void SetReceiving(Service::Interface* self) { conversion.dst.transfer_unit, conversion.dst.gap, dst_process_handle); + cmd_buff[0] = IPC::MakeHeader(0x18, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; } +/** + * Y2R::IsFinishedReceiving service function + * Output: + * 1 : Result of the function, 0 on success, otherwise error code + * 2 : u8, 0 = Not Finished, 1 = Finished + */ +static void IsFinishedReceiving(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x19, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + static void SetInputLineWidth(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); LOG_DEBUG(Service_Y2R, "called input_line_width=%u", cmd_buff[1]); + cmd_buff[0] = IPC::MakeHeader(0x1A, 1, 0); cmd_buff[1] = conversion.SetInputLineWidth(cmd_buff[1]).raw; } +static void GetInputLineWidth(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x1B, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = conversion.input_line_width; + LOG_DEBUG(Service_Y2R, "called input_line_width=%u", conversion.input_line_width); +} + static void SetInputLines(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - LOG_DEBUG(Service_Y2R, "called input_line_number=%u", cmd_buff[1]); + LOG_DEBUG(Service_Y2R, "called input_lines=%u", cmd_buff[1]); + cmd_buff[0] = IPC::MakeHeader(0x1C, 1, 0); cmd_buff[1] = conversion.SetInputLines(cmd_buff[1]).raw; } +static void GetInputLines(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x1D, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast(conversion.input_lines); + LOG_DEBUG(Service_Y2R, "called input_lines=%u", conversion.input_lines); +} + static void SetCoefficient(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -239,15 +470,45 @@ static void SetCoefficient(Service::Interface* self) { coefficients[0], coefficients[1], coefficients[2], coefficients[3], coefficients[4], coefficients[5], coefficients[6], coefficients[7]); + cmd_buff[0] = IPC::MakeHeader(0x1E, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; } +static void GetCoefficient(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x1F, 5, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + std::memcpy(&cmd_buff[2], conversion.coefficients.data(), sizeof(CoefficientSet)); +} + static void SetStandardCoefficient(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u", cmd_buff[1]); + u32 index = cmd_buff[1]; - cmd_buff[1] = conversion.SetStandardCoefficient((StandardCoefficient)cmd_buff[1]).raw; + cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0); + cmd_buff[1] = conversion.SetStandardCoefficient((StandardCoefficient)index).raw; + LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u", index); +} + +static void GetStandardCoefficient(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 index = cmd_buff[1]; + + if (index < ARRAY_SIZE(standard_coefficients)) { + std::memcpy(&cmd_buff[2], &standard_coefficients[index], sizeof(CoefficientSet)); + + cmd_buff[0] = IPC::MakeHeader(0x21, 5, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u ", index); + } + else { + cmd_buff[0] = IPC::MakeHeader(0x21, 1, 0); + cmd_buff[1] = -1; + LOG_ERROR(Service_Y2R, "called standard_coefficient=%u The argument is invalid!", index); + } } static void SetAlpha(Service::Interface* self) { @@ -256,9 +517,37 @@ static void SetAlpha(Service::Interface* self) { conversion.alpha = cmd_buff[1]; LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); + cmd_buff[0] = IPC::MakeHeader(0x22, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; } +static void GetAlpha(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x23, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = conversion.alpha; + LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); +} + +static void SetDitheringWeightParams(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + std::memcpy(&dithering_weight_params, &cmd_buff[1], sizeof(DitheringWeightParams)); + + cmd_buff[0] = IPC::MakeHeader(0x24, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called"); +} + +static void GetDitheringWeightParams(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x25, 9, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + std::memcpy(&cmd_buff[2], &dithering_weight_params, sizeof(DitheringWeightParams)); + LOG_DEBUG(Service_Y2R, "called"); +} + static void StartConversion(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -273,6 +562,7 @@ static void StartConversion(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called"); completion_event->Signal(); + cmd_buff[0] = IPC::MakeHeader(0x26, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; } @@ -293,15 +583,16 @@ static void StopConversion(Service::Interface* self) { static void IsBusyConversion(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x28, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 0; // StartConversion always finishes immediately LOG_DEBUG(Service_Y2R, "called"); } /** - * Y2R_U::SetConversionParams service function + * Y2R_U::SetPackageParameter service function */ -static void SetConversionParams(Service::Interface* self) { +static void SetPackageParameter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); auto params = reinterpret_cast(&cmd_buff[1]); @@ -311,7 +602,7 @@ static void SetConversionParams(Service::Interface* self) { "reserved=%hhu alpha=%hX", params->input_format, params->output_format, params->rotation, params->block_alignment, params->input_line_width, params->input_lines, params->standard_coefficient, - params->reserved, params->alpha); + params->padding, params->alpha); ResultCode result = RESULT_SUCCESS; @@ -325,6 +616,7 @@ static void SetConversionParams(Service::Interface* self) { if (result.IsError()) goto cleanup; result = conversion.SetStandardCoefficient(params->standard_coefficient); if (result.IsError()) goto cleanup; + conversion.padding = params->padding; conversion.alpha = params->alpha; cleanup: @@ -335,6 +627,7 @@ cleanup: static void PingProcess(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x2A, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 0; LOG_WARNING(Service_Y2R, "(STUBBED) called"); @@ -373,51 +666,63 @@ static void DriverFinalize(Service::Interface* self) { LOG_DEBUG(Service_Y2R, "called"); } + +static void GetPackageParameter(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x2D, 4, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + std::memcpy(&cmd_buff[2], &conversion, sizeof(ConversionParameters)); + + LOG_DEBUG(Service_Y2R, "called"); +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010040, SetInputFormat, "SetInputFormat"}, - {0x00020000, nullptr, "GetInputFormat"}, + {0x00020000, GetInputFormat, "GetInputFormat"}, {0x00030040, SetOutputFormat, "SetOutputFormat"}, - {0x00040000, nullptr, "GetOutputFormat"}, + {0x00040000, GetOutputFormat, "GetOutputFormat"}, {0x00050040, SetRotation, "SetRotation"}, - {0x00060000, nullptr, "GetRotation"}, + {0x00060000, GetRotation, "GetRotation"}, {0x00070040, SetBlockAlignment, "SetBlockAlignment"}, - {0x00080000, nullptr, "GetBlockAlignment"}, - {0x00090040, nullptr, "SetSpacialDithering"}, - {0x000A0000, nullptr, "GetSpacialDithering"}, - {0x000B0040, nullptr, "SetTemporalDithering"}, - {0x000C0000, nullptr, "GetTemporalDithering"}, + {0x00080000, GetBlockAlignment, "GetBlockAlignment"}, + {0x00090040, SetSpacialDithering, "SetSpacialDithering"}, + {0x000A0000, GetSpacialDithering, "GetSpacialDithering"}, + {0x000B0040, SetTemporalDithering, "SetTemporalDithering"}, + {0x000C0000, GetTemporalDithering, "GetTemporalDithering"}, {0x000D0040, SetTransferEndInterrupt, "SetTransferEndInterrupt"}, + {0x000E0000, GetTransferEndInterrupt, "GetTransferEndInterrupt"}, {0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"}, {0x00100102, SetSendingY, "SetSendingY"}, {0x00110102, SetSendingU, "SetSendingU"}, {0x00120102, SetSendingV, "SetSendingV"}, {0x00130102, SetSendingYUYV, "SetSendingYUYV"}, - {0x00140000, nullptr, "IsFinishedSendingYuv"}, - {0x00150000, nullptr, "IsFinishedSendingY"}, - {0x00160000, nullptr, "IsFinishedSendingU"}, - {0x00170000, nullptr, "IsFinishedSendingV"}, + {0x00140000, IsFinishedSendingYuv, "IsFinishedSendingYuv"}, + {0x00150000, IsFinishedSendingY, "IsFinishedSendingY"}, + {0x00160000, IsFinishedSendingU, "IsFinishedSendingU"}, + {0x00170000, IsFinishedSendingV, "IsFinishedSendingV"}, {0x00180102, SetReceiving, "SetReceiving"}, - {0x00190000, nullptr, "IsFinishedReceiving"}, + {0x00190000, IsFinishedReceiving, "IsFinishedReceiving"}, {0x001A0040, SetInputLineWidth, "SetInputLineWidth"}, - {0x001B0000, nullptr, "GetInputLineWidth"}, + {0x001B0000, GetInputLineWidth, "GetInputLineWidth"}, {0x001C0040, SetInputLines, "SetInputLines"}, - {0x001D0000, nullptr, "GetInputLines"}, + {0x001D0000, GetInputLines, "GetInputLines"}, {0x001E0100, SetCoefficient, "SetCoefficient"}, - {0x001F0000, nullptr, "GetCoefficient"}, + {0x001F0000, GetCoefficient, "GetCoefficient"}, {0x00200040, SetStandardCoefficient, "SetStandardCoefficient"}, - {0x00210040, nullptr, "GetStandardCoefficientParams"}, + {0x00210040, GetStandardCoefficient, "GetStandardCoefficient"}, {0x00220040, SetAlpha, "SetAlpha"}, - {0x00230000, nullptr, "GetAlpha"}, - {0x00240200, nullptr, "SetDitheringWeightParams"}, - {0x00250000, nullptr, "GetDitheringWeightParams"}, + {0x00230000, GetAlpha, "GetAlpha"}, + {0x00240200, SetDitheringWeightParams,"SetDitheringWeightParams"}, + {0x00250000, GetDitheringWeightParams,"GetDitheringWeightParams"}, {0x00260000, StartConversion, "StartConversion"}, {0x00270000, StopConversion, "StopConversion"}, {0x00280000, IsBusyConversion, "IsBusyConversion"}, - {0x002901C0, SetConversionParams, "SetConversionParams"}, + {0x002901C0, SetPackageParameter, "SetPackageParameter"}, {0x002A0000, PingProcess, "PingProcess"}, {0x002B0000, DriverInitialize, "DriverInitialize"}, {0x002C0000, DriverFinalize, "DriverFinalize"}, - {0x002D0000, nullptr, "GetPackageParameter"}, + {0x002D0000, GetPackageParameter, "GetPackageParameter"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/y2r_u.h b/src/core/hle/service/y2r_u.h index 3965a5545..95fa2fdb7 100644 --- a/src/core/hle/service/y2r_u.h +++ b/src/core/hle/service/y2r_u.h @@ -97,6 +97,7 @@ struct ConversionConfiguration { u16 input_line_width; u16 input_lines; CoefficientSet coefficients; + u8 padding; u16 alpha; /// Input parameters for the Y (luma) plane @@ -109,6 +110,25 @@ struct ConversionConfiguration { ResultCode SetStandardCoefficient(StandardCoefficient standard_coefficient); }; +struct DitheringWeightParams { + u16 w0_xEven_yEven; + u16 w0_xOdd_yEven; + u16 w0_xEven_yOdd; + u16 w0_xOdd_yOdd; + u16 w1_xEven_yEven; + u16 w1_xOdd_yEven; + u16 w1_xEven_yOdd; + u16 w1_xOdd_yOdd; + u16 w2_xEven_yEven; + u16 w2_xOdd_yEven; + u16 w2_xEven_yOdd; + u16 w2_xOdd_yOdd; + u16 w3_xEven_yEven; + u16 w3_xOdd_yEven; + u16 w3_xEven_yOdd; + u16 w3_xOdd_yOdd; +}; + class Interface : public Service::Interface { public: Interface(); From 15b44fb38093b05413acc00ee36c396e5f649764 Mon Sep 17 00:00:00 2001 From: tfarley Date: Wed, 2 Mar 2016 01:06:38 -0500 Subject: [PATCH 05/39] Update to ext-boost with interval_map --- externals/boost | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/boost b/externals/boost index d81b92699..2dcb9d979 160000 --- a/externals/boost +++ b/externals/boost @@ -1 +1 @@ -Subproject commit d81b9269900ae183d0dc98403eea4c971590a807 +Subproject commit 2dcb9d979665b6aabb1635c617973e02914e60ec From e46d086189908ce9b9d7d68e3e4453396e48c7cc Mon Sep 17 00:00:00 2001 From: tfarley Date: Sat, 16 Apr 2016 18:51:49 -0400 Subject: [PATCH 06/39] Config: Add scaled resolution option --- src/citra/config.cpp | 1 + src/citra/default_ini.h | 4 ++++ src/citra_qt/config.cpp | 2 ++ src/citra_qt/configure_general.cpp | 2 ++ src/citra_qt/configure_general.ui | 7 +++++++ src/core/settings.cpp | 2 +- src/core/settings.h | 1 + src/video_core/video_core.cpp | 1 + src/video_core/video_core.h | 1 + 9 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 6b6617352..9e2ecd307 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -65,6 +65,7 @@ void Config::ReadValues() { // Renderer Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", false); Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true); + Settings::values.use_scaled_resolution = sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false); Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0); Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index c9b490a00..1f1aa716b 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -46,6 +46,10 @@ use_hw_renderer = # 0 : Interpreter (slow), 1 (default): JIT (fast) use_shader_jit = +# Whether to use native 3DS screen resolution or to scale rendering resolution to the displayed screen size. +# 0 (default): Native, 1: Scaled +use_scaled_resolution = + # The clear color for the renderer. What shows up on the sides of the bottom screen. # Must be in range of 0.0-1.0. Defaults to 1.0 for all. bg_red = diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index e363be38a..7dc61fe40 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp @@ -45,6 +45,7 @@ void Config::ReadValues() { qt_config->beginGroup("Renderer"); Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", false).toBool(); Settings::values.use_shader_jit = qt_config->value("use_shader_jit", true).toBool(); + Settings::values.use_scaled_resolution = qt_config->value("use_scaled_resolution", false).toBool(); Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat(); Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat(); @@ -129,6 +130,7 @@ void Config::SaveValues() { qt_config->beginGroup("Renderer"); qt_config->setValue("use_hw_renderer", Settings::values.use_hw_renderer); qt_config->setValue("use_shader_jit", Settings::values.use_shader_jit); + qt_config->setValue("use_scaled_resolution", Settings::values.use_scaled_resolution); // Cast to double because Qt's written float values are not human-readable qt_config->setValue("bg_red", (double)Settings::values.bg_red); diff --git a/src/citra_qt/configure_general.cpp b/src/citra_qt/configure_general.cpp index a27d0d26c..62648e665 100644 --- a/src/citra_qt/configure_general.cpp +++ b/src/citra_qt/configure_general.cpp @@ -25,6 +25,7 @@ void ConfigureGeneral::setConfiguration() { ui->region_combobox->setCurrentIndex(Settings::values.region_value); ui->toogle_hw_renderer->setChecked(Settings::values.use_hw_renderer); ui->toogle_shader_jit->setChecked(Settings::values.use_shader_jit); + ui->toogle_scaled_resolution->setChecked(Settings::values.use_scaled_resolution); } void ConfigureGeneral::applyConfiguration() { @@ -33,5 +34,6 @@ void ConfigureGeneral::applyConfiguration() { Settings::values.region_value = ui->region_combobox->currentIndex(); Settings::values.use_hw_renderer = ui->toogle_hw_renderer->isChecked(); Settings::values.use_shader_jit = ui->toogle_shader_jit->isChecked(); + Settings::values.use_scaled_resolution = ui->toogle_scaled_resolution->isChecked(); Settings::Apply(); } diff --git a/src/citra_qt/configure_general.ui b/src/citra_qt/configure_general.ui index 47184c5c6..5eb309793 100644 --- a/src/citra_qt/configure_general.ui +++ b/src/citra_qt/configure_general.ui @@ -128,6 +128,13 @@ + + + + Enable scaled resolution + + + diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 1aa26fbd2..eaf5c8461 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -19,7 +19,7 @@ void Apply() { VideoCore::g_hw_renderer_enabled = values.use_hw_renderer; VideoCore::g_shader_jit_enabled = values.use_shader_jit; - + VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution; } } // namespace diff --git a/src/core/settings.h b/src/core/settings.h index 4933a516d..d620d8461 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -55,6 +55,7 @@ struct Values { // Renderer bool use_hw_renderer; bool use_shader_jit; + bool use_scaled_resolution; float bg_red; float bg_green; diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 256899c89..855286173 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -25,6 +25,7 @@ std::unique_ptr g_renderer; ///< Renderer plugin std::atomic g_hw_renderer_enabled; std::atomic g_shader_jit_enabled; +std::atomic g_scaled_resolution_enabled; /// Initialize the video core bool Init(EmuWindow* emu_window) { diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index bca67fb8c..30267489e 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -36,6 +36,7 @@ extern EmuWindow* g_emu_window; ///< Emu window // TODO: Wrap these in a user settings struct along with any other graphics settings (often set from qt ui) extern std::atomic g_hw_renderer_enabled; extern std::atomic g_shader_jit_enabled; +extern std::atomic g_scaled_resolution_enabled; /// Start the video core void Start(); From 22f3a7e94ce0e325a8c3dfeb0814c4b82a42649d Mon Sep 17 00:00:00 2001 From: tfarley Date: Sat, 16 Apr 2016 18:57:57 -0400 Subject: [PATCH 07/39] HWRasterizer: Texture forwarding --- src/core/hle/service/fs/archive.cpp | 1 + src/core/hle/service/gsp_gpu.cpp | 31 +- src/core/hle/service/y2r_u.cpp | 10 +- src/core/hw/gpu.cpp | 351 ++++---- src/core/hw/gpu.h | 4 +- src/core/memory.cpp | 140 +++ src/core/memory.h | 16 + src/video_core/debug_utils/debug_utils.cpp | 4 +- src/video_core/pica.h | 2 +- src/video_core/rasterizer_interface.h | 31 +- src/video_core/renderer_base.cpp | 2 - .../renderer_opengl/gl_rasterizer.cpp | 851 +++++++----------- .../renderer_opengl/gl_rasterizer.h | 76 +- .../renderer_opengl/gl_rasterizer_cache.cpp | 719 ++++++++++++++- .../renderer_opengl/gl_rasterizer_cache.h | 215 ++++- src/video_core/renderer_opengl/gl_state.cpp | 63 +- src/video_core/renderer_opengl/gl_state.h | 25 +- .../renderer_opengl/renderer_opengl.cpp | 128 +-- .../renderer_opengl/renderer_opengl.h | 44 +- src/video_core/swrasterizer.h | 6 +- 20 files changed, 1749 insertions(+), 970 deletions(-) diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index e9588cb72..cc51ede0c 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -114,6 +114,7 @@ ResultVal File::SyncRequest() { return read.Code(); } cmd_buff[2] = static_cast(*read); + Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(address), length); break; } diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 0c655395e..211fcf599 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -4,6 +4,7 @@ #include "common/bit_field.h" #include "common/microprofile.h" +#include "common/profiler.h" #include "core/memory.h" #include "core/hle/kernel/event.h" @@ -15,8 +16,6 @@ #include "video_core/gpu_debugger.h" #include "video_core/debug_utils/debug_utils.h" -#include "video_core/renderer_base.h" -#include "video_core/video_core.h" #include "gsp_gpu.h" @@ -291,8 +290,6 @@ static void FlushDataCache(Service::Interface* self) { u32 size = cmd_buff[2]; u32 process = cmd_buff[4]; - VideoCore::g_renderer->Rasterizer()->InvalidateRegion(Memory::VirtualToPhysicalAddress(address), size); - // TODO(purpasmart96): Verify return header on HW cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -408,6 +405,8 @@ void SignalInterrupt(InterruptId interrupt_id) { g_interrupt_event->Signal(); } +MICROPROFILE_DEFINE(GPU_GSP_DMA, "GPU", "GSP DMA", MP_RGB(100, 0, 255)); + /// Executes the next GSP command static void ExecuteCommand(const Command& command, u32 thread_id) { // Utility function to convert register ID to address @@ -419,18 +418,21 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { // GX request DMA - typically used for copying memory from GSP heap to VRAM case CommandId::REQUEST_DMA: - VideoCore::g_renderer->Rasterizer()->FlushRegion(Memory::VirtualToPhysicalAddress(command.dma_request.source_address), - command.dma_request.size); + { + MICROPROFILE_SCOPE(GPU_GSP_DMA); + + // TODO: Consider attempting rasterizer-accelerated surface blit if that usage is ever possible/likely + Memory::RasterizerFlushRegion(Memory::VirtualToPhysicalAddress(command.dma_request.source_address), + command.dma_request.size); + Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address), + command.dma_request.size); memcpy(Memory::GetPointer(command.dma_request.dest_address), Memory::GetPointer(command.dma_request.source_address), command.dma_request.size); SignalInterrupt(InterruptId::DMA); - - VideoCore::g_renderer->Rasterizer()->InvalidateRegion(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address), - command.dma_request.size); break; - + } // TODO: This will need some rework in the future. (why?) case CommandId::SUBMIT_GPU_CMDLIST: { @@ -517,13 +519,8 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { case CommandId::CACHE_FLUSH: { - for (auto& region : command.cache_flush.regions) { - if (region.size == 0) - break; - - VideoCore::g_renderer->Rasterizer()->InvalidateRegion( - Memory::VirtualToPhysicalAddress(region.address), region.size); - } + // NOTE: Rasterizer flushing handled elsewhere in CPU read/write and other GPU handlers + // Use command.cache_flush.regions to implement this handler break; } diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 22f373adf..1672ad775 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -12,9 +12,6 @@ #include "core/hle/service/y2r_u.h" #include "core/hw/y2r.h" -#include "video_core/renderer_base.h" -#include "video_core/video_core.h" - //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Y2R_U @@ -262,13 +259,12 @@ static void SetAlpha(Service::Interface* self) { static void StartConversion(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - HW::Y2R::PerformConversion(conversion); - // dst_image_size would seem to be perfect for this, but it doesn't include the gap :( u32 total_output_size = conversion.input_lines * (conversion.dst.transfer_unit + conversion.dst.gap); - VideoCore::g_renderer->Rasterizer()->InvalidateRegion( - Memory::VirtualToPhysicalAddress(conversion.dst.address), total_output_size); + Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(conversion.dst.address), total_output_size); + + HW::Y2R::PerformConversion(conversion); LOG_DEBUG(Service_Y2R, "called"); completion_event->Signal(); diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 7e2f9cdfa..2fe856293 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -115,21 +115,39 @@ inline void Write(u32 addr, const T data) { u8* start = Memory::GetPhysicalPointer(config.GetStartAddress()); u8* end = Memory::GetPhysicalPointer(config.GetEndAddress()); - if (config.fill_24bit) { - // fill with 24-bit values - for (u8* ptr = start; ptr < end; ptr += 3) { - ptr[0] = config.value_24bit_r; - ptr[1] = config.value_24bit_g; - ptr[2] = config.value_24bit_b; + // TODO: Consider always accelerating and returning vector of + // regions that the accelerated fill did not cover to + // reduce/eliminate the fill that the cpu has to do. + // This would also mean that the flush below is not needed. + // Fill should first flush all surfaces that touch but are + // not completely within the fill range. + // Then fill all completely covered surfaces, and return the + // regions that were between surfaces or within the touching + // ones for cpu to manually fill here. + if (!VideoCore::g_renderer->Rasterizer()->AccelerateFill(config)) { + Memory::RasterizerFlushAndInvalidateRegion(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress()); + + if (config.fill_24bit) { + // fill with 24-bit values + for (u8* ptr = start; ptr < end; ptr += 3) { + ptr[0] = config.value_24bit_r; + ptr[1] = config.value_24bit_g; + ptr[2] = config.value_24bit_b; + } + } else if (config.fill_32bit) { + // fill with 32-bit values + if (end > start) { + u32 value = config.value_32bit; + size_t len = (end - start) / sizeof(u32); + for (size_t i = 0; i < len; ++i) + memcpy(&start[i * sizeof(u32)], &value, sizeof(u32)); + } + } else { + // fill with 16-bit values + u16 value_16bit = config.value_16bit.Value(); + for (u8* ptr = start; ptr < end; ptr += sizeof(u16)) + memcpy(ptr, &value_16bit, sizeof(u16)); } - } else if (config.fill_32bit) { - // fill with 32-bit values - for (u32* ptr = (u32*)start; ptr < (u32*)end; ++ptr) - *ptr = config.value_32bit; - } else { - // fill with 16-bit values - for (u16* ptr = (u16*)start; ptr < (u16*)end; ++ptr) - *ptr = config.value_16bit; } LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); @@ -139,8 +157,6 @@ inline void Write(u32 addr, const T data) { } else { GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1); } - - VideoCore::g_renderer->Rasterizer()->InvalidateRegion(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress()); } // Reset "trigger" flag and set the "finish" flag @@ -161,184 +177,185 @@ inline void Write(u32 addr, const T data) { if (Pica::g_debug_context) Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::IncomingDisplayTransfer, nullptr); - u8* src_pointer = Memory::GetPhysicalPointer(config.GetPhysicalInputAddress()); - u8* dst_pointer = Memory::GetPhysicalPointer(config.GetPhysicalOutputAddress()); + if (!VideoCore::g_renderer->Rasterizer()->AccelerateDisplayTransfer(config)) { + u8* src_pointer = Memory::GetPhysicalPointer(config.GetPhysicalInputAddress()); + u8* dst_pointer = Memory::GetPhysicalPointer(config.GetPhysicalOutputAddress()); - if (config.is_texture_copy) { - u32 input_width = config.texture_copy.input_width * 16; - u32 input_gap = config.texture_copy.input_gap * 16; - u32 output_width = config.texture_copy.output_width * 16; - u32 output_gap = config.texture_copy.output_gap * 16; + if (config.is_texture_copy) { + u32 input_width = config.texture_copy.input_width * 16; + u32 input_gap = config.texture_copy.input_gap * 16; + u32 output_width = config.texture_copy.output_width * 16; + u32 output_gap = config.texture_copy.output_gap * 16; - size_t contiguous_input_size = config.texture_copy.size / input_width * (input_width + input_gap); - VideoCore::g_renderer->Rasterizer()->FlushRegion(config.GetPhysicalInputAddress(), contiguous_input_size); + size_t contiguous_input_size = config.texture_copy.size / input_width * (input_width + input_gap); + Memory::RasterizerFlushRegion(config.GetPhysicalInputAddress(), contiguous_input_size); - u32 remaining_size = config.texture_copy.size; - u32 remaining_input = input_width; - u32 remaining_output = output_width; - while (remaining_size > 0) { - u32 copy_size = std::min({ remaining_input, remaining_output, remaining_size }); + size_t contiguous_output_size = config.texture_copy.size / output_width * (output_width + output_gap); + Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(), contiguous_output_size); - std::memcpy(dst_pointer, src_pointer, copy_size); - src_pointer += copy_size; - dst_pointer += copy_size; + u32 remaining_size = config.texture_copy.size; + u32 remaining_input = input_width; + u32 remaining_output = output_width; + while (remaining_size > 0) { + u32 copy_size = std::min({ remaining_input, remaining_output, remaining_size }); - remaining_input -= copy_size; - remaining_output -= copy_size; - remaining_size -= copy_size; + std::memcpy(dst_pointer, src_pointer, copy_size); + src_pointer += copy_size; + dst_pointer += copy_size; - if (remaining_input == 0) { - remaining_input = input_width; - src_pointer += input_gap; - } - if (remaining_output == 0) { - remaining_output = output_width; - dst_pointer += output_gap; - } - } + remaining_input -= copy_size; + remaining_output -= copy_size; + remaining_size -= copy_size; - LOG_TRACE(HW_GPU, "TextureCopy: 0x%X bytes from 0x%08X(%u+%u)-> 0x%08X(%u+%u), flags 0x%08X", - config.texture_copy.size, - config.GetPhysicalInputAddress(), input_width, input_gap, - config.GetPhysicalOutputAddress(), output_width, output_gap, - config.flags); - - size_t contiguous_output_size = config.texture_copy.size / output_width * (output_width + output_gap); - VideoCore::g_renderer->Rasterizer()->InvalidateRegion(config.GetPhysicalOutputAddress(), contiguous_output_size); - - GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); - break; - } - - if (config.scaling > config.ScaleXY) { - LOG_CRITICAL(HW_GPU, "Unimplemented display transfer scaling mode %u", config.scaling.Value()); - UNIMPLEMENTED(); - break; - } - - if (config.input_linear && config.scaling != config.NoScale) { - LOG_CRITICAL(HW_GPU, "Scaling is only implemented on tiled input"); - UNIMPLEMENTED(); - break; - } - - bool horizontal_scale = config.scaling != config.NoScale; - bool vertical_scale = config.scaling == config.ScaleXY; - - u32 output_width = config.output_width >> horizontal_scale; - u32 output_height = config.output_height >> vertical_scale; - - u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format); - u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format); - - VideoCore::g_renderer->Rasterizer()->FlushRegion(config.GetPhysicalInputAddress(), input_size); - - for (u32 y = 0; y < output_height; ++y) { - for (u32 x = 0; x < output_width; ++x) { - Math::Vec4 src_color; - - // Calculate the [x,y] position of the input image - // based on the current output position and the scale - u32 input_x = x << horizontal_scale; - u32 input_y = y << vertical_scale; - - if (config.flip_vertically) { - // Flip the y value of the output data, - // we do this after calculating the [x,y] position of the input image - // to account for the scaling options. - y = output_height - y - 1; - } - - u32 dst_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.output_format); - u32 src_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.input_format); - u32 src_offset; - u32 dst_offset; - - if (config.input_linear) { - if (!config.dont_swizzle) { - // Interpret the input as linear and the output as tiled - u32 coarse_y = y & ~7; - u32 stride = output_width * dst_bytes_per_pixel; - - src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; - dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride; - } else { - // Both input and output are linear - src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; - dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + if (remaining_input == 0) { + remaining_input = input_width; + src_pointer += input_gap; } - } else { - if (!config.dont_swizzle) { - // Interpret the input as tiled and the output as linear - u32 coarse_y = input_y & ~7; - u32 stride = config.input_width * src_bytes_per_pixel; - - src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + coarse_y * stride; - dst_offset = (x + y * output_width) * dst_bytes_per_pixel; - } else { - // Both input and output are tiled - u32 out_coarse_y = y & ~7; - u32 out_stride = output_width * dst_bytes_per_pixel; - - u32 in_coarse_y = input_y & ~7; - u32 in_stride = config.input_width * src_bytes_per_pixel; - - src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + in_coarse_y * in_stride; - dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + out_coarse_y * out_stride; + if (remaining_output == 0) { + remaining_output = output_width; + dst_pointer += output_gap; } } - const u8* src_pixel = src_pointer + src_offset; - src_color = DecodePixel(config.input_format, src_pixel); - if (config.scaling == config.ScaleX) { - Math::Vec4 pixel = DecodePixel(config.input_format, src_pixel + src_bytes_per_pixel); - src_color = ((src_color + pixel) / 2).Cast(); - } else if (config.scaling == config.ScaleXY) { - Math::Vec4 pixel1 = DecodePixel(config.input_format, src_pixel + 1 * src_bytes_per_pixel); - Math::Vec4 pixel2 = DecodePixel(config.input_format, src_pixel + 2 * src_bytes_per_pixel); - Math::Vec4 pixel3 = DecodePixel(config.input_format, src_pixel + 3 * src_bytes_per_pixel); - src_color = (((src_color + pixel1) + (pixel2 + pixel3)) / 4).Cast(); - } + LOG_TRACE(HW_GPU, "TextureCopy: 0x%X bytes from 0x%08X(%u+%u)-> 0x%08X(%u+%u), flags 0x%08X", + config.texture_copy.size, + config.GetPhysicalInputAddress(), input_width, input_gap, + config.GetPhysicalOutputAddress(), output_width, output_gap, + config.flags); - u8* dst_pixel = dst_pointer + dst_offset; - switch (config.output_format) { - case Regs::PixelFormat::RGBA8: - Color::EncodeRGBA8(src_color, dst_pixel); - break; + GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); + break; + } - case Regs::PixelFormat::RGB8: - Color::EncodeRGB8(src_color, dst_pixel); - break; + if (config.scaling > config.ScaleXY) { + LOG_CRITICAL(HW_GPU, "Unimplemented display transfer scaling mode %u", config.scaling.Value()); + UNIMPLEMENTED(); + break; + } - case Regs::PixelFormat::RGB565: - Color::EncodeRGB565(src_color, dst_pixel); - break; + if (config.input_linear && config.scaling != config.NoScale) { + LOG_CRITICAL(HW_GPU, "Scaling is only implemented on tiled input"); + UNIMPLEMENTED(); + break; + } - case Regs::PixelFormat::RGB5A1: - Color::EncodeRGB5A1(src_color, dst_pixel); - break; + int horizontal_scale = config.scaling != config.NoScale ? 1 : 0; + int vertical_scale = config.scaling == config.ScaleXY ? 1 : 0; - case Regs::PixelFormat::RGBA4: - Color::EncodeRGBA4(src_color, dst_pixel); - break; + u32 output_width = config.output_width >> horizontal_scale; + u32 output_height = config.output_height >> vertical_scale; - default: - LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", config.output_format.Value()); - break; + u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format); + u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format); + + Memory::RasterizerFlushRegion(config.GetPhysicalInputAddress(), input_size); + Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(), output_size); + + for (u32 y = 0; y < output_height; ++y) { + for (u32 x = 0; x < output_width; ++x) { + Math::Vec4 src_color; + + // Calculate the [x,y] position of the input image + // based on the current output position and the scale + u32 input_x = x << horizontal_scale; + u32 input_y = y << vertical_scale; + + if (config.flip_vertically) { + // Flip the y value of the output data, + // we do this after calculating the [x,y] position of the input image + // to account for the scaling options. + y = output_height - y - 1; + } + + u32 dst_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.output_format); + u32 src_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.input_format); + u32 src_offset; + u32 dst_offset; + + if (config.input_linear) { + if (!config.dont_swizzle) { + // Interpret the input as linear and the output as tiled + u32 coarse_y = y & ~7; + u32 stride = output_width * dst_bytes_per_pixel; + + src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; + dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride; + } else { + // Both input and output are linear + src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; + dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + } + } else { + if (!config.dont_swizzle) { + // Interpret the input as tiled and the output as linear + u32 coarse_y = input_y & ~7; + u32 stride = config.input_width * src_bytes_per_pixel; + + src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + coarse_y * stride; + dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + } else { + // Both input and output are tiled + u32 out_coarse_y = y & ~7; + u32 out_stride = output_width * dst_bytes_per_pixel; + + u32 in_coarse_y = input_y & ~7; + u32 in_stride = config.input_width * src_bytes_per_pixel; + + src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + in_coarse_y * in_stride; + dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + out_coarse_y * out_stride; + } + } + + const u8* src_pixel = src_pointer + src_offset; + src_color = DecodePixel(config.input_format, src_pixel); + if (config.scaling == config.ScaleX) { + Math::Vec4 pixel = DecodePixel(config.input_format, src_pixel + src_bytes_per_pixel); + src_color = ((src_color + pixel) / 2).Cast(); + } else if (config.scaling == config.ScaleXY) { + Math::Vec4 pixel1 = DecodePixel(config.input_format, src_pixel + 1 * src_bytes_per_pixel); + Math::Vec4 pixel2 = DecodePixel(config.input_format, src_pixel + 2 * src_bytes_per_pixel); + Math::Vec4 pixel3 = DecodePixel(config.input_format, src_pixel + 3 * src_bytes_per_pixel); + src_color = (((src_color + pixel1) + (pixel2 + pixel3)) / 4).Cast(); + } + + u8* dst_pixel = dst_pointer + dst_offset; + switch (config.output_format) { + case Regs::PixelFormat::RGBA8: + Color::EncodeRGBA8(src_color, dst_pixel); + break; + + case Regs::PixelFormat::RGB8: + Color::EncodeRGB8(src_color, dst_pixel); + break; + + case Regs::PixelFormat::RGB565: + Color::EncodeRGB565(src_color, dst_pixel); + break; + + case Regs::PixelFormat::RGB5A1: + Color::EncodeRGB5A1(src_color, dst_pixel); + break; + + case Regs::PixelFormat::RGBA4: + Color::EncodeRGBA4(src_color, dst_pixel); + break; + + default: + LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", config.output_format.Value()); + break; + } } } - } - LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x, flags 0x%08X", + LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x, flags 0x%08X", config.output_height * output_width * GPU::Regs::BytesPerPixel(config.output_format), config.GetPhysicalInputAddress(), config.input_width.Value(), config.input_height.Value(), config.GetPhysicalOutputAddress(), output_width, output_height, config.output_format.Value(), config.flags); + } g_regs.display_transfer_config.trigger = 0; GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); - - VideoCore::g_renderer->Rasterizer()->InvalidateRegion(config.GetPhysicalOutputAddress(), output_size); } break; } diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index a00adbf53..da4c345b4 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -78,7 +78,7 @@ struct Regs { INSERT_PADDING_WORDS(0x4); - struct { + struct MemoryFillConfig { u32 address_start; u32 address_end; @@ -165,7 +165,7 @@ struct Regs { INSERT_PADDING_WORDS(0x169); - struct { + struct DisplayTransferConfig { u32 input_address; u32 output_address; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 7de5bd15d..ee9b69f81 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -15,6 +15,9 @@ #include "core/memory_setup.h" #include "core/mmio.h" +#include "video_core/renderer_base.h" +#include "video_core/video_core.h" + namespace Memory { enum class PageType { @@ -22,8 +25,12 @@ enum class PageType { Unmapped, /// Page is mapped to regular memory. This is the only type you can get pointers to. Memory, + /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and invalidation + RasterizerCachedMemory, /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. Special, + /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and invalidation + RasterizerCachedSpecial, }; struct SpecialRegion { @@ -57,6 +64,12 @@ struct PageTable { * the corresponding entry in `pointers` MUST be set to null. */ std::array attributes; + + /** + * Indicates the number of externally cached resources touching a page that should be + * flushed before the memory is accessed + */ + std::array cached_res_count; }; /// Singular page table used for the singleton process @@ -72,8 +85,15 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { while (base != end) { ASSERT_MSG(base < PageTable::NUM_ENTRIES, "out of range mapping at %08X", base); + // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be null here + if (current_page_table->attributes[base] == PageType::RasterizerCachedMemory || + current_page_table->attributes[base] == PageType::RasterizerCachedSpecial) { + RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(base << PAGE_BITS), PAGE_SIZE); + } + current_page_table->attributes[base] = type; current_page_table->pointers[base] = memory; + current_page_table->cached_res_count[base] = 0; base += 1; if (memory != nullptr) @@ -84,6 +104,7 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { void InitMemoryMap() { main_page_table.pointers.fill(nullptr); main_page_table.attributes.fill(PageType::Unmapped); + main_page_table.cached_res_count.fill(0); } void MapMemoryRegion(VAddr base, u32 size, u8* target) { @@ -106,6 +127,28 @@ void UnmapRegion(VAddr base, u32 size) { MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); } +/** + * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) + * using a VMA from the current process + */ +static u8* GetPointerFromVMA(VAddr vaddr) { + u8* direct_pointer = nullptr; + + auto& vma = Kernel::g_current_process->vm_manager.FindVMA(vaddr)->second; + switch (vma.type) { + case Kernel::VMAType::AllocatedMemoryBlock: + direct_pointer = vma.backing_block->data() + vma.offset; + break; + case Kernel::VMAType::BackingMemory: + direct_pointer = vma.backing_memory; + break; + default: + UNREACHABLE(); + } + + return direct_pointer + (vaddr - vma.base); +} + /** * This function should only be called for virtual addreses with attribute `PageType::Special`. */ @@ -126,6 +169,7 @@ template T Read(const VAddr vaddr) { const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; if (page_pointer) { + // NOTE: Avoid adding any extra logic to this fast-path block T value; std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T)); return value; @@ -139,8 +183,22 @@ T Read(const VAddr vaddr) { case PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); break; + case PageType::RasterizerCachedMemory: + { + RasterizerFlushRegion(VirtualToPhysicalAddress(vaddr), sizeof(T)); + + T value; + std::memcpy(&value, GetPointerFromVMA(vaddr), sizeof(T)); + return value; + } case PageType::Special: return ReadMMIO(GetMMIOHandler(vaddr), vaddr); + case PageType::RasterizerCachedSpecial: + { + RasterizerFlushRegion(VirtualToPhysicalAddress(vaddr), sizeof(T)); + + return ReadMMIO(GetMMIOHandler(vaddr), vaddr); + } default: UNREACHABLE(); } @@ -153,6 +211,7 @@ template void Write(const VAddr vaddr, const T data) { u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; if (page_pointer) { + // NOTE: Avoid adding any extra logic to this fast-path block std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T)); return; } @@ -165,9 +224,23 @@ void Write(const VAddr vaddr, const T data) { case PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); break; + case PageType::RasterizerCachedMemory: + { + RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(vaddr), sizeof(T)); + + std::memcpy(GetPointerFromVMA(vaddr), &data, sizeof(T)); + break; + } case PageType::Special: WriteMMIO(GetMMIOHandler(vaddr), vaddr, data); break; + case PageType::RasterizerCachedSpecial: + { + RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(vaddr), sizeof(T)); + + WriteMMIO(GetMMIOHandler(vaddr), vaddr, data); + break; + } default: UNREACHABLE(); } @@ -179,6 +252,10 @@ u8* GetPointer(const VAddr vaddr) { return page_pointer + (vaddr & PAGE_MASK); } + if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) { + return GetPointerFromVMA(vaddr); + } + LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%08x", vaddr); return nullptr; } @@ -187,6 +264,69 @@ u8* GetPhysicalPointer(PAddr address) { return GetPointer(PhysicalToVirtualAddress(address)); } +void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) { + if (start == 0) { + return; + } + + u32 num_pages = ((start + size - 1) >> PAGE_BITS) - (start >> PAGE_BITS) + 1; + PAddr paddr = start; + + for (unsigned i = 0; i < num_pages; ++i) { + VAddr vaddr = PhysicalToVirtualAddress(paddr); + u8& res_count = current_page_table->cached_res_count[vaddr >> PAGE_BITS]; + ASSERT_MSG(count_delta <= UINT8_MAX - res_count, "Rasterizer resource cache counter overflow!"); + ASSERT_MSG(count_delta >= -res_count, "Rasterizer resource cache counter underflow!"); + + // Switch page type to cached if now cached + if (res_count == 0) { + PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; + switch (page_type) { + case PageType::Memory: + page_type = PageType::RasterizerCachedMemory; + current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; + break; + case PageType::Special: + page_type = PageType::RasterizerCachedSpecial; + break; + default: + UNREACHABLE(); + } + } + + res_count += count_delta; + + // Switch page type to uncached if now uncached + if (res_count == 0) { + PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; + switch (page_type) { + case PageType::RasterizerCachedMemory: + page_type = PageType::Memory; + current_page_table->pointers[vaddr >> PAGE_BITS] = GetPointerFromVMA(vaddr & ~PAGE_MASK); + break; + case PageType::RasterizerCachedSpecial: + page_type = PageType::Special; + break; + default: + UNREACHABLE(); + } + } + paddr += PAGE_SIZE; + } +} + +void RasterizerFlushRegion(PAddr start, u32 size) { + if (VideoCore::g_renderer != nullptr) { + VideoCore::g_renderer->Rasterizer()->FlushRegion(start, size); + } +} + +void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size) { + if (VideoCore::g_renderer != nullptr) { + VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(start, size); + } +} + u8 Read8(const VAddr addr) { return Read(addr); } diff --git a/src/core/memory.h b/src/core/memory.h index 5af72b7a7..9caa3c3f5 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -148,4 +148,20 @@ VAddr PhysicalToVirtualAddress(PAddr addr); */ u8* GetPhysicalPointer(PAddr address); +/** + * Adds the supplied value to the rasterizer resource cache counter of each + * page touching the region. + */ +void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta); + +/** + * Flushes any externally cached rasterizer resources touching the given region. + */ +void RasterizerFlushRegion(PAddr start, u32 size); + +/** + * Flushes and invalidates any externally cached rasterizer resources touching the given region. + */ +void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size); + } diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index c3a9c9598..1f058c4e2 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp @@ -47,8 +47,8 @@ void DebugContext::OnEvent(Event event, void* data) { { std::unique_lock lock(breakpoint_mutex); - // Commit the hardware renderer's framebuffer so it will show on debug widgets - VideoCore::g_renderer->Rasterizer()->FlushFramebuffer(); + // Commit the rasterizer's caches so framebuffers, render targets, etc. will show on debug widgets + VideoCore::g_renderer->Rasterizer()->FlushAll(); // TODO: Should stop the CPU thread here once we multithread emulation. diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 4552ff81c..1810eca98 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -577,7 +577,7 @@ struct Regs { } } - struct { + struct FramebufferConfig { INSERT_PADDING_WORDS(0x3); union { diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 008c5827b..bf7101665 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -6,6 +6,10 @@ #include "common/common_types.h" +#include "core/hw/gpu.h" + +struct ScreenInfo; + namespace Pica { namespace Shader { struct OutputVertex; @@ -18,12 +22,6 @@ class RasterizerInterface { public: virtual ~RasterizerInterface() {} - /// Initialize API-specific GPU objects - virtual void InitObjects() = 0; - - /// Reset the rasterizer, such as flushing all caches and updating all state - virtual void Reset() = 0; - /// Queues the primitive formed by the given vertices for rendering virtual void AddTriangle(const Pica::Shader::OutputVertex& v0, const Pica::Shader::OutputVertex& v1, @@ -32,17 +30,26 @@ public: /// Draw the current batch of triangles virtual void DrawTriangles() = 0; - /// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer - virtual void FlushFramebuffer() = 0; - /// Notify rasterizer that the specified PICA register has been changed virtual void NotifyPicaRegisterChanged(u32 id) = 0; - /// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory. + /// Notify rasterizer that all caches should be flushed to 3DS memory + virtual void FlushAll() = 0; + + /// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory virtual void FlushRegion(PAddr addr, u32 size) = 0; - /// Notify rasterizer that any caches of the specified region should be discraded and reloaded from 3DS memory. - virtual void InvalidateRegion(PAddr addr, u32 size) = 0; + /// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory and invalidated + virtual void FlushAndInvalidateRegion(PAddr addr, u32 size) = 0; + + /// Attempt to use a faster method to perform a display transfer + virtual bool AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) { return false; } + + /// Attempt to use a faster method to fill a region + virtual bool AccelerateFill(const GPU::Regs::MemoryFillConfig& config) { return false; } + + /// Attempt to use a faster method to display the framebuffer to screen + virtual bool AccelerateDisplay(const GPU::Regs::FramebufferConfig& config, PAddr framebuffer_addr, u32 pixel_stride, ScreenInfo& screen_info) { return false; } }; } diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index 101f84eb9..ccd497de0 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp @@ -21,7 +21,5 @@ void RendererBase::RefreshRasterizerSetting() { } else { rasterizer = std::make_unique(); } - rasterizer->InitObjects(); - rasterizer->Reset(); } } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 6ca9f45e2..da4121c35 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -36,10 +36,7 @@ static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) { stage.GetAlphaMultiplier() == 1); } -RasterizerOpenGL::RasterizerOpenGL() : cached_fb_color_addr(0), cached_fb_depth_addr(0) { } -RasterizerOpenGL::~RasterizerOpenGL() { } - -void RasterizerOpenGL::InitObjects() { +RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { // Create sampler objects for (size_t i = 0; i < texture_samplers.size(); ++i) { texture_samplers[i].Create(); @@ -61,6 +58,10 @@ void RasterizerOpenGL::InitObjects() { uniform_block_data.dirty = true; + for (unsigned index = 0; index < lighting_luts.size(); index++) { + uniform_block_data.lut_dirty[index] = true; + } + // Set vertex attributes glVertexAttribPointer(GLShader::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); glEnableVertexAttribArray(GLShader::ATTRIBUTE_POSITION); @@ -81,70 +82,24 @@ void RasterizerOpenGL::InitObjects() { glVertexAttribPointer(GLShader::ATTRIBUTE_VIEW, 3, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, view)); glEnableVertexAttribArray(GLShader::ATTRIBUTE_VIEW); - SetShader(); - - // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation - fb_color_texture.texture.Create(); - ReconfigureColorTexture(fb_color_texture, Pica::Regs::ColorFormat::RGBA8, 1, 1); - - state.texture_units[0].texture_2d = fb_color_texture.texture.handle; - state.Apply(); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - state.texture_units[0].texture_2d = 0; - state.Apply(); - - fb_depth_texture.texture.Create(); - ReconfigureDepthTexture(fb_depth_texture, Pica::Regs::DepthFormat::D16, 1, 1); - - state.texture_units[0].texture_2d = fb_depth_texture.texture.handle; - state.Apply(); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); - - state.texture_units[0].texture_2d = 0; - state.Apply(); - - // Configure OpenGL framebuffer + // Create render framebuffer framebuffer.Create(); - state.draw.framebuffer = framebuffer.handle; + // Allocate and bind lighting lut textures + for (size_t i = 0; i < lighting_luts.size(); ++i) { + lighting_luts[i].Create(); + state.lighting_luts[i].texture_1d = lighting_luts[i].handle; + } state.Apply(); - glActiveTexture(GL_TEXTURE0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_color_texture.texture.handle, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fb_depth_texture.texture.handle, 0); - - for (size_t i = 0; i < lighting_lut.size(); ++i) { - lighting_lut[i].Create(); - state.lighting_lut[i].texture_1d = lighting_lut[i].handle; - + for (size_t i = 0; i < lighting_luts.size(); ++i) { glActiveTexture(GL_TEXTURE3 + i); - glBindTexture(GL_TEXTURE_1D, state.lighting_lut[i].texture_1d); - glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } - state.Apply(); - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - ASSERT_MSG(status == GL_FRAMEBUFFER_COMPLETE, - "OpenGL rasterizer framebuffer setup failed, status %X", status); -} - -void RasterizerOpenGL::Reset() { + // Sync fixed function OpenGL state SyncCullMode(); SyncDepthModifiers(); SyncBlendEnabled(); @@ -156,10 +111,10 @@ void RasterizerOpenGL::Reset() { SyncColorWriteMask(); SyncStencilWriteMask(); SyncDepthWriteMask(); +} - SetShader(); +RasterizerOpenGL::~RasterizerOpenGL() { - res_cache.InvalidateAll(); } /** @@ -196,47 +151,98 @@ void RasterizerOpenGL::DrawTriangles() { if (vertex_batch.empty()) return; - SyncFramebuffer(); - SyncDrawState(); + const auto& regs = Pica::g_state.regs; - if (state.draw.shader_dirty) { - SetShader(); - state.draw.shader_dirty = false; + // Sync and bind the framebuffer surfaces + CachedSurface* color_surface; + CachedSurface* depth_surface; + MathUtil::Rectangle rect; + std::tie(color_surface, depth_surface, rect) = res_cache.GetFramebufferSurfaces(regs.framebuffer); + + state.draw.draw_framebuffer = framebuffer.handle; + state.Apply(); + + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_surface != nullptr ? color_surface->texture.handle : 0, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_surface != nullptr ? depth_surface->texture.handle : 0, 0); + bool has_stencil = regs.framebuffer.depth_format == Pica::Regs::DepthFormat::D24S8; + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, (has_stencil && depth_surface != nullptr) ? depth_surface->texture.handle : 0, 0); + + if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + return; } - for (unsigned index = 0; index < lighting_lut.size(); index++) { + // Sync the viewport + // These registers hold half-width and half-height, so must be multiplied by 2 + GLsizei viewport_width = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_x).ToFloat32() * 2; + GLsizei viewport_height = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_y).ToFloat32() * 2; + + glViewport((GLint)(rect.left + regs.viewport_corner.x * color_surface->res_scale_width), + (GLint)(rect.bottom + regs.viewport_corner.y * color_surface->res_scale_height), + (GLsizei)(viewport_width * color_surface->res_scale_width), (GLsizei)(viewport_height * color_surface->res_scale_height)); + + // Sync and bind the texture surfaces + const auto pica_textures = regs.GetTextures(); + for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) { + const auto& texture = pica_textures[texture_index]; + + if (texture.enabled) { + texture_samplers[texture_index].SyncWithConfig(texture.config); + CachedSurface* surface = res_cache.GetTextureSurface(texture); + if (surface != nullptr) { + state.texture_units[texture_index].texture_2d = surface->texture.handle; + } else { + // Can occur when texture addr is null or its memory is unmapped/invalid + state.texture_units[texture_index].texture_2d = 0; + } + } else { + state.texture_units[texture_index].texture_2d = 0; + } + } + + // Sync and bind the shader + if (shader_dirty) { + SetShader(); + shader_dirty = false; + } + + // Sync the lighting luts + for (unsigned index = 0; index < lighting_luts.size(); index++) { if (uniform_block_data.lut_dirty[index]) { SyncLightingLUT(index); uniform_block_data.lut_dirty[index] = false; } } + // Sync the uniform data if (uniform_block_data.dirty) { glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), &uniform_block_data.data, GL_STATIC_DRAW); uniform_block_data.dirty = false; } + state.Apply(); + + // Draw the vertex batch glBufferData(GL_ARRAY_BUFFER, vertex_batch.size() * sizeof(HardwareVertex), vertex_batch.data(), GL_STREAM_DRAW); glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size()); + // Mark framebuffer surfaces as dirty + // TODO: Restrict invalidation area to the viewport + if (color_surface != nullptr) { + color_surface->dirty = true; + res_cache.FlushRegion(color_surface->addr, color_surface->size, color_surface, true); + } + if (depth_surface != nullptr) { + depth_surface->dirty = true; + res_cache.FlushRegion(depth_surface->addr, depth_surface->size, depth_surface, true); + } + vertex_batch.clear(); - // Flush the resource cache at the current depth and color framebuffer addresses for render-to-texture - const auto& regs = Pica::g_state.regs; - - u32 cached_fb_color_size = Pica::Regs::BytesPerColorPixel(fb_color_texture.format) - * fb_color_texture.width * fb_color_texture.height; - - u32 cached_fb_depth_size = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format) - * fb_depth_texture.width * fb_depth_texture.height; - - res_cache.InvalidateInRange(cached_fb_color_addr, cached_fb_color_size, true); - res_cache.InvalidateInRange(cached_fb_depth_addr, cached_fb_depth_size, true); -} - -void RasterizerOpenGL::FlushFramebuffer() { - CommitColorBuffer(); - CommitDepthBuffer(); + // Unbind textures for potential future use as framebuffer attachments + for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) { + state.texture_units[texture_index].texture_2d = 0; + } + state.Apply(); } void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { @@ -268,7 +274,7 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { // Alpha test case PICA_REG_INDEX(output_merger.alpha_test): SyncAlphaTest(); - state.draw.shader_dirty = true; + shader_dirty = true; break; // Sync GL stencil test + stencil write mask @@ -334,7 +340,7 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { case PICA_REG_INDEX(tev_stage5.color_op): case PICA_REG_INDEX(tev_stage5.color_scale): case PICA_REG_INDEX(tev_combiner_buffer_input): - state.draw.shader_dirty = true; + shader_dirty = true; break; case PICA_REG_INDEX(tev_stage0.const_r): SyncTevConstColor(0, regs.tev_stage0); @@ -521,41 +527,257 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { } } -void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) { - const auto& regs = Pica::g_state.regs; - - u32 cached_fb_color_size = Pica::Regs::BytesPerColorPixel(fb_color_texture.format) - * fb_color_texture.width * fb_color_texture.height; - - u32 cached_fb_depth_size = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format) - * fb_depth_texture.width * fb_depth_texture.height; - - // If source memory region overlaps 3DS framebuffers, commit them before the copy happens - if (MathUtil::IntervalsIntersect(addr, size, cached_fb_color_addr, cached_fb_color_size)) - CommitColorBuffer(); - - if (MathUtil::IntervalsIntersect(addr, size, cached_fb_depth_addr, cached_fb_depth_size)) - CommitDepthBuffer(); +void RasterizerOpenGL::FlushAll() { + res_cache.FlushAll(); } -void RasterizerOpenGL::InvalidateRegion(PAddr addr, u32 size) { - const auto& regs = Pica::g_state.regs; +void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) { + res_cache.FlushRegion(addr, size, nullptr, false); +} - u32 cached_fb_color_size = Pica::Regs::BytesPerColorPixel(fb_color_texture.format) - * fb_color_texture.width * fb_color_texture.height; +void RasterizerOpenGL::FlushAndInvalidateRegion(PAddr addr, u32 size) { + res_cache.FlushRegion(addr, size, nullptr, true); +} - u32 cached_fb_depth_size = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format) - * fb_depth_texture.width * fb_depth_texture.height; +bool RasterizerOpenGL::AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) { + using PixelFormat = CachedSurface::PixelFormat; + using SurfaceType = CachedSurface::SurfaceType; - // If modified memory region overlaps 3DS framebuffers, reload their contents into OpenGL - if (MathUtil::IntervalsIntersect(addr, size, cached_fb_color_addr, cached_fb_color_size)) - ReloadColorBuffer(); + if (config.is_texture_copy) { + // TODO(tfarley): Try to hardware accelerate this + return false; + } - if (MathUtil::IntervalsIntersect(addr, size, cached_fb_depth_addr, cached_fb_depth_size)) - ReloadDepthBuffer(); + CachedSurface src_params; + src_params.addr = config.GetPhysicalInputAddress(); + src_params.width = config.output_width; + src_params.height = config.output_height; + src_params.is_tiled = !config.input_linear; + src_params.pixel_format = CachedSurface::PixelFormatFromGPUPixelFormat(config.input_format); - // Notify cache of flush in case the region touches a cached resource - res_cache.InvalidateInRange(addr, size); + CachedSurface dst_params; + dst_params.addr = config.GetPhysicalOutputAddress(); + dst_params.width = config.scaling != config.NoScale ? config.output_width / 2 : config.output_width.Value(); + dst_params.height = config.scaling == config.ScaleXY ? config.output_height / 2 : config.output_height.Value(); + dst_params.is_tiled = config.input_linear != config.dont_swizzle; + dst_params.pixel_format = CachedSurface::PixelFormatFromGPUPixelFormat(config.output_format); + + MathUtil::Rectangle src_rect; + CachedSurface* src_surface = res_cache.GetSurfaceRect(src_params, false, true, src_rect); + + if (src_surface == nullptr) { + return false; + } + + // Require destination surface to have same resolution scale as source to preserve scaling + dst_params.res_scale_width = src_surface->res_scale_width; + dst_params.res_scale_height = src_surface->res_scale_height; + + MathUtil::Rectangle dst_rect; + CachedSurface* dst_surface = res_cache.GetSurfaceRect(dst_params, true, false, dst_rect); + + if (dst_surface == nullptr) { + return false; + } + + // Don't accelerate if the src and dst surfaces are the same + if (src_surface == dst_surface) { + return false; + } + + if (config.flip_vertically) { + std::swap(dst_rect.top, dst_rect.bottom); + } + + if (!res_cache.TryBlitSurfaces(src_surface, src_rect, dst_surface, dst_rect)) { + return false; + } + + u32 dst_size = dst_params.width * dst_params.height * CachedSurface::GetFormatBpp(dst_params.pixel_format) / 8; + dst_surface->dirty = true; + res_cache.FlushRegion(config.GetPhysicalOutputAddress(), dst_size, dst_surface, true); + return true; +} + +bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) { + using PixelFormat = CachedSurface::PixelFormat; + using SurfaceType = CachedSurface::SurfaceType; + + CachedSurface* dst_surface = res_cache.TryGetFillSurface(config); + + if (dst_surface == nullptr) { + return false; + } + + OpenGLState cur_state = OpenGLState::GetCurState(); + + SurfaceType dst_type = CachedSurface::GetFormatType(dst_surface->pixel_format); + + GLuint old_fb = cur_state.draw.draw_framebuffer; + cur_state.draw.draw_framebuffer = framebuffer.handle; + // TODO: When scissor test is implemented, need to disable scissor test in cur_state here so Clear call isn't affected + cur_state.Apply(); + + if (dst_type == SurfaceType::Color || dst_type == SurfaceType::Texture) { + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_surface->texture.handle, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + + if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + return false; + } + + GLfloat color_values[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + + // TODO: Handle additional pixel format and fill value size combinations to accelerate more cases + // For instance, checking if fill value's bytes/bits repeat to allow filling I8/A8/I4/A4/... + // Currently only handles formats that are multiples of the fill value size + + if (config.fill_24bit) { + switch (dst_surface->pixel_format) { + case PixelFormat::RGB8: + color_values[0] = config.value_24bit_r / 255.0f; + color_values[1] = config.value_24bit_g / 255.0f; + color_values[2] = config.value_24bit_b / 255.0f; + break; + default: + return false; + } + } else if (config.fill_32bit) { + u32 value = config.value_32bit; + + switch (dst_surface->pixel_format) { + case PixelFormat::RGBA8: + color_values[0] = (value >> 24) / 255.0f; + color_values[1] = ((value >> 16) & 0xFF) / 255.0f; + color_values[2] = ((value >> 8) & 0xFF) / 255.0f; + color_values[3] = (value & 0xFF) / 255.0f; + break; + default: + return false; + } + } else { + u16 value_16bit = config.value_16bit.Value(); + Math::Vec4 color; + + switch (dst_surface->pixel_format) { + case PixelFormat::RGBA8: + color_values[0] = (value_16bit >> 8) / 255.0f; + color_values[1] = (value_16bit & 0xFF) / 255.0f; + color_values[2] = color_values[0]; + color_values[3] = color_values[1]; + break; + case PixelFormat::RGB5A1: + color = Color::DecodeRGB5A1((const u8*)&value_16bit); + color_values[0] = color[0] / 31.0f; + color_values[1] = color[1] / 31.0f; + color_values[2] = color[2] / 31.0f; + color_values[3] = color[3]; + break; + case PixelFormat::RGB565: + color = Color::DecodeRGB565((const u8*)&value_16bit); + color_values[0] = color[0] / 31.0f; + color_values[1] = color[1] / 63.0f; + color_values[2] = color[2] / 31.0f; + break; + case PixelFormat::RGBA4: + color = Color::DecodeRGBA4((const u8*)&value_16bit); + color_values[0] = color[0] / 15.0f; + color_values[1] = color[1] / 15.0f; + color_values[2] = color[2] / 15.0f; + color_values[3] = color[3] / 15.0f; + break; + case PixelFormat::IA8: + case PixelFormat::RG8: + color_values[0] = (value_16bit >> 8) / 255.0f; + color_values[1] = (value_16bit & 0xFF) / 255.0f; + break; + default: + return false; + } + } + + cur_state.color_mask.red_enabled = true; + cur_state.color_mask.green_enabled = true; + cur_state.color_mask.blue_enabled = true; + cur_state.color_mask.alpha_enabled = true; + cur_state.Apply(); + glClearBufferfv(GL_COLOR, 0, color_values); + } else if (dst_type == SurfaceType::Depth) { + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, dst_surface->texture.handle, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + + if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + return false; + } + + GLfloat value_float; + if (dst_surface->pixel_format == CachedSurface::PixelFormat::D16) { + value_float = config.value_32bit / 65535.0f; // 2^16 - 1 + } else if (dst_surface->pixel_format == CachedSurface::PixelFormat::D24) { + value_float = config.value_32bit / 16777215.0f; // 2^24 - 1 + } + + cur_state.depth.write_mask = true; + cur_state.Apply(); + glClearBufferfv(GL_DEPTH, 0, &value_float); + } else if (dst_type == SurfaceType::DepthStencil) { + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, dst_surface->texture.handle, 0); + + if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + return false; + } + + GLfloat value_float = (config.value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1 + GLint value_int = (config.value_32bit >> 24); + + cur_state.depth.write_mask = true; + cur_state.stencil.write_mask = true; + cur_state.Apply(); + glClearBufferfi(GL_DEPTH_STENCIL, 0, value_float, value_int); + } + + cur_state.draw.draw_framebuffer = old_fb; + // TODO: Return scissor test to previous value when scissor test is implemented + cur_state.Apply(); + + dst_surface->dirty = true; + res_cache.FlushRegion(dst_surface->addr, dst_surface->size, dst_surface, true); + return true; +} + +bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& config, PAddr framebuffer_addr, u32 pixel_stride, ScreenInfo& screen_info) { + if (framebuffer_addr == 0) { + return false; + } + + CachedSurface src_params; + src_params.addr = framebuffer_addr; + src_params.width = config.width; + src_params.height = config.height; + src_params.stride = pixel_stride; + src_params.is_tiled = false; + src_params.pixel_format = CachedSurface::PixelFormatFromGPUPixelFormat(config.color_format); + + MathUtil::Rectangle src_rect; + CachedSurface* src_surface = res_cache.GetSurfaceRect(src_params, false, true, src_rect); + + if (src_surface == nullptr) { + return false; + } + + u32 scaled_width = src_surface->GetScaledWidth(); + u32 scaled_height = src_surface->GetScaledHeight(); + + screen_info.display_texcoords = MathUtil::Rectangle((float)src_rect.top / (float)scaled_height, + (float)src_rect.left / (float)scaled_width, + (float)src_rect.bottom / (float)scaled_height, + (float)src_rect.right / (float)scaled_width); + + screen_info.display_texture = src_surface->texture.handle; + + return true; } void RasterizerOpenGL::SamplerInfo::Create() { @@ -597,108 +819,6 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Pica::Regs::TextureConf } } -void RasterizerOpenGL::ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height) { - GLint internal_format; - - texture.format = format; - texture.width = width; - texture.height = height; - - switch (format) { - case Pica::Regs::ColorFormat::RGBA8: - internal_format = GL_RGBA; - texture.gl_format = GL_RGBA; - texture.gl_type = GL_UNSIGNED_INT_8_8_8_8; - break; - - case Pica::Regs::ColorFormat::RGB8: - // This pixel format uses BGR since GL_UNSIGNED_BYTE specifies byte-order, unlike every - // specific OpenGL type used in this function using native-endian (that is, little-endian - // mostly everywhere) for words or half-words. - // TODO: check how those behave on big-endian processors. - internal_format = GL_RGB; - texture.gl_format = GL_BGR; - texture.gl_type = GL_UNSIGNED_BYTE; - break; - - case Pica::Regs::ColorFormat::RGB5A1: - internal_format = GL_RGBA; - texture.gl_format = GL_RGBA; - texture.gl_type = GL_UNSIGNED_SHORT_5_5_5_1; - break; - - case Pica::Regs::ColorFormat::RGB565: - internal_format = GL_RGB; - texture.gl_format = GL_RGB; - texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; - break; - - case Pica::Regs::ColorFormat::RGBA4: - internal_format = GL_RGBA; - texture.gl_format = GL_RGBA; - texture.gl_type = GL_UNSIGNED_SHORT_4_4_4_4; - break; - - default: - LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer texture color format %x", format); - UNIMPLEMENTED(); - break; - } - - state.texture_units[0].texture_2d = texture.texture.handle; - state.Apply(); - - glActiveTexture(GL_TEXTURE0); - glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, - texture.gl_format, texture.gl_type, nullptr); - - state.texture_units[0].texture_2d = 0; - state.Apply(); -} - -void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height) { - GLint internal_format; - - texture.format = format; - texture.width = width; - texture.height = height; - - switch (format) { - case Pica::Regs::DepthFormat::D16: - internal_format = GL_DEPTH_COMPONENT16; - texture.gl_format = GL_DEPTH_COMPONENT; - texture.gl_type = GL_UNSIGNED_SHORT; - break; - - case Pica::Regs::DepthFormat::D24: - internal_format = GL_DEPTH_COMPONENT24; - texture.gl_format = GL_DEPTH_COMPONENT; - texture.gl_type = GL_UNSIGNED_INT; - break; - - case Pica::Regs::DepthFormat::D24S8: - internal_format = GL_DEPTH24_STENCIL8; - texture.gl_format = GL_DEPTH_STENCIL; - texture.gl_type = GL_UNSIGNED_INT_24_8; - break; - - default: - LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer texture depth format %x", format); - UNIMPLEMENTED(); - break; - } - - state.texture_units[0].texture_2d = texture.texture.handle; - state.Apply(); - - glActiveTexture(GL_TEXTURE0); - glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, - texture.gl_format, texture.gl_type, nullptr); - - state.texture_units[0].texture_2d = 0; - state.Apply(); -} - void RasterizerOpenGL::SetShader() { PicaShaderConfig config = PicaShaderConfig::CurrentConfig(); std::unique_ptr shader = std::make_unique(); @@ -761,83 +881,6 @@ void RasterizerOpenGL::SetShader() { } } -void RasterizerOpenGL::SyncFramebuffer() { - const auto& regs = Pica::g_state.regs; - - PAddr new_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress(); - Pica::Regs::ColorFormat new_fb_color_format = regs.framebuffer.color_format; - - PAddr new_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress(); - Pica::Regs::DepthFormat new_fb_depth_format = regs.framebuffer.depth_format; - - bool fb_size_changed = fb_color_texture.width != static_cast(regs.framebuffer.GetWidth()) || - fb_color_texture.height != static_cast(regs.framebuffer.GetHeight()); - - bool color_fb_prop_changed = fb_color_texture.format != new_fb_color_format || - fb_size_changed; - - bool depth_fb_prop_changed = fb_depth_texture.format != new_fb_depth_format || - fb_size_changed; - - bool color_fb_modified = cached_fb_color_addr != new_fb_color_addr || - color_fb_prop_changed; - - bool depth_fb_modified = cached_fb_depth_addr != new_fb_depth_addr || - depth_fb_prop_changed; - - // Commit if framebuffer modified in any way - if (color_fb_modified) - CommitColorBuffer(); - - if (depth_fb_modified) - CommitDepthBuffer(); - - // Reconfigure framebuffer textures if any property has changed - if (color_fb_prop_changed) { - ReconfigureColorTexture(fb_color_texture, new_fb_color_format, - regs.framebuffer.GetWidth(), regs.framebuffer.GetHeight()); - } - - if (depth_fb_prop_changed) { - ReconfigureDepthTexture(fb_depth_texture, new_fb_depth_format, - regs.framebuffer.GetWidth(), regs.framebuffer.GetHeight()); - - // Only attach depth buffer as stencil if it supports stencil - switch (new_fb_depth_format) { - case Pica::Regs::DepthFormat::D16: - case Pica::Regs::DepthFormat::D24: - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - break; - - case Pica::Regs::DepthFormat::D24S8: - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, fb_depth_texture.texture.handle, 0); - break; - - default: - LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer depth format %x", new_fb_depth_format); - UNIMPLEMENTED(); - break; - } - } - - // Load buffer data again if fb modified in any way - if (color_fb_modified) { - cached_fb_color_addr = new_fb_color_addr; - - ReloadColorBuffer(); - } - - if (depth_fb_modified) { - cached_fb_depth_addr = new_fb_depth_addr; - - ReloadDepthBuffer(); - } - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - ASSERT_MSG(status == GL_FRAMEBUFFER_COMPLETE, - "OpenGL rasterizer framebuffer setup failed, status %X", status); -} - void RasterizerOpenGL::SyncCullMode() { const auto& regs = Pica::g_state.regs; @@ -1034,229 +1077,3 @@ void RasterizerOpenGL::SyncLightPosition(int light_index) { uniform_block_data.dirty = true; } } - -void RasterizerOpenGL::SyncDrawState() { - const auto& regs = Pica::g_state.regs; - - // Sync the viewport - GLsizei viewport_width = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_x).ToFloat32() * 2; - GLsizei viewport_height = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_y).ToFloat32() * 2; - - // OpenGL uses different y coordinates, so negate corner offset and flip origin - // TODO: Ensure viewport_corner.x should not be negated or origin flipped - // TODO: Use floating-point viewports for accuracy if supported - glViewport((GLsizei)regs.viewport_corner.x, - (GLsizei)regs.viewport_corner.y, - viewport_width, viewport_height); - - // Sync bound texture(s), upload if not cached - const auto pica_textures = regs.GetTextures(); - for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) { - const auto& texture = pica_textures[texture_index]; - - if (texture.enabled) { - texture_samplers[texture_index].SyncWithConfig(texture.config); - res_cache.LoadAndBindTexture(state, texture_index, texture); - } else { - state.texture_units[texture_index].texture_2d = 0; - } - } - - state.draw.uniform_buffer = uniform_buffer.handle; - state.Apply(); -} - -MICROPROFILE_DEFINE(OpenGL_FramebufferReload, "OpenGL", "FB Reload", MP_RGB(70, 70, 200)); - -void RasterizerOpenGL::ReloadColorBuffer() { - u8* color_buffer = Memory::GetPhysicalPointer(cached_fb_color_addr); - - if (color_buffer == nullptr) - return; - - MICROPROFILE_SCOPE(OpenGL_FramebufferReload); - - u32 bytes_per_pixel = Pica::Regs::BytesPerColorPixel(fb_color_texture.format); - - std::unique_ptr temp_fb_color_buffer(new u8[fb_color_texture.width * fb_color_texture.height * bytes_per_pixel]); - - // Directly copy pixels. Internal OpenGL color formats are consistent so no conversion is necessary. - for (int y = 0; y < fb_color_texture.height; ++y) { - for (int x = 0; x < fb_color_texture.width; ++x) { - const u32 coarse_y = y & ~7; - u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel; - u32 gl_pixel_index = (x + (fb_color_texture.height - 1 - y) * fb_color_texture.width) * bytes_per_pixel; - - u8* pixel = color_buffer + dst_offset; - memcpy(&temp_fb_color_buffer[gl_pixel_index], pixel, bytes_per_pixel); - } - } - - state.texture_units[0].texture_2d = fb_color_texture.texture.handle; - state.Apply(); - - glActiveTexture(GL_TEXTURE0); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_color_texture.width, fb_color_texture.height, - fb_color_texture.gl_format, fb_color_texture.gl_type, temp_fb_color_buffer.get()); - - state.texture_units[0].texture_2d = 0; - state.Apply(); -} - -void RasterizerOpenGL::ReloadDepthBuffer() { - if (cached_fb_depth_addr == 0) - return; - - // TODO: Appears to work, but double-check endianness of depth values and order of depth-stencil - u8* depth_buffer = Memory::GetPhysicalPointer(cached_fb_depth_addr); - - if (depth_buffer == nullptr) - return; - - MICROPROFILE_SCOPE(OpenGL_FramebufferReload); - - u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format); - - // OpenGL needs 4 bpp alignment for D24 - u32 gl_bpp = bytes_per_pixel == 3 ? 4 : bytes_per_pixel; - - std::unique_ptr temp_fb_depth_buffer(new u8[fb_depth_texture.width * fb_depth_texture.height * gl_bpp]); - - u8* temp_fb_depth_data = bytes_per_pixel == 3 ? (temp_fb_depth_buffer.get() + 1) : temp_fb_depth_buffer.get(); - - if (fb_depth_texture.format == Pica::Regs::DepthFormat::D24S8) { - for (int y = 0; y < fb_depth_texture.height; ++y) { - for (int x = 0; x < fb_depth_texture.width; ++x) { - const u32 coarse_y = y & ~7; - u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; - u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width); - - u8* pixel = depth_buffer + dst_offset; - u32 depth_stencil = *(u32*)pixel; - ((u32*)temp_fb_depth_data)[gl_pixel_index] = (depth_stencil << 8) | (depth_stencil >> 24); - } - } - } else { - for (int y = 0; y < fb_depth_texture.height; ++y) { - for (int x = 0; x < fb_depth_texture.width; ++x) { - const u32 coarse_y = y & ~7; - u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; - u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width) * gl_bpp; - - u8* pixel = depth_buffer + dst_offset; - memcpy(&temp_fb_depth_data[gl_pixel_index], pixel, bytes_per_pixel); - } - } - } - - state.texture_units[0].texture_2d = fb_depth_texture.texture.handle; - state.Apply(); - - glActiveTexture(GL_TEXTURE0); - if (fb_depth_texture.format == Pica::Regs::DepthFormat::D24S8) { - // TODO(Subv): There is a bug with Intel Windows drivers that makes glTexSubImage2D not change the stencil buffer. - // The bug has been reported to Intel (https://communities.intel.com/message/324464) - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, fb_depth_texture.width, fb_depth_texture.height, 0, - GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, temp_fb_depth_buffer.get()); - } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_depth_texture.width, fb_depth_texture.height, - fb_depth_texture.gl_format, fb_depth_texture.gl_type, temp_fb_depth_buffer.get()); - } - - state.texture_units[0].texture_2d = 0; - state.Apply(); -} - -Common::Profiling::TimingCategory buffer_commit_category("Framebuffer Commit"); -MICROPROFILE_DEFINE(OpenGL_FramebufferCommit, "OpenGL", "FB Commit", MP_RGB(70, 70, 200)); - -void RasterizerOpenGL::CommitColorBuffer() { - if (cached_fb_color_addr != 0) { - u8* color_buffer = Memory::GetPhysicalPointer(cached_fb_color_addr); - - if (color_buffer != nullptr) { - Common::Profiling::ScopeTimer timer(buffer_commit_category); - MICROPROFILE_SCOPE(OpenGL_FramebufferCommit); - - u32 bytes_per_pixel = Pica::Regs::BytesPerColorPixel(fb_color_texture.format); - - std::unique_ptr temp_gl_color_buffer(new u8[fb_color_texture.width * fb_color_texture.height * bytes_per_pixel]); - - state.texture_units[0].texture_2d = fb_color_texture.texture.handle; - state.Apply(); - - glActiveTexture(GL_TEXTURE0); - glGetTexImage(GL_TEXTURE_2D, 0, fb_color_texture.gl_format, fb_color_texture.gl_type, temp_gl_color_buffer.get()); - - state.texture_units[0].texture_2d = 0; - state.Apply(); - - // Directly copy pixels. Internal OpenGL color formats are consistent so no conversion is necessary. - for (int y = 0; y < fb_color_texture.height; ++y) { - for (int x = 0; x < fb_color_texture.width; ++x) { - const u32 coarse_y = y & ~7; - u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel; - u32 gl_pixel_index = x * bytes_per_pixel + (fb_color_texture.height - 1 - y) * fb_color_texture.width * bytes_per_pixel; - - u8* pixel = color_buffer + dst_offset; - memcpy(pixel, &temp_gl_color_buffer[gl_pixel_index], bytes_per_pixel); - } - } - } - } -} - -void RasterizerOpenGL::CommitDepthBuffer() { - if (cached_fb_depth_addr != 0) { - // TODO: Output seems correct visually, but doesn't quite match sw renderer output. One of them is wrong. - u8* depth_buffer = Memory::GetPhysicalPointer(cached_fb_depth_addr); - - if (depth_buffer != nullptr) { - Common::Profiling::ScopeTimer timer(buffer_commit_category); - MICROPROFILE_SCOPE(OpenGL_FramebufferCommit); - - u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format); - - // OpenGL needs 4 bpp alignment for D24 - u32 gl_bpp = bytes_per_pixel == 3 ? 4 : bytes_per_pixel; - - std::unique_ptr temp_gl_depth_buffer(new u8[fb_depth_texture.width * fb_depth_texture.height * gl_bpp]); - - state.texture_units[0].texture_2d = fb_depth_texture.texture.handle; - state.Apply(); - - glActiveTexture(GL_TEXTURE0); - glGetTexImage(GL_TEXTURE_2D, 0, fb_depth_texture.gl_format, fb_depth_texture.gl_type, temp_gl_depth_buffer.get()); - - state.texture_units[0].texture_2d = 0; - state.Apply(); - - u8* temp_gl_depth_data = bytes_per_pixel == 3 ? (temp_gl_depth_buffer.get() + 1) : temp_gl_depth_buffer.get(); - - if (fb_depth_texture.format == Pica::Regs::DepthFormat::D24S8) { - for (int y = 0; y < fb_depth_texture.height; ++y) { - for (int x = 0; x < fb_depth_texture.width; ++x) { - const u32 coarse_y = y & ~7; - u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; - u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width); - - u8* pixel = depth_buffer + dst_offset; - u32 depth_stencil = ((u32*)temp_gl_depth_data)[gl_pixel_index]; - *(u32*)pixel = (depth_stencil >> 8) | (depth_stencil << 24); - } - } - } else { - for (int y = 0; y < fb_depth_texture.height; ++y) { - for (int x = 0; x < fb_depth_texture.width; ++x) { - const u32 coarse_y = y & ~7; - u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel; - u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width) * gl_bpp; - - u8* pixel = depth_buffer + dst_offset; - memcpy(pixel, &temp_gl_depth_data[gl_pixel_index], bytes_per_pixel); - } - } - } - } - } -} diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 390349a0c..5aa638985 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -19,6 +19,7 @@ #include "video_core/renderer_opengl/gl_rasterizer_cache.h" #include "video_core/renderer_opengl/gl_state.h" #include "video_core/renderer_opengl/pica_to_gl.h" +#include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/shader/shader_interpreter.h" /** @@ -191,16 +192,17 @@ public: RasterizerOpenGL(); ~RasterizerOpenGL() override; - void InitObjects() override; - void Reset() override; void AddTriangle(const Pica::Shader::OutputVertex& v0, const Pica::Shader::OutputVertex& v1, const Pica::Shader::OutputVertex& v2) override; void DrawTriangles() override; - void FlushFramebuffer() override; void NotifyPicaRegisterChanged(u32 id) override; + void FlushAll() override; void FlushRegion(PAddr addr, u32 size) override; - void InvalidateRegion(PAddr addr, u32 size) override; + void FlushAndInvalidateRegion(PAddr addr, u32 size) override; + bool AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) override; + bool AccelerateFill(const GPU::Regs::MemoryFillConfig& config) override; + bool AccelerateDisplay(const GPU::Regs::FramebufferConfig& config, PAddr framebuffer_addr, u32 pixel_stride, ScreenInfo& screen_info) override; /// OpenGL shader generated for a given Pica register state struct PicaShader { @@ -210,26 +212,6 @@ public: private: - /// Structure used for storing information about color textures - struct TextureInfo { - OGLTexture texture; - GLsizei width; - GLsizei height; - Pica::Regs::ColorFormat format; - GLenum gl_format; - GLenum gl_type; - }; - - /// Structure used for storing information about depth textures - struct DepthTextureInfo { - OGLTexture texture; - GLsizei width; - GLsizei height; - Pica::Regs::DepthFormat format; - GLenum gl_format; - GLenum gl_type; - }; - struct SamplerInfo { using TextureConfig = Pica::Regs::TextureConfig; @@ -311,18 +293,9 @@ private: static_assert(sizeof(UniformData) == 0x310, "The size of the UniformData structure has changed, update the structure in the shader"); static_assert(sizeof(UniformData) < 16384, "UniformData structure must be less than 16kb as per the OpenGL spec"); - /// Reconfigure the OpenGL color texture to use the given format and dimensions - void ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height); - - /// Reconfigure the OpenGL depth texture to use the given format and dimensions - void ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height); - /// Sets the OpenGL shader in accordance with the current PICA register state void SetShader(); - /// Syncs the state and contents of the OpenGL framebuffer to match the current PICA framebuffer - void SyncFramebuffer(); - /// Syncs the cull mode to match the PICA register void SyncCullMode(); @@ -386,45 +359,15 @@ private: /// Syncs the specified light's specular 1 color to match the PICA register void SyncLightSpecular1(int light_index); - /// Syncs the remaining OpenGL drawing state to match the current PICA state - void SyncDrawState(); - - /// Copies the 3DS color framebuffer into the OpenGL color framebuffer texture - void ReloadColorBuffer(); - - /// Copies the 3DS depth framebuffer into the OpenGL depth framebuffer texture - void ReloadDepthBuffer(); - - /** - * Save the current OpenGL color framebuffer to the current PICA framebuffer in 3DS memory - * Loads the OpenGL framebuffer textures into temporary buffers - * Then copies into the 3DS framebuffer using proper Morton order - */ - void CommitColorBuffer(); - - /** - * Save the current OpenGL depth framebuffer to the current PICA framebuffer in 3DS memory - * Loads the OpenGL framebuffer textures into temporary buffers - * Then copies into the 3DS framebuffer using proper Morton order - */ - void CommitDepthBuffer(); + OpenGLState state; RasterizerCacheOpenGL res_cache; std::vector vertex_batch; - OpenGLState state; - - PAddr cached_fb_color_addr; - PAddr cached_fb_depth_addr; - - // Hardware rasterizer - std::array texture_samplers; - TextureInfo fb_color_texture; - DepthTextureInfo fb_depth_texture; - std::unordered_map> shader_cache; const PicaShader* current_shader = nullptr; + bool shader_dirty; struct { UniformData data; @@ -432,11 +375,12 @@ private: bool dirty; } uniform_block_data; + std::array texture_samplers; OGLVertexArray vertex_array; OGLBuffer vertex_buffer; OGLBuffer uniform_buffer; OGLFramebuffer framebuffer; - std::array lighting_lut; + std::array lighting_luts; std::array, 6> lighting_lut_data; }; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 1323c12e4..55c2fb283 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -2,8 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include +#include +#include "common/emu_window.h" #include "common/hash.h" #include "common/math_util.h" #include "common/microprofile.h" @@ -12,71 +13,693 @@ #include "core/memory.h" #include "video_core/debug_utils/debug_utils.h" +#include "video_core/pica_state.h" #include "video_core/renderer_opengl/gl_rasterizer_cache.h" #include "video_core/renderer_opengl/pica_to_gl.h" +#include "video_core/utils.h" +#include "video_core/video_core.h" + +struct FormatTuple { + GLint internal_format; + GLenum format; + GLenum type; +}; + +static const std::array fb_format_tuples = {{ + { GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8 }, // RGBA8 + { GL_RGB8, GL_BGR, GL_UNSIGNED_BYTE }, // RGB8 + { GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 }, // RGB5A1 + { GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5 }, // RGB565 + { GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 }, // RGBA4 +}}; + +static const std::array depth_format_tuples = {{ + { GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT }, // D16 + {}, + { GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT }, // D24 + { GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8 }, // D24S8 +}}; + +RasterizerCacheOpenGL::RasterizerCacheOpenGL() { + transfer_framebuffers[0].Create(); + transfer_framebuffers[1].Create(); +} RasterizerCacheOpenGL::~RasterizerCacheOpenGL() { - InvalidateAll(); + FlushAll(); } -MICROPROFILE_DEFINE(OpenGL_TextureUpload, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); +static void MortonCopyPixels(CachedSurface::PixelFormat pixel_format, u32 width, u32 height, u32 bytes_per_pixel, u32 gl_bytes_per_pixel, u8* morton_data, u8* gl_data, bool morton_to_gl) { + using PixelFormat = CachedSurface::PixelFormat; -void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::DebugUtils::TextureInfo& info) { - const auto cached_texture = texture_cache.find(info.physical_address); + u8* data_ptrs[2]; + u32 depth_stencil_shifts[2] = {24, 8}; - if (cached_texture != texture_cache.end()) { - state.texture_units[texture_unit].texture_2d = cached_texture->second->texture.handle; - state.Apply(); - } else { - MICROPROFILE_SCOPE(OpenGL_TextureUpload); + if (morton_to_gl) { + std::swap(depth_stencil_shifts[0], depth_stencil_shifts[1]); + } - std::unique_ptr new_texture = std::make_unique(); + if (pixel_format == PixelFormat::D24S8) { + for (unsigned y = 0; y < height; ++y) { + for (unsigned x = 0; x < width; ++x) { + const u32 coarse_y = y & ~7; + u32 morton_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * width * bytes_per_pixel; + u32 gl_pixel_index = (x + (height - 1 - y) * width) * gl_bytes_per_pixel; - new_texture->texture.Create(); - state.texture_units[texture_unit].texture_2d = new_texture->texture.handle; - state.Apply(); - glActiveTexture(GL_TEXTURE0 + texture_unit); + data_ptrs[morton_to_gl] = morton_data + morton_offset; + data_ptrs[!morton_to_gl] = &gl_data[gl_pixel_index]; - u8* texture_src_data = Memory::GetPhysicalPointer(info.physical_address); + // Swap depth and stencil value ordering since 3DS does not match OpenGL + u32 depth_stencil; + memcpy(&depth_stencil, data_ptrs[1], sizeof(u32)); + depth_stencil = (depth_stencil << depth_stencil_shifts[0]) | (depth_stencil >> depth_stencil_shifts[1]); - new_texture->width = info.width; - new_texture->height = info.height; - new_texture->size = info.stride * info.height; - new_texture->addr = info.physical_address; - new_texture->hash = Common::ComputeHash64(texture_src_data, new_texture->size); - - std::unique_ptr[]> temp_texture_buffer_rgba(new Math::Vec4[info.width * info.height]); - - for (int y = 0; y < info.height; ++y) { - for (int x = 0; x < info.width; ++x) { - temp_texture_buffer_rgba[x + info.width * y] = Pica::DebugUtils::LookupTexture(texture_src_data, x, info.height - 1 - y, info); + memcpy(data_ptrs[0], &depth_stencil, sizeof(u32)); } } + } else { + for (unsigned y = 0; y < height; ++y) { + for (unsigned x = 0; x < width; ++x) { + const u32 coarse_y = y & ~7; + u32 morton_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * width * bytes_per_pixel; + u32 gl_pixel_index = (x + (height - 1 - y) * width) * gl_bytes_per_pixel; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, info.width, info.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp_texture_buffer_rgba.get()); + data_ptrs[morton_to_gl] = morton_data + morton_offset; + data_ptrs[!morton_to_gl] = &gl_data[gl_pixel_index]; - texture_cache.emplace(info.physical_address, std::move(new_texture)); - } -} - -void RasterizerCacheOpenGL::InvalidateInRange(PAddr addr, u32 size, bool ignore_hash) { - // TODO: Optimize by also inserting upper bound (addr + size) of each texture into the same map and also narrow using lower_bound - auto cache_upper_bound = texture_cache.upper_bound(addr + size); - - for (auto it = texture_cache.begin(); it != cache_upper_bound;) { - const auto& info = *it->second; - - // Flush the texture only if the memory region intersects and a change is detected - if (MathUtil::IntervalsIntersect(addr, size, info.addr, info.size) && - (ignore_hash || info.hash != Common::ComputeHash64(Memory::GetPhysicalPointer(info.addr), info.size))) { - - it = texture_cache.erase(it); - } else { - ++it; + memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel); + } } } } -void RasterizerCacheOpenGL::InvalidateAll() { - texture_cache.clear(); +bool RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type, const MathUtil::Rectangle& src_rect, const MathUtil::Rectangle& dst_rect) { + using SurfaceType = CachedSurface::SurfaceType; + + OpenGLState cur_state = OpenGLState::GetCurState(); + + // Make sure textures aren't bound to texture units, since going to bind them to framebuffer components + OpenGLState::ResetTexture(src_tex); + OpenGLState::ResetTexture(dst_tex); + + // Keep track of previous framebuffer bindings + GLuint old_fbs[2] = { cur_state.draw.read_framebuffer, cur_state.draw.draw_framebuffer }; + cur_state.draw.read_framebuffer = transfer_framebuffers[0].handle; + cur_state.draw.draw_framebuffer = transfer_framebuffers[1].handle; + cur_state.Apply(); + + u32 buffers = 0; + + if (type == SurfaceType::Color || type == SurfaceType::Texture) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, src_tex, 0); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + + buffers = GL_COLOR_BUFFER_BIT; + } else if (type == SurfaceType::Depth) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, src_tex, 0); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, dst_tex, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + + buffers = GL_DEPTH_BUFFER_BIT; + } else if (type == SurfaceType::DepthStencil) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, src_tex, 0); + + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, dst_tex, 0); + + buffers = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; + } + + if (OpenGLState::CheckFBStatus(GL_READ_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + return false; + } + + if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + return false; + } + + glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, + dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, + buffers, buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST); + + // Restore previous framebuffer bindings + cur_state.draw.read_framebuffer = old_fbs[0]; + cur_state.draw.draw_framebuffer = old_fbs[1]; + cur_state.Apply(); + + return true; +} + +bool RasterizerCacheOpenGL::TryBlitSurfaces(CachedSurface* src_surface, const MathUtil::Rectangle& src_rect, CachedSurface* dst_surface, const MathUtil::Rectangle& dst_rect) { + using SurfaceType = CachedSurface::SurfaceType; + + if (!CachedSurface::CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format)) { + return false; + } + + return BlitTextures(src_surface->texture.handle, dst_surface->texture.handle, CachedSurface::GetFormatType(src_surface->pixel_format), src_rect, dst_rect); +} + +static void AllocateSurfaceTexture(GLuint texture, CachedSurface::PixelFormat pixel_format, u32 width, u32 height) { + // Allocate an uninitialized texture of appropriate size and format for the surface + using SurfaceType = CachedSurface::SurfaceType; + + OpenGLState cur_state = OpenGLState::GetCurState(); + + // Keep track of previous texture bindings + GLuint old_tex = cur_state.texture_units[0].texture_2d; + cur_state.texture_units[0].texture_2d = texture; + cur_state.Apply(); + glActiveTexture(GL_TEXTURE0); + + SurfaceType type = CachedSurface::GetFormatType(pixel_format); + + FormatTuple tuple; + if (type == SurfaceType::Color) { + ASSERT((size_t)pixel_format < fb_format_tuples.size()); + tuple = fb_format_tuples[(unsigned int)pixel_format]; + } else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) { + size_t tuple_idx = (size_t)pixel_format - 14; + ASSERT(tuple_idx < depth_format_tuples.size()); + tuple = depth_format_tuples[tuple_idx]; + } else { + tuple = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }; + } + + glTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, width, height, 0, + tuple.format, tuple.type, nullptr); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // Restore previous texture bindings + cur_state.texture_units[0].texture_2d = old_tex; + cur_state.Apply(); +} + +MICROPROFILE_DEFINE(OpenGL_SurfaceUpload, "OpenGL", "Surface Upload", MP_RGB(128, 64, 192)); +CachedSurface* RasterizerCacheOpenGL::GetSurface(const CachedSurface& params, bool match_res_scale, bool load_if_create) { + using PixelFormat = CachedSurface::PixelFormat; + using SurfaceType = CachedSurface::SurfaceType; + + if (params.addr == 0) { + return nullptr; + } + + u32 params_size = params.width * params.height * CachedSurface::GetFormatBpp(params.pixel_format) / 8; + + // Check for an exact match in existing surfaces + CachedSurface* best_exact_surface = nullptr; + float exact_surface_goodness = -1.f; + + auto surface_interval = boost::icl::interval::right_open(params.addr, params.addr + params_size); + auto range = surface_cache.equal_range(surface_interval); + for (auto it = range.first; it != range.second; ++it) { + for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2) { + CachedSurface* surface = it2->get(); + + // Check if the request matches the surface exactly + if (params.addr == surface->addr && + params.width == surface->width && params.height == surface->height && + params.pixel_format == surface->pixel_format) + { + // Make sure optional param-matching criteria are fulfilled + bool tiling_match = (params.is_tiled == surface->is_tiled); + bool res_scale_match = (params.res_scale_width == surface->res_scale_width && params.res_scale_height == surface->res_scale_height); + if (!match_res_scale || res_scale_match) { + // Prioritize same-tiling and highest resolution surfaces + float match_goodness = (float)tiling_match + surface->res_scale_width * surface->res_scale_height; + if (match_goodness > exact_surface_goodness || surface->dirty) { + exact_surface_goodness = match_goodness; + best_exact_surface = surface; + } + } + } + } + } + + // Return the best exact surface if found + if (best_exact_surface != nullptr) { + return best_exact_surface; + } + + // No matching surfaces found, so create a new one + u8* texture_src_data = Memory::GetPhysicalPointer(params.addr); + if (texture_src_data == nullptr) { + return nullptr; + } + + MICROPROFILE_SCOPE(OpenGL_SurfaceUpload); + + std::shared_ptr new_surface = std::make_shared(); + + new_surface->addr = params.addr; + new_surface->size = params_size; + + new_surface->texture.Create(); + new_surface->width = params.width; + new_surface->height = params.height; + new_surface->stride = params.stride; + new_surface->res_scale_width = params.res_scale_width; + new_surface->res_scale_height = params.res_scale_height; + + new_surface->is_tiled = params.is_tiled; + new_surface->pixel_format = params.pixel_format; + new_surface->dirty = false; + + if (!load_if_create) { + // Don't load any data; just allocate the surface's texture + AllocateSurfaceTexture(new_surface->texture.handle, new_surface->pixel_format, new_surface->GetScaledWidth(), new_surface->GetScaledHeight()); + } else { + // TODO: Consider attempting subrect match in existing surfaces and direct blit here instead of memory upload below if that's a common scenario in some game + + Memory::RasterizerFlushRegion(params.addr, params_size); + + // Load data from memory to the new surface + OpenGLState cur_state = OpenGLState::GetCurState(); + + GLuint old_tex = cur_state.texture_units[0].texture_2d; + cur_state.texture_units[0].texture_2d = new_surface->texture.handle; + cur_state.Apply(); + glActiveTexture(GL_TEXTURE0); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)new_surface->stride); + if (!new_surface->is_tiled) { + // TODO: Ensure this will always be a color format, not a depth or other format + ASSERT((size_t)new_surface->pixel_format < fb_format_tuples.size()); + const FormatTuple& tuple = fb_format_tuples[(unsigned int)params.pixel_format]; + + glTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, params.width, params.height, 0, + tuple.format, tuple.type, texture_src_data); + } else { + SurfaceType type = CachedSurface::GetFormatType(new_surface->pixel_format); + if (type != SurfaceType::Depth && type != SurfaceType::DepthStencil) { + FormatTuple tuple; + if ((size_t)params.pixel_format < fb_format_tuples.size()) { + tuple = fb_format_tuples[(unsigned int)params.pixel_format]; + } else { + // Texture + tuple = { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE }; + } + + std::vector> tex_buffer(params.width * params.height); + + Pica::DebugUtils::TextureInfo tex_info; + tex_info.width = params.width; + tex_info.height = params.height; + tex_info.stride = params.width * CachedSurface::GetFormatBpp(params.pixel_format) / 8; + tex_info.format = (Pica::Regs::TextureFormat)params.pixel_format; + tex_info.physical_address = params.addr; + + for (unsigned y = 0; y < params.height; ++y) { + for (unsigned x = 0; x < params.width; ++x) { + tex_buffer[x + params.width * y] = Pica::DebugUtils::LookupTexture(texture_src_data, x, params.height - 1 - y, tex_info); + } + } + + glTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, params.width, params.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_buffer.data()); + } else { + // Depth/Stencil formats need special treatment since they aren't sampleable using LookupTexture and can't use RGBA format + size_t tuple_idx = (size_t)params.pixel_format - 14; + ASSERT(tuple_idx < depth_format_tuples.size()); + const FormatTuple& tuple = depth_format_tuples[tuple_idx]; + + u32 bytes_per_pixel = CachedSurface::GetFormatBpp(params.pixel_format) / 8; + + // OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type + bool use_4bpp = (params.pixel_format == PixelFormat::D24); + + u32 gl_bytes_per_pixel = use_4bpp ? 4 : bytes_per_pixel; + + std::vector temp_fb_depth_buffer(params.width * params.height * gl_bytes_per_pixel); + + u8* temp_fb_depth_buffer_ptr = use_4bpp ? temp_fb_depth_buffer.data() + 1 : temp_fb_depth_buffer.data(); + + MortonCopyPixels(params.pixel_format, params.width, params.height, bytes_per_pixel, gl_bytes_per_pixel, texture_src_data, temp_fb_depth_buffer_ptr, true); + + glTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, params.width, params.height, 0, + tuple.format, tuple.type, temp_fb_depth_buffer.data()); + } + } + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + // If not 1x scale, blit 1x texture to a new scaled texture and replace texture in surface + if (new_surface->res_scale_width != 1.f || new_surface->res_scale_height != 1.f) { + OGLTexture scaled_texture; + scaled_texture.Create(); + + AllocateSurfaceTexture(scaled_texture.handle, new_surface->pixel_format, new_surface->GetScaledWidth(), new_surface->GetScaledHeight()); + BlitTextures(new_surface->texture.handle, scaled_texture.handle, CachedSurface::GetFormatType(new_surface->pixel_format), + MathUtil::Rectangle(0, 0, new_surface->width, new_surface->height), + MathUtil::Rectangle(0, 0, new_surface->GetScaledWidth(), new_surface->GetScaledHeight())); + + new_surface->texture.Release(); + new_surface->texture.handle = scaled_texture.handle; + scaled_texture.handle = 0; + cur_state.texture_units[0].texture_2d = new_surface->texture.handle; + cur_state.Apply(); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + cur_state.texture_units[0].texture_2d = old_tex; + cur_state.Apply(); + } + + Memory::RasterizerMarkRegionCached(new_surface->addr, new_surface->size, 1); + surface_cache.add(std::make_pair(boost::icl::interval::right_open(new_surface->addr, new_surface->addr + new_surface->size), std::set>({ new_surface }))); + return new_surface.get(); +} + +CachedSurface* RasterizerCacheOpenGL::GetSurfaceRect(const CachedSurface& params, bool match_res_scale, bool load_if_create, MathUtil::Rectangle& out_rect) { + if (params.addr == 0) { + return nullptr; + } + + u32 total_pixels = params.width * params.height; + u32 params_size = total_pixels * CachedSurface::GetFormatBpp(params.pixel_format) / 8; + + // Attempt to find encompassing surfaces + CachedSurface* best_subrect_surface = nullptr; + float subrect_surface_goodness = -1.f; + + auto surface_interval = boost::icl::interval::right_open(params.addr, params.addr + params_size); + auto cache_upper_bound = surface_cache.upper_bound(surface_interval); + for (auto it = surface_cache.lower_bound(surface_interval); it != cache_upper_bound; ++it) { + for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2) { + CachedSurface* surface = it2->get(); + + // Check if the request is contained in the surface + if (params.addr >= surface->addr && + params.addr + params_size - 1 <= surface->addr + surface->size - 1 && + params.pixel_format == surface->pixel_format) + { + // Make sure optional param-matching criteria are fulfilled + bool tiling_match = (params.is_tiled == surface->is_tiled); + bool res_scale_match = (params.res_scale_width == surface->res_scale_width && params.res_scale_height == surface->res_scale_height); + if (!match_res_scale || res_scale_match) { + // Prioritize same-tiling and highest resolution surfaces + float match_goodness = (float)tiling_match + surface->res_scale_width * surface->res_scale_height; + if (match_goodness > subrect_surface_goodness || surface->dirty) { + subrect_surface_goodness = match_goodness; + best_subrect_surface = surface; + } + } + } + } + } + + // Return the best subrect surface if found + if (best_subrect_surface != nullptr) { + unsigned int bytes_per_pixel = (CachedSurface::GetFormatBpp(best_subrect_surface->pixel_format) / 8); + + int x0, y0; + + if (!params.is_tiled) { + u32 begin_pixel_index = (params.addr - best_subrect_surface->addr) / bytes_per_pixel; + x0 = begin_pixel_index % best_subrect_surface->width; + y0 = begin_pixel_index / best_subrect_surface->width; + + out_rect = MathUtil::Rectangle(x0, y0, x0 + params.width, y0 + params.height); + } else { + u32 bytes_per_tile = 8 * 8 * bytes_per_pixel; + u32 tiles_per_row = best_subrect_surface->width / 8; + + u32 begin_tile_index = (params.addr - best_subrect_surface->addr) / bytes_per_tile; + x0 = begin_tile_index % tiles_per_row * 8; + y0 = begin_tile_index / tiles_per_row * 8; + + // Tiled surfaces are flipped vertically in the rasterizer vs. 3DS memory. + out_rect = MathUtil::Rectangle(x0, best_subrect_surface->height - y0, x0 + params.width, best_subrect_surface->height - (y0 + params.height)); + } + + out_rect.left = (int)(out_rect.left * best_subrect_surface->res_scale_width); + out_rect.right = (int)(out_rect.right * best_subrect_surface->res_scale_width); + out_rect.top = (int)(out_rect.top * best_subrect_surface->res_scale_height); + out_rect.bottom = (int)(out_rect.bottom * best_subrect_surface->res_scale_height); + + return best_subrect_surface; + } + + // No subrect found - create and return a new surface + if (!params.is_tiled) { + out_rect = MathUtil::Rectangle(0, 0, (int)(params.width * params.res_scale_width), (int)(params.height * params.res_scale_height)); + } else { + out_rect = MathUtil::Rectangle(0, (int)(params.height * params.res_scale_height), (int)(params.width * params.res_scale_width), 0); + } + + return GetSurface(params, match_res_scale, load_if_create); +} + +CachedSurface* RasterizerCacheOpenGL::GetTextureSurface(const Pica::Regs::FullTextureConfig& config) { + Pica::DebugUtils::TextureInfo info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format); + + CachedSurface params; + params.addr = info.physical_address; + params.width = info.width; + params.height = info.height; + params.is_tiled = true; + params.pixel_format = CachedSurface::PixelFormatFromTextureFormat(info.format); + return GetSurface(params, false, true); +} + +std::tuple> RasterizerCacheOpenGL::GetFramebufferSurfaces(const Pica::Regs::FramebufferConfig& config) { + const auto& regs = Pica::g_state.regs; + + // Make sur that framebuffers don't overlap if both color and depth are being used + u32 fb_area = config.GetWidth() * config.GetHeight(); + bool framebuffers_overlap = config.GetColorBufferPhysicalAddress() != 0 && + config.GetDepthBufferPhysicalAddress() != 0 && + MathUtil::IntervalsIntersect(config.GetColorBufferPhysicalAddress(), fb_area * GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(config.color_format.Value())), + config.GetDepthBufferPhysicalAddress(), fb_area * Pica::Regs::BytesPerDepthPixel(config.depth_format)); + bool using_color_fb = config.GetColorBufferPhysicalAddress() != 0; + bool using_depth_fb = config.GetDepthBufferPhysicalAddress() != 0 && (regs.output_merger.depth_test_enable || regs.output_merger.depth_write_enable || !framebuffers_overlap); + + if (framebuffers_overlap && using_color_fb && using_depth_fb) { + LOG_CRITICAL(Render_OpenGL, "Color and depth framebuffer memory regions overlap; overlapping framebuffers not supported!"); + using_depth_fb = false; + } + + // get color and depth surfaces + CachedSurface color_params; + CachedSurface depth_params; + color_params.width = depth_params.width = config.GetWidth(); + color_params.height = depth_params.height = config.GetHeight(); + color_params.is_tiled = depth_params.is_tiled = true; + if (VideoCore::g_scaled_resolution_enabled) { + auto layout = VideoCore::g_emu_window->GetFramebufferLayout(); + + // Assume same scaling factor for top and bottom screens + color_params.res_scale_width = depth_params.res_scale_width = (float)layout.top_screen.GetWidth() / VideoCore::kScreenTopWidth; + color_params.res_scale_height = depth_params.res_scale_height = (float)layout.top_screen.GetHeight() / VideoCore::kScreenTopHeight; + } + + color_params.addr = config.GetColorBufferPhysicalAddress(); + color_params.pixel_format = CachedSurface::PixelFormatFromColorFormat(config.color_format); + + depth_params.addr = config.GetDepthBufferPhysicalAddress(); + depth_params.pixel_format = CachedSurface::PixelFormatFromDepthFormat(config.depth_format); + + MathUtil::Rectangle color_rect; + CachedSurface* color_surface = using_color_fb ? GetSurfaceRect(color_params, true, true, color_rect) : nullptr; + + MathUtil::Rectangle depth_rect; + CachedSurface* depth_surface = using_depth_fb ? GetSurfaceRect(depth_params, true, true, depth_rect) : nullptr; + + // Sanity check to make sure found surfaces aren't the same + if (using_depth_fb && using_color_fb && color_surface == depth_surface) { + LOG_CRITICAL(Render_OpenGL, "Color and depth framebuffer surfaces overlap; overlapping surfaces not supported!"); + using_depth_fb = false; + depth_surface = nullptr; + } + + MathUtil::Rectangle rect; + + if (color_surface != nullptr && depth_surface != nullptr && (depth_rect.left != color_rect.left || depth_rect.top != color_rect.top)) { + // Can't specify separate color and depth viewport offsets in OpenGL, so re-zero both if they don't match + if (color_rect.left != 0 || color_rect.top != 0) { + color_surface = GetSurface(color_params, true, true); + } + + if (depth_rect.left != 0 || depth_rect.top != 0) { + depth_surface = GetSurface(depth_params, true, true); + } + + if (!color_surface->is_tiled) { + rect = MathUtil::Rectangle(0, 0, (int)(color_params.width * color_params.res_scale_width), (int)(color_params.height * color_params.res_scale_height)); + } else { + rect = MathUtil::Rectangle(0, (int)(color_params.height * color_params.res_scale_height), (int)(color_params.width * color_params.res_scale_width), 0); + } + } else if (color_surface != nullptr) { + rect = color_rect; + } else if (depth_surface != nullptr) { + rect = depth_rect; + } else { + rect = MathUtil::Rectangle(0, 0, 0, 0); + } + + return std::make_tuple(color_surface, depth_surface, rect); +} + +CachedSurface* RasterizerCacheOpenGL::TryGetFillSurface(const GPU::Regs::MemoryFillConfig& config) { + auto surface_interval = boost::icl::interval::right_open(config.GetStartAddress(), config.GetEndAddress()); + auto range = surface_cache.equal_range(surface_interval); + for (auto it = range.first; it != range.second; ++it) { + for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2) { + int bits_per_value = 0; + if (config.fill_24bit) { + bits_per_value = 24; + } else if (config.fill_32bit) { + bits_per_value = 32; + } else { + bits_per_value = 16; + } + + CachedSurface* surface = it2->get(); + + if (surface->addr == config.GetStartAddress() && + CachedSurface::GetFormatBpp(surface->pixel_format) == bits_per_value && + (surface->width * surface->height * CachedSurface::GetFormatBpp(surface->pixel_format) / 8) == (config.GetEndAddress() - config.GetStartAddress())) + { + return surface; + } + } + } + + return nullptr; +} + +MICROPROFILE_DEFINE(OpenGL_SurfaceDownload, "OpenGL", "Surface Download", MP_RGB(128, 192, 64)); +void RasterizerCacheOpenGL::FlushSurface(CachedSurface* surface) { + using PixelFormat = CachedSurface::PixelFormat; + using SurfaceType = CachedSurface::SurfaceType; + + if (!surface->dirty) { + return; + } + + MICROPROFILE_SCOPE(OpenGL_SurfaceDownload); + + u8* dst_buffer = Memory::GetPhysicalPointer(surface->addr); + if (dst_buffer == nullptr) { + return; + } + + OpenGLState cur_state = OpenGLState::GetCurState(); + GLuint old_tex = cur_state.texture_units[0].texture_2d; + + OGLTexture unscaled_tex; + GLuint texture_to_flush = surface->texture.handle; + + // If not 1x scale, blit scaled texture to a new 1x texture and use that to flush + if (surface->res_scale_width != 1.f || surface->res_scale_height != 1.f) { + unscaled_tex.Create(); + + AllocateSurfaceTexture(unscaled_tex.handle, surface->pixel_format, surface->width, surface->height); + BlitTextures(surface->texture.handle, unscaled_tex.handle, CachedSurface::GetFormatType(surface->pixel_format), + MathUtil::Rectangle(0, 0, surface->GetScaledWidth(), surface->GetScaledHeight()), + MathUtil::Rectangle(0, 0, surface->width, surface->height)); + + texture_to_flush = unscaled_tex.handle; + } + + cur_state.texture_units[0].texture_2d = texture_to_flush; + cur_state.Apply(); + glActiveTexture(GL_TEXTURE0); + + glPixelStorei(GL_PACK_ROW_LENGTH, (GLint)surface->stride); + if (!surface->is_tiled) { + // TODO: Ensure this will always be a color format, not a depth or other format + ASSERT((size_t)surface->pixel_format < fb_format_tuples.size()); + const FormatTuple& tuple = fb_format_tuples[(unsigned int)surface->pixel_format]; + + glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, dst_buffer); + } else { + SurfaceType type = CachedSurface::GetFormatType(surface->pixel_format); + if (type != SurfaceType::Depth && type != SurfaceType::DepthStencil) { + ASSERT((size_t)surface->pixel_format < fb_format_tuples.size()); + const FormatTuple& tuple = fb_format_tuples[(unsigned int)surface->pixel_format]; + + u32 bytes_per_pixel = CachedSurface::GetFormatBpp(surface->pixel_format) / 8; + + std::vector temp_gl_buffer(surface->width * surface->height * bytes_per_pixel); + + glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, temp_gl_buffer.data()); + + // Directly copy pixels. Internal OpenGL color formats are consistent so no conversion is necessary. + MortonCopyPixels(surface->pixel_format, surface->width, surface->height, bytes_per_pixel, bytes_per_pixel, dst_buffer, temp_gl_buffer.data(), false); + } else { + // Depth/Stencil formats need special treatment since they aren't sampleable using LookupTexture and can't use RGBA format + size_t tuple_idx = (size_t)surface->pixel_format - 14; + ASSERT(tuple_idx < depth_format_tuples.size()); + const FormatTuple& tuple = depth_format_tuples[tuple_idx]; + + u32 bytes_per_pixel = CachedSurface::GetFormatBpp(surface->pixel_format) / 8; + + // OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type + bool use_4bpp = (surface->pixel_format == PixelFormat::D24); + + u32 gl_bytes_per_pixel = use_4bpp ? 4 : bytes_per_pixel; + + std::vector temp_gl_buffer(surface->width * surface->height * gl_bytes_per_pixel); + + glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, temp_gl_buffer.data()); + + u8* temp_gl_buffer_ptr = use_4bpp ? temp_gl_buffer.data() + 1 : temp_gl_buffer.data(); + + MortonCopyPixels(surface->pixel_format, surface->width, surface->height, bytes_per_pixel, gl_bytes_per_pixel, dst_buffer, temp_gl_buffer_ptr, false); + } + } + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + + surface->dirty = false; + + cur_state.texture_units[0].texture_2d = old_tex; + cur_state.Apply(); +} + +void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u32 size, const CachedSurface* skip_surface, bool invalidate) { + if (size == 0) { + return; + } + + // Gather up unique surfaces that touch the region + std::unordered_set> touching_surfaces; + + auto surface_interval = boost::icl::interval::right_open(addr, addr + size); + auto cache_upper_bound = surface_cache.upper_bound(surface_interval); + for (auto it = surface_cache.lower_bound(surface_interval); it != cache_upper_bound; ++it) { + std::copy_if(it->second.begin(), it->second.end(), std::inserter(touching_surfaces, touching_surfaces.end()), + [skip_surface](std::shared_ptr surface) { return (surface.get() != skip_surface); }); + } + + // Flush and invalidate surfaces + for (auto surface : touching_surfaces) { + FlushSurface(surface.get()); + if (invalidate) { + Memory::RasterizerMarkRegionCached(surface->addr, surface->size, -1); + surface_cache.subtract(std::make_pair(boost::icl::interval::right_open(surface->addr, surface->addr + surface->size), std::set>({ surface }))); + } + } +} + +void RasterizerCacheOpenGL::FlushAll() { + for (auto& surfaces : surface_cache) { + for (auto& surface : surfaces.second) { + FlushSurface(surface.get()); + } + } } diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index b69651427..893d51138 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -6,38 +6,211 @@ #include #include +#include + +#include + +#include "common/math_util.h" + +#include "core/hw/gpu.h" #include "video_core/pica.h" #include "video_core/debug_utils/debug_utils.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_state.h" -class RasterizerCacheOpenGL : NonCopyable { -public: - ~RasterizerCacheOpenGL(); +struct CachedSurface; - /// Loads a texture from 3DS memory to OpenGL and caches it (if not already cached) - void LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::DebugUtils::TextureInfo& info); +using SurfaceCache = boost::icl::interval_map>>; - void LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::Regs::FullTextureConfig& config) { - LoadAndBindTexture(state, texture_unit, Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format)); - } +struct CachedSurface { + enum class PixelFormat { + // First 5 formats are shared between textures and color buffers + RGBA8 = 0, + RGB8 = 1, + RGB5A1 = 2, + RGB565 = 3, + RGBA4 = 4, - /// Invalidate any cached resource intersecting the specified region. - void InvalidateInRange(PAddr addr, u32 size, bool ignore_hash = false); + // Texture-only formats + IA8 = 5, + RG8 = 6, + I8 = 7, + A8 = 8, + IA4 = 9, + I4 = 10, + A4 = 11, + ETC1 = 12, + ETC1A4 = 13, - /// Invalidate all cached OpenGL resources tracked by this cache manager - void InvalidateAll(); + // Depth buffer-only formats + D16 = 14, + // gap + D24 = 16, + D24S8 = 17, -private: - struct CachedTexture { - OGLTexture texture; - GLuint width; - GLuint height; - u32 size; - u64 hash; - PAddr addr; + Invalid = 255, }; - std::map> texture_cache; + enum class SurfaceType { + Color = 0, + Texture = 1, + Depth = 2, + DepthStencil = 3, + Invalid = 4, + }; + + static unsigned int GetFormatBpp(CachedSurface::PixelFormat format) { + static const std::array bpp_table = { + 32, // RGBA8 + 24, // RGB8 + 16, // RGB5A1 + 16, // RGB565 + 16, // RGBA4 + 16, // IA8 + 16, // RG8 + 8, // I8 + 8, // A8 + 8, // IA4 + 4, // I4 + 4, // A4 + 4, // ETC1 + 8, // ETC1A4 + 16, // D16 + 0, + 24, // D24 + 32, // D24S8 + }; + + ASSERT((unsigned int)format < ARRAY_SIZE(bpp_table)); + return bpp_table[(unsigned int)format]; + } + + static PixelFormat PixelFormatFromTextureFormat(Pica::Regs::TextureFormat format) { + return ((unsigned int)format < 14) ? (PixelFormat)format : PixelFormat::Invalid; + } + + static PixelFormat PixelFormatFromColorFormat(Pica::Regs::ColorFormat format) { + return ((unsigned int)format < 5) ? (PixelFormat)format : PixelFormat::Invalid; + } + + static PixelFormat PixelFormatFromDepthFormat(Pica::Regs::DepthFormat format) { + return ((unsigned int)format < 4) ? (PixelFormat)((unsigned int)format + 14) : PixelFormat::Invalid; + } + + static PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format) { + switch (format) { + // RGB565 and RGB5A1 are switched in PixelFormat compared to ColorFormat + case GPU::Regs::PixelFormat::RGB565: + return PixelFormat::RGB565; + case GPU::Regs::PixelFormat::RGB5A1: + return PixelFormat::RGB5A1; + default: + return ((unsigned int)format < 5) ? (PixelFormat)format : PixelFormat::Invalid; + } + } + + static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) { + SurfaceType a_type = GetFormatType(pixel_format_a); + SurfaceType b_type = GetFormatType(pixel_format_b); + + if ((a_type == SurfaceType::Color || a_type == SurfaceType::Texture) && (b_type == SurfaceType::Color || b_type == SurfaceType::Texture)) { + return true; + } + + if (a_type == SurfaceType::Depth && b_type == SurfaceType::Depth) { + return true; + } + + if (a_type == SurfaceType::DepthStencil && b_type == SurfaceType::DepthStencil) { + return true; + } + + return false; + } + + static SurfaceType GetFormatType(PixelFormat pixel_format) { + if ((unsigned int)pixel_format < 5) { + return SurfaceType::Color; + } + + if ((unsigned int)pixel_format < 14) { + return SurfaceType::Texture; + } + + if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) { + return SurfaceType::Depth; + } + + if (pixel_format == PixelFormat::D24S8) { + return SurfaceType::DepthStencil; + } + + return SurfaceType::Invalid; + } + + u32 GetScaledWidth() const { + return (u32)(width * res_scale_width); + } + + u32 GetScaledHeight() const { + return (u32)(height * res_scale_height); + } + + PAddr addr; + u32 size; + + PAddr min_valid; + PAddr max_valid; + + OGLTexture texture; + u32 width; + u32 height; + u32 stride = 0; + float res_scale_width = 1.f; + float res_scale_height = 1.f; + + bool is_tiled; + PixelFormat pixel_format; + bool dirty; +}; + +class RasterizerCacheOpenGL : NonCopyable { +public: + RasterizerCacheOpenGL(); + ~RasterizerCacheOpenGL(); + + /// Blits one texture to another + bool BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type, const MathUtil::Rectangle& src_rect, const MathUtil::Rectangle& dst_rect); + + /// Attempt to blit one surface's texture to another + bool TryBlitSurfaces(CachedSurface* src_surface, const MathUtil::Rectangle& src_rect, CachedSurface* dst_surface, const MathUtil::Rectangle& dst_rect); + + /// Loads a texture from 3DS memory to OpenGL and caches it (if not already cached) + CachedSurface* GetSurface(const CachedSurface& params, bool match_res_scale, bool load_if_create); + + /// Attempt to find a subrect (resolution scaled) of a surface, otherwise loads a texture from 3DS memory to OpenGL and caches it (if not already cached) + CachedSurface* GetSurfaceRect(const CachedSurface& params, bool match_res_scale, bool load_if_create, MathUtil::Rectangle& out_rect); + + /// Gets a surface based on the texture configuration + CachedSurface* GetTextureSurface(const Pica::Regs::FullTextureConfig& config); + + /// Gets the color and depth surfaces and rect (resolution scaled) based on the framebuffer configuration + std::tuple> GetFramebufferSurfaces(const Pica::Regs::FramebufferConfig& config); + + /// Attempt to get a surface that exactly matches the fill region and format + CachedSurface* TryGetFillSurface(const GPU::Regs::MemoryFillConfig& config); + + /// Write the surface back to memory + void FlushSurface(CachedSurface* surface); + + /// Write any cached resources overlapping the region back to memory (if dirty) and optionally invalidate them in the cache + void FlushRegion(PAddr addr, u32 size, const CachedSurface* skip_surface, bool invalidate); + + /// Flush all cached resources tracked by this cache manager + void FlushAll(); + +private: + SurfaceCache surface_cache; + OGLFramebuffer transfer_framebuffers[2]; }; diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 08e4d0b54..f04bdd8c5 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "video_core/pica.h" +#include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_state.h" OpenGLState OpenGLState::cur_state; @@ -48,17 +49,19 @@ OpenGLState::OpenGLState() { texture_unit.sampler = 0; } - for (auto& lut : lighting_lut) { + for (auto& lut : lighting_luts) { lut.texture_1d = 0; } - draw.framebuffer = 0; + draw.read_framebuffer = 0; + draw.draw_framebuffer = 0; draw.vertex_array = 0; draw.vertex_buffer = 0; + draw.uniform_buffer = 0; draw.shader_program = 0; } -void OpenGLState::Apply() { +void OpenGLState::Apply() const { // Culling if (cull.enabled != cur_state.cull.enabled) { if (cull.enabled) { @@ -175,16 +178,19 @@ void OpenGLState::Apply() { } // Lighting LUTs - for (unsigned i = 0; i < ARRAY_SIZE(lighting_lut); ++i) { - if (lighting_lut[i].texture_1d != cur_state.lighting_lut[i].texture_1d) { + for (unsigned i = 0; i < ARRAY_SIZE(lighting_luts); ++i) { + if (lighting_luts[i].texture_1d != cur_state.lighting_luts[i].texture_1d) { glActiveTexture(GL_TEXTURE3 + i); - glBindTexture(GL_TEXTURE_1D, lighting_lut[i].texture_1d); + glBindTexture(GL_TEXTURE_1D, lighting_luts[i].texture_1d); } } // Framebuffer - if (draw.framebuffer != cur_state.draw.framebuffer) { - glBindFramebuffer(GL_FRAMEBUFFER, draw.framebuffer); + if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { + glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); + } + if (draw.draw_framebuffer != cur_state.draw.draw_framebuffer) { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer); } // Vertex array @@ -210,45 +216,58 @@ void OpenGLState::Apply() { cur_state = *this; } -void OpenGLState::ResetTexture(GLuint id) { +GLenum OpenGLState::CheckFBStatus(GLenum target) { + GLenum fb_status = glCheckFramebufferStatus(target); + if (fb_status != GL_FRAMEBUFFER_COMPLETE) { + const char* fb_description = (target == GL_READ_FRAMEBUFFER ? "READ" : (target == GL_DRAW_FRAMEBUFFER ? "DRAW" : "UNK")); + LOG_CRITICAL(Render_OpenGL, "OpenGL %s framebuffer check failed, status %X", fb_description, fb_status); + } + + return fb_status; +} + +void OpenGLState::ResetTexture(GLuint handle) { for (auto& unit : cur_state.texture_units) { - if (unit.texture_2d == id) { + if (unit.texture_2d == handle) { unit.texture_2d = 0; } } } -void OpenGLState::ResetSampler(GLuint id) { +void OpenGLState::ResetSampler(GLuint handle) { for (auto& unit : cur_state.texture_units) { - if (unit.sampler == id) { + if (unit.sampler == handle) { unit.sampler = 0; } } } -void OpenGLState::ResetProgram(GLuint id) { - if (cur_state.draw.shader_program == id) { +void OpenGLState::ResetProgram(GLuint handle) { + if (cur_state.draw.shader_program == handle) { cur_state.draw.shader_program = 0; } } -void OpenGLState::ResetBuffer(GLuint id) { - if (cur_state.draw.vertex_buffer == id) { +void OpenGLState::ResetBuffer(GLuint handle) { + if (cur_state.draw.vertex_buffer == handle) { cur_state.draw.vertex_buffer = 0; } - if (cur_state.draw.uniform_buffer == id) { + if (cur_state.draw.uniform_buffer == handle) { cur_state.draw.uniform_buffer = 0; } } -void OpenGLState::ResetVertexArray(GLuint id) { - if (cur_state.draw.vertex_array == id) { +void OpenGLState::ResetVertexArray(GLuint handle) { + if (cur_state.draw.vertex_array == handle) { cur_state.draw.vertex_array = 0; } } -void OpenGLState::ResetFramebuffer(GLuint id) { - if (cur_state.draw.framebuffer == id) { - cur_state.draw.framebuffer = 0; +void OpenGLState::ResetFramebuffer(GLuint handle) { + if (cur_state.draw.read_framebuffer == handle) { + cur_state.draw.read_framebuffer = 0; + } + if (cur_state.draw.draw_framebuffer == handle) { + cur_state.draw.draw_framebuffer = 0; } } diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index e848058d7..0f72e9004 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -5,6 +5,7 @@ #pragma once #include +#include class OpenGLState { public: @@ -63,15 +64,15 @@ public: struct { GLuint texture_1d; // GL_TEXTURE_BINDING_1D - } lighting_lut[6]; + } lighting_luts[6]; struct { - GLuint framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING + GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING + GLuint draw_framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING GLuint uniform_buffer; // GL_UNIFORM_BUFFER_BINDING GLuint shader_program; // GL_CURRENT_PROGRAM - bool shader_dirty; } draw; OpenGLState(); @@ -82,14 +83,18 @@ public: } /// Apply this state as the current OpenGL state - void Apply(); + void Apply() const; - static void ResetTexture(GLuint id); - static void ResetSampler(GLuint id); - static void ResetProgram(GLuint id); - static void ResetBuffer(GLuint id); - static void ResetVertexArray(GLuint id); - static void ResetFramebuffer(GLuint id); + /// Check the status of the current OpenGL read or draw framebuffer configuration + static GLenum CheckFBStatus(GLenum target); + + /// Resets and unbinds any references to the given resource in the current OpenGL state + static void ResetTexture(GLuint handle); + static void ResetSampler(GLuint handle); + static void ResetProgram(GLuint handle); + static void ResetBuffer(GLuint handle); + static void ResetVertexArray(GLuint handle); + static void ResetFramebuffer(GLuint handle); private: static OpenGLState cur_state; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 11c4d0daf..8f907593f 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -107,7 +107,7 @@ void RendererOpenGL::SwapBuffers() { OpenGLState prev_state = OpenGLState::GetCurState(); state.Apply(); - for(int i : {0, 1}) { + for (int i : {0, 1}) { const auto& framebuffer = GPU::g_regs.framebuffer_config[i]; // Main LCD (0): 0x1ED02204, Sub LCD (1): 0x1ED02A04 @@ -117,25 +117,25 @@ void RendererOpenGL::SwapBuffers() { LCD::Read(color_fill.raw, lcd_color_addr); if (color_fill.is_enabled) { - LoadColorToActiveGLTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b, textures[i]); + LoadColorToActiveGLTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b, screen_infos[i].texture); // Resize the texture in case the framebuffer size has changed - textures[i].width = 1; - textures[i].height = 1; + screen_infos[i].texture.width = 1; + screen_infos[i].texture.height = 1; } else { - if (textures[i].width != (GLsizei)framebuffer.width || - textures[i].height != (GLsizei)framebuffer.height || - textures[i].format != framebuffer.color_format) { + if (screen_infos[i].texture.width != (GLsizei)framebuffer.width || + screen_infos[i].texture.height != (GLsizei)framebuffer.height || + screen_infos[i].texture.format != framebuffer.color_format) { // Reallocate texture if the framebuffer size has changed. // This is expected to not happen very often and hence should not be a // performance problem. - ConfigureFramebufferTexture(textures[i], framebuffer); + ConfigureFramebufferTexture(screen_infos[i].texture, framebuffer); } - LoadFBToActiveGLTexture(framebuffer, textures[i]); + LoadFBToScreenInfo(framebuffer, screen_infos[i]); // Resize the texture in case the framebuffer size has changed - textures[i].width = framebuffer.width; - textures[i].height = framebuffer.height; + screen_infos[i].texture.width = framebuffer.width; + screen_infos[i].texture.height = framebuffer.height; } } @@ -166,8 +166,8 @@ void RendererOpenGL::SwapBuffers() { /** * Loads framebuffer from emulated memory into the active OpenGL texture. */ -void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, - const TextureInfo& texture) { +void RendererOpenGL::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer, + ScreenInfo& screen_info) { const PAddr framebuffer_addr = framebuffer.active_fb == 0 ? framebuffer.address_left1 : framebuffer.address_left2; @@ -177,8 +177,6 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer_addr, (int)framebuffer.width, (int)framebuffer.height, (int)framebuffer.format); - const u8* framebuffer_data = Memory::GetPhysicalPointer(framebuffer_addr); - int bpp = GPU::Regs::BytesPerPixel(framebuffer.color_format); size_t pixel_stride = framebuffer.stride / bpp; @@ -189,24 +187,34 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& // only allows rows to have a memory alignement of 4. ASSERT(pixel_stride % 4 == 0); - state.texture_units[0].texture_2d = texture.handle; - state.Apply(); + if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, pixel_stride, screen_info)) { + // Reset the screen info's display texture to its own permanent texture + screen_info.display_texture = screen_info.texture.resource.handle; + screen_info.display_texcoords = MathUtil::Rectangle(0.f, 0.f, 1.f, 1.f); - glActiveTexture(GL_TEXTURE0); - glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride); + Memory::RasterizerFlushRegion(framebuffer_addr, framebuffer.stride * framebuffer.height); - // Update existing texture - // TODO: Test what happens on hardware when you change the framebuffer dimensions so that they - // differ from the LCD resolution. - // TODO: Applications could theoretically crash Citra here by specifying too large - // framebuffer sizes. We should make sure that this cannot happen. - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height, - texture.gl_format, texture.gl_type, framebuffer_data); + const u8* framebuffer_data = Memory::GetPhysicalPointer(framebuffer_addr); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + state.texture_units[0].texture_2d = screen_info.texture.resource.handle; + state.Apply(); - state.texture_units[0].texture_2d = 0; - state.Apply(); + glActiveTexture(GL_TEXTURE0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pixel_stride); + + // Update existing texture + // TODO: Test what happens on hardware when you change the framebuffer dimensions so that they + // differ from the LCD resolution. + // TODO: Applications could theoretically crash Citra here by specifying too large + // framebuffer sizes. We should make sure that this cannot happen. + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, framebuffer.width, framebuffer.height, + screen_info.texture.gl_format, screen_info.texture.gl_type, framebuffer_data); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + state.texture_units[0].texture_2d = 0; + state.Apply(); + } } /** @@ -216,7 +224,7 @@ void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& */ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const TextureInfo& texture) { - state.texture_units[0].texture_2d = texture.handle; + state.texture_units[0].texture_2d = texture.resource.handle; state.Apply(); glActiveTexture(GL_TEXTURE0); @@ -224,6 +232,9 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color // Update existing texture glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, framebuffer_data); + + state.texture_units[0].texture_2d = 0; + state.Apply(); } /** @@ -233,20 +244,22 @@ void RendererOpenGL::InitOpenGLObjects() { glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f); // Link shaders and get variable locations - program_id = GLShader::LoadProgram(vertex_shader, fragment_shader); - uniform_modelview_matrix = glGetUniformLocation(program_id, "modelview_matrix"); - uniform_color_texture = glGetUniformLocation(program_id, "color_texture"); - attrib_position = glGetAttribLocation(program_id, "vert_position"); - attrib_tex_coord = glGetAttribLocation(program_id, "vert_tex_coord"); + shader.Create(vertex_shader, fragment_shader); + state.draw.shader_program = shader.handle; + state.Apply(); + uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix"); + uniform_color_texture = glGetUniformLocation(shader.handle, "color_texture"); + attrib_position = glGetAttribLocation(shader.handle, "vert_position"); + attrib_tex_coord = glGetAttribLocation(shader.handle, "vert_tex_coord"); // Generate VBO handle for drawing - glGenBuffers(1, &vertex_buffer_handle); + vertex_buffer.Create(); // Generate VAO - glGenVertexArrays(1, &vertex_array_handle); + vertex_array.Create(); - state.draw.vertex_array = vertex_array_handle; - state.draw.vertex_buffer = vertex_buffer_handle; + state.draw.vertex_array = vertex_array.handle; + state.draw.vertex_buffer = vertex_buffer.handle; state.draw.uniform_buffer = 0; state.Apply(); @@ -258,13 +271,13 @@ void RendererOpenGL::InitOpenGLObjects() { glEnableVertexAttribArray(attrib_tex_coord); // Allocate textures for each screen - for (auto& texture : textures) { - glGenTextures(1, &texture.handle); + for (auto& screen_info : screen_infos) { + screen_info.texture.resource.Create(); // Allocation of storage is deferred until the first frame, when we // know the framebuffer size. - state.texture_units[0].texture_2d = texture.handle; + state.texture_units[0].texture_2d = screen_info.texture.resource.handle; state.Apply(); glActiveTexture(GL_TEXTURE0); @@ -273,6 +286,8 @@ void RendererOpenGL::InitOpenGLObjects() { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + screen_info.display_texture = screen_info.texture.resource.handle; } state.texture_units[0].texture_2d = 0; @@ -327,30 +342,38 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, UNIMPLEMENTED(); } - state.texture_units[0].texture_2d = texture.handle; + state.texture_units[0].texture_2d = texture.resource.handle; state.Apply(); glActiveTexture(GL_TEXTURE0); glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, texture.gl_format, texture.gl_type, nullptr); + + state.texture_units[0].texture_2d = 0; + state.Apply(); } /** * Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD rotation. */ -void RendererOpenGL::DrawSingleScreenRotated(const TextureInfo& texture, float x, float y, float w, float h) { +void RendererOpenGL::DrawSingleScreenRotated(const ScreenInfo& screen_info, float x, float y, float w, float h) { + auto& texcoords = screen_info.display_texcoords; + std::array vertices = {{ - ScreenRectVertex(x, y, 1.f, 0.f), - ScreenRectVertex(x+w, y, 1.f, 1.f), - ScreenRectVertex(x, y+h, 0.f, 0.f), - ScreenRectVertex(x+w, y+h, 0.f, 1.f), + ScreenRectVertex(x, y, texcoords.bottom, texcoords.left), + ScreenRectVertex(x+w, y, texcoords.bottom, texcoords.right), + ScreenRectVertex(x, y+h, texcoords.top, texcoords.left), + ScreenRectVertex(x+w, y+h, texcoords.top, texcoords.right), }}; - state.texture_units[0].texture_2d = texture.handle; + state.texture_units[0].texture_2d = screen_info.display_texture; state.Apply(); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + state.texture_units[0].texture_2d = 0; + state.Apply(); } /** @@ -362,9 +385,6 @@ void RendererOpenGL::DrawScreens() { glViewport(0, 0, layout.width, layout.height); glClear(GL_COLOR_BUFFER_BIT); - state.draw.shader_program = program_id; - state.Apply(); - // Set projection matrix std::array ortho_matrix = MakeOrthographicMatrix((float)layout.width, (float)layout.height); @@ -374,9 +394,9 @@ void RendererOpenGL::DrawScreens() { glActiveTexture(GL_TEXTURE0); glUniform1i(uniform_color_texture, 0); - DrawSingleScreenRotated(textures[0], (float)layout.top_screen.left, (float)layout.top_screen.top, + DrawSingleScreenRotated(screen_infos[0], (float)layout.top_screen.left, (float)layout.top_screen.top, (float)layout.top_screen.GetWidth(), (float)layout.top_screen.GetHeight()); - DrawSingleScreenRotated(textures[1], (float)layout.bottom_screen.left,(float)layout.bottom_screen.top, + DrawSingleScreenRotated(screen_infos[1], (float)layout.bottom_screen.left,(float)layout.bottom_screen.top, (float)layout.bottom_screen.GetWidth(), (float)layout.bottom_screen.GetHeight()); m_current_frame++; diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index fe4d142a5..5ca5255ac 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -11,10 +11,28 @@ #include "core/hw/gpu.h" #include "video_core/renderer_base.h" +#include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_state.h" class EmuWindow; +/// Structure used for storing information about the textures for each 3DS screen +struct TextureInfo { + OGLTexture resource; + GLsizei width; + GLsizei height; + GPU::Regs::PixelFormat format; + GLenum gl_format; + GLenum gl_type; +}; + +/// Structure used for storing information about the display target for each 3DS screen +struct ScreenInfo { + GLuint display_texture; + MathUtil::Rectangle display_texcoords; + TextureInfo texture; +}; + class RendererOpenGL : public RendererBase { public: @@ -37,26 +55,16 @@ public: void ShutDown() override; private: - /// Structure used for storing information about the textures for each 3DS screen - struct TextureInfo { - GLuint handle; - GLsizei width; - GLsizei height; - GPU::Regs::PixelFormat format; - GLenum gl_format; - GLenum gl_type; - }; - void InitOpenGLObjects(); void ConfigureFramebufferTexture(TextureInfo& texture, const GPU::Regs::FramebufferConfig& framebuffer); void DrawScreens(); - void DrawSingleScreenRotated(const TextureInfo& texture, float x, float y, float w, float h); + void DrawSingleScreenRotated(const ScreenInfo& screen_info, float x, float y, float w, float h); void UpdateFramerate(); - // Loads framebuffer from emulated memory into the active OpenGL texture. - void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, - const TextureInfo& texture); + // Loads framebuffer from emulated memory into the display information structure + void LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer, + ScreenInfo& screen_info); // Fills active OpenGL texture with the given RGB color. void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const TextureInfo& texture); @@ -69,10 +77,10 @@ private: OpenGLState state; // OpenGL object IDs - GLuint vertex_array_handle; - GLuint vertex_buffer_handle; - GLuint program_id; - std::array textures; ///< Textures for top and bottom screens respectively + OGLVertexArray vertex_array; + OGLBuffer vertex_buffer; + OGLShader shader; + std::array screen_infos; ///< Display information for top and bottom screens respectively // Shader uniform location indices GLuint uniform_modelview_matrix; GLuint uniform_color_texture; diff --git a/src/video_core/swrasterizer.h b/src/video_core/swrasterizer.h index 9a9a76d7a..090f899bc 100644 --- a/src/video_core/swrasterizer.h +++ b/src/video_core/swrasterizer.h @@ -11,16 +11,14 @@ namespace VideoCore { class SWRasterizer : public RasterizerInterface { - void InitObjects() override {} - void Reset() override {} void AddTriangle(const Pica::Shader::OutputVertex& v0, const Pica::Shader::OutputVertex& v1, const Pica::Shader::OutputVertex& v2) override; void DrawTriangles() override {} - void FlushFramebuffer() override {} void NotifyPicaRegisterChanged(u32 id) override {} + void FlushAll() override {} void FlushRegion(PAddr addr, u32 size) override {} - void InvalidateRegion(PAddr addr, u32 size) override {} + void FlushAndInvalidateRegion(PAddr addr, u32 size) override {} }; } From 3268cab26b49468bbab7c09e6eb8f36ed4a91158 Mon Sep 17 00:00:00 2001 From: tfarley Date: Fri, 22 Apr 2016 10:48:00 -0400 Subject: [PATCH 08/39] HWRasterizer: sync specular uniform for new shaders --- src/video_core/renderer_opengl/gl_rasterizer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index da4121c35..30187d4cf 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -874,6 +874,8 @@ void RasterizerOpenGL::SetShader() { SyncGlobalAmbient(); for (int light_index = 0; light_index < 8; light_index++) { + SyncLightSpecular0(light_index); + SyncLightSpecular1(light_index); SyncLightDiffuse(light_index); SyncLightAmbient(light_index); SyncLightPosition(light_index); From 562f36a1442f112fa4bedfce12f7a0e8470369cf Mon Sep 17 00:00:00 2001 From: tfarley Date: Fri, 22 Apr 2016 10:52:02 -0400 Subject: [PATCH 09/39] HWRasterizer: reorder declarations to match defs --- src/video_core/renderer_opengl/gl_rasterizer.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 5aa638985..8d6177e88 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -332,18 +332,24 @@ private: /// Syncs the depth test states to match the PICA register void SyncDepthTest(); - /// Syncs the TEV constant color to match the PICA register - void SyncTevConstColor(int tev_index, const Pica::Regs::TevStageConfig& tev_stage); - /// Syncs the TEV combiner color buffer to match the PICA register void SyncCombinerColor(); + /// Syncs the TEV constant color to match the PICA register + void SyncTevConstColor(int tev_index, const Pica::Regs::TevStageConfig& tev_stage); + /// Syncs the lighting global ambient color to match the PICA register void SyncGlobalAmbient(); /// Syncs the lighting lookup tables void SyncLightingLUT(unsigned index); + /// Syncs the specified light's specular 0 color to match the PICA register + void SyncLightSpecular0(int light_index); + + /// Syncs the specified light's specular 1 color to match the PICA register + void SyncLightSpecular1(int light_index); + /// Syncs the specified light's diffuse color to match the PICA register void SyncLightDiffuse(int light_index); @@ -353,12 +359,6 @@ private: /// Syncs the specified light's position to match the PICA register void SyncLightPosition(int light_index); - /// Syncs the specified light's specular 0 color to match the PICA register - void SyncLightSpecular0(int light_index); - - /// Syncs the specified light's specular 1 color to match the PICA register - void SyncLightSpecular1(int light_index); - OpenGLState state; RasterizerCacheOpenGL res_cache; From d051bd303225838e0e50a3e3ddc22337c1fc0f0c Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 27 Mar 2016 11:59:50 +0800 Subject: [PATCH 10/39] CMakeLists: Use CMAKE_THREAD_LIBS_INIT --- CMakeLists.txt | 6 ++++-- src/citra/CMakeLists.txt | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a0a161e7..ddde19760 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,8 +65,8 @@ endif() message(STATUS "Target architecture: ${ARCHITECTURE}") if (NOT MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -Wno-attributes -pthread") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -Wno-attributes") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") if (ARCHITECTURE_x86_64) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.1") @@ -135,6 +135,8 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/cmake-modules") find_package(OpenGL REQUIRED) include_directories(${OPENGL_INCLUDE_DIR}) +find_package(Threads REQUIRED) + if (ENABLE_SDL2) if (CITRA_USE_BUNDLED_SDL2) # Detect toolchain and platform diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index fa615deb9..351752c1c 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt @@ -21,7 +21,7 @@ target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad) if (MSVC) target_link_libraries(citra getopt) endif() -target_link_libraries(citra ${PLATFORM_LIBRARIES}) +target_link_libraries(citra ${PLATFORM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD") install(TARGETS citra RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") From aacc3a4a59a9a66c3768c8cbba0ab4c03a4e5920 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 27 Mar 2016 12:01:29 +0800 Subject: [PATCH 11/39] microprofile: Use std::abs Using the global-namespace C function will cause the wrong overload to get picked --- externals/microprofile/microprofileui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/microprofile/microprofileui.h b/externals/microprofile/microprofileui.h index eac1119a4..cc12739cb 100644 --- a/externals/microprofile/microprofileui.h +++ b/externals/microprofile/microprofileui.h @@ -879,7 +879,7 @@ void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int nBaseY, static int64_t nRefCpu = 0, nRefGpu = 0; if(MicroProfileGetGpuTickReference(&nTickReferenceCpu, &nTickReferenceGpu)) { - if(0 == nRefCpu || abs(nRefCpu-nBaseTicksCpu) > abs(nTickReferenceCpu-nBaseTicksCpu)) + if(0 == nRefCpu || std::abs(nRefCpu-nBaseTicksCpu) > std::abs(nTickReferenceCpu-nBaseTicksCpu)) { nRefCpu = nTickReferenceCpu; nRefGpu = nTickReferenceGpu; From fdd7e9e86aba614b20d1773112c43c094f668803 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 27 Mar 2016 12:01:57 +0800 Subject: [PATCH 12/39] microprofileui: Use correct printf specifier --- externals/microprofile/microprofileui.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/externals/microprofile/microprofileui.h b/externals/microprofile/microprofileui.h index cc12739cb..45bec8af6 100644 --- a/externals/microprofile/microprofileui.h +++ b/externals/microprofile/microprofileui.h @@ -1230,7 +1230,12 @@ void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int nBaseY, char ThreadName[MicroProfileThreadLog::THREAD_MAX_LEN + 16]; const char* cLocal = MicroProfileIsLocalThread(nThreadId) ? "*": " "; +#if defined(WIN32) + // nThreadId is 32-bit on Windows int nStrLen = snprintf(ThreadName, sizeof(ThreadName)-1, "%04x: %s%s", nThreadId, cLocal, i < nNumThreadsBase ? &S.Pool[i]->ThreadName[0] : MICROPROFILE_THREAD_NAME_FROM_ID(nThreadId) ); +#else + int nStrLen = snprintf(ThreadName, sizeof(ThreadName)-1, "%04llx: %s%s", nThreadId, cLocal, i < nNumThreadsBase ? &S.Pool[i]->ThreadName[0] : MICROPROFILE_THREAD_NAME_FROM_ID(nThreadId) ); +#endif uint32_t nThreadColor = -1; if(nThreadId == nContextSwitchHoverThreadAfter || nThreadId == nContextSwitchHoverThreadBefore) nThreadColor = UI.nHoverColorShared|0x906060; From 2850a223592edad677e2fcd31a5f53b057c4ecc8 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 27 Mar 2016 12:02:22 +0800 Subject: [PATCH 13/39] debugger: Warn if we reach an unreachable format --- src/citra_qt/debugger/graphics_framebuffer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp index c30e75933..68cff78b2 100644 --- a/src/citra_qt/debugger/graphics_framebuffer.cpp +++ b/src/citra_qt/debugger/graphics_framebuffer.cpp @@ -346,5 +346,11 @@ u32 GraphicsFramebufferWidget::BytesPerPixel(GraphicsFramebufferWidget::Format f case Format::RGBA4: case Format::D16: return 2; + default: + UNREACHABLE_MSG("GraphicsFramebufferWidget::BytesPerPixel: this " + "should not be reached as this function should " + "be given a format which is in " + "GraphicsFramebufferWidget::Format. Instead got %i", + static_cast(format)); } } From 41ec40e9aa2e8445ac73f90f9784c4130b977dae Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 27 Mar 2016 12:03:19 +0800 Subject: [PATCH 14/39] gdbstub: Don't check if unsigned int is > 0 --- src/core/gdbstub/gdbstub.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index c1a7ec5bf..ae0c116ef 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -529,7 +529,7 @@ static void ReadRegister() { id |= HexCharToValue(command_buffer[2]); } - if (id >= R0_REGISTER && id <= R15_REGISTER) { + if (id <= R15_REGISTER) { IntToGdbHex(reply, Core::g_app_core->GetReg(id)); } else if (id == CPSR_REGISTER) { IntToGdbHex(reply, Core::g_app_core->GetCPSR()); @@ -584,7 +584,7 @@ static void WriteRegister() { id |= HexCharToValue(command_buffer[2]); } - if (id >= R0_REGISTER && id <= R15_REGISTER) { + if (id <= R15_REGISTER) { Core::g_app_core->SetReg(id, GdbHexToInt(buffer_ptr)); } else if (id == CPSR_REGISTER) { Core::g_app_core->SetCPSR(GdbHexToInt(buffer_ptr)); From 040b7386a92fcadc5c4b08643299567de876c4d6 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 27 Mar 2016 12:03:44 +0800 Subject: [PATCH 15/39] fs: Fix what appears to be a typo (filename_size / file_size) --- src/core/hle/service/fs/fs_user.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 3ec7ceb30..7df7da5a4 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -250,7 +250,7 @@ static void CreateFile(Service::Interface* self) { FileSys::Path file_path(filename_type, filename_size, filename_ptr); - LOG_DEBUG(Service_FS, "type=%d size=%llu data=%s", filename_type, filename_size, file_path.DebugStr().c_str()); + LOG_DEBUG(Service_FS, "type=%d size=%llu data=%s", filename_type, file_size, file_path.DebugStr().c_str()); cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw; } From 60f2587eaca6d9e3d73f5361b37b67dcd4a2a72d Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 27 Mar 2016 12:04:01 +0800 Subject: [PATCH 16/39] ncch: Use correct format specifier (for long long uint) --- src/core/loader/ncch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index a4b47ef8c..066e91a9e 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -255,7 +255,7 @@ ResultStatus AppLoader_NCCH::Load() { resource_limit_category = exheader_header.arm11_system_local_caps.resource_limit_category; LOG_INFO(Loader, "Name: %s" , exheader_header.codeset_info.name); - LOG_INFO(Loader, "Program ID: %016X" , ncch_header.program_id); + LOG_INFO(Loader, "Program ID: %016llX" , ncch_header.program_id); LOG_DEBUG(Loader, "Code compressed: %s" , is_compressed ? "yes" : "no"); LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point); LOG_DEBUG(Loader, "Code size: 0x%08X", code_size); From 39d4994c15c46aeef0b3643e6db8b1f735674a7a Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 27 Mar 2016 12:04:16 +0800 Subject: [PATCH 17/39] pica: Handle default lighting case --- src/video_core/pica.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 1810eca98..cf130d7f8 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -747,8 +747,13 @@ struct Regs { case LightingSampler::ReflectGreen: case LightingSampler::ReflectBlue: return (config == LightingConfig::Config4) || (config == LightingConfig::Config5) || (config == LightingConfig::Config7); + default: + UNREACHABLE_MSG("Regs::IsLightingSamplerSupported: Reached " + "unreachable section, sampler should be one " + "of Distribution0, Distribution1, Fresnel, " + "ReflectRed, ReflectGreen or ReflectBlue, instead " + "got %i", static_cast(config)); } - return false; } struct { From 205e8f9f9ece7ee335a1987aa4a1b72f7409abd5 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Mon, 11 Apr 2016 07:22:41 +0800 Subject: [PATCH 18/39] assert: Add _MSG variations for UNREACHABLE and UNIMPLEMENTED --- src/common/assert.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/assert.h b/src/common/assert.h index 6849778b7..d7f19f5eb 100644 --- a/src/common/assert.h +++ b/src/common/assert.h @@ -39,6 +39,7 @@ static void assert_noinline_call(const Fn& fn) { }); } while (0) #define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!") +#define UNREACHABLE_MSG(_a_, ...) ASSERT_MSG(false, _a_, __VA_ARGS__) #ifdef _DEBUG #define DEBUG_ASSERT(_a_) ASSERT(_a_) @@ -49,3 +50,4 @@ static void assert_noinline_call(const Fn& fn) { #endif #define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!") +#define UNIMPLEMENTED_MSG(_a_, ...) ASSERT_MSG(false, _a_, __VA_ARGS__) \ No newline at end of file From 9572652ddce1ff51f48829d787ebe33272c6aff6 Mon Sep 17 00:00:00 2001 From: LittleWhite Date: Sat, 23 Apr 2016 15:45:35 +0200 Subject: [PATCH 19/39] Protect use of std::is_trivially_copyable to compile with GCC 4.9 --- src/common/file_util.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/common/file_util.h b/src/common/file_util.h index b54a9fb72..3aac4fa46 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -192,7 +192,9 @@ public: size_t ReadArray(T* data, size_t length) { static_assert(std::is_standard_layout(), "Given array does not consist of standard layout objects"); +#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER) static_assert(std::is_trivially_copyable(), "Given array does not consist of trivially copyable objects"); +#endif if (!IsOpen()) { m_good = false; @@ -210,7 +212,9 @@ public: size_t WriteArray(const T* data, size_t length) { static_assert(std::is_standard_layout(), "Given array does not consist of standard layout objects"); +#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER) static_assert(std::is_trivially_copyable(), "Given array does not consist of trivially copyable objects"); +#endif if (!IsOpen()) { m_good = false; From 913f7ee524f9691ae2dc22625b5af9be3dcfe49e Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sun, 24 Apr 2016 09:01:53 +0100 Subject: [PATCH 20/39] DSP_DSP: Remove unused variable --- src/core/hle/service/dsp_dsp.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 08e437125..751cb3d61 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -17,7 +17,6 @@ namespace DSP_DSP { -static u32 read_pipe_count; static Kernel::SharedPtr semaphore_event; struct PairHash { @@ -458,7 +457,6 @@ const Interface::FunctionInfo FunctionTable[] = { Interface::Interface() { semaphore_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "DSP_DSP::semaphore_event"); - read_pipe_count = 0; Register(FunctionTable); } From 01a1555b5d6d6b560c3168b76e5235175e7d081b Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 24 Apr 2016 14:19:49 +0200 Subject: [PATCH 21/39] Replace std::map with std::array for graphics event breakpoints, and allow the compiler to inline. Saves 1%+ in vertex heavy situations. --- src/citra_qt/debugger/graphics_breakpoints.cpp | 4 ++-- src/video_core/debug_utils/debug_utils.cpp | 5 +---- src/video_core/debug_utils/debug_utils.h | 16 +++++++++++++--- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/citra_qt/debugger/graphics_breakpoints.cpp b/src/citra_qt/debugger/graphics_breakpoints.cpp index 819ec7707..c8510128a 100644 --- a/src/citra_qt/debugger/graphics_breakpoints.cpp +++ b/src/citra_qt/debugger/graphics_breakpoints.cpp @@ -75,7 +75,7 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const case Role_IsEnabled: { auto context = context_weak.lock(); - return context && context->breakpoints[event].enabled; + return context && context->breakpoints[(int)event].enabled; } default: @@ -110,7 +110,7 @@ bool BreakPointModel::setData(const QModelIndex& index, const QVariant& value, i if (!context) return false; - context->breakpoints[event].enabled = value == Qt::Checked; + context->breakpoints[(int)event].enabled = value == Qt::Checked; QModelIndex changed_index = createIndex(index.row(), 0); emit dataChanged(changed_index, changed_index); return true; diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 1f058c4e2..178a566f7 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp @@ -40,10 +40,7 @@ using nihstro::DVLPHeader; namespace Pica { -void DebugContext::OnEvent(Event event, void* data) { - if (!breakpoints[event].enabled) - return; - +void DebugContext::DoOnEvent(Event event, void* data) { { std::unique_lock lock(breakpoint_mutex); diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index 7df941619..56f9bd958 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h @@ -114,7 +114,15 @@ public: * @param event Event which has happened * @param data Optional data pointer (pass nullptr if unused). Needs to remain valid until Resume() is called. */ - void OnEvent(Event event, void* data); + void OnEvent(Event event, void* data) { + // This check is left in the header to allow the compiler to inline it. + if (!breakpoints[(int)event].enabled) + return; + // For the rest of event handling, call a separate function. + DoOnEvent(event, data); + } + + void DoOnEvent(Event event, void *data); /** * Resume from the current breakpoint. @@ -126,12 +134,14 @@ public: * Delete all set breakpoints and resume emulation. */ void ClearBreakpoints() { - breakpoints.clear(); + for (auto &bp : breakpoints) { + bp.enabled = false; + } Resume(); } // TODO: Evaluate if access to these members should be hidden behind a public interface. - std::map breakpoints; + std::array breakpoints; Event active_breakpoint; bool at_breakpoint = false; From df81fa11fc8972a5775a57ccde1e0ef8d7fbfe64 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 24 Apr 2016 23:39:25 +0800 Subject: [PATCH 22/39] CMakeLists: Use imported version of Threads::Threads This requires bumping up to a minimum of CMake 3.1. The benefit of using the imported target is that you can switch to the -pthread compiler flag on request, which may be necessary for some systems if available. --- CMakeLists.txt | 8 +++++--- src/citra/CMakeLists.txt | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ddde19760..019321ad8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ -# CMake 2.8.11 required for Qt5 settings to be applied automatically on -# dependent libraries. -cmake_minimum_required(VERSION 2.8.11) +# CMake 3.1 required for Qt5 settings to be applied automatically on +# dependent libraries and IMPORTED targets. +cmake_minimum_required(VERSION 3.1) function(download_bundled_external remote_path lib_name prefix_var) set(prefix "${CMAKE_BINARY_DIR}/externals/${lib_name}") @@ -135,6 +135,8 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/cmake-modules") find_package(OpenGL REQUIRED) include_directories(${OPENGL_INCLUDE_DIR}) +# Prefer the -pthread flag on Linux. +set (THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) if (ENABLE_SDL2) diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index 351752c1c..43fa06b4e 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt @@ -21,7 +21,7 @@ target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad) if (MSVC) target_link_libraries(citra getopt) endif() -target_link_libraries(citra ${PLATFORM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(citra ${PLATFORM_LIBRARIES} Threads::Threads) if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD") install(TARGETS citra RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") From ef6873980eb3854d351c319514fca4116165283c Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 24 Apr 2016 23:40:14 +0800 Subject: [PATCH 23/39] assert: Allow UNREACHABLE_MSG to have just one argument --- src/common/assert.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/assert.h b/src/common/assert.h index d7f19f5eb..cd9b819a9 100644 --- a/src/common/assert.h +++ b/src/common/assert.h @@ -39,7 +39,7 @@ static void assert_noinline_call(const Fn& fn) { }); } while (0) #define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!") -#define UNREACHABLE_MSG(_a_, ...) ASSERT_MSG(false, _a_, __VA_ARGS__) +#define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__) #ifdef _DEBUG #define DEBUG_ASSERT(_a_) ASSERT(_a_) From e16541e47c657923ad5dedd64560afd0902cdda2 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 24 Apr 2016 23:40:41 +0800 Subject: [PATCH 24/39] am: title_id is long long uint --- src/core/hle/service/am/am.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 9591522e5..3f71e7f2b 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -43,7 +43,7 @@ void FindContentInfos(Service::Interface* self) { am_content_count[media_type] = cmd_buff[4]; cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_id=0x%016lx, content_cound=%u, content_ids_pointer=0x%08x, content_info_pointer=0x%08x", + LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_id=0x%016llx, content_cound=%u, content_ids_pointer=0x%08x, content_info_pointer=0x%08x", media_type, title_id, am_content_count[media_type], content_ids_pointer, content_info_pointer); } From bbffa6ad6945f2ea91a61dc7ab45c0fd814fcf82 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 24 Apr 2016 23:40:52 +0800 Subject: [PATCH 25/39] shader: Format string must be provided inline and not as a variable --- src/video_core/shader/shader_jit_x64.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp index b47d3beda..d5a6a349c 100644 --- a/src/video_core/shader/shader_jit_x64.cpp +++ b/src/video_core/shader/shader_jit_x64.cpp @@ -148,7 +148,7 @@ static Instruction GetVertexShaderInstruction(size_t offset) { } static void LogCritical(const char* msg) { - LOG_CRITICAL(HW_GPU, msg); + LOG_CRITICAL(HW_GPU, "%s", msg); } void JitShader::Compile_Assert(bool condition, const char* msg) { From c6709d97bce27f248b78374a531a3ee01275c65d Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 24 Apr 2016 23:41:25 +0800 Subject: [PATCH 26/39] shader: Handle non-CALL opcodes with a break --- src/video_core/shader/shader_jit_x64.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp index d5a6a349c..9ee4065bf 100644 --- a/src/video_core/shader/shader_jit_x64.cpp +++ b/src/video_core/shader/shader_jit_x64.cpp @@ -795,6 +795,8 @@ void JitShader::FindReturnOffsets() { case OpCode::Id::CALLU: return_offsets.push_back(instr.flow_control.dest_offset + instr.flow_control.num_instructions); break; + default: + break; } } From 656a4424332824c144ef5b91482ee2a8d89fc0fb Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 24 Apr 2016 23:41:44 +0800 Subject: [PATCH 27/39] shader: Shader size is long uint, not uint. --- src/video_core/shader/shader_jit_x64.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp index 9ee4065bf..b7747fa42 100644 --- a/src/video_core/shader/shader_jit_x64.cpp +++ b/src/video_core/shader/shader_jit_x64.cpp @@ -856,7 +856,7 @@ void JitShader::Compile() { uintptr_t size = reinterpret_cast(GetCodePtr()) - reinterpret_cast(program); ASSERT_MSG(size <= MAX_SHADER_SIZE, "Compiled a shader that exceeds the allocated size!"); - LOG_DEBUG(HW_GPU, "Compiled shader size=%d", size); + LOG_DEBUG(HW_GPU, "Compiled shader size=%lu", size); } JitShader::JitShader() { From 599dc2bbb5e7325b9baa8d55ef17915dd283173d Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Mon, 25 Apr 2016 09:40:12 +0800 Subject: [PATCH 28/39] travis: Install cmake 3.1 --- .travis-deps.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis-deps.sh b/.travis-deps.sh index c7bb7e785..4a79feb70 100755 --- a/.travis-deps.sh +++ b/.travis-deps.sh @@ -9,7 +9,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then export CXX=g++-5 mkdir -p $HOME/.local - curl -L http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \ + curl -L http://www.cmake.org/files/v3.1/cmake-3.1.0-Linux-i386.tar.gz \ | tar -xz -C $HOME/.local --strip-components=1 ( @@ -20,6 +20,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then ) elif [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update > /dev/null # silence the very verbose output - brew install qt5 sdl2 dylibbundler + brew unlink cmake + brew install cmake31 qt5 sdl2 dylibbundler gem install xcpretty fi From 555907ce8dc842ef0859537a0c25443a5e9527bb Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sun, 24 Apr 2016 09:12:15 +0100 Subject: [PATCH 29/39] DSP/Pipe: There are 8 pipes --- src/audio_core/hle/pipe.cpp | 28 +++++++++++++++++----------- src/audio_core/hle/pipe.h | 4 ++-- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/audio_core/hle/pipe.cpp b/src/audio_core/hle/pipe.cpp index 9381883b4..7ec97dfda 100644 --- a/src/audio_core/hle/pipe.cpp +++ b/src/audio_core/hle/pipe.cpp @@ -17,7 +17,7 @@ namespace HLE { static DspState dsp_state = DspState::Off; -static std::array, static_cast(DspPipe::DspPipe_MAX)> pipe_data; +static std::array, NUM_DSP_PIPE> pipe_data; void ResetPipes() { for (auto& data : pipe_data) { @@ -27,16 +27,18 @@ void ResetPipes() { } std::vector PipeRead(DspPipe pipe_number, u32 length) { - if (pipe_number >= DspPipe::DspPipe_MAX) { - LOG_ERROR(Audio_DSP, "pipe_number = %u invalid", pipe_number); + const size_t pipe_index = static_cast(pipe_number); + + if (pipe_index >= NUM_DSP_PIPE) { + LOG_ERROR(Audio_DSP, "pipe_number = %zu invalid", pipe_index); return {}; } - std::vector& data = pipe_data[static_cast(pipe_number)]; + std::vector& data = pipe_data[pipe_index]; if (length > data.size()) { - LOG_WARNING(Audio_DSP, "pipe_number = %u is out of data, application requested read of %u but %zu remain", - pipe_number, length, data.size()); + LOG_WARNING(Audio_DSP, "pipe_number = %zu is out of data, application requested read of %u but %zu remain", + pipe_index, length, data.size()); length = data.size(); } @@ -49,16 +51,20 @@ std::vector PipeRead(DspPipe pipe_number, u32 length) { } size_t GetPipeReadableSize(DspPipe pipe_number) { - if (pipe_number >= DspPipe::DspPipe_MAX) { - LOG_ERROR(Audio_DSP, "pipe_number = %u invalid", pipe_number); + const size_t pipe_index = static_cast(pipe_number); + + if (pipe_index >= NUM_DSP_PIPE) { + LOG_ERROR(Audio_DSP, "pipe_number = %zu invalid", pipe_index); return 0; } - return pipe_data[static_cast(pipe_number)].size(); + return pipe_data[pipe_index].size(); } static void WriteU16(DspPipe pipe_number, u16 value) { - std::vector& data = pipe_data[static_cast(pipe_number)]; + const size_t pipe_index = static_cast(pipe_number); + + std::vector& data = pipe_data.at(pipe_index); // Little endian data.emplace_back(value & 0xFF); data.emplace_back(value >> 8); @@ -145,7 +151,7 @@ void PipeWrite(DspPipe pipe_number, const std::vector& buffer) { return; } default: - LOG_CRITICAL(Audio_DSP, "pipe_number = %u unimplemented", pipe_number); + LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented", static_cast(pipe_number)); UNIMPLEMENTED(); return; } diff --git a/src/audio_core/hle/pipe.h b/src/audio_core/hle/pipe.h index 382d35e87..64d97f8ba 100644 --- a/src/audio_core/hle/pipe.h +++ b/src/audio_core/hle/pipe.h @@ -19,9 +19,9 @@ enum class DspPipe { Debug = 0, Dma = 1, Audio = 2, - Binary = 3, - DspPipe_MAX + Binary = 3 }; +constexpr size_t NUM_DSP_PIPE = 8; /** * Read a DSP pipe. From 591ffad670d742d85bdf2e975415a7bbdd82a14f Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 26 Apr 2016 21:01:11 +0100 Subject: [PATCH 30/39] Qt Frontend: Add Threads::Threads import in CMakeLists.txt. This had been forgotten in df81fa11fc8972a5775a57ccde1e0ef8d7fbfe64. Fixes #1711. --- src/citra_qt/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 6660d9879..cc9e0c624 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -92,7 +92,7 @@ else() endif() target_link_libraries(citra-qt core video_core audio_core common qhexedit) target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) -target_link_libraries(citra-qt ${PLATFORM_LIBRARIES}) +target_link_libraries(citra-qt ${PLATFORM_LIBRARIES} Threads::Threads) if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD") install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") From 12f72a65973716799e1ecd3885cdbc27603e631c Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 26 Apr 2016 19:49:19 -0400 Subject: [PATCH 31/39] y2r_u: Cleanup some formatting. --- src/core/hle/service/y2r_u.cpp | 141 +++++++++++++++++++++------------ 1 file changed, 89 insertions(+), 52 deletions(-) diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 76ec154dd..d16578f87 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -88,10 +88,11 @@ static void SetInputFormat(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.input_format = static_cast(cmd_buff[1]); - LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); } static void GetInputFormat(Service::Interface* self) { @@ -100,6 +101,7 @@ static void GetInputFormat(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = static_cast(conversion.input_format); + LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); } @@ -107,10 +109,11 @@ static void SetOutputFormat(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.output_format = static_cast(cmd_buff[1]); - LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); cmd_buff[0] = IPC::MakeHeader(0x3, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); } static void GetOutputFormat(Service::Interface* self) { @@ -119,6 +122,7 @@ static void GetOutputFormat(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x4, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = static_cast(conversion.output_format); + LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); } @@ -126,10 +130,11 @@ static void SetRotation(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.rotation = static_cast(cmd_buff[1]); - LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); } static void GetRotation(Service::Interface* self) { @@ -138,6 +143,7 @@ static void GetRotation(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x6, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = static_cast(conversion.rotation); + LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); } @@ -145,10 +151,11 @@ static void SetBlockAlignment(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.block_alignment = static_cast(cmd_buff[1]); - LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); } static void GetBlockAlignment(Service::Interface* self) { @@ -157,6 +164,7 @@ static void GetBlockAlignment(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x8, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = static_cast(conversion.block_alignment); + LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); } @@ -173,6 +181,7 @@ static void SetSpacialDithering(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } @@ -188,6 +197,7 @@ static void GetSpacialDithering(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = spacial_dithering_enabled; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } @@ -204,6 +214,7 @@ static void SetTemporalDithering(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0xB, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } @@ -219,6 +230,7 @@ static void GetTemporalDithering(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = temporal_dithering_enabled; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } @@ -235,6 +247,7 @@ static void SetTransferEndInterrupt(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } @@ -250,6 +263,7 @@ static void GetTransferEndInterrupt(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0xE, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = transfer_end_interrupt_enabled; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } @@ -265,6 +279,7 @@ static void GetTransferEndEvent(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); + LOG_DEBUG(Service_Y2R, "called"); } @@ -275,13 +290,12 @@ static void SetSendingY(Service::Interface* self) { conversion.src_Y.image_size = cmd_buff[2]; conversion.src_Y.transfer_unit = cmd_buff[3]; conversion.src_Y.gap = cmd_buff[4]; - u32 src_process_handle = cmd_buff[6]; - LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " - "src_process_handle=0x%08X", conversion.src_Y.image_size, - conversion.src_Y.transfer_unit, conversion.src_Y.gap, src_process_handle); cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", + conversion.src_Y.image_size, conversion.src_Y.transfer_unit, conversion.src_Y.gap, cmd_buff[6]); } static void SetSendingU(Service::Interface* self) { @@ -291,13 +305,12 @@ static void SetSendingU(Service::Interface* self) { conversion.src_U.image_size = cmd_buff[2]; conversion.src_U.transfer_unit = cmd_buff[3]; conversion.src_U.gap = cmd_buff[4]; - u32 src_process_handle = cmd_buff[6]; - LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " - "src_process_handle=0x%08X", conversion.src_U.image_size, - conversion.src_U.transfer_unit, conversion.src_U.gap, src_process_handle); cmd_buff[0] = IPC::MakeHeader(0x11, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", + conversion.src_U.image_size, conversion.src_U.transfer_unit, conversion.src_U.gap, cmd_buff[6]); } static void SetSendingV(Service::Interface* self) { @@ -307,13 +320,12 @@ static void SetSendingV(Service::Interface* self) { conversion.src_V.image_size = cmd_buff[2]; conversion.src_V.transfer_unit = cmd_buff[3]; conversion.src_V.gap = cmd_buff[4]; - u32 src_process_handle = cmd_buff[6]; - LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " - "src_process_handle=0x%08X", conversion.src_V.image_size, - conversion.src_V.transfer_unit, conversion.src_V.gap, src_process_handle); cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", + conversion.src_V.image_size, conversion.src_V.transfer_unit, conversion.src_V.gap, cmd_buff[6]); } static void SetSendingYUYV(Service::Interface* self) { @@ -323,13 +335,12 @@ static void SetSendingYUYV(Service::Interface* self) { conversion.src_YUYV.image_size = cmd_buff[2]; conversion.src_YUYV.transfer_unit = cmd_buff[3]; conversion.src_YUYV.gap = cmd_buff[4]; - u32 src_process_handle = cmd_buff[6]; - LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " - "src_process_handle=0x%08X", conversion.src_YUYV.image_size, - conversion.src_YUYV.transfer_unit, conversion.src_YUYV.gap, src_process_handle); cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", + conversion.src_YUYV.image_size, conversion.src_YUYV.transfer_unit, conversion.src_YUYV.gap, cmd_buff[6]); } /** @@ -344,6 +355,7 @@ static void IsFinishedSendingYuv(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x14, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 1; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } @@ -359,6 +371,7 @@ static void IsFinishedSendingY(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x15, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 1; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } @@ -374,6 +387,7 @@ static void IsFinishedSendingU(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 1; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } @@ -389,6 +403,7 @@ static void IsFinishedSendingV(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x17, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 1; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } @@ -399,14 +414,12 @@ static void SetReceiving(Service::Interface* self) { conversion.dst.image_size = cmd_buff[2]; conversion.dst.transfer_unit = cmd_buff[3]; conversion.dst.gap = cmd_buff[4]; - u32 dst_process_handle = cmd_buff[6]; - LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " - "dst_process_handle=0x%08X", conversion.dst.image_size, - conversion.dst.transfer_unit, conversion.dst.gap, - dst_process_handle); cmd_buff[0] = IPC::MakeHeader(0x18, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, dst_process_handle=0x%08X", + conversion.dst.image_size, conversion.dst.transfer_unit, conversion.dst.gap, cmd_buff[6]); } /** @@ -421,15 +434,17 @@ static void IsFinishedReceiving(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x19, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 1; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } static void SetInputLineWidth(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - LOG_DEBUG(Service_Y2R, "called input_line_width=%u", cmd_buff[1]); cmd_buff[0] = IPC::MakeHeader(0x1A, 1, 0); cmd_buff[1] = conversion.SetInputLineWidth(cmd_buff[1]).raw; + + LOG_DEBUG(Service_Y2R, "called input_line_width=%u", cmd_buff[1]); } static void GetInputLineWidth(Service::Interface* self) { @@ -438,15 +453,17 @@ static void GetInputLineWidth(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x1B, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = conversion.input_line_width; + LOG_DEBUG(Service_Y2R, "called input_line_width=%u", conversion.input_line_width); } static void SetInputLines(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - LOG_DEBUG(Service_Y2R, "called input_lines=%u", cmd_buff[1]); cmd_buff[0] = IPC::MakeHeader(0x1C, 1, 0); cmd_buff[1] = conversion.SetInputLines(cmd_buff[1]).raw; + + LOG_DEBUG(Service_Y2R, "called input_lines=%u", cmd_buff[1]); } static void GetInputLines(Service::Interface* self) { @@ -455,6 +472,7 @@ static void GetInputLines(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x1D, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = static_cast(conversion.input_lines); + LOG_DEBUG(Service_Y2R, "called input_lines=%u", conversion.input_lines); } @@ -463,12 +481,13 @@ static void SetCoefficient(Service::Interface* self) { const u16* coefficients = reinterpret_cast(&cmd_buff[1]); std::memcpy(conversion.coefficients.data(), coefficients, sizeof(CoefficientSet)); - LOG_DEBUG(Service_Y2R, "called coefficients=[%hX, %hX, %hX, %hX, %hX, %hX, %hX, %hX]", - coefficients[0], coefficients[1], coefficients[2], coefficients[3], - coefficients[4], coefficients[5], coefficients[6], coefficients[7]); cmd_buff[0] = IPC::MakeHeader(0x1E, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called coefficients=[%hX, %hX, %hX, %hX, %hX, %hX, %hX, %hX]", + coefficients[0], coefficients[1], coefficients[2], coefficients[3], + coefficients[4], coefficients[5], coefficients[6], coefficients[7]); } static void GetCoefficient(Service::Interface* self) { @@ -477,6 +496,8 @@ static void GetCoefficient(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x1F, 5, 0); cmd_buff[1] = RESULT_SUCCESS.raw; std::memcpy(&cmd_buff[2], conversion.coefficients.data(), sizeof(CoefficientSet)); + + LOG_DEBUG(Service_Y2R, "called"); } static void SetStandardCoefficient(Service::Interface* self) { @@ -486,6 +507,7 @@ static void SetStandardCoefficient(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0); cmd_buff[1] = conversion.SetStandardCoefficient((StandardCoefficient)index).raw; + LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u", index); } @@ -495,15 +517,15 @@ static void GetStandardCoefficient(Service::Interface* self) { u32 index = cmd_buff[1]; if (index < ARRAY_SIZE(standard_coefficients)) { - std::memcpy(&cmd_buff[2], &standard_coefficients[index], sizeof(CoefficientSet)); - cmd_buff[0] = IPC::MakeHeader(0x21, 5, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + std::memcpy(&cmd_buff[2], &standard_coefficients[index], sizeof(CoefficientSet)); + LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u ", index); - } - else { + } else { cmd_buff[0] = IPC::MakeHeader(0x21, 1, 0); - cmd_buff[1] = -1; + cmd_buff[1] = -1; // TODO(bunnei): Identify the correct error code for this + LOG_ERROR(Service_Y2R, "called standard_coefficient=%u The argument is invalid!", index); } } @@ -512,10 +534,11 @@ static void SetAlpha(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.alpha = cmd_buff[1]; - LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); cmd_buff[0] = IPC::MakeHeader(0x22, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); } static void GetAlpha(Service::Interface* self) { @@ -524,6 +547,7 @@ static void GetAlpha(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x23, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = conversion.alpha; + LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); } @@ -533,6 +557,7 @@ static void SetDitheringWeightParams(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x24, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called"); } @@ -542,6 +567,7 @@ static void GetDitheringWeightParams(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x25, 9, 0); cmd_buff[1] = RESULT_SUCCESS.raw; std::memcpy(&cmd_buff[2], &dithering_weight_params, sizeof(DitheringWeightParams)); + LOG_DEBUG(Service_Y2R, "called"); } @@ -549,17 +575,17 @@ static void StartConversion(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); // dst_image_size would seem to be perfect for this, but it doesn't include the gap :( - u32 total_output_size = conversion.input_lines * - (conversion.dst.transfer_unit + conversion.dst.gap); + u32 total_output_size = conversion.input_lines * (conversion.dst.transfer_unit + conversion.dst.gap); Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(conversion.dst.address), total_output_size); HW::Y2R::PerformConversion(conversion); - LOG_DEBUG(Service_Y2R, "called"); completion_event->Signal(); cmd_buff[0] = IPC::MakeHeader(0x26, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called"); } static void StopConversion(Service::Interface* self) { @@ -567,6 +593,7 @@ static void StopConversion(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x27, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called"); } @@ -582,6 +609,7 @@ static void IsBusyConversion(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x28, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 0; // StartConversion always finishes immediately + LOG_DEBUG(Service_Y2R, "called"); } @@ -592,32 +620,38 @@ static void SetPackageParameter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); auto params = reinterpret_cast(&cmd_buff[1]); - LOG_DEBUG(Service_Y2R, - "called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu " - "input_line_width=%hu input_lines=%hu standard_coefficient=%hhu " - "reserved=%hhu alpha=%hX", - params->input_format, params->output_format, params->rotation, params->block_alignment, - params->input_line_width, params->input_lines, params->standard_coefficient, - params->padding, params->alpha); - - ResultCode result = RESULT_SUCCESS; conversion.input_format = params->input_format; conversion.output_format = params->output_format; conversion.rotation = params->rotation; conversion.block_alignment = params->block_alignment; - result = conversion.SetInputLineWidth(params->input_line_width); - if (result.IsError()) goto cleanup; + + ResultCode result = conversion.SetInputLineWidth(params->input_line_width); + + if (result.IsError()) + goto cleanup; + result = conversion.SetInputLines(params->input_lines); - if (result.IsError()) goto cleanup; + + if (result.IsError()) + goto cleanup; + result = conversion.SetStandardCoefficient(params->standard_coefficient); - if (result.IsError()) goto cleanup; + + if (result.IsError()) + goto cleanup; + conversion.padding = params->padding; conversion.alpha = params->alpha; cleanup: cmd_buff[0] = IPC::MakeHeader(0x29, 1, 0); cmd_buff[1] = result.raw; + + LOG_DEBUG(Service_Y2R, "called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu " + "input_line_width=%hu input_lines=%hu standard_coefficient=%hhu reserved=%hhu alpha=%hX", + params->input_format, params->output_format, params->rotation, params->block_alignment, + params->input_line_width, params->input_lines, params->standard_coefficient, params->padding, params->alpha); } static void PingProcess(Service::Interface* self) { @@ -626,6 +660,7 @@ static void PingProcess(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x2A, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 0; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } @@ -651,6 +686,7 @@ static void DriverInitialize(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x2B, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called"); } @@ -659,6 +695,7 @@ static void DriverFinalize(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x2C, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called"); } From ff6db69c6052f674265c453932a3dc7637c46412 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sun, 24 Apr 2016 10:21:10 +0100 Subject: [PATCH 32/39] DSP_DSP: Updated interrupt implementation --- src/audio_core/audio_core.cpp | 7 +- src/audio_core/hle/pipe.cpp | 4 + src/core/hle/service/dsp_dsp.cpp | 129 +++++++++++++++++++++++-------- src/core/hle/service/dsp_dsp.h | 19 ++--- 4 files changed, 113 insertions(+), 46 deletions(-) diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index 894f46990..0685eaf85 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp @@ -4,6 +4,7 @@ #include "audio_core/audio_core.h" #include "audio_core/hle/dsp.h" +#include "audio_core/hle/pipe.h" #include "core/core_timing.h" #include "core/hle/kernel/vm_manager.h" @@ -17,10 +18,8 @@ static constexpr u64 audio_frame_ticks = 1310252ull; ///< Units: ARM11 cycles static void AudioTickCallback(u64 /*userdata*/, int cycles_late) { if (DSP::HLE::Tick()) { - // HACK: We're not signaling the interrups when they should be, but just firing them all off together. - // It should be only (interrupt_id = 2, channel_id = 2) that's signalled here. - // TODO(merry): Understand when the other interrupts are fired. - DSP_DSP::SignalAllInterrupts(); + // TODO(merry): Signal all the other interrupts as appropriate. + DSP_DSP::SignalPipeInterrupt(DSP::HLE::DspPipe::Audio); } // Reschedule recurrent event diff --git a/src/audio_core/hle/pipe.cpp b/src/audio_core/hle/pipe.cpp index 7ec97dfda..03280780f 100644 --- a/src/audio_core/hle/pipe.cpp +++ b/src/audio_core/hle/pipe.cpp @@ -12,6 +12,8 @@ #include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/service/dsp_dsp.h" + namespace DSP { namespace HLE { @@ -97,6 +99,8 @@ static void AudioPipeWriteStructAddresses() { for (u16 addr : struct_addresses) { WriteU16(DspPipe::Audio, addr); } + // Signal that we have data on this pipe. + DSP_DSP::SignalPipeInterrupt(DspPipe::Audio); } void PipeWrite(DspPipe pipe_number, const std::vector& buffer) { diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 751cb3d61..e9ef50f86 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include "audio_core/hle/pipe.h" @@ -12,6 +13,8 @@ #include "core/hle/kernel/event.h" #include "core/hle/service/dsp_dsp.h" +using DspPipe = DSP::HLE::DspPipe; + //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace DSP_DSP @@ -19,29 +22,71 @@ namespace DSP_DSP { static Kernel::SharedPtr semaphore_event; -struct PairHash { - template - std::size_t operator()(const std::pair &x) const { - // TODO(yuriks): Replace with better hash combining function. - return std::hash()(x.first) ^ std::hash()(x.second); +/// There are three types of interrupts +enum class InterruptType { + Zero, One, Pipe +}; +constexpr size_t NUM_INTERRUPT_TYPE = 3; + +class InterruptEvents final { +public: + void Signal(InterruptType type, DspPipe pipe) { + Kernel::SharedPtr& event = Get(type, pipe); + if (event) { + event->Signal(); + } } + + Kernel::SharedPtr& Get(InterruptType type, DspPipe dsp_pipe) { + switch (type) { + case InterruptType::Zero: + return zero; + case InterruptType::One: + return one; + case InterruptType::Pipe: { + const size_t pipe_index = static_cast(dsp_pipe); + ASSERT(pipe_index < DSP::HLE::NUM_DSP_PIPE); + return pipe[pipe_index]; + } + } + + UNREACHABLE_MSG("Invalid interrupt type = %zu", static_cast(type)); + } + + bool HasTooManyEventsRegistered() const { + // Actual service implementation only has 6 'slots' for interrupts. + constexpr size_t max_number_of_interrupt_events = 6; + + size_t number = std::count_if(pipe.begin(), pipe.end(), [](const auto& evt) { + return evt != nullptr; + }); + + if (zero != nullptr) + number++; + if (one != nullptr) + number++; + + return number >= max_number_of_interrupt_events; + } + +private: + /// Currently unknown purpose + Kernel::SharedPtr zero = nullptr; + /// Currently unknown purpose + Kernel::SharedPtr one = nullptr; + /// Each DSP pipe has an associated interrupt + std::array, DSP::HLE::NUM_DSP_PIPE> pipe = {{}}; }; -/// Map of (audio interrupt number, channel number) to Kernel::Events. See: RegisterInterruptEvents -static std::unordered_map, Kernel::SharedPtr, PairHash> interrupt_events; +static InterruptEvents interrupt_events; // DSP Interrupts: -// Interrupt #2 occurs every frame tick. Userland programs normally have a thread that's waiting -// for an interrupt event. Immediately after this interrupt event, userland normally updates the -// state in the next region and increments the relevant frame counter by two. -void SignalAllInterrupts() { - // HACK: The other interrupts have currently unknown purpose, we trigger them each tick in any case. - for (auto& interrupt_event : interrupt_events) - interrupt_event.second->Signal(); -} - -void SignalInterrupt(u32 interrupt, u32 channel) { - interrupt_events[std::make_pair(interrupt, channel)]->Signal(); +// The audio-pipe interrupt occurs every frame tick. Userland programs normally have a thread +// that's waiting for an interrupt event. Immediately after this interrupt event, userland +// normally updates the state in the next region and increments the relevant frame counter by +// two. +void SignalPipeInterrupt(DspPipe pipe) { + interrupt_events.Signal(InterruptType::Pipe, pipe); } /** @@ -147,8 +192,8 @@ static void FlushDataCache(Service::Interface* self) { /** * DSP_DSP::RegisterInterruptEvents service function * Inputs: - * 1 : Interrupt Number - * 2 : Channel Number + * 1 : Interrupt Type + * 2 : Pipe Number * 4 : Interrupt event handle * Outputs: * 1 : Result of function, 0 on success, otherwise error code @@ -156,23 +201,40 @@ static void FlushDataCache(Service::Interface* self) { static void RegisterInterruptEvents(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 interrupt = cmd_buff[1]; - u32 channel = cmd_buff[2]; + u32 type_index = cmd_buff[1]; + u32 pipe_index = cmd_buff[2]; u32 event_handle = cmd_buff[4]; + ASSERT_MSG(type_index < NUM_INTERRUPT_TYPE && pipe_index < DSP::HLE::NUM_DSP_PIPE, + "Invalid type or pipe: type = %u, pipe = %u", type_index, pipe_index); + + InterruptType type = static_cast(cmd_buff[1]); + DspPipe pipe = static_cast(cmd_buff[2]); + + cmd_buff[0] = IPC::MakeHeader(0x15, 1, 0); + if (event_handle) { auto evt = Kernel::g_handle_table.Get(cmd_buff[4]); - if (evt) { - interrupt_events[std::make_pair(interrupt, channel)] = evt; - cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_INFO(Service_DSP, "Registered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); - } else { - LOG_CRITICAL(Service_DSP, "Invalid event handle! interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); - ASSERT(false); // This should really be handled at a IPC translation layer. + + if (!evt) { + LOG_INFO(Service_DSP, "Invalid event handle! type=%u, pipe=%u, event_handle=0x%08X", type_index, pipe_index, event_handle); + ASSERT(false); // TODO: This should really be handled at an IPC translation layer. } + + if (interrupt_events.HasTooManyEventsRegistered()) { + LOG_INFO(Service_DSP, "Ran out of space to register interrupts (Attempted to register type=%u, pipe=%u, event_handle=0x%08X)", + type_index, pipe_index, event_handle); + cmd_buff[1] = ResultCode(ErrorDescription::InvalidResultValue, ErrorModule::DSP, ErrorSummary::OutOfResource, ErrorLevel::Status).raw; + return; + } + + interrupt_events.Get(type, pipe) = evt; + LOG_INFO(Service_DSP, "Registered type=%u, pipe=%u, event_handle=0x%08X", type_index, pipe_index, event_handle); + cmd_buff[1] = RESULT_SUCCESS.raw; } else { - interrupt_events.erase(std::make_pair(interrupt, channel)); - LOG_INFO(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); + interrupt_events.Get(type, pipe) = nullptr; + LOG_INFO(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", type_index, pipe_index, event_handle); + cmd_buff[1] = RESULT_SUCCESS.raw; } } @@ -194,7 +256,7 @@ static void SetSemaphore(Service::Interface* self) { /** * DSP_DSP::WriteProcessPipe service function * Inputs: - * 1 : Channel + * 1 : Pipe Number * 2 : Size * 3 : (size << 14) | 0x402 * 4 : Buffer @@ -457,13 +519,14 @@ const Interface::FunctionInfo FunctionTable[] = { Interface::Interface() { semaphore_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "DSP_DSP::semaphore_event"); + interrupt_events = {}; Register(FunctionTable); } Interface::~Interface() { semaphore_event = nullptr; - interrupt_events.clear(); + interrupt_events = {}; } } // namespace diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h index 32b89e9bb..22f6687cc 100644 --- a/src/core/hle/service/dsp_dsp.h +++ b/src/core/hle/service/dsp_dsp.h @@ -8,6 +8,12 @@ #include "core/hle/service/service.h" +namespace DSP { +namespace HLE { +enum class DspPipe; +} +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace DSP_DSP @@ -23,15 +29,10 @@ public: } }; -/// Signal all audio related interrupts. -void SignalAllInterrupts(); - /** - * Signal a specific audio related interrupt based on interrupt id and channel id. - * @param interrupt_id The interrupt id - * @param channel_id The channel id - * The significance of various values of interrupt_id and channel_id is not yet known. + * Signal a specific DSP related interrupt of type == InterruptType::Pipe, pipe == pipe. + * @param pipe The DSP pipe for which to signal an interrupt for. */ -void SignalInterrupt(u32 interrupt_id, u32 channel_id); +void SignalPipeInterrupt(DSP::HLE::DspPipe pipe); -} // namespace +} // namespace DSP_DSP From 2929b67c5f76a6bb7bc99e612fc582c5de28353f Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sun, 24 Apr 2016 11:39:59 +0100 Subject: [PATCH 33/39] DSP_DSP: Add return IPC headers --- src/core/hle/result.h | 1 + src/core/hle/service/dsp_dsp.cpp | 30 ++++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 2d22652d9..53931a106 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -18,6 +18,7 @@ /// Detailed description of the error. This listing is likely incomplete. enum class ErrorDescription : u32 { Success = 0, + OS_InvalidBufferDescriptor = 48, WrongAddress = 53, FS_NotFound = 120, FS_AlreadyExists = 190, diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index e9ef50f86..58ded7eb5 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -102,7 +102,10 @@ static void ConvertProcessAddressFromDspDram(Service::Interface* self) { u32 addr = cmd_buff[1]; + cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + // TODO(merry): There is a per-region offset missing in this calculation (that seems to be always zero). cmd_buff[2] = (addr << 1) + (Memory::DSP_RAM_VADDR + 0x40000); LOG_DEBUG(Service_DSP, "addr=0x%08X", addr); @@ -157,7 +160,9 @@ static void LoadComponent(Service::Interface* self) { static void GetSemaphoreEventHandle(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x16, 1, 2); cmd_buff[1] = RESULT_SUCCESS.raw; // No error + // cmd_buff[2] not set cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).MoveFrom(); // Event handle LOG_WARNING(Service_DSP, "(STUBBED) called"); @@ -182,8 +187,7 @@ static void FlushDataCache(Service::Interface* self) { u32 size = cmd_buff[2]; u32 process = cmd_buff[4]; - // TODO(purpasmart96): Verify return header on HW - + cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_TRACE(Service_DSP, "called address=0x%08X, size=0x%X, process=0x%08X", address, size, process); @@ -248,6 +252,7 @@ static void RegisterInterruptEvents(Service::Interface* self) { static void SetSemaphore(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_DSP, "(STUBBED) called"); @@ -271,17 +276,23 @@ static void WriteProcessPipe(Service::Interface* self) { u32 size = cmd_buff[2]; u32 buffer = cmd_buff[4]; - ASSERT_MSG(IPC::StaticBufferDesc(size, 1) == cmd_buff[3], "IPC static buffer descriptor failed validation (0x%X). pipe=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], pipe, size, buffer); + if (IPC::StaticBufferDesc(size, 1) != cmd_buff[3]) { + LOG_ERROR(Service_DSP, "IPC static buffer descriptor failed validation (0x%X). pipe=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], pipe, size, buffer); + cmd_buff[0] = IPC::MakeHeader(0, 1, 0); + cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw; + return; + } + ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer); std::vector message(size); - for (size_t i = 0; i < size; i++) { message[i] = Memory::Read8(buffer + i); } DSP::HLE::PipeWrite(pipe, message); + cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_DEBUG(Service_DSP, "pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer); @@ -311,6 +322,7 @@ static void ReadPipeIfPossible(Service::Interface* self) { ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr); + cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2); cmd_buff[1] = RESULT_SUCCESS.raw; // No error if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { std::vector response = DSP::HLE::PipeRead(pipe, size); @@ -321,6 +333,8 @@ static void ReadPipeIfPossible(Service::Interface* self) { } else { cmd_buff[2] = 0; // Return no data } + cmd_buff[3] = IPC::StaticBufferDesc(size, 0); + cmd_buff[4] = addr; LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, size, addr, cmd_buff[2]); } @@ -351,8 +365,11 @@ static void ReadPipe(Service::Interface* self) { Memory::WriteBlock(addr, response.data(), response.size()); + cmd_buff[0] = IPC::MakeHeader(0xE, 2, 2); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = static_cast(response.size()); + cmd_buff[3] = IPC::StaticBufferDesc(size, 0); + cmd_buff[4] = addr; } else { // No more data is in pipe. Hardware hangs in this case; this should never happen. UNREACHABLE(); @@ -376,6 +393,7 @@ static void GetPipeReadableSize(Service::Interface* self) { DSP::HLE::DspPipe pipe = static_cast(cmd_buff[1]); u32 unknown = cmd_buff[2]; + cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = DSP::HLE::GetPipeReadableSize(pipe); @@ -394,6 +412,7 @@ static void SetSemaphoreMask(Service::Interface* self) { u32 mask = cmd_buff[1]; + cmd_buff[0] = IPC::MakeHeader(0x17, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_DSP, "(STUBBED) called mask=0x%08X", mask); @@ -411,6 +430,7 @@ static void SetSemaphoreMask(Service::Interface* self) { static void GetHeadphoneStatus(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x1F, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = 0; // Not using headphones? @@ -437,6 +457,7 @@ static void RecvData(Service::Interface* self) { // Application reads this after requesting DSP shutdown, to verify the DSP has indeed shutdown or slept. + cmd_buff[0] = IPC::MakeHeader(0x1, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; switch (DSP::HLE::GetDspState()) { case DSP::HLE::DspState::On: @@ -472,6 +493,7 @@ static void RecvDataIsReady(Service::Interface* self) { ASSERT_MSG(register_number == 0, "Unknown register_number %u", register_number); + cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 1; // Ready to read From a47f149e0769127b137ec98dcd732aff6464c333 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sun, 24 Apr 2016 13:36:44 +0100 Subject: [PATCH 34/39] AudioCore: Hack to prevent regressions: Trigger Binary pipe interrupt every audio frame --- src/audio_core/audio_core.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index 0685eaf85..b512b0f9b 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp @@ -20,6 +20,8 @@ static void AudioTickCallback(u64 /*userdata*/, int cycles_late) { if (DSP::HLE::Tick()) { // TODO(merry): Signal all the other interrupts as appropriate. DSP_DSP::SignalPipeInterrupt(DSP::HLE::DspPipe::Audio); + // HACK(merry): Added to prevent regressions. Will remove soon. + DSP_DSP::SignalPipeInterrupt(DSP::HLE::DspPipe::Binary); } // Reschedule recurrent event From c379b22117f61bcbeda8f8edb3035d080a4dc223 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Tue, 26 Apr 2016 19:28:53 +0100 Subject: [PATCH 35/39] DSP_DSP: Fix log format strings and arguments --- src/core/hle/service/dsp_dsp.cpp | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 58ded7eb5..995bee3f9 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -272,18 +272,20 @@ static void SetSemaphore(Service::Interface* self) { static void WriteProcessPipe(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - DSP::HLE::DspPipe pipe = static_cast(cmd_buff[1]); + u32 pipe_index = cmd_buff[1]; u32 size = cmd_buff[2]; u32 buffer = cmd_buff[4]; + DSP::HLE::DspPipe pipe = static_cast(pipe_index); + if (IPC::StaticBufferDesc(size, 1) != cmd_buff[3]) { - LOG_ERROR(Service_DSP, "IPC static buffer descriptor failed validation (0x%X). pipe=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], pipe, size, buffer); + LOG_ERROR(Service_DSP, "IPC static buffer descriptor failed validation (0x%X). pipe=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], pipe_index, size, buffer); cmd_buff[0] = IPC::MakeHeader(0, 1, 0); cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw; return; } - ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer); + ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer); std::vector message(size); for (size_t i = 0; i < size; i++) { @@ -295,7 +297,7 @@ static void WriteProcessPipe(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_DEBUG(Service_DSP, "pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer); + LOG_DEBUG(Service_DSP, "pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer); } /** @@ -315,12 +317,14 @@ static void WriteProcessPipe(Service::Interface* self) { static void ReadPipeIfPossible(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - DSP::HLE::DspPipe pipe = static_cast(cmd_buff[1]); + u32 pipe_index = cmd_buff[1]; u32 unknown = cmd_buff[2]; u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size VAddr addr = cmd_buff[0x41]; - ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr); + DSP::HLE::DspPipe pipe = static_cast(pipe_index); + + ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr); cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2); cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -336,7 +340,7 @@ static void ReadPipeIfPossible(Service::Interface* self) { cmd_buff[3] = IPC::StaticBufferDesc(size, 0); cmd_buff[4] = addr; - LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, size, addr, cmd_buff[2]); + LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, size, addr, cmd_buff[2]); } /** @@ -353,12 +357,14 @@ static void ReadPipeIfPossible(Service::Interface* self) { static void ReadPipe(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - DSP::HLE::DspPipe pipe = static_cast(cmd_buff[1]); + u32 pipe_index = cmd_buff[1]; u32 unknown = cmd_buff[2]; u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size VAddr addr = cmd_buff[0x41]; - ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr); + DSP::HLE::DspPipe pipe = static_cast(pipe_index); + + ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr); if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { std::vector response = DSP::HLE::PipeRead(pipe, size); @@ -375,7 +381,7 @@ static void ReadPipe(Service::Interface* self) { UNREACHABLE(); } - LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, size, addr, cmd_buff[2]); + LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, size, addr, cmd_buff[2]); } /** @@ -390,14 +396,16 @@ static void ReadPipe(Service::Interface* self) { static void GetPipeReadableSize(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - DSP::HLE::DspPipe pipe = static_cast(cmd_buff[1]); + u32 pipe_index = cmd_buff[1]; u32 unknown = cmd_buff[2]; + DSP::HLE::DspPipe pipe = static_cast(pipe_index); + cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = DSP::HLE::GetPipeReadableSize(pipe); - LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, cmd_buff[2]); + LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, cmd_buff[2]); } /** From dda9ffe7901b354d45ab48844801a16b806da9a2 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sun, 24 Apr 2016 13:20:13 +0100 Subject: [PATCH 36/39] AudioCore: Move samples_per_frame and num_sources into hle/common.h --- src/audio_core/audio_core.h | 2 -- src/audio_core/hle/common.h | 9 +++++---- src/audio_core/hle/dsp.h | 12 ++++++------ 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h index 64c330914..b349895ea 100644 --- a/src/audio_core/audio_core.h +++ b/src/audio_core/audio_core.h @@ -10,8 +10,6 @@ class VMManager; namespace AudioCore { -constexpr int num_sources = 24; -constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate constexpr int native_sample_rate = 32728; ///< 32kHz /// Initialise Audio Core diff --git a/src/audio_core/hle/common.h b/src/audio_core/hle/common.h index 37d441eb2..7910f42ae 100644 --- a/src/audio_core/hle/common.h +++ b/src/audio_core/hle/common.h @@ -7,18 +7,19 @@ #include #include -#include "audio_core/audio_core.h" - #include "common/common_types.h" namespace DSP { namespace HLE { +constexpr int num_sources = 24; +constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate + /// The final output to the speakers is stereo. Preprocessing output in Source is also stereo. -using StereoFrame16 = std::array, AudioCore::samples_per_frame>; +using StereoFrame16 = std::array, samples_per_frame>; /// The DSP is quadraphonic internally. -using QuadFrame32 = std::array, AudioCore::samples_per_frame>; +using QuadFrame32 = std::array, samples_per_frame>; /** * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place. diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h index c15ef0b7a..c76350bdd 100644 --- a/src/audio_core/hle/dsp.h +++ b/src/audio_core/hle/dsp.h @@ -7,7 +7,7 @@ #include #include -#include "audio_core/audio_core.h" +#include "audio_core/hle/common.h" #include "common/bit_field.h" #include "common/common_funcs.h" @@ -305,7 +305,7 @@ struct SourceConfiguration { u16_le buffer_id; }; - Configuration config[AudioCore::num_sources]; + Configuration config[num_sources]; }; ASSERT_DSP_STRUCT(SourceConfiguration::Configuration, 192); ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20); @@ -320,7 +320,7 @@ struct SourceStatus { INSERT_PADDING_DSPWORDS(1); }; - Status status[AudioCore::num_sources]; + Status status[num_sources]; }; ASSERT_DSP_STRUCT(SourceStatus::Status, 12); @@ -413,7 +413,7 @@ ASSERT_DSP_STRUCT(DspConfiguration::ReverbEffect, 52); struct AdpcmCoefficients { /// Coefficients are signed fixed point with 11 fractional bits. /// Each source has 16 coefficients associated with it. - s16_le coeff[AudioCore::num_sources][16]; + s16_le coeff[num_sources][16]; }; ASSERT_DSP_STRUCT(AdpcmCoefficients, 768); @@ -427,7 +427,7 @@ ASSERT_DSP_STRUCT(DspStatus, 32); /// Final mixed output in PCM16 stereo format, what you hear out of the speakers. /// When the application writes to this region it has no effect. struct FinalMixSamples { - s16_le pcm16[2 * AudioCore::samples_per_frame]; + s16_le pcm16[2 * samples_per_frame]; }; ASSERT_DSP_STRUCT(FinalMixSamples, 640); @@ -437,7 +437,7 @@ ASSERT_DSP_STRUCT(FinalMixSamples, 640); /// Values that exceed s16 range will be clipped by the DSP after further processing. struct IntermediateMixSamples { struct Samples { - s32_le pcm32[4][AudioCore::samples_per_frame]; ///< Little-endian as opposed to DSP middle-endian. + s32_le pcm32[4][samples_per_frame]; ///< Little-endian as opposed to DSP middle-endian. }; Samples mix1; From 27ce3b3f51998b7bd8f25d7cb1c9cc1321ad6f31 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sun, 24 Apr 2016 14:18:30 +0100 Subject: [PATCH 37/39] Externals: Add soundtouch --- .gitmodules | 3 +++ CMakeLists.txt | 3 +++ externals/soundtouch | 1 + src/audio_core/CMakeLists.txt | 5 ++++- 4 files changed, 11 insertions(+), 1 deletion(-) create mode 160000 externals/soundtouch diff --git a/.gitmodules b/.gitmodules index 598e4c64d..059512902 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "nihstro"] path = externals/nihstro url = https://github.com/neobrain/nihstro.git +[submodule "soundtouch"] + path = externals/soundtouch + url = https://github.com/citra-emu/soundtouch.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 019321ad8..d628ecc50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,6 +249,9 @@ if(ENABLE_QT) include_directories(externals/qhexedit) add_subdirectory(externals/qhexedit) endif() + +add_subdirectory(externals/soundtouch) + add_subdirectory(src) # Install freedesktop.org metadata files, following those specifications: diff --git a/externals/soundtouch b/externals/soundtouch new file mode 160000 index 000000000..5274ec4de --- /dev/null +++ b/externals/soundtouch @@ -0,0 +1 @@ +Subproject commit 5274ec4dec498bd88ccbcd28862a0f78a3b95eff diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 869da5e83..c08ce69e0 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -16,6 +16,9 @@ set(HEADERS sink.h ) +include_directories(../../externals/soundtouch/include) + create_directory_groups(${SRCS} ${HEADERS}) -add_library(audio_core STATIC ${SRCS} ${HEADERS}) \ No newline at end of file +add_library(audio_core STATIC ${SRCS} ${HEADERS}) +target_link_libraries(audio_core SoundTouch) From 90501f20e6b91fcd2beaccf0b1a0174b189da09c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 29 Apr 2016 02:17:31 +0200 Subject: [PATCH 38/39] Make Citra build with MICROPROFILE_ENABLED set to 0 (#1709) * Make Citra build with MICROPROFILE_ENABLED set to 0 * Buildfix with microprofile kept on * moc did not like a dialog to conditionally exist. * Cleanup * Fix end of line --- src/citra_qt/bootmanager.cpp | 2 ++ src/citra_qt/debugger/profiler.cpp | 13 +++++++++++++ src/citra_qt/debugger/profiler.h | 3 +++ src/citra_qt/main.cpp | 9 ++++++++- src/common/microprofile.h | 4 ++++ 5 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 8e60b9cad..01b81c11c 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -71,7 +71,9 @@ void EmuThread::run() { // Shutdown the core emulation System::Shutdown(); +#if MICROPROFILE_ENABLED MicroProfileOnThreadExit(); +#endif render_window->moveContext(); } diff --git a/src/citra_qt/debugger/profiler.cpp b/src/citra_qt/debugger/profiler.cpp index 4f6ba0e1f..e90973b8e 100644 --- a/src/citra_qt/debugger/profiler.cpp +++ b/src/citra_qt/debugger/profiler.cpp @@ -14,8 +14,10 @@ // Include the implementation of the UI in this file. This isn't in microprofile.cpp because the // non-Qt frontends don't need it (and don't implement the UI drawing hooks either). +#if MICROPROFILE_ENABLED #define MICROPROFILEUI_IMPL 1 #include "common/microprofileui.h" +#endif using namespace Common::Profiling; @@ -148,6 +150,8 @@ void ProfilerWidget::setProfilingInfoUpdateEnabled(bool enable) } } +#if MICROPROFILE_ENABLED + class MicroProfileWidget : public QWidget { public: MicroProfileWidget(QWidget* parent = nullptr); @@ -171,6 +175,8 @@ private: QTimer update_timer; }; +#endif + MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Dialog) { @@ -180,6 +186,8 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent) // Remove the "?" button from the titlebar and enable the maximize button setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint | Qt::WindowMaximizeButtonHint); +#if MICROPROFILE_ENABLED + MicroProfileWidget* widget = new MicroProfileWidget(this); QLayout* layout = new QVBoxLayout(this); @@ -191,6 +199,7 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent) setFocusProxy(widget); widget->setFocusPolicy(Qt::StrongFocus); widget->setFocus(); +#endif } QAction* MicroProfileDialog::toggleViewAction() { @@ -218,6 +227,9 @@ void MicroProfileDialog::hideEvent(QHideEvent* ev) { QWidget::hideEvent(ev); } + +#if MICROPROFILE_ENABLED + /// There's no way to pass a user pointer to MicroProfile, so this variable is used to make the /// QPainter available inside the drawing callbacks. static QPainter* mp_painter = nullptr; @@ -337,3 +349,4 @@ void MicroProfileDrawLine2D(u32 vertices_length, float* vertices, u32 hex_color) mp_painter->drawPolyline(point_buf.data(), vertices_length); point_buf.clear(); } +#endif diff --git a/src/citra_qt/debugger/profiler.h b/src/citra_qt/debugger/profiler.h index 036054740..3b38ed8ec 100644 --- a/src/citra_qt/debugger/profiler.h +++ b/src/citra_qt/debugger/profiler.h @@ -7,8 +7,10 @@ #include #include #include + #include "ui_profiler.h" +#include "common/microprofile.h" #include "common/profiler_reporting.h" class ProfilerModel : public QAbstractItemModel @@ -49,6 +51,7 @@ private: QTimer update_timer; }; + class MicroProfileDialog : public QWidget { Q_OBJECT diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 2ca1e51f6..f1ab29755 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -69,8 +69,10 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) addDockWidget(Qt::BottomDockWidgetArea, profilerWidget); profilerWidget->hide(); +#if MICROPROFILE_ENABLED microProfileDialog = new MicroProfileDialog(this); microProfileDialog->hide(); +#endif disasmWidget = new DisassemblerWidget(this, emu_thread.get()); addDockWidget(Qt::BottomDockWidgetArea, disasmWidget); @@ -110,7 +112,9 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) QMenu* debug_menu = ui.menu_View->addMenu(tr("Debugging")); debug_menu->addAction(profilerWidget->toggleViewAction()); +#if MICROPROFILE_ENABLED debug_menu->addAction(microProfileDialog->toggleViewAction()); +#endif debug_menu->addAction(disasmWidget->toggleViewAction()); debug_menu->addAction(registersWidget->toggleViewAction()); debug_menu->addAction(callstackWidget->toggleViewAction()); @@ -136,8 +140,10 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) restoreGeometry(UISettings::values.geometry); restoreState(UISettings::values.state); render_window->restoreGeometry(UISettings::values.renderwindow_geometry); +#if MICROPROFILE_ENABLED microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry); microProfileDialog->setVisible(UISettings::values.microprofile_visible); +#endif game_list->LoadInterfaceLayout(); @@ -511,9 +517,10 @@ void GMainWindow::closeEvent(QCloseEvent* event) { UISettings::values.geometry = saveGeometry(); UISettings::values.state = saveState(); UISettings::values.renderwindow_geometry = render_window->saveGeometry(); +#if MICROPROFILE_ENABLED UISettings::values.microprofile_geometry = microProfileDialog->saveGeometry(); UISettings::values.microprofile_visible = microProfileDialog->isVisible(); - +#endif UISettings::values.single_window_mode = ui.action_Single_Window_Mode->isChecked(); UISettings::values.display_titlebar = ui.actionDisplay_widget_title_bars->isChecked(); UISettings::values.first_start = false; diff --git a/src/common/microprofile.h b/src/common/microprofile.h index d3b6cb97c..ef312c6e1 100644 --- a/src/common/microprofile.h +++ b/src/common/microprofile.h @@ -4,6 +4,10 @@ #pragma once +// Uncomment this to disable microprofile. This will get you cleaner profiles when using +// external sampling profilers like "Very Sleepy", and will improve performance somewhat. +// #define MICROPROFILE_ENABLED 0 + // Customized Citra settings. // This file wraps the MicroProfile header so that these are consistent everywhere. #define MICROPROFILE_WEBSERVER 0 From e3a8292495cf0fb4297be27c696649dc44e011f0 Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Fri, 29 Apr 2016 00:07:10 -0700 Subject: [PATCH 39/39] Common: Remove section measurement from profiler (#1731) This has been entirely superseded by MicroProfile. The rest of the code can go when a simpler frametime/FPS meter is added to the GUI. --- src/citra_qt/debugger/profiler.cpp | 26 +-- src/common/CMakeLists.txt | 1 - src/common/microprofileui.h | 3 + src/common/profiler.cpp | 82 ---------- src/common/profiler.h | 152 ------------------ src/common/profiler_reporting.h | 27 +--- .../arm/dyncom/arm_dyncom_interpreter.cpp | 7 - src/core/hle/service/gsp_gpu.cpp | 1 - src/core/hle/svc.cpp | 4 - src/video_core/command_processor.cpp | 4 - src/video_core/rasterizer.cpp | 3 - .../renderer_opengl/gl_rasterizer.cpp | 1 - src/video_core/shader/shader.cpp | 3 - 13 files changed, 8 insertions(+), 306 deletions(-) delete mode 100644 src/common/profiler.h diff --git a/src/citra_qt/debugger/profiler.cpp b/src/citra_qt/debugger/profiler.cpp index e90973b8e..7bb010f77 100644 --- a/src/citra_qt/debugger/profiler.cpp +++ b/src/citra_qt/debugger/profiler.cpp @@ -9,6 +9,7 @@ #include "citra_qt/debugger/profiler.h" #include "citra_qt/util/util.h" +#include "common/common_types.h" #include "common/microprofile.h" #include "common/profiler_reporting.h" @@ -36,21 +37,9 @@ static QVariant GetDataForColumn(int col, const AggregatedDuration& duration) } } -static const TimingCategoryInfo* GetCategoryInfo(int id) -{ - const auto& categories = GetProfilingManager().GetTimingCategoriesInfo(); - if ((size_t)id >= categories.size()) { - return nullptr; - } else { - return &categories[id]; - } -} - ProfilerModel::ProfilerModel(QObject* parent) : QAbstractItemModel(parent) { updateProfilingInfo(); - const auto& categories = GetProfilingManager().GetTimingCategoriesInfo(); - results.time_per_category.resize(categories.size()); } QVariant ProfilerModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -87,7 +76,7 @@ int ProfilerModel::rowCount(const QModelIndex& parent) const if (parent.isValid()) { return 0; } else { - return static_cast(results.time_per_category.size() + 2); + return 2; } } @@ -106,17 +95,6 @@ QVariant ProfilerModel::data(const QModelIndex& index, int role) const } else { return GetDataForColumn(index.column(), results.interframe_time); } - } else { - if (index.column() == 0) { - const TimingCategoryInfo* info = GetCategoryInfo(index.row() - 2); - return info != nullptr ? QString(info->name) : QVariant(); - } else { - if (index.row() - 2 < (int)results.time_per_category.size()) { - return GetDataForColumn(index.column(), results.time_per_category[index.row() - 2]); - } else { - return QVariant(); - } - } } } diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index c839ce173..aa6eee2a3 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -47,7 +47,6 @@ set(HEADERS microprofile.h microprofileui.h platform.h - profiler.h profiler_reporting.h scm_rev.h scope_exit.h diff --git a/src/common/microprofileui.h b/src/common/microprofileui.h index 97c369bd9..41abe6b75 100644 --- a/src/common/microprofileui.h +++ b/src/common/microprofileui.h @@ -13,4 +13,7 @@ #define MICROPROFILE_HELP_ALT "Right-Click" #define MICROPROFILE_HELP_MOD "Ctrl" +// This isn't included by microprofileui.h :( +#include // For std::abs + #include diff --git a/src/common/profiler.cpp b/src/common/profiler.cpp index 7792edd2f..49eb3f40c 100644 --- a/src/common/profiler.cpp +++ b/src/common/profiler.cpp @@ -7,71 +7,16 @@ #include #include "common/assert.h" -#include "common/profiler.h" #include "common/profiler_reporting.h" #include "common/synchronized_wrapper.h" -#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013. - #define WIN32_LEAN_AND_MEAN - #include // For QueryPerformanceCounter/Frequency -#endif - namespace Common { namespace Profiling { -#if ENABLE_PROFILING -thread_local Timer* Timer::current_timer = nullptr; -#endif - -#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013 -QPCClock::time_point QPCClock::now() { - static LARGE_INTEGER freq; - // Use this dummy local static to ensure this gets initialized once. - static BOOL dummy = QueryPerformanceFrequency(&freq); - - LARGE_INTEGER ticks; - QueryPerformanceCounter(&ticks); - - // This is prone to overflow when multiplying, which is why I'm using micro instead of nano. The - // correct way to approach this would be to just return ticks as a time_point and then subtract - // and do this conversion when creating a duration from two time_points, however, as far as I - // could tell the C++ requirements for these types are incompatible with this approach. - return time_point(duration(ticks.QuadPart * std::micro::den / freq.QuadPart)); -} -#endif - -TimingCategory::TimingCategory(const char* name, TimingCategory* parent) - : accumulated_duration(0) { - - ProfilingManager& manager = GetProfilingManager(); - category_id = manager.RegisterTimingCategory(this, name); - if (parent != nullptr) - manager.SetTimingCategoryParent(category_id, parent->category_id); -} - ProfilingManager::ProfilingManager() : last_frame_end(Clock::now()), this_frame_start(Clock::now()) { } -unsigned int ProfilingManager::RegisterTimingCategory(TimingCategory* category, const char* name) { - TimingCategoryInfo info; - info.category = category; - info.name = name; - info.parent = TimingCategoryInfo::NO_PARENT; - - unsigned int id = (unsigned int)timing_categories.size(); - timing_categories.push_back(std::move(info)); - - return id; -} - -void ProfilingManager::SetTimingCategoryParent(unsigned int category, unsigned int parent) { - ASSERT(category < timing_categories.size()); - ASSERT(parent < timing_categories.size()); - - timing_categories[category].parent = parent; -} - void ProfilingManager::BeginFrame() { this_frame_start = Clock::now(); } @@ -82,11 +27,6 @@ void ProfilingManager::FinishFrame() { results.interframe_time = now - last_frame_end; results.frame_time = now - this_frame_start; - results.time_per_category.resize(timing_categories.size()); - for (size_t i = 0; i < timing_categories.size(); ++i) { - results.time_per_category[i] = timing_categories[i].category->GetAccumulatedTime(); - } - last_frame_end = now; } @@ -100,26 +40,9 @@ void TimingResultsAggregator::Clear() { window_size = cursor = 0; } -void TimingResultsAggregator::SetNumberOfCategories(size_t n) { - size_t old_size = times_per_category.size(); - if (n == old_size) - return; - - times_per_category.resize(n); - - for (size_t i = old_size; i < n; ++i) { - times_per_category[i].resize(max_window_size, Duration::zero()); - } -} - void TimingResultsAggregator::AddFrame(const ProfilingFrameResult& frame_result) { - SetNumberOfCategories(frame_result.time_per_category.size()); - interframe_times[cursor] = frame_result.interframe_time; frame_times[cursor] = frame_result.frame_time; - for (size_t i = 0; i < frame_result.time_per_category.size(); ++i) { - times_per_category[i][cursor] = frame_result.time_per_category[i]; - } ++cursor; if (cursor == max_window_size) @@ -162,11 +85,6 @@ AggregatedFrameResult TimingResultsAggregator::GetAggregatedResults() const { result.fps = 0.0f; } - result.time_per_category.resize(times_per_category.size()); - for (size_t i = 0; i < times_per_category.size(); ++i) { - result.time_per_category[i] = AggregateField(times_per_category[i], window_size); - } - return result; } diff --git a/src/common/profiler.h b/src/common/profiler.h deleted file mode 100644 index 3e967b4bc..000000000 --- a/src/common/profiler.h +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include - -#include "common/assert.h" -#include "common/thread.h" - -namespace Common { -namespace Profiling { - -// If this is defined to 0, it turns all Timers into no-ops. -#ifndef ENABLE_PROFILING -#define ENABLE_PROFILING 1 -#endif - -#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013 -// MSVC up to 2013 doesn't use QueryPerformanceCounter for high_resolution_clock, so it has bad -// precision. We manually implement a clock based on QPC to get good results. - -struct QPCClock { - using duration = std::chrono::microseconds; - using time_point = std::chrono::time_point; - using rep = duration::rep; - using period = duration::period; - static const bool is_steady = false; - - static time_point now(); -}; - -using Clock = QPCClock; -#else -using Clock = std::chrono::high_resolution_clock; -#endif - -using Duration = Clock::duration; - -/** - * Represents a timing category that measured time can be accounted towards. Should be declared as a - * global variable and passed to Timers. - */ -class TimingCategory final { -public: - TimingCategory(const char* name, TimingCategory* parent = nullptr); - - unsigned int GetCategoryId() const { - return category_id; - } - - /// Adds some time to this category. Can safely be called from multiple threads at the same time. - void AddTime(Duration amount) { - std::atomic_fetch_add_explicit( - &accumulated_duration, amount.count(), - std::memory_order_relaxed); - } - - /** - * Atomically retrieves the accumulated measured time for this category and resets the counter - * to zero. Can be safely called concurrently with AddTime. - */ - Duration GetAccumulatedTime() { - return Duration(std::atomic_exchange_explicit( - &accumulated_duration, (Duration::rep)0, - std::memory_order_relaxed)); - } - -private: - unsigned int category_id; - std::atomic accumulated_duration; -}; - -/** - * Measures time elapsed between a call to Start and a call to Stop and attributes it to the given - * TimingCategory. Start/Stop can be called multiple times on the same timer, but each call must be - * appropriately paired. - * - * When a Timer is started, it automatically pauses a previously running timer on the same thread, - * which is resumed when it is stopped. As such, no special action needs to be taken to avoid - * double-accounting of time on two categories. - */ -class Timer { -public: - Timer(TimingCategory& category) : category(category) { - } - - void Start() { -#if ENABLE_PROFILING - ASSERT(!running); - previous_timer = current_timer; - current_timer = this; - if (previous_timer != nullptr) - previous_timer->StopTiming(); - - StartTiming(); -#endif - } - - void Stop() { -#if ENABLE_PROFILING - ASSERT(running); - StopTiming(); - - if (previous_timer != nullptr) - previous_timer->StartTiming(); - current_timer = previous_timer; -#endif - } - -private: -#if ENABLE_PROFILING - void StartTiming() { - start = Clock::now(); - running = true; - } - - void StopTiming() { - auto duration = Clock::now() - start; - running = false; - category.AddTime(std::chrono::duration_cast(duration)); - } - - Clock::time_point start; - bool running = false; - - Timer* previous_timer; - static thread_local Timer* current_timer; -#endif - - TimingCategory& category; -}; - -/** - * A Timer that automatically starts timing when created and stops at the end of the scope. Should - * be used in the majority of cases. - */ -class ScopeTimer : public Timer { -public: - ScopeTimer(TimingCategory& category) : Timer(category) { - Start(); - } - - ~ScopeTimer() { - Stop(); - } -}; - -} // namespace Profiling -} // namespace Common diff --git a/src/common/profiler_reporting.h b/src/common/profiler_reporting.h index df98e05b7..fa1ac883f 100644 --- a/src/common/profiler_reporting.h +++ b/src/common/profiler_reporting.h @@ -4,22 +4,17 @@ #pragma once +#include #include #include -#include "common/profiler.h" #include "common/synchronized_wrapper.h" namespace Common { namespace Profiling { -struct TimingCategoryInfo { - static const unsigned int NO_PARENT = -1; - - TimingCategory* category; - const char* name; - unsigned int parent; -}; +using Clock = std::chrono::high_resolution_clock; +using Duration = Clock::duration; struct ProfilingFrameResult { /// Time since the last delivered frame @@ -27,22 +22,12 @@ struct ProfilingFrameResult { /// Time spent processing a frame, excluding VSync Duration frame_time; - - /// Total amount of time spent inside each category in this frame. Indexed by the category id - std::vector time_per_category; }; class ProfilingManager final { public: ProfilingManager(); - unsigned int RegisterTimingCategory(TimingCategory* category, const char* name); - void SetTimingCategoryParent(unsigned int category, unsigned int parent); - - const std::vector& GetTimingCategoriesInfo() const { - return timing_categories; - } - /// This should be called after swapping screen buffers. void BeginFrame(); /// This should be called before swapping screen buffers. @@ -54,7 +39,6 @@ public: } private: - std::vector timing_categories; Clock::time_point last_frame_end; Clock::time_point this_frame_start; @@ -73,9 +57,6 @@ struct AggregatedFrameResult { AggregatedDuration frame_time; float fps; - - /// Total amount of time spent inside each category in this frame. Indexed by the category id - std::vector time_per_category; }; class TimingResultsAggregator final { @@ -83,7 +64,6 @@ public: TimingResultsAggregator(size_t window_size); void Clear(); - void SetNumberOfCategories(size_t n); void AddFrame(const ProfilingFrameResult& frame_result); @@ -95,7 +75,6 @@ public: std::vector interframe_times; std::vector frame_times; - std::vector> times_per_category; }; ProfilingManager& GetProfilingManager(); diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 647784208..8d4b26815 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -10,7 +10,6 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "common/microprofile.h" -#include "common/profiler.h" #include "core/memory.h" #include "core/hle/svc.h" @@ -25,9 +24,6 @@ #include "core/gdbstub/gdbstub.h" -Common::Profiling::TimingCategory profile_execute("DynCom::Execute"); -Common::Profiling::TimingCategory profile_decode("DynCom::Decode"); - enum { COND = (1 << 0), NON_BRANCH = (1 << 1), @@ -3496,7 +3492,6 @@ static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, cons } static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) { - Common::Profiling::ScopeTimer timer_decode(profile_decode); MICROPROFILE_SCOPE(DynCom_Decode); // Decode instruction, get index @@ -3530,7 +3525,6 @@ static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) } static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) { - Common::Profiling::ScopeTimer timer_decode(profile_decode); MICROPROFILE_SCOPE(DynCom_Decode); ARM_INST_PTR inst_base = nullptr; @@ -3565,7 +3559,6 @@ static int clz(unsigned int x) { MICROPROFILE_DEFINE(DynCom_Execute, "DynCom", "Execute", MP_RGB(255, 0, 0)); unsigned InterpreterMainLoop(ARMul_State* cpu) { - Common::Profiling::ScopeTimer timer_execute(profile_execute); MICROPROFILE_SCOPE(DynCom_Execute); GDBStub::BreakpointAddress breakpoint_data; diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 211fcf599..233592d7f 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -4,7 +4,6 @@ #include "common/bit_field.h" #include "common/microprofile.h" -#include "common/profiler.h" #include "core/memory.h" #include "core/hle/kernel/event.h" diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index ae54afb1c..a9a1a3244 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -6,7 +6,6 @@ #include "common/logging/log.h" #include "common/microprofile.h" -#include "common/profiler.h" #include "common/string_util.h" #include "common/symbols.h" @@ -1031,8 +1030,6 @@ static const FunctionDef SVC_Table[] = { {0x7D, HLE::Wrap, "QueryProcessMemory"}, }; -Common::Profiling::TimingCategory profiler_svc("SVC Calls"); - static const FunctionDef* GetSVCInfo(u32 func_num) { if (func_num >= ARRAY_SIZE(SVC_Table)) { LOG_ERROR(Kernel_SVC, "unknown svc=0x%02X", func_num); @@ -1044,7 +1041,6 @@ static const FunctionDef* GetSVCInfo(u32 func_num) { MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); void CallSVC(u32 immediate) { - Common::Profiling::ScopeTimer timer_svc(profiler_svc); MICROPROFILE_SCOPE(Kernel_SVC); const FunctionDef* info = GetSVCInfo(immediate); diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index 3abe79c09..97ba8214e 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -7,7 +7,6 @@ #include "common/alignment.h" #include "common/microprofile.h" -#include "common/profiler.h" #include "core/settings.h" #include "core/hle/service/gsp_gpu.h" @@ -35,8 +34,6 @@ static int default_attr_counter = 0; static u32 default_attr_write_buffer[3]; -Common::Profiling::TimingCategory category_drawing("Drawing"); - // Expand a 4-bit mask to 4-byte mask, e.g. 0b0101 -> 0x00FF00FF static const u32 expand_bits_to_bytes[] = { 0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff, @@ -186,7 +183,6 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { case PICA_REG_INDEX(trigger_draw): case PICA_REG_INDEX(trigger_draw_indexed): { - Common::Profiling::ScopeTimer scope_timer(category_drawing); MICROPROFILE_SCOPE(GPU_Drawing); #if PICA_LOG_TEV diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index 0434ad05a..9cf77b1f2 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp @@ -9,7 +9,6 @@ #include "common/common_types.h" #include "common/math_util.h" #include "common/microprofile.h" -#include "common/profiler.h" #include "core/memory.h" #include "core/hw/gpu.h" @@ -287,7 +286,6 @@ static int SignedArea (const Math::Vec2& vtx1, return Math::Cross(vec1, vec2).z; }; -static Common::Profiling::TimingCategory rasterization_category("Rasterization"); MICROPROFILE_DEFINE(GPU_Rasterization, "GPU", "Rasterization", MP_RGB(50, 50, 240)); /** @@ -300,7 +298,6 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, bool reversed = false) { const auto& regs = g_state.regs; - Common::Profiling::ScopeTimer timer(rasterization_category); MICROPROFILE_SCOPE(GPU_Rasterization); // vertex positions in rasterizer coordinates diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 30187d4cf..a8c775c80 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -11,7 +11,6 @@ #include "common/file_util.h" #include "common/math_util.h" #include "common/microprofile.h" -#include "common/profiler.h" #include "core/memory.h" #include "core/settings.h" diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp index 75301accd..043e99190 100644 --- a/src/video_core/shader/shader.cpp +++ b/src/video_core/shader/shader.cpp @@ -9,7 +9,6 @@ #include "common/hash.h" #include "common/microprofile.h" -#include "common/profiler.h" #include "video_core/debug_utils/debug_utils.h" #include "video_core/pica.h" @@ -57,13 +56,11 @@ void Shutdown() { #endif // ARCHITECTURE_x86_64 } -static Common::Profiling::TimingCategory shader_category("Vertex Shader"); MICROPROFILE_DEFINE(GPU_VertexShader, "GPU", "Vertex Shader", MP_RGB(50, 50, 240)); OutputVertex Run(UnitState& state, const InputVertex& input, int num_attributes) { auto& config = g_state.regs.vs; - Common::Profiling::ScopeTimer timer(shader_category); MICROPROFILE_SCOPE(GPU_VertexShader); state.program_counter = config.main_offset;