vulkan: Add data structures to store all graphics state

Reviewed-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/17328>
This commit is contained in:
Jason Ekstrand 2022-07-13 10:02:30 -05:00 committed by Marge Bot
parent fed81dc306
commit 7ca8dcb05f
6 changed files with 1701 additions and 7 deletions

View File

@ -0,0 +1,135 @@
Graphics state
==============
The Mesa Vulkan runtime provides helpers for managing the numerous pieces
of graphics state associated with a ``VkPipeline`` or set dynamically on a
command buffer. No such helpers are provided for compute or ray-tracing
because they have little or no state besides the shaders themselves.
Pipeline state
--------------
All (possibly dynamic) Vulkan graphics pipeline state is encapsulated into
a single :cpp:struct:`vk_graphics_pipeline_state` structure which contains
pointers to sub-structures for each of the different state categories.
Unlike :cpp:type:`VkGraphicsPipelineCreateInfo`, the pointers in
:cpp:struct:`vk_graphics_pipeline_state` are guaranteed to be either be
NULL or point to valid and properly populated memory.
When creating a pipeline, the
:cpp:func:`vk_graphics_pipeline_state_fill()` function can be used to
gather all of the state from the core structures as well as various `pNext`
chains into a single state structure. Whenever an extension struct is
missing, a reasonable default value is provided whenever possible. The
usual flow for creating a full graphics pipeline (not library) looks like
this:
.. code-block:: c
struct vk_graphics_pipeline_state state = { };
struct vk_graphics_pipeline_all_state all;
vk_graphics_pipeline_state_fill(&device->vk, &state, pCreateInfo,
NULL, &all, NULL, 0, NULL);
/* Emit stuff using the state in `state` */
The :cpp:struct:`vk_graphics_pipeline_all_state` structure exists to allow
the state to sit on the stack instead of requiring a heap allocation. This
is useful if you intend to use the state right away and don't need to store
it. For pipeline libraries, it's likely more useful to use the dynamically
allocated version and store the dynamically allocated memory in the
library pipeline.
.. code-block:: c
/* Assuming we have a vk_graphics_pipeline_state in pipeline */
memset(&pipeline->state, 0, sizeof(pipeline->state));
for (uint32_t i = 0; i < lib_info->libraryCount; i++) {
VK_FROM_HANDLE(drv_graphics_pipeline_library, lib, lib_info->pLibraries[i]);
vk_graphics_pipeline_state_merge(&pipeline->state, &lib->state);
}
/* This assumes you have a void **state_mem in pipeline */
result = vk_graphics_pipeline_state_fill(&device->vk, &pipeline->state,
pCreateInfo, NULL, NULL, pAllocator,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT,
&pipeline->state_mem);
if (result != VK_SUCCESS)
return result;
State from dependent libraries can be merged together using
:cpp:func:`vk_graphics_pipeline_state_merge`.
:cpp:func:`vk_graphics_pipeline_state_fill` will then only attempt to
populate missing fields. You can also merge dependent pipeline libraries
together but store the final state on the stack for immediate consumption:
.. code-block:: c
struct vk_graphics_pipeline_state state = { };
for (uint32_t i = 0; i < lib_info->libraryCount; i++) {
VK_FROM_HANDLE(drv_graphics_pipeline_library, lib, lib_info->pLibraries[i]);
vk_graphics_pipeline_state_merge(&state, &lib->state);
}
struct vk_graphics_pipeline_all_state all;
vk_graphics_pipeline_state_fill(&device->vk, &state, pCreateInfo,
NULL, &all, NULL, 0, NULL);
.. doxygenfunction:: vk_graphics_pipeline_state_fill
.. doxygenfunction:: vk_graphics_pipeline_state_merge
Reference
---------
.. doxygenstruct:: vk_graphics_pipeline_state
:members:
.. doxygenstruct:: vk_vertex_binding_state
:members:
.. doxygenstruct:: vk_vertex_attribute_state
:members:
.. doxygenstruct:: vk_vertex_input_state
:members:
.. doxygenstruct:: vk_input_assembly_state
:members:
.. doxygenstruct:: vk_tessellation_state
:members:
.. doxygenstruct:: vk_viewport_state
:members:
.. doxygenstruct:: vk_discard_rectangles_state
:members:
.. doxygenstruct:: vk_rasterization_state
:members:
.. doxygenstruct:: vk_fragment_shading_rate_state
:members:
.. doxygenstruct:: vk_sample_locations_state
:members:
.. doxygenstruct:: vk_multisample_state
:members:
.. doxygenstruct:: vk_stencil_test_face_state
:members:
.. doxygenstruct:: vk_depth_stencil_state
:members:
.. doxygenstruct:: vk_color_blend_state
:members:
.. doxygenstruct:: vk_render_pass_state
:members:

View File

@ -11,4 +11,5 @@ hardware-agnostic bits in common code.
base-objs
dispatch
graphics-state
renderpass

File diff suppressed because it is too large Load Diff

View File

@ -26,12 +26,16 @@
#include "vulkan/vulkan_core.h"
#include "vk_limits.h"
#include "util/bitset.h"
#ifdef __cplusplus
extern "C" {
#endif
struct vk_device;
/** Enumeration of all Vulkan dynamic graphics states
*
* Enumerants are named with both the abreviation of the state group to which
@ -89,6 +93,519 @@ void
vk_get_dynamic_graphics_states(BITSET_WORD *dynamic,
const VkPipelineDynamicStateCreateInfo *info);
struct vk_vertex_binding_state {
/** VkVertexInputBindingDescription::stride */
uint16_t stride;
/** VkVertexInputBindingDescription::inputRate */
uint16_t input_rate;
/** VkVertexInputBindingDivisorDescriptionEXT::divisor or 1 */
uint32_t divisor;
};
struct vk_vertex_attribute_state {
/** VkVertexInputAttributeDescription::binding */
uint32_t binding;
/** VkVertexInputAttributeDescription::format */
VkFormat format;
/** VkVertexInputAttributeDescription::offset */
uint32_t offset;
};
struct vk_vertex_input_state {
/** Bitset of which bindings are valid, indexed by binding */
uint32_t bindings_valid;
struct vk_vertex_binding_state bindings[MESA_VK_MAX_VERTEX_BINDINGS];
/** Bitset of which attributes are valid, indexed by location */
uint32_t attributes_valid;
struct vk_vertex_attribute_state attributes[MESA_VK_MAX_VERTEX_ATTRIBUTES];
};
struct vk_input_assembly_state {
/** VkPipelineInputAssemblyStateCreateInfo::topology
*
* MESA_VK_DYNAMIC_GRAPHICS_STATE_IA_PRIMITIVE_TOPOLOGY
*/
uint8_t primitive_topology;
/** VkPipelineInputAssemblyStateCreateInfo::primitiveRestartEnable
*
* MESA_VK_DYNAMIC_GRAPHICS_STATE_IA_PRIMITIVE_RESTART_ENABLE
*/
bool primitive_restart_enable;
};
struct vk_tessellation_state {
/** VkPipelineTessellationStateCreateInfo::patchControlPoints */
uint8_t patch_control_points;
/** VkPipelineTessellationDomainOriginStateCreateInfo::domainOrigin */
uint8_t domain_origin;
};
struct vk_viewport_state {
/** VkPipelineViewportDepthClipControlCreateInfoEXT::negativeOneToOne */
bool negative_one_to_one;
/** VkPipelineViewportStateCreateInfo::viewportCount */
uint8_t viewport_count;
/** VkPipelineViewportStateCreateInfo::scissorCount */
uint8_t scissor_count;
/** VkPipelineViewportStateCreateInfo::pViewports */
VkRect2D scissors[MESA_VK_MAX_SCISSORS];
/** VkPipelineViewportStateCreateInfo::pScissors */
VkViewport viewports[MESA_VK_MAX_VIEWPORTS];
};
struct vk_discard_rectangles_state {
/** VkPipelineDiscardRectangleStateCreateInfoEXT::discardRectangleMode */
VkDiscardRectangleModeEXT mode;
/** VkPipelineDiscardRectangleStateCreateInfoEXT::discardRectangleCount */
uint32_t rectangle_count;
/** VkPipelineDiscardRectangleStateCreateInfoEXT::pDiscardRectangles */
VkRect2D rectangles[MESA_VK_MAX_DISCARD_RECTANGLES];
};
struct vk_rasterization_state {
/** VkPipelineRasterizationStateCreateInfo::rasterizerDiscardEnable
*
* This will be false if rasterizer discard is dynamic
*/
bool rasterizer_discard_enable;
/** VkPipelineRasterizationStateCreateInfo::depthClampEnable */
bool depth_clamp_enable;
/** VkPipelineRasterizationDepthClipStateCreateInfoEXT::depthClipEnable */
bool depth_clip_enable;
/** VkPipelineRasterizationStateCreateInfo::polygonMode */
VkPolygonMode polygon_mode;
/** VkPipelineRasterizationStateCreateInfo::cullMode */
VkCullModeFlags cull_mode;
/** VkPipelineRasterizationStateCreateInfo::frontFace */
VkFrontFace front_face;
/** VkPipelineRasterizationConservativeStateCreateInfoEXT::conservativeRasterizationMode */
VkConservativeRasterizationModeEXT conservative_mode;
/** VkPipelineRasterizationStateRasterizationOrderAMD::rasterizationOrder */
VkRasterizationOrderAMD rasterization_order_amd;
/** VkPipelineRasterizationProvokingVertexStateCreateInfoEXT::provokingVertexMode */
VkProvokingVertexModeEXT provoking_vertex;
/** VkPipelineRasterizationStateStreamCreateInfoEXT::rasterizationStream */
uint32_t rasterization_stream;
struct {
/** VkPipelineRasterizationStateCreateInfo::depthBiasEnable */
bool enable;
/** VkPipelineRasterizationStateCreateInfo::depthBiasConstantFactor */
float constant;
/** VkPipelineRasterizationStateCreateInfo::depthBiasClamp */
float clamp;
/** VkPipelineRasterizationStateCreateInfo::depthBiasSlopeFactor */
float slope;
} depth_bias;
struct {
/** VkPipelineRasterizationStateCreateInfo::lineWidth */
float width;
/** VkPipelineRasterizationLineStateCreateInfoEXT::lineRasterizationMode
*
* Will be set to VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT if
* VkPipelineRasterizationLineStateCreateInfoEXT is not provided.
*/
VkLineRasterizationModeEXT mode;
struct {
/** VkPipelineRasterizationLineStateCreateInfoEXT::stippledLineEnable */
bool enable;
/** VkPipelineRasterizationLineStateCreateInfoEXT::lineStippleFactor */
uint32_t factor;
/** VkPipelineRasterizationLineStateCreateInfoEXT::lineStipplePattern */
uint16_t pattern;
} stipple;
} line;
};
struct vk_fragment_shading_rate_state {
/** VkPipelineFragmentShadingRateStateCreateInfoKHR::fragmentSize
*
* MESA_VK_DYNAMIC_GRAPHICS_STATE_FSR
*/
VkExtent2D fragment_size;
/** VkPipelineFragmentShadingRateStateCreateInfoKHR::combinerOps
*
* MESA_VK_DYNAMIC_GRAPHICS_STATE_FSR
*/
VkFragmentShadingRateCombinerOpKHR combiner_ops[2];
};
struct vk_sample_locations_state {
/** VkSampleLocationsInfoEXT::sampleLocationsPerPixel */
VkSampleCountFlagBits per_pixel;
/** VkSampleLocationsInfoEXT::sampleLocationGridSize */
VkExtent2D grid_size;
/** VkSampleLocationsInfoEXT::sampleLocations */
VkSampleLocationEXT locations[MESA_VK_MAX_SAMPLE_LOCATIONS];
};
struct vk_multisample_state {
/** VkPipelineMultisampleStateCreateInfo::rasterizationSamples */
VkSampleCountFlagBits rasterization_samples;
/** VkPipelineMultisampleStateCreateInfo::sampleShadingEnable */
bool sample_shading_enable;
/** VkPipelineMultisampleStateCreateInfo::minSampleShading */
float min_sample_shading;
/** VkPipelineMultisampleStateCreateInfo::pSampleMask */
uint16_t sample_mask;
/** VkPipelineMultisampleStateCreateInfo::alphaToCoverageEnable */
bool alpha_to_coverage_enable;
/** VkPipelineMultisampleStateCreateInfo::alphaToOneEnable */
bool alpha_to_one_enable;
/** VkPipelineSampleLocationsStateCreateInfoEXT::sampleLocationsEnable */
bool sample_locations_enable;
/** VkPipelineSampleLocationsStateCreateInfoEXT::sampleLocationsInfo
*
* May be NULL for dynamic sample locations.
*/
const struct vk_sample_locations_state *sample_locations;
};
/** Represents the stencil test state for a face */
struct vk_stencil_test_face_state {
/*
* MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_STENCIL_OP
*/
struct {
/** VkStencilOpState::failOp */
uint8_t fail;
/** VkStencilOpState::passOp */
uint8_t pass;
/** VkStencilOpState::depthFailOp */
uint8_t depth_fail;
/** VkStencilOpState::compareOp */
uint8_t compare;
} op;
/** VkStencilOpState::compareMask
*
* MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_STENCIL_COMPARE_MASK
*/
uint8_t compare_mask;
/** VkStencilOpState::writeMask
*
* MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_STENCIL_WRITE_MASK
*/
uint8_t write_mask;
/** VkStencilOpState::reference
*
* MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_STENCIL_REFERENCE
*/
uint8_t reference;
};
struct vk_depth_stencil_state {
struct {
/** VkPipelineDepthStencilStateCreateInfo::depthTestEnable
*
* MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_DEPTH_TEST_ENABLE
*/
bool test_enable;
/** VkPipelineDepthStencilStateCreateInfo::depthWriteEnable
*
* MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_DEPTH_WRITE_ENABLE
*/
bool write_enable;
/** VkPipelineDepthStencilStateCreateInfo::depthCompareOp
*
* MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_DEPTH_COMPARE_OP
*/
VkCompareOp compare_op;
struct {
/** VkPipelineDepthStencilStateCreateInfo::depthBoundsTestEnable
*
* MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_DEPTH_BOUNDS_TEST_ENABLE
*/
bool enable;
/** VkPipelineDepthStencilStateCreateInfo::min/maxDepthBounds
*
* MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_DEPTH_BOUNDS_TEST_BOUNDS
*/
float min, max;
} bounds_test;
} depth;
struct {
/** VkPipelineDepthStencilStateCreateInfo::stencilTestEnable
*
* MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_STENCIL_TEST_ENABLE
*/
bool test_enable;
/** VkPipelineDepthStencilStateCreateInfo::front */
struct vk_stencil_test_face_state front;
/** VkPipelineDepthStencilStateCreateInfo::back */
struct vk_stencil_test_face_state back;
} stencil;
};
struct vk_color_blend_attachment_state {
/** VkPipelineColorBlendAttachmentState::blendEnable */
bool blend_enable;
/** VkPipelineColorBlendAttachmentState::srcColorBlendFactor */
uint8_t src_color_blend_factor;
/** VkPipelineColorBlendAttachmentState::dstColorBlendFactor */
uint8_t dst_color_blend_factor;
/** VkPipelineColorBlendAttachmentState::srcAlphaBlendFactor */
uint8_t src_alpha_blend_factor;
/** VkPipelineColorBlendAttachmentState::dstAlphaBlendFactor */
uint8_t dst_alpha_blend_factor;
/** VkPipelineColorBlendAttachmentState::colorWriteMask */
uint8_t write_mask;
/** VkPipelineColorBlendAttachmentState::colorBlendOp */
VkBlendOp color_blend_op;
/** VkPipelineColorBlendAttachmentState::alphaBlendOp */
VkBlendOp alpha_blend_op;
};
struct vk_color_blend_state {
/** VkPipelineColorBlendStateCreateInfo::logicOpEnable */
bool logic_op_enable;
/** VkPipelineColorBlendStateCreateInfo::logicOp */
uint8_t logic_op;
/** VkPipelineColorWriteCreateInfoEXT::pColorWriteEnables */
uint8_t color_write_enables;
/** VkPipelineColorBlendStateCreateInfo::attachmentCount */
uint8_t attachment_count;
/** VkPipelineColorBlendStateCreateInfo::pAttachments */
struct vk_color_blend_attachment_state attachments[MESA_VK_MAX_COLOR_ATTACHMENTS];
/** VkPipelineColorBlendStateCreateInfo::blendConstants */
float blend_constants[4];
};
struct vk_render_pass_state {
/** Set of image aspects bound as color/depth/stencil attachments
*
* Set to VK_IMAGE_ASPECT_METADATA_BIT to indicate that attachment info
* is invalid.
*/
VkImageAspectFlags attachment_aspects;
/** VkGraphicsPipelineCreateInfo::renderPass */
VkRenderPass render_pass;
/** VkGraphicsPipelineCreateInfo::subpass */
uint32_t subpass;
/** VkPipelineRenderingCreateInfo::viewMask */
uint32_t view_mask;
/** VkRenderingSelfDependencyInfoMESA::colorSelfDependencies */
uint8_t color_self_dependencies;
/** VkRenderingSelfDependencyInfoMESA::depthSelfDependency */
bool depth_self_dependency;
/** VkRenderingSelfDependencyInfoMESA::stencilSelfDependency */
bool stencil_self_dependency;
/** VkPipelineRenderingCreateInfo::colorAttachmentCount */
uint8_t color_attachment_count;
/** VkPipelineRenderingCreateInfo::pColorAttachmentFormats */
VkFormat color_attachment_formats[MESA_VK_MAX_COLOR_ATTACHMENTS];
/** VkPipelineRenderingCreateInfo::depthAttachmentFormat */
VkFormat depth_attachment_format;
/** VkPipelineRenderingCreateInfo::stencilAttachmentFormat */
VkFormat stencil_attachment_format;
};
struct vk_graphics_pipeline_all_state {
struct vk_vertex_input_state vi;
struct vk_input_assembly_state ia;
struct vk_tessellation_state ts;
struct vk_viewport_state vp;
struct vk_discard_rectangles_state dr;
struct vk_rasterization_state rs;
struct vk_fragment_shading_rate_state fsr;
struct vk_multisample_state ms;
struct vk_sample_locations_state ms_sample_locations;
struct vk_depth_stencil_state ds;
struct vk_color_blend_state cb;
struct vk_render_pass_state rp;
};
struct vk_graphics_pipeline_state {
/** Bitset of which states are dynamic */
BITSET_DECLARE(dynamic, MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX);
/** Vertex input state */
const struct vk_vertex_input_state *vi;
/** Input assembly state */
const struct vk_input_assembly_state *ia;
/** Tessellation state */
const struct vk_tessellation_state *ts;
/** Viewport state */
const struct vk_viewport_state *vp;
/** Discard Rectangles state */
const struct vk_discard_rectangles_state *dr;
/** Rasterization state */
const struct vk_rasterization_state *rs;
/** Fragment shading rate state */
const struct vk_fragment_shading_rate_state *fsr;
/** Multiesample state */
const struct vk_multisample_state *ms;
/** Depth stencil state */
const struct vk_depth_stencil_state *ds;
/** Color blend state */
const struct vk_color_blend_state *cb;
/** Render pass state */
const struct vk_render_pass_state *rp;
};
/** Struct for extra information that we need from the subpass.
*
* This struct need only be provided if the driver has its own render pass
* implementation. If the driver uses the common render pass implementation,
* we can get this information ourselves.
*/
struct vk_subpass_info {
uint32_t view_mask;
VkImageAspectFlags attachment_aspects;
};
/** Populate a vk_graphics_pipeline_state from VkGraphicsPipelineCreateInfo
*
* This function crawls the provided VkGraphicsPipelineCreateInfo and uses it
* to populate the vk_graphics_pipeline_state. Upon returning from this
* function, all pointers in `state` will either be `NULL` or point to a valid
* sub-state structure. Whenever an extension struct is missing, a reasonable
* default value is provided whenever possible. Some states may be left NULL
* if the state does not exist (such as when rasterizer discard is enabled) or
* if all of the corresponding states are dynamic.
*
* This function assumes that the vk_graphics_pipeline_state is already valid
* (i.e., all pointers are NULL or point to valid states). Any states already
* present are assumed to be identical to how we would populate them from
* VkGraphicsPipelineCreateInfo.
*
* This function can operate in one of two modes with respect to how the
* memory for states is allocated. If a `vk_graphics_pipeline_all_state`
* struct is provided, any newly populated states will point to the relevant
* field in `all`. If `all == NULL`, it attempts to dynamically allocate any
* newly required states using the provided allocator and scope. The pointer
* to this new blob of memory is returned via `alloc_ptr_out` and must
* eventually be freed by the driver.
*
* @param[in] device The Vulkan device
* @param[out] state The graphics pipeline state to populate
* @param[in] info The pCreateInfo from vkCreateGraphicsPipelines
* @param[in] sp_info Subpass info if the driver implements render
* passes itself. This should be NULL for drivers
* that use the common render pass infrastructure
* built on top of dynamic rendering.
* @param[in] all The vk_graphics_pipeline_all_state to use to
* back any newly needed states. If NULL, newly
* needed states will be dynamically allocated
* instead.
* @param[in] alloc Allocation callbacks for dynamically allocating
* new state memory.
* @param[in] scope Allocation scope for dynamically allocating new
* state memory.
* @param[out] alloc_ptr_out Will be populated with a pointer to any newly
* allocated state. The driver is responsible for
* freeing this pointer.
*/
VkResult
vk_graphics_pipeline_state_fill(const struct vk_device *device,
struct vk_graphics_pipeline_state *state,
const VkGraphicsPipelineCreateInfo *info,
const struct vk_subpass_info *sp_info,
struct vk_graphics_pipeline_all_state *all,
const VkAllocationCallbacks *alloc,
VkSystemAllocationScope scope,
void **alloc_ptr_out);
/** Merge one vk_graphics_pipeline_state into another
*
* Both the destination and source states are assumed to be valid (i.e., all
* pointers are NULL or point to valid states). Any states which exist in
* both are expected to be identical and the state already in dst is used.
* The only exception here is render pass state which may be only partially
* defined in which case the fully defined one (if any) is used.
*
* @param[out] dst The destination state. When the function returns, this
* will be the union of the original dst and src.
* @param[in] src The source state
*/
void
vk_graphics_pipeline_state_merge(struct vk_graphics_pipeline_state *dst,
const struct vk_graphics_pipeline_state *src);
#ifdef __cplusplus
}
#endif

View File

@ -24,6 +24,32 @@
#ifndef VK_LIMITS_H
#define VK_LIMITS_H
#define MESA_VK_MAX_VERTEX_BINDINGS 32
#define MESA_VK_MAX_VERTEX_ATTRIBUTES 32
/* As of June 29, 2022, according to vulkan.gpuinfo.org, 99% of all reports
* listed a max vertex stride that fits in 16 bits.
*/
#define MESA_VK_MAX_VERTEX_BINDING_STRIDE UINT16_MAX
#define MESA_VK_MAX_VIEWPORTS 16
#define MESA_VK_MAX_SCISSORS 16
#define MESA_VK_MAX_DISCARD_RECTANGLES 4
/* As of June 29, 2022, according to vulkan.gpuinfo.org, no reports list more
* than 16 samples for framebufferColorSampleCounts except one layer running
* on top of WARP on Windows.
*/
#define MESA_VK_MAX_SAMPLES 16
/* As of June 29, 2022, according to vulkan.gpuinfo.org, the only GPUs
* claiming support for maxSampleLocationGridSize greater than 1x1 is AMD
* which supports 2x2 but only up to 8 samples.
*/
#define MESA_VK_MAX_SAMPLE_LOCATIONS 32
#define MESA_VK_MAX_COLOR_ATTACHMENTS 8
/* Since VkSubpassDescription2::viewMask is a 32-bit integer, there are a
* maximum of 32 possible views.
*/

View File

@ -23,13 +23,7 @@
#include "vk_standard_sample_locations.h"
#include "util/macros.h"
struct vk_sample_locations_state {
VkSampleCountFlagBits per_pixel;
VkExtent2D grid_size;
VkSampleLocationEXT locations[16];
};
#include "vk_graphics_state.h"
/**
* 1x MSAA has a single sample at the center: (0.5, 0.5) -> (0x8, 0x8).