/*
 * Copyright © Microsoft 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 "d3d12_screen.h"

#include "d3d12_bufmgr.h"
#ifdef HAVE_GALLIUM_D3D12_GRAPHICS
#include "d3d12_compiler.h"
#include "d3d12_nir_passes.h"
#include "nir.h"
#include "nir_to_dxil.h"
#endif // HAVE_GALLIUM_D3D12_GRAPHICS
#include "d3d12_context.h"
#include "d3d12_debug.h"
#include "d3d12_fence.h"
#ifdef HAVE_GALLIUM_D3D12_VIDEO
#include "d3d12_video_screen.h"
#endif
#include "d3d12_format.h"
#include "d3d12_interop_public.h"
#include "d3d12_residency.h"
#include "d3d12_resource.h"
#include "pipebuffer/pb_bufmgr.h"
#include "util/u_debug.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "util/u_screen.h"
#include "util/u_dl.h"
#include "util/mesa-blake3.h"

#include "frontend/sw_winsys.h"

#include "git_sha1.h"

#ifndef _GAMING_XBOX
#include <directx/d3d12sdklayers.h>
#endif

#if defined(_WIN32) && defined(_WIN64) && !defined(_GAMING_XBOX)
#include <filesystem>
#include <shlobj.h>
#endif

#include <dxguids/dxguids.h>
static GUID OpenGLOn12CreatorID = { 0x6bb3cd34, 0x0d19, 0x45ab, { 0x97, 0xed, 0xd7, 0x20, 0xba, 0x3d, 0xfc, 0x80 } };

static const struct debug_named_value
d3d12_debug_options[] = {
   { "verbose",      D3D12_DEBUG_VERBOSE,       NULL },
   { "blit",         D3D12_DEBUG_BLIT,          "Trace blit and copy resource calls" },
   { "experimental", D3D12_DEBUG_EXPERIMENTAL,  "Enable experimental shader models feature" },
   { "dxil",         D3D12_DEBUG_DXIL,          "Dump DXIL during program compile" },
   { "disass",       D3D12_DEBUG_DISASS,        "Dump disassambly of created DXIL shader" },
   { "res",          D3D12_DEBUG_RESOURCE,      "Debug resources" },
   { "debuglayer",   D3D12_DEBUG_DEBUG_LAYER,   "Enable debug layer" },
   { "gpuvalidator", D3D12_DEBUG_GPU_VALIDATOR, "Enable GPU validator" },
   { "singleton",    D3D12_DEBUG_SINGLETON,     "Disallow use of device factory" },
   { "pix",          D3D12_DEBUG_PIX,           "Load WinPixGpuCaptuerer.dll" },
   DEBUG_NAMED_VALUE_END
};

DEBUG_GET_ONCE_FLAGS_OPTION(d3d12_debug, "D3D12_DEBUG", d3d12_debug_options, 0)

uint32_t
d3d12_debug;

enum {
    HW_VENDOR_AMD                   = 0x1002,
    HW_VENDOR_INTEL                 = 0x8086,
    HW_VENDOR_MICROSOFT             = 0x1414,
    HW_VENDOR_NVIDIA                = 0x10de,
};

static const char *
d3d12_get_vendor(struct pipe_screen *pscreen)
{
   return "Microsoft Corporation";
}

static const char *
d3d12_get_device_vendor(struct pipe_screen *pscreen)
{
   struct d3d12_screen* screen = d3d12_screen(pscreen);

   switch (screen->vendor_id) {
   case HW_VENDOR_MICROSOFT:
      return "Microsoft";
   case HW_VENDOR_AMD:
      return "AMD";
   case HW_VENDOR_NVIDIA:
      return "NVIDIA";
   case HW_VENDOR_INTEL:
      return "Intel";
   default:
      return "Unknown";
   }
}

static int
d3d12_get_video_mem(struct pipe_screen *pscreen)
{
   struct d3d12_screen* screen = d3d12_screen(pscreen);

   return static_cast<int>(screen->memory_device_size_megabytes + screen->memory_system_size_megabytes);
}

static void
d3d12_init_shader_caps(struct d3d12_screen *screen)
{
   for (unsigned i = 0; i <= MESA_SHADER_COMPUTE; i++) {
      struct pipe_shader_caps *caps =
         (struct pipe_shader_caps *)&screen->base.shader_caps[i];

      caps->max_instructions =
      caps->max_alu_instructions =
      caps->max_tex_instructions =
      caps->max_tex_indirections =
      caps->max_control_flow_depth = INT_MAX;

      switch (i) {
      case MESA_SHADER_VERTEX:
         caps->max_inputs = D3D12_VS_INPUT_REGISTER_COUNT;
         caps->max_outputs = D3D12_VS_OUTPUT_REGISTER_COUNT;
         break;
      case MESA_SHADER_FRAGMENT:
         caps->max_inputs = D3D12_PS_INPUT_REGISTER_COUNT;
         caps->max_outputs = D3D12_PS_OUTPUT_REGISTER_COUNT;
         break;
      case MESA_SHADER_GEOMETRY:
         caps->max_inputs = D3D12_GS_INPUT_REGISTER_COUNT;
         caps->max_outputs = D3D12_GS_OUTPUT_REGISTER_COUNT;
         break;
      case MESA_SHADER_TESS_CTRL:
         caps->max_inputs = D3D12_HS_CONTROL_POINT_PHASE_INPUT_REGISTER_COUNT;
         caps->max_outputs = D3D12_HS_CONTROL_POINT_PHASE_OUTPUT_REGISTER_COUNT;
         break;
      case MESA_SHADER_TESS_EVAL:
         caps->max_inputs = D3D12_DS_INPUT_CONTROL_POINT_REGISTER_COUNT;
         caps->max_outputs = D3D12_DS_OUTPUT_REGISTER_COUNT;
         break;
      default:
         break;
      }

      caps->max_texture_samplers =
         screen->opts.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_1 ?
         16 : PIPE_MAX_SAMPLERS;

      caps->max_const_buffer0_size = 65536;

      caps->max_const_buffers =
         screen->opts.ResourceBindingTier < D3D12_RESOURCE_BINDING_TIER_3 ?
         13 /* 15 - 2 for lowered uniforms and state vars*/ : 15;

      caps->max_temps = INT_MAX;

      caps->indirect_const_addr = true;
      caps->integers = true;

      /* Note: This is wrong, but this is the max value that
       * TC can support to avoid overflowing an array.
       */
      caps->max_sampler_views = PIPE_MAX_SAMPLERS;

      caps->max_shader_buffers =
         (screen->max_feature_level >= D3D_FEATURE_LEVEL_11_1 ||
          screen->opts.ResourceBindingTier >= D3D12_RESOURCE_BINDING_TIER_3) ?
         PIPE_MAX_SHADER_BUFFERS : D3D12_PS_CS_UAV_REGISTER_COUNT;

      caps->supported_irs = 1 << PIPE_SHADER_IR_NIR;

      if (screen->support_shader_images) {
         caps->max_shader_images =
            (screen->max_feature_level >= D3D_FEATURE_LEVEL_11_1 ||
             screen->opts.ResourceBindingTier >= D3D12_RESOURCE_BINDING_TIER_3) ?
            PIPE_MAX_SHADER_IMAGES : D3D12_PS_CS_UAV_REGISTER_COUNT;
      }
   }
}

static void
d3d12_init_compute_caps(struct d3d12_screen *screen)
{
   struct pipe_compute_caps *caps =
      (struct pipe_compute_caps *)&screen->base.compute_caps;

   caps->max_grid_size[0] =
   caps->max_grid_size[1] =
   caps->max_grid_size[2] = D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION;

   caps->max_block_size[0] = D3D12_CS_THREAD_GROUP_MAX_X;
   caps->max_block_size[1] = D3D12_CS_THREAD_GROUP_MAX_Y;
   caps->max_block_size[2] = D3D12_CS_THREAD_GROUP_MAX_Z;

   caps->max_variable_threads_per_block =
   caps->max_threads_per_block = D3D12_CS_THREAD_GROUP_MAX_THREADS_PER_GROUP;

   caps->max_local_size = D3D12_CS_TGSM_REGISTER_COUNT /*DWORDs*/ * 4;
}

static void
d3d12_init_screen_caps(struct d3d12_screen *screen)
{
   struct pipe_caps *caps = (struct pipe_caps *)&screen->base.caps;

   caps->accelerated = screen->vendor_id != HW_VENDOR_MICROSOFT ? 1 : 0;
   caps->uma = screen->architecture.UMA;
   caps->video_memory = d3d12_get_video_mem(&screen->base);

   if (screen->max_feature_level < D3D_FEATURE_LEVEL_11_0)
      return;

   u_init_pipe_screen_caps(&screen->base, caps->accelerated);

   caps->prefer_real_buffer_in_constbuf0 = true;
   caps->npot_textures = true;

   /* D3D12 only supports dual-source blending for a single
    * render-target. From the D3D11 functional spec (which also defines
    * this for D3D12):
    *
    * "When Dual Source Color Blending is enabled, the Pixel Shader must
    *  have only a single RenderTarget bound, at slot 0, and must output
    *  both o0 and o1. Writing to other outputs (o2, o3 etc.) produces
    *  undefined results for the corresponding RenderTargets, if bound
    *  illegally."
    *
    * Source: https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#17.6%20Dual%20Source%20Color%20Blending
    */
   caps->max_dual_source_render_targets = 1;

   caps->anisotropic_filter = true;

   caps->max_render_targets = D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT;

   caps->texture_swizzle = true;

   caps->max_texel_buffer_elements = 1 << D3D12_REQ_BUFFER_RESOURCE_TEXEL_COUNT_2_TO_EXP;

   caps->max_texture_2d_size = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION;

   static_assert(D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION == (1 << 11),
                 "D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION");
   caps->max_texture_3d_levels = 12;

   caps->max_texture_cube_levels = D3D12_REQ_MIP_LEVELS;

   caps->primitive_restart = true;
   caps->indep_blend_enable = true;
   caps->indep_blend_func = true;
   caps->fragment_shader_texture_lod = true;
   caps->fragment_shader_derivatives = true;
   caps->quads_follow_provoking_vertex_convention = true;
   caps->mixed_color_depth_bits = true;

   caps->vertex_input_alignment = PIPE_VERTEX_INPUT_ALIGNMENT_4BYTE;

   /* We need to do some lowering that requires a link to the sampler */
   caps->nir_samplers_as_deref = true;

   caps->nir_images_as_deref = true;

   caps->max_texture_array_layers = D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION;

   caps->depth_clip_disable = true;

   caps->tgsi_texcoord = true;

   caps->vertex_color_unclamped = true;

   caps->glsl_feature_level = 460;
   caps->glsl_feature_level_compatibility = 460;
   caps->essl_feature_level = 310;

   caps->compute = true;

   caps->texture_multisample = true;

   caps->cube_map_array = true;

   caps->texture_buffer_objects = true;

   caps->texture_transfer_modes = PIPE_TEXTURE_TRANSFER_BLIT;

   caps->endianness = PIPE_ENDIAN_NATIVE; /* unsure */

   caps->max_viewports = D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;

   caps->mixed_framebuffer_sizes = true;

   caps->max_texture_gather_components = 4;

   caps->fs_coord_pixel_center_half_integer = true;
   caps->fs_coord_origin_upper_left = true;

   caps->max_vertex_attrib_stride = 2048; /* FIXME: no clue how to query this */

   caps->texture_float_linear = true;
   caps->texture_half_float_linear = true;

   caps->shader_buffer_offset_alignment = D3D12_RAW_UAV_SRV_BYTE_ALIGNMENT;

   caps->constant_buffer_offset_alignment = D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT;

   caps->pci_group =
   caps->pci_bus =
   caps->pci_device =
   caps->pci_function = 0; /* TODO: figure these out */

   caps->flatshade = false;
   caps->alpha_test = false;
   caps->two_sided_color = false;
   caps->clip_planes = 0;

   caps->shader_stencil_export = screen->opts.PSSpecifiedStencilRefSupported;

   caps->seamless_cube_map = true;
   caps->texture_query_lod = true;
   caps->vs_instanceid = true;
   caps->tgsi_tex_txf_lz = true;
   caps->occlusion_query = true;
   caps->viewport_transform_lowered = true;
   caps->psiz_clamped = true;
   caps->blend_equation_separate = true;
   caps->conditional_render = true;
   caps->conditional_render_inverted = true;
   caps->query_timestamp = true;
   caps->vertex_element_instance_divisor = true;
   caps->image_store_formatted = true;
   caps->glsl_tess_levels_as_inputs = true;

   caps->max_stream_output_buffers = D3D12_SO_BUFFER_SLOT_COUNT;

   caps->max_stream_output_separate_components =
   caps->max_stream_output_interleaved_components = D3D12_SO_OUTPUT_COMPONENT_COUNT;

   /* Geometry shader output. */
   caps->max_geometry_output_vertices =
      D3D12_GS_MAX_OUTPUT_VERTEX_COUNT_ACROSS_INSTANCES;
   caps->max_geometry_total_output_components =
      D3D12_REQ_GS_INVOCATION_32BIT_OUTPUT_COMPONENT_LIMIT;

   /* Subtract one so that implicit position can be added */
   caps->max_varyings = D3D12_PS_INPUT_REGISTER_COUNT - 1;

   caps->max_combined_shader_output_resources =
      screen->max_feature_level <= D3D_FEATURE_LEVEL_11_0 ? D3D12_PS_CS_UAV_REGISTER_COUNT :
      (screen->opts.ResourceBindingTier <= D3D12_RESOURCE_BINDING_TIER_2 ? D3D12_UAV_SLOT_COUNT : 0);

   caps->start_instance = true;
   caps->draw_parameters = true;
   caps->draw_indirect = true;
   caps->multi_draw_indirect = true;
   caps->multi_draw_indirect_params = true;
   caps->framebuffer_no_attachment = true;
   caps->sample_shading = true;
   caps->stream_output_pause_resume = true;
   caps->stream_output_interleave_buffers = true;
   caps->int64 = true;
   caps->doubles = true;
   caps->device_reset_status_query = true;
   caps->robust_buffer_access_behavior = true;
   caps->memobj = true;
   caps->fence_signal = true;
   caps->timeline_semaphore_import = true;
   caps->clip_halfz = true;
   caps->vs_layer_viewport = true;
   caps->copy_between_compressed_and_plain_formats = true;
   caps->shader_array_components = true;
   caps->texture_mirror_clamp_to_edge = true;
   caps->query_time_elapsed = true;
   caps->fs_fine_derivative = true;
   caps->cull_distance = true;
   caps->texture_query_samples = true;
   caps->texture_barrier = true;
   caps->gl_spirv = true;
   caps->polygon_offset_clamp = true;
   caps->shader_group_vote = true;
   caps->shader_ballot = true;
   caps->query_pipeline_statistics = true;
   caps->query_so_overflow = true;

   caps->query_buffer_object =
      (screen->opts3.WriteBufferImmediateSupportFlags & D3D12_COMMAND_LIST_SUPPORT_FLAG_DIRECT) != 0;

   caps->max_vertex_streams = D3D12_SO_BUFFER_SLOT_COUNT;

   /* This is asking about varyings, not total registers, so remove the 2 tess factor registers. */
   caps->max_shader_patch_varyings = D3D12_HS_OUTPUT_PATCH_CONSTANT_REGISTER_COUNT - 2;

   /* Picking a value in line with other drivers. Without this, we can end up easily hitting OOM
    * if an app just creates, initializes, and destroys resources without explicitly flushing. */
   caps->max_texture_upload_memory_budget = 64 * 1024 * 1024;

   caps->sampler_view_target = screen->opts12.RelaxedFormatCastingSupported;

#ifndef _GAMING_XBOX
   caps->query_memory_info = true;
#endif

   caps->min_line_width =
   caps->min_line_width_aa =
   caps->min_point_size =
   caps->min_point_size_aa = 1;

   caps->point_size_granularity =
   caps->line_width_granularity = 0.1f;

   caps->max_line_width =
   caps->max_line_width_aa = 1.0f; /* no clue */

#ifdef HAVE_GALLIUM_D3D12_GRAPHICS
   caps->max_point_size =
   caps->max_point_size_aa = D3D12_MAX_POINT_SIZE;
#endif // HAVE_GALLIUM_D3D12_GRAPHICS

   caps->max_texture_anisotropy = D3D12_MAX_MAXANISOTROPY;

   caps->max_texture_lod_bias = 15.99f;
   caps->packed_uniforms = true;
}

static bool
d3d12_is_format_supported(struct pipe_screen *pscreen,
                          enum pipe_format format,
                          enum pipe_texture_target target,
                          unsigned sample_count,
                          unsigned storage_sample_count,
                          unsigned bind)
{
   struct d3d12_screen *screen = d3d12_screen(pscreen);

   if (MAX2(1, sample_count) != MAX2(1, storage_sample_count))
      return false;

   if (target == PIPE_BUFFER) {
      /* Replace emulated vertex element formats for the tests */
      format = d3d12_emulated_vtx_format(format);
   } else {
      /* Allow 3-comp 32 bit formats only for BOs (needed for ARB_tbo_rgb32) */
      if ((format == PIPE_FORMAT_R32G32B32_FLOAT ||
           format == PIPE_FORMAT_R32G32B32_SINT ||
           format == PIPE_FORMAT_R32G32B32_UINT))
         return false;
   }

   /* Don't advertise alpha/luminance_alpha formats because they can't be used
    * for render targets (except A8_UNORM) and can't be emulated by R/RG formats.
    * Let the state tracker choose an RGBA format instead. For YUV formats, we
    * want the state tracker to lower these to individual planes. */
   if (format != PIPE_FORMAT_A8_UNORM &&
       (util_format_is_alpha(format) ||
        util_format_is_luminance_alpha(format) ||
        util_format_is_yuv(format)))
      return false;

   if (format == PIPE_FORMAT_NONE) {
      /* For UAV-only rendering, aka ARB_framebuffer_no_attachments */
      switch (sample_count) {
      case 0:
      case 1:
      case 4:
      case 8:
      case 16:
         return true;
      default:
         return false;
      }
   }

   DXGI_FORMAT dxgi_format = d3d12_get_format(format);
   if (dxgi_format == DXGI_FORMAT_UNKNOWN)
      return false;

   enum D3D12_FORMAT_SUPPORT1 dim_support = D3D12_FORMAT_SUPPORT1_NONE;
   switch (target) {
   case PIPE_TEXTURE_1D:
   case PIPE_TEXTURE_1D_ARRAY:
      dim_support = D3D12_FORMAT_SUPPORT1_TEXTURE1D;
      break;
   case PIPE_TEXTURE_2D:
   case PIPE_TEXTURE_RECT:
   case PIPE_TEXTURE_2D_ARRAY:
      dim_support = D3D12_FORMAT_SUPPORT1_TEXTURE2D;
      break;
   case PIPE_TEXTURE_3D:
      dim_support = D3D12_FORMAT_SUPPORT1_TEXTURE3D;
      break;
   case PIPE_TEXTURE_CUBE:
   case PIPE_TEXTURE_CUBE_ARRAY:
      dim_support = D3D12_FORMAT_SUPPORT1_TEXTURECUBE;
      break;
   case PIPE_BUFFER:
      dim_support = D3D12_FORMAT_SUPPORT1_BUFFER;
      break;
   default:
      UNREACHABLE("Unknown target");
   }

   if (bind & PIPE_BIND_DISPLAY_TARGET) {
      enum pipe_format dt_format = format == PIPE_FORMAT_R16G16B16A16_FLOAT ? PIPE_FORMAT_R8G8B8A8_UNORM : format;
      if (!screen->winsys->is_displaytarget_format_supported(screen->winsys, bind, dt_format))
         return false;
   }

   D3D12_FEATURE_DATA_FORMAT_SUPPORT fmt_info;
   fmt_info.Format = d3d12_get_resource_rt_format(format);
   if (FAILED(screen->dev->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT,
                                               &fmt_info, sizeof(fmt_info))))
      return false;

   if (!(fmt_info.Support1 & dim_support))
      return false;

   if (target == PIPE_BUFFER) {
      if (bind & PIPE_BIND_VERTEX_BUFFER &&
          !(fmt_info.Support1 & D3D12_FORMAT_SUPPORT1_IA_VERTEX_BUFFER))
         return false;

      if (bind & PIPE_BIND_INDEX_BUFFER) {
         if (format != PIPE_FORMAT_R16_UINT &&
             format != PIPE_FORMAT_R32_UINT)
            return false;
      }

      if (sample_count > 0)
         return false;
   } else {
      /* all other targets are texture-targets */
      if (bind & PIPE_BIND_RENDER_TARGET &&
          !(fmt_info.Support1 & D3D12_FORMAT_SUPPORT1_RENDER_TARGET))
         return false;

      if (bind & PIPE_BIND_BLENDABLE &&
         !(fmt_info.Support1 & D3D12_FORMAT_SUPPORT1_BLENDABLE))
         return false;

      if (bind & PIPE_BIND_SHADER_IMAGE &&
         (fmt_info.Support2 & (D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD | D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE)) !=
            (D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD | D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE))
         return false;

      D3D12_FEATURE_DATA_FORMAT_SUPPORT fmt_info_sv;
      if (util_format_is_depth_or_stencil(format)) {
         fmt_info_sv.Format = d3d12_get_resource_srv_format(format, target);
         if (FAILED(screen->dev->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT,
                                                     &fmt_info_sv, sizeof(fmt_info_sv))))
            return false;
      } else
         fmt_info_sv = fmt_info;

      if (bind & PIPE_BIND_DEPTH_STENCIL &&
          !(fmt_info.Support1 & D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL))
            return false;

      if (sample_count > 0) {
         if (!(fmt_info_sv.Support1 & D3D12_FORMAT_SUPPORT1_MULTISAMPLE_LOAD))
            return false;

         if (!util_is_power_of_two_nonzero(sample_count))
            return false;

         if (bind & PIPE_BIND_SHADER_IMAGE)
            return false;

         D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS ms_info = {};
         ms_info.Format = dxgi_format;
         ms_info.SampleCount = sample_count;
         if (FAILED(screen->dev->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
                                                     &ms_info,
                                                     sizeof(ms_info))) ||
             !ms_info.NumQualityLevels)
            return false;
      }
   }
   return true;
}

void
d3d12_deinit_screen(struct d3d12_screen *screen)
{
#ifdef HAVE_GALLIUM_D3D12_GRAPHICS
   if (screen->max_feature_level >= D3D_FEATURE_LEVEL_11_0) {
      if (screen->rtv_pool) {
         d3d12_descriptor_pool_free(screen->rtv_pool);
         screen->rtv_pool = nullptr;
      }
      if (screen->dsv_pool) {
         d3d12_descriptor_pool_free(screen->dsv_pool);
         screen->dsv_pool = nullptr;
      }
      if (screen->view_pool) {
         d3d12_descriptor_pool_free(screen->view_pool);
         screen->view_pool = nullptr;
      }
   }
#endif // HAVE_GALLIUM_D3D12_GRAPHICS
   if (screen->readback_slab_bufmgr) {
      screen->readback_slab_bufmgr->destroy(screen->readback_slab_bufmgr);
      screen->readback_slab_bufmgr = nullptr;
   }
   if (screen->slab_bufmgr) {
      screen->slab_bufmgr->destroy(screen->slab_bufmgr);
      screen->slab_bufmgr = nullptr;
   }
   if (screen->cache_bufmgr) {
      screen->cache_bufmgr->destroy(screen->cache_bufmgr);
      screen->cache_bufmgr = nullptr;
   }
   if (screen->slab_cache_bufmgr) {
      screen->slab_cache_bufmgr->destroy(screen->slab_cache_bufmgr);
      screen->slab_cache_bufmgr = nullptr;
   }
   if (screen->readback_slab_cache_bufmgr) {
      screen->readback_slab_cache_bufmgr->destroy(screen->readback_slab_cache_bufmgr);
      screen->readback_slab_cache_bufmgr = nullptr;
   }
   if (screen->bufmgr) {
      screen->bufmgr->destroy(screen->bufmgr);
      screen->bufmgr = nullptr;
   }
   d3d12_deinit_residency(screen);
   if (screen->fence) {
      screen->fence->Release();
      screen->fence = nullptr;
   }
   if (screen->cmdqueue) {
      screen->cmdqueue->Release();
      screen->cmdqueue = nullptr;
   }
   if (screen->dev10) {
      screen->dev10->Release();
      screen->dev10 = nullptr;
   }
   if (screen->dev15) {
      screen->dev15->Release();
      screen->dev15 = nullptr;
   }
   if (screen->dev) {
      screen->dev->Release();
      screen->dev = nullptr;
   }
}

void
d3d12_destroy_screen(struct d3d12_screen *screen)
{
   slab_destroy_parent(&screen->transfer_pool);
   mtx_destroy(&screen->submit_mutex);
   mtx_destroy(&screen->descriptor_pool_mutex);

#ifdef HAVE_GALLIUM_D3D12_GRAPHICS
   d3d12_varying_cache_destroy(screen);
   mtx_destroy(&screen->varying_info_mutex);
#endif // HAVE_GALLIUM_D3D12_GRAPHICS

   if (screen->d3d12_mod)
      util_dl_close(screen->d3d12_mod);
#ifdef HAVE_GALLIUM_D3D12_GRAPHICS
   glsl_type_singleton_decref();
#endif // HAVE_GALLIUM_D3D12_GRAPHICS
   FREE(screen);
}

static void
d3d12_flush_frontbuffer(struct pipe_screen * pscreen,
                        struct pipe_context *pctx,
                        struct pipe_resource *pres,
                        unsigned level, unsigned layer,
                        void *winsys_drawable_handle,
                        unsigned nboxes,
                        struct pipe_box *sub_box)
{
   struct d3d12_screen *screen = d3d12_screen(pscreen);
   struct sw_winsys *winsys = screen->winsys;
   struct d3d12_resource *res = d3d12_resource(pres);

   if (!winsys || !pctx)
     return;

   assert(res->dt || res->dt_proxy);
   if (res->dt_proxy) {
     struct pipe_blit_info blit;

     memset(&blit, 0, sizeof(blit));
     blit.dst.resource = res->dt_proxy;
     blit.dst.box.width = blit.dst.resource->width0;
     blit.dst.box.height = blit.dst.resource->height0;
     blit.dst.box.depth = 1;
     blit.dst.format = blit.dst.resource->format;
     blit.src.resource = pres;
     blit.src.box.width = blit.src.resource->width0;
     blit.src.box.height = blit.src.resource->height0;
     blit.src.box.depth = 1;
     blit.src.format = blit.src.resource->format;
     blit.mask = PIPE_MASK_RGBA;
     blit.filter = PIPE_TEX_FILTER_NEAREST;

     pctx->blit(pctx, &blit);
     pres = res->dt_proxy;
     res = d3d12_resource(pres);
   }

   assert(res->dt);
   void *map = winsys->displaytarget_map(winsys, res->dt, 0);

   if (map) {
      pctx = threaded_context_unwrap_sync(pctx);
      pipe_transfer *transfer = nullptr;
      void *res_map = pipe_texture_map(pctx, pres, level, layer, PIPE_MAP_READ, 0, 0,
                                        u_minify(pres->width0, level),
                                        u_minify(pres->height0, level),
                                        &transfer);
      if (res_map) {
         util_copy_rect((uint8_t*)map, pres->format, res->dt_stride, 0, 0,
                        transfer->box.width, transfer->box.height,
                        (const uint8_t*)res_map, transfer->stride, 0, 0);
         pipe_texture_unmap(pctx, transfer);
      }
      winsys->displaytarget_unmap(winsys, res->dt);
   }

#if defined(_WIN32) && !defined(_GAMING_XBOX) && defined(HAVE_GALLIUM_D3D12_GRAPHICS)
   // WindowFromDC is Windows-only, and this method requires an HWND, so only use it on Windows
   ID3D12SharingContract *sharing_contract;
   if (SUCCEEDED(screen->cmdqueue->QueryInterface(IID_PPV_ARGS(&sharing_contract)))) {
      ID3D12Resource *d3d12_res = d3d12_resource_resource(res);
      sharing_contract->Present(d3d12_res, 0, WindowFromDC((HDC)winsys_drawable_handle));
      sharing_contract->Release();
   }
#endif

   winsys->displaytarget_display(winsys, res->dt, winsys_drawable_handle, nboxes, sub_box);
}

#ifndef _GAMING_XBOX
static ID3D12Debug *
get_debug_interface(util_dl_library *d3d12_mod, ID3D12DeviceFactory *factory)
{
   ID3D12Debug *debug = nullptr;
   if (factory) {
      factory->GetConfigurationInterface(CLSID_D3D12Debug, IID_PPV_ARGS(&debug));
      return debug;
   }

   typedef HRESULT(WINAPI *PFN_D3D12_GET_DEBUG_INTERFACE)(REFIID riid, void **ppFactory);
   PFN_D3D12_GET_DEBUG_INTERFACE D3D12GetDebugInterface;

   D3D12GetDebugInterface = (PFN_D3D12_GET_DEBUG_INTERFACE)util_dl_get_proc_address(d3d12_mod, "D3D12GetDebugInterface");
   if (!D3D12GetDebugInterface) {
      debug_printf("D3D12: failed to load D3D12GetDebugInterface from D3D12.DLL\n");
      return NULL;
   }

   if (FAILED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug)))) {
      debug_printf("D3D12: D3D12GetDebugInterface failed\n");
      return NULL;
   }

   return debug;
}

static void
enable_d3d12_debug_layer(util_dl_library *d3d12_mod, ID3D12DeviceFactory *factory)
{
   ID3D12Debug *debug = get_debug_interface(d3d12_mod, factory);
   if (debug) {
      debug->EnableDebugLayer();
      debug->Release();
   }
}

static void
enable_gpu_validation(util_dl_library *d3d12_mod, ID3D12DeviceFactory *factory)
{
   ID3D12Debug *debug = get_debug_interface(d3d12_mod, factory);
   ID3D12Debug3 *debug3;
   if (debug) {
      if (SUCCEEDED(debug->QueryInterface(IID_PPV_ARGS(&debug3)))) {
         debug3->SetEnableGPUBasedValidation(true);
         debug3->Release();
      }
      debug->Release();
   }
}
#endif

#ifdef _GAMING_XBOX

static ID3D12Device3 *
create_device(util_dl_library *d3d12_mod, IUnknown *adapter)
{
   D3D12XBOX_PROCESS_DEBUG_FLAGS debugFlags =
      D3D12XBOX_PROCESS_DEBUG_FLAG_ENABLE_COMMON_STATE_PROMOTION; /* For compatibility with desktop D3D12 */

   if (d3d12_debug & D3D12_DEBUG_EXPERIMENTAL) {
      debug_printf("D3D12: experimental shader models are not supported on GDKX\n");
      return nullptr;
   }

   if (d3d12_debug & D3D12_DEBUG_GPU_VALIDATOR) {
      debug_printf("D3D12: gpu validation is not supported on GDKX\n"); /* FIXME: Is this right? */
      return nullptr;
   }

   if (d3d12_debug & D3D12_DEBUG_DEBUG_LAYER)
      debugFlags |= D3D12XBOX_PROCESS_DEBUG_FLAG_DEBUG;

   D3D12XBOX_CREATE_DEVICE_PARAMETERS params = {};
   params.Version = D3D12_SDK_VERSION;
   params.ProcessDebugFlags = debugFlags;
   params.GraphicsCommandQueueRingSizeBytes = D3D12XBOX_DEFAULT_SIZE_BYTES;
   params.GraphicsScratchMemorySizeBytes = D3D12XBOX_DEFAULT_SIZE_BYTES;
   params.ComputeScratchMemorySizeBytes = D3D12XBOX_DEFAULT_SIZE_BYTES;

   ID3D12Device3 *dev = nullptr;

   typedef HRESULT(WINAPI * PFN_D3D12XBOXCREATEDEVICE)(IGraphicsUnknown *, const D3D12XBOX_CREATE_DEVICE_PARAMETERS *, REFIID, void **);
   PFN_D3D12XBOXCREATEDEVICE D3D12XboxCreateDevice =
      (PFN_D3D12XBOXCREATEDEVICE) util_dl_get_proc_address(d3d12_mod, "D3D12XboxCreateDevice");
   if (!D3D12XboxCreateDevice) {
      debug_printf("D3D12: failed to load D3D12XboxCreateDevice from D3D12 DLL\n");
      return NULL;
   }
   if (FAILED(D3D12XboxCreateDevice((IGraphicsUnknown*) adapter, &params, IID_PPV_ARGS(&dev))))
      debug_printf("D3D12: D3D12XboxCreateDevice failed\n");

   return dev;
}

#else

static ID3D12Device3 *
create_device(util_dl_library *d3d12_mod, IUnknown *adapter, ID3D12DeviceFactory *factory)
{

#ifdef _WIN32
   if (d3d12_debug & D3D12_DEBUG_EXPERIMENTAL)
#endif
   {
      if (factory) {
         if (FAILED(factory->EnableExperimentalFeatures(1, &D3D12ExperimentalShaderModels, nullptr, nullptr))) {
            debug_printf("D3D12: failed to enable experimental shader models\n");
            return nullptr;
         }
      } else {
         typedef HRESULT(WINAPI *PFN_D3D12ENABLEEXPERIMENTALFEATURES)(UINT, const IID*, void*, UINT*);
         PFN_D3D12ENABLEEXPERIMENTALFEATURES D3D12EnableExperimentalFeatures =
            (PFN_D3D12ENABLEEXPERIMENTALFEATURES)util_dl_get_proc_address(d3d12_mod, "D3D12EnableExperimentalFeatures");

         if (!D3D12EnableExperimentalFeatures ||
             FAILED(D3D12EnableExperimentalFeatures(1, &D3D12ExperimentalShaderModels, NULL, NULL))) {
            debug_printf("D3D12: failed to enable experimental shader models\n");
            return nullptr;
         }
      }
   }

   ID3D12Device3 *dev = nullptr;
   if (factory) {
      factory->SetFlags(D3D12_DEVICE_FACTORY_FLAG_ALLOW_RETURNING_EXISTING_DEVICE |
         D3D12_DEVICE_FACTORY_FLAG_ALLOW_RETURNING_INCOMPATIBLE_EXISTING_DEVICE);
      /* Fallback to D3D_FEATURE_LEVEL_11_0 for D3D12 versions without generic support */
      if (FAILED(factory->CreateDevice(adapter, D3D_FEATURE_LEVEL_1_0_GENERIC, IID_PPV_ARGS(&dev))))
         if (FAILED(factory->CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&dev))))
            debug_printf("D3D12: D3D12CreateDevice failed\n");
   } else {
      typedef HRESULT(WINAPI *PFN_D3D12CREATEDEVICE)(IUnknown*, D3D_FEATURE_LEVEL, REFIID, void**);
      PFN_D3D12CREATEDEVICE D3D12CreateDevice = (PFN_D3D12CREATEDEVICE)util_dl_get_proc_address(d3d12_mod, "D3D12CreateDevice");
      if (!D3D12CreateDevice) {
         debug_printf("D3D12: failed to load D3D12CreateDevice from D3D12.DLL\n");
         return NULL;
      }
      /* Fallback to D3D_FEATURE_LEVEL_11_0 for D3D12 versions without generic support */
      if (FAILED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_1_0_GENERIC, IID_PPV_ARGS(&dev))))
         if (FAILED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&dev))))
            debug_printf("D3D12: D3D12CreateDevice failed\n");
   }

   return dev;
}

#endif /* _GAMING_XBOX */

static bool
can_attribute_at_vertex(struct d3d12_screen *screen)
{
   switch (screen->vendor_id)  {
   case HW_VENDOR_MICROSOFT:
      return true;
   default:
      return screen->opts3.BarycentricsSupported;
   }
}

static bool
can_shader_image_load_all_formats(struct d3d12_screen *screen)
{
   if (!screen->opts.TypedUAVLoadAdditionalFormats)
      return false;

   /* All of these are required by ARB_shader_image_load_store */
   static const DXGI_FORMAT additional_formats[] = {
      DXGI_FORMAT_R16G16B16A16_UNORM,
      DXGI_FORMAT_R16G16B16A16_SNORM,
      DXGI_FORMAT_R32G32_FLOAT,
      DXGI_FORMAT_R32G32_UINT,
      DXGI_FORMAT_R32G32_SINT,
      DXGI_FORMAT_R10G10B10A2_UNORM,
      DXGI_FORMAT_R10G10B10A2_UINT,
      DXGI_FORMAT_R11G11B10_FLOAT,
      DXGI_FORMAT_R8G8B8A8_SNORM,
      DXGI_FORMAT_R16G16_FLOAT,
      DXGI_FORMAT_R16G16_UNORM,
      DXGI_FORMAT_R16G16_UINT,
      DXGI_FORMAT_R16G16_SNORM,
      DXGI_FORMAT_R16G16_SINT,
      DXGI_FORMAT_R8G8_UNORM,
      DXGI_FORMAT_R8G8_UINT,
      DXGI_FORMAT_R8G8_SNORM,
      DXGI_FORMAT_R8G8_SINT,
      DXGI_FORMAT_R16_UNORM,
      DXGI_FORMAT_R16_SNORM,
      DXGI_FORMAT_R8_SNORM,
   };

   for (unsigned i = 0; i < ARRAY_SIZE(additional_formats); ++i) {
      D3D12_FEATURE_DATA_FORMAT_SUPPORT support = { additional_formats[i] };
      if (FAILED(screen->dev->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &support, sizeof(support))) ||
         (support.Support1 & D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW) == D3D12_FORMAT_SUPPORT1_NONE ||
         (support.Support2 & (D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD | D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE)) !=
            (D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD | D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE))
         return false;
   }

   return true;
}

static void
d3d12_init_null_srvs(struct d3d12_screen *screen)
{
   for (unsigned i = 0; i < RESOURCE_DIMENSION_COUNT; ++i) {
      D3D12_SHADER_RESOURCE_VIEW_DESC srv = {};

      srv.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
      srv.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
      switch (i) {
      case RESOURCE_DIMENSION_BUFFER:
      case RESOURCE_DIMENSION_UNKNOWN:
         srv.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
         srv.Buffer.FirstElement = 0;
         srv.Buffer.NumElements = 0;
         srv.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
         srv.Buffer.StructureByteStride = 0;
         break;
      case RESOURCE_DIMENSION_TEXTURE1D:
         srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D;
         srv.Texture1D.MipLevels = 1;
         srv.Texture1D.MostDetailedMip = 0;
         srv.Texture1D.ResourceMinLODClamp = 0.0f;
         break;
      case RESOURCE_DIMENSION_TEXTURE1DARRAY:
         srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY;
         srv.Texture1DArray.MipLevels = 1;
         srv.Texture1DArray.ArraySize = 1;
         srv.Texture1DArray.MostDetailedMip = 0;
         srv.Texture1DArray.FirstArraySlice = 0;
         srv.Texture1DArray.ResourceMinLODClamp = 0.0f;
         break;
      case RESOURCE_DIMENSION_TEXTURE2D:
         srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
         srv.Texture2D.MipLevels = 1;
         srv.Texture2D.MostDetailedMip = 0;
         srv.Texture2D.PlaneSlice = 0;
         srv.Texture2D.ResourceMinLODClamp = 0.0f;
         break;
      case RESOURCE_DIMENSION_TEXTURE2DARRAY:
         srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
         srv.Texture2DArray.MipLevels = 1;
         srv.Texture2DArray.ArraySize = 1;
         srv.Texture2DArray.MostDetailedMip = 0;
         srv.Texture2DArray.FirstArraySlice = 0;
         srv.Texture2DArray.PlaneSlice = 0;
         srv.Texture2DArray.ResourceMinLODClamp = 0.0f;
         break;
      case RESOURCE_DIMENSION_TEXTURE2DMS:
         srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
         break;
      case RESOURCE_DIMENSION_TEXTURE2DMSARRAY:
         srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY;
         srv.Texture2DMSArray.ArraySize = 1;
         srv.Texture2DMSArray.FirstArraySlice = 0;
         break;
      case RESOURCE_DIMENSION_TEXTURE3D:
         srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
         srv.Texture3D.MipLevels = 1;
         srv.Texture3D.MostDetailedMip = 0;
         srv.Texture3D.ResourceMinLODClamp = 0.0f;
         break;
      case RESOURCE_DIMENSION_TEXTURECUBE:
         srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
         srv.TextureCube.MipLevels = 1;
         srv.TextureCube.MostDetailedMip = 0;
         srv.TextureCube.ResourceMinLODClamp = 0.0f;
         break;
      case RESOURCE_DIMENSION_TEXTURECUBEARRAY:
         srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
         srv.TextureCubeArray.MipLevels = 1;
         srv.TextureCubeArray.NumCubes = 1;
         srv.TextureCubeArray.MostDetailedMip = 0;
         srv.TextureCubeArray.First2DArrayFace = 0;
         srv.TextureCubeArray.ResourceMinLODClamp = 0.0f;
         break;
      }

      if (srv.ViewDimension != D3D12_SRV_DIMENSION_UNKNOWN)
      {
         d3d12_descriptor_pool_alloc_handle(screen->view_pool, &screen->null_srvs[i]);
         screen->dev->CreateShaderResourceView(NULL, &srv, screen->null_srvs[i].cpu_handle);
      }
   }
}

static void
d3d12_init_null_uavs(struct d3d12_screen *screen)
{
   for (unsigned i = 0; i < RESOURCE_DIMENSION_COUNT; ++i) {
      D3D12_UNORDERED_ACCESS_VIEW_DESC uav = {};

      uav.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
      switch (i) {
      case RESOURCE_DIMENSION_BUFFER:
      case RESOURCE_DIMENSION_UNKNOWN:
         uav.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
         uav.Buffer.FirstElement = 0;
         uav.Buffer.NumElements = 0;
         uav.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
         uav.Buffer.StructureByteStride = 0;
         uav.Buffer.CounterOffsetInBytes = 0;
         break;
      case RESOURCE_DIMENSION_TEXTURE1D:
         uav.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1D;
         uav.Texture1D.MipSlice = 0;
         break;
      case RESOURCE_DIMENSION_TEXTURE1DARRAY:
         uav.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1DARRAY;
         uav.Texture1DArray.MipSlice = 0;
         uav.Texture1DArray.ArraySize = 1;
         uav.Texture1DArray.FirstArraySlice = 0;
         break;
      case RESOURCE_DIMENSION_TEXTURE2D:
         uav.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
         uav.Texture2D.MipSlice = 0;
         uav.Texture2D.PlaneSlice = 0;
         break;
      case RESOURCE_DIMENSION_TEXTURE2DARRAY:
      case RESOURCE_DIMENSION_TEXTURECUBE:
      case RESOURCE_DIMENSION_TEXTURECUBEARRAY:
         uav.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
         uav.Texture2DArray.MipSlice = 0;
         uav.Texture2DArray.ArraySize = 1;
         uav.Texture2DArray.FirstArraySlice = 0;
         uav.Texture2DArray.PlaneSlice = 0;
         break;
      case RESOURCE_DIMENSION_TEXTURE2DMS:
      case RESOURCE_DIMENSION_TEXTURE2DMSARRAY:
         break;
      case RESOURCE_DIMENSION_TEXTURE3D:
         uav.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
         uav.Texture3D.MipSlice = 0;
         uav.Texture3D.FirstWSlice = 0;
         uav.Texture3D.WSize = 1;
         break;
      }

      if (uav.ViewDimension != D3D12_UAV_DIMENSION_UNKNOWN)
      {
         d3d12_descriptor_pool_alloc_handle(screen->view_pool, &screen->null_uavs[i]);
         screen->dev->CreateUnorderedAccessView(NULL, NULL, &uav, screen->null_uavs[i].cpu_handle);
      }
   }
}

static void
d3d12_init_null_rtv(struct d3d12_screen *screen)
{
   D3D12_RENDER_TARGET_VIEW_DESC rtv = {};
   rtv.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
   rtv.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
   rtv.Texture2D.MipSlice = 0;
   rtv.Texture2D.PlaneSlice = 0;
   d3d12_descriptor_pool_alloc_handle(screen->rtv_pool, &screen->null_rtv);
   screen->dev->CreateRenderTargetView(NULL, &rtv, screen->null_rtv.cpu_handle);
}

static void
d3d12_get_adapter_luid(struct pipe_screen *pscreen, char *luid)
{
   struct d3d12_screen *screen = d3d12_screen(pscreen);
   memcpy(luid, &screen->adapter_luid, PIPE_LUID_SIZE);
}

static void
d3d12_get_device_uuid(struct pipe_screen *pscreen, char *uuid)
{
   struct d3d12_screen *screen = d3d12_screen(pscreen);
   memcpy(uuid, &screen->device_uuid, PIPE_UUID_SIZE);
}

static void
d3d12_get_driver_uuid(struct pipe_screen *pscreen, char *uuid)
{
   struct d3d12_screen *screen = d3d12_screen(pscreen);
   memcpy(uuid, &screen->driver_uuid, PIPE_UUID_SIZE);
}

static uint32_t
d3d12_get_node_mask(struct pipe_screen *pscreen)
{
   /* This implementation doesn't support linked adapters */
   return 1;
}

static void
d3d12_create_fence_win32(struct pipe_screen *pscreen, struct pipe_fence_handle **pfence, void *handle, const void *name, enum pipe_fd_type type)
{
   d3d12_fence_reference((struct d3d12_fence **)pfence, nullptr);
   if(type == PIPE_FD_TYPE_TIMELINE_SEMAPHORE_D3D12)
      *pfence = (struct pipe_fence_handle*) d3d12_open_fence(d3d12_screen(pscreen), handle, name, type);
}

static uint32_t
d3d12_interop_query_device_info(struct pipe_screen *pscreen, uint32_t data_size, void *data)
{
   if (data_size < sizeof(d3d12_interop_device_info) || !data)
      return 0;
   d3d12_interop_device_info *info = (d3d12_interop_device_info *)data;
   struct d3d12_screen *screen = d3d12_screen(pscreen);

   static_assert(sizeof(info->adapter_luid) == sizeof(screen->adapter_luid),
                 "Using uint64_t instead of Windows-specific type");
   memcpy(&info->adapter_luid, &screen->adapter_luid, sizeof(screen->adapter_luid));
   info->device = screen->dev;
   info->queue = screen->cmdqueue;

   if (data_size >= sizeof(d3d12_interop_device_info1)) {
      d3d12_interop_device_info1 *info1 = (d3d12_interop_device_info1 *)data;
      info1->set_context_queue_priority_manager = d3d12_context_set_queue_priority_manager;
#ifdef HAVE_GALLIUM_D3D12_VIDEO
      info1->set_video_encoder_max_async_queue_depth = d3d12_video_encoder_set_max_async_queue_depth;
      info1->get_video_enc_last_slice_completion_fence = d3d12_video_encoder_get_last_slice_completion_fence;
#else
      info1->set_video_encoder_max_async_queue_depth = NULL;
      info1->get_video_enc_last_slice_completion_fence = NULL;
#endif // HAVE_GALLIUM_D3D12_VIDEO

      return sizeof(*info1);
   }

   return sizeof(*info);
}

static uint32_t
d3d12_interop_export_object(struct pipe_screen *pscreen, struct pipe_resource *res,
                              uint32_t data_size, void *data, bool *need_export_dmabuf)
{
   if (data_size < sizeof(d3d12_interop_resource_info) || !data)
      return 0;
   d3d12_interop_resource_info *info = (d3d12_interop_resource_info *)data;
   
   info->resource = d3d12_resource_underlying(d3d12_resource(res), &info->buffer_offset);
   *need_export_dmabuf = false;
   return sizeof(*info);
}

static int
d3d12_screen_get_fd(struct pipe_screen *pscreen)
{
   struct d3d12_screen *screen = d3d12_screen(pscreen);
   struct sw_winsys *winsys = screen->winsys;

   if (winsys->get_fd)
      return winsys->get_fd(winsys);
   else
      return -1;
}

#ifdef _WIN32
static void* d3d12_fence_get_win32_handle(struct pipe_screen *pscreen,
                                          struct pipe_fence_handle *fence_handle,
                                          uint64_t *fence_value)
{
   struct d3d12_screen *screen = d3d12_screen(pscreen);
   struct d3d12_fence* fence = (struct d3d12_fence*) fence_handle;
   HANDLE shared_handle = nullptr;
   screen->dev->CreateSharedHandle(fence->cmdqueue_fence,
                                   NULL,
                                   GENERIC_ALL,
                                   NULL,
                                   &shared_handle);
   if(shared_handle)
      *fence_value = fence->value;

   return (void*) shared_handle;
}

static void *d3d12_fence_get_win32_event([[maybe_unused]] struct pipe_screen *pscreen,
                                         struct pipe_fence_handle *fence_handle)
{
   struct d3d12_fence* fence = (struct d3d12_fence*) fence_handle;

   if (fence->type != PIPE_FD_TYPE_NATIVE_SYNC)
      return NULL;

   /* Create a new manual-reset event rather than duplicating the fence's
    * auto-reset event.  Duplicated handles share the same kernel object, so
    * a wait on any handle consumes the single auto-reset signal — causing
    * other waiters to hang.
    * A dedicated manual-reset event with its own SetEventOnCompletion avoids
    * this by giving each caller an independent signal. */
   HANDLE event = CreateEvent(NULL, TRUE /* bManualReset */, FALSE, NULL);
   if (!event)
      return NULL;

   if (FAILED(fence->cmdqueue_fence->SetEventOnCompletion(fence->value, event))) {
      CloseHandle(event);
      return NULL;
   }

   return event;
}
#endif

static void
d3d12_query_memory_info(struct pipe_screen *pscreen, struct pipe_memory_info *info)
{
   struct d3d12_screen *screen = d3d12_screen(pscreen);

   // megabytes to kilobytes
   if (screen->architecture.UMA) {
      /* https://asawicki.info/news_1755_untangling_direct3d_12_memory_heap_types_and_pools
         All allocations are made in D3D12_MEMORY_POOL_L0 and they increase the usage of
         DXGI_MEMORY_SEGMENT_GROUP_LOCAL, as there is only one unified memory and it's all "local" to the GPU.
       */
      info->total_device_memory =
         static_cast<unsigned int>(CLAMP((screen->memory_device_size_megabytes << 10) + (screen->memory_system_size_megabytes << 10), 0u, UINT32_MAX));
      info->total_staging_memory = 0;
   } else {
      info->total_device_memory = static_cast<unsigned int>(CLAMP(screen->memory_device_size_megabytes << 10, 0u, UINT32_MAX));
      info->total_staging_memory = static_cast<unsigned int>(CLAMP(screen->memory_system_size_megabytes << 10, 0u, UINT32_MAX));;
   }

   d3d12_memory_info m;
   screen->get_memory_info(screen, &m);
   // bytes to kilobytes
   if (m.budget_local > m.usage_local) {
      info->avail_device_memory = static_cast<unsigned int>(CLAMP((m.budget_local - m.usage_local) / 1024, 0u, UINT32_MAX));
   } else {
      info->avail_device_memory = 0;
   }
   if (m.budget_nonlocal > m.usage_nonlocal) {
      info->avail_staging_memory = static_cast<unsigned int>(CLAMP(m.budget_nonlocal - m.usage_nonlocal / 1024, 0u, UINT32_MAX));
   } else {
      info->avail_staging_memory = 0;
   }

   info->device_memory_evicted = static_cast<unsigned int>(CLAMP(screen->total_bytes_evicted / 1024, 0u, UINT32_MAX));
   info->nr_device_memory_evictions = screen->num_evictions;
}

bool
d3d12_init_screen_base(struct d3d12_screen *screen, struct sw_winsys *winsys, LUID *adapter_luid)
{
#ifdef HAVE_GALLIUM_D3D12_GRAPHICS
   glsl_type_singleton_init_or_ref();
#endif // HAVE_GALLIUM_D3D12_GRAPHICS
   d3d12_debug = static_cast<uint32_t>(debug_get_option_d3d12_debug());

   screen->winsys = winsys;
   if (adapter_luid)
      screen->adapter_luid = *adapter_luid;
   mtx_init(&screen->descriptor_pool_mutex, mtx_plain);
   mtx_init(&screen->submit_mutex, mtx_plain);

   list_inithead(&screen->context_list);
   screen->context_id_count = 16;

   // Fill the array backwards, because we'll pop off the back to assign ids
   for (unsigned i = 0; i < 16; ++i)
      screen->context_id_list[i] = 15 - i;

#ifdef HAVE_GALLIUM_D3D12_GRAPHICS
   d3d12_varying_cache_init(screen);
   mtx_init(&screen->varying_info_mutex, mtx_plain);

   for (unsigned i = 0; i <= MESA_SHADER_COMPUTE; i++)
      screen->base.nir_options[i] = &screen->nir_options;
#endif // HAVE_GALLIUM_D3D12_GRAPHICS

   slab_create_parent(&screen->transfer_pool, sizeof(struct d3d12_transfer), 16);

   screen->base.get_vendor = d3d12_get_vendor;
   screen->base.get_device_vendor = d3d12_get_device_vendor;
   screen->base.get_screen_fd = d3d12_screen_get_fd;
   screen->base.is_format_supported = d3d12_is_format_supported;

   screen->base.context_create = d3d12_context_create;
   screen->base.flush_frontbuffer = d3d12_flush_frontbuffer;
   screen->base.get_device_luid = d3d12_get_adapter_luid;
   screen->base.get_device_uuid = d3d12_get_device_uuid;
   screen->base.get_driver_uuid = d3d12_get_driver_uuid;
   screen->base.get_device_node_mask = d3d12_get_node_mask;
   screen->base.create_fence_win32 = d3d12_create_fence_win32;
   screen->base.interop_query_device_info = d3d12_interop_query_device_info;
   screen->base.interop_export_object = d3d12_interop_export_object;
#ifdef _WIN32
   screen->base.fence_get_win32_handle = d3d12_fence_get_win32_handle;
   screen->base.fence_get_win32_event = d3d12_fence_get_win32_event;
#endif
   screen->base.query_memory_info = d3d12_query_memory_info;

   screen->d3d12_mod = util_dl_open(
      UTIL_DL_PREFIX
#ifdef _GAMING_XBOX_SCARLETT
      "d3d12_xs"
#elif defined(_GAMING_XBOX)
      "d3d12_x"
#else
      "d3d12"
#endif
      UTIL_DL_EXT
   );
   if (!screen->d3d12_mod) {
      debug_printf("D3D12: failed to load D3D12.DLL\n");
      return false;
   }
   return true;
}

#ifdef _WIN32
extern "C" IMAGE_DOS_HEADER __ImageBase;
static const char *
try_find_d3d12core_next_to_self(char *path, DWORD path_arr_size)
{
   uint32_t path_size = GetModuleFileNameA((HINSTANCE)&__ImageBase,
                                           path, path_arr_size);
   if (!path_arr_size || path_size == path_arr_size) {
      debug_printf("Unable to get path to self\n");
      return nullptr;
   }

   auto last_slash = strrchr(path, '\\');
   if (!last_slash) {
      debug_printf("Unable to get path to self\n");
      return nullptr;
   }

   *(last_slash + 1) = '\0';
   if (strcat_s(path, path_arr_size, "D3D12Core.dll") != 0) {
      debug_printf("Unable to get path to D3D12Core.dll next to self\n");
      return nullptr;
   }

   if (GetFileAttributesA(path) == INVALID_FILE_ATTRIBUTES) {
      debug_printf("No D3D12Core.dll exists next to self\n");
      return nullptr;
   }

   *(last_slash + 1) = '\0';
   return path;
}
#endif

#ifndef _GAMING_XBOX
static ID3D12DeviceFactory *
try_create_device_factory(util_dl_library *d3d12_mod)
{
#if defined(_WIN32) && defined(_WIN64)
   if (d3d12_debug & D3D12_DEBUG_PIX) {
      if (GetModuleHandleW(L"WinPixGpuCapturer.dll") == nullptr) {
         LPWSTR program_files_path = nullptr;
         SHGetKnownFolderPath(FOLDERID_ProgramFiles, KF_FLAG_DEFAULT, NULL, &program_files_path);

         auto pix_installation_path = std::filesystem::path(program_files_path) / "Microsoft PIX";
         std::wstring newest_version;
         for (auto const &directory : std::filesystem::directory_iterator(pix_installation_path)) {
            if (directory.is_directory() &&
                (newest_version.empty() || newest_version < directory.path().filename().c_str()))
               newest_version = directory.path().filename().wstring();
         }
         if (newest_version.empty()) {
            debug_printf("D3D12: Failed to find any PIX installations\n");
         }
         else if (!LoadLibraryW((pix_installation_path / newest_version / L"WinPixGpuCapturer.dll").c_str()) &&
                  // Try the x64 subdirectory for x64-on-arm64
                  !LoadLibraryW((pix_installation_path / newest_version / L"x64/WinPixGpuCapturer.dll").c_str())) {
            debug_printf("D3D12: Failed to load WinPixGpuCapturer.dll from %S\n", newest_version.c_str());
         }
      }
   }
#endif

   if (d3d12_debug & D3D12_DEBUG_SINGLETON)
      return nullptr;

   /* A device factory allows us to isolate things like debug layer enablement from other callers,
    * and can potentially even refer to a different D3D12 redist implementation from others.
    */
   ID3D12DeviceFactory *factory = nullptr;

   typedef HRESULT(WINAPI *PFN_D3D12_GET_INTERFACE)(REFCLSID clsid, REFIID riid, void **ppFactory);
   PFN_D3D12_GET_INTERFACE D3D12GetInterface = (PFN_D3D12_GET_INTERFACE)util_dl_get_proc_address(d3d12_mod, "D3D12GetInterface");
   if (!D3D12GetInterface) {
      debug_printf("D3D12: Failed to retrieve D3D12GetInterface");
      return nullptr;
   }

#ifdef _WIN32
   /* First, try to create a device factory from a DLL-parallel D3D12Core.dll */
   ID3D12SDKConfiguration *sdk_config = nullptr;
   if (SUCCEEDED(D3D12GetInterface(CLSID_D3D12SDKConfiguration, IID_PPV_ARGS(&sdk_config)))) {
      ID3D12SDKConfiguration1 *sdk_config1 = nullptr;
      if (SUCCEEDED(sdk_config->QueryInterface(&sdk_config1))) {
         char self_path[MAX_PATH];
         const char *d3d12core_path = try_find_d3d12core_next_to_self(self_path, sizeof(self_path));
         if (d3d12core_path) {
            if (SUCCEEDED(sdk_config1->CreateDeviceFactory(D3D12_PREVIEW_SDK_VERSION, d3d12core_path, IID_PPV_ARGS(&factory))) ||
                SUCCEEDED(sdk_config1->CreateDeviceFactory(D3D12_SDK_VERSION, d3d12core_path, IID_PPV_ARGS(&factory)))) {
               sdk_config->Release();
               sdk_config1->Release();
               return factory;
            }
         }

         /* Nope, seems we don't have a matching D3D12Core.dll next to ourselves */
         sdk_config1->Release();
      }

      /* It's possible there's a D3D12Core.dll next to the .exe, for development/testing purposes. If so, we'll be notified
       * by environment variables what the relative path is and the version to use.
       */
      char *d3d12core_relative_path = os_get_option_dup("D3D12_AGILITY_RELATIVE_PATH");
      char *d3d12core_sdk_version = os_get_option_dup("D3D12_AGILITY_SDK_VERSION");
      if (d3d12core_relative_path && d3d12core_sdk_version) {
         (void)sdk_config->SetSDKVersion(atoi(d3d12core_sdk_version), d3d12core_relative_path);
      }
      free(d3d12core_relative_path);
      free(d3d12core_sdk_version);
      sdk_config->Release();
   }
#endif

   (void)D3D12GetInterface(CLSID_D3D12DeviceFactory, IID_PPV_ARGS(&factory));
   return factory;
}
#endif

static bool
d3d12_init_screen_command_queue(struct d3d12_screen *screen, D3D12_COMMAND_QUEUE_FLAGS queue_flags)
{
   D3D12_COMMAND_QUEUE_DESC queue_desc = {};
   queue_desc.Type = screen->queue_type;
   queue_desc.Flags = queue_flags;

#ifndef _GAMING_XBOX
   ID3D12Device9 *device9 = NULL;
   if (SUCCEEDED(screen->dev->QueryInterface(&device9))) {
      if (FAILED(device9->CreateCommandQueue1(&queue_desc, OpenGLOn12CreatorID,
                                              IID_PPV_ARGS(&screen->cmdqueue)))) {
         device9->Release();
         return false;
      }
      device9->Release();
   } else
#endif
   {
      if (FAILED(screen->dev->CreateCommandQueue(&queue_desc,
                                                 IID_PPV_ARGS(&screen->cmdqueue))))
         return false;
   }
   return true;
}

bool
d3d12_init_screen(struct d3d12_screen *screen, IUnknown *adapter)
{
   assert(screen->base.destroy != nullptr);

   // Device can be imported with d3d12_create_dxcore_screen_from_d3d12_device
   if (!screen->dev) {
#ifndef _GAMING_XBOX
      ID3D12DeviceFactory *factory = try_create_device_factory(screen->d3d12_mod);

#if !MESA_DEBUG
      if (d3d12_debug & D3D12_DEBUG_DEBUG_LAYER)
#endif
         enable_d3d12_debug_layer(screen->d3d12_mod, factory);

      if (d3d12_debug & D3D12_DEBUG_GPU_VALIDATOR)
         enable_gpu_validation(screen->d3d12_mod, factory);

      screen->dev = create_device(screen->d3d12_mod, adapter, factory);

      if (factory)
         factory->Release();
#else
      screen->dev = create_device(screen->d3d12_mod, adapter);
#endif

      if (!screen->dev) {
         debug_printf("D3D12: failed to create device\n");
         return false;
      }
   }
   screen->adapter_luid = GetAdapterLuid(screen->dev);

#ifndef _GAMING_XBOX
   ID3D12InfoQueue *info_queue;
   if (SUCCEEDED(screen->dev->QueryInterface(IID_PPV_ARGS(&info_queue)))) {
      D3D12_MESSAGE_SEVERITY severities[] = {
         D3D12_MESSAGE_SEVERITY_INFO,
         D3D12_MESSAGE_SEVERITY_WARNING,
      };

      D3D12_MESSAGE_ID msg_ids[] = {
         D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE,
      };

      D3D12_INFO_QUEUE_FILTER NewFilter = {};
      NewFilter.DenyList.NumSeverities = ARRAY_SIZE(severities);
      NewFilter.DenyList.pSeverityList = severities;
      NewFilter.DenyList.NumIDs = ARRAY_SIZE(msg_ids);
      NewFilter.DenyList.pIDList = msg_ids;

      info_queue->PushStorageFilter(&NewFilter);
      info_queue->Release();
   }
#endif /* !_GAMING_XBOX */

   if (FAILED(screen->dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS,
                                               &screen->opts,
                                               sizeof(screen->opts)))) {
      debug_printf("D3D12: failed to get device options\n");
      return false;
   }
   if (FAILED(screen->dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS1,
                                               &screen->opts1,
                                               sizeof(screen->opts1)))) {
      debug_printf("D3D12: failed to get device options\n");
      return false;
   }
   if (FAILED(screen->dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS2,
                                               &screen->opts2,
                                               sizeof(screen->opts2)))) {
      debug_printf("D3D12: failed to get device options\n");
      return false;
   }
   if (FAILED(screen->dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS3,
                                               &screen->opts3,
                                               sizeof(screen->opts3)))) {
      debug_printf("D3D12: failed to get device options\n");
      return false;
   }
   if (FAILED(screen->dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS4,
                                               &screen->opts4,
                                               sizeof(screen->opts4)))) {
      debug_printf("D3D12: failed to get device options\n");
      return false;
   }
   screen->dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS12, &screen->opts12, sizeof(screen->opts12));
   screen->dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS14, &screen->opts14, sizeof(screen->opts14));
#ifndef _GAMING_XBOX
   screen->dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS19, &screen->opts19, sizeof(screen->opts19));
#endif

   screen->architecture.NodeIndex = 0;
   if (FAILED(screen->dev->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE,
                                               &screen->architecture,
                                               sizeof(screen->architecture)))) {
      debug_printf("D3D12: failed to get device architecture\n");
      return false;
   }

   D3D12_FEATURE_DATA_FEATURE_LEVELS feature_levels;
   static const D3D_FEATURE_LEVEL levels[] = {
#ifndef _GAMING_XBOX
      D3D_FEATURE_LEVEL_1_0_GENERIC,
      D3D_FEATURE_LEVEL_1_0_CORE,
#endif
      D3D_FEATURE_LEVEL_11_0,
      D3D_FEATURE_LEVEL_11_1,
      D3D_FEATURE_LEVEL_12_0,
      D3D_FEATURE_LEVEL_12_1,
   };
   feature_levels.NumFeatureLevels = ARRAY_SIZE(levels);
   feature_levels.pFeatureLevelsRequested = levels;
   if (FAILED(screen->dev->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS,
                                               &feature_levels,
                                               sizeof(feature_levels)))) {
      debug_printf("D3D12: failed to get device feature levels\n");
      return false;
   }

#ifdef HAVE_GALLIUM_D3D12_GRAPHICS
   screen->max_feature_level = feature_levels.MaxSupportedFeatureLevel;
#else
   screen->max_feature_level = D3D_FEATURE_LEVEL_1_0_GENERIC;
#endif // HAVE_GALLIUM_D3D12_GRAPHICS

   screen->queue_type = (screen->max_feature_level >= D3D_FEATURE_LEVEL_11_0) ? D3D12_COMMAND_LIST_TYPE_DIRECT : D3D12_COMMAND_LIST_TYPE_COMPUTE;

#ifdef HAVE_GALLIUM_D3D12_GRAPHICS
   if (screen->max_feature_level >= D3D_FEATURE_LEVEL_11_0) {
      static const D3D_SHADER_MODEL valid_shader_models[] = {
#ifndef _GAMING_XBOX
         D3D_SHADER_MODEL_6_8,
#endif
         D3D_SHADER_MODEL_6_7, D3D_SHADER_MODEL_6_6, D3D_SHADER_MODEL_6_5, D3D_SHADER_MODEL_6_4,
         D3D_SHADER_MODEL_6_3, D3D_SHADER_MODEL_6_2, D3D_SHADER_MODEL_6_1, D3D_SHADER_MODEL_6_0,
      };
      for (UINT i = 0; i < ARRAY_SIZE(valid_shader_models); ++i) {
         D3D12_FEATURE_DATA_SHADER_MODEL shader_model = { valid_shader_models[i] };
         if (SUCCEEDED(screen->dev->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &shader_model, sizeof(shader_model)))) {
            static_assert(D3D_SHADER_MODEL_6_0 == 0x60 && SHADER_MODEL_6_0 == 0x60000, "Validating math below");
#ifndef _GAMING_XBOX
            static_assert(D3D_SHADER_MODEL_6_8 == 0x68 && SHADER_MODEL_6_8 == 0x60008, "Validating math below");
#endif
            screen->max_shader_model = static_cast<dxil_shader_model>(((shader_model.HighestShaderModel & 0xf0) << 12) |
                                                                     (shader_model.HighestShaderModel & 0xf));
            break;
         }
      }
   }
#endif // HAVE_GALLIUM_D3D12_GRAPHICS

   if (d3d12_init_screen_command_queue(screen, D3D12_COMMAND_QUEUE_FLAG_ALLOW_DYNAMIC_PRIORITY)) {
      screen->supports_dynamic_queue_priority = true;
   } else
   {
      screen->supports_dynamic_queue_priority = false;
      if (!d3d12_init_screen_command_queue(screen, D3D12_COMMAND_QUEUE_FLAG_NONE)) {
         debug_printf("D3D12: failed to create command queue\n");
         return false;
      }
   }

   if (FAILED(screen->dev->CreateFence(0, D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS(&screen->fence))))
      return false;

   screen->dev->QueryInterface(&screen->dev15);

   // Uses screen->dev15 so QI must be before this
   if (!d3d12_init_residency(screen))
      return false;

   UINT64 timestamp_freq;
   if (FAILED(screen->cmdqueue->GetTimestampFrequency(&timestamp_freq)))
       timestamp_freq = 10000000;
   screen->timestamp_multiplier = 1000000000.0f / timestamp_freq;

   d3d12_screen_fence_init(&screen->base);
   d3d12_screen_resource_init(&screen->base);
#ifdef HAVE_GALLIUM_D3D12_VIDEO
   d3d12_screen_video_init(&screen->base);
#endif

   struct pb_desc desc;
   desc.alignment = D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT;
   desc.usage = (pb_usage_flags)(PB_USAGE_CPU_WRITE | PB_USAGE_GPU_READ);

   screen->bufmgr = d3d12_bufmgr_create(screen);
   if (!screen->bufmgr)
      return false;

   screen->cache_bufmgr = pb_cache_manager_create(screen->bufmgr, 0xfffff, 2, 0, 512 * 1024 * 1024);
   if (!screen->cache_bufmgr)
      return false;

   screen->slab_cache_bufmgr = pb_cache_manager_create(screen->bufmgr, 0xfffff, 2, 0, 512 * 1024 * 1024);
   if (!screen->slab_cache_bufmgr)
      return false;

   screen->slab_bufmgr = pb_slab_range_manager_create(screen->slab_cache_bufmgr, 16,
                                                      D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
                                                      D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
                                                      &desc);
   if (!screen->slab_bufmgr)
      return false;
   
   screen->readback_slab_cache_bufmgr = pb_cache_manager_create(screen->bufmgr, 0xfffff, 2, 0, 512 * 1024 * 1024);
   if (!screen->readback_slab_cache_bufmgr)
      return false;

   desc.usage = (pb_usage_flags)(PB_USAGE_CPU_READ_WRITE | PB_USAGE_GPU_WRITE);
   screen->readback_slab_bufmgr = pb_slab_range_manager_create(screen->readback_slab_cache_bufmgr, 16,
                                                               D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
                                                               D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
                                                               &desc);
   if (!screen->readback_slab_bufmgr)
      return false;

#ifdef HAVE_GALLIUM_D3D12_GRAPHICS
   if (screen->max_feature_level >= D3D_FEATURE_LEVEL_11_0) {
      screen->rtv_pool = d3d12_descriptor_pool_new(screen,
                                                   D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
                                                   64);
      screen->dsv_pool = d3d12_descriptor_pool_new(screen,
                                                   D3D12_DESCRIPTOR_HEAP_TYPE_DSV,
                                                   64);
      screen->view_pool = d3d12_descriptor_pool_new(screen,
                                                   D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
                                                   1024);
      if (!screen->rtv_pool || !screen->dsv_pool || !screen->view_pool)
         return false;

      d3d12_init_null_srvs(screen);
      d3d12_init_null_uavs(screen);
      d3d12_init_null_rtv(screen);

      screen->have_load_at_vertex = can_attribute_at_vertex(screen);
      screen->support_shader_images = can_shader_image_load_all_formats(screen);
      static constexpr uint64_t known_good_warp_version = 10ull << 48 | 22000ull << 16;
      bool warp_with_broken_int64 =
         (screen->vendor_id == HW_VENDOR_MICROSOFT && screen->driver_version < known_good_warp_version);
      unsigned supported_int_sizes = 32 | (screen->opts1.Int64ShaderOps && !warp_with_broken_int64 ? 64 : 0);
      unsigned supported_float_sizes = 32 | (screen->opts.DoublePrecisionFloatShaderOps ? 64 : 0);
      dxil_get_nir_compiler_options(&screen->nir_options,
                                    screen->max_shader_model,
                                    supported_int_sizes,
                                    supported_float_sizes);
   }
#endif // HAVE_GALLIUM_D3D12_GRAPHICS

#ifndef _GAMING_XBOX
      ID3D12Device8 *dev8;
      if (SUCCEEDED(screen->dev->QueryInterface(&dev8))) {
         dev8->Release();
         screen->support_create_not_resident = true;
      }
      screen->dev->QueryInterface(&screen->dev10);
#endif

   const char *mesa_version = "Mesa " PACKAGE_VERSION MESA_GIT_SHA1;
   blake3_hasher blake3_ctx;
   uint8_t blake3[BLAKE3_KEY_LEN];
   STATIC_ASSERT(PIPE_UUID_SIZE <= sizeof(blake3));

   /* The driver UUID is used for determining sharability of images and memory
    * between two instances in separate processes.  People who want to
    * share memory need to also check the device UUID or LUID so all this
    * needs to be is the build-id.
    */
   _mesa_blake3_compute(mesa_version, strlen(mesa_version), blake3);
   memcpy(screen->driver_uuid, blake3, PIPE_UUID_SIZE);

   /* The device UUID uniquely identifies the given device within the machine. */
   _mesa_blake3_init(&blake3_ctx);
   _mesa_blake3_update(&blake3_ctx, &screen->vendor_id, sizeof(screen->vendor_id));
   _mesa_blake3_update(&blake3_ctx, &screen->device_id, sizeof(screen->device_id));
   _mesa_blake3_update(&blake3_ctx, &screen->subsys_id, sizeof(screen->subsys_id));
   _mesa_blake3_update(&blake3_ctx, &screen->revision, sizeof(screen->revision));
   _mesa_blake3_final(&blake3_ctx, blake3);
   memcpy(screen->device_uuid, blake3, PIPE_UUID_SIZE);

   d3d12_init_shader_caps(screen);
   d3d12_init_compute_caps(screen);
   d3d12_init_screen_caps(screen);

   return true;
}
