Vulkan spec for external formats를 정확히 따랐습니다:
  1. format = VK_FORMAT_UNDEFINED
  2. VkExternalFormatANDROID chain (externalFormat = 0x7FA30C04)
  3. usage = VK_IMAGE_USAGE_SAMPLED_BIT ONLY (TRANSFER_DST 제거)
  4. flags = 0
  5. mipLevels = 1
  6. arrayLayers = 1
  7. samples = VK_SAMPLE_COUNT_1_BIT
This commit is contained in:
2025-11-20 21:58:02 +09:00
parent da6965d979
commit e18f9c5681
2 changed files with 83 additions and 19 deletions

View File

@@ -13,6 +13,30 @@ void main() {
// and the YCbCr conversion parameters from AHardwareBuffer format properties
vec4 rgba = texture(ycbcrTexture, fragTexCoord);
// Output RGB color with full alpha
// DEBUG: Test if YCbCr conversion is actually happening
// If conversion works: rgba should be RGB (red sky = high R, low G, low B)
// If conversion FAILS: rgba will be YUV (Y in .r, U in .g, V in .b)
// - For red pixels: Y=high, U=low, V=high -> would show as purple/magenta!
// DEBUG TEST 1: Show Y channel only (should show grayscale brightness)
// If this shows correct brightness, Y is in the right place
// outColor = vec4(rgba.r, rgba.r, rgba.r, 1.0);
// DEBUG TEST 2: Show U channel only (should show blue-yellow gradient)
// outColor = vec4(rgba.g, rgba.g, rgba.g, 1.0);
// DEBUG TEST 3: Show V channel only (should show red-green gradient)
// outColor = vec4(rgba.b, rgba.b, rgba.b, 1.0);
// DEBUG TEST 4: Try swapped UV (maybe U and V are reversed in NV12?)
// float Y = rgba.r - 0.0625;
// float V = rgba.g - 0.5; // SWAPPED: V from .g
// float U = rgba.b - 0.5; // SWAPPED: U from .b
// float R = Y + 1.5748 * V;
// float G = Y - 0.1873 * U - 0.4681 * V;
// float B = Y + 1.8556 * U;
// outColor = vec4(R, G, B, 1.0);
// DEBUG TEST 5: Raw passthrough (see what hardware gives us)
outColor = vec4(rgba.rgb, 1.0);
}

View File

@@ -365,25 +365,45 @@ bool MediaCodecSurfaceManager::CreateVulkanImage(void* vk_device, void* vk_insta
LogInfo(" is_qualcomm_gpu (Adreno): " + std::string(is_qualcomm_gpu ? "true" : "false"));
LogInfo(" is_samsung: " + std::string(is_samsung ? "true" : "false"));
// CRITICAL: For external formats (Qualcomm vendor formats), we MUST attach VkExternalFormatANDROID
VkExternalFormatANDROID externalFormat = {};
externalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID;
externalFormat.pNext = nullptr;
externalFormat.externalFormat = 0; // Will be set if using external format
VkSamplerYcbcrConversionCreateInfo ycbcrConversionCreateInfo = {};
ycbcrConversionCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO;
ycbcrConversionCreateInfo.pNext = nullptr;
ycbcrConversionCreateInfo.pNext = nullptr; // Will chain externalFormat if needed
// Use format properties from AHardwareBuffer
// CRITICAL FIX: Use AHardwareBuffer's actual format, NOT Vulkan's suggested format!
// Qualcomm returns VK_FORMAT_UNDEFINED (0) in ahb_format_props.format
// But the REAL format is in ahb_desc.format (Qualcomm vendor format 0x7FA30C04)
//
// We need to use the AHardwareBuffer format directly as a Vulkan "external format"
VkFormat vulkan_format = ahb_format_props.format;
// Log both formats to debug
LogInfo("Format detection:");
LogInfo(" AHardwareBuffer format (ahb_desc.format): 0x" + std::to_string(ahb_desc.format));
LogInfo(" Vulkan suggested format (ahb_format_props.format): " + std::to_string(ahb_format_props.format));
if (vulkan_format == VK_FORMAT_UNDEFINED || vulkan_format == 0) {
// WORKAROUND: GPU-specific format detection
// Qualcomm Adreno GPU → NV21 (CrCb) requires component swizzle
// Other GPUs → NV12 (CbCr) standard format
if (is_qualcomm_gpu || is_nv21_device) {
vulkan_format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR; // NV12 format (will use BT.601)
is_nv21_device = true;
LogInfo("Qualcomm Adreno GPU detected → Using NV12 format with BT.601 + component swizzle");
} else {
vulkan_format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR; // NV12 format
is_nv21_device = false;
LogInfo("Non-Qualcomm GPU → Using NV12 format with MediaCodec settings");
}
// CRITICAL: Qualcomm GPU returns UNDEFINED format
// We MUST use external format instead of hardcoding NV12!
LogInfo("Vulkan driver returned VK_FORMAT_UNDEFINED - using external format path");
LogInfo(" This is EXPECTED on Qualcomm Adreno GPUs with vendor-specific formats");
// Use the AHardwareBuffer's native format as external format
externalFormat.externalFormat = ahb_desc.format; // 0x7FA30C04 for Qualcomm
ycbcrConversionCreateInfo.pNext = &externalFormat; // Chain external format
vulkan_format = VK_FORMAT_UNDEFINED; // MUST be UNDEFINED when using external format
LogInfo("Using external format:");
LogInfo(" externalFormat = 0x" + std::to_string(externalFormat.externalFormat));
LogInfo(" Vulkan format = VK_FORMAT_UNDEFINED (required for external formats)");
} else {
LogInfo("Vulkan driver provided explicit format: " + std::to_string(vulkan_format));
LogInfo(" NOT using external format path");
}
ycbcrConversionCreateInfo.format = vulkan_format;
@@ -447,20 +467,40 @@ bool MediaCodecSurfaceManager::CreateVulkanImage(void* vk_device, void* vk_insta
external_mem_info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
external_mem_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
// CRITICAL: For external formats, chain VkExternalFormatANDROID to VkImageCreateInfo
if (vulkan_format == VK_FORMAT_UNDEFINED) {
external_mem_info.pNext = &externalFormat; // Chain external format
LogInfo("VkImage: Chaining VkExternalFormatANDROID (format=0x" + std::to_string(externalFormat.externalFormat) + ")");
} else {
external_mem_info.pNext = nullptr;
LogInfo("VkImage: Using explicit Vulkan format (no external format)");
}
VkImageCreateInfo image_info = {};
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.pNext = &external_mem_info;
image_info.flags = 0; // CRITICAL: Must be 0 for external formats
image_info.imageType = VK_IMAGE_TYPE_2D;
// Use the same format as YCbCr conversion (already validated above)
image_info.format = vulkan_format;
image_info.extent.width = ahb_desc.width;
image_info.extent.height = ahb_desc.height;
image_info.extent.depth = 1;
image_info.mipLevels = 1;
image_info.arrayLayers = 1;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_info.mipLevels = 1; // CRITICAL: Must be 1 for external formats
image_info.arrayLayers = 1; // CRITICAL: Must be 1 for external formats
image_info.samples = VK_SAMPLE_COUNT_1_BIT; // CRITICAL: Must be 1 for external formats
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
// CRITICAL: External formats ONLY support VK_IMAGE_USAGE_SAMPLED_BIT
// Adding TRANSFER_DST causes crash on Qualcomm GPUs
if (vulkan_format == VK_FORMAT_UNDEFINED) {
image_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT; // ONLY sampled for external formats
LogInfo("VkImage: usage = SAMPLED_BIT only (external format restriction)");
} else {
image_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
LogInfo("VkImage: usage = SAMPLED_BIT | TRANSFER_DST_BIT");
}
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;