Reland "Use op cache when emitting types."

This is a reland of commit 60ff0facbf

Structs are now deduplicated using a [Type*, SpvId] map.

Original change's description:
> Use op cache when emitting types.
>
> We no longer need to maintain a separate `fTypeMap` for mapping types
> to SpvIds, since the op cache handles this automatically.
>
> We also now support deduplicating instructions that don't have a result,
> such as decorations. (In particular, we needed to avoid emitting the
> SpvDecorationArrayStride op every time the array type was accessed, but
> this op doesn't have a result ID.)
>
> Change-Id: I779b8c8e3de5973b8f487b28c0a8ece9a1041845
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/529732
> Reviewed-by: Brian Osman <brianosman@google.com>
> Commit-Queue: John Stiles <johnstiles@google.com>
> Auto-Submit: John Stiles <johnstiles@google.com>

Change-Id: I9f6a78d58e8af38a1fd690a8860d8b5aa3193be6
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/529748
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
John Stiles 2022-04-14 12:20:59 -04:00 committed by SkCQ
parent 5977429087
commit 0688ea21b2
9 changed files with 217 additions and 209 deletions

View File

@ -83,7 +83,6 @@ bool SPIRVCodeGenerator::Instruction::operator==(const SPIRVCodeGenerator::Instr
return fOp == that.fOp &&
fResultKind == that.fResultKind &&
fWords == that.fWords;
}
struct SPIRVCodeGenerator::Instruction::Hash {
@ -103,6 +102,7 @@ struct SPIRVCodeGenerator::Word {
kNumber,
kDefaultPrecisionResult,
kRelaxedPrecisionResult,
kUncachedResult,
};
Word(SpvId id) : fValue(id), fKind(Kind::kSpvId) {}
@ -122,6 +122,10 @@ struct SPIRVCodeGenerator::Word {
return Word{(int32_t)NA, kind};
}
static Word UncachedResult() {
return Word{(int32_t)NA, kUncachedResult};
}
static Word Result() {
return Word{(int32_t)NA, kDefaultPrecisionResult};
}
@ -515,10 +519,9 @@ void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t
this->writeWord(word8, out);
}
SpvId SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode,
const SkTArray<Word>& words,
OutputStream& out) {
// Build up an cache key for this word.
SPIRVCodeGenerator::Instruction SPIRVCodeGenerator::BuildInstructionKey(
SpvOp_ opCode, const SkTArray<Word>& words) {
// Assemble a cache key for this instruction.
Instruction key;
key.fOp = opCode;
key.fWords.resize(words.count());
@ -533,27 +536,52 @@ SpvId SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode,
}
}
return key;
}
SpvId SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode,
const SkTArray<Word>& words,
OutputStream& out) {
// If this instruction exists in our op cache, return the cached SpvId.
Instruction key = BuildInstructionKey(opCode, words);
if (SpvId* cachedOp = fOpCache.find(key)) {
return *cachedOp;
}
SpvId result = NA;
if (key.fResultKind != Word::Kind::kNone) {
// If this instruction exists in our op cache, return the cached SpvId.
if (SpvId* cachedOp = fOpCache.find(key)) {
return *cachedOp;
}
// Consume a new SpvId and cache the instruction.
result = fIdCount++;
fOpCache.set(key, result);
fSpvIdCache.set(result, key);
switch (key.fResultKind) {
case Word::Kind::kUncachedResult:
// The instruction returns a SpvId, but we do not want caching or deduplication.
result = fIdCount++;
break;
// Globally-reachable ops are not subject to the whims of flow control.
if (!is_globally_reachable_op(opCode)) {
fReachableOps.push_back(result);
}
// If the result is relaxed-precision, add the requisite decoration.
if (key.fResultKind == Word::Kind::kRelaxedPrecisionResult) {
this->writeInstruction(SpvOpDecorate, result, SpvDecorationRelaxedPrecision,
fDecorationBuffer);
}
case Word::Kind::kNone:
// The instruction doesn't return a SpvId, but we can still cache and deduplicate it.
fOpCache.set(key, result);
break;
case Word::Kind::kDefaultPrecisionResult:
case Word::Kind::kRelaxedPrecisionResult:
// Consume a new SpvId and cache the instruction.
result = fIdCount++;
fOpCache.set(key, result);
fSpvIdCache.set(result, key);
// Globally-reachable ops are not subject to the whims of flow control.
if (!is_globally_reachable_op(opCode)) {
fReachableOps.push_back(result);
}
// If the result is relaxed-precision, add the requisite decoration.
if (key.fResultKind == Word::Kind::kRelaxedPrecisionResult) {
this->writeInstruction(SpvOpDecorate, result, SpvDecorationRelaxedPrecision,
fDecorationBuffer);
}
break;
default:
SkDEBUGFAIL("unexpected result kind");
break;
}
// Write the requested instruction.
@ -750,27 +778,30 @@ SpvId SPIRVCodeGenerator::nextId(Precision precision) {
return fIdCount++;
}
void SPIRVCodeGenerator::writeStruct(const Type& type, const MemoryLayout& memoryLayout,
SpvId resultId) {
this->writeInstruction(SpvOpName, resultId, type.name(), fNameBuffer);
// go ahead and write all of the field types, so we don't inadvertently write them while we're
// in the middle of writing the struct instruction
std::vector<SpvId> types;
SpvId SPIRVCodeGenerator::writeStruct(const Type& type, const MemoryLayout& memoryLayout) {
// If we've already written out this struct, return its existing SpvId.
if (SpvId* cachedStructId = fStructMap.find(&type)) {
return *cachedStructId;
}
// Write all of the field types first, so we don't inadvertently write them while we're in the
// middle of writing the struct instruction.
Words words;
words.push_back(Word::UncachedResult());
for (const auto& f : type.fields()) {
types.push_back(this->getType(*f.fType, memoryLayout));
}
this->writeOpCode(SpvOpTypeStruct, 2 + (int32_t) types.size(), fConstantBuffer);
this->writeWord(resultId, fConstantBuffer);
for (SpvId id : types) {
this->writeWord(id, fConstantBuffer);
words.push_back(this->getType(*f.fType, memoryLayout));
}
SpvId resultId = this->writeInstruction(SpvOpTypeStruct, words, fConstantBuffer);
this->writeInstruction(SpvOpName, resultId, type.name(), fNameBuffer);
fStructMap.set(&type, resultId);
size_t offset = 0;
for (int32_t i = 0; i < (int32_t) type.fields().size(); i++) {
const Type::Field& field = type.fields()[i];
if (!MemoryLayout::LayoutIsSupported(*field.fType)) {
fContext.fErrors->error(type.fPosition, "type '" + field.fType->displayName() +
"' is not permitted here");
return;
"' is not permitted here");
return resultId;
}
size_t size = memoryLayout.size(*field.fType);
size_t alignment = memoryLayout.alignment(*field.fType);
@ -782,8 +813,8 @@ void SPIRVCodeGenerator::writeStruct(const Type& type, const MemoryLayout& memor
}
if (fieldLayout.fOffset % alignment) {
fContext.fErrors->error(field.fPosition,
"offset of field '" + std::string(field.fName) +
"' must be a multiple of " + std::to_string(alignment));
"offset of field '" + std::string(field.fName) +
"' must be a multiple of " + std::to_string(alignment));
}
offset = fieldLayout.fOffset;
} else {
@ -814,6 +845,8 @@ void SPIRVCodeGenerator::writeStruct(const Type& type, const MemoryLayout& memor
offset += alignment - offset % alignment;
}
}
return resultId;
}
const Type& SPIRVCodeGenerator::getActualType(const Type& type) {
@ -845,132 +878,105 @@ SpvId SPIRVCodeGenerator::getType(const Type& type) {
}
SpvId SPIRVCodeGenerator::getType(const Type& rawType, const MemoryLayout& layout) {
const Type* type;
std::unique_ptr<Type> arrayType;
std::string arrayName;
const Type* type = &rawType;
if (rawType.isArray()) {
// For arrays, we need to synthesize a temporary Array type using the "actual" component
// type. That is, if `short[10]` is passed in, we need to synthesize a `int[10]` Type.
// Otherwise, we can end up with two different SpvIds for the same array type.
const Type& component = this->getActualType(rawType.componentType());
arrayName = component.getArrayName(rawType.columns());
arrayType = Type::MakeArrayType(arrayName, component, rawType.columns());
type = arrayType.get();
} else {
// For non-array types, we can simply look up the "actual" type and use it.
type = &this->getActualType(rawType);
}
std::string key(type->name());
if (type->isStruct() || type->isArray()) {
key += std::to_string(layout.fStd);
#ifdef SK_DEBUG
SkASSERT(layout.fStd == MemoryLayout::Standard::k140_Standard ||
layout.fStd == MemoryLayout::Standard::k430_Standard);
MemoryLayout::Standard otherStd = layout.fStd == MemoryLayout::Standard::k140_Standard
? MemoryLayout::Standard::k430_Standard
: MemoryLayout::Standard::k140_Standard;
std::string otherKey = type->displayName() + std::to_string(otherStd);
SkASSERT(!fTypeMap.find(otherKey));
#endif
}
SpvId* entry = fTypeMap.find(key);
if (!entry) {
SpvId result = this->nextId(nullptr);
switch (type->typeKind()) {
case Type::TypeKind::kScalar:
if (type->isBoolean()) {
this->writeInstruction(SpvOpTypeBool, result, fConstantBuffer);
} else if (type->isSigned()) {
this->writeInstruction(SpvOpTypeInt, result, 32, 1, fConstantBuffer);
} else if (type->isUnsigned()) {
this->writeInstruction(SpvOpTypeInt, result, 32, 0, fConstantBuffer);
} else if (type->isFloat()) {
this->writeInstruction(SpvOpTypeFloat, result, 32, fConstantBuffer);
} else {
SkDEBUGFAILF("unrecognized scalar type '%s'", type->description().c_str());
}
break;
case Type::TypeKind::kVector:
this->writeInstruction(SpvOpTypeVector, result,
this->getType(type->componentType(), layout),
type->columns(), fConstantBuffer);
break;
case Type::TypeKind::kMatrix:
this->writeInstruction(
SpvOpTypeMatrix,
result,
this->getType(IndexExpression::IndexType(fContext, *type), layout),
type->columns(),
fConstantBuffer);
break;
case Type::TypeKind::kStruct:
this->writeStruct(*type, layout, result);
break;
case Type::TypeKind::kArray: {
if (!MemoryLayout::LayoutIsSupported(*type)) {
fContext.fErrors->error(type->fPosition, "type '" + type->displayName() +
"' is not permitted here");
return this->nextId(nullptr);
}
if (type->columns() > 0) {
SpvId typeId = this->getType(type->componentType(), layout);
SpvId countId = this->writeLiteral(type->columns(), *fContext.fTypes.fInt);
this->writeInstruction(SpvOpTypeArray, result, typeId, countId,
fConstantBuffer);
this->writeInstruction(SpvOpDecorate, result, SpvDecorationArrayStride,
(int32_t) layout.stride(*type),
fDecorationBuffer);
} else {
// We shouldn't have any runtime-sized arrays right now
fContext.fErrors->error(type->fPosition,
"runtime-sized arrays are not supported in SPIR-V");
this->writeInstruction(SpvOpTypeRuntimeArray, result,
this->getType(type->componentType(), layout),
fConstantBuffer);
this->writeInstruction(SpvOpDecorate, result, SpvDecorationArrayStride,
(int32_t) layout.stride(*type),
fDecorationBuffer);
}
break;
}
case Type::TypeKind::kSampler: {
// Subpass inputs should use the Texture type, not a Sampler.
SkASSERT(type->dimensions() != SpvDimSubpassData);
if (type->dimensions() == SpvDimBuffer) {
fCapabilities |= 1ULL << SpvCapabilitySampledBuffer;
}
SpvId imageTypeId = this->getType(type->textureType(), layout);
this->writeInstruction(SpvOpTypeSampledImage, result, imageTypeId,
fConstantBuffer);
break;
}
case Type::TypeKind::kSeparateSampler: {
this->writeInstruction(SpvOpTypeSampler, result, fConstantBuffer);
break;
}
case Type::TypeKind::kTexture: {
this->writeInstruction(SpvOpTypeImage, result,
this->getType(*fContext.fTypes.fFloat, layout),
type->dimensions(), type->isDepth(),
type->isArrayedTexture(), type->isMultisampled(),
type->isSampled() ? 1 : 2, SpvImageFormatUnknown,
fConstantBuffer);
break;
}
default:
if (type->isVoid()) {
this->writeInstruction(SpvOpTypeVoid, result, fConstantBuffer);
} else {
SkDEBUGFAILF("invalid type: %s", type->description().c_str());
}
break;
switch (type->typeKind()) {
case Type::TypeKind::kVoid: {
return this->writeInstruction(SpvOpTypeVoid, {Word::Result()}, fConstantBuffer);
}
case Type::TypeKind::kScalar:
case Type::TypeKind::kLiteral: {
if (type->isBoolean()) {
return this->writeInstruction(SpvOpTypeBool, {Word::Result()}, fConstantBuffer);
}
if (type->isSigned()) {
return this->writeInstruction(SpvOpTypeInt,
{Word::Result(), Word::Number(32), Word::Number(1)},
fConstantBuffer);
}
if (type->isUnsigned()) {
return this->writeInstruction(SpvOpTypeInt,
{Word::Result(), Word::Number(32), Word::Number(0)},
fConstantBuffer);
}
if (type->isFloat()) {
return this->writeInstruction(SpvOpTypeFloat,
{Word::Result(), Word::Number(32)},
fConstantBuffer);
}
SkDEBUGFAILF("unrecognized scalar type '%s'", type->description().c_str());
return (SpvId)-1;
}
case Type::TypeKind::kVector: {
SpvId scalarTypeId = this->getType(type->componentType(), layout);
return this->writeInstruction(
SpvOpTypeVector,
{Word::Result(), scalarTypeId, Word::Number(type->columns())},
fConstantBuffer);
}
case Type::TypeKind::kMatrix: {
SpvId vectorTypeId = this->getType(IndexExpression::IndexType(fContext, *type), layout);
return this->writeInstruction(
SpvOpTypeMatrix,
{Word::Result(), vectorTypeId, Word::Number(type->columns())},
fConstantBuffer);
}
case Type::TypeKind::kArray: {
if (!MemoryLayout::LayoutIsSupported(*type)) {
fContext.fErrors->error(type->fPosition, "type '" + type->displayName() +
"' is not permitted here");
return NA;
}
if (type->columns() == 0) {
// We do not support runtime-sized arrays.
fContext.fErrors->error(type->fPosition, "runtime-sized arrays are not supported");
return NA;
}
SpvId typeId = this->getType(type->componentType(), layout);
SpvId countId = this->writeLiteral(type->columns(), *fContext.fTypes.fInt);
SpvId result = this->writeInstruction(SpvOpTypeArray, {Word::Result(), typeId, countId},
fConstantBuffer);
this->writeInstruction(
SpvOpDecorate,
{result, SpvDecorationArrayStride, Word::Number(layout.stride(*type))},
fDecorationBuffer);
return result;
}
case Type::TypeKind::kStruct: {
return this->writeStruct(*type, layout);
}
case Type::TypeKind::kSeparateSampler: {
return this->writeInstruction(SpvOpTypeSampler, {Word::Result()}, fConstantBuffer);
}
case Type::TypeKind::kSampler: {
// Subpass inputs should use the Texture type, not a Sampler.
SkASSERT(type->dimensions() != SpvDimSubpassData);
if (SpvDimBuffer == type->dimensions()) {
fCapabilities |= 1ULL << SpvCapabilitySampledBuffer;
}
SpvId imageTypeId = this->getType(type->textureType(), layout);
return this->writeInstruction(SpvOpTypeSampledImage,
{Word::Result(), imageTypeId},
fConstantBuffer);
}
case Type::TypeKind::kTexture: {
SpvId floatTypeId = this->getType(*fContext.fTypes.fFloat, layout);
return this->writeInstruction(SpvOpTypeImage,
{Word::Result(),
floatTypeId,
Word::Number(type->dimensions()),
Word::Number(type->isDepth()),
Word::Number(type->isArrayedTexture()),
Word::Number(type->isMultisampled()),
Word::Number(type->isSampled() ? 1 : 2),
SpvImageFormatUnknown},
fConstantBuffer);
}
default: {
SkDEBUGFAILF("invalid type: %s", type->description().c_str());
return NA;
}
fTypeMap[key] = result;
return result;
}
return *entry;
}
SpvId SPIRVCodeGenerator::getFunctionType(const FunctionDeclaration& function) {

View File

@ -180,7 +180,7 @@ private:
void writeFieldLayout(const Layout& layout, SpvId target, int member);
void writeStruct(const Type& type, const MemoryLayout& layout, SpvId resultId);
SpvId writeStruct(const Type& type, const MemoryLayout& memoryLayout);
void writeProgramElement(const ProgramElement& pe, OutputStream& out);
@ -435,6 +435,8 @@ private:
struct Hash;
};
static Instruction BuildInstructionKey(SpvOp_ opCode, const SkTArray<Word>& words);
// The writeOpXxxxx calls will simplify and deduplicate ops where possible.
SpvId writeOpConstantTrue(const Type& type);
SpvId writeOpConstantFalse(const Type& type);
@ -492,7 +494,7 @@ private:
SkTHashMap<IntrinsicKind, Intrinsic> fIntrinsicMap;
SkTHashMap<const FunctionDeclaration*, SpvId> fFunctionMap;
SkTHashMap<const Variable*, SpvId> fVariableMap;
SkTHashMap<std::string, SpvId> fTypeMap;
SkTHashMap<const Type*, SpvId> fStructMap;
StringStream fGlobalInitializersBuffer;
StringStream fConstantBuffer;
StringStream fVariableBuffer;

View File

@ -31,10 +31,10 @@ OpDecorate %24 RelaxedPrecision
%bool = OpTypeBool
%_ptr_Input_bool = OpTypePointer Input %bool
%sk_Clockwise = OpVariable %_ptr_Input_bool Input
%12 = OpTypeImage %float 2D 0 0 0 1 Unknown
%11 = OpTypeSampledImage %12
%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
%s = OpVariable %_ptr_UniformConstant_11 UniformConstant
%11 = OpTypeImage %float 2D 0 0 0 1 Unknown
%12 = OpTypeSampledImage %11
%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
%s = OpVariable %_ptr_UniformConstant_12 UniformConstant
%mat4v4float = OpTypeMatrix %v4float 4
%_UniformBuffer = OpTypeStruct %mat4v4float
%_ptr_Uniform__UniformBuffer = OpTypePointer Uniform %_UniformBuffer
@ -61,7 +61,7 @@ OpDecorate %24 RelaxedPrecision
%20 = OpLabel
%tmpColor = OpVariable %_ptr_Function_v4float Function
%55 = OpVariable %_ptr_Function_v4float Function
%24 = OpLoad %11 %s
%24 = OpLoad %12 %s
%23 = OpImageSampleImplicitLod %v4float %24 %27
OpStore %tmpColor %23
%28 = OpAccessChain %_ptr_Uniform_mat4v4float %14 %int_0

View File

@ -260,10 +260,10 @@ OpDecorate %527 RelaxedPrecision
%bool = OpTypeBool
%_ptr_Input_bool = OpTypePointer Input %bool
%sk_Clockwise = OpVariable %_ptr_Input_bool Input
%22 = OpTypeImage %float 2D 0 0 0 1 Unknown
%21 = OpTypeSampledImage %22
%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21
%uTextureSampler_0_Stage1 = OpVariable %_ptr_UniformConstant_21 UniformConstant
%21 = OpTypeImage %float 2D 0 0 0 1 Unknown
%22 = OpTypeSampledImage %21
%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22
%uTextureSampler_0_Stage1 = OpVariable %_ptr_UniformConstant_22 UniformConstant
%_ptr_Input_v2float = OpTypePointer Input %v2float
%vLocalCoord_Stage0 = OpVariable %_ptr_Input_v2float Input
%_ptr_Function_v4float = OpTypePointer Function %v4float
@ -325,7 +325,7 @@ OpStore %54 %53
OpStore %59 %58
%62 = OpLoad %v2float %_2_subsetCoord
OpStore %_3_clampedCoord %62
%65 = OpLoad %21 %uTextureSampler_0_Stage1
%65 = OpLoad %22 %uTextureSampler_0_Stage1
%66 = OpLoad %v2float %_3_clampedCoord
%67 = OpAccessChain %_ptr_Uniform_v4float %4 %int_6
%68 = OpLoad %v4float %67

View File

@ -1,6 +1,6 @@
### Compilation failed:
error: SPIR-V validation error: Block decoration on target <id> '4[%_arr_testBlock_int_2]' must be a structure type
error: SPIR-V validation error: Block decoration on target <id> '8[%_arr_testBlock_int_2]' must be a structure type
OpDecorate %_arr_testBlock_int_2 Block
OpCapability Shader

View File

@ -28,11 +28,11 @@ OpDecorate %26 RelaxedPrecision
%bool = OpTypeBool
%_ptr_Input_bool = OpTypePointer Input %bool
%sk_Clockwise = OpVariable %_ptr_Input_bool Input
%12 = OpTypeImage %float 2D 0 0 0 1 Unknown
%11 = OpTypeSampledImage %12
%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
%test2D = OpVariable %_ptr_UniformConstant_11 UniformConstant
%test2DRect = OpVariable %_ptr_UniformConstant_11 UniformConstant
%11 = OpTypeImage %float 2D 0 0 0 1 Unknown
%12 = OpTypeSampledImage %11
%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
%test2D = OpVariable %_ptr_UniformConstant_12 UniformConstant
%test2DRect = OpVariable %_ptr_UniformConstant_12 UniformConstant
%void = OpTypeVoid
%16 = OpTypeFunction %void
%float_0_5 = OpConstant %float 0.5
@ -42,13 +42,13 @@ OpDecorate %26 RelaxedPrecision
%28 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5
%main = OpFunction %void None %16
%17 = OpLabel
%19 = OpLoad %11 %test2D
%19 = OpLoad %12 %test2D
%18 = OpImageSampleImplicitLod %v4float %19 %22
OpStore %sk_FragColor %18
%24 = OpLoad %11 %test2DRect
%24 = OpLoad %12 %test2DRect
%23 = OpImageSampleImplicitLod %v4float %24 %22
OpStore %sk_FragColor %23
%26 = OpLoad %11 %test2DRect
%26 = OpLoad %12 %test2DRect
%25 = OpImageSampleProjImplicitLod %v4float %26 %28
OpStore %sk_FragColor %25
OpReturn

View File

@ -1,7 +1,7 @@
### Compilation failed:
error: SPIR-V validation error: Operand 3 of TypeImage requires one of these capabilities: Sampled1D Image1D
%12 = OpTypeImage %float 1D 0 0 0 1 Unknown
%11 = OpTypeImage %float 1D 0 0 0 1 Unknown
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@ -35,10 +35,10 @@ OpDecorate %35 RelaxedPrecision
%bool = OpTypeBool
%_ptr_Input_bool = OpTypePointer Input %bool
%sk_Clockwise = OpVariable %_ptr_Input_bool Input
%12 = OpTypeImage %float 1D 0 0 0 1 Unknown
%11 = OpTypeSampledImage %12
%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
%tex = OpVariable %_ptr_UniformConstant_11 UniformConstant
%11 = OpTypeImage %float 1D 0 0 0 1 Unknown
%12 = OpTypeSampledImage %11
%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
%tex = OpVariable %_ptr_UniformConstant_12 UniformConstant
%void = OpTypeVoid
%15 = OpTypeFunction %void
%_ptr_Function_v4float = OpTypePointer Function %v4float
@ -49,10 +49,10 @@ OpDecorate %35 RelaxedPrecision
%16 = OpLabel
%a = OpVariable %_ptr_Function_v4float Function
%b = OpVariable %_ptr_Function_v4float Function
%20 = OpLoad %11 %tex
%20 = OpLoad %12 %tex
%19 = OpImageSampleImplicitLod %v4float %20 %float_0
OpStore %a %19
%24 = OpLoad %11 %tex
%24 = OpLoad %12 %tex
%23 = OpImageSampleProjImplicitLod %v4float %24 %26
OpStore %b %23
%27 = OpLoad %v4float %a

View File

@ -30,10 +30,10 @@ OpDecorate %37 RelaxedPrecision
%bool = OpTypeBool
%_ptr_Input_bool = OpTypePointer Input %bool
%sk_Clockwise = OpVariable %_ptr_Input_bool Input
%12 = OpTypeImage %float 2D 0 0 0 1 Unknown
%11 = OpTypeSampledImage %12
%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
%tex = OpVariable %_ptr_UniformConstant_11 UniformConstant
%11 = OpTypeImage %float 2D 0 0 0 1 Unknown
%12 = OpTypeSampledImage %11
%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
%tex = OpVariable %_ptr_UniformConstant_12 UniformConstant
%void = OpTypeVoid
%15 = OpTypeFunction %void
%_ptr_Function_v4float = OpTypePointer Function %v4float
@ -46,10 +46,10 @@ OpDecorate %37 RelaxedPrecision
%16 = OpLabel
%a = OpVariable %_ptr_Function_v4float Function
%b = OpVariable %_ptr_Function_v4float Function
%20 = OpLoad %11 %tex
%20 = OpLoad %12 %tex
%19 = OpImageSampleImplicitLod %v4float %20 %23
OpStore %a %19
%26 = OpLoad %11 %tex
%26 = OpLoad %12 %tex
%25 = OpImageSampleProjImplicitLod %v4float %26 %28
OpStore %b %25
%29 = OpLoad %v4float %a

View File

@ -1,7 +1,7 @@
### Compilation failed:
error: SPIR-V validation error: Operand 3 of TypeImage requires one of these capabilities: Sampled1D Image1D
%12 = OpTypeImage %float 1D 0 0 0 1 Unknown
%11 = OpTypeImage %float 1D 0 0 0 1 Unknown
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@ -39,14 +39,14 @@ OpDecorate %48 RelaxedPrecision
%bool = OpTypeBool
%_ptr_Input_bool = OpTypePointer Input %bool
%sk_Clockwise = OpVariable %_ptr_Input_bool Input
%12 = OpTypeImage %float 1D 0 0 0 1 Unknown
%11 = OpTypeSampledImage %12
%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
%one = OpVariable %_ptr_UniformConstant_11 UniformConstant
%16 = OpTypeImage %float 2D 0 0 0 1 Unknown
%15 = OpTypeSampledImage %16
%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15
%two = OpVariable %_ptr_UniformConstant_15 UniformConstant
%11 = OpTypeImage %float 1D 0 0 0 1 Unknown
%12 = OpTypeSampledImage %11
%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12
%one = OpVariable %_ptr_UniformConstant_12 UniformConstant
%15 = OpTypeImage %float 2D 0 0 0 1 Unknown
%16 = OpTypeSampledImage %15
%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16
%two = OpVariable %_ptr_UniformConstant_16 UniformConstant
%void = OpTypeVoid
%19 = OpTypeFunction %void
%_ptr_Function_v4float = OpTypePointer Function %v4float
@ -62,16 +62,16 @@ OpDecorate %48 RelaxedPrecision
%b = OpVariable %_ptr_Function_v4float Function
%c = OpVariable %_ptr_Function_v4float Function
%d = OpVariable %_ptr_Function_v4float Function
%24 = OpLoad %11 %one
%24 = OpLoad %12 %one
%23 = OpImageSampleImplicitLod %v4float %24 %float_0 Bias %float_n0_474999994
OpStore %a %23
%29 = OpLoad %15 %two
%29 = OpLoad %16 %two
%28 = OpImageSampleImplicitLod %v4float %29 %31 Bias %float_n0_474999994
OpStore %b %28
%34 = OpLoad %11 %one
%34 = OpLoad %12 %one
%33 = OpImageSampleProjImplicitLod %v4float %34 %31 Bias %float_n0_474999994
OpStore %c %33
%37 = OpLoad %15 %two
%37 = OpLoad %16 %two
%36 = OpImageSampleProjImplicitLod %v4float %37 %39 Bias %float_n0_474999994
OpStore %d %36
%40 = OpLoad %v4float %a