Files
clang-p2996/llvm/lib/ObjectYAML/DXContainerYAML.cpp
Chris Bieneman 5fdf860510 [DX] Fix PSV resource serialization
When writing this initially I missed including the resource stride.
This change adds the resources stride to the serialized value.

I've also extended the testing and error reporting around parsing PSV
information. This adds tests to verify that the reader produces
meaningful error messages for malformed DXContainer files, and a test
that verifies the resource stride is respected in the reader even if
the stride isn't an expected or known value (as would happen if the
format changes in the future).

This is part of #59479.

Reviewed By: bogner, bob80905

Differential Revision: https://reviews.llvm.org/D155143
2023-07-19 18:01:01 -05:00

262 lines
9.5 KiB
C++

//===- DXContainerYAML.cpp - DXContainer YAMLIO implementation ------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines classes for handling the YAML representation of
// DXContainerYAML.
//
//===----------------------------------------------------------------------===//
#include "llvm/ObjectYAML/DXContainerYAML.h"
#include "llvm/BinaryFormat/DXContainer.h"
namespace llvm {
// This assert is duplicated here to leave a breadcrumb of the places that need
// to be updated if flags grow past 64-bits.
static_assert((uint64_t)dxbc::FeatureFlags::NextUnusedBit <= 1ull << 63,
"Shader flag bits exceed enum size.");
DXContainerYAML::ShaderFlags::ShaderFlags(uint64_t FlagData) {
#define SHADER_FLAG(Num, Val, Str) \
Val = (FlagData & (uint64_t)dxbc::FeatureFlags::Val) > 0;
#include "llvm/BinaryFormat/DXContainerConstants.def"
}
uint64_t DXContainerYAML::ShaderFlags::getEncodedFlags() {
uint64_t Flag = 0;
#define SHADER_FLAG(Num, Val, Str) \
if (Val) \
Flag |= (uint64_t)dxbc::FeatureFlags::Val;
#include "llvm/BinaryFormat/DXContainerConstants.def"
return Flag;
}
DXContainerYAML::ShaderHash::ShaderHash(const dxbc::ShaderHash &Data)
: IncludesSource((Data.Flags & static_cast<uint32_t>(
dxbc::HashFlags::IncludesSource)) != 0),
Digest(16, 0) {
memcpy(Digest.data(), &Data.Digest[0], 16);
}
DXContainerYAML::PSVInfo::PSVInfo() : Version(0) {
memset(&Info, 0, sizeof(Info));
}
DXContainerYAML::PSVInfo::PSVInfo(const dxbc::PSV::v0::RuntimeInfo *P,
uint16_t Stage)
: Version(0) {
memset(&Info, 0, sizeof(Info));
memcpy(&Info, P, sizeof(dxbc::PSV::v0::RuntimeInfo));
assert(Stage < std::numeric_limits<uint8_t>::max() &&
"Stage should be a very small number");
// We need to bring the stage in separately since it isn't part of the v1 data
// structure.
Info.ShaderStage = static_cast<uint8_t>(Stage);
}
DXContainerYAML::PSVInfo::PSVInfo(const dxbc::PSV::v1::RuntimeInfo *P)
: Version(1) {
memset(&Info, 0, sizeof(Info));
memcpy(&Info, P, sizeof(dxbc::PSV::v1::RuntimeInfo));
}
DXContainerYAML::PSVInfo::PSVInfo(const dxbc::PSV::v2::RuntimeInfo *P)
: Version(2) {
memset(&Info, 0, sizeof(Info));
memcpy(&Info, P, sizeof(dxbc::PSV::v2::RuntimeInfo));
}
namespace yaml {
void MappingTraits<DXContainerYAML::VersionTuple>::mapping(
IO &IO, DXContainerYAML::VersionTuple &Version) {
IO.mapRequired("Major", Version.Major);
IO.mapRequired("Minor", Version.Minor);
}
void MappingTraits<DXContainerYAML::FileHeader>::mapping(
IO &IO, DXContainerYAML::FileHeader &Header) {
IO.mapRequired("Hash", Header.Hash);
IO.mapRequired("Version", Header.Version);
IO.mapOptional("FileSize", Header.FileSize);
IO.mapRequired("PartCount", Header.PartCount);
IO.mapOptional("PartOffsets", Header.PartOffsets);
}
void MappingTraits<DXContainerYAML::DXILProgram>::mapping(
IO &IO, DXContainerYAML::DXILProgram &Program) {
IO.mapRequired("MajorVersion", Program.MajorVersion);
IO.mapRequired("MinorVersion", Program.MinorVersion);
IO.mapRequired("ShaderKind", Program.ShaderKind);
IO.mapOptional("Size", Program.Size);
IO.mapRequired("DXILMajorVersion", Program.DXILMajorVersion);
IO.mapRequired("DXILMinorVersion", Program.DXILMinorVersion);
IO.mapOptional("DXILSize", Program.DXILSize);
IO.mapOptional("DXIL", Program.DXIL);
}
void MappingTraits<DXContainerYAML::ShaderFlags>::mapping(
IO &IO, DXContainerYAML::ShaderFlags &Flags) {
#define SHADER_FLAG(Num, Val, Str) IO.mapRequired(#Val, Flags.Val);
#include "llvm/BinaryFormat/DXContainerConstants.def"
}
void MappingTraits<DXContainerYAML::ShaderHash>::mapping(
IO &IO, DXContainerYAML::ShaderHash &Hash) {
IO.mapRequired("IncludesSource", Hash.IncludesSource);
IO.mapRequired("Digest", Hash.Digest);
}
void MappingTraits<DXContainerYAML::PSVInfo>::mapping(
IO &IO, DXContainerYAML::PSVInfo &PSV) {
IO.mapRequired("Version", PSV.Version);
// Store the PSV version in the YAML context.
void *OldContext = IO.getContext();
uint32_t Version = PSV.Version;
IO.setContext(&Version);
// Shader stage is only included in binaries for v1 and later, but we always
// include it since it simplifies parsing and file construction.
IO.mapRequired("ShaderStage", PSV.Info.ShaderStage);
PSV.mapInfoForVersion(IO);
IO.mapRequired("ResourceStride", PSV.ResourceStride);
IO.mapRequired("Resources", PSV.Resources);
// Restore the YAML context.
IO.setContext(OldContext);
}
void MappingTraits<DXContainerYAML::Part>::mapping(IO &IO,
DXContainerYAML::Part &P) {
IO.mapRequired("Name", P.Name);
IO.mapRequired("Size", P.Size);
IO.mapOptional("Program", P.Program);
IO.mapOptional("Flags", P.Flags);
IO.mapOptional("Hash", P.Hash);
IO.mapOptional("PSVInfo", P.Info);
}
void MappingTraits<DXContainerYAML::Object>::mapping(
IO &IO, DXContainerYAML::Object &Obj) {
IO.mapTag("!dxcontainer", true);
IO.mapRequired("Header", Obj.Header);
IO.mapRequired("Parts", Obj.Parts);
}
void MappingTraits<DXContainerYAML::ResourceBindInfo>::mapping(
IO &IO, DXContainerYAML::ResourceBindInfo &Res) {
IO.mapRequired("Type", Res.Type);
IO.mapRequired("Space", Res.Space);
IO.mapRequired("LowerBound", Res.LowerBound);
IO.mapRequired("UpperBound", Res.UpperBound);
const uint32_t *PSVVersion = static_cast<uint32_t *>(IO.getContext());
if (*PSVVersion < 2)
return;
IO.mapRequired("Kind", Res.Kind);
IO.mapRequired("Flags", Res.Flags);
}
} // namespace yaml
void DXContainerYAML::PSVInfo::mapInfoForVersion(yaml::IO &IO) {
dxbc::PipelinePSVInfo &StageInfo = Info.StageInfo;
Triple::EnvironmentType Stage = dxbc::getShaderStage(Info.ShaderStage);
switch (Stage) {
case Triple::EnvironmentType::Pixel:
IO.mapRequired("DepthOutput", StageInfo.PS.DepthOutput);
IO.mapRequired("SampleFrequency", StageInfo.PS.SampleFrequency);
break;
case Triple::EnvironmentType::Vertex:
IO.mapRequired("OutputPositionPresent", StageInfo.VS.OutputPositionPresent);
break;
case Triple::EnvironmentType::Geometry:
IO.mapRequired("InputPrimitive", StageInfo.GS.InputPrimitive);
IO.mapRequired("OutputTopology", StageInfo.GS.OutputTopology);
IO.mapRequired("OutputStreamMask", StageInfo.GS.OutputStreamMask);
IO.mapRequired("OutputPositionPresent", StageInfo.GS.OutputPositionPresent);
break;
case Triple::EnvironmentType::Hull:
IO.mapRequired("InputControlPointCount",
StageInfo.HS.InputControlPointCount);
IO.mapRequired("OutputControlPointCount",
StageInfo.HS.OutputControlPointCount);
IO.mapRequired("TessellatorDomain", StageInfo.HS.TessellatorDomain);
IO.mapRequired("TessellatorOutputPrimitive",
StageInfo.HS.TessellatorOutputPrimitive);
break;
case Triple::EnvironmentType::Domain:
IO.mapRequired("InputControlPointCount",
StageInfo.DS.InputControlPointCount);
IO.mapRequired("OutputPositionPresent", StageInfo.DS.OutputPositionPresent);
IO.mapRequired("TessellatorDomain", StageInfo.DS.TessellatorDomain);
break;
case Triple::EnvironmentType::Mesh:
IO.mapRequired("GroupSharedBytesUsed", StageInfo.MS.GroupSharedBytesUsed);
IO.mapRequired("GroupSharedBytesDependentOnViewID",
StageInfo.MS.GroupSharedBytesDependentOnViewID);
IO.mapRequired("PayloadSizeInBytes", StageInfo.MS.PayloadSizeInBytes);
IO.mapRequired("MaxOutputVertices", StageInfo.MS.MaxOutputVertices);
IO.mapRequired("MaxOutputPrimitives", StageInfo.MS.MaxOutputPrimitives);
break;
case Triple::EnvironmentType::Amplification:
IO.mapRequired("PayloadSizeInBytes", StageInfo.AS.PayloadSizeInBytes);
break;
default:
break;
}
IO.mapRequired("MinimumWaveLaneCount", Info.MinimumWaveLaneCount);
IO.mapRequired("MaximumWaveLaneCount", Info.MaximumWaveLaneCount);
if (Version == 0)
return;
IO.mapRequired("UsesViewID", Info.UsesViewID);
switch (Stage) {
case Triple::EnvironmentType::Geometry:
IO.mapRequired("MaxVertexCount", Info.GeomData.MaxVertexCount);
break;
case Triple::EnvironmentType::Hull:
case Triple::EnvironmentType::Domain:
IO.mapRequired("SigPatchConstOrPrimVectors",
Info.GeomData.SigPatchConstOrPrimVectors);
break;
case Triple::EnvironmentType::Mesh:
IO.mapRequired("SigPrimVectors", Info.GeomData.MeshInfo.SigPrimVectors);
IO.mapRequired("MeshOutputTopology",
Info.GeomData.MeshInfo.MeshOutputTopology);
break;
default:
break;
}
IO.mapRequired("SigInputElements", Info.SigInputElements);
IO.mapRequired("SigOutputElements", Info.SigOutputElements);
IO.mapRequired("SigPatchConstOrPrimElements",
Info.SigPatchConstOrPrimElements);
IO.mapRequired("SigInputVectors", Info.SigInputVectors);
MutableArrayRef<uint8_t> Vec(Info.SigOutputVectors);
IO.mapRequired("SigOutputVectors", Vec);
if (Version == 1)
return;
IO.mapRequired("NumThreadsX", Info.NumThreadsX);
IO.mapRequired("NumThreadsY", Info.NumThreadsY);
IO.mapRequired("NumThreadsZ", Info.NumThreadsZ);
}
} // namespace llvm