Coverage Report

Created: 2026-11-12 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/val/validate_ray_query.cpp
Line
Count
Source
1
// Copyright (c) 2022 The Khronos Group Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
// Validates ray query instructions from SPV_KHR_ray_query
16
17
#include "source/opcode.h"
18
#include "source/val/instruction.h"
19
#include "source/val/validate.h"
20
#include "source/val/validation_state.h"
21
22
namespace spvtools {
23
namespace val {
24
namespace {
25
26
0
uint32_t GetArrayLength(ValidationState_t& _, const Instruction* array_type) {
27
0
  assert(array_type->opcode() == spv::Op::OpTypeArray);
28
0
  uint32_t const_int_id = array_type->GetOperandAs<uint32_t>(2U);
29
0
  Instruction* array_length_inst = _.FindDef(const_int_id);
30
0
  uint32_t array_length = 0;
31
0
  if (array_length_inst->opcode() == spv::Op::OpConstant) {
32
0
    array_length = array_length_inst->GetOperandAs<uint32_t>(2);
33
0
  }
34
0
  return array_length;
35
0
}
36
37
spv_result_t ValidateRayQueryPointer(ValidationState_t& _,
38
                                     const Instruction* inst,
39
0
                                     uint32_t ray_query_index) {
40
0
  const uint32_t ray_query_id = inst->GetOperandAs<uint32_t>(ray_query_index);
41
0
  auto variable = _.FindDef(ray_query_id);
42
0
  const auto var_opcode = variable->opcode();
43
0
  if (!variable || (var_opcode != spv::Op::OpVariable &&
44
0
                    var_opcode != spv::Op::OpFunctionParameter &&
45
0
                    var_opcode != spv::Op::OpAccessChain &&
46
0
                    var_opcode != spv::Op::OpInBoundsAccessChain &&
47
0
                    var_opcode != spv::Op::OpCopyObject)) {
48
0
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
49
0
           << "Ray Query must be a memory object declaration";
50
0
  }
51
0
  auto pointer = _.FindDef(variable->GetOperandAs<uint32_t>(0));
52
0
  if (!pointer || pointer->opcode() != spv::Op::OpTypePointer) {
53
0
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
54
0
           << "Ray Query must be a pointer";
55
0
  }
56
0
  auto type = _.FindDef(pointer->GetOperandAs<uint32_t>(2));
57
0
  if (!type || type->opcode() != spv::Op::OpTypeRayQueryKHR) {
58
0
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
59
0
           << "Ray Query must be a pointer to OpTypeRayQueryKHR";
60
0
  }
61
0
  return SPV_SUCCESS;
62
0
}
63
64
spv_result_t ValidateIntersectionId(ValidationState_t& _,
65
                                    const Instruction* inst,
66
0
                                    uint32_t intersection_index) {
67
0
  const uint32_t intersection_id =
68
0
      inst->GetOperandAs<uint32_t>(intersection_index);
69
0
  const uint32_t intersection_type = _.GetTypeId(intersection_id);
70
0
  const spv::Op intersection_opcode = _.GetIdOpcode(intersection_id);
71
0
  if (!_.IsIntScalarType(intersection_type, 32) ||
72
0
      !spvOpcodeIsConstant(intersection_opcode)) {
73
0
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
74
0
           << "expected Intersection ID to be a constant 32-bit int scalar";
75
0
  }
76
77
0
  return SPV_SUCCESS;
78
0
}
79
80
}  // namespace
81
82
14.1M
spv_result_t RayQueryPass(ValidationState_t& _, const Instruction* inst) {
83
14.1M
  const spv::Op opcode = inst->opcode();
84
14.1M
  const uint32_t result_type = inst->type_id();
85
86
14.1M
  switch (opcode) {
87
0
    case spv::Op::OpRayQueryInitializeKHR: {
88
0
      if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error;
89
90
0
      if (_.GetIdOpcode(_.GetOperandTypeId(inst, 1)) !=
91
0
          spv::Op::OpTypeAccelerationStructureKHR) {
92
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
93
0
               << "Expected Acceleration Structure to be of type "
94
0
                  "OpTypeAccelerationStructureKHR";
95
0
      }
96
97
0
      const uint32_t ray_flags = _.GetOperandTypeId(inst, 2);
98
0
      if (!_.IsIntScalarType(ray_flags, 32)) {
99
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
100
0
               << "Ray Flags must be a 32-bit int scalar";
101
0
      }
102
103
0
      if (!_.CheckForceOpacityMicromap2StateKHRCapabilityRequirement(inst, 2)) {
104
0
        return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
105
0
               << "The ForceOpacityMicromap2StateKHR flag requires the "
106
0
                  "RayTracingOpacityMicromapKHR and RayQueryKHR or "
107
0
                  "RayTracingKHR capabilities";
108
0
      }
109
110
0
      if (auto error = ValidateRayFlagsValue(_, inst, 2)) return error;
111
112
0
      const uint32_t cull_mask = _.GetOperandTypeId(inst, 3);
113
0
      if (!_.IsIntScalarType(cull_mask, 32)) {
114
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
115
0
               << "Cull Mask must be a 32-bit int scalar";
116
0
      }
117
118
0
      const uint32_t ray_origin = _.GetOperandTypeId(inst, 4);
119
0
      if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 ||
120
0
          _.GetBitWidth(ray_origin) != 32) {
121
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
122
0
               << "Ray Origin must be a 32-bit float 3-component vector";
123
0
      }
124
125
0
      const uint32_t ray_tmin = _.GetOperandTypeId(inst, 5);
126
0
      if (!_.IsFloatScalarType(ray_tmin, 32)) {
127
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
128
0
               << "Ray TMin must be a 32-bit float scalar";
129
0
      }
130
131
0
      const uint32_t ray_direction = _.GetOperandTypeId(inst, 6);
132
0
      if (!_.IsFloatVectorType(ray_direction) ||
133
0
          _.GetDimension(ray_direction) != 3 ||
134
0
          _.GetBitWidth(ray_direction) != 32) {
135
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
136
0
               << "Ray Direction must be a 32-bit float 3-component vector";
137
0
      }
138
139
0
      const uint32_t ray_tmax = _.GetOperandTypeId(inst, 7);
140
0
      if (!_.IsFloatScalarType(ray_tmax, 32)) {
141
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
142
0
               << "Ray TMax must be a 32-bit float scalar";
143
0
      }
144
145
0
      if (auto error = ValidateRayTMinTMaxValue(_, inst, 5, 7, 6349, 6350)) {
146
0
        return error;
147
0
      }
148
0
      break;
149
0
    }
150
151
0
    case spv::Op::OpRayQueryTerminateKHR:
152
0
    case spv::Op::OpRayQueryConfirmIntersectionKHR: {
153
0
      if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error;
154
0
      break;
155
0
    }
156
157
0
    case spv::Op::OpRayQueryGenerateIntersectionKHR: {
158
0
      if (auto error = ValidateRayQueryPointer(_, inst, 0)) return error;
159
160
0
      const uint32_t hit_t_id = _.GetOperandTypeId(inst, 1);
161
0
      if (!_.IsFloatScalarType(hit_t_id, 32)) {
162
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
163
0
               << "Hit T must be a 32-bit float scalar";
164
0
      }
165
166
0
      break;
167
0
    }
168
169
0
    case spv::Op::OpRayQueryGetIntersectionFrontFaceKHR:
170
0
    case spv::Op::OpRayQueryProceedKHR:
171
0
    case spv::Op::OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: {
172
0
      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
173
174
0
      if (!_.IsBoolScalarType(result_type)) {
175
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
176
0
               << "expected Result Type to be bool scalar type";
177
0
      }
178
179
0
      if (opcode == spv::Op::OpRayQueryGetIntersectionFrontFaceKHR) {
180
0
        if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
181
0
      }
182
183
0
      break;
184
0
    }
185
186
0
    case spv::Op::OpRayQueryGetIntersectionTKHR:
187
0
    case spv::Op::OpRayQueryGetRayTMinKHR: {
188
0
      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
189
190
0
      if (!_.IsFloatScalarType(result_type, 32)) {
191
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
192
0
               << "expected Result Type to be 32-bit float scalar type";
193
0
      }
194
195
0
      if (opcode == spv::Op::OpRayQueryGetIntersectionTKHR) {
196
0
        if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
197
0
      }
198
199
0
      break;
200
0
    }
201
202
0
    case spv::Op::OpRayQueryGetIntersectionTypeKHR:
203
0
    case spv::Op::OpRayQueryGetIntersectionInstanceCustomIndexKHR:
204
0
    case spv::Op::OpRayQueryGetIntersectionInstanceIdKHR:
205
0
    case spv::Op::
206
0
        OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR:
207
0
    case spv::Op::OpRayQueryGetIntersectionGeometryIndexKHR:
208
0
    case spv::Op::OpRayQueryGetIntersectionPrimitiveIndexKHR:
209
0
    case spv::Op::OpRayQueryGetRayFlagsKHR: {
210
0
      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
211
212
0
      if (!_.IsIntScalarType(result_type, 32)) {
213
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
214
0
               << "expected Result Type to be 32-bit int scalar type";
215
0
      }
216
217
0
      if (opcode != spv::Op::OpRayQueryGetRayFlagsKHR) {
218
0
        if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
219
0
      }
220
221
0
      break;
222
0
    }
223
224
0
    case spv::Op::OpRayQueryGetIntersectionObjectRayDirectionKHR:
225
0
    case spv::Op::OpRayQueryGetIntersectionObjectRayOriginKHR:
226
0
    case spv::Op::OpRayQueryGetWorldRayDirectionKHR:
227
0
    case spv::Op::OpRayQueryGetWorldRayOriginKHR: {
228
0
      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
229
230
0
      if (!_.IsFloatVectorType(result_type) ||
231
0
          _.GetDimension(result_type) != 3 ||
232
0
          _.GetBitWidth(result_type) != 32) {
233
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
234
0
               << "expected Result Type to be 32-bit float 3-component "
235
0
                  "vector type";
236
0
      }
237
238
0
      if (opcode == spv::Op::OpRayQueryGetIntersectionObjectRayDirectionKHR ||
239
0
          opcode == spv::Op::OpRayQueryGetIntersectionObjectRayOriginKHR) {
240
0
        if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
241
0
      }
242
243
0
      break;
244
0
    }
245
246
0
    case spv::Op::OpRayQueryGetIntersectionBarycentricsKHR: {
247
0
      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
248
0
      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
249
250
0
      if (!_.IsFloatVectorType(result_type) ||
251
0
          _.GetDimension(result_type) != 2 ||
252
0
          _.GetBitWidth(result_type) != 32) {
253
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
254
0
               << "expected Result Type to be 32-bit float 2-component "
255
0
                  "vector type";
256
0
      }
257
258
0
      break;
259
0
    }
260
261
0
    case spv::Op::OpRayQueryGetIntersectionObjectToWorldKHR:
262
0
    case spv::Op::OpRayQueryGetIntersectionWorldToObjectKHR: {
263
0
      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
264
0
      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
265
266
0
      uint32_t num_rows = 0;
267
0
      uint32_t num_cols = 0;
268
0
      uint32_t col_type = 0;
269
0
      uint32_t component_type = 0;
270
0
      if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type,
271
0
                               &component_type)) {
272
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
273
0
               << "expected matrix type as Result Type";
274
0
      }
275
276
0
      if (num_cols != 4) {
277
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
278
0
               << "expected Result Type matrix to have a Column Count of 4";
279
0
      }
280
281
0
      if (!_.IsFloatScalarType(component_type) ||
282
0
          _.GetBitWidth(result_type) != 32 || num_rows != 3) {
283
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
284
0
               << "expected Result Type matrix to have a Column Type of "
285
0
                  "3-component 32-bit float vectors";
286
0
      }
287
0
      break;
288
0
    }
289
290
0
    case spv::Op::OpRayQueryGetClusterIdNV: {
291
0
      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
292
0
      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
293
294
0
      if (!_.IsIntScalarType(result_type, 32)) {
295
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
296
0
               << "expected Result Type to be 32-bit int scalar type";
297
0
      }
298
0
      break;
299
0
    }
300
301
0
    case spv::Op::OpRayQueryGetIntersectionSpherePositionNV: {
302
0
      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
303
0
      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
304
305
0
      if (!_.IsFloatVectorType(result_type) ||
306
0
          _.GetDimension(result_type) != 3 ||
307
0
          _.GetBitWidth(result_type) != 32) {
308
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
309
0
               << "expected Result Type to be 32-bit float 3-component "
310
0
                  "vector type";
311
0
      }
312
0
      break;
313
0
    }
314
315
0
    case spv::Op::OpRayQueryGetIntersectionLSSPositionsNV: {
316
0
      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
317
0
      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
318
319
0
      auto result_id = _.FindDef(result_type);
320
0
      if ((result_id->opcode() != spv::Op::OpTypeArray) ||
321
0
          (GetArrayLength(_, result_id) != 2) ||
322
0
          !_.IsFloatVectorType(_.GetComponentType(result_type)) ||
323
0
          _.GetDimension(_.GetComponentType(result_type)) != 3) {
324
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
325
0
               << "Expected 2 element array of 32-bit 3 component float point "
326
0
                  "vector as Result Type: "
327
0
               << spvOpcodeString(opcode);
328
0
      }
329
0
      break;
330
0
    }
331
332
0
    case spv::Op::OpRayQueryGetIntersectionLSSRadiiNV: {
333
0
      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
334
0
      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
335
336
0
      if (!_.IsFloatArrayType(result_type) ||
337
0
          (GetArrayLength(_, _.FindDef(result_type)) != 2) ||
338
0
          !_.IsFloatScalarType(_.GetComponentType(result_type))) {
339
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
340
0
               << "Expected 32-bit floating point scalar as Result Type: "
341
0
               << spvOpcodeString(opcode);
342
0
      }
343
0
      break;
344
0
    }
345
346
0
    case spv::Op::OpRayQueryGetIntersectionSphereRadiusNV:
347
0
    case spv::Op::OpRayQueryGetIntersectionLSSHitValueNV: {
348
0
      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
349
0
      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
350
351
0
      if (!_.IsFloatScalarType(result_type, 32)) {
352
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
353
0
               << "expected Result Type to be 32-bit floating point "
354
0
                  "scalar type";
355
0
      }
356
0
      break;
357
0
    }
358
359
0
    case spv::Op::OpRayQueryIsSphereHitNV:
360
0
    case spv::Op::OpRayQueryIsLSSHitNV: {
361
0
      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
362
0
      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
363
364
0
      if (!_.IsBoolScalarType(result_type)) {
365
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
366
0
               << "expected Result Type to be Boolean "
367
0
                  "scalar type";
368
0
      }
369
370
0
      break;
371
0
    }
372
373
    // SPV_KHR_ray_tracing_position_fetch
374
0
    case spv::Op::OpRayQueryGetIntersectionTriangleVertexPositionsKHR: {
375
0
      if (auto error = ValidateRayQueryPointer(_, inst, 2)) return error;
376
0
      if (auto error = ValidateIntersectionId(_, inst, 3)) return error;
377
378
0
      auto result_id = _.FindDef(result_type);
379
0
      if ((result_id->opcode() != spv::Op::OpTypeArray) ||
380
0
          (GetArrayLength(_, result_id) != 3) ||
381
0
          !_.IsFloatVectorType(_.GetComponentType(result_type)) ||
382
0
          _.GetDimension(_.GetComponentType(result_type)) != 3 ||
383
0
          _.GetBitWidth(_.GetComponentType(result_type)) != 32) {
384
0
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
385
0
               << "Expected 3 element array of 32-bit 3 component float point "
386
0
                  "vector as Result Type: "
387
0
               << spvOpcodeString(opcode);
388
0
      }
389
390
0
      break;
391
0
    }
392
14.1M
    default:
393
14.1M
      break;
394
14.1M
  }
395
396
14.1M
  return SPV_SUCCESS;
397
14.1M
}
398
399
}  // namespace val
400
}  // namespace spvtools