Skip to content

Commit

Permalink
Implement parsing vulkan image formats (#1134)
Browse files Browse the repository at this point in the history
  • Loading branch information
yalcinmelihyasin authored Jun 24, 2022
1 parent 4f8d0d3 commit e00ff4e
Show file tree
Hide file tree
Showing 5 changed files with 419 additions and 1 deletion.
5 changes: 5 additions & 0 deletions vulkan_generator/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ def print_vulkan_metadata(vulkan_metadata: types.VulkanMetadata) -> None:
print("=== Vulkan Command Aliases ===")
pretty_printer.pprint(vulkan_commands.command_aliases)

image_format_metadata = vulkan_metadata.image_format_metadata

print("=== Vulkan Image Formats ===")
pretty_printer.pprint(image_format_metadata.formats)

spirv_metadata = vulkan_metadata.spirv_metadata

print("=== Spirv Extensions ===")
Expand Down
188 changes: 188 additions & 0 deletions vulkan_generator/tests/test_format_parsing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
# Copyright (C) 2022 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
This module is responsible for testing Vulkan image formats
Examples in this files stems from vk.xml that relesed by Khronos.
Anytime the particular xml updated, test should be checked
if they reflect the new XML
"""

import xml.etree.ElementTree as ET

from vulkan_generator.vulkan_parser import formats_parser
from vulkan_generator.vulkan_parser import types


def test_vulkan_image_format() -> None:
""""Test an image format"""

xml = """<?xml version="1.0" encoding="UTF-8"?>
<formats>
<format name="VK_FORMAT_R8_USCALED" class="8-bit" blockSize="1" texelsPerBlock="1">
<component name="R" bits="8" numericFormat="USCALED"/>
</format>
</formats>"""

image_format_metadata = formats_parser.parse(ET.fromstring(xml))
assert "VK_FORMAT_R8_USCALED" in image_format_metadata.formats

image_format = image_format_metadata.formats["VK_FORMAT_R8_USCALED"]

assert isinstance(image_format, types.ImageFormat)
assert image_format.name == "VK_FORMAT_R8_USCALED"
assert image_format.format_class == "8-bit"
assert image_format.block_size == 1
assert image_format.texels_per_block == 1


def test_vulkan_image_format_with_spirv_format() -> None:
""""Test an image format with spirv format"""

xml = """<?xml version="1.0" encoding="UTF-8"?>
<formats>
<format name="VK_FORMAT_R8_UNORM" class="8-bit" blockSize="1" texelsPerBlock="1">
<component name="R" bits="8" numericFormat="UNORM"/>
<spirvimageformat name="R8"/>
</format>
</formats>"""

image_format_metadata = formats_parser.parse(ET.fromstring(xml))
image_format = image_format_metadata.formats["VK_FORMAT_R8_UNORM"]
assert image_format.spirv_format == "R8"


def test_vulkan_image_format_with_packed() -> None:
""""Test an image format with packed"""

xml = """<?xml version="1.0" encoding="UTF-8"?>
<formats>
<format name="VK_FORMAT_R4G4_UNORM_PACK8" class="8-bit" blockSize="1" texelsPerBlock="1" packed="8">
<component name="R" bits="4" numericFormat="UNORM"/>
<component name="G" bits="4" numericFormat="UNORM"/>
</format>
</formats>"""

image_format_metadata = formats_parser.parse(ET.fromstring(xml))
image_format = image_format_metadata.formats["VK_FORMAT_R4G4_UNORM_PACK8"]
assert image_format.packed == 8


def test_vulkan_image_format_components() -> None:
""""Test an image format with components"""

xml = """<?xml version="1.0" encoding="UTF-8"?>
<formats>
<format name="VK_FORMAT_A1R5G5B5_UNORM_PACK16" class="16-bit" blockSize="2" texelsPerBlock="1" packed="16">
<component name="A" bits="1" numericFormat="UNORM"/>
<component name="R" bits="5" numericFormat="UNORM"/>
<component name="G" bits="5" numericFormat="UNORM"/>
<component name="B" bits="5" numericFormat="UNORM"/>
</format>
</formats>"""

image_format_metadata = formats_parser.parse(ET.fromstring(xml))
image_format = image_format_metadata.formats["VK_FORMAT_A1R5G5B5_UNORM_PACK16"]

components = list(image_format.components.values())

assert components[0].name == "A"
assert components[0].bits == 1
assert components[0].numeric_format == "UNORM"

assert components[1].name == "R"
assert components[1].bits == 5
assert components[1].numeric_format == "UNORM"

assert components[2].name == "G"
assert components[2].bits == 5
assert components[2].numeric_format == "UNORM"

assert components[3].name == "B"
assert components[3].bits == 5
assert components[3].numeric_format == "UNORM"


def test_vulkan_image_format_uncompressed_components() -> None:
""""Test an image format with uncompressed components"""

xml = """<?xml version="1.0" encoding="UTF-8"?>
<formats>
<format name="VK_FORMAT_A1R5G5B5_UNORM_PACK16" class="16-bit" blockSize="2" texelsPerBlock="1" packed="16">
<component name="A" bits="1" numericFormat="UNORM"/>
<component name="R" bits="5" numericFormat="UNORM"/>
<component name="G" bits="5" numericFormat="UNORM"/>
<component name="B" bits="5" numericFormat="UNORM"/>
</format>
</formats>"""

image_format_metadata = formats_parser.parse(ET.fromstring(xml))
image_format = image_format_metadata.formats["VK_FORMAT_A1R5G5B5_UNORM_PACK16"]

components = list(image_format.components.values())
assert not components[0].compressed
assert not components[1].compressed
assert not components[2].compressed
assert not components[3].compressed


def test_vulkan_image_format_compressed_components() -> None:
""""Test an image format with compressed components"""

xml = """<?xml version="1.0" encoding="UTF-8"?>
<formats>
<format name="VK_FORMAT_BC5_SNORM_BLOCK" class="BC5" blockSize="16" texelsPerBlock="16"
blockExtent="4,4,1" compressed="BC">
<component name="R" bits="compressed" numericFormat="SRGB"/>
<component name="G" bits="compressed" numericFormat="SRGB"/>
</format>
</formats>"""

image_format_metadata = formats_parser.parse(ET.fromstring(xml))
image_format = image_format_metadata.formats["VK_FORMAT_BC5_SNORM_BLOCK"]

components = list(image_format.components.values())

assert components[0].compressed
assert components[1].compressed


def test_vulkan_image_format_planes() -> None:
""""Test an image format with planes"""

xml = """<?xml version="1.0" encoding="UTF-8"?>
<formats>
<format name="VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16" class="10-bit 2-plane 444" blockSize="6"
texelsPerBlock="1" packed="16" chroma="444">
<component name="G" bits="10" numericFormat="UNORM" planeIndex="0"/>
<component name="B" bits="10" numericFormat="UNORM" planeIndex="1"/>
<component name="R" bits="10" numericFormat="UNORM" planeIndex="1"/>
<plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R10X6_UNORM_PACK16"/>
<plane index="1" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R10X6G10X6_UNORM_2PACK16"/>
</format>
</formats>"""

image_format_metadata = formats_parser.parse(ET.fromstring(xml))
image_format = image_format_metadata.formats["VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16"]

assert image_format.planes[0].index == 0
assert image_format.planes[0].width_divisor == 1
assert image_format.planes[0].height_divisor == 1
assert image_format.planes[0].compatible == "VK_FORMAT_R10X6_UNORM_PACK16"

assert image_format.planes[1].index == 1
assert image_format.planes[1].width_divisor == 1
assert image_format.planes[1].height_divisor == 1
assert image_format.planes[1].compatible == "VK_FORMAT_R10X6G10X6_UNORM_2PACK16"
159 changes: 159 additions & 0 deletions vulkan_generator/vulkan_parser/formats_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Copyright (C) 2022 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

""" This module is responsible for parsing Vulkan Image Formats"""

from typing import OrderedDict
from typing import List
from typing import Optional

import xml.etree.ElementTree as ET

from vulkan_generator.vulkan_utils import parsing_utils
from vulkan_generator.vulkan_parser import types


def parse_format_planes(format_element: ET.Element) -> List[types.ImageFormatPlane]:
"""
Parser for the image format planes if format is multi-planar
Sample image format plane:
<plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R8_UNORM"/>
"""
planes: List[types.ImageFormatPlane] = []

for plane in format_element:
if plane.tag == "spirvimageformat":
# this is handled by image format parser
continue

if plane.tag == "component":
# this is handled by image format component parser
continue

if plane.tag == "plane":
index = int(plane.attrib["index"], 0)
with_divisor = int(plane.attrib["widthDivisor"], 0)
height_divisor = int(plane.attrib["heightDivisor"], 0)
compatible = plane.attrib["compatible"]

planes.append(types.ImageFormatPlane(
index=index,
width_divisor=with_divisor,
height_divisor=height_divisor,
compatible=compatible
))
else:
raise SyntaxError(f"Uknown image format element{ET.tostring(plane, 'utf-8')!r}")

return planes


def parse_format_components(format_element: ET.Element) -> OrderedDict[str, types.ImageFormatComponent]:
"""
Parser for the image formats components
Sample image format component:
<component name="R" bits="compressed" numericFormat="SRGB"/>
"""
components: OrderedDict[str, types.ImageFormatComponent] = OrderedDict()

for component in format_element:
if component.tag == "spirvimageformat":
# this is handled by image format parser
continue

if component.tag == "plane":
# this is handled by image format planes parser
continue

if component.tag == "component":
name = component.attrib["name"]
numeric_format = component.attrib["numericFormat"]

# if the image is not compressed then bits per component is available
bits = None
compressed = component.attrib["bits"] == "compressed"
if not compressed:
bits = int(component.attrib["bits"], 0)

components[name] = types.ImageFormatComponent(
name=name,
numeric_format=numeric_format,
bits=bits,
compressed=compressed
)
else:
raise SyntaxError(f"Uknown image format element{ET.tostring(component, 'utf-8')!r}")

return components


def parse_format(format_element: ET.Element) -> types.ImageFormat:
"""
Parser for the Vulkan image formats
A sample Vulkan image format:
<format name="VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM" class="8-bit 3-plane 420"
blockSize="3" texelsPerBlock="1" chroma="420">
<component name="G" bits="8" numericFormat="UNORM" planeIndex="0"/>
<component name="B" bits="8" numericFormat="UNORM" planeIndex="1"/>
<component name="R" bits="8" numericFormat="UNORM" planeIndex="2"/>
<plane index="0" widthDivisor="1" heightDivisor="1" compatible="VK_FORMAT_R8_UNORM"/>
<plane index="1" widthDivisor="2" heightDivisor="2" compatible="VK_FORMAT_R8_UNORM"/>
<plane index="2" widthDivisor="2" heightDivisor="2" compatible="VK_FORMAT_R8_UNORM"/>
</format>
"""
name = format_element.attrib["name"]
format_class = format_element.attrib["class"]
block_size = int(format_element.attrib["blockSize"], 0)
texels_per_block = int(format_element.attrib["texelsPerBlock"], 0)

# Pack size for the packed formats
packed: Optional[int] = None
packed_str = parsing_utils.try_get_attribute(format_element, "packed")
if packed_str:
packed = int(format_element.attrib["packed"], 0)

# if the image has a spirv equavelant, it's stated in a child node
spirv_format: Optional[str] = None
for child in format_element:
if child.tag == "spirvimageformat":
spirv_format = child.attrib["name"]

components = parse_format_components(format_element)

# Parse plane info for multi-planar formats. For non-planar formats
# this list will be empty
planes = parse_format_planes(format_element)

return types.ImageFormat(
name=name,
format_class=format_class,
block_size=block_size,
texels_per_block=texels_per_block,
packed=packed,
spirv_format=spirv_format,
components=components,
planes=planes)


def parse(formats_element: ET.Element) -> types.ImageFormatMetadata:
format_metadata = types.ImageFormatMetadata()

for format_element in formats_element:
image_format = parse_format(format_element)
format_metadata.formats[image_format.name] = image_format

return format_metadata
10 changes: 9 additions & 1 deletion vulkan_generator/vulkan_parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import xml.etree.ElementTree as ET

from vulkan_generator.vulkan_parser import types
from vulkan_generator.vulkan_parser import formats_parser
from vulkan_generator.vulkan_parser import type_parser
from vulkan_generator.vulkan_parser import enums_parser
from vulkan_generator.vulkan_parser import commands_parser
Expand Down Expand Up @@ -51,6 +52,7 @@ def parse(filename: Path) -> types.VulkanMetadata:
tree = ET.parse(filename)
all_types = types.AllVulkanTypes()
all_commands = types.AllVulkanCommands()
format_metadata = types.ImageFormatMetadata()
spirv_metadata = types.SpirvMetadata()

for child in tree.getroot():
Expand All @@ -60,7 +62,13 @@ def parse(filename: Path) -> types.VulkanMetadata:
process_enums(all_types, child)
elif child.tag == "commands":
all_commands = commands_parser.parse(child)
elif child.tag == "formats":
format_metadata = formats_parser.parse(child)
elif child.tag.startswith("spirv"):
spirv_metadata = spirv_parser.parse(child)

return types.VulkanMetadata(types=all_types, commands=all_commands, spirv_metadata=spirv_metadata)
return types.VulkanMetadata(
types=all_types,
commands=all_commands,
image_format_metadata=format_metadata,
spirv_metadata=spirv_metadata)
Loading

0 comments on commit e00ff4e

Please sign in to comment.