code:
#pragma once

#include "dxvk_barrier.h"
#include "dxvk_bind_mask.h"
#include "dxvk_cmdlist.h"
#include "dxvk_context_state.h"
#include "dxvk_data.h"
#include "dxvk_objects.h"
#include "dxvk_resource.h"
#include "dxvk_util.h"

namespace dxvk {

  /**
   * \brief DXVk context
   *
   * Tracks pipeline state and records command lists.
   * This is where the actual rendering commands are
   * recorded.
   */
  class DxvkContext : public RcObject {
    constexpr static VkDeviceSize StagingBufferSize = 4ull << 20;
  public:

    DxvkContext(const Rc<DxvkDevice>& device);
    ~DxvkContext();

    /**
     * \brief Begins command buffer recording
     *
     * Begins recording a command list. This does
     * not alter any context state other than the
     * active command list.
     * \param [in] cmdList Target command list
     */
    void beginRecording(
      const Rc<DxvkCommandList>& cmdList);

    /**
     * \brief Ends command buffer recording
     *
     * Finishes recording the active command list.
     * The command list can then be submitted to
     * the device.
     *
     * This will not change any context state
     * other than the active command list.
     * \returns Active command list
     */
    Rc<DxvkCommandList> endRecording();

    /**
     * \brief Flushes command buffer
     *
     * Transparently submits the current command
     * buffer and allocates a new one.
     */
    void flushCommandList();

    /**
     * \brief Begins generating query data
     * \param [in] query The query to end
     */
    void beginQuery(
      const Rc<DxvkGpuQuery>&   query);

    /**
     * \brief Ends generating query data
     * \param [in] query The query to end
     */
    void endQuery(
      const Rc<DxvkGpuQuery>&   query);

    /**
     * \brief Sets render targets
     *
     * Creates a framebuffer on the fly if necessary
     * and binds it using \c bindFramebuffer.
     * \param [in] targets Render targets to bind
     */
    void bindRenderTargets(
      const DxvkRenderTargets&    targets);

    /**
     * \brief Binds indirect argument buffer
     *
     * Sets the buffers that are going to be used
     * for indirect draw and dispatch operations.
     * \param [in] argBuffer New argument buffer
     * \param [in] cntBuffer New count buffer
     */
    void bindDrawBuffers(
      const DxvkBufferSlice&      argBuffer,
      const DxvkBufferSlice&      cntBuffer);

    /**
     * \brief Binds index buffer
     *
     * The index buffer will be used when
     * issuing \c drawIndexed commands.
     * \param [in] buffer New index buffer
     * \param [in] indexType Index type
     */
    void bindIndexBuffer(
      const DxvkBufferSlice&      buffer,
            VkIndexType           indexType);

    /**
     * \brief Binds buffer as a shader resource
     *
     * Can be used for uniform and storage buffers.
     * \param [in] slot Resource binding slot
     * \param [in] buffer Buffer to bind
     */
    void bindResourceBuffer(
            uint32_t              slot,
      const DxvkBufferSlice&      buffer);

    /**
     * \brief Binds image or buffer view
     *
     * Can be used for sampled images with a dedicated
     * sampler and for storage images, as well as for
     * uniform texel buffers and storage texel buffers.
     * \param [in] slot Resource binding slot
     * \param [in] imageView Image view to bind
     * \param [in] bufferView Buffer view to bind
     */
    void bindResourceView(
            uint32_t              slot,
      const Rc<DxvkImageView>&    imageView,
      const Rc<DxvkBufferView>&   bufferView);

    /**
     * \brief Binds image sampler
     *
     * Binds a sampler that can be used together with
     * an image in order to read from a texture.
     * \param [in] slot Resource binding slot
     * \param [in] sampler Sampler view to bind
     */
    void bindResourceSampler(
            uint32_t              slot,
      const Rc<DxvkSampler>&      sampler);

    /**
     * \brief Binds a shader to a given state
     *
     * \param [in] stage Target shader stage
     * \param [in] shader The shader to bind
     */
    void bindShader(
            VkShaderStageFlagBits stage,
      const Rc<DxvkShader>&       shader);

    /**
     * \brief Binds vertex buffer
     *
     * When binding a null buffer, stride must be 0.
     * \param [in] binding Vertex buffer binding
     * \param [in] buffer New vertex buffer
     * \param [in] stride Stride between vertices
     */
    void bindVertexBuffer(
            uint32_t              binding,
      const DxvkBufferSlice&      buffer,
            uint32_t              stride);

    /**
     * \brief Binds transform feedback buffer
     *
     * \param [in] binding Xfb buffer binding
     * \param [in] buffer The buffer to bind
     * \param [in] counter Xfb counter buffer
     */
    void bindXfbBuffer(
            uint32_t              binding,
      const DxvkBufferSlice&      buffer,
      const DxvkBufferSlice&      counter);

    /**
     * \brief Blits an image
     *
     * \param [in] dstImage Destination image
     * \param [in] dstMapping Destination swizzle
     * \param [in] srcImage Source image
     * \param [in] srcMapping Source swizzle
     * \param [in] region Blit region
     * \param [in] filter Texture filter
     */
    void blitImage(
      const Rc<DxvkImage>&        dstImage,
      const VkComponentMapping&   dstMapping,
      const Rc<DxvkImage>&        srcImage,
      const VkComponentMapping&   srcMapping,
      const VkImageBlit&          region,
            VkFilter              filter);

    /**
     * \brief Changes image layout
     *
     * Permanently changes the layout for a given
     * image. Immediately performs the transition.
     * \param [in] image The image to transition
     * \param [in] layout New image layout
     */
    void changeImageLayout(
      const Rc<DxvkImage>&        image,
            VkImageLayout         layout);

    /**
     * \brief Clears a buffer with a fixed value
     *
     * Note that both \c offset and \c length must
     * be multiples of four, and that \c value is
     * consumed as a four-byte word.
     * \param [in] buffer The buffer to clear
     * \param [in] offset Offset of the range to clear
     * \param [in] length Bumber of bytes to clear
     * \param [in] value Clear value
     */
    void clearBuffer(
      const Rc<DxvkBuffer>&       buffer,
            VkDeviceSize          offset,
            VkDeviceSize          length,
            uint32_t              value);

    /**
     * \brief Clears a buffer view
     *
     * Unlike \c clearBuffer, this method can be used
     * to clear a buffer view with format conversion.
     * \param [in] bufferView The buffer view
     * \param [in] offset Offset of the region to clear
     * \param [in] length Extent of the region to clear
     * \param [in] value The clear value
     */
    void clearBufferView(
      const Rc<DxvkBufferView>&   bufferView,
            VkDeviceSize          offset,
            VkDeviceSize          length,
            VkClearColorValue     value);

    /**
     * \brief Clears an active render target
     *
     * \param [in] imageView Render target view to clear
     * \param [in] clearAspects Image aspects to clear
     * \param [in] clearValue The clear value
     */
    void clearRenderTarget(
      const Rc<DxvkImageView>&    imageView,
            VkImageAspectFlags    clearAspects,
            VkClearValue          clearValue);

    /**
     * \brief Clears an image view
     *
     * Can be used to clear sub-regions of storage images
     * that are not going to be used as render targets.
     * Implicit format conversion will be applied.
     * \param [in] imageView The image view
     * \param [in] offset Offset of the rect to clear
     * \param [in] extent Extent of the rect to clear
     * \param [in] aspect Aspect mask to clear
     * \param [in] value The clear value
     */
    void clearImageView(
      const Rc<DxvkImageView>&    imageView,
            VkOffset3D            offset,
            VkExtent3D            extent,
            VkImageAspectFlags    aspect,
            VkClearValue          value);

    /**
     * \brief Copies data from one buffer to another
     *
     * \param [in] dstBuffer Destination buffer
     * \param [in] dstOffset Destination data offset
     * \param [in] srcBuffer Source buffer
     * \param [in] srcOffset Source data offset
     * \param [in] numBytes Number of bytes to copy
     */
    void copyBuffer(
      const Rc<DxvkBuffer>&       dstBuffer,
            VkDeviceSize          dstOffset,
      const Rc<DxvkBuffer>&       srcBuffer,
            VkDeviceSize          srcOffset,
            VkDeviceSize          numBytes);

    /**
     * \brief Copies overlapping buffer region
     *
     * Can be used to copy potentially overlapping
     * buffer regions within the same buffer. If
     * the source and destination regions do not
     * overlap, it will behave as \ref copyBuffer.
     * \param [in] dstBuffer The buffer
     * \param [in] dstOffset Offset of target region
     * \param [in] srcOffset Offset of source region
     * \param [in] numBytes Number of bytes to copy
     */
    void copyBufferRegion(
      const Rc<DxvkBuffer>&       dstBuffer,
            VkDeviceSize          dstOffset,
            VkDeviceSize          srcOffset,
            VkDeviceSize          numBytes);

    /**
     * \brief Copies data from a buffer to an image
     *
     * Source data must be packed, except for the row alignment.
     * \param [in] dstImage Destination image
     * \param [in] dstSubresource Destination subresource
     * \param [in] dstOffset Destination area offset
     * \param [in] dstExtent Destination area size
     * \param [in] srcBuffer Source buffer
     * \param [in] srcOffset Source offset, in bytes
     * \param [in] rowAlignment Row alignment, in bytes
     * \param [in] sliceAlignment Slice alignment, in bytes
     */
    void copyBufferToImage(
      const Rc<DxvkImage>&        dstImage,
            VkImageSubresourceLayers dstSubresource,
            VkOffset3D            dstOffset,
            VkExtent3D            dstExtent,
      const Rc<DxvkBuffer>&       srcBuffer,
            VkDeviceSize          srcOffset,
            VkDeviceSize          rowAlignment,
            VkDeviceSize          sliceAlignment);

    /**
     * \brief Copies data from one image to another
     *
     * \param [in] dstImage Destination image
     * \param [in] dstSubresource Destination subresource
     * \param [in] dstOffset Destination area offset
     * \param [in] srcImage Source image
     * \param [in] srcSubresource Source subresource
     * \param [in] srcOffset Source area offset
     * \param [in] extent Size of the area to copy
     */
    void copyImage(
      const Rc<DxvkImage>&        dstImage,
            VkImageSubresourceLayers dstSubresource,
            VkOffset3D            dstOffset,
      const Rc<DxvkImage>&        srcImage,
            VkImageSubresourceLayers srcSubresource,
            VkOffset3D            srcOffset,
            VkExtent3D            extent);

    /**
     * \brief Copies overlapping image region
     *
     * \param [in] dstImage The image
     * \param [in] dstSubresource The image subresource
     * \param [in] dstOffset Destination region offset
     * \param [in] srcOffset Source region offset
     * \param [in] extent Size of the copy region
     */
    void copyImageRegion(
      const Rc<DxvkImage>&        dstImage,
            VkImageSubresourceLayers dstSubresource,
            VkOffset3D            dstOffset,
            VkOffset3D            srcOffset,
            VkExtent3D            extent);

    /**
     * \brief Copies data from an image into a buffer
     *
     * \param [in] dstBuffer Destination buffer
     * \param [in] dstOffset Destination offset, in bytes
     * \param [in] dstExtent Destination data extent
     * \param [in] rowAlignment Row alignment, in bytes
     * \param [in] sliceAlignment Slice alignment, in bytes
     * \param [in] srcImage Source image
     * \param [in] srcSubresource Source subresource
     * \param [in] srcOffset Source area offset
     * \param [in] srcExtent Source area size
     */
    void copyImageToBuffer(
      const Rc<DxvkBuffer>&       dstBuffer,
            VkDeviceSize          dstOffset,
            VkDeviceSize          rowAlignment,
            VkDeviceSize          sliceAlignment,
      const Rc<DxvkImage>&        srcImage,
            VkImageSubresourceLayers srcSubresource,
            VkOffset3D            srcOffset,
            VkExtent3D            srcExtent);

    /**
     * \brief Packs depth-stencil image data to a buffer
     *
     * Packs data from both the depth and stencil aspects
     * of an image into a buffer. The supported formats are:
     * - \c VK_FORMAT_D24_UNORM_S8_UINT: 0xssdddddd
     * - \c VK_FORMAT_D32_SFLOAT_S8_UINT: 0xdddddddd 0x000000ss
     * \param [in] dstBuffer Destination buffer
     * \param [in] dstBufferOffset Destination offset, in bytes
     * \param [in] dstOffset Destination image offset
     * \param [in] dstSize Destination image size
     * \param [in] srcImage Source image
     * \param [in] srcSubresource Source subresource
     * \param [in] srcOffset Source area offset
     * \param [in] srcExtent Source area size
     * \param [in] format Packed data format
     */
    void copyDepthStencilImageToPackedBuffer(
      const Rc<DxvkBuffer>&       dstBuffer,
            VkDeviceSize          dstBufferOffset,
            VkOffset2D            dstOffset,
            VkExtent2D            dstExtent,
      const Rc<DxvkImage>&        srcImage,
            VkImageSubresourceLayers srcSubresource,
            VkOffset2D            srcOffset,
            VkExtent2D            srcExtent,
            VkFormat              format);

    /**
     * \brief Copies image data stored in a linear buffer to another
     *
     * The source and destination regions may overlap, in which case
     * a temporary copy of the source buffer will be created.
     * \param [in] dstBuffer Destination buffer
     * \param [in] dstBufferOffset Destination subresource offset
     * \param [in] dstOffset Destination image offset
     * \param [in] dstSize Total size of the destination image
     * \param [in] srcBuffer Source buffer
     * \param [in] srcBufferOffset Source subresource offset
     * \param [in] srcOffset Source image offset
     * \param [in] srcSize Total size of the source image
     * \param [in] extent Number of pixels to copy
     * \param [in] elementSize Pixel size, in bytes
     */
    void copyPackedBufferImage(
      const Rc<DxvkBuffer>&       dstBuffer,
            VkDeviceSize          dstBufferOffset,
            VkOffset3D            dstOffset,
            VkExtent3D            dstSize,
      const Rc<DxvkBuffer>&       srcBuffer,
            VkDeviceSize          srcBufferOffset,
            VkOffset3D            srcOffset,
            VkExtent3D            srcSize,
            VkExtent3D            extent,
            VkDeviceSize          elementSize);

    /**
     * \brief Unpacks buffer data to a depth-stencil image
     *
     * Writes the packed depth-stencil data to an image.
     * See \ref copyDepthStencilImageToPackedBuffer for
     * which formats are supported and how they are packed.
     * \param [in] dstImage Destination image
     * \param [in] dstSubresource Destination subresource
     * \param [in] dstOffset Image area offset
     * \param [in] dstExtent Image area size
     * \param [in] srcBuffer Packed data buffer
     * \param [in] srcBufferOffset Buffer offset of source image
     * \param [in] srcOffset Offset into the source image
     * \param [in] srcExtent Total size of the source image
     * \param [in] format Packed data format
     */
    void copyPackedBufferToDepthStencilImage(
      const Rc<DxvkImage>&        dstImage,
            VkImageSubresourceLayers dstSubresource,
            VkOffset2D            dstOffset,
            VkExtent2D            dstExtent,
      const Rc<DxvkBuffer>&       srcBuffer,
            VkDeviceSize          srcBufferOffset,
            VkOffset2D            srcOffset,
            VkExtent2D            srcExtent,
            VkFormat              format);

    /**
     * \brief Discards a buffer
     *
     * Renames the buffer in case it is currently
     * used by the GPU in order to avoid having to
     * insert barriers before future commands using
     * the buffer.
     * \param [in] buffer The buffer to discard
     */
    void discardBuffer(
      const Rc<DxvkBuffer>&       buffer);

    /**
     * \brief Discards contents of an image view
     *
     * Discards the current contents of the image
     * and performs a fast layout transition. This
     * may improve performance in some cases.
     * \param [in] imageView View to discard
     * \param [in] discardAspects Image aspects to discard
     */
    void discardImageView(
      const Rc<DxvkImageView>&      imageView,
            VkImageAspectFlags      discardAspects);

    /**
     * \brief Starts compute jobs
     *
     * \param [in] x Number of threads in X direction
     * \param [in] y Number of threads in Y direction
     * \param [in] z Number of threads in Z direction
     */
    void dispatch(
            uint32_t          x,
            uint32_t          y,
            uint32_t          z);

    /**
     * \brief Indirect dispatch call
     *
     * Takes arguments from a buffer. The buffer must contain
     * a structure of the type \c VkDispatchIndirectCommand.
     * \param [in] offset Draw buffer offset
     */
    void dispatchIndirect(
            VkDeviceSize      offset);

    /**
     * \brief Draws primitive without using an index buffer
     *
     * \param [in] vertexCount Number of vertices to draw
     * \param [in] instanceCount Number of instances to render
     * \param [in] firstVertex First vertex in vertex buffer
     * \param [in] firstInstance First instance ID
     */
    void draw(
            uint32_t          vertexCount,
            uint32_t          instanceCount,
            uint32_t          firstVertex,
            uint32_t          firstInstance);

    /**
     * \brief Indirect draw call
     *
     * Takes arguments from a buffer. The structure stored
     * in the buffer must be of type \c VkDrawIndirectCommand.
     * \param [in] offset Draw buffer offset
     * \param [in] count Number of draws
     * \param [in] stride Stride between dispatch calls
     */
    void drawIndirect(
            VkDeviceSize      offset,
            uint32_t          count,
            uint32_t          stride);

    /**
     * \brief Indirect draw call
     *
     * Takes arguments from a buffer. The structure stored
     * in the buffer must be of type \c VkDrawIndirectCommand.
     * \param [in] offset Draw buffer offset
     * \param [in] countOffset Draw count offset
     * \param [in] maxCount Maximum number of draws
     * \param [in] stride Stride between dispatch calls
     */
    void drawIndirectCount(
            VkDeviceSize      offset,
            VkDeviceSize      countOffset,
            uint32_t          maxCount,
            uint32_t          stride);

    /**
     * \brief Draws primitives using an index buffer
     *
     * \param [in] indexCount Number of indices to draw
     * \param [in] instanceCount Number of instances to render
     * \param [in] firstIndex First index within the index buffer
     * \param [in] vertexOffset Vertex ID that corresponds to index 0
     * \param [in] firstInstance First instance ID
     */
    void drawIndexed(
            uint32_t indexCount,
            uint32_t instanceCount,
            uint32_t firstIndex,
            uint32_t vertexOffset,
            uint32_t firstInstance);

    /**
     * \brief Indirect indexed draw call
     *
     * Takes arguments from a buffer. The structure type for
     * the draw buffer is \c VkDrawIndexedIndirectCommand.
     * \param [in] offset Draw buffer offset
     * \param [in] count Number of draws
     * \param [in] stride Stride between dispatch calls
     */
    void drawIndexedIndirect(
            VkDeviceSize      offset,
            uint32_t          count,
            uint32_t          stride);

    /**
     * \brief Indirect indexed draw call
     *
     * Takes arguments from a buffer. The structure type for
     * the draw buffer is \c VkDrawIndexedIndirectCommand.
     * \param [in] offset Draw buffer offset
     * \param [in] countOffset Draw count offset
     * \param [in] maxCount Maximum number of draws
     * \param [in] stride Stride between dispatch calls
     */
    void drawIndexedIndirectCount(
            VkDeviceSize      offset,
            VkDeviceSize      countOffset,
            uint32_t          maxCount,
            uint32_t          stride);

    /**
     * \brief Transform feddback draw call

     * \param [in] counterBuffer Xfb counter buffer
     * \param [in] counterDivisor Vertex stride
     * \param [in] counterBias Counter bias
     */
    void drawIndirectXfb(
      const DxvkBufferSlice&  counterBuffer,
            uint32_t          counterDivisor,
            uint32_t          counterBias);

    /**
     * \brief Emits barrier for render target readback
     *
     * Use between draw calls if the fragment shader
     * reads one of the currently bound render targets.
     */
    void emitRenderTargetReadbackBarrier();

    /**
     * \brief Generates mip maps
     *
     * Uses blitting to generate lower mip levels from
     * the top-most mip level passed to this method.
     * \param [in] imageView The image to generate mips for
     * \param [in] filter The filter to use for generation
     */
    void generateMipmaps(
      const Rc<DxvkImageView>&        imageView,
            VkFilter                  filter);

    /**
     * \brief Initializes a buffer
     *
     * Clears the given buffer to zero. Only safe to call
     * if the buffer is not currently in use by the GPU.
     * \param [in] buffer Buffer to clear
     */
    void initBuffer(
      const Rc<DxvkBuffer>&           buffer);

    /**
     * \brief Initializes an image
     *
     * Transitions the image into its default layout, and clears
     * it to black unless the initial layout is preinitialized.
     * Only safe to call if the image is not in use by the GPU.
     * \param [in] image The image to initialize
     * \param [in] subresources Image subresources
     * \param [in] initialLayout Initial image layout
     */
    void initImage(
      const Rc<DxvkImage>&            image,
      const VkImageSubresourceRange&  subresources,
            VkImageLayout             initialLayout);

    /**
     * \brief Invalidates a buffer's contents
     *
     * Discards a buffer's contents by replacing the
     * backing resource. This allows the host to access
     * the buffer while the GPU is still accessing the
     * original backing resource.
     *
     * \warning If the buffer is used by another context,
     * invalidating it will result in undefined behaviour.
     * \param [in] buffer The buffer to invalidate
     * \param [in] slice New buffer slice handle
     */
    void invalidateBuffer(
      const Rc<DxvkBuffer>&           buffer,
      const DxvkBufferSliceHandle&    slice);

    /**
     * \brief Updates push constants
     *
     * Updates the given push constant range.
     * \param [in] offset Byte offset of data to update
     * \param [in] size Number of bytes to update
     * \param [in] data Pointer to raw data
     */
    void pushConstants(
            uint32_t                  offset,
            uint32_t                  size,
      const void*                     data);

    /**
     * \brief Resolves a multisampled image resource
     *
     * Resolves a multisampled image into a non-multisampled
     * image. The subresources of both images must have the
     * same size and compatible formats.
     * A format can be specified for the resolve operation.
     * If it is \c VK_FORMAT_UNDEFINED, the resolve operation
     * will use the source image format.
     * \param [in] dstImage Destination image
     * \param [in] srcImage Source image
     * \param [in] region Region to resolve
     * \param [in] format Format for the resolve operation
     */
    void resolveImage(
      const Rc<DxvkImage>&            dstImage,
      const Rc<DxvkImage>&            srcImage,
      const VkImageResolve&           region,
            VkFormat                  format);

    /**
     * \brief Resolves a multisampled depth-stencil resource
     *
     * \param [in] dstImage Destination image
     * \param [in] srcImage Source image
     * \param [in] region Region to resolve
     * \param [in] depthMode Resolve mode for depth aspect
     * \param [in] stencilMode Resolve mode for stencil aspect
     */
    void resolveDepthStencilImage(
      const Rc<DxvkImage>&            dstImage,
      const Rc<DxvkImage>&            srcImage,
      const VkImageResolve&           region,
            VkResolveModeFlagBitsKHR  depthMode,
            VkResolveModeFlagBitsKHR  stencilMode);

    /**
     * \brief Transforms image subresource layouts
     *
     * \param [in] dstImage Image to transform
     * \param [in] dstSubresources Subresources
     * \param [in] srcLayout Current layout
     * \param [in] dstLayout Desired layout
     */
    void transformImage(
      const Rc<DxvkImage>&            dstImage,
      const VkImageSubresourceRange&  dstSubresources,
            VkImageLayout             srcLayout,
            VkImageLayout             dstLayout);

    /**
     * \brief Updates a buffer
     *
     * Copies data from the host into a buffer.
     * \param [in] buffer Destination buffer
     * \param [in] offset Offset of sub range to update
     * \param [in] size Length of sub range to update
     * \param [in] data Data to upload
     */
    void updateBuffer(
      const Rc<DxvkBuffer>&           buffer,
            VkDeviceSize              offset,
            VkDeviceSize              size,
      const void*                     data);

    /**
     * \brief Updates an depth-stencil image
     *
     * \param [in] image Destination image
     * \param [in] subsresources Image subresources to update
     * \param [in] imageOffset Offset of the image area to update
     * \param [in] imageExtent Size of the image area to update
     * \param [in] data Source data
     * \param [in] pitchPerRow Row pitch of the source data
     * \param [in] pitchPerLayer Layer pitch of the source data
     * \param [in] format Packed depth-stencil format
     */
    void updateDepthStencilImage(
      const Rc<DxvkImage>&            image,
      const VkImageSubresourceLayers& subresources,
            VkOffset2D                imageOffset,
            VkExtent2D                imageExtent,
      const void*                     data,
            VkDeviceSize              pitchPerRow,
            VkDeviceSize              pitchPerLayer,
            VkFormat                  format);

    /**
     * \brief Uses transfer queue to initialize buffer
     *
     * Only safe to use if the buffer is not in use by the GPU.
     * \param [in] buffer The buffer to initialize
     * \param [in] data The data to copy to the buffer
     */
    void uploadBuffer(
      const Rc<DxvkBuffer>&           buffer,
      const void*                     data);

    /**
     * \brief Uses transfer queue to initialize image
     *
     * Only safe to use if the image is not in use by the GPU.
     * \param [in] image The image to initialize
     * \param [in] subresources Subresources to initialize
     * \param [in] data Source data
     * \param [in] pitchPerRow Row pitch of the source data
     * \param [in] pitchPerLayer Layer pitch of the source data
     */
    void uploadImage(
      const Rc<DxvkImage>&            image,
      const VkImageSubresourceLayers& subresources,
      const void*                     data,
            VkDeviceSize              pitchPerRow,
            VkDeviceSize              pitchPerLayer);

    /**
     * \brief Sets viewports
     *
     * \param [in] viewportCount Number of viewports
     * \param [in] viewports The viewports
     * \param [in] scissorRects Schissor rectangles
     */
    void setViewports(
            uint32_t            viewportCount,
      const VkViewport*         viewports,
      const VkRect2D*           scissorRects);

    /**
     * \brief Sets blend constants
     *
     * Blend constants are a set of four floating
     * point numbers that may be used as an input
     * for blending operations.
     * \param [in] blendConstants Blend constants
     */
    void setBlendConstants(
            DxvkBlendConstants  blendConstants);

    /**
     * \brief Sets depth bias
     *
     * Depth bias has to be enabled explicitly in
     * the rasterizer state to have any effect.
     * \param [in] depthBias Depth bias values
     */
    void setDepthBias(
            DxvkDepthBias       depthBias);

    /**
     * \brief Sets depth bounds
     *
     * Enables or disables the depth bounds test,
     * and updates the values if necessary.
     * \param [in] depthBounds Depth bounds
     */
    void setDepthBounds(
            DxvkDepthBounds     depthBounds);

    /**
     * \brief Sets stencil reference
     *
     * Sets the reference value for stencil compare operations.
     * \param [in] reference Reference value
     */
    void setStencilReference(
            uint32_t            reference);

    /**
     * \brief Sets input assembly state
     * \param [in] ia New state object
     */
    void setInputAssemblyState(
      const DxvkInputAssemblyState& ia);

    /**
     * \brief Sets input layout
     *
     * \param [in] attributeCount Number of vertex attributes
     * \param [in] attributes The vertex attributes
     * \param [in] bindingCount Number of buffer bindings
     * \param [in] bindings Vertex buffer bindigs
     */
    void setInputLayout(
            uint32_t             attributeCount,
      const DxvkVertexAttribute* attributes,
            uint32_t             bindingCount,
      const DxvkVertexBinding*   bindings);

    /**
     * \brief Sets rasterizer state
     * \param [in] rs New state object
     */
    void setRasterizerState(
      const DxvkRasterizerState& rs);

    /**
     * \brief Sets multisample state
     * \param [in] ms New state object
     */
    void setMultisampleState(
      const DxvkMultisampleState& ms);

    /**
     * \brief Sets depth stencil state
     * \param [in] ds New state object
     */
    void setDepthStencilState(
      const DxvkDepthStencilState& ds);

    /**
     * \brief Sets logic op state
     * \param [in] lo New state object
     */
    void setLogicOpState(
      const DxvkLogicOpState&   lo);

    /**
     * \brief Sets blend mode for an attachment
     *
     * \param [in] attachment The attachment index
     * \param [in] blendMode The blend mode
     */
    void setBlendMode(
            uint32_t            attachment,
      const DxvkBlendMode&      blendMode);

    /**
     * \brief Sets specialization constants
     *
     * Replaces current specialization constants with
     * the given list of constant entries. The specId
     * in the shader can be computed with \c getSpecId.
     * \param [in] pipeline Graphics or Compute pipeline
     * \param [in] index Constant index
     * \param [in] value Constant value
     */
    void setSpecConstant(
            VkPipelineBindPoint pipeline,
            uint32_t            index,
            uint32_t            value);

    /**
     * \brief Sets barrier control flags
     *
     * Barrier control flags can be used to control
     * implicit synchronization of compute shaders.
     * \param [in] control New barrier control flags
     */
    void setBarrierControl(
            DxvkBarrierControlFlags control);

    /**
     * \brief Launches a Cuda kernel
     *
     * Since the kernel is launched with an opaque set of
     * kernel-specific parameters which may reference
     * resources bindlessly, such resources must be listed by
     * the caller in the 'buffers' and 'images' parameters so
     * that their access may be tracked appropriately.
     * \param [in] nvxLaunchInfo Kernel launch parameter struct
     * \param [in] buffers List of {buffer,read,write} used by kernel
     * \param [in] images List of {image,read,write} used by kernel
     */
    void launchCuKernelNVX(
      const VkCuLaunchInfoNVX& nvxLaunchInfo,
      const std::vector<std::pair<Rc<DxvkBuffer>, DxvkAccessFlags>>& buffers,
      const std::vector<std::pair<Rc<DxvkImage>, DxvkAccessFlags>>& images);

    /**
     * \brief Signals a GPU event
     * \param [in] event The event
     */
    void signalGpuEvent(
      const Rc<DxvkGpuEvent>&   event);

    /**
     * \brief Writes to a timestamp query
     * \param [in] query The timestamp query
     */
    void writeTimestamp(
      const Rc<DxvkGpuQuery>&   query);

    /**
     * \brief Queues a signal
     *
     * The signal will be notified after all
     * previously submitted commands have
     * finished execution on the GPU.
     * \param [in] signal The signal
     * \param [in] value Signal value
     */
    void signal(
      const Rc<sync::Signal>&   signal,
            uint64_t            value);

    /**
     * \brief Waits for fence
     *
     * Stalls current command list execution until
     * the fence reaches the given value or higher.
     * \param [in] fence Fence to wait on
     * \param [in] value Value to wait on
     */
    void waitFence(const Rc<DxvkFence>& fence, uint64_t value);

    /**
     * \brief Signals fence
     *
     * Signals fence to the given value once the current
     * command list execution completes on the GPU.
     * \param [in] fence Fence to signal
     * \param [in] value Value to signal
     */
    void signalFence(const Rc<DxvkFence>& fence, uint64_t value);

    /**
     * \brief Begins a debug label region
     * \param [in] label The debug label
     *
     * Marks the start of a debug label region. Used by debugging/profiling
     * tools to mark different workloads within a frame.
     */
    void beginDebugLabel(VkDebugUtilsLabelEXT *label);

    /**
     * \brief Ends a debug label region
     *
     * Marks the close of a debug label region. Used by debugging/profiling
     * tools to mark different workloads within a frame.
     */
    void endDebugLabel();

    /**
     * \brief Inserts a debug label
     * \param [in] label The debug label
     *
     * Inserts an instantaneous debug label. Used by debugging/profiling
     * tools to mark different workloads within a frame.
     */
    void insertDebugLabel(VkDebugUtilsLabelEXT *label);

    /**
     * \brief Increments a given stat counter
     *
     * The stat counters will be merged into the global
     * stat counters upon execution of the command list.
     * \param [in] counter Stat counter to increment
     * \param [in] value Increment value
     */
    void addStatCtr(DxvkStatCounter counter, uint64_t value) {
      if (m_cmd != nullptr)
        m_cmd->addStatCtr(counter, value);
    }

  private:

    Rc<DxvkDevice>          m_device;
    DxvkObjects*            m_common;

    Rc<DxvkCommandList>     m_cmd;
    Rc<DxvkDescriptorPool>  m_descPool;
    Rc<DxvkBuffer>          m_zeroBuffer;

    DxvkContextFlags        m_flags;
    DxvkContextState        m_state;
    DxvkContextFeatures     m_features;

    DxvkBarrierSet          m_sdmaAcquires;
    DxvkBarrierSet          m_sdmaBarriers;
    DxvkBarrierSet          m_initBarriers;
    DxvkBarrierSet          m_execAcquires;
    DxvkBarrierSet          m_execBarriers;
    DxvkBarrierSet          m_gfxBarriers;
    DxvkBarrierControlFlags m_barrierControl;

    DxvkGpuQueryManager     m_queryManager;
    DxvkStagingBuffer       m_staging;

    DxvkRenderTargetLayouts m_rtLayouts = { };

    VkPipeline m_gpActivePipeline = VK_NULL_HANDLE;
    VkPipeline m_cpActivePipeline = VK_NULL_HANDLE;

    VkDescriptorSet m_gpSet = VK_NULL_HANDLE;
    VkDescriptorSet m_cpSet = VK_NULL_HANDLE;

    DxvkBindingSet<MaxNumVertexBindings + 1>  m_vbTracked;
    DxvkBindingSet<MaxNumResourceSlots>       m_rcTracked;

    std::vector<DxvkDeferredClear> m_deferredClears;

    std::array<DxvkShaderResourceSlot, MaxNumResourceSlots>  m_rc;
    std::array<DxvkGraphicsPipeline*, 4096> m_gpLookupCache = { };
    std::array<DxvkComputePipeline*,   256> m_cpLookupCache = { };
    std::array<Rc<DxvkFramebuffer>,    512> m_framebufferCache = { };

    void blitImageFb(
      const Rc<DxvkImage>&        dstImage,
      const Rc<DxvkImage>&        srcImage,
      const VkImageBlit&          region,
      const VkComponentMapping&   mapping,
            VkFilter              filter);

    void blitImageHw(
      const Rc<DxvkImage>&        dstImage,
      const Rc<DxvkImage>&        srcImage,
      const VkImageBlit&          region,
            VkFilter              filter);

    template<bool ToImage>
    void copyImageBufferData(
            DxvkCmdBuffer         cmd,
      const Rc<DxvkImage>&        image,
      const VkImageSubresourceLayers& imageSubresource,
            VkOffset3D            imageOffset,
            VkExtent3D            imageExtent,
            VkImageLayout         imageLayout,
      const DxvkBufferSliceHandle& bufferSlice,
            VkDeviceSize          bufferRowAlignment,
            VkDeviceSize          bufferSliceAlignment);

    void copyImageHostData(
            DxvkCmdBuffer         cmd,
      const Rc<DxvkImage>&        image,
      const VkImageSubresourceLayers& imageSubresource,
            VkOffset3D            imageOffset,
            VkExtent3D            imageExtent,
      const void*                 hostData,
            VkDeviceSize          rowPitch,
            VkDeviceSize          slicePitch);

    void clearImageViewFb(
      const Rc<DxvkImageView>&    imageView,
            VkOffset3D            offset,
            VkExtent3D            extent,
            VkImageAspectFlags    aspect,
            VkClearValue          value);

    void clearImageViewCs(
      const Rc<DxvkImageView>&    imageView,
            VkOffset3D            offset,
            VkExtent3D            extent,
            VkClearValue          value);

    void copyImageHw(
      const Rc<DxvkImage>&        dstImage,
            VkImageSubresourceLayers dstSubresource,
            VkOffset3D            dstOffset,
      const Rc<DxvkImage>&        srcImage,
            VkImageSubresourceLayers srcSubresource,
            VkOffset3D            srcOffset,
            VkExtent3D            extent);

    void copyImageFb(
      const Rc<DxvkImage>&        dstImage,
            VkImageSubresourceLayers dstSubresource,
            VkOffset3D            dstOffset,
      const Rc<DxvkImage>&        srcImage,
            VkImageSubresourceLayers srcSubresource,
            VkOffset3D            srcOffset,
            VkExtent3D            extent);

    bool copyImageClear(
      const Rc<DxvkImage>&        dstImage,
            VkImageSubresourceLayers dstSubresource,
            VkOffset3D            dstOffset,
            VkExtent3D            dstExtent,
      const Rc<DxvkImage>&        srcImage,
            VkImageSubresourceLayers srcSubresource);

    void resolveImageHw(
      const Rc<DxvkImage>&            dstImage,
      const Rc<DxvkImage>&            srcImage,
      const VkImageResolve&           region);

    void resolveImageDs(
      const Rc<DxvkImage>&            dstImage,
      const Rc<DxvkImage>&            srcImage,
      const VkImageResolve&           region,
            VkResolveModeFlagBitsKHR  depthMode,
            VkResolveModeFlagBitsKHR  stencilMode);

    void resolveImageFb(
      const Rc<DxvkImage>&            dstImage,
      const Rc<DxvkImage>&            srcImage,
      const VkImageResolve&           region,
            VkFormat                  format,
            VkResolveModeFlagBitsKHR  depthMode,
            VkResolveModeFlagBitsKHR  stencilMode);

    void performClear(
      const Rc<DxvkImageView>&        imageView,
            int32_t                   attachmentIndex,
            VkImageAspectFlags        discardAspects,
            VkImageAspectFlags        clearAspects,
            VkClearValue              clearValue);

    void deferClear(
      const Rc<DxvkImageView>&        imageView,
            VkImageAspectFlags        clearAspects,
            VkClearValue              clearValue);

    void deferDiscard(
      const Rc<DxvkImageView>&        imageView,
            VkImageAspectFlags        discardAspects);

    void flushClears(
            bool                      useRenderPass);

    void flushSharedImages();

    void startRenderPass();
    void spillRenderPass(bool suspend);

    void renderPassBindFramebuffer(
      const DxvkFramebufferInfo&  framebufferInfo,
      const DxvkRenderPassOps&    ops,
            uint32_t              clearValueCount,
      const VkClearValue*         clearValues);

    void renderPassUnbindFramebuffer();

    void resetRenderPassOps(
      const DxvkRenderTargets&    renderTargets,
            DxvkRenderPassOps&    renderPassOps);

    void startTransformFeedback();
    void pauseTransformFeedback();

    void unbindComputePipeline();
    bool updateComputePipeline();
    bool updateComputePipelineState();

    void unbindGraphicsPipeline();
    bool updateGraphicsPipeline();
    bool updateGraphicsPipelineState();

    void updateComputeShaderResources();
    void updateGraphicsShaderResources();

    template<VkPipelineBindPoint BindPoint>
    void updateShaderResources(
      const DxvkPipelineLayout*     layout);

    template<VkPipelineBindPoint BindPoint>
    void updateShaderDescriptorSetBinding(
            VkDescriptorSet         set,
      const DxvkPipelineLayout*     layout);

    DxvkFramebufferInfo makeFramebufferInfo(
      const DxvkRenderTargets&      renderTargets);

    void updateFramebuffer();

    void applyRenderTargetLoadLayouts();

    void applyRenderTargetStoreLayouts();

    void transitionRenderTargetLayouts(
            DxvkBarrierSet&         barriers,
            bool                    sharedOnly);

    void transitionColorAttachment(
            DxvkBarrierSet&         barriers,
      const DxvkAttachment&         attachment,
            VkImageLayout           oldLayout);

    void transitionDepthAttachment(
            DxvkBarrierSet&         barriers,
      const DxvkAttachment&         attachment,
            VkImageLayout           oldLayout);

    void updateRenderTargetLayouts(
      const DxvkFramebufferInfo&    newFb,
      const DxvkFramebufferInfo&    oldFb);

    void prepareImage(
            DxvkBarrierSet&         barriers,
      const Rc<DxvkImage>&          image,
      const VkImageSubresourceRange& subresources,
            bool                    flushClears = true);

    bool updateIndexBufferBinding();
    void updateVertexBufferBindings();

    void updateTransformFeedbackBuffers();
    void updateTransformFeedbackState();

    void updateDynamicState();

    template<VkPipelineBindPoint BindPoint>
    void updatePushConstants();

    bool commitComputeState();

    template<bool Indexed, bool Indirect>
    bool commitGraphicsState();

    void commitComputeInitBarriers();
    void commitComputePostBarriers();

    template<bool Indexed, bool Indirect, bool DoEmit>
    void commitGraphicsBarriers();

    template<bool DoEmit>
    DxvkAccessFlags checkGfxBufferBarrier(
      const DxvkBufferSlice&          slice,
            VkPipelineStageFlags      stages,
            VkAccessFlags             access);

    template<bool DoEmit>
    DxvkAccessFlags checkGfxImageBarrier(
      const Rc<DxvkImageView>&        imageView,
            VkPipelineStageFlags      stages,
            VkAccessFlags             access);

    void emitMemoryBarrier(
            VkDependencyFlags         flags,
            VkPipelineStageFlags      srcStages,
            VkAccessFlags             srcAccess,
            VkPipelineStageFlags      dstStages,
            VkAccessFlags             dstAccess);

    VkDescriptorSet allocateDescriptorSet(
            VkDescriptorSetLayout     layout);

    void trackDrawBuffer();

    bool tryInvalidateDeviceLocalBuffer(
      const Rc<DxvkBuffer>&           buffer,
            VkDeviceSize              copySize);

    DxvkGraphicsPipeline* lookupGraphicsPipeline(
      const DxvkGraphicsPipelineShaders&  shaders);

    DxvkComputePipeline* lookupComputePipeline(
      const DxvkComputePipelineShaders&   shaders);

    Rc<DxvkFramebuffer> lookupFramebuffer(
      const DxvkFramebufferInfo&      framebufferInfo);

    Rc<DxvkBuffer> createZeroBuffer(
            VkDeviceSize              size);

  };

}

repo_name: orochi/dxvk


code:
#pragma once

#include "dxvk_buffer.h"
#include "dxvk_compute.h"
#include "dxvk_constant_state.h"
#include "dxvk_framebuffer.h"
#include "dxvk_graphics.h"
#include "dxvk_image.h"
#include "dxvk_limits.h"
#include "dxvk_pipelayout.h"
#include "dxvk_sampler.h"
#include "dxvk_shader.h"

namespace dxvk {

  /**
   * \brief Graphics pipeline state flags
   *
   * Stores some information on which state
   * of the graphics and compute pipelines
   * has changed and/or needs to be updated.
   */
  enum class DxvkContextFlag : uint32_t  {
    GpRenderPassBound,          ///< Render pass is currently bound
    GpRenderPassSuspended,      ///< Render pass is currently suspended
    GpXfbActive,                ///< Transform feedback is enabled
    GpDirtyFramebuffer,         ///< Framebuffer binding is out of date
    GpDirtyPipeline,            ///< Graphics pipeline binding is out of date
    GpDirtyPipelineState,       ///< Graphics pipeline needs to be recompiled
    GpDirtyResources,           ///< Graphics pipeline resource bindings are out of date
    GpDirtyDescriptorBinding,   ///< Graphics descriptor set needs to be rebound
    GpDirtyVertexBuffers,       ///< Vertex buffer bindings are out of date
    GpDirtyIndexBuffer,         ///< Index buffer binding are out of date
    GpDirtyXfbBuffers,          ///< Transform feedback buffer bindings are out of date
    GpDirtyBlendConstants,      ///< Blend constants have changed
    GpDirtyDepthBias,           ///< Depth bias has changed
    GpDirtyDepthBounds,         ///< Depth bounds have changed
    GpDirtyStencilRef,          ///< Stencil reference has changed
    GpDirtyViewport,            ///< Viewport state has changed
    GpDynamicBlendConstants,    ///< Blend constants are dynamic
    GpDynamicDepthBias,         ///< Depth bias is dynamic
    GpDynamicDepthBounds,       ///< Depth bounds are dynamic
    GpDynamicStencilRef,        ///< Stencil reference is dynamic

    CpDirtyPipeline,            ///< Compute pipeline binding are out of date
    CpDirtyPipelineState,       ///< Compute pipeline needs to be recompiled
    CpDirtyResources,           ///< Compute pipeline resource bindings are out of date
    CpDirtyDescriptorBinding,   ///< Compute descriptor set needs to be rebound

    DirtyDrawBuffer,            ///< Indirect argument buffer is dirty
    DirtyPushConstants,         ///< Push constant data has changed
  };

  using DxvkContextFlags = Flags<DxvkContextFlag>;

  /**
   * \brief Context feature bits
   */
  enum class DxvkContextFeature {
    NullDescriptors,
    ExtendedDynamicState,
  };

  using DxvkContextFeatures = Flags<DxvkContextFeature>;

  /**
   * \brief Barrier control flags
   *
   * These flags specify what (not) to
   * synchronize implicitly.
   */
  enum class DxvkBarrierControl : uint32_t {
    IgnoreWriteAfterWrite       = 1,
    IgnoreGraphicsBarriers      = 2,
  };

  using DxvkBarrierControlFlags  = Flags<DxvkBarrierControl>;

  struct DxvkIndirectDrawState {
    DxvkBufferSlice argBuffer;
    DxvkBufferSlice cntBuffer;
  };

  struct DxvkVertexInputState {
    DxvkBufferSlice indexBuffer;
    VkIndexType     indexType   = VK_INDEX_TYPE_UINT32;

    std::array<DxvkBufferSlice, DxvkLimits::MaxNumVertexBindings> vertexBuffers = { };
    std::array<uint32_t,        DxvkLimits::MaxNumVertexBindings> vertexStrides = { };
  };

  struct DxvkViewportState {
    std::array<VkViewport, DxvkLimits::MaxNumViewports> viewports    = { };
    std::array<VkRect2D,   DxvkLimits::MaxNumViewports> scissorRects = { };
  };

  struct DxvkOutputMergerState {
    std::array<VkClearValue, MaxNumRenderTargets + 1> clearValues = { };

    DxvkRenderTargets   renderTargets;
    DxvkRenderPassOps   renderPassOps;
    DxvkFramebufferInfo framebufferInfo;
  };

  struct DxvkPushConstantState {
    char data[MaxPushConstantSize];
  };

  struct DxvkXfbState {
    std::array<DxvkBufferSlice, MaxNumXfbBuffers> buffers;
    std::array<DxvkBufferSlice, MaxNumXfbBuffers> counters;
  };

  struct DxvkGraphicsPipelineState {
    DxvkGraphicsPipelineShaders   shaders;
    DxvkGraphicsPipelineStateInfo state;
    DxvkGraphicsPipelineFlags     flags;
    DxvkGraphicsPipeline*         pipeline = nullptr;
  };

  struct DxvkComputePipelineState {
    DxvkComputePipelineShaders    shaders;
    DxvkComputePipelineStateInfo  state;
    DxvkComputePipeline*          pipeline = nullptr;
  };

  struct DxvkDynamicState {
    DxvkBlendConstants  blendConstants    = { 0.0f, 0.0f, 0.0f, 0.0f };
    DxvkDepthBias       depthBias         = { 0.0f, 0.0f, 0.0f };
    DxvkDepthBounds     depthBounds       = { false, 0.0f, 1.0f };
    uint32_t            stencilReference  = 0;
  };

  struct DxvkDeferredClear {
    Rc<DxvkImageView> imageView;
    VkImageAspectFlags discardAspects;
    VkImageAspectFlags clearAspects;
    VkClearValue clearValue;
  };

  /**
   * \brief Pipeline state
   *
   * Stores all bound shaders, resources,
   * and constant pipeline state objects.
   */
  struct DxvkContextState {
    DxvkIndirectDrawState     id;
    DxvkVertexInputState      vi;
    DxvkViewportState         vp;
    DxvkOutputMergerState     om;
    DxvkPushConstantState     pc;
    DxvkXfbState              xfb;
    DxvkDynamicState          dyn;

    DxvkGraphicsPipelineState gp;
    DxvkComputePipelineState  cp;
  };

}


code:
#include "dxvk_cs.h"

namespace dxvk {

  DxvkCsChunk::DxvkCsChunk() {

  }

  DxvkCsChunk::~DxvkCsChunk() {
    this->reset();
  }

  void DxvkCsChunk::init(DxvkCsChunkFlags flags) {
    m_flags = flags;
  }

  void DxvkCsChunk::executeAll(DxvkContext* ctx) {
    auto cmd = m_head;

    if (m_flags.test(DxvkCsChunkFlag::SingleUse)) {
      m_commandOffset = 0;

      while (cmd != nullptr) {
        auto next = cmd->next();
        cmd->exec(ctx);
        cmd->~DxvkCsCmd();
        cmd = next;
      }

      m_head = nullptr;
      m_tail = nullptr;
    } else {
      while (cmd != nullptr) {
        cmd->exec(ctx);
        cmd = cmd->next();
      }
    }
  }

  void DxvkCsChunk::reset() {
    auto cmd = m_head;

    while (cmd != nullptr) {
      auto next = cmd->next();
      cmd->~DxvkCsCmd();
      cmd = next;
    }

    m_head = nullptr;
    m_tail = nullptr;

    m_commandOffset = 0;
  }

  DxvkCsChunkPool::DxvkCsChunkPool() {

  }

  DxvkCsChunkPool::~DxvkCsChunkPool() {
    for (DxvkCsChunk* chunk : m_chunks)
      delete chunk;
  }

  DxvkCsChunk* DxvkCsChunkPool::allocChunk(DxvkCsChunkFlags flags) {
    DxvkCsChunk* chunk = nullptr;

    { std::lock_guard<dxvk::mutex> lock(m_mutex);

      if (m_chunks.size() != 0) {
        chunk = m_chunks.back();
        m_chunks.pop_back();
      }
    }

    if (!chunk)
      chunk = new DxvkCsChunk();

    chunk->init(flags);
    return chunk;
  }

  void DxvkCsChunkPool::freeChunk(DxvkCsChunk* chunk) {
    chunk->reset();

    std::lock_guard<dxvk::mutex> lock(m_mutex);
    m_chunks.push_back(chunk);
  }

  DxvkCsThread::DxvkCsThread(
    const Rc<DxvkDevice>&   device,
    const Rc<DxvkContext>&  context)
  : m_device(device), m_context(context),
    m_thread([this] { threadFunc(); }) {

  }

  DxvkCsThread::~DxvkCsThread() {
    { std::unique_lock<dxvk::mutex> lock(m_mutex);
      m_stopped.store(true);
    }

    m_condOnAdd.notify_one();
    m_thread.join();
  }

  uint64_t DxvkCsThread::dispatchChunk(DxvkCsChunkRef&& chunk) {
    uint64_t seq;

    { std::unique_lock<dxvk::mutex> lock(m_mutex);
      seq = ++m_chunksDispatched;
      m_chunksQueued.push(std::move(chunk));
    }

    m_condOnAdd.notify_one();
    return seq;
  }

  void DxvkCsThread::synchronize(uint64_t seq) {
    // Avoid locking if we know the sync is a no-op, may
    // reduce overhead if this is being called frequently
    if (seq > m_chunksExecuted.load(std::memory_order_acquire)) {
      std::unique_lock<dxvk::mutex> lock(m_mutex);

      if (seq == SynchronizeAll)
        seq = m_chunksDispatched.load();

      auto t0 = dxvk::high_resolution_clock::now();
      m_condOnSync.wait(lock, [this, seq] {
        return m_chunksExecuted.load() >= seq;
      });
      auto t1 = dxvk::high_resolution_clock::now();
      auto ticks = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0);

      m_device->addStatCtr(DxvkStatCounter::CsSyncCount, 1);
      m_device->addStatCtr(DxvkStatCounter::CsSyncTicks, ticks.count());
    }
  }

  void DxvkCsThread::threadFunc() {
    env::setThreadName("dxvk-cs");

    DxvkCsChunkRef chunk;

    try {
      while (!m_stopped.load()) {
        { std::unique_lock<dxvk::mutex> lock(m_mutex);
          if (chunk) {
            m_chunksExecuted++;
            m_condOnSync.notify_one();

            chunk = DxvkCsChunkRef();
          }

          if (m_chunksQueued.size() == 0) {
            m_condOnAdd.wait(lock, [this] {
              return (m_chunksQueued.size() != 0)
                  || (m_stopped.load());
            });
          }

          if (m_chunksQueued.size() != 0) {
            chunk = std::move(m_chunksQueued.front());
            m_chunksQueued.pop();
          }
        }

        if (chunk) {
          m_context->addStatCtr(DxvkStatCounter::CsChunkCount, 1);
          chunk->executeAll(m_context.ptr());
        }
      }
    } catch (const DxvkError& e) {
      Logger::err("Exception on CS thread!");
      Logger::err(e.message());
    }
  }

}


code:
#pragma once

#include <atomic>
#include <condition_variable>
#include <mutex>
#include <queue>

#include "../util/thread.h"

#include "dxvk_device.h"
#include "dxvk_context.h"

namespace dxvk {

  /**
   * \brief Command stream operation
   *
   * An abstract representation of an operation
   * that can be recorded into a command list.
   */
  class DxvkCsCmd {

  public:

    virtual ~DxvkCsCmd() { }

    /**
     * \brief Retrieves next command in a command chain
     *
     * This can be used to quickly iterate
     * over commands within a chunk.
     * \returns Pointer the next command
     */
    DxvkCsCmd* next() const {
      return m_next;
    }

    /**
     * \brief Sets next command in a command chain
     * \param [in] next Next command
     */
    void setNext(DxvkCsCmd* next) {
      m_next = next;
    }

    /**
     * \brief Executes embedded commands
     * \param [in] ctx The target context
     */
    virtual void exec(DxvkContext* ctx) const = 0;

  private:

    DxvkCsCmd* m_next = nullptr;

  };

  /**
   * \brief Typed command
   *
   * Stores a function object which is
   * used to execute an embedded command.
   */
  template<typename T>
  class alignas(16) DxvkCsTypedCmd : public DxvkCsCmd {

  public:

    DxvkCsTypedCmd(T&& cmd)
    : m_command(std::move(cmd)) { }

    DxvkCsTypedCmd             (DxvkCsTypedCmd&&) = delete;
    DxvkCsTypedCmd& operator = (DxvkCsTypedCmd&&) = delete;

    void exec(DxvkContext* ctx) const {
      m_command(ctx);
    }

  private:

    T m_command;

  };

  /**
   * \brief Typed command with metadata
   *
   * Stores a function object and an arbitrary
   * data structure which can be modified after
   * submitting the command to a cs chunk.
   */
  template<typename T, typename M>
  class alignas(16) DxvkCsDataCmd : public DxvkCsCmd {

  public:

    template<typename... Args>
    DxvkCsDataCmd(T&& cmd, Args&&... args)
    : m_command (std::move(cmd)),
      m_data    (std::forward<Args>(args)...) { }

    DxvkCsDataCmd             (DxvkCsDataCmd&&) = delete;
    DxvkCsDataCmd& operator = (DxvkCsDataCmd&&) = delete;

    void exec(DxvkContext* ctx) const {
      m_command(ctx, &m_data);
    }

    M* data() {
      return &m_data;
    }

  private:

    T m_command;
    M m_data;

  };

  /**
   * \brief Submission flags
   */
  enum class DxvkCsChunkFlag : uint32_t {
    /// Indicates that the submitted chunk will
    /// no longer be needed after one submission.
    SingleUse,
  };

  using DxvkCsChunkFlags = Flags<DxvkCsChunkFlag>;

  /**
   * \brief Command chunk
   *
   * Stores a list of commands.
   */
  class DxvkCsChunk : public RcObject {
    constexpr static size_t MaxBlockSize = 16384;
  public:

    DxvkCsChunk();
    ~DxvkCsChunk();

    /**
     * \brief Checks whether the chunk is empty
     * \returns \c true if the chunk is empty
     */
    bool empty() const {
      return m_commandOffset == 0;
    }

    /**
     * \brief Tries to add a command to the chunk
     *
     * If the given command can be added to the chunk, it
     * will be consumed. Otherwise, a new chunk must be
     * created which is large enough to hold the command.
     * \param [in] command The command to add
     * \returns \c true on success, \c false if
     *          a new chunk needs to be allocated
     */
    template<typename T>
    bool push(T& command) {
      using FuncType = DxvkCsTypedCmd<T>;

      if (unlikely(m_commandOffset > MaxBlockSize - sizeof(FuncType)))
        return false;

      DxvkCsCmd* tail = m_tail;

      m_tail = new (m_data + m_commandOffset)
        FuncType(std::move(command));

      if (likely(tail != nullptr))
        tail->setNext(m_tail);
      else
        m_head = m_tail;

      m_commandOffset += sizeof(FuncType);
      return true;
    }

    /**
     * \brief Adds a command with data to the chunk
     *
     * \param [in] command The command to add
     * \param [in] args Constructor args for the data object
     * \returns Pointer to the data object, or \c nullptr
     */
    template<typename M, typename T, typename... Args>
    M* pushCmd(T& command, Args&&... args) {
      using FuncType = DxvkCsDataCmd<T, M>;

      if (unlikely(m_commandOffset > MaxBlockSize - sizeof(FuncType)))
        return nullptr;

      FuncType* func = new (m_data + m_commandOffset)
        FuncType(std::move(command), std::forward<Args>(args)...);

      if (likely(m_tail != nullptr))
        m_tail->setNext(func);
      else
        m_head = func;
      m_tail = func;

      m_commandOffset += sizeof(FuncType);
      return func->data();
    }

    /**
     * \brief Initializes chunk for recording
     * \param [in] flags Chunk flags
     */
    void init(DxvkCsChunkFlags flags);

    /**
     * \brief Executes all commands
     *
     * This will also reset the chunk
     * so that it can be reused.
     * \param [in] ctx The context
     */
    void executeAll(DxvkContext* ctx);

    /**
     * \brief Resets chunk
     *
     * Destroys all recorded commands and
     * marks the chunk itself as empty, so
     * that it can be reused later.
     */
    void reset();

  private:

    size_t m_commandOffset = 0;

    DxvkCsCmd* m_head = nullptr;
    DxvkCsCmd* m_tail = nullptr;

    DxvkCsChunkFlags m_flags;

    alignas(64)
    char m_data[MaxBlockSize];

  };

  /**
   * \brief Chunk pool
   *
   * Implements a pool of CS chunks which can be
   * recycled. The goal is to reduce the number
   * of dynamic memory allocations.
   */
  class DxvkCsChunkPool {

  public:

    DxvkCsChunkPool();
    ~DxvkCsChunkPool();

    DxvkCsChunkPool             (const DxvkCsChunkPool&) = delete;
    DxvkCsChunkPool& operator = (const DxvkCsChunkPool&) = delete;

    /**
     * \brief Allocates a chunk
     *
     * Takes an existing chunk from the pool,
     * or creates a new one if necessary.
     * \param [in] flags Chunk flags
     * \returns Allocated chunk object
     */
    DxvkCsChunk* allocChunk(DxvkCsChunkFlags flags);

    /**
     * \brief Releases a chunk
     *
     * Resets the chunk and adds it to the pool.
     * \param [in] chunk Chunk to release
     */
    void freeChunk(DxvkCsChunk* chunk);

  private:

    dxvk::mutex               m_mutex;
    std::vector<DxvkCsChunk*> m_chunks;

  };

  /**
   * \brief Chunk reference
   *
   * Implements basic reference counting for
   * CS chunks and returns them to the pool
   * as soon as they are no longer needed.
   */
  class DxvkCsChunkRef {

  public:

    DxvkCsChunkRef() { }
    DxvkCsChunkRef(
      DxvkCsChunk*      chunk,
      DxvkCsChunkPool*  pool)
    : m_chunk (chunk),
      m_pool  (pool) {
      this->incRef();
    }

    DxvkCsChunkRef(const DxvkCsChunkRef& other)
    : m_chunk (other.m_chunk),
      m_pool  (other.m_pool) {
      this->incRef();
    }

    DxvkCsChunkRef(DxvkCsChunkRef&& other)
    : m_chunk (other.m_chunk),
      m_pool  (other.m_pool) {
      other.m_chunk = nullptr;
      other.m_pool  = nullptr;
    }

    DxvkCsChunkRef& operator = (const DxvkCsChunkRef& other) {
      other.incRef();
      this->decRef();
      this->m_chunk = other.m_chunk;
      this->m_pool  = other.m_pool;
      return *this;
    }

    DxvkCsChunkRef& operator = (DxvkCsChunkRef&& other) {
      this->decRef();
      this->m_chunk = other.m_chunk;
      this->m_pool  = other.m_pool;
      other.m_chunk = nullptr;
      other.m_pool  = nullptr;
      return *this;
    }

    ~DxvkCsChunkRef() {
      this->decRef();
    }

    DxvkCsChunk* operator -> () const {
      return m_chunk;
    }

    operator bool () const {
      return m_chunk != nullptr;
    }

  private:

    DxvkCsChunk*      m_chunk = nullptr;
    DxvkCsChunkPool*  m_pool  = nullptr;

    void incRef() const {
      if (m_chunk != nullptr)
        m_chunk->incRef();
    }

    void decRef() const {
      if (m_chunk != nullptr && m_chunk->decRef() == 0)
        m_pool->freeChunk(m_chunk);
    }

  };

  /**
   * \brief Command stream thread
   *
   * Spawns a thread that will execute
   * commands on a DXVK context.
   */
  class DxvkCsThread {

  public:

    constexpr static uint64_t SynchronizeAll = ~0ull;

    DxvkCsThread(
      const Rc<DxvkDevice>&   device,
      const Rc<DxvkContext>&  context);
    ~DxvkCsThread();

    /**
     * \brief Dispatches an entire chunk
     *
     * Can be used to efficiently play back large
     * command lists recorded on another thread.
     * \param [in] chunk The chunk to dispatch
     * \returns Sequence number of the submission
     */
    uint64_t dispatchChunk(DxvkCsChunkRef&& chunk);

    /**
     * \brief Synchronizes with the thread
     *
     * This waits for all chunks in the dispatch queue to
     * be processed by the thread, up to the given sequence
     * number. If the sequence number is 0, this will wait
     * for all pending chunks to complete execution.
     * \param [in] seq Sequence number to wait for.
     */
    void synchronize(uint64_t seq);

    /**
     * \brief Retrieves last executed sequence number
     *
     * Can be used to avoid synchronization in some cases.
     * \returns Sequence number of last executed chunk
     */
    uint64_t lastSequenceNumber() const {
      return m_chunksExecuted.load();
    }

  private:

    Rc<DxvkDevice>              m_device;
    Rc<DxvkContext>             m_context;

    std::atomic<uint64_t>       m_chunksDispatched = { 0ull };
    std::atomic<uint64_t>       m_chunksExecuted   = { 0ull };

    std::atomic<bool>           m_stopped = { false };
    dxvk::mutex                 m_mutex;
    dxvk::condition_variable    m_condOnAdd;
    dxvk::condition_variable    m_condOnSync;
    std::queue<DxvkCsChunkRef>  m_chunksQueued;
    dxvk::thread                m_thread;

    void threadFunc();

  };

}


code:
#include <cstring>

#include "dxvk_data.h"

namespace dxvk {

  DxvkDataBuffer:: DxvkDataBuffer() { }
  DxvkDataBuffer::DxvkDataBuffer(size_t size)
  : m_data(new char[size]), m_size(size) { }

  DxvkDataBuffer::~DxvkDataBuffer() {
    delete[] m_data;
  }

  DxvkDataSlice DxvkDataBuffer::alloc(size_t n) {
    const size_t offset = m_offset;

    if (offset + n <= m_size) {
      m_offset += align(n, CACHE_LINE_SIZE);
      return DxvkDataSlice(this, offset, n);
    } return DxvkDataSlice();
  }

}


code:
#pragma once

#include "dxvk_include.h"

namespace dxvk {

  class DxvkDataSlice;

  /**
   * \brief Data buffer
   *
   * Provides a fixed-size buffer with a linear memory
   * allocator for arbitrary data. Can be used to copy
   * data to or from resources. Note that allocations
   * will be aligned to a cache line boundary.
   */
  class DxvkDataBuffer : public RcObject {
    friend class DxvkDataSlice;
  public:

    DxvkDataBuffer();
    DxvkDataBuffer(size_t size);
    ~DxvkDataBuffer();

    /**
     * \brief Allocates a slice
     *
     * If the desired slice length is larger than the
     * number of bytes left in the buffer, this will
     * fail and the returned slice points to \c nullptr.
     * \param [in] n Number of bytes to allocate
     * \returns The slice, or an empty slice on failure
     */
    DxvkDataSlice alloc(size_t n);

  private:

    char*   m_data   = nullptr;
    size_t  m_size   = 0;
    size_t  m_offset = 0;

  };

  /**
   * \brief Data buffer slice
   *
   * A slice of a \ref DxvkDataBuffer which stores
   * a strong reference to the backing buffer object.
   */
  class DxvkDataSlice {

  public:

    DxvkDataSlice() { }
    DxvkDataSlice(
      const Rc<DxvkDataBuffer>& buffer,
            size_t              offset,
            size_t              length)
    : m_buffer(buffer),
      m_offset(offset),
      m_length(length) { }

    void* ptr() const {
      return m_buffer != nullptr
        ? m_buffer->m_data + m_offset
        : nullptr;
    }

    size_t offset() const { return m_offset; }
    size_t length() const { return m_length; }

  private:

    Rc<DxvkDataBuffer> m_buffer;
    size_t             m_offset = 0;
    size_t             m_length = 0;

  };

}


code:
#include "dxvk_descriptor.h"
#include "dxvk_device.h"

namespace dxvk {

  DxvkDescriptorPool::DxvkDescriptorPool(const Rc<vk::DeviceFn>& vkd)
  : m_vkd(vkd) {
    constexpr uint32_t MaxSets = 2048;

    std::array<VkDescriptorPoolSize, 9> pools = {{
      { VK_DESCRIPTOR_TYPE_SAMPLER,                MaxSets * 2 },
      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,          MaxSets * 3 },
      { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,          MaxSets / 8 },
      { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,         MaxSets * 3 },
      { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,         MaxSets / 8 },
      { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,   MaxSets * 3 },
      { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,   MaxSets / 8 },
      { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, MaxSets * 3 },
      { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, MaxSets * 2 } }};

    VkDescriptorPoolCreateInfo info;
    info.sType         = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
    info.pNext         = nullptr;
    info.flags         = 0;
    info.maxSets       = MaxSets;
    info.poolSizeCount = pools.size();
    info.pPoolSizes    = pools.data();

    if (m_vkd->vkCreateDescriptorPool(m_vkd->device(), &info, nullptr, &m_pool) != VK_SUCCESS)
      throw DxvkError("DxvkDescriptorPool: Failed to create descriptor pool");
  }

  DxvkDescriptorPool::~DxvkDescriptorPool() {
    m_vkd->vkDestroyDescriptorPool(
      m_vkd->device(), m_pool, nullptr);
  }

  VkDescriptorSet DxvkDescriptorPool::alloc(VkDescriptorSetLayout layout) {
    VkDescriptorSetAllocateInfo info;
    info.sType              = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
    info.pNext              = nullptr;
    info.descriptorPool     = m_pool;
    info.descriptorSetCount = 1;
    info.pSetLayouts        = &layout;

    VkDescriptorSet set = VK_NULL_HANDLE;
    if (m_vkd->vkAllocateDescriptorSets(m_vkd->device(), &info, &set) != VK_SUCCESS)
      return VK_NULL_HANDLE;
    return set;
  }

  void DxvkDescriptorPool::reset() {
    m_vkd->vkResetDescriptorPool(
      m_vkd->device(), m_pool, 0);
  }

  DxvkDescriptorPoolTracker::DxvkDescriptorPoolTracker(DxvkDevice* device)
  : m_device(device) {

  }

  DxvkDescriptorPoolTracker::~DxvkDescriptorPoolTracker() {

  }

  void DxvkDescriptorPoolTracker::trackDescriptorPool(Rc<DxvkDescriptorPool> pool) {
    m_pools.push_back(std::move(pool));
  }

  void DxvkDescriptorPoolTracker::reset() {
    for (const auto& pool : m_pools) {
      pool->reset();
      m_device->recycleDescriptorPool(pool);
    }

    m_pools.clear();
  }

}


code:
#pragma once

#include <vector>

#include "dxvk_include.h"

namespace dxvk {

  class DxvkDevice;

  /**
   * \brief Descriptor info
   *
   * Stores information that is required to
   * update a single resource descriptor.
   */
  union DxvkDescriptorInfo {
    VkDescriptorImageInfo  image;
    VkDescriptorBufferInfo buffer;
    VkBufferView           texelBuffer;
  };

  /**
   * \brief Descriptor pool
   *
   * Wrapper around a Vulkan descriptor pool that
   * descriptor sets can be allocated from.
   */
  class DxvkDescriptorPool : public RcObject {

  public:

    DxvkDescriptorPool(
      const Rc<vk::DeviceFn>& vkd);
    ~DxvkDescriptorPool();

    /**
     * \brief Allocates a descriptor set
     *
     * \param [in] layout Descriptor set layout
     * \returns The descriptor set
     */
    VkDescriptorSet alloc(
      VkDescriptorSetLayout layout);

    /**
     * \brief Resets descriptor set allocator
     *
     * Destroys all descriptor sets and
     * resets the Vulkan descriptor pools.
     */
    void reset();

  private:

    Rc<vk::DeviceFn> m_vkd;
    VkDescriptorPool m_pool;

  };

  /**
   * \brief Descriptor pool tracker
   *
   * Tracks descriptor pools that are either full
   * or no longer needed by the DXVK context. The
   * command list will reset and recycle all pools
   * once it has completed execution on the GPU.
   */
  class DxvkDescriptorPoolTracker {

  public:

    DxvkDescriptorPoolTracker(DxvkDevice* device);
    ~DxvkDescriptorPoolTracker();

    /**
     * \brief Adds a descriptor pool to track
     * \param [in] pool The descriptor pool
     */
    void trackDescriptorPool(Rc<DxvkDescriptorPool> pool);

    /**
     * \brief Resets event tracker
     *
     * Resets all tracked descriptor pools
     * and returns them to the device.
     */
    void reset();

  private:

    DxvkDevice* m_device;

    std::vector<Rc<DxvkDescriptorPool>> m_pools;

  };

}


code:
#include "dxvk_device.h"
#include "dxvk_instance.h"

namespace dxvk {

  DxvkDevice::DxvkDevice(
    const Rc<DxvkInstance>&         instance,
    const Rc<DxvkAdapter>&          adapter,
    const Rc<vk::DeviceFn>&         vkd,
    const DxvkDeviceExtensions&     extensions,
    const DxvkDeviceFeatures&       features)
  : m_options           (instance->options()),
    m_instance          (instance),
    m_adapter           (adapter),
    m_vkd               (vkd),
    m_extensions        (extensions),
    m_features          (features),
    m_properties        (adapter->devicePropertiesExt()),
    m_perfHints         (getPerfHints()),
    m_objects           (this),
    m_submissionQueue   (this) {
    auto queueFamilies = m_adapter->findQueueFamilies();
    m_queues.graphics = getQueue(queueFamilies.graphics, 0);
    m_queues.transfer = getQueue(queueFamilies.transfer, 0);
  }

  DxvkDevice::~DxvkDevice() {
    // If we are being destroyed during/after DLL process detachment
    // from TerminateProcess, etc, our CS threads are already destroyed
    // and we cannot synchronize against them.
    // The best we can do is just wait for the Vulkan device to be idle.
    if (this_thread::isInModuleDetachment())
      return;

    // Wait for all pending Vulkan commands to be
    // executed before we destroy any resources.
    this->waitForIdle();

    // Stop workers explicitly in order to prevent
    // access to structures that are being destroyed.
    m_objects.pipelineManager().stopWorkerThreads();
  }

  bool DxvkDevice::isUnifiedMemoryArchitecture() const {
    return m_adapter->isUnifiedMemoryArchitecture();
  }

  DxvkFramebufferSize DxvkDevice::getDefaultFramebufferSize() const {
    return DxvkFramebufferSize {
      m_properties.core.properties.limits.maxFramebufferWidth,
      m_properties.core.properties.limits.maxFramebufferHeight,
      m_properties.core.properties.limits.maxFramebufferLayers };
  }

  VkPipelineStageFlags DxvkDevice::getShaderPipelineStages() const {
    VkPipelineStageFlags result = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT
                                | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT
                                | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;

    if (m_features.core.features.geometryShader)
      result |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;

    if (m_features.core.features.tessellationShader) {
      result |= VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT
             |  VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
    }

    return result;
  }

  DxvkDeviceOptions DxvkDevice::options() const {
    DxvkDeviceOptions options;
    options.maxNumDynamicUniformBuffers = m_properties.core.properties.limits.maxDescriptorSetUniformBuffersDynamic;
    options.maxNumDynamicStorageBuffers = m_properties.core.properties.limits.maxDescriptorSetStorageBuffersDynamic;
    return options;
  }

  Rc<DxvkCommandList> DxvkDevice::createCommandList() {
    Rc<DxvkCommandList> cmdList = m_recycledCommandLists.retrieveObject();

    if (cmdList == nullptr)
      cmdList = new DxvkCommandList(this);

    return cmdList;
  }

  Rc<DxvkDescriptorPool> DxvkDevice::createDescriptorPool() {
    Rc<DxvkDescriptorPool> pool = m_recycledDescriptorPools.retrieveObject();

    if (pool == nullptr)
      pool = new DxvkDescriptorPool(m_vkd);

    return pool;
  }

  Rc<DxvkContext> DxvkDevice::createContext() {
    return new DxvkContext(this);
  }

  Rc<DxvkGpuEvent> DxvkDevice::createGpuEvent() {
    return new DxvkGpuEvent(m_vkd);
  }

  Rc<DxvkGpuQuery> DxvkDevice::createGpuQuery(
          VkQueryType           type,
          VkQueryControlFlags   flags,
          uint32_t              index) {
    return new DxvkGpuQuery(m_vkd, type, flags, index);
  }

  Rc<DxvkFence> DxvkDevice::createFence(
    const DxvkFenceCreateInfo& fenceInfo) {
    return new DxvkFence(this, fenceInfo);
  }

  Rc<DxvkFramebuffer> DxvkDevice::createFramebuffer(
    const DxvkFramebufferInfo&  info) {
    return new DxvkFramebuffer(m_vkd, info);
  }

  Rc<DxvkBuffer> DxvkDevice::createBuffer(
    const DxvkBufferCreateInfo& createInfo,
          VkMemoryPropertyFlags memoryType) {
    return new DxvkBuffer(this, createInfo, m_objects.memoryManager(), memoryType);
  }

  Rc<DxvkBufferView> DxvkDevice::createBufferView(
    const Rc<DxvkBuffer>&           buffer,
    const DxvkBufferViewCreateInfo& createInfo) {
    return new DxvkBufferView(m_vkd, buffer, createInfo);
  }

  Rc<DxvkImage> DxvkDevice::createImage(
    const DxvkImageCreateInfo&  createInfo,
          VkMemoryPropertyFlags memoryType) {
    return new DxvkImage(this, createInfo, m_objects.memoryManager(), memoryType);
  }

  Rc<DxvkImage> DxvkDevice::createImageFromVkImage(
    const DxvkImageCreateInfo&  createInfo,
          VkImage               image) {
    return new DxvkImage(this, createInfo, image);
  }

  Rc<DxvkImageView> DxvkDevice::createImageView(
    const Rc<DxvkImage>&            image,
    const DxvkImageViewCreateInfo&  createInfo) {
    return new DxvkImageView(m_vkd, image, createInfo);
  }

  Rc<DxvkSampler> DxvkDevice::createSampler(
    const DxvkSamplerCreateInfo&  createInfo) {
    return new DxvkSampler(this, createInfo);
  }

  DxvkStatCounters DxvkDevice::getStatCounters() {
    DxvkPipelineCount pipe = m_objects.pipelineManager().getPipelineCount();

    DxvkStatCounters result;
    result.setCtr(DxvkStatCounter::PipeCountGraphics, pipe.numGraphicsPipelines);
    result.setCtr(DxvkStatCounter::PipeCountCompute,  pipe.numComputePipelines);
    result.setCtr(DxvkStatCounter::PipeCompilerBusy,  m_objects.pipelineManager().isCompilingShaders());
    result.setCtr(DxvkStatCounter::GpuIdleTicks,      m_submissionQueue.gpuIdleTicks());

    std::lock_guard<sync::Spinlock> lock(m_statLock);
    result.merge(m_statCounters);
    return result;
  }

  DxvkMemoryStats DxvkDevice::getMemoryStats(uint32_t heap) {
    return m_objects.memoryManager().getMemoryStats(heap);
  }

  uint32_t DxvkDevice::getCurrentFrameId() const {
    return m_statCounters.getCtr(DxvkStatCounter::QueuePresentCount);
  }

  void DxvkDevice::initResources() {
    m_objects.dummyResources().clearResources(this);
  }

  void DxvkDevice::registerShader(const Rc<DxvkShader>& shader) {
    m_objects.pipelineManager().registerShader(shader);
  }

  void DxvkDevice::presentImage(
    const Rc<vk::Presenter>&        presenter,
          DxvkSubmitStatus*         status) {
    status->result = VK_NOT_READY;

    DxvkPresentInfo presentInfo;
    presentInfo.presenter = presenter;
    m_submissionQueue.present(presentInfo, status);

    std::lock_guard<sync::Spinlock> statLock(m_statLock);
    m_statCounters.addCtr(DxvkStatCounter::QueuePresentCount, 1);
  }

  void DxvkDevice::submitCommandList(
    const Rc<DxvkCommandList>&      commandList,
          VkSemaphore               waitSync,
          VkSemaphore               wakeSync) {
    DxvkSubmitInfo submitInfo;
    submitInfo.cmdList  = commandList;
    submitInfo.waitSync = waitSync;
    submitInfo.wakeSync = wakeSync;
    m_submissionQueue.submit(submitInfo);

    std::lock_guard<sync::Spinlock> statLock(m_statLock);
    m_statCounters.merge(commandList->statCounters());
    m_statCounters.addCtr(DxvkStatCounter::QueueSubmitCount, 1);
  }

  VkResult DxvkDevice::waitForSubmission(DxvkSubmitStatus* status) {
    VkResult result = status->result.load();

    if (result == VK_NOT_READY) {
      m_submissionQueue.synchronizeSubmission(status);
      result = status->result.load();
    }

    return result;
  }

  void DxvkDevice::waitForResource(const Rc<DxvkResource>& resource, DxvkAccess access) {
    if (resource->isInUse(access)) {
      auto t0 = dxvk::high_resolution_clock::now();

      m_submissionQueue.synchronizeUntil([resource, access] {
        return !resource->isInUse(access);
      });

      auto t1 = dxvk::high_resolution_clock::now();
      auto us = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0);

      std::lock_guard<sync::Spinlock> lock(m_statLock);
      m_statCounters.addCtr(DxvkStatCounter::GpuSyncCount, 1);
      m_statCounters.addCtr(DxvkStatCounter::GpuSyncTicks, us.count());
    }
  }

  void DxvkDevice::waitForIdle() {
    this->lockSubmission();
    if (m_vkd->vkDeviceWaitIdle(m_vkd->device()) != VK_SUCCESS)
      Logger::err("DxvkDevice: waitForIdle: Operation failed");
    this->unlockSubmission();
  }

  DxvkDevicePerfHints DxvkDevice::getPerfHints() {
    DxvkDevicePerfHints hints;
    hints.preferFbDepthStencilCopy = m_extensions.extShaderStencilExport
      && (m_adapter->matchesDriver(DxvkGpuVendor::Amd, VK_DRIVER_ID_MESA_RADV_KHR, 0, 0)
       || m_adapter->matchesDriver(DxvkGpuVendor::Amd, VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, 0, 0)
       || m_adapter->matchesDriver(DxvkGpuVendor::Amd, VK_DRIVER_ID_AMD_PROPRIETARY_KHR, 0, 0));
    hints.preferFbResolve = m_extensions.amdShaderFragmentMask
      && (m_adapter->matchesDriver(DxvkGpuVendor::Amd, VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, 0, 0)
       || m_adapter->matchesDriver(DxvkGpuVendor::Amd, VK_DRIVER_ID_AMD_PROPRIETARY_KHR, 0, 0));
    return hints;
  }

  void DxvkDevice::recycleCommandList(const Rc<DxvkCommandList>& cmdList) {
    m_recycledCommandLists.returnObject(cmdList);
  }

  void DxvkDevice::recycleDescriptorPool(const Rc<DxvkDescriptorPool>& pool) {
    m_recycledDescriptorPools.returnObject(pool);
  }

  DxvkDeviceQueue DxvkDevice::getQueue(
          uint32_t                family,
          uint32_t                index) const {
    VkQueue queue = VK_NULL_HANDLE;
    m_vkd->vkGetDeviceQueue(m_vkd->device(), family, index, &queue);
    return DxvkDeviceQueue { queue, family, index };
  }

}


code:
#pragma once

#include "dxvk_adapter.h"
#include "dxvk_buffer.h"
#include "dxvk_compute.h"
#include "dxvk_constant_state.h"
#include "dxvk_context.h"
#include "dxvk_extensions.h"
#include "dxvk_fence.h"
#include "dxvk_framebuffer.h"
#include "dxvk_image.h"
#include "dxvk_instance.h"
#include "dxvk_memory.h"
#include "dxvk_meta_clear.h"
#include "dxvk_objects.h"
#include "dxvk_options.h"
#include "dxvk_pipecache.h"
#include "dxvk_pipemanager.h"
#include "dxvk_queue.h"
#include "dxvk_recycler.h"
#include "dxvk_renderpass.h"
#include "dxvk_sampler.h"
#include "dxvk_shader.h"
#include "dxvk_stats.h"
#include "dxvk_unbound.h"

#include "../vulkan/vulkan_presenter.h"

namespace dxvk {

  class DxvkInstance;

  /**
   * \brief Device options
   */
  struct DxvkDeviceOptions {
    uint32_t maxNumDynamicUniformBuffers = 0;
    uint32_t maxNumDynamicStorageBuffers = 0;
  };

  /**
   * \brief Device performance hints
   */
  struct DxvkDevicePerfHints {
    VkBool32 preferFbDepthStencilCopy : 1;
    VkBool32 preferFbResolve          : 1;
  };

  /**
   * \brief Device queue
   *
   * Stores a Vulkan queue and the
   * queue family that it belongs to.
   */
  struct DxvkDeviceQueue {
    VkQueue   queueHandle = VK_NULL_HANDLE;
    uint32_t  queueFamily = 0;
    uint32_t  queueIndex  = 0;
  };

  /**
   * \brief Device queue infos
   */
  struct DxvkDeviceQueueSet {
    DxvkDeviceQueue graphics;
    DxvkDeviceQueue transfer;
  };

  /**
   * \brief DXVK device
   *
   * Device object. This is responsible for resource creation,
   * memory allocation, command submission and state tracking.
   * Rendering commands are recorded into command lists using
   * contexts. Multiple contexts can be created for a device.
   */
  class DxvkDevice : public RcObject {
    friend class DxvkContext;
    friend class DxvkSubmissionQueue;
    friend class DxvkDescriptorPoolTracker;
  public:

    DxvkDevice(
      const Rc<DxvkInstance>&         instance,
      const Rc<DxvkAdapter>&          adapter,
      const Rc<vk::DeviceFn>&         vkd,
      const DxvkDeviceExtensions&     extensions,
      const DxvkDeviceFeatures&       features);

    ~DxvkDevice();

    /**
     * \brief Vulkan device functions
     * \returns Vulkan device functions
     */
    Rc<vk::DeviceFn> vkd() const {
      return m_vkd;
    }

    /**
     * \brief Logical device handle
     * \returns The device handle
     */
    VkDevice handle() const {
      return m_vkd->device();
    }

    /**
     * \brief Device options
     * \returns Device options
     */
    const DxvkOptions& config() const {
      return m_options;
    }

    /**
     * \brief Queue handles
     *
     * Handles and queue family indices
     * of all known device queues.
     * \returns Device queue infos
     */
    const DxvkDeviceQueueSet& queues() const {
      return m_queues;
    }

    /**
     * \brief Tests whether a dedicated transfer queue is available
     * \returns \c true if an SDMA queue is supported by the device
     */
    bool hasDedicatedTransferQueue() const {
      return m_queues.transfer.queueHandle
          != m_queues.graphics.queueHandle;
    }

    /**
     * \brief The instance
     *
     * The DXVK instance that created this device.
     * \returns Instance
     */
    Rc<DxvkInstance> instance() const {
      return m_instance;
    }

    /**
     * \brief The adapter
     *
     * The physical device that the
     * device has been created for.
     * \returns Adapter
     */
    Rc<DxvkAdapter> adapter() const {
      return m_adapter;
    }

    /**
     * \brief Enabled device extensions
     * \returns Enabled device extensions
     */
    const DxvkDeviceExtensions& extensions() const {
      return m_extensions;
    }

    /**
     * \brief Enabled device features
     * \returns Enabled features
     */
    const DxvkDeviceFeatures& features() const {
      return m_features;
    }

    /**
     * \brief Device properties
     * \returns Device properties
     */
    const DxvkDeviceInfo& properties() const {
      return m_properties;
    }

    /**
     * \brief Get device status
     *
     * This may report device loss in
     * case a submission failed.
     * \returns Device status
     */
    VkResult getDeviceStatus() const {
      return m_submissionQueue.getLastError();
    }

    /**
     * \brief Checks whether this is a UMA system
     *
     * Basically tests whether all heaps are device-local.
     * Can be used for various optimizations in client APIs.
     * \returns \c true if the system has unified memory.
     */
    bool isUnifiedMemoryArchitecture() const;

    /**
     * \brief Queries default framebuffer size
     * \returns Default framebuffer size
     */
    DxvkFramebufferSize getDefaultFramebufferSize() const;

    /**
     * \brief Queries supported shader stages
     * \returns Supported shader pipeline stages
     */
    VkPipelineStageFlags getShaderPipelineStages() const;

    /**
     * \brief Retrieves device options
     * \returns Device options
     */
    DxvkDeviceOptions options() const;

    /**
     * \brief Retrieves performance hints
     * \returns Device-specific perf hints
     */
    DxvkDevicePerfHints perfHints() const {
      return m_perfHints;
    }

    /**
     * \brief Creates a command list
     * \returns The command list
     */
    Rc<DxvkCommandList> createCommandList();

    /**
     * \brief Creates a descriptor pool
     *
     * Returns a previously recycled pool, or creates
     * a new one if necessary. The context should take
     * ownership of the returned pool.
     * \returns Descriptor pool
     */
    Rc<DxvkDescriptorPool> createDescriptorPool();

    /**
     * \brief Creates a context
     *
     * Creates a context object that can
     * be used to record command buffers.
     * \returns The context object
     */
    Rc<DxvkContext> createContext();

    /**
     * \brief Creates a GPU event
     * \returns New GPU event
     */
    Rc<DxvkGpuEvent> createGpuEvent();

    /**
     * \brief Creates a query
     *
     * \param [in] type Query type
     * \param [in] flags Query flags
     * \param [in] index Query index
     * \returns New query
     */
    Rc<DxvkGpuQuery> createGpuQuery(
            VkQueryType           type,
            VkQueryControlFlags   flags,
            uint32_t              index);

    /**
     * \brief Creates new fence
     *
     * \param [in] info Fence create info
     * \returns The fence
     */
    Rc<DxvkFence> createFence(
      const DxvkFenceCreateInfo& fenceInfo);

    /**
     * \brief Creates framebuffer for a set of render targets
     *
     * \param [in] info Framebuffer info
     * \returns The framebuffer object
     */
    Rc<DxvkFramebuffer> createFramebuffer(
      const DxvkFramebufferInfo&  info);

    /**
     * \brief Creates a buffer object
     *
     * \param [in] createInfo Buffer create info
     * \param [in] memoryType Memory type flags
     * \returns The buffer object
     */
    Rc<DxvkBuffer> createBuffer(
      const DxvkBufferCreateInfo& createInfo,
            VkMemoryPropertyFlags memoryType);

    /**
     * \brief Creates a buffer view
     *
     * \param [in] buffer The buffer to view
     * \param [in] createInfo Buffer view properties
     * \returns The buffer view object
     */
    Rc<DxvkBufferView> createBufferView(
      const Rc<DxvkBuffer>&           buffer,
      const DxvkBufferViewCreateInfo& createInfo);

    /**
     * \brief Creates an image object
     *
     * \param [in] createInfo Image create info
     * \param [in] memoryType Memory type flags
     * \returns The image object
     */
    Rc<DxvkImage> createImage(
      const DxvkImageCreateInfo&  createInfo,
            VkMemoryPropertyFlags memoryType);

    /**
     * \brief Creates an image object for an existing VkImage
     *
     * \param [in] createInfo Image create info
     * \param [in] image Vulkan image to wrap
     * \returns The image object
     */
    Rc<DxvkImage> createImageFromVkImage(
      const DxvkImageCreateInfo&  createInfo,
            VkImage               image);

    /**
     * \brief Creates an image view
     *
     * \param [in] image The image to create a view for
     * \param [in] createInfo Image view create info
     * \returns The image view
     */
    Rc<DxvkImageView> createImageView(
      const Rc<DxvkImage>&            image,
      const DxvkImageViewCreateInfo&  createInfo);

    /**
     * \brief Creates a sampler object
     *
     * \param [in] createInfo Sampler parameters
     * \returns Newly created sampler object
     */
    Rc<DxvkSampler> createSampler(
      const DxvkSamplerCreateInfo&  createInfo);

    /**
     * \brief Retrieves stat counters
     *
     * Can be used by the HUD to display some
     * internal information, such as memory
     * usage, draw calls, etc.
     */
    DxvkStatCounters getStatCounters();

    /**
     * \brief Retrieves memors statistics
     *
     * \param [in] heap Memory heap index
     * \returns Memory stats for this heap
     */
    DxvkMemoryStats getMemoryStats(uint32_t heap);

    /**
     * \brief Retreves current frame ID
     * \returns Current frame ID
     */
    uint32_t getCurrentFrameId() const;

    /**
     * \brief Initializes dummy resources
     *
     * Should be called after creating the device in
     * case the device initialization was successful
     * and the device is usable.
     */
    void initResources();

    /**
     * \brief Registers a shader
     * \param [in] shader Newly compiled shader
     */
    void registerShader(
      const Rc<DxvkShader>&         shader);

    /**
     * \brief Presents a swap chain image
     *
     * Invokes the presenter's \c presentImage method on
     * the submission thread. The status of this operation
     * can be retrieved with \ref waitForSubmission.
     * \param [in] presenter The presenter
     * \param [out] status Present status
     */
    void presentImage(
      const Rc<vk::Presenter>&        presenter,
            DxvkSubmitStatus*         status);

    /**
     * \brief Submits a command list
     *
     * Submits the given command list to the device using
     * the given set of optional synchronization primitives.
     * \param [in] commandList The command list to submit
     * \param [in] waitSync (Optional) Semaphore to wait on
     * \param [in] wakeSync (Optional) Semaphore to notify
     */
    void submitCommandList(
      const Rc<DxvkCommandList>&      commandList,
            VkSemaphore               waitSync,
            VkSemaphore               wakeSync);

    /**
     * \brief Locks submission queue
     *
     * Since Vulkan queues are only meant to be accessed
     * from one thread at a time, external libraries need
     * to lock the queue before submitting command buffers.
     */
    void lockSubmission() {
      m_submissionQueue.synchronize();
      m_submissionQueue.lockDeviceQueue();
    }

    /**
     * \brief Unlocks submission queue
     *
     * Releases the Vulkan queues again so that DXVK
     * itself can use them for submissions again.
     */
    void unlockSubmission() {
      m_submissionQueue.unlockDeviceQueue();
    }

    /**
     * \brief Number of pending submissions
     *
     * A return value of 0 indicates
     * that the GPU is currently idle.
     * \returns Pending submission count
     */
    uint32_t pendingSubmissions() const {
      return m_submissionQueue.pendingSubmissions();
    }

    /**
     * \brief Increments a given stat counter
     *
     * \param [in] counter Stat counter to increment
     * \param [in] value Increment value
     */
    void addStatCtr(DxvkStatCounter counter, uint64_t value) {
      std::lock_guard<sync::Spinlock> lock(m_statLock);
      m_statCounters.addCtr(counter, value);
    }

    /**
     * \brief Waits for a given submission
     *
     * \param [in,out] status Submission status
     * \returns Result of the submission
     */
    VkResult waitForSubmission(DxvkSubmitStatus* status);

    /**
     * \brief Waits for resource to become idle
     *
     * \param [in] resource Resource to wait for
     * \param [in] access Access mode to check
     */
    void waitForResource(const Rc<DxvkResource>& resource, DxvkAccess access);

    /**
     * \brief Waits until the device becomes idle
     *
     * Waits for the GPU to complete the execution of all
     * previously submitted command buffers. This may be
     * used to ensure that resources that were previously
     * used by the GPU can be safely destroyed.
     */
    void waitForIdle();

  private:

    DxvkOptions                 m_options;

    Rc<DxvkInstance>            m_instance;
    Rc<DxvkAdapter>             m_adapter;
    Rc<vk::DeviceFn>            m_vkd;
    DxvkDeviceExtensions        m_extensions;

    DxvkDeviceFeatures          m_features;
    DxvkDeviceInfo              m_properties;

    DxvkDevicePerfHints         m_perfHints;
    DxvkObjects                 m_objects;

    sync::Spinlock              m_statLock;
    DxvkStatCounters            m_statCounters;

    DxvkDeviceQueueSet          m_queues;

    DxvkRecycler<DxvkCommandList,    16> m_recycledCommandLists;
    DxvkRecycler<DxvkDescriptorPool, 16> m_recycledDescriptorPools;

    DxvkSubmissionQueue m_submissionQueue;

    DxvkDevicePerfHints getPerfHints();

    void recycleCommandList(
      const Rc<DxvkCommandList>& cmdList);

    void recycleDescriptorPool(
      const Rc<DxvkDescriptorPool>& pool);

    DxvkDeviceQueue getQueue(
            uint32_t                family,
            uint32_t                index) const;

  };

}


code:
#include "dxvk_device_filter.h"

namespace dxvk {

  DxvkDeviceFilter::DxvkDeviceFilter(DxvkDeviceFilterFlags flags)
  : m_flags(flags) {
    m_matchDeviceName = env::getEnvVar("DXVK_FILTER_DEVICE_NAME");

    if (m_matchDeviceName.size() != 0)
      m_flags.set(DxvkDeviceFilterFlag::MatchDeviceName);
  }

  DxvkDeviceFilter::~DxvkDeviceFilter() {

  }

  bool DxvkDeviceFilter::testAdapter(const VkPhysicalDeviceProperties& properties) const {
    if (properties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) {
      Logger::warn(str::format("Skipping Vulkan 1.0 adapter: ", properties.deviceName));
      return false;
    }

    if (m_flags.test(DxvkDeviceFilterFlag::MatchDeviceName)) {
      if (std::string(properties.deviceName).find(m_matchDeviceName) == std::string::npos)
        return false;
    }

    if (m_flags.test(DxvkDeviceFilterFlag::SkipCpuDevices)) {
      if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) {
        Logger::warn(str::format("Skipping CPU adapter: ", properties.deviceName));
        return false;
      }
    }

    return true;
  }

}


code:
#pragma once

#include "dxvk_adapter.h"

namespace dxvk {

  /**
   * \brief Device filter flags
   *
   * The device filter flags specify which device
   * properties are considered when testing adapters.
   * If no flags are set, all devices pass the test.
   */
  enum class DxvkDeviceFilterFlag {
    MatchDeviceName   = 0,
    SkipCpuDevices    = 1,
  };

  using DxvkDeviceFilterFlags = Flags<DxvkDeviceFilterFlag>;

  /**
   * \brief DXVK device filter
   *
   * Used to select specific Vulkan devices to use
   * with DXVK. This may be useful for games which
   * do not offer an option to select the correct
   * device.
   */
  class DxvkDeviceFilter {

  public:

    DxvkDeviceFilter(DxvkDeviceFilterFlags flags);
    ~DxvkDeviceFilter();

    /**
     * \brief Tests an adapter
     *
     * \param [in] properties Adapter properties
     * \returns \c true if the test passes
     */
    bool testAdapter(
      const VkPhysicalDeviceProperties& properties) const;

  private:

    DxvkDeviceFilterFlags m_flags;

    std::string m_matchDeviceName;

  };

}


code:
#pragma once

#include "dxvk_include.h"

namespace dxvk {

  /**
   * \brief Device info
   *
   * Stores core properties and a bunch of extension-specific
   * properties, if the respective extensions are available.
   * Structures for unsupported extensions will be undefined,
   * so before using them, check whether they are supported.
   */
  struct DxvkDeviceInfo {
    VkPhysicalDeviceProperties2                               core;
    VkPhysicalDeviceIDProperties                              coreDeviceId;
    VkPhysicalDeviceSubgroupProperties                        coreSubgroup;
    VkPhysicalDeviceConservativeRasterizationPropertiesEXT    extConservativeRasterization;
    VkPhysicalDeviceCustomBorderColorPropertiesEXT            extCustomBorderColor;
    VkPhysicalDeviceRobustness2PropertiesEXT                  extRobustness2;
    VkPhysicalDeviceTransformFeedbackPropertiesEXT            extTransformFeedback;
    VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT       extVertexAttributeDivisor;
    VkPhysicalDeviceDepthStencilResolvePropertiesKHR          khrDepthStencilResolve;
    VkPhysicalDeviceDriverPropertiesKHR                       khrDeviceDriverProperties;
    VkPhysicalDeviceFloatControlsPropertiesKHR                khrShaderFloatControls;
    VkPhysicalDeviceTimelineSemaphorePropertiesKHR            khrTimelineSemaphore;
  };

  /**
   * \brief Device features
   *
   * Stores core features and extension-specific features.
   * If the respective extensions are not available, the
   * extended features will be marked as unsupported.
   */
  struct DxvkDeviceFeatures {
    VkPhysicalDeviceFeatures2                                 core;
    VkPhysicalDeviceShaderDrawParametersFeatures              shaderDrawParameters;
    VkPhysicalDevice4444FormatsFeaturesEXT                    ext4444Formats;
    VkPhysicalDeviceCustomBorderColorFeaturesEXT              extCustomBorderColor;
    VkPhysicalDeviceDepthClipEnableFeaturesEXT                extDepthClipEnable;
    VkPhysicalDeviceExtendedDynamicStateFeaturesEXT           extExtendedDynamicState;
    VkPhysicalDeviceHostQueryResetFeaturesEXT                 extHostQueryReset;
    VkPhysicalDeviceMemoryPriorityFeaturesEXT                 extMemoryPriority;
    VkPhysicalDeviceNonSeamlessCubeMapFeaturesEXT             extNonSeamlessCubeMap;
    VkPhysicalDeviceRobustness2FeaturesEXT                    extRobustness2;
    VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT extShaderDemoteToHelperInvocation;
    VkPhysicalDeviceTransformFeedbackFeaturesEXT              extTransformFeedback;
    VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT         extVertexAttributeDivisor;
    VkPhysicalDeviceBufferDeviceAddressFeaturesKHR            khrBufferDeviceAddress;
    VkPhysicalDeviceTimelineSemaphoreFeaturesKHR              khrTimelineSemaphore;
  };

}