diff --git a/vav2/platforms/android/applications/vav2player/app/src/main/cpp/yuv_fragment.glsl b/vav2/platforms/android/applications/vav2player/app/src/main/cpp/yuv_fragment.glsl index 2040fba..fa12588 100644 --- a/vav2/platforms/android/applications/vav2player/app/src/main/cpp/yuv_fragment.glsl +++ b/vav2/platforms/android/applications/vav2player/app/src/main/cpp/yuv_fragment.glsl @@ -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); } \ No newline at end of file diff --git a/vav2/platforms/windows/vavcore/src/Decoder/MediaCodecSurfaceManager.cpp b/vav2/platforms/windows/vavcore/src/Decoder/MediaCodecSurfaceManager.cpp index 487d2bd..ac49980 100644 --- a/vav2/platforms/windows/vavcore/src/Decoder/MediaCodecSurfaceManager.cpp +++ b/vav2/platforms/windows/vavcore/src/Decoder/MediaCodecSurfaceManager.cpp @@ -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;