diff --git a/.github/workflows/build-termux.yml b/.github/workflows/build-termux.yml new file mode 100644 index 00000000..26353a6d --- /dev/null +++ b/.github/workflows/build-termux.yml @@ -0,0 +1,38 @@ +name: Termux-Build of Mesa Vulkan Wrapper + +on: [push, pull_request] + +jobs: + build-aarch64-wrapper: + runs-on: ubuntu-latest + + env: + BUILD_DIR: build + IMAGE_TAG: ghcr.io/leegao/mesa-wrapper-ci/wrapper-compiler:latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: wrapper-compiler + run: | + docker run --rm -v "${{ github.workspace }}:/workspace" ${{ env.IMAGE_TAG }} ${{ env.BUILD_DIR }} + + - name: Upload Stripped libvulkan_wrapper.so + uses: actions/upload-artifact@v4 + with: + name: libvulkan_wrapper + path: | + ${{ env.BUILD_DIR }}/libvulkan_wrapper.so + if-no-files-found: error + + - name: Upload Unstripped libvulkan_wrapper.so + uses: actions/upload-artifact@v4 + with: + name: libvulkan_wrapper-unstripped + path: | + ${{ env.BUILD_DIR }}/libvulkan_wrapper.so.unstripped + if-no-files-found: error diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index de62c02e..85a69957 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,6 @@ name: Android NDK Build (Wrapper Variant) -on: [push] +on: [workflow_dispatch] jobs: build-android-wrapper: @@ -48,7 +48,7 @@ jobs: meson setup _build \ --cross-file android-cross.toml \ -Dbuildtype=${{ matrix.build-type }} \ - -Dplatforms=android,x11 \ + -Dplatforms=x11 \ -Dandroid-stub=true \ -Dandroid-libbacktrace=disabled \ -Dplatform-sdk-version=30 \ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..f7d59f82 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,86 @@ +FROM ghcr.io/termux/package-builder:latest + +USER root + +RUN apt-get update && \ + apt-get install -y --no-install-recommends ninja-build && \ + pip3 install --break-system-packages --ignore-installed --no-cache-dir meson ninja mako pyyaml packaging + +RUN mkdir -p /tmp/sysroot && \ + cd /tmp/sysroot && \ + TERMUX_REPO="https://packages-cf.termux.dev/apt/termux-main" && \ + curl -s "${TERMUX_REPO}/dists/stable/main/binary-aarch64/Packages" > Packages && \ + PACKAGES="libdrm libandroid-shmem libxcb libx11 libxshmfence libxext libxrandr libxrender xorgproto libxau libxdmcp" && \ + for pkg in $PACKAGES; do \ + pkg_path=$(awk -v p="Package: $pkg" '$0==p{flag=1} flag && /^Filename:/{print $2; exit}' Packages) && \ + curl -L -O "${TERMUX_REPO}/${pkg_path}"; \ + done && \ + mkdir -p /data/data/com.termux/files/usr && \ + for f in *.deb; do dpkg-deb -x "$f" /; done && \ + rm -rf /tmp/sysroot + +RUN NDK_DIR=$(find /home/builder/lib -maxdepth 2 -name "android-ndk*" 2>/dev/null | head -n 1) && \ + NDK_BIN="${NDK_DIR}/toolchains/llvm/prebuilt/linux-x86_64/bin" && \ + mkdir -p /root/build-config && \ + cat << EOF > /root/build-config/cross_file.txt +[binaries] +c = '${NDK_BIN}/aarch64-linux-android30-clang' +cpp = '${NDK_BIN}/aarch64-linux-android30-clang++' +ar = '${NDK_BIN}/llvm-ar' +strip = '${NDK_BIN}/llvm-strip' +pkg-config = 'pkg-config' + +[constants] +termux_dir = '/data/data/com.termux/files/usr' + +[properties] +pkg_config_libdir = termux_dir + '/lib/pkgconfig:' + termux_dir + '/share/pkgconfig' + +[built-in options] +c_args = ['-D__TERMUX__', '-D__USE_GNU', '-U__ANDROID__', '-I' + termux_dir + '/include', '-include', 'fcntl.h', '-include', 'unistd.h'] +cpp_args = ['-D__TERMUX__', '-D__USE_GNU', '-U__ANDROID__', '-I' + termux_dir + '/include', '-include', 'fcntl.h', '-include', 'unistd.h'] +c_link_args = ['-L' + termux_dir + '/lib', '-landroid-shmem'] +cpp_link_args = ['-L' + termux_dir + '/lib', '-landroid-shmem'] + +[host_machine] +system = 'android' +cpu_family = 'aarch64' +cpu = 'armv8-a' +endian = 'little' +EOF + +RUN cat << 'EOF' > /root/build.sh +#!/bin/bash +set -e + +BUILD_DIR="${1:-${BUILD_DIR:-build}}" + +if [ ! -d "${BUILD_DIR}" ]; then + meson setup "${BUILD_DIR}" --cross-file /root/build-config/cross_file.txt \ + -Dcpp_rtti=false \ + -Dgbm=disabled \ + -Dopengl=false \ + -Dllvm=disabled \ + -Dshared-llvm=disabled \ + -Dplatforms=x11 \ + -Dgallium-drivers= \ + -Dxmlconfig=disabled \ + -Dvulkan-drivers=wrapper +fi + +ninja -C "${BUILD_DIR}" src/vulkan/wrapper/libvulkan_wrapper.so + +cp "${BUILD_DIR}/src/vulkan/wrapper/libvulkan_wrapper.so" "${BUILD_DIR}/libvulkan_wrapper.so.unstripped" + +NDK_DIR=$(find /home/builder/lib -maxdepth 2 -name "android-ndk*" 2>/dev/null | head -n 1) +STRIP="${NDK_DIR}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip" +$STRIP --strip-unneeded -o "${BUILD_DIR}/libvulkan_wrapper.so" "${BUILD_DIR}/libvulkan_wrapper.so.unstripped" + +echo "Build successful:" +echo " - libvulkan_wrapper.so" +echo " - libvulkan_wrapper.so.unstripped" +EOF +RUN chmod +x /root/build.sh + +WORKDIR /workspace +ENTRYPOINT ["/root/build.sh"] diff --git a/src/vulkan/wrapper/wrapper_device.c b/src/vulkan/wrapper/wrapper_device.c index e3b0a84c..120ebf88 100644 --- a/src/vulkan/wrapper/wrapper_device.c +++ b/src/vulkan/wrapper/wrapper_device.c @@ -52,7 +52,7 @@ get_wrapper_buffer_from_handle(struct wrapper_device *device, VkBuffer buffer) { return wb; } -static struct wrapper_image * +struct wrapper_image * get_wrapper_image_from_handle(struct wrapper_device *device, VkImage image) { struct wrapper_image *wi = NULL; @@ -1001,6 +1001,29 @@ wrapper_CreateImage(VkDevice _device, VK_FROM_HANDLE(wrapper_device, device, _device); VkResult res; VkImageCreateInfo create_info = *pCreateInfo; + bool is_emulated_bgra8 = false; + bool is_wsi_image = false; + + // Wrapper specific extension for B8G8R8A8 AHB img emulation for the swapchain + VkBaseInStructure *prev = (VkBaseInStructure *) pCreateInfo; + for (const VkBaseInStructure *s = pCreateInfo->pNext; s; s = s->pNext) { + if (s->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EMULATED_B8G8R8A8_CREATE_INFO_EXT) { + is_emulated_bgra8 = true; + prev->pNext = s->pNext; // unlink + break; + } + prev = (VkBaseInStructure *) s; + } + + // Tag swapchain images using VK_STRUCTURE_TYPE_WSI_IMAGE_CREATE_INFO_MESA + prev = (VkBaseInStructure *) pCreateInfo; + for (const VkBaseInStructure *s = pCreateInfo->pNext; s; s = s->pNext) { + if (s->sType == VK_STRUCTURE_TYPE_WSI_IMAGE_CREATE_INFO_MESA) { + is_wsi_image = true; + break; + } + prev = (VkBaseInStructure *) s; + } if (is_emulated_bcn(device->physical, pCreateInfo->format)) { create_info.format = get_format_for_bcn(pCreateInfo->format); @@ -1042,6 +1065,8 @@ wrapper_CreateImage(VkDevice _device, wi->device = device; wi->info = *pCreateInfo; wi->dispatch_handle = *pImage; + wi->is_emulated_bgra8 = is_emulated_bgra8; + wi->is_wsi_image = is_wsi_image; list_add(&wi->link, &device->image_list); _mesa_hash_table_u64_insert(device->image_table, (uint64_t)wi->dispatch_handle, wi); @@ -2695,6 +2720,48 @@ wrapper_CmdCopyBufferToImage2(VkCommandBuffer commandBuffer, format, pInfo->regionCount, regions); } +VKAPI_ATTR void VKAPI_CALL +wrapper_CmdBlitImage( + VkCommandBuffer commandBuffer, + VkImage srcImage, VkImageLayout srcImageLayout, + VkImage dstImage, VkImageLayout dstImageLayout, + uint32_t regionCount, const VkImageBlit* pRegions, + VkFilter filter) +{ + VK_FROM_HANDLE(wrapper_command_buffer, wcb, commandBuffer); + struct wrapper_device *device = wcb->device; + struct wrapper_image *dst_img = get_wrapper_image_from_handle(device, dstImage); + const VkImageBlit *regions = (const VkImageBlit *)pRegions; + + if (dst_img && dst_img->is_emulated_bgra8) { + WRAPPER_LOG(error, "vkCmdBlitImage with is_emulated_bgra8 image"); + } + + device->dispatch_table.CmdBlitImage( + wcb->dispatch_handle, + srcImage, srcImageLayout, + dstImage, dstImageLayout, + regionCount, regions, filter); +} + +VKAPI_ATTR void VKAPI_CALL +wrapper_CmdBlitImage2( + VkCommandBuffer commandBuffer, + const VkBlitImageInfo2 *pBlitImageInfo) +{ + VK_FROM_HANDLE(wrapper_command_buffer, wcb, commandBuffer); + struct wrapper_device *device = wcb->device; + struct wrapper_image *dst_img = get_wrapper_image_from_handle(device, pBlitImageInfo->dstImage); + + if (dst_img && dst_img->is_emulated_bgra8) { + WRAPPER_LOG(error, "vkCmdBlitImage2 with is_emulated_bgra8 image"); + } + + if (device->dispatch_table.CmdBlitImage2) { + device->dispatch_table.CmdBlitImage2(wcb->dispatch_handle, pBlitImageInfo); + } +} + VKAPI_ATTR void VKAPI_CALL wrapper_FreeCommandBuffers(VkDevice _device, VkCommandPool commandPool, diff --git a/src/vulkan/wrapper/wrapper_device_memory.c b/src/vulkan/wrapper/wrapper_device_memory.c index 3eacfd5a..7d2d6ae1 100644 --- a/src/vulkan/wrapper/wrapper_device_memory.c +++ b/src/vulkan/wrapper/wrapper_device_memory.c @@ -143,7 +143,7 @@ ion_heap_alloc(int heap_fd, size_t size) { int saved_errno = errno; struct ion_handle_data_1 free_data = { .handle = alloc_data.handle }; safe_ioctl(heap_fd, ION_IOC_FREE_1, &free_data); - WRAPPER_LOG("error", "Failed to share handle, errno=%d", alloc_data.handle, saved_errno); + WRAPPER_LOG("error", "Failed to share handle, errno=%d", saved_errno); errno = saved_errno; return -1; } @@ -167,7 +167,6 @@ wrapper_dmabuf_alloc(struct wrapper_device *device, size_t size) return fd; } - uint32_t wrapper_select_device_memory_type(struct wrapper_device *device, VkMemoryPropertyFlags flags) { @@ -183,6 +182,26 @@ wrapper_select_device_memory_type(struct wrapper_device *device, return idx < props->memoryTypeCount ? idx : UINT32_MAX; } +static uint32_t +wrapper_select_allowed_device_memory_type(struct wrapper_device *device, + uint32_t allowed_type_bits, + VkMemoryPropertyFlags flags) { + VkPhysicalDeviceMemoryProperties *props = + &device->physical->memory_properties; + int idx; + + for (idx = 0; idx < props->memoryTypeCount; idx++) { + if (!(allowed_type_bits & (1U << idx))) { + continue; + } + + if (props->memoryTypes[idx].propertyFlags & flags) { + return idx; + } + } + return UINT32_MAX; +} + static VkResult wrapper_allocate_memory_dmaheap(struct wrapper_device *device, const VkMemoryAllocateInfo* pAllocateInfo, @@ -209,6 +228,17 @@ wrapper_allocate_memory_dmaheap(struct wrapper_device *device, WRAPPER_LOG(error, "Failed to get memory fd properties, res %d", result); return VK_ERROR_INVALID_EXTERNAL_HANDLE; } + + int memory_type_index = wrapper_select_allowed_device_memory_type(device, + memory_fd_props.memoryTypeBits, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + if (memory_type_index == UINT32_MAX) { + WRAPPER_LOG(error, "No compatible memory type found for fd %d", *out_fd); + return VK_ERROR_INVALID_EXTERNAL_HANDLE; + } import_fd_info = (VkImportMemoryFdInfoKHR) { .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, @@ -218,12 +248,7 @@ wrapper_allocate_memory_dmaheap(struct wrapper_device *device, }; allocate_info = *pAllocateInfo; allocate_info.pNext = &import_fd_info; - allocate_info.memoryTypeIndex = - wrapper_select_device_memory_type(device, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | - memory_fd_props.memoryTypeBits); + allocate_info.memoryTypeIndex = memory_type_index; result = device->dispatch_table.AllocateMemory( device->dispatch_handle, &allocate_info, @@ -439,6 +464,22 @@ wrapper_AllocateMemory(VkDevice _device, if (vk_find_struct_const(pAllocateInfo, EXPORT_MEMORY_ALLOCATE_INFO)) goto fallback; + const VkMemoryDedicatedAllocateInfo *dedicated_allocate_info = + vk_find_struct_const((void*) pAllocateInfo->pNext, MEMORY_DEDICATED_ALLOCATE_INFO); + + static int bypass_swapchains = -1; + if (bypass_swapchains == -1) + bypass_swapchains = getenv("WRAPPER_BYPASS_SWAPCHAIN_PLACED") ? + atoi(getenv("WRAPPER_BYPASS_SWAPCHAIN_PLACED")) : 0; // TODO: turn on by default if safe + + if (bypass_swapchains && dedicated_allocate_info && dedicated_allocate_info->image != VK_NULL_HANDLE) { + struct wrapper_image *img = get_wrapper_image_from_handle(device, dedicated_allocate_info->image); + if (img && img->is_wsi_image) { + WRAPPER_LOG(info, "Bypassing EXT_map_memory_placed emulation for swapchain image"); + goto fallback; + } + } + WRAPPER_LOG(info, "Emulating vkAllocateMemory"); simple_mtx_lock(&device->resource_mutex); @@ -485,13 +526,25 @@ wrapper_AllocateMemory(VkDevice _device, if (result != VK_SUCCESS) { WRAPPER_LOG(error, "Failed to allocate memory, res %d", result); wrapper_device_memory_destroy(mem); + + if (dedicated_allocate_info && dedicated_allocate_info->image != VK_NULL_HANDLE) { + struct wrapper_image *img = get_wrapper_image_from_handle(device, dedicated_allocate_info->image); + if (img && img->is_wsi_image) { + // Fixes failure to blit on ion-heap (< GKI 5.10) Mali devices at the cost of + // not being able to mmap these. + WRAPPER_LOG(error, "EXT_map_memory_placed emulation failed for swapchain image, bypassing emulation"); + simple_mtx_lock(&device->resource_mutex); + goto fallback; + } + } + vk_error(device, result); } else { *pMemory = mem->dispatch_handle; } out: - simple_mtx_unlock(&mem->device->resource_mutex); + simple_mtx_unlock(&device->resource_mutex); return result; fallback: diff --git a/src/vulkan/wrapper/wrapper_instance.c b/src/vulkan/wrapper/wrapper_instance.c index ba615b8b..1283b609 100644 --- a/src/vulkan/wrapper/wrapper_instance.c +++ b/src/vulkan/wrapper/wrapper_instance.c @@ -8,6 +8,7 @@ #include "vk_common_entrypoints.h" #include "vk_dispatch_table.h" #include "vk_extensions.h" +#include "adrenotools/driver.h" const struct vk_instance_extension_table wrapper_instance_extensions = { .KHR_get_surface_capabilities2 = true, diff --git a/src/vulkan/wrapper/wrapper_private.h b/src/vulkan/wrapper/wrapper_private.h index 1806a916..3a6ecf05 100644 --- a/src/vulkan/wrapper/wrapper_private.h +++ b/src/vulkan/wrapper/wrapper_private.h @@ -14,7 +14,6 @@ #include "vulkan/wsi/wsi_common.h" #include "util/simple_mtx.h" #include "util/hash_table.h" -#include "adrenotools/driver.h" /* Limit advertised for emulated VK_KHR_push_descriptor. 32 is the common * hardware value and comfortably covers what vkd3d/DXVK push. */ @@ -132,6 +131,12 @@ struct wrapper_buffer { VkDeviceSize bcn_inflight; }; +#define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EMULATED_B8G8R8A8_CREATE_INFO_EXT 1000701234 +typedef struct VkEmulatedB8G8R8A8CreateInfoExt { + VkStructureType sType; + const void* pNext; +} VkEmulatedB8G8R8A8CreateInfoExt; + struct wrapper_image { struct vk_image vk; @@ -139,8 +144,14 @@ struct wrapper_image { struct list_head link; VkImage dispatch_handle; VkImageCreateInfo info; + + bool is_emulated_bgra8; + bool is_wsi_image; }; +struct wrapper_image * +get_wrapper_image_from_handle(struct wrapper_device *device, VkImage image); + struct wrapper_fence { struct vk_fence vk; diff --git a/src/vulkan/wsi/wsi_common.c b/src/vulkan/wsi/wsi_common.c index 6e586e0e..cbcec139 100644 --- a/src/vulkan/wsi/wsi_common.c +++ b/src/vulkan/wsi/wsi_common.c @@ -119,9 +119,32 @@ wsi_device_init(struct wsi_device *wsi, .pNext = &pddp, }; GetPhysicalDeviceProperties2(pdevice, &wsi->properties2); + + const char *wine_preload_reserve = getenv("WINEPRELOADRESERVE"); // e.g. 000400000-0008b4000 or 140000000-1400a8000 + bool is_win32 = false; + + if (wine_preload_reserve && *wine_preload_reserve) { + char *end_ptr = NULL; + uint64_t base_addr = strtoull(wine_preload_reserve, &end_ptr, 16); - if (pddp.driverID == VK_DRIVER_ID_ARM_PROPRIETARY) - wsi->needs_blit = true; + if (end_ptr != wine_preload_reserve) { + is_win32 = (base_addr < 0xFFFFFFFFULL); + } + } + + WRAPPER_LOG(info, "is_win32=%d, WINEPRELOADRESERVE=%s", + is_win32, wine_preload_reserve ? wine_preload_reserve : "nullptr"); + + static int emulate_bgra8 = -1; + if (emulate_bgra8 == -1) { + // TODO(leegao): enable generally for all Mali devices (enables direct rendering on Mali) + bool is_mali_win32 = pddp.driverID == VK_DRIVER_ID_ARM_PROPRIETARY && is_win32; + emulate_bgra8 = getenv("WRAPPER_EMULATE_BGRA8") ? atoi(getenv("WRAPPER_EMULATE_BGRA8")) : is_mali_win32; + int dont_emulate_bgra8 = getenv("WRAPPER_DONT_EMULATE_BGRA8") ? atoi(getenv("WRAPPER_DONT_EMULATE_BGRA8")) : 0; + if (dont_emulate_bgra8) emulate_bgra8 = 0; + } + + wsi->emulate_bgra8 = emulate_bgra8; wsi->maxImageDimension2D = wsi->properties2.properties.limits.maxImageDimension2D; assert(wsi->properties2.properties.limits.optimalBufferCopyRowPitchAlignment <= UINT32_MAX); @@ -666,6 +689,15 @@ wsi_configure_image(const struct wsi_swapchain *chain, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, }; + if (chain->wsi->emulate_bgra8) { + // TODO(leegao): actually add the convertible formats + // The WSI emulation on x11 will still attach an ImageView for bgra8 + // but Mali can only support direct rendering on rgba8 + // This is a workaround so WSI can vkCreateImageView of bgra8 views + // over physical rgba8 vkImages backed by an AHB on Mali + info->create.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + } + if (handle_types != 0) { info->ext_mem = (VkExternalMemoryImageCreateInfo) { .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, @@ -1052,6 +1084,16 @@ wsi_CreateSwapchainKHR(VkDevice _device, VkSwapchainCreateInfoKHR info = *pCreateInfo; + if (wsi_device->emulate_bgra8) { + if (info.imageFormat == VK_FORMAT_B8G8R8A8_UNORM) { + WRAPPER_LOG(info, "wsi_CreateSwapchainKHR: Emulating B8G8R8A8_UNORM framebuffers on Mali"); + info.imageFormat = VK_FORMAT_R8G8B8A8_UNORM; + } else if (info.imageFormat == VK_FORMAT_B8G8R8A8_SRGB) { + WRAPPER_LOG(info, "wsi_CreateSwapchainKHR: Emulating B8G8R8A8_UNORM framebuffers on Mali"); + info.imageFormat = VK_FORMAT_R8G8B8A8_SRGB; + } + } + if (wsi_device->force_swapchain_to_currentExtent) { VkSurfaceCapabilities2KHR caps2 = { .sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR, diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h index bb03e2f4..eb9d5546 100644 --- a/src/vulkan/wsi/wsi_common.h +++ b/src/vulkan/wsi/wsi_common.h @@ -183,6 +183,7 @@ struct wsi_device { bool forcesync; bool needs_blit; + bool emulate_bgra8; /* Set to true if the implementation is ok with linear WSI images. */ bool wants_linear; diff --git a/src/vulkan/wsi/wsi_common_android.c b/src/vulkan/wsi/wsi_common_android.c index 6220559f..be1ff0b5 100644 --- a/src/vulkan/wsi/wsi_common_android.c +++ b/src/vulkan/wsi/wsi_common_android.c @@ -2,9 +2,11 @@ #include "wsi_common_private.h" #include "vk_log.h" #include "../wrapper/wrapper_log.h" +#include "../wrapper/wrapper_private.h" #include +#define AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM 1 #define AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM 5 static enum wsi_swapchain_blit_type @@ -13,12 +15,15 @@ wsi_get_ahardware_buffer_blit_type(const struct wsi_device *wsi, { AHardwareBuffer *ahardware_buffer; VkResult result; + uint32_t probe_format = wsi->emulate_bgra8 + ? AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM + : AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM; if (AHardwareBuffer_allocate(&(AHardwareBuffer_Desc){ .width = 500, .height = 500, .layers = 1, - .format = AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM, + .format = probe_format, .usage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | @@ -83,6 +88,7 @@ wsi_get_ahardware_buffer_blit_type(const struct wsi_device *wsi, return WSI_SWAPCHAIN_IMAGE_BLIT; } + WRAPPER_LOG(info, "wsi_get_ahardware_buffer_blit_type: WSI_SWAPCHAIN_NO_BLIT"); return WSI_SWAPCHAIN_NO_BLIT; } @@ -129,11 +135,19 @@ wsi_create_ahardware_buffer_image_mem(const struct wsi_swapchain *chain, } VkImageCreateInfo new_image_create_info = info->create; - if (ahardware_buffer_format_props.externalFormat) + if (ahardware_buffer_format_props.externalFormat && !wsi->emulate_bgra8) new_image_create_info.flags &= ~VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; new_image_create_info.format = ahardware_buffer_format_props.format; + VkEmulatedB8G8R8A8CreateInfoExt emulated_bgra8_ext = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EMULATED_B8G8R8A8_CREATE_INFO_EXT, + .pNext = new_image_create_info.pNext, + }; + if (wsi->emulate_bgra8) { + new_image_create_info.pNext = &emulated_bgra8_ext; + } + result = wsi->CreateImage(chain->device, &new_image_create_info, &chain->alloc, &image->image); @@ -302,6 +316,9 @@ wsi_create_ahardware_buffer_blit_context(const struct wsi_swapchain *chain, inline static uint32_t to_ahardware_buffer_format(VkFormat format) { switch (format) { + case VK_FORMAT_R8G8B8A8_SRGB: + case VK_FORMAT_R8G8B8A8_UNORM: + return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; case VK_FORMAT_B8G8R8A8_SRGB: case VK_FORMAT_B8G8R8A8_UNORM: return AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM; @@ -382,4 +399,3 @@ wsi_configure_android_image( return VK_SUCCESS; } - diff --git a/src/vulkan/wsi/wsi_common_x11.c b/src/vulkan/wsi/wsi_common_x11.c index 32441938..6145e16e 100644 --- a/src/vulkan/wsi/wsi_common_x11.c +++ b/src/vulkan/wsi/wsi_common_x11.c @@ -447,6 +447,8 @@ static const VkFormat formats[] = { VK_FORMAT_R5G6B5_UNORM_PACK16, VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, + VK_FORMAT_R8G8B8A8_SRGB, // Mali only supports rgba8 direct rendering + VK_FORMAT_R8G8B8A8_UNORM, // Mali only supports rgba8 direct rendering VK_FORMAT_A2R10G10B10_UNORM_PACK32, }; @@ -863,11 +865,26 @@ get_sorted_vk_formats(VkIcdSurfaceBase *surface, struct wsi_device *wsi_device, /* use the root window's visual to set the default */ *count = 0; for (unsigned i = 0; i < ARRAY_SIZE(formats); i++) { + // dxvk will prefer an rgba8 framebuffer, but x11 MUST be given + // a bgra8 one. Since we now expose rgba8 as an option for Mali + // bgra8 emulation, we need to filter them out when they're turned + // off or else dxvk buffers will have swapped blue/red channels. + if (!wsi_device->emulate_bgra8 && + (formats[i] == VK_FORMAT_R8G8B8A8_UNORM || + formats[i] == VK_FORMAT_R8G8B8A8_SRGB)) + continue; + if (rgb_component_bits_are_equal(formats[i], rootvis)) sorted_formats[(*count)++] = formats[i]; } for (unsigned i = 0; i < ARRAY_SIZE(formats); i++) { + // See above + if (!wsi_device->emulate_bgra8 && + (formats[i] == VK_FORMAT_R8G8B8A8_UNORM || + formats[i] == VK_FORMAT_R8G8B8A8_SRGB)) + continue; + for (unsigned j = 0; j < *count; j++) if (formats[i] == sorted_formats[j]) goto next_format; @@ -875,8 +892,15 @@ get_sorted_vk_formats(VkIcdSurfaceBase *surface, struct wsi_device *wsi_device, sorted_formats[(*count)++] = formats[i]; next_format:; } - - if (wsi_device->force_bgra8_unorm_first) { + if (wsi_device->emulate_bgra8) { + for (unsigned i = 0; i < *count; i++) { + if (sorted_formats[i] == VK_FORMAT_R8G8B8A8_UNORM) { + sorted_formats[i] = sorted_formats[0]; + sorted_formats[0] = VK_FORMAT_R8G8B8A8_UNORM; + break; + } + } + } else if (wsi_device->force_bgra8_unorm_first) { for (unsigned i = 0; i < *count; i++) { if (sorted_formats[i] == VK_FORMAT_B8G8R8A8_UNORM) { sorted_formats[i] = sorted_formats[0]; @@ -903,11 +927,22 @@ x11_surface_get_formats(VkIcdSurfaceBase *surface, if (!get_sorted_vk_formats(surface, wsi_device, sorted_formats, &count)) return VK_ERROR_SURFACE_LOST_KHR; - for (unsigned i = 0; i < count; i++) { + if (wsi_device->emulate_bgra8) { + vk_outarray_append_typed(VkSurfaceFormatKHR, &out, f) { + f->format = VK_FORMAT_B8G8R8A8_UNORM; + f->colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + } vk_outarray_append_typed(VkSurfaceFormatKHR, &out, f) { - f->format = sorted_formats[i]; + f->format = VK_FORMAT_B8G8R8A8_SRGB; f->colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; } + } else { + for (unsigned i = 0; i < count; i++) { + vk_outarray_append_typed(VkSurfaceFormatKHR, &out, f) { + f->format = sorted_formats[i]; + f->colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + } + } } return vk_outarray_status(&out); @@ -928,12 +963,25 @@ x11_surface_get_formats2(VkIcdSurfaceBase *surface, if (!get_sorted_vk_formats(surface, wsi_device, sorted_formats, &count)) return VK_ERROR_SURFACE_LOST_KHR; - for (unsigned i = 0; i < count; i++) { + if (wsi_device->emulate_bgra8) { + vk_outarray_append_typed(VkSurfaceFormat2KHR, &out, f) { + assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR); + f->surfaceFormat.format = VK_FORMAT_B8G8R8A8_UNORM; + f->surfaceFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + } vk_outarray_append_typed(VkSurfaceFormat2KHR, &out, f) { assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR); - f->surfaceFormat.format = sorted_formats[i]; + f->surfaceFormat.format = VK_FORMAT_B8G8R8A8_SRGB; f->surfaceFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; } + } else { + for (unsigned i = 0; i < count; i++) { + vk_outarray_append_typed(VkSurfaceFormat2KHR, &out, f) { + assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR); + f->surfaceFormat.format = sorted_formats[i]; + f->surfaceFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + } + } } return vk_outarray_status(&out);