// Copyright 2018-2022 The Khronos Group Inc.
//
// SPDX-License-Identifier: CC-BY-4.0

[[fragmentdensitymapops]]
= Fragment Density Map Operations


== Fragment Density Map Operations Overview

When a fragment is generated in a render pass that has a fragment density
map attachment, its area is determined by the properties of the local
framebuffer region that the fragment occupies.
The framebuffer is divided into a uniform grid of these local regions, and
their fragment area property is derived from the density map with the
following operations:

  * <<fragmentdensitymap-fetch-density-value,Fetch density value>>
  ** <<fragmentdensitymap-component-swizzle,Component swizzle>>
  ** <<fragmentdensitymap-component-mapping,Component mapping>>
  * <<fragmentdensitymap-conversion-to-fragment-area,Fragment area
    conversion>>
  ** <<fragmentdensitymap-fragment-area-filter,Fragment area filter>>
  ** <<fragmentdensitymap-fragment-area-clamp,Fragment area clamp>>


[[fragmentdensitymap-fetch-density-value]]
== Fetch Density Value

ifndef::VK_QCOM_fragment_density_map_offset[]
Each local framebuffer region at center coordinate [eq]#(x,y)# fetches a
texel from the fragment density map at integer coordinates:

  {empty}:: latexmath:[i =
            \left\lfloor{\frac{x}{fragmentDensityTexelSize_{width}}}\right\rfloor]
  {empty}:: latexmath:[j =
            \left\lfloor{\frac{y}{fragmentDensityTexelSize_{height}}}\right\rfloor]
endif::VK_QCOM_fragment_density_map_offset[]

ifdef::VK_QCOM_fragment_density_map_offset[]
Each local framebuffer region at center coordinate [eq]#(x,y)# fetches a
texel from the fragment density map.

First, the local framebuffer region center coordinate [eq]#(x,y)# is offset
by the value specified in
slink:VkSubpassFragmentDensityMapOffsetEndInfoQCOM.
If no offset is specified, then the default offset [eq]#(0,0)# is used.
The offsetted coordinate [eq]#(x',y')# is computed as follows:

[latexmath]
[latexmath]
++++++++++++++++++++++++
\begin{aligned}
x' &= \mathbin{clamp}(x + pFragmentDensityOffsets[layer]_{x}, 0, framebuffer_{width} - 1)   \\
y' &= \mathbin{clamp}(y + pFragmentDensityOffsets[layer]_{y}, 0, framebuffer_{height} - 1)  \\
\end{aligned}
++++++++++++++++++++++++

The offsetted coordinate [eq]#(x',y')# fetches a texel from the fragment
density map at integer coordinates:

  {empty}:: latexmath:[i =
            \left\lfloor{\frac{x'}{fragmentDensityTexelSize_{width}}}\right\rfloor]
  {empty}:: latexmath:[j =
            \left\lfloor{\frac{y'}{fragmentDensityTexelSize_{height}}}\right\rfloor]
endif::VK_QCOM_fragment_density_map_offset[]

Where the size of each region in the framebuffer is:

  {empty}:: latexmath:[fragmentDensityTexelSize'_{width} =
            {2^{\lceil{\log_2(\frac{framebuffer_{width}}{fragmentDensityMap_{width}})}\rceil}}]
  {empty}:: latexmath:[fragmentDensityTexelSize'_{height} =
            {2^{\lceil{\log_2(\frac{framebuffer_{height}}{fragmentDensityMap_{height}})}\rceil}}]

This region is subject to the limits in
sname:VkPhysicalDeviceFragmentDensityMapPropertiesEXT and therefore the
final region size is clamped:

  {empty}:: latexmath:[fragmentDensityTexelSize_{width} =
            \mathbin{clamp}(fragmentDensityTexelSize'_{width},minFragmentDensityTexelSize_{width},maxFragmentDensityTexelSize_{width})]
  {empty}:: latexmath:[fragmentDensityTexelSize_{height} =
            \mathbin{clamp}(fragmentDensityTexelSize'_{height},minFragmentDensityTexelSize_{height},maxFragmentDensityTexelSize_{height})]

When multiview is enabled for the render pass and the fragment density map
attachment view was created with pname:layerCount greater than `1`, the
layer used for offsets and for fetching from the fragment density map is:

  {empty}:: latexmath:[layer = baseArrayLayer + ViewIndex]

Otherwise:

  {empty}:: latexmath:[layer = baseArrayLayer]

The texel fetched from the density map at [eq]#(i,j,layer)# is next
converted to density with the following operations.


[[fragmentdensitymap-component-swizzle]]
=== Component Swizzle

The pname:components member of slink:VkImageViewCreateInfo is applied to the
fetched texel as defined in <<textures-component-swizzle,Image component
swizzle>>.


[[fragmentdensitymap-component-mapping]]
=== Component Mapping

The swizzled texel's components are mapped to a density value:

  {empty}:: latexmath:[densityValue_{xy} = (C'_{r},C'_{g})]


[[fragmentdensitymap-conversion-to-fragment-area]]
== Fragment Area Conversion

Fragment area for the framebuffer region is undefined: if the density
fetched is not a normalized floating-point value greater than `0.0`.
Otherwise, the fetched fragment area for that region is derived as:

  {empty}:: latexmath:[fragmentArea_{wh} = \frac{1.0}{densityValue_{xy}}]


[[fragmentdensitymap-fragment-area-filter]]
===  Fragment Area Filter

Optionally, the implementation may: fetch additional density map texels in
an implementation defined window around [eq]#(i,j)#.
The texels follow the standard conversion steps up to and including
<<fragmentdensitymap-conversion-to-fragment-area,fragment area conversion>>.

A single fetched fragment area for the framebuffer region is chosen by the
implementation and must: have an area between the _min_ and _max_ areas of
the fetched set.


[[fragmentdensitymap-fragment-area-clamp]]
=== Fragment Area Clamp

The implementation may: clamp the fetched fragment area to one that it
supports.
The clamped fragment area must: have a size less than or equal to the
original fetched value.
Implementations may: vary the supported set of fragment areas per
framebuffer region.
Fragment area [eq]#(1,1)# must: always be in the supported set.

[NOTE]
.Note
====
For example, if the fetched fragment area is [eq]#(1,4)# but the
implementation only supports areas of [eq]#{(1,1),(2,2)}#, it could choose
to clamp the area to [eq]#(2,2)# since it has the same size as [eq]#(1,4)#.
While this would produce fragments that have lower quality strictly in the
x-axis, the overall density is maintained.
====

The clamped fragment area is assigned to the corresponding framebuffer
region.
