diff --git a/src/gallium/drivers/zink/zink_kopper.c b/src/gallium/drivers/zink/zink_kopper.c new file mode 100644 index 00000000000..4122712d5e4 --- /dev/null +++ b/src/gallium/drivers/zink/zink_kopper.c @@ -0,0 +1,648 @@ +/* + * Copyright 2020 Red Hat, Inc. + * Copyright © 2021 Valve Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + + +#include "zink_context.h" +#include "zink_screen.h" +#include "zink_resource.h" +#include "zink_kopper.h" +#include "vk_enum_to_str.h" + +#define kopper_displaytarget(dt) ((struct kopper_displaytarget*)dt) + +static void +init_dt_type(struct kopper_displaytarget *cdt) +{ + VkStructureType type = cdt->info.bos.sType; + switch (type) { +#ifdef VK_USE_PLATFORM_XCB_KHR + case VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR: + cdt->type = KOPPER_X11; + break; +#endif +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + case VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR: + cdt->type = KOPPER_WAYLAND; + break; +#endif + default: + unreachable("unsupported!"); + } +} + +static VkSurfaceKHR +kopper_CreateSurface(struct zink_screen *screen, struct kopper_displaytarget *cdt) +{ + VkSurfaceKHR surface = VK_NULL_HANDLE; + VkResult error = VK_SUCCESS; + + init_dt_type(cdt); + VkStructureType type = cdt->info.bos.sType; + switch (type) { +#ifdef VK_USE_PLATFORM_XCB_KHR + case VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR: + error = VKSCR(CreateXcbSurfaceKHR)(screen->instance, &cdt->info.xcb, NULL, &surface); + break; +#endif +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + case VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR: + error = VKSCR(CreateWaylandSurfaceKHR)(screen->instance, &cdt->info.wl, NULL, &surface); + break; +#endif + default: + unreachable("unsupported!"); + } + if (error != VK_SUCCESS) { + return VK_NULL_HANDLE; + } + + VkBool32 supported; + error = VKSCR(GetPhysicalDeviceSurfaceSupportKHR)(screen->pdev, screen->gfx_queue, surface, &supported); + if (!zink_screen_handle_vkresult(screen, error) || !supported) { + VKSCR(DestroySurfaceKHR)(screen->instance, surface, NULL); + return VK_NULL_HANDLE; + } + + return surface; +} + +static void +destroy_swapchain(struct zink_screen *screen, struct kopper_swapchain *cswap) +{ + if (!cswap) + return; + free(cswap->images); + free(cswap->inits); + for (unsigned i = 0; i < cswap->num_images; i++) { + VKSCR(DestroySemaphore)(screen->dev, cswap->acquires[i], NULL); + } + free(cswap->acquires); + hash_table_foreach(cswap->presents, he) { + struct util_dynarray *arr = he->data; + while (util_dynarray_contains(arr, VkSemaphore)) + VKSCR(DestroySemaphore)(screen->dev, util_dynarray_pop(arr, VkSemaphore), NULL); + util_dynarray_fini(arr); + free(arr); + } + _mesa_hash_table_destroy(cswap->presents, NULL); + VKSCR(DestroySwapchainKHR)(screen->dev, cswap->swapchain, NULL); + free(cswap); +} + +static struct hash_entry * +find_dt_entry(struct zink_screen *screen, const struct kopper_displaytarget *cdt) +{ + struct hash_entry *he = NULL; + switch (cdt->type) { +#ifdef VK_USE_PLATFORM_XCB_KHR + case KOPPER_X11: + he = _mesa_hash_table_search_pre_hashed(&screen->dts, cdt->info.xcb.window, (void*)(uintptr_t)cdt->info.xcb.window); + break; +#endif +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + case KOPPER_WAYLAND: + he = _mesa_hash_table_search(&screen->dts, cdt->info.wl.surface); + break; +#endif + default: + unreachable("unsupported!"); + } + return he; +} + +void +zink_kopper_deinit_displaytarget(struct zink_screen *screen, struct kopper_displaytarget *cdt) +{ + if (!cdt->surface) + return; + simple_mtx_lock(&screen->dt_lock); + struct hash_entry *he = find_dt_entry(screen, cdt); + assert(he); + /* this deinits the registered entry, which should always be the "right" entry */ + cdt = he->data; + _mesa_hash_table_remove(&screen->dts, he); + simple_mtx_unlock(&screen->dt_lock); + destroy_swapchain(screen, cdt->swapchain); + destroy_swapchain(screen, cdt->old_swapchain); + VKSCR(DestroySurfaceKHR)(screen->instance, cdt->surface, NULL); + cdt->swapchain = cdt->old_swapchain = NULL; + cdt->surface = VK_NULL_HANDLE; +} + +static struct kopper_swapchain * +kopper_CreateSwapchain(struct zink_screen *screen, struct kopper_displaytarget *cdt, unsigned w, unsigned h) +{ + VkResult error = VK_SUCCESS; + struct kopper_swapchain *cswap = CALLOC_STRUCT(kopper_swapchain); + if (!cswap) + return NULL; + cswap->last_present_prune = 1; + + bool has_alpha = cdt->info.has_alpha && (cdt->caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR); + if (cdt->swapchain) { + cswap->scci = cdt->swapchain->scci; + cswap->scci.oldSwapchain = cdt->swapchain->swapchain; + } else { + cswap->scci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + cswap->scci.pNext = NULL; + cswap->scci.surface = cdt->surface; + cswap->scci.flags = zink_kopper_has_srgb(cdt) ? VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR : 0; + cswap->scci.imageFormat = cdt->formats[0]; + cswap->scci.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + // TODO: This is where you'd hook up stereo + cswap->scci.imageArrayLayers = 1; + cswap->scci.imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | + VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; + cswap->scci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + cswap->scci.queueFamilyIndexCount = 0; + cswap->scci.pQueueFamilyIndices = NULL; + cswap->scci.compositeAlpha = has_alpha ? VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + // TODO: This is where you'd hook up GLX_EXT_swap_interval and friends + cswap->scci.presentMode = cdt->type == KOPPER_X11 ? VK_PRESENT_MODE_IMMEDIATE_KHR : VK_PRESENT_MODE_FIFO_KHR; + cswap->scci.clipped = VK_TRUE; + } + cswap->scci.minImageCount = cdt->caps.minImageCount; + cswap->scci.preTransform = cdt->caps.currentTransform; + if (cdt->formats[1]) + cswap->scci.pNext = &cdt->format_list; + + /* different display platforms have, by vulkan spec, different sizing methodologies */ + switch (cdt->type) { + case KOPPER_X11: + /* With Xcb, minImageExtent, maxImageExtent, and currentExtent must always equal the window size. + * ... + * Due to above restrictions, it is only possible to create a new swapchain on this + * platform with imageExtent being equal to the current size of the window. + */ + cswap->scci.imageExtent.width = cdt->caps.currentExtent.width; + cswap->scci.imageExtent.height = cdt->caps.currentExtent.height; + break; + case KOPPER_WAYLAND: + /* On Wayland, currentExtent is the special value (0xFFFFFFFF, 0xFFFFFFFF), indicating that the + * surface size will be determined by the extent of a swapchain targeting the surface. Whatever the + * application sets a swapchain’s imageExtent to will be the size of the window, after the first image is + * presented. + */ + cswap->scci.imageExtent.width = w; + cswap->scci.imageExtent.height = h; + break; + default: + unreachable("unknown display platform"); + } + + error = VKSCR(CreateSwapchainKHR)(screen->dev, &cswap->scci, NULL, + &cswap->swapchain); + if (error == VK_ERROR_NATIVE_WINDOW_IN_USE_KHR) { + if (util_queue_is_initialized(&screen->flush_queue)) + util_queue_finish(&screen->flush_queue); + if (VKSCR(QueueWaitIdle)(screen->queue) != VK_SUCCESS) + debug_printf("vkQueueWaitIdle failed\n"); + zink_kopper_deinit_displaytarget(screen, cdt); + error = VKSCR(CreateSwapchainKHR)(screen->dev, &cswap->scci, NULL, + &cswap->swapchain); + } + if (error != VK_SUCCESS) { + mesa_loge("CreateSwapchainKHR failed with %s\n", vk_Result_to_str(error)); + free(cswap); + return NULL; + } + cswap->max_acquires = cswap->scci.minImageCount - cdt->caps.minImageCount; + cswap->last_present = UINT32_MAX; + + return cswap; +} + +static bool +kopper_GetSwapchainImages(struct zink_screen *screen, struct kopper_swapchain *cswap) +{ + VkResult error = VKSCR(GetSwapchainImagesKHR)(screen->dev, cswap->swapchain, &cswap->num_images, NULL); + if (!zink_screen_handle_vkresult(screen, error)) + return false; + cswap->images = malloc(sizeof(VkImage) * cswap->num_images); + cswap->acquires = calloc(cswap->num_images, sizeof(VkSemaphore)); + cswap->inits = calloc(cswap->num_images, sizeof(bool)); + cswap->presents = _mesa_hash_table_create_u32_keys(NULL); + error = VKSCR(GetSwapchainImagesKHR)(screen->dev, cswap->swapchain, &cswap->num_images, cswap->images); + return zink_screen_handle_vkresult(screen, error); +} + +static bool +update_caps(struct zink_screen *screen, struct kopper_displaytarget *cdt) +{ + VkResult error = VKSCR(GetPhysicalDeviceSurfaceCapabilitiesKHR)(screen->pdev, cdt->surface, &cdt->caps); + return zink_screen_handle_vkresult(screen, error); +} + +static bool +update_swapchain(struct zink_screen *screen, struct kopper_displaytarget *cdt, unsigned w, unsigned h) +{ + if (!update_caps(screen, cdt)) + return false; + struct kopper_swapchain *cswap = kopper_CreateSwapchain(screen, cdt, w, h); + if (!cswap) + return false; + destroy_swapchain(screen, cdt->old_swapchain); + cdt->old_swapchain = cdt->swapchain; + cdt->swapchain = cswap; + + return kopper_GetSwapchainImages(screen, cdt->swapchain); +} + +struct kopper_displaytarget * +zink_kopper_displaytarget_create(struct zink_screen *screen, unsigned tex_usage, + enum pipe_format format, unsigned width, + unsigned height, unsigned alignment, + const void *loader_private, unsigned *stride) +{ + struct kopper_displaytarget *cdt; + const struct kopper_loader_info *info = loader_private; + + { + struct kopper_displaytarget k; + struct hash_entry *he = NULL; + k.info = *info; + init_dt_type(&k); + simple_mtx_lock(&screen->dt_lock); + if (unlikely(!screen->dts.table)) { + switch (k.type) { + case KOPPER_X11: + _mesa_hash_table_init(&screen->dts, screen, NULL, _mesa_key_pointer_equal); + break; + case KOPPER_WAYLAND: + _mesa_hash_table_init(&screen->dts, screen, _mesa_hash_pointer, _mesa_key_pointer_equal); + break; + default: + unreachable("unknown kopper type"); + } + } else { + he = find_dt_entry(screen, &k); + } + simple_mtx_unlock(&screen->dt_lock); + if (he) { + cdt = he->data; + p_atomic_inc(&cdt->refcount); + *stride = cdt->stride; + return cdt; + } + } + + cdt = CALLOC_STRUCT(kopper_displaytarget); + if (!cdt) + return NULL; + + cdt->refcount = 1; + cdt->loader_private = (void*)loader_private; + cdt->info = *info; + + enum pipe_format srgb = PIPE_FORMAT_NONE; + if (screen->info.have_KHR_swapchain_mutable_format) { + srgb = util_format_is_srgb(format) ? util_format_linear(format) : util_format_srgb(format); + /* why do these helpers have different default return values? */ + if (srgb == format) + srgb = PIPE_FORMAT_NONE; + } + cdt->formats[0] = zink_get_format(screen, format); + if (srgb) { + cdt->format_list.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO; + cdt->format_list.pNext = NULL; + cdt->format_list.viewFormatCount = 2; + cdt->format_list.pViewFormats = cdt->formats; + + cdt->formats[1] = zink_get_format(screen, srgb); + } + + cdt->surface = kopper_CreateSurface(screen, cdt); + if (!cdt->surface) + goto out; + + if (!update_swapchain(screen, cdt, width, height)) + goto out; + + simple_mtx_lock(&screen->dt_lock); + switch (cdt->type) { +#ifdef VK_USE_PLATFORM_XCB_KHR + case KOPPER_X11: + _mesa_hash_table_insert_pre_hashed(&screen->dts, cdt->info.xcb.window, (void*)(uintptr_t)cdt->info.xcb.window, cdt); + break; +#endif +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + case KOPPER_WAYLAND: + _mesa_hash_table_insert(&screen->dts, cdt->info.wl.surface, cdt); + break; +#endif + default: + unreachable("unsupported!"); + } + simple_mtx_unlock(&screen->dt_lock); + + *stride = cdt->stride; + return cdt; + +//moar cleanup +out: + return NULL; +} + +void +zink_kopper_displaytarget_destroy(struct zink_screen *screen, struct kopper_displaytarget *cdt) +{ + if (!p_atomic_dec_zero(&cdt->refcount)) + return; + zink_kopper_deinit_displaytarget(screen, cdt); + FREE(cdt); +} + +static bool +kopper_acquire(struct zink_screen *screen, struct zink_resource *res, uint64_t timeout) +{ + struct kopper_displaytarget *cdt = kopper_displaytarget(res->obj->dt); + if (res->obj->acquire) + return true; + res->obj->acquire = VK_NULL_HANDLE; + VkSemaphore acquire = VK_NULL_HANDLE; + if (res->obj->new_dt) { +update_swapchain: + if (!update_swapchain(screen, cdt, res->base.b.width0, res->base.b.height0)) { + //??? + } + res->obj->new_dt = false; + res->layout = VK_IMAGE_LAYOUT_UNDEFINED; + res->obj->access = 0; + res->obj->access_stage = 0; + } + if (timeout == UINT64_MAX && util_queue_is_initialized(&screen->flush_queue) && + p_atomic_read_relaxed(&cdt->swapchain->num_acquires) > cdt->swapchain->max_acquires) { + util_queue_fence_wait(&res->obj->present_fence); + } + VkSemaphoreCreateInfo sci = { + VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + NULL, + 0 + }; + VkResult ret; + if (!acquire) { + ret = VKSCR(CreateSemaphore)(screen->dev, &sci, NULL, &acquire); + assert(acquire); + if (ret != VK_SUCCESS) + return false; + } + ASSERTED unsigned prev = res->obj->dt_idx; + ret = VKSCR(AcquireNextImageKHR)(screen->dev, cdt->swapchain->swapchain, timeout, acquire, VK_NULL_HANDLE, &res->obj->dt_idx); + if (ret != VK_SUCCESS && ret != VK_SUBOPTIMAL_KHR) { + if (ret == VK_ERROR_OUT_OF_DATE_KHR) + goto update_swapchain; + VKSCR(DestroySemaphore)(screen->dev, acquire, NULL); + return false; + } + assert(prev != res->obj->dt_idx); + cdt->swapchain->acquires[res->obj->dt_idx] = res->obj->acquire = acquire; + res->obj->image = cdt->swapchain->images[res->obj->dt_idx]; + res->obj->acquired = false; + if (!cdt->swapchain->inits[res->obj->dt_idx]) { + /* swapchain images are initially in the UNDEFINED layout */ + res->layout = VK_IMAGE_LAYOUT_UNDEFINED; + cdt->swapchain->inits[res->obj->dt_idx] = true; + } + if (timeout == UINT64_MAX) { + res->obj->indefinite_acquire = true; + p_atomic_inc(&cdt->swapchain->num_acquires); + } + res->obj->dt_has_data = false; + return ret == VK_SUCCESS; +} + +bool +zink_kopper_acquire(struct zink_context *ctx, struct zink_resource *res, uint64_t timeout) +{ + assert(res->obj->dt); + struct kopper_displaytarget *cdt = kopper_displaytarget(res->obj->dt); + const struct kopper_swapchain *cswap = cdt->swapchain; + res->obj->new_dt |= res->base.b.width0 != cswap->scci.imageExtent.width || + res->base.b.height0 != cswap->scci.imageExtent.height; + bool ret = kopper_acquire(zink_screen(ctx->base.screen), res, timeout); + if (cswap != cdt->swapchain) + ctx->swapchain_size = cdt->swapchain->scci.imageExtent; + return ret; +} + +VkSemaphore +zink_kopper_acquire_submit(struct zink_screen *screen, struct zink_resource *res) +{ + assert(res->obj->dt); + struct kopper_displaytarget *cdt = kopper_displaytarget(res->obj->dt); + if (res->obj->acquired) + return VK_NULL_HANDLE; + assert(res->obj->acquire); + res->obj->acquired = true; + /* this is now owned by the batch */ + cdt->swapchain->acquires[res->obj->dt_idx] = VK_NULL_HANDLE; + return res->obj->acquire; +} + +VkSemaphore +zink_kopper_present(struct zink_screen *screen, struct zink_resource *res) +{ + assert(res->obj->dt); + assert(!res->obj->present); + VkSemaphoreCreateInfo sci = { + VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + NULL, + 0 + }; + assert(res->obj->acquired); + VkResult ret = VKSCR(CreateSemaphore)(screen->dev, &sci, NULL, &res->obj->present); + return zink_screen_handle_vkresult(screen, ret) ? res->obj->present : VK_NULL_HANDLE; +} + +struct kopper_present_info { + VkPresentInfoKHR info; + uint32_t image; + struct zink_resource *res; + VkSemaphore sem; + bool indefinite_acquire; +}; + +static void +kopper_present(void *data, void *gdata, int thread_idx) +{ + struct kopper_present_info *cpi = data; + struct kopper_displaytarget *cdt = cpi->res->obj->dt; + struct zink_screen *screen = gdata; + VkResult error; + cpi->info.pResults = &error; + + simple_mtx_lock(&screen->queue_lock); + VkResult error2 = VKSCR(QueuePresentKHR)(screen->thread_queue, &cpi->info); + simple_mtx_unlock(&screen->queue_lock); + cdt->swapchain->last_present = cpi->image; + if (cpi->indefinite_acquire) + p_atomic_dec(&cdt->swapchain->num_acquires); + if (error2 == VK_SUBOPTIMAL_KHR) + cpi->res->obj->new_dt = true; + + /* it's illegal to destroy semaphores if they're in use by a cmdbuf. + * but what does "in use" actually mean? + * in truth, when using timelines, nobody knows. especially not VVL. + * + * thus, to avoid infinite error spam and thread-related races, + * present semaphores need their own free queue based on the + * last-known completed timeline id so that the semaphore persists through + * normal cmdbuf submit/signal and then also exists here when it's needed for the present operation + */ + struct util_dynarray *arr; + for (; screen->last_finished && cdt->swapchain->last_present_prune != screen->last_finished; cdt->swapchain->last_present_prune++) { + struct hash_entry *he = _mesa_hash_table_search(cdt->swapchain->presents, + (void*)(uintptr_t)cdt->swapchain->last_present_prune); + if (he) { + arr = he->data; + while (util_dynarray_contains(arr, VkSemaphore)) + VKSCR(DestroySemaphore)(screen->dev, util_dynarray_pop(arr, VkSemaphore), NULL); + util_dynarray_fini(arr); + free(arr); + _mesa_hash_table_remove(cdt->swapchain->presents, he); + } + } + /* queue this wait semaphore for deletion on completion of the next batch */ + assert(screen->curr_batch > 0); + uint32_t next = screen->curr_batch + 1; + struct hash_entry *he = _mesa_hash_table_search(cdt->swapchain->presents, (void*)(uintptr_t)next); + if (he) + arr = he->data; + else { + arr = malloc(sizeof(struct util_dynarray)); + util_dynarray_init(arr, NULL); + _mesa_hash_table_insert(cdt->swapchain->presents, (void*)(uintptr_t)next, arr); + } + util_dynarray_append(arr, VkSemaphore, cpi->sem); + free(cpi); +} + +void +zink_kopper_present_queue(struct zink_screen *screen, struct zink_resource *res) +{ + assert(res->obj->dt); + struct kopper_displaytarget *cdt = kopper_displaytarget(res->obj->dt); + assert(res->obj->acquired); + assert(res->obj->present); + struct kopper_present_info *cpi = malloc(sizeof(struct kopper_present_info)); + cpi->sem = res->obj->present; + cpi->res = res; + cpi->indefinite_acquire = res->obj->indefinite_acquire; + res->obj->last_dt_idx = cpi->image = res->obj->dt_idx; + cpi->info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + cpi->info.pNext = NULL; + cpi->info.waitSemaphoreCount = 1; + cpi->info.pWaitSemaphores = &cpi->sem; + cpi->info.swapchainCount = 1; + cpi->info.pSwapchains = &cdt->swapchain->swapchain; + cpi->info.pImageIndices = &cpi->image; + cpi->info.pResults = NULL; + res->obj->present = VK_NULL_HANDLE; + if (util_queue_is_initialized(&screen->flush_queue)) { + util_queue_add_job(&screen->flush_queue, cpi, &res->obj->present_fence, + kopper_present, NULL, 0); + } else { + kopper_present(cpi, screen, 0); + } + res->obj->acquire = VK_NULL_HANDLE; + res->obj->indefinite_acquire = res->obj->acquired = false; + res->obj->dt_idx = UINT32_MAX; +} + +bool +zink_kopper_acquire_readback(struct zink_context *ctx, struct zink_resource *res) +{ + struct zink_screen *screen = zink_screen(ctx->base.screen); + assert(res->obj->dt); + struct kopper_displaytarget *cdt = kopper_displaytarget(res->obj->dt); + const struct kopper_swapchain *cswap = cdt->swapchain; + uint32_t last_dt_idx = res->obj->last_dt_idx; + if (!res->obj->acquire) + kopper_acquire(screen, res, UINT64_MAX); + /* if this hasn't been presented or if it has data, use this as the readback target */ + if (res->obj->last_dt_idx == UINT32_MAX || res->obj->dt_has_data) + return false; + while (res->obj->dt_idx != last_dt_idx) { + if (!zink_kopper_present_readback(ctx, res)) + break; + while (!kopper_acquire(screen, res, 0)); + } + if (cswap != cdt->swapchain) + ctx->swapchain_size = cdt->swapchain->scci.imageExtent; + return true; +} + +bool +zink_kopper_present_readback(struct zink_context *ctx, struct zink_resource *res) +{ + struct zink_screen *screen = zink_screen(ctx->base.screen); + VkSubmitInfo si = {0}; + if (res->obj->last_dt_idx == UINT32_MAX) + return true; + if (res->layout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { + zink_resource_image_barrier(ctx, res, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 0, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); + ctx->base.flush(&ctx->base, NULL, 0); + } + si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + si.signalSemaphoreCount = 1; + VkPipelineStageFlags mask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + si.pWaitDstStageMask = &mask; + VkSemaphore acquire = zink_kopper_acquire_submit(screen, res); + VkSemaphore present = zink_kopper_present(screen, res); + if (screen->threaded) + util_queue_finish(&screen->flush_queue); + si.waitSemaphoreCount = !!acquire; + si.pWaitSemaphores = &acquire; + si.pSignalSemaphores = &present; + VkResult error = VKSCR(QueueSubmit)(screen->thread_queue, 1, &si, VK_NULL_HANDLE); + if (!zink_screen_handle_vkresult(screen, error)) + return false; + + zink_kopper_present_queue(screen, res); + error = VKSCR(QueueWaitIdle)(screen->queue); + return zink_screen_handle_vkresult(screen, error); +} + +bool +zink_kopper_update(struct pipe_screen *pscreen, struct pipe_resource *pres, int *w, int *h) +{ + struct zink_resource *res = zink_resource(pres); + struct zink_screen *screen = zink_screen(pscreen); + assert(res->obj->dt); + struct kopper_displaytarget *cdt = kopper_displaytarget(res->obj->dt); + if (cdt->type != KOPPER_X11) { + *w = res->base.b.width0; + *h = res->base.b.height0; + return true; + } + if (!update_caps(screen, cdt)) { + debug_printf("zink: failed to update swapchain capabilities"); + return false; + } + *w = cdt->caps.currentExtent.width; + *h = cdt->caps.currentExtent.height; + return true; +} diff --git a/src/gallium/drivers/zink/zink_kopper.h b/src/gallium/drivers/zink/zink_kopper.h new file mode 100644 index 00000000000..1d0b834bca7 --- /dev/null +++ b/src/gallium/drivers/zink/zink_kopper.h @@ -0,0 +1,109 @@ +/* + * Copyright © 2021 Valve Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Mike Blumenkrantz + */ + +#ifndef ZINK_KOPPER_H +#define ZINK_KOPPER_H + +#include "kopper_interface.h" + +struct kopper_swapchain { + VkSwapchainKHR swapchain; + VkImage *images; + bool *inits; + unsigned last_present; + unsigned num_images; + VkSemaphore *acquires; + uint32_t last_present_prune; + struct hash_table *presents; + VkSwapchainCreateInfoKHR scci; + unsigned num_acquires; + unsigned max_acquires; +}; + +enum kopper_type { + KOPPER_X11, + KOPPER_WAYLAND, +}; + +struct kopper_displaytarget +{ + unsigned refcount; + VkFormat formats[2]; + unsigned width; + unsigned height; + unsigned stride; + void *loader_private; + + VkSurfaceKHR surface; + struct kopper_swapchain *swapchain; + struct kopper_swapchain *old_swapchain; + + struct kopper_loader_info info; + + VkSurfaceCapabilitiesKHR caps; + VkImageFormatListCreateInfoKHR format_list; + enum kopper_type type; +}; + +struct zink_screen; +struct zink_resource; + +static inline bool +zink_kopper_has_srgb(const struct kopper_displaytarget *cdt) +{ + return cdt->formats[1] != VK_FORMAT_UNDEFINED; +} + +static inline bool +zink_kopper_last_present_eq(const struct kopper_displaytarget *cdt, uint32_t idx) +{ + return cdt->swapchain->last_present == idx; +} + +struct kopper_displaytarget * +zink_kopper_displaytarget_create(struct zink_screen *screen, unsigned tex_usage, + enum pipe_format format, unsigned width, + unsigned height, unsigned alignment, + const void *loader_private, unsigned *stride); +void +zink_kopper_displaytarget_destroy(struct zink_screen *screen, struct kopper_displaytarget *cdt); + + +bool +zink_kopper_acquire(struct zink_context *ctx, struct zink_resource *res, uint64_t timeout); +VkSemaphore +zink_kopper_acquire_submit(struct zink_screen *screen, struct zink_resource *res); +VkSemaphore +zink_kopper_present(struct zink_screen *screen, struct zink_resource *res); +void +zink_kopper_present_queue(struct zink_screen *screen, struct zink_resource *res); +bool +zink_kopper_acquire_readback(struct zink_context *ctx, struct zink_resource *res); +bool +zink_kopper_present_readback(struct zink_context *ctx, struct zink_resource *res); +void +zink_kopper_deinit_displaytarget(struct zink_screen *screen, struct kopper_displaytarget *cdt); +#endif