Hash :
6542fea9
Author :
Date :
2025-05-07T17:28:45
Vulkan: Fix SpirV Error in Tessellation Control Shader
When feature varyingsRequireMatchingPrecisionInSpirv is enabled,
in the generated SpirV, if the shader output varying precision
needs to be adjusted, we create a new shader varying to replace the
original shader output varying, and save the original shader output
varying to the replacement shader output varying at the end of the
function. However, this does not work for tessellation control shader,
because the SpirV generated will perform following equivalent peudo
code:
layout (vertices=3) out;
in mediump float tc_in[];
out mediump float tc_out[];
highp float original_out[];
void main()
{
original_out[gl_InvocationID] = tc_in[gl_InvocationID];
// some other code in between
tc_out = original_out;
}
The last line will attempt to write to all indice of tc_out. However,
according to the spec, "each tessellation control shader invocation may
write only to those outputs corresponding to its output patch vertex.
Tessellation control shader must use the special variable
gl_InvocationID as the vertex number index when writing to per-vertex
output variables."
This change fixes the problem by keeping the precision of tessellation
control shader output as it is, and adjust the precision of
tessellation evaluation shader input instead.
Bug: b/42266751
Change-Id: I398545e2cbbf703c716d6738f1ba278baac4171f
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/6521225
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Charlie Lao <cclao@google.com>
Commit-Queue: Yuxin Hu <yuxinhu@google.com>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Utilities to map shader interface variables to Vulkan mappings, and transform the SPIR-V
// accordingly.
//
#ifndef LIBANGLE_RENDERER_VULKAN_SPV_UTILS_H_
#define LIBANGLE_RENDERER_VULKAN_SPV_UTILS_H_
#include <functional>
#include "common/spirv/spirv_types.h"
#include "libANGLE/renderer/ProgramImpl.h"
#include "libANGLE/renderer/renderer_utils.h"
#include "platform/autogen/FeaturesVk_autogen.h"
namespace rx
{
class ShaderInterfaceVariableInfoMap;
struct SpvProgramInterfaceInfo
{
uint32_t currentUniformBindingIndex = 0;
uint32_t currentTextureBindingIndex = 0;
uint32_t currentShaderResourceBindingIndex = 0;
uint32_t locationsUsedForXfbExtension = 0;
};
struct SpvSourceOptions
{
uint32_t maxColorInputAttachmentCount = 0;
bool supportsTransformFeedbackExtension = false;
bool supportsTransformFeedbackEmulation = false;
bool enableTransformFeedbackEmulation = false;
bool supportsDepthStencilInputAttachments = false;
};
SpvSourceOptions SpvCreateSourceOptions(const angle::FeaturesVk &features,
uint32_t maxColorInputAttachmentCount);
struct SpvTransformOptions
{
gl::ShaderType shaderType = gl::ShaderType::InvalidEnum;
bool isLastPreFragmentStage = false;
bool isTransformFeedbackStage = false;
bool isTransformFeedbackEmulated = false;
bool isMultisampledFramebufferFetch = false;
bool enableSampleShading = false;
bool validate = true;
bool useSpirvVaryingPrecisionFixer = false;
bool removeDepthStencilInput = false;
};
struct ShaderInterfaceVariableXfbInfo
{
static constexpr uint32_t kInvalid = std::numeric_limits<uint32_t>::max();
ANGLE_ENABLE_STRUCT_PADDING_WARNINGS
struct PODStruct
{
// Used by both extension and emulation
uint32_t buffer = kInvalid;
uint32_t offset = kInvalid;
uint32_t stride = kInvalid;
// Used only by emulation (array index support is missing from VK_EXT_transform_feedback)
uint32_t arraySize = kInvalid;
uint32_t columnCount = kInvalid;
uint32_t rowCount = kInvalid;
uint32_t arrayIndex = kInvalid;
GLenum componentType = GL_FLOAT;
} pod;
ANGLE_DISABLE_STRUCT_PADDING_WARNINGS
// If empty, the whole array is captured. Otherwise only the specified members are captured.
std::vector<ShaderInterfaceVariableXfbInfo> arrayElements;
};
enum class PrecisionAdjustmentEnum : uint8_t
{
kUnchanged,
kLowerPrecision,
kUpperPrecision,
};
// Information for each shader interface variable. Not all fields are relevant to each shader
// interface variable. For example opaque uniforms require a set and binding index, while vertex
// attributes require a location.
ANGLE_ENABLE_STRUCT_PADDING_WARNINGS
struct ShaderInterfaceVariableInfo
{
ShaderInterfaceVariableInfo()
: descriptorSet(kInvalid),
binding(kInvalid),
location(kInvalid),
component(kInvalid),
index(kInvalid),
varyingIsInput(false),
varyingIsOutput(false),
hasTransformFeedback(false),
isArray(false),
padding(0),
attributeComponentCount(0),
attributeLocationCount(0)
{
SetBitField(useRelaxedPrecision, PrecisionAdjustmentEnum::kUnchanged);
}
static constexpr uint32_t kInvalid = std::numeric_limits<uint32_t>::max();
// Used for interface blocks and opaque uniforms.
uint32_t descriptorSet;
uint32_t binding;
// Used for vertex attributes, fragment shader outputs and varyings. There could be different
// variables that share the same name, such as a vertex attribute and a fragment output. They
// will share this object since they have the same name, but will find possibly different
// locations in their respective slots.
uint32_t location;
uint32_t component;
uint32_t index;
// The stages this shader interface variable is active.
gl::ShaderBitSet activeStages;
// Indicates that the precision needs to be modified in the generated SPIR-V
// to support only transferring same precision data when there's a precision
// mismatch between the shaders. For example, either the VS casts highp->mediump
// or the FS casts mediump->highp.
PrecisionAdjustmentEnum useRelaxedPrecision : 2;
// Indicate if varying is input or output, or both (in case of for example gl_Position in a
// geometry shader)
uint8_t varyingIsInput : 1;
uint8_t varyingIsOutput : 1;
uint8_t hasTransformFeedback : 1;
uint8_t isArray : 1;
uint8_t padding : 2;
// For vertex attributes, this is the number of components / locations. These are used by the
// vertex attribute aliasing transformation only.
uint8_t attributeComponentCount;
uint8_t attributeLocationCount;
};
ANGLE_DISABLE_STRUCT_PADDING_WARNINGS
struct XFBInterfaceVariableInfo
{
// Used for transform feedback extension to decorate vertex shader output.
ShaderInterfaceVariableXfbInfo xfb;
std::vector<ShaderInterfaceVariableXfbInfo> fieldXfb;
};
using XFBVariableInfoPtr = std::unique_ptr<XFBInterfaceVariableInfo>;
uint32_t SpvGetXfbBufferBlockId(const uint32_t bufferIndex);
void SpvAssignLocations(const SpvSourceOptions &options,
const gl::ProgramExecutable &programExecutable,
const gl::ProgramVaryingPacking &varyingPacking,
const gl::ShaderType transformFeedbackStage,
SpvProgramInterfaceInfo *programInterfaceInfo,
ShaderInterfaceVariableInfoMap *variableInfoMapOut);
void SpvAssignTransformFeedbackLocations(gl::ShaderType shaderType,
const gl::ProgramExecutable &programExecutable,
bool isTransformFeedbackStage,
SpvProgramInterfaceInfo *programInterfaceInfo,
ShaderInterfaceVariableInfoMap *variableInfoMapOut);
// Retrieves the compiled SPIR-V code for each shader stage.
void SpvGetShaderSpirvCode(const gl::ProgramState &programState,
gl::ShaderMap<const angle::spirv::Blob *> *spirvBlobsOut);
// Calls |SpvAssign*Locations| as necessary.
void SpvAssignAllLocations(const SpvSourceOptions &options,
const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
ShaderInterfaceVariableInfoMap *variableInfoMapOut);
angle::Result SpvTransformSpirvCode(const SpvTransformOptions &options,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const angle::spirv::Blob &initialSpirvBlob,
angle::spirv::Blob *spirvBlobOut);
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_SPV_UTILS_H_