blob: 71eba3d3b50eea85df1bc6076d886d366f0436cd [file] [log] [blame]
#!/usr/bin/python3 -i
#
# Copyright 2013-2023 The Khronos Group Inc.
#
# SPDX-License-Identifier: Apache-2.0
from generator import OutputGenerator, write
from spec_tools.attributes import ExternSyncEntry
from spec_tools.util import getElemName
import pdb
def makeLink(link, altlink = None):
"""Create an asciidoctor link, optionally with altlink text
if provided"""
if altlink is not None:
return '<<{},{}>>'.format(link, altlink)
else:
return '<<{}>>'.format(link)
class SpirvCapabilityOutputGenerator(OutputGenerator):
"""SpirvCapabilityOutputGenerator - subclass of OutputGenerator.
Generates AsciiDoc includes of the SPIR-V capabilities table for the
features chapter of the API specification.
---- methods ----
SpirvCapabilityOutputGenerator(errFile, warnFile, diagFile) - args as for
OutputGenerator. Defines additional internal state.
---- methods overriding base class ----
genCmd(cmdinfo)"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def beginFile(self, genOpts):
OutputGenerator.beginFile(self, genOpts)
# Accumulate SPIR-V capability and feature information
self.spirv = []
def getCondition(self, enable, parent):
"""Return a strings which is the condition under which an
enable is supported.
- enable - ElementTree corresponding to an <enable> XML tag for a
SPIR-V capability or extension
- parent - Parent <spirvcapability> or <spirvenable> ElementTree,
used for error reporting"""
if enable.get('version'):
return enable.get('version')
elif enable.get('extension'):
return enable.get('extension')
elif enable.get('struct') or enable.get('property'):
return enable.get('requires')
else:
self.logMsg('error', f"<{parent.tag} name=\"{parent.get('name')}\"> is missing a required attribute for an <enable>")
return ''
def getConditions(self, enables):
"""Return a sorted list of strings which are conditions under which
one or more of the enables is supported.
- enables - ElementTree corresponding to a <spirvcapability> or
<spirvextension> XML tag"""
conditions = set()
for enable in enables.findall('enable'):
condition = self.getCondition(enable, parent=enables)
if condition != None:
conditions.add(condition)
return sorted(conditions)
def endFile(self):
captable = []
exttable = []
# How to "indent" a pseudo-column for better use of space.
# {captableindent} is defined in appendices/spirvenv.adoc
indent = '{captableindent}'
for elem in self.spirv:
conditions = self.getConditions(elem)
# Combine all conditions for enables and surround the row with
# them
if len(conditions) > 0:
condition_string = ','.join(conditions)
prefix = [ 'ifdef::{}[]'.format(condition_string) ]
suffix = [ 'endif::{}[]'.format(condition_string) ]
else:
prefix = []
suffix = []
body = []
# Generate an anchor for each capability
if elem.tag == 'spirvcapability':
anchor = '[[spirvenv-capabilities-table-{}]]'.format(
elem.get('name'))
else:
# <spirvextension> entries do not get anchors
anchor = ''
# First "cell" in a table row, and a break for the other "cells"
body.append('| {}code:{} +'.format(anchor, elem.get('name')))
# Iterate over each enable emitting a formatting tag for it
# Protect the term if there is a version or extension
# requirement, and if there are multiple enables (otherwise,
# the ifdef protecting the entire row will suffice).
enables = [e for e in elem.findall('enable')]
remaining = len(enables)
for subelem in enables:
remaining -= 1
# Sentinel value
linktext = None
if subelem.get('version'):
version = subelem.get('version')
# Convert API enum to anchor for version appendices (versions-m.n)
# version must be the spec conditional macro VK_VERSION_m_n, not
# the API version macro VK_API_VERSION_m_n.
enable = version
link = 'versions-' + version[-3:].replace('_', '.')
altlink = version
linktext = makeLink(link, altlink)
elif subelem.get('extension'):
extension = subelem.get('extension')
enable = extension
link = extension
altlink = None
# This uses the extension name macro, rather than
# asciidoc markup
linktext = '`apiext:{}`'.format(extension)
elif subelem.get('struct'):
struct = subelem.get('struct')
feature = subelem.get('feature')
requires = subelem.get('requires')
alias = subelem.get('alias')
link_name = feature
# For cases, like bufferDeviceAddressEXT where need manual help
if alias:
link_name = alias
exceptions = {
'VkPhysicalDeviceCooperativeMatrixFeaturesNV::cooperativeMatrix': 'cooperativeMatrix-NV',
}
if struct + '::' + feature in exceptions:
link_name = exceptions[struct + '::' + feature]
enable = requires
link = 'features-' + link_name
altlink = 'sname:{}::pname:{}'.format(struct, feature)
linktext = makeLink(link, altlink)
else:
property = subelem.get('property')
member = subelem.get('member')
requires = subelem.get('requires')
value = subelem.get('value')
enable = requires
# Properties should have a "feature" prefix
link = 'limits-' + member
# Display the property value by itself if it is not a boolean (matches original table)
# DenormPreserve is an example where it makes sense to just show the
# member value as it is just a boolean and the name implies "true"
# GroupNonUniformVote is an example where the whole name is too long
# better to just display the value
if value == "VK_TRUE":
altlink = 'sname:{}::pname:{}'.format(property, member)
else:
altlink = '{}'.format(value)
linktext = makeLink(link, altlink)
# If there are no more enables, do not continue the last line
if remaining > 0:
continuation = ' +'
else:
continuation = ''
# condition_string != enable is a small optimization
if enable is not None and condition_string != enable:
body.append('ifdef::{}[]'.format(enable))
body.append('{} {}{}'.format(indent, linktext, continuation))
if enable is not None and condition_string != enable:
body.append('endif::{}[]'.format(enable))
if elem.tag == 'spirvcapability':
captable += prefix + body + suffix
else:
exttable += prefix + body + suffix
# Generate the asciidoc include files
self.writeBlock('captable.adoc', captable)
self.writeBlock('exttable.adoc', exttable)
# Finish processing in superclass
OutputGenerator.endFile(self)
def writeBlock(self, basename, contents):
"""Generate an include file.
- directory - subdirectory to put file in
- basename - base name of the file
- contents - contents of the file (Asciidoc boilerplate aside)"""
filename = self.genOpts.directory + '/' + basename
self.logMsg('diag', '# Generating include file:', filename)
with open(filename, 'w', encoding='utf-8') as fp:
write(self.genOpts.conventions.warning_comment, file=fp)
if len(contents) > 0:
for str in contents:
write(str, file=fp)
else:
self.logMsg('diag', '# No contents for:', filename)
def genSpirv(self, capinfo, name, alias):
"""Generate SPIR-V capabilities
capinfo - dictionary entry for an XML <spirvcapability> or
<spirvextension> element
name - name attribute of capinfo.elem"""
OutputGenerator.genSpirv(self, capinfo, name, alias)
# Just accumulate each element, process in endFile
self.spirv.append(capinfo.elem)