From 61ebde196b56c6f650cd3201dda062078ce4cab3 Mon Sep 17 00:00:00 2001 From: ened Date: Sat, 20 Sep 2025 02:36:55 +0900 Subject: [PATCH] Fix SIMD bug by Gemini --- .../Vav2Player/src/Output/FileOutput.cpp | 290 +++++++----------- .../Vav2Player/src/Output/FileOutput.h | 3 +- 2 files changed, 117 insertions(+), 176 deletions(-) diff --git a/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.cpp b/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.cpp index 585ba9e..1da0246 100644 --- a/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.cpp +++ b/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.cpp @@ -1,4 +1,4 @@ -#include "pch.h" +๏ปฟ#include "pch.h" #include "FileOutput.h" #include #include @@ -11,7 +11,6 @@ namespace Vav2Player { -// ๐Ÿ“ˆ ์ •์  ๋ฃฉ์—… ํ…Œ์ด๋ธ” ์ •์˜ int FileOutput::s_yuv_table_r[256]; int FileOutput::s_yuv_table_g_u[256]; int FileOutput::s_yuv_table_g_v[256]; @@ -22,7 +21,6 @@ FileOutput::FileOutput(const OutputConfig& config) : m_config(config) { m_stats.start_time = std::chrono::high_resolution_clock::now(); - // SIMD ์ง€์› ์—ฌ๋ถ€ ํ™•์ธ ๋ฐ ๋กœ๊น… bool simd_supported = IsSIMDSupported(); OutputDebugStringA(simd_supported ? "[FileOutput] CPU SIMD AVX2 support: ENABLED\n" : @@ -33,14 +31,12 @@ FileOutput::FileOutput(const OutputConfig& config) OutputDebugStringA("[FileOutput] YUV->RGB conversion will use lookup table optimization\n"); } - // ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•œ ์บ์‹œ ์ดˆ๊ธฐํ™” InitializeCache(); } void FileOutput::SetConfig(const OutputConfig& config) { m_config = config; - // ์„ค์ • ๋ณ€๊ฒฝ ์‹œ ์บ์‹œ ๋‹ค์‹œ ์ดˆ๊ธฐํ™” m_directory_initialized = false; InitializeCache(); } @@ -52,20 +48,15 @@ void FileOutput::SetProgressCallback(ProgressCallback callback) { FileOutput::SaveResult FileOutput::SaveFrame(const VideoFrame& frame, uint64_t frame_index, double timestamp) { auto start_time = std::chrono::high_resolution_clock::now(); - // ๐Ÿ“ˆ ์ตœ์ ํ™”: ๋””๋ ‰ํ† ๋ฆฌ๋Š” ์ฒ˜์Œ ํ•œ ๋ฒˆ๋งŒ ์ƒ์„ฑ if (!m_directory_initialized) { - std::cout << "[FileOutput Debug] Attempting to create directory: " << m_config.output_directory.string() << std::endl; if (!CreateOutputDirectory()) { return CreateErrorResult("Failed to create output directory"); } m_directory_initialized = true; - std::cout << "[FileOutput Debug] Directory creation successful" << std::endl; } - // ๐Ÿ“ˆ ์ตœ์ ํ™”: ํŒŒ์ผ๋ช…์„ ๋น ๋ฅด๊ฒŒ ์ƒ์„ฑ (๋ฌธ์ž์—ด ์žฌํ• ๋‹น ์ตœ์†Œํ™”) auto file_path = GenerateOptimizedFilename(frame_index); - // ๊ธฐ์กด ํŒŒ์ผ ๋ฎ์–ด์“ฐ๊ธฐ ํ™•์ธ try { if (!m_config.overwrite_existing && std::filesystem::exists(file_path)) { return CreateErrorResult("File already exists and overwrite is disabled"); @@ -76,7 +67,6 @@ FileOutput::SaveResult FileOutput::SaveFrame(const VideoFrame& frame, uint64_t f SaveResult result; - // ํฌ๋งท๋ณ„ ์ €์žฅ switch (m_config.format) { case OutputFormat::RawYUV: result = SaveAsRawYUV(frame, file_path); @@ -92,12 +82,18 @@ FileOutput::SaveResult FileOutput::SaveFrame(const VideoFrame& frame, uint64_t f break; } - // ํ†ต๊ณ„ ์—…๋ฐ์ดํŠธ auto end_time = std::chrono::high_resolution_clock::now(); auto duration_ms = std::chrono::duration(end_time - start_time).count(); UpdateSaveStats(result.success, result.file_size_bytes, duration_ms); - // ์ง„ํ–‰ ์ƒํ™ฉ ์ฝœ๋ฐฑ ํ˜ธ์ถœ + if (result.success) { + char buffer[256]; + sprintf_s(buffer, sizeof(buffer), "[FileOutput] Saved frame %llu. Total time: %.2f ms, Conversion time: %.2f ms\n", + frame_index, duration_ms, result.conversion_time_ms); + OutputDebugStringA(buffer); + std::cout << buffer; + } + if (result.success && m_progress_callback) { m_progress_callback(frame_index, result.saved_path); } @@ -112,20 +108,15 @@ bool FileOutput::SaveFrameSequence(const VideoFrame& frame, uint64_t frame_index bool FileOutput::CreateOutputDirectory() { try { - // ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค ๊ฒฝ๋กœ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ถœ๋ ฅ ๋””๋ ‰ํ† ๋ฆฌ ๊ฒฝ๋กœ ์ƒ์„ฑ std::filesystem::path exe_path = GetProcessPath(); std::filesystem::path full_output_path = exe_path / m_config.output_directory; - std::cout << "[FileOutput Debug] Process path: " << exe_path.string() << std::endl; - std::cout << "[FileOutput Debug] Final output directory: " << full_output_path.string() << std::endl; - if (!std::filesystem::exists(full_output_path)) { return std::filesystem::create_directories(full_output_path); } return true; } catch (const std::filesystem::filesystem_error& e) { std::cout << "[FileOutput Error] Failed to create directory: " << e.what() << std::endl; - std::cout << "[FileOutput Error] Directory path: " << m_config.output_directory.string() << std::endl; return false; } } @@ -140,7 +131,6 @@ void FileOutput::ClearOutputDirectory() { } } catch (const std::filesystem::filesystem_error& e) { std::cout << "[FileOutput Error] Failed to clear directory: " << e.what() << std::endl; - std::cout << "[FileOutput Error] Directory path: " << (GetProcessPath() / m_config.output_directory).string() << std::endl; } } @@ -178,7 +168,6 @@ std::filesystem::path FileOutput::GenerateFilename(const std::string& prefix, return std::filesystem::path(prefix + "_" + std::to_string(frame_index) + extension); } -// Raw YUV ํŒŒ์ผ ์ €์žฅ FileOutput::SaveResult FileOutput::SaveAsRawYUV(const VideoFrame& frame, const std::filesystem::path& file_path) { try { std::ofstream file(file_path, std::ios::binary); @@ -188,14 +177,12 @@ FileOutput::SaveResult FileOutput::SaveAsRawYUV(const VideoFrame& frame, const s size_t total_bytes = 0; - // Y ํ”Œ๋ ˆ์ธ ์ €์žฅ for (uint32_t y = 0; y < frame.height; ++y) { const uint8_t* row_data = frame.y_plane.get() + (y * frame.y_stride); file.write(reinterpret_cast(row_data), frame.width); total_bytes += frame.width; } - // U ํ”Œ๋ ˆ์ธ ์ €์žฅ uint32_t chroma_width = (frame.color_space == ColorSpace::YUV444P) ? frame.width : frame.width / 2; uint32_t chroma_height = (frame.color_space == ColorSpace::YUV420P) ? frame.height / 2 : frame.height; @@ -205,7 +192,6 @@ FileOutput::SaveResult FileOutput::SaveAsRawYUV(const VideoFrame& frame, const s total_bytes += chroma_width; } - // V ํ”Œ๋ ˆ์ธ ์ €์žฅ for (uint32_t y = 0; y < chroma_height; ++y) { const uint8_t* row_data = frame.v_plane.get() + (y * frame.v_stride); file.write(reinterpret_cast(row_data), chroma_width); @@ -223,30 +209,25 @@ FileOutput::SaveResult FileOutput::SaveAsRawYUV(const VideoFrame& frame, const s } } -// BMP ํŒŒ์ผ ์ €์žฅ (๊ธฐ์กด ๋ฐฉ์‹ - ํ˜ธํ™˜์„ฑ์šฉ) FileOutput::SaveResult FileOutput::SaveAsBMP(const VideoFrame& frame, const std::filesystem::path& file_path) { - // ๐Ÿ“ˆ ์„ฑ๋Šฅ ์ตœ์ ํ™”: ๋ฉ”๋ชจ๋ฆฌ ๋งคํ•‘ ๋ฐฉ์‹ ์‚ฌ์šฉ return SaveAsBMP_MemoryMapped(frame, file_path); } -// ๐Ÿ“ˆ ๋ฉ”๋ชจ๋ฆฌ ๋งคํ•‘ ๊ธฐ๋ฐ˜ BMP ํŒŒ์ผ ์ €์žฅ (๊ณ ์„ฑ๋Šฅ) FileOutput::SaveResult FileOutput::SaveAsBMP_MemoryMapped(const VideoFrame& frame, const std::filesystem::path& file_path) { - // YUV โ†’ RGB ๋ณ€ํ™˜ + auto conversion_start_time = std::chrono::high_resolution_clock::now(); RGBFrame rgb_frame; if (!ConvertYUVToRGB(frame, rgb_frame)) { return CreateErrorResult("Failed to convert YUV to RGB"); } try { - // BMP ํ—ค๋” ์ƒ์„ฑ auto header = CreateBMPHeader(rgb_frame.width, rgb_frame.height); uint32_t row_size = rgb_frame.width * 3; uint32_t padding = (4 - (row_size % 4)) % 4; uint32_t padded_row_size = row_size + padding; - uint32_t header_size = 54; // BMP ํ—ค๋” ํฌ๊ธฐ + uint32_t header_size = 54; uint32_t total_file_size = header_size + (padded_row_size * rgb_frame.height); - // ๐Ÿ“ˆ Windows ๋ฉ”๋ชจ๋ฆฌ ๋งคํ•‘ ํŒŒ์ผ ์ƒ์„ฑ HANDLE hFile = CreateFileW( file_path.wstring().c_str(), GENERIC_READ | GENERIC_WRITE, @@ -289,10 +270,8 @@ FileOutput::SaveResult FileOutput::SaveAsBMP_MemoryMapped(const VideoFrame& fram return CreateErrorResult("Failed to map view of file: " + std::to_string(GetLastError())); } - // ๐Ÿ“ˆ ๋ฉ”๋ชจ๋ฆฌ์— ์ง์ ‘ ํ—ค๋” ์“ฐ๊ธฐ (ํŒŒ์ผ I/O ์—†์Œ) uint8_t* write_ptr = mapped_memory; - // BMP ํ—ค๋” ๋ณต์‚ฌ memcpy(write_ptr, &header.file_type, sizeof(header.file_type)); write_ptr += sizeof(header.file_type); memcpy(write_ptr, &header.file_size, sizeof(header.file_size)); write_ptr += sizeof(header.file_size); memcpy(write_ptr, &header.reserved1, sizeof(header.reserved1)); write_ptr += sizeof(header.reserved1); @@ -310,24 +289,25 @@ FileOutput::SaveResult FileOutput::SaveAsBMP_MemoryMapped(const VideoFrame& fram memcpy(write_ptr, &header.colors_used, sizeof(header.colors_used)); write_ptr += sizeof(header.colors_used); memcpy(write_ptr, &header.colors_important, sizeof(header.colors_important)); - // ๐Ÿ“ˆ RGBโ†’BGR ๋ณ€ํ™˜ํ•˜๋ฉด์„œ ๋ฉ”๋ชจ๋ฆฌ์— ์ง์ ‘ ์“ฐ๊ธฐ (BMP๋Š” bottom-up) for (int32_t y = static_cast(rgb_frame.height) - 1; y >= 0; --y) { uint8_t* dst_row = mapped_memory + header_size + ((rgb_frame.height - 1 - y) * padded_row_size); const uint8_t* src_row = rgb_frame.data.data() + (y * rgb_frame.stride); - // ๋ผ์ธ ๋‹จ์œ„ RGBโ†’BGR ๋ณ€ํ™˜ ๋ฐ ํŒจ๋”ฉ ์ฒ˜๋ฆฌ ConvertRGBToBGRLine(src_row, dst_row, rgb_frame.width, padding); } - // ๋ฉ”๋ชจ๋ฆฌ ๋งคํ•‘ ํ•ด์ œ UnmapViewOfFile(mapped_memory); CloseHandle(hMapping); CloseHandle(hFile); + auto conversion_end_time = std::chrono::high_resolution_clock::now(); + double conversion_time_ms = std::chrono::duration(conversion_end_time - conversion_start_time).count(); + SaveResult result; result.success = true; result.saved_path = file_path; result.file_size_bytes = total_file_size; + result.conversion_time_ms = conversion_time_ms; return result; } catch (const std::exception& e) { @@ -335,11 +315,10 @@ FileOutput::SaveResult FileOutput::SaveAsBMP_MemoryMapped(const VideoFrame& fram } } -// YUV โ†’ RGB ๋ณ€ํ™˜ bool FileOutput::ConvertYUVToRGB(const VideoFrame& yuv_frame, RGBFrame& rgb_frame) { rgb_frame.width = yuv_frame.width; rgb_frame.height = yuv_frame.height; - rgb_frame.stride = yuv_frame.width * 3; // RGB24 + rgb_frame.stride = yuv_frame.width * 3; rgb_frame.data.resize(rgb_frame.height * rgb_frame.stride); switch (yuv_frame.color_space) { @@ -368,34 +347,31 @@ bool FileOutput::ConvertYUVToRGB(const VideoFrame& yuv_frame, RGBFrame& rgb_fram return true; } -// YUV420P โ†’ RGB24 ๋ณ€ํ™˜ (์ตœ์ ํ™”๋œ ๋ฒ„์ „ - SIMD ์ง€์› ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์„ ํƒ) void FileOutput::ConvertYUV420PToRGB24(const uint8_t* y_plane, const uint8_t* u_plane, const uint8_t* v_plane, uint32_t width, uint32_t height, uint32_t y_stride, uint32_t u_stride, uint32_t v_stride, uint8_t* rgb_data, uint32_t rgb_stride) { - // ๐Ÿ“ˆ SIMD ์ง€์› ์‹œ AVX2 ์‚ฌ์šฉ, ์•„๋‹ˆ๋ฉด ๋ฃฉ์—… ํ…Œ์ด๋ธ” ์‚ฌ์šฉ if (IsSIMDSupported()) { - ConvertYUV420PToRGB24_SIMD(y_plane, u_plane, v_plane, width, height, + ConvertYUV420PToRGB24_SIMD(y_plane, u_plane, v_plane, + width, height, y_stride, u_stride, v_stride, rgb_data, rgb_stride); } else { - ConvertYUV420PToRGB24_Optimized(y_plane, u_plane, v_plane, width, height, + ConvertYUV420PToRGB24_Optimized(y_plane, u_plane, v_plane, + width, height, y_stride, u_stride, v_stride, rgb_data, rgb_stride); } } -// YUV422P โ†’ RGB24 ๋ณ€ํ™˜ (4:2:2 ์„œ๋ธŒ์ƒ˜ํ”Œ๋ง) void FileOutput::ConvertYUV422PToRGB24(const uint8_t* y_plane, const uint8_t* u_plane, const uint8_t* v_plane, uint32_t width, uint32_t height, uint32_t y_stride, uint32_t u_stride, uint32_t v_stride, uint8_t* rgb_data, uint32_t rgb_stride) { for (uint32_t y = 0; y < height; ++y) { for (uint32_t x = 0; x < width; ++x) { - // YUV ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ uint8_t Y = y_plane[y * y_stride + x]; - uint8_t U = u_plane[y * u_stride + (x / 2)]; // ์ˆ˜ํ‰๋งŒ ์„œ๋ธŒ์ƒ˜ํ”Œ๋ง + uint8_t U = u_plane[y * u_stride + (x / 2)]; uint8_t V = v_plane[y * v_stride + (x / 2)]; - // YUV โ†’ RGB ๋ณ€ํ™˜ int C = Y - 16; int D = U - 128; int E = V - 128; @@ -416,19 +392,16 @@ void FileOutput::ConvertYUV422PToRGB24(const uint8_t* y_plane, const uint8_t* u_ } } -// YUV444P โ†’ RGB24 ๋ณ€ํ™˜ (4:4:4 ํ’€ ํ•ด์ƒ๋„) void FileOutput::ConvertYUV444PToRGB24(const uint8_t* y_plane, const uint8_t* u_plane, const uint8_t* v_plane, uint32_t width, uint32_t height, uint32_t y_stride, uint32_t u_stride, uint32_t v_stride, uint8_t* rgb_data, uint32_t rgb_stride) { for (uint32_t y = 0; y < height; ++y) { for (uint32_t x = 0; x < width; ++x) { - // YUV ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ uint8_t Y = y_plane[y * y_stride + x]; - uint8_t U = u_plane[y * u_stride + x]; // ์„œ๋ธŒ์ƒ˜ํ”Œ๋ง ์—†์Œ + uint8_t U = u_plane[y * u_stride + x]; uint8_t V = v_plane[y * v_stride + x]; - // YUV โ†’ RGB ๋ณ€ํ™˜ int C = Y - 16; int D = U - 128; int E = V - 128; @@ -449,7 +422,6 @@ void FileOutput::ConvertYUV444PToRGB24(const uint8_t* y_plane, const uint8_t* u_ } } -// BMP ํ—ค๋” ์ƒ์„ฑ FileOutput::BMPHeader FileOutput::CreateBMPHeader(uint32_t width, uint32_t height) { BMPHeader header; @@ -461,12 +433,11 @@ FileOutput::BMPHeader FileOutput::CreateBMPHeader(uint32_t width, uint32_t heigh header.width = static_cast(width); header.height = static_cast(height); header.size_image = image_size; - header.file_size = 54 + image_size; // ํ—ค๋” ํฌ๊ธฐ + ์ด๋ฏธ์ง€ ํฌ๊ธฐ + header.file_size = 54 + image_size; return header; } -// ํŒŒ์ผ ์“ฐ๊ธฐ ์œ ํ‹ธ๋ฆฌํ‹ฐ bool FileOutput::WriteDataToFile(const std::filesystem::path& file_path, const void* data, size_t size) { try { @@ -483,7 +454,6 @@ bool FileOutput::WriteDataToFile(const std::filesystem::path& file_path, } } -// ํ†ต๊ณ„ ์—…๋ฐ์ดํŠธ void FileOutput::UpdateSaveStats(bool success, size_t bytes_written, double save_time_ms) { std::lock_guard lock(m_stats_mutex); @@ -491,7 +461,6 @@ void FileOutput::UpdateSaveStats(bool success, size_t bytes_written, double save m_stats.frames_saved++; m_stats.total_bytes_written += bytes_written; - // ํ‰๊ท  ์ €์žฅ ์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ (์ง€์ˆ˜ ์ด๋™ ํ‰๊ท ) const double alpha = 0.1; m_stats.avg_save_time_ms = alpha * save_time_ms + (1.0 - alpha) * m_stats.avg_save_time_ms; } else { @@ -499,7 +468,6 @@ void FileOutput::UpdateSaveStats(bool success, size_t bytes_written, double save } } -// ์—๋Ÿฌ ๊ฒฐ๊ณผ ์ƒ์„ฑ FileOutput::SaveResult FileOutput::CreateErrorResult(const std::string& error_message) { SaveResult result; result.success = false; @@ -508,27 +476,20 @@ FileOutput::SaveResult FileOutput::CreateErrorResult(const std::string& error_me return result; } -// ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค ๊ฒฝ๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ std::filesystem::path FileOutput::GetProcessPath() const { - // Windows์—์„œ ํ˜„์žฌ ์‹คํ–‰ ํŒŒ์ผ์˜ ๊ฒฝ๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ wchar_t exe_full_path[MAX_PATH]; DWORD path_length = GetModuleFileNameW(nullptr, exe_full_path, MAX_PATH); if (path_length == 0 || path_length == MAX_PATH) { - // GetModuleFileName ์‹คํŒจ ์‹œ ํ˜„์žฌ ์ž‘์—… ๋””๋ ‰ํ† ๋ฆฌ ์‚ฌ์šฉ - std::cout << "[FileOutput Warning] Failed to get executable path, using current working directory" << std::endl; return std::filesystem::current_path(); } else { return std::filesystem::path(exe_full_path).parent_path(); } } -// ๐Ÿ“ˆ ์„ฑ๋Šฅ ์ตœ์ ํ™”: ์บ์‹œ ์ดˆ๊ธฐํ™” void FileOutput::InitializeCache() { - // ์ „์ฒด ์ถœ๋ ฅ ๊ฒฝ๋กœ ์บ์‹œ (ํ”„๋กœ์„ธ์Šค ๊ฒฝ๋กœ + ์ถœ๋ ฅ ๋””๋ ‰ํ† ๋ฆฌ) m_cached_output_path = GetProcessPath() / m_config.output_directory; - // ํŒŒ์ผ ํ™•์žฅ์ž ์บ์‹œ switch (m_config.format) { case OutputFormat::RawYUV: m_cached_file_extension = ".yuv"; break; case OutputFormat::BMP: m_cached_file_extension = ".bmp"; break; @@ -536,102 +497,79 @@ void FileOutput::InitializeCache() { default: m_cached_file_extension = ".dat"; break; } - // ํŒŒ์ผ๋ช… ๋ฒ„ํผ ๋ฏธ๋ฆฌ ์˜ˆ์•ฝ (๋ฉ”๋ชจ๋ฆฌ ์žฌํ• ๋‹น ์ตœ์†Œํ™”) - // ์˜ˆ์ƒ ์ตœ๋Œ€ ๊ธธ์ด: prefix + "_" + frame_number + extension - // ๊ฐ€์ •: prefix=20์ž, frame_number=10์ž, extension=4์ž, ์—ฌ์œ ๋ถ„=50์ž m_filename_buffer.reserve(100); } -// ๐Ÿ“ˆ ์„ฑ๋Šฅ ์ตœ์ ํ™”: ํŒŒ์ผ๋ช… ๋น ๋ฅธ ์ƒ์„ฑ std::filesystem::path FileOutput::GenerateOptimizedFilename(uint64_t frame_index) { - // ๊ธฐ์กด ๋ฒ„ํผ ๋‚ด์šฉ ์ง€์šฐ๊ธฐ (๋ฉ”๋ชจ๋ฆฌ ์žฌํ• ๋‹น ์—†์Œ) m_filename_buffer.clear(); - // ํšจ์œจ์ ์ธ ๋ฌธ์ž์—ด ์ƒ์„ฑ (append ๊ธฐ๋ฐ˜, ์žฌํ• ๋‹น ์ตœ์†Œํ™”) m_filename_buffer.append(m_config.filename_prefix); m_filename_buffer.append("_"); m_filename_buffer.append(std::to_string(frame_index)); m_filename_buffer.append(m_cached_file_extension); - // ์บ์‹œ๋œ ์ „์ฒด ๊ฒฝ๋กœ์™€ ๊ฒฐํ•ฉ return m_cached_output_path / m_filename_buffer; } -// ๐Ÿ“ˆ RGBโ†’BGR ๋ผ์ธ ๋ณ€ํ™˜ (๋ฉ”๋ชจ๋ฆฌ ๋งคํ•‘ ์ตœ์ ํ™”์šฉ) void FileOutput::ConvertRGBToBGRLine(const uint8_t* src_rgb, uint8_t* dst_bgr, uint32_t width, uint32_t padding) { - // RGB โ†’ BGR ๋ณ€ํ™˜ (3ํ”ฝ์…€์”ฉ ์ฒ˜๋ฆฌ๋กœ ์ตœ์ ํ™”) for (uint32_t x = 0; x < width; ++x) { - dst_bgr[x * 3 + 0] = src_rgb[x * 3 + 2]; // B = R - dst_bgr[x * 3 + 1] = src_rgb[x * 3 + 1]; // G = G - dst_bgr[x * 3 + 2] = src_rgb[x * 3 + 0]; // R = B + dst_bgr[x * 3 + 0] = src_rgb[x * 3 + 2]; + dst_bgr[x * 3 + 1] = src_rgb[x * 3 + 1]; + dst_bgr[x * 3 + 2] = src_rgb[x * 3 + 0]; } - // ํŒจ๋”ฉ ์˜์—ญ์„ 0์œผ๋กœ ์ฑ„์›€ (4๋ฐ”์ดํŠธ ์ •๋ ฌ) if (padding > 0) { memset(dst_bgr + width * 3, 0, padding); } } -// ๐Ÿ“ˆ YUVโ†’RGB ๋ฃฉ์—… ํ…Œ์ด๋ธ” ์ดˆ๊ธฐํ™” (ํ”„๋กœ๊ทธ๋žจ ์‹œ์ž‘ ์‹œ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰) void FileOutput::InitializeYUVTables() { if (s_tables_initialized) { return; } - // BT.601 ํ‘œ์ค€ YUVโ†’RGB ๋ณ€ํ™˜ ๊ณ„์ˆ˜๋ฅผ ๋ฃฉ์—… ํ…Œ์ด๋ธ”๋กœ ๋ฏธ๋ฆฌ ๊ณ„์‚ฐ + // Store only the multiplication result, not the shifted one, to match SIMD logic. for (int i = 0; i < 256; ++i) { - // V โ†’ R ๋ณ€ํ™˜: 409 * (V - 128) / 256 - s_yuv_table_r[i] = (409 * (i - 128)) >> 8; - - // U โ†’ G ๋ณ€ํ™˜: -100 * (U - 128) / 256 - s_yuv_table_g_u[i] = (-100 * (i - 128)) >> 8; - - // V โ†’ G ๋ณ€ํ™˜: -208 * (V - 128) / 256 - s_yuv_table_g_v[i] = (-208 * (i - 128)) >> 8; - - // U โ†’ B ๋ณ€ํ™˜: 516 * (U - 128) / 256 - s_yuv_table_b[i] = (516 * (i - 128)) >> 8; + s_yuv_table_r[i] = 409 * (i - 128); + s_yuv_table_g_u[i] = -100 * (i - 128); + s_yuv_table_g_v[i] = -208 * (i - 128); + s_yuv_table_b[i] = 516 * (i - 128); } s_tables_initialized = true; } -// ๐Ÿ“ˆ ์ตœ์ ํ™”๋œ YUV420P โ†’ RGB24 ๋ณ€ํ™˜ (๋ฃฉ์—… ํ…Œ์ด๋ธ” + ๋ผ์ธ ์ฒ˜๋ฆฌ) +// Optimized YUV420P -> RGB24 conversion (lookup table) void FileOutput::ConvertYUV420PToRGB24_Optimized(const uint8_t* y_plane, const uint8_t* u_plane, const uint8_t* v_plane, uint32_t width, uint32_t height, uint32_t y_stride, uint32_t u_stride, uint32_t v_stride, uint8_t* rgb_data, uint32_t rgb_stride) { - // ๋ฃฉ์—… ํ…Œ์ด๋ธ” ์ดˆ๊ธฐํ™” (์ฒซ ๋ฒˆ์งธ ํ˜ธ์ถœ ์‹œ๋งŒ) InitializeYUVTables(); - // ๋ผ์ธ ๋‹จ์œ„๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ์บ์‹œ ํšจ์œจ์„ฑ ํ–ฅ์ƒ for (uint32_t y = 0; y < height; ++y) { const uint8_t* y_row = y_plane + (y * y_stride); const uint8_t* u_row = u_plane + ((y / 2) * u_stride); const uint8_t* v_row = v_plane + ((y / 2) * v_stride); uint8_t* rgb_row = rgb_data + (y * rgb_stride); - // 2ํ”ฝ์…€์”ฉ ์ฒ˜๋ฆฌํ•˜์—ฌ YUV420P ํŠน์„ฑ ํ™œ์šฉ (U, V๊ฐ€ 2x2 ๋ธ”๋ก ๊ณต์œ ) for (uint32_t x = 0; x < width; x += 2) { uint8_t U = u_row[x / 2]; uint8_t V = v_row[x / 2]; - // ๐Ÿ“ˆ ๋ฃฉ์—… ํ…Œ์ด๋ธ”๋กœ ๋ณ€ํ™˜ ๊ณ„์ˆ˜ ์กฐํšŒ (๊ณฑ์…ˆ ์—ฐ์‚ฐ ์ œ๊ฑฐ) + // Look up pre-multiplied transform coefficients int r_offset = s_yuv_table_r[V]; int g_offset = s_yuv_table_g_u[U] + s_yuv_table_g_v[V]; int b_offset = s_yuv_table_b[U]; - // 2ํ”ฝ์…€ ๋™์‹œ ์ฒ˜๋ฆฌ (U, V ๊ณต์œ ๋กœ ํšจ์œจ์„ฑ ์ฆ๋Œ€) for (int px = 0; px < 2 && (x + px) < width; ++px) { - int Y = y_row[x + px] - 16; - Y = (Y * 298) >> 8; // ๐Ÿ“ˆ ๋น„ํŠธ ์‹œํ”„ํŠธ๋กœ ๋‚˜๋ˆ—์…ˆ ๋Œ€์ฒด (๋” ๋น ๋ฆ„) + int C = y_row[x + px] - 16; + int y_base = C * 298; - // ๐Ÿ“ˆ ๋ฃฉ์—… ํ…Œ์ด๋ธ” ๊ฒฐ๊ณผ์™€ Y ๊ฐ’ ์กฐํ•ฉ - int R = Y + r_offset; - int G = Y + g_offset; - int B = Y + b_offset; + // Combine all terms, add rounding factor, and shift at the end + int R = (y_base + r_offset + 128) >> 8; + int G = (y_base + g_offset + 128) >> 8; + int B = (y_base + b_offset + 128) >> 8; - // ๐Ÿ“ˆ ์ธ๋ผ์ธ ํด๋žจํ•‘ (std::clamp๋ณด๋‹ค ๋น ๋ฆ„) rgb_row[(x + px) * 3 + 0] = ClampToU8(R); rgb_row[(x + px) * 3 + 1] = ClampToU8(G); rgb_row[(x + px) * 3 + 2] = ClampToU8(B); @@ -640,14 +578,14 @@ void FileOutput::ConvertYUV420PToRGB24_Optimized(const uint8_t* y_plane, const u } } -// ๐Ÿ“ˆ ๋น ๋ฅธ ํด๋žจํ•‘ ํ•จ์ˆ˜ (0-255 ๋ฒ”์œ„๋กœ ์ œํ•œ) +// Fast clamping function (0-255) int FileOutput::ClampToU8(int value) { if (value < 0) return 0; if (value > 255) return 255; return value; } -// ๐Ÿ“ˆ SIMD (AVX2) ์ง€์› ์—ฌ๋ถ€ ํ™•์ธ +// Check for SIMD (AVX2) support bool FileOutput::IsSIMDSupported() { static bool checked = false; static bool supported = false; @@ -656,7 +594,7 @@ bool FileOutput::IsSIMDSupported() { int cpuInfo[4]; __cpuid(cpuInfo, 7); - // EBX ๋ ˆ์ง€์Šคํ„ฐ์˜ 5๋ฒˆ์งธ ๋น„ํŠธ๊ฐ€ AVX2 ์ง€์› ์—ฌ๋ถ€ + // Check the 5th bit of the EBX register for AVX2 support supported = (cpuInfo[1] & (1 << 5)) != 0; checked = true; } @@ -664,14 +602,13 @@ bool FileOutput::IsSIMDSupported() { return supported; } -// ๐Ÿ“ˆ SIMD AVX2 ๊ธฐ๋ฐ˜ YUV420P โ†’ RGB24 ๋ณ€ํ™˜ (8ํ”ฝ์…€ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ) +// SIMD AVX2 based YUV420P -> RGB24 conversion void FileOutput::ConvertYUV420PToRGB24_SIMD(const uint8_t* y_plane, const uint8_t* u_plane, const uint8_t* v_plane, uint32_t width, uint32_t height, uint32_t y_stride, uint32_t u_stride, uint32_t v_stride, uint8_t* rgb_data, uint32_t rgb_stride) { - // AVX2๋Š” 8ํ”ฝ์…€ ๋‹จ์œ„๋กœ ์ฒ˜๋ฆฌ, YUV420P ํŠน์„ฑ์ƒ ํญ์€ 2์˜ ๋ฐฐ์ˆ˜์—ฌ์•ผ ํ•จ const uint32_t simd_width = 8; - const uint32_t safe_width = width & ~1; // ํ™€์ˆ˜ ํญ ๋ฐฉ์ง€ (YUV420P ์š”๊ตฌ์‚ฌํ•ญ) + const uint32_t safe_width = width & ~1; // YUV420P requires even width const uint32_t aligned_width = (safe_width / simd_width) * simd_width; for (uint32_t y = 0; y < height; ++y) { @@ -680,47 +617,44 @@ void FileOutput::ConvertYUV420PToRGB24_SIMD(const uint8_t* y_plane, const uint8_ const uint8_t* v_row = v_plane + ((y / 2) * v_stride); uint8_t* rgb_row = rgb_data + (y * rgb_stride); - // ๐Ÿ“ˆ 8ํ”ฝ์…€์”ฉ SIMD ์ฒ˜๋ฆฌ (๊ฒฝ๊ณ„ ์•ˆ์ „์„ฑ ๋ณด์žฅ) for (uint32_t x = 0; x < aligned_width; x += simd_width) { - // ๊ฒฝ๊ณ„ ๊ฒ€์‚ฌ: U/V ์ ‘๊ทผ์ด ์•ˆ์ „ํ•œ์ง€ ํ™•์ธ uint32_t uv_idx = x / 2; - if (uv_idx + 3 >= u_stride) break; // 4๋ฐ”์ดํŠธ ์ฝ๊ธฐ๊ฐ€ ์•ˆ์ „ํ•˜์ง€ ์•Š์œผ๋ฉด ์ค‘๋‹จ + // Need 4 bytes of U/V data for 8 Y pixels. Break if not available. + if (uv_idx + 4 > u_stride || uv_idx + 4 > v_stride) break; - // Y๊ฐ’ 8๊ฐœ ๋กœ๋“œ (8bit ร— 8 = 64bit) + // Load 8 Y values __m128i y_128 = _mm_loadl_epi64((__m128i*)(y_row + x)); - __m256i y_vec = _mm256_cvtepu8_epi16(y_128); // 16bit๋กœ ํ™•์žฅ + __m256i y_vec = _mm256_cvtepu8_epi16(y_128); // extend to 16-bit - // U, V๊ฐ’ 4๊ฐœ์”ฉ ์•ˆ์ „ํ•˜๊ฒŒ ๋กœ๋“œ + // Safely load 4 U and 4 V values uint32_t u_data = 0, v_data = 0; - memcpy(&u_data, u_row + uv_idx, std::min(4u, u_stride - uv_idx)); - memcpy(&v_data, v_row + uv_idx, std::min(4u, v_stride - uv_idx)); + memcpy(&u_data, u_row + uv_idx, 4); + memcpy(&v_data, v_row + uv_idx, 4); - // YUV420P ํŠน์„ฑ์— ๋งž๋Š” U/V ํ™•์žฅ: [a,b,c,d] โ†’ [a,a,b,b,c,c,d,d] + // Expand U/V to match Y: [u0,u1,u2,u3] -> [u0,u0,u1,u1,u2,u2,u3,u3] __m128i u_packed = _mm_cvtsi32_si128(u_data); __m128i v_packed = _mm_cvtsi32_si128(v_data); - // ์ •ํ™•ํ•œ 2๋ฐฐ ํ™•์žฅ: ๊ฐ U/V ๊ฐ’์ด 2๊ฐœ์˜ Y๊ฐ’์— ๋Œ€์‘ - __m128i u_lo = _mm_unpacklo_epi8(u_packed, u_packed); // [a,a,b,b,c,c,d,d,0,0,0,0,0,0,0,0] + __m128i u_lo = _mm_unpacklo_epi8(u_packed, u_packed); __m128i v_lo = _mm_unpacklo_epi8(v_packed, v_packed); __m256i u_vec = _mm256_cvtepu8_epi16(u_lo); __m256i v_vec = _mm256_cvtepu8_epi16(v_lo); - // ๐Ÿ“ˆ ๋ฒกํ„ฐํ™”๋œ YUVโ†’RGB ๋ณ€ํ™˜ + // Vectorized YUV->RGB conversion __m256i r_vec, g_vec, b_vec; YUVToRGB_SIMD(y_vec, u_vec, v_vec, r_vec, g_vec, b_vec); - // ๐Ÿ“ˆ RGB24 ํ˜•ํƒœ๋กœ ์ธํ„ฐ๋ฆฌ๋ธŒํ•ด์„œ ์ €์žฅ + // Interleave and store as RGB24 StoreRGB24_SIMD(rgb_row + x * 3, r_vec, g_vec, b_vec, simd_width); } - // ๐Ÿ“ˆ ๋‚˜๋จธ์ง€ ํ”ฝ์…€๋“ค์€ ์Šค์นผ๋ผ ์ฒ˜๋ฆฌ (ํด๋ฐฑ) + // Scalar fallback for remaining pixels for (uint32_t x = aligned_width; x < width; ++x) { uint8_t Y = y_row[x]; uint8_t U = u_row[x / 2]; uint8_t V = v_row[x / 2]; - // ๊ธฐ๋ณธ YUVโ†’RGB ๋ณ€ํ™˜ int C = Y - 16; int D = U - 128; int E = V - 128; @@ -736,87 +670,93 @@ void FileOutput::ConvertYUV420PToRGB24_SIMD(const uint8_t* y_plane, const uint8_ } } -// ๐Ÿ“ˆ SIMD YUVโ†’RGB ๋ณ€ํ™˜ ์—ฐ์‚ฐ -void FileOutput::YUVToRGB_SIMD(__m256i y_vec, __m256i u_vec, __m256i v_vec, +// SIMD YUV->RGB conversion kernel (prevents overflow by using 32-bit intermediates) +void FileOutput::YUVToRGB_SIMD(__m256i y_vec, __m256i u_vec, __m256i v_vec, __m256i& r_vec, __m256i& g_vec, __m256i& b_vec) { - // ์ƒ์ˆ˜ ์ •์˜ (BT.601 ํ‘œ์ค€) - __m256i c16 = _mm256_set1_epi16(16); - __m256i c128 = _mm256_set1_epi16(128); - __m256i c298 = _mm256_set1_epi16(298); - __m256i c409 = _mm256_set1_epi16(409); - __m256i c100 = _mm256_set1_epi16(100); - __m256i c208 = _mm256_set1_epi16(208); - __m256i c516 = _mm256_set1_epi16(516); + // Input vectors have 8 valid 16-bit values in their low 128 bits. + __m128i y_128 = _mm256_castsi256_si128(y_vec); + __m128i u_128 = _mm256_castsi256_si128(u_vec); + __m128i v_128 = _mm256_castsi256_si128(v_vec); - // Y, U, V ๊ฐ’ ์กฐ์ • - __m256i y_adj = _mm256_sub_epi16(y_vec, c16); // Y - 16 - __m256i u_adj = _mm256_sub_epi16(u_vec, c128); // U - 128 - __m256i v_adj = _mm256_sub_epi16(v_vec, c128); // V - 128 + // Extend all 8 16-bit values to 32-bit. + __m256i y_32 = _mm256_cvtepi16_epi32(y_128); + __m256i u_32 = _mm256_cvtepi16_epi32(u_128); + __m256i v_32 = _mm256_cvtepi16_epi32(v_128); - // Y ๊ธฐ๋ณธ๊ฐ’ ๊ณ„์‚ฐ: 298 * (Y - 16) - __m256i y_base = _mm256_mullo_epi16(y_adj, c298); + // Define 32-bit constants + const __m256i c16 = _mm256_set1_epi32(16); + const __m256i c128 = _mm256_set1_epi32(128); + const __m256i c298 = _mm256_set1_epi32(298); + const __m256i c409 = _mm256_set1_epi32(409); + const __m256i c100 = _mm256_set1_epi32(100); + const __m256i c208 = _mm256_set1_epi32(208); + const __m256i c516 = _mm256_set1_epi32(516); + const __m256i c128_offset = _mm256_set1_epi32(128); - // 128 ์˜คํ”„์…‹ ์ƒ์ˆ˜ ์ถ”๊ฐ€ - __m256i c128_offset = _mm256_set1_epi16(128); + // --- Process all 8 pixels --- + __m256i y_adj = _mm256_sub_epi32(y_32, c16); + __m256i u_adj = _mm256_sub_epi32(u_32, c128); + __m256i v_adj = _mm256_sub_epi32(v_32, c128); + __m256i y_base = _mm256_mullo_epi32(y_adj, c298); - // R ๊ณ„์‚ฐ: (Y_base + 409 * (V - 128) + 128) >> 8 - __m256i r_offset = _mm256_mullo_epi16(v_adj, c409); - __m256i r_full = _mm256_add_epi16(y_base, r_offset); - r_full = _mm256_add_epi16(r_full, c128_offset); // +128 ์ถ”๊ฐ€ - r_vec = _mm256_srai_epi16(r_full, 8); // >> 8 + __m256i r_full = _mm256_add_epi32(_mm256_add_epi32(y_base, _mm256_mullo_epi32(v_adj, c409)), c128_offset); + __m256i g_full = _mm256_add_epi32(_mm256_sub_epi32(_mm256_sub_epi32(y_base, _mm256_mullo_epi32(u_adj, c100)), _mm256_mullo_epi32(v_adj, c208)), c128_offset); + __m256i b_full = _mm256_add_epi32(_mm256_add_epi32(y_base, _mm256_mullo_epi32(u_adj, c516)), c128_offset); - // G ๊ณ„์‚ฐ: (Y_base - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8 - __m256i g_offset1 = _mm256_mullo_epi16(u_adj, c100); - __m256i g_offset2 = _mm256_mullo_epi16(v_adj, c208); - __m256i g_full = _mm256_sub_epi16(y_base, g_offset1); - g_full = _mm256_sub_epi16(g_full, g_offset2); - g_full = _mm256_add_epi16(g_full, c128_offset); // +128 ์ถ”๊ฐ€ - g_vec = _mm256_srai_epi16(g_full, 8); // >> 8 + __m256i r_vec_32 = _mm256_srai_epi32(r_full, 8); + __m256i g_vec_32 = _mm256_srai_epi32(g_full, 8); + __m256i b_vec_32 = _mm256_srai_epi32(b_full, 8); - // B ๊ณ„์‚ฐ: (Y_base + 516 * (U - 128) + 128) >> 8 - __m256i b_offset = _mm256_mullo_epi16(u_adj, c516); - __m256i b_full = _mm256_add_epi16(y_base, b_offset); - b_full = _mm256_add_epi16(b_full, c128_offset); // +128 ์ถ”๊ฐ€ - b_vec = _mm256_srai_epi16(b_full, 8); // >> 8 + // --- Pack results --- + // Pack 8x 32-bit results back to 8x 16-bit results + __m128i r_128_lo = _mm256_castsi256_si128(r_vec_32); + __m128i r_128_hi = _mm256_extracti128_si256(r_vec_32, 1); + __m128i r_packed_128 = _mm_packs_epi32(r_128_lo, r_128_hi); - // ํด๋žจํ•‘ (0-255) - __m256i zero = _mm256_setzero_si256(); - __m256i max_val = _mm256_set1_epi16(255); + __m128i g_128_lo = _mm256_castsi256_si128(g_vec_32); + __m128i g_128_hi = _mm256_extracti128_si256(g_vec_32, 1); + __m128i g_packed_128 = _mm_packs_epi32(g_128_lo, g_128_hi); - r_vec = _mm256_max_epi16(r_vec, zero); - r_vec = _mm256_min_epi16(r_vec, max_val); + __m128i b_128_lo = _mm256_castsi256_si128(b_vec_32); + __m128i b_128_hi = _mm256_extracti128_si256(b_vec_32, 1); + __m128i b_packed_128 = _mm_packs_epi32(b_128_lo, b_128_hi); - g_vec = _mm256_max_epi16(g_vec, zero); - g_vec = _mm256_min_epi16(g_vec, max_val); + // Combine back to 256-bit vectors for clamping + r_vec = _mm256_inserti128_si256(_mm256_castsi128_si256(r_packed_128), r_packed_128, 0); + g_vec = _mm256_inserti128_si256(_mm256_castsi128_si256(g_packed_128), g_packed_128, 0); + b_vec = _mm256_inserti128_si256(_mm256_castsi128_si256(b_packed_128), b_packed_128, 0); - b_vec = _mm256_max_epi16(b_vec, zero); - b_vec = _mm256_min_epi16(b_vec, max_val); + // --- Clamp --- + const __m256i zero = _mm256_setzero_si256(); + const __m256i max_val = _mm256_set1_epi16(255); + + r_vec = _mm256_max_epi16(_mm256_min_epi16(r_vec, max_val), zero); + g_vec = _mm256_max_epi16(_mm256_min_epi16(g_vec, max_val), zero); + b_vec = _mm256_max_epi16(_mm256_min_epi16(b_vec, max_val), zero); } -// ๐Ÿ“ˆ SIMD RGB24 ์ €์žฅ (์ธํ„ฐ๋ฆฌ๋ธŒ) - ์ตœ์ ํ™”๋œ ๋ฒ„์ „ +// SIMD RGB24 storage (interleave) - optimized version void FileOutput::StoreRGB24_SIMD(uint8_t* dst, __m256i r_vec, __m256i g_vec, __m256i b_vec, uint32_t pixel_count) { - // 16bit โ†’ 8bit ํŒฉํ‚น (ํฌํ™” ์—ฐ์‚ฐ์œผ๋กœ 0-255 ๋ฒ”์œ„ ๋ณด์žฅ) - __m128i r_8 = _mm_packus_epi16(_mm256_extracti128_si256(r_vec, 0), _mm256_extracti128_si256(r_vec, 1)); - __m128i g_8 = _mm_packus_epi16(_mm256_extracti128_si256(g_vec, 0), _mm256_extracti128_si256(g_vec, 1)); - __m128i b_8 = _mm_packus_epi16(_mm256_extracti128_si256(b_vec, 0), _mm256_extracti128_si256(b_vec, 1)); + // 16bit -> 8bit packing (with saturation) + __m128i r_8 = _mm_packus_epi16(_mm256_castsi256_si128(r_vec), _mm256_extracti128_si256(r_vec, 1)); + __m128i g_8 = _mm_packus_epi16(_mm256_castsi256_si128(g_vec), _mm256_extracti128_si256(g_vec, 1)); + __m128i b_8 = _mm_packus_epi16(_mm256_castsi256_si128(b_vec), _mm256_extracti128_si256(b_vec, 1)); - // ์ตœ์ ํ™”๋œ RGB24 ์ธํ„ฐ๋ฆฌ๋ธŒ ์ €์žฅ alignas(16) uint8_t r_array[16], g_array[16], b_array[16]; _mm_store_si128((__m128i*)r_array, r_8); _mm_store_si128((__m128i*)g_array, g_8); _mm_store_si128((__m128i*)b_array, b_8); - // 4ํ”ฝ์…€์”ฉ ์ฒ˜๋ฆฌํ•˜์—ฌ ์บ์‹œ ํšจ์œจ์„ฑ ํ–ฅ์ƒ + // Process 4 pixels at a time for cache efficiency uint32_t i = 0; for (; i + 3 < pixel_count; i += 4) { - // 4ํ”ฝ์…€ = 12๋ฐ”์ดํŠธ๋ฅผ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌ dst[i * 3 + 0] = r_array[i]; dst[i * 3 + 1] = g_array[i]; dst[i * 3 + 2] = b_array[i]; dst[(i+1) * 3 + 0] = r_array[i+1]; dst[(i+1) * 3 + 1] = g_array[i+1]; dst[(i+1) * 3 + 2] = b_array[i+1]; dst[(i+2) * 3 + 0] = r_array[i+2]; dst[(i+2) * 3 + 1] = g_array[i+2]; dst[(i+2) * 3 + 2] = b_array[i+2]; dst[(i+3) * 3 + 0] = r_array[i+3]; dst[(i+3) * 3 + 1] = g_array[i+3]; dst[(i+3) * 3 + 2] = b_array[i+3]; } - // ๋‚˜๋จธ์ง€ ํ”ฝ์…€ ์ฒ˜๋ฆฌ + // Process remaining pixels for (; i < pixel_count; ++i) { dst[i * 3 + 0] = r_array[i]; dst[i * 3 + 1] = g_array[i]; diff --git a/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.h b/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.h index 32cb86c..2b28d1e 100644 --- a/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.h +++ b/vav2/Vav2Player/Vav2Player/src/Output/FileOutput.h @@ -1,4 +1,4 @@ -#pragma once +๏ปฟ#pragma once #include "../Common/VideoTypes.h" #include #include @@ -32,6 +32,7 @@ public: bool success = false; std::filesystem::path saved_path; size_t file_size_bytes = 0; + double conversion_time_ms = 0.0; // YUV->RGB ๋ณ€ํ™˜ ์‹œ๊ฐ„ std::string error_message; };