blob: efd8f99f4df488d29b283679993adc60719b2f7e [file] [log] [blame]
Robert Konrad45270f62016-08-14 16:21:43 +02001/*
Hans-Kristian Arntzen47044822021-01-14 16:07:49 +01002 * Copyright 2016-2021 Robert Konrad
Jon Leechf2a65542021-05-08 01:47:48 -07003 * SPDX-License-Identifier: Apache-2.0 OR MIT
Robert Konrad41cd8522017-01-26 15:33:49 +01004 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
Hans-Kristian Arntzencf1e9e02020-11-25 15:22:08 +010016 *
17 */
18
19/*
20 * At your option, you may choose to accept this material under either:
21 * 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
22 * 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
Robert Konrad41cd8522017-01-26 15:33:49 +010023 */
Robert Konrad45270f62016-08-14 16:21:43 +020024
25#include "spirv_hlsl.hpp"
Robert Konrad8e9bf1f2016-08-18 12:40:35 +020026#include "GLSL.std.450.h"
Robert Konrada89686822016-08-15 20:33:10 +020027#include <algorithm>
Hans-Kristian Arntzen4375aa32017-08-15 10:12:08 +020028#include <assert.h>
Robert Konrad45270f62016-08-14 16:21:43 +020029
30using namespace spv;
Hans-Kristian Arntzen9b92e682019-03-29 10:29:44 +010031using namespace SPIRV_CROSS_NAMESPACE;
Robert Konrad45270f62016-08-14 16:21:43 +020032using namespace std;
33
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +020034enum class ImageFormatNormalizedState
35{
36 None = 0,
37 Unorm = 1,
38 Snorm = 2
39};
40
41static ImageFormatNormalizedState image_format_to_normalized_state(ImageFormat fmt)
42{
43 switch (fmt)
44 {
45 case ImageFormatR8:
46 case ImageFormatR16:
47 case ImageFormatRg8:
48 case ImageFormatRg16:
49 case ImageFormatRgba8:
50 case ImageFormatRgba16:
51 case ImageFormatRgb10A2:
52 return ImageFormatNormalizedState::Unorm;
53
54 case ImageFormatR8Snorm:
55 case ImageFormatR16Snorm:
56 case ImageFormatRg8Snorm:
57 case ImageFormatRg16Snorm:
58 case ImageFormatRgba8Snorm:
59 case ImageFormatRgba16Snorm:
60 return ImageFormatNormalizedState::Snorm;
61
62 default:
63 break;
64 }
65
66 return ImageFormatNormalizedState::None;
67}
68
Hans-Kristian Arntzena95295c2017-10-24 09:52:12 +020069static unsigned image_format_to_components(ImageFormat fmt)
70{
71 switch (fmt)
72 {
73 case ImageFormatR8:
74 case ImageFormatR16:
75 case ImageFormatR8Snorm:
76 case ImageFormatR16Snorm:
77 case ImageFormatR16f:
78 case ImageFormatR32f:
79 case ImageFormatR8i:
80 case ImageFormatR16i:
81 case ImageFormatR32i:
82 case ImageFormatR8ui:
83 case ImageFormatR16ui:
84 case ImageFormatR32ui:
85 return 1;
86
87 case ImageFormatRg8:
88 case ImageFormatRg16:
89 case ImageFormatRg8Snorm:
90 case ImageFormatRg16Snorm:
91 case ImageFormatRg16f:
92 case ImageFormatRg32f:
93 case ImageFormatRg8i:
94 case ImageFormatRg16i:
95 case ImageFormatRg32i:
96 case ImageFormatRg8ui:
97 case ImageFormatRg16ui:
98 case ImageFormatRg32ui:
99 return 2;
100
101 case ImageFormatR11fG11fB10f:
102 return 3;
103
104 case ImageFormatRgba8:
105 case ImageFormatRgba16:
106 case ImageFormatRgb10A2:
107 case ImageFormatRgba8Snorm:
108 case ImageFormatRgba16Snorm:
109 case ImageFormatRgba16f:
110 case ImageFormatRgba32f:
111 case ImageFormatRgba8i:
112 case ImageFormatRgba16i:
113 case ImageFormatRgba32i:
114 case ImageFormatRgba8ui:
115 case ImageFormatRgba16ui:
116 case ImageFormatRgba32ui:
117 case ImageFormatRgb10a2ui:
118 return 4;
119
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100120 case ImageFormatUnknown:
121 return 4; // Assume 4.
122
Hans-Kristian Arntzena95295c2017-10-24 09:52:12 +0200123 default:
124 SPIRV_CROSS_THROW("Unrecognized typed image format.");
125 }
126}
127
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100128static string image_format_to_type(ImageFormat fmt, SPIRType::BaseType basetype)
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200129{
130 switch (fmt)
131 {
132 case ImageFormatR8:
133 case ImageFormatR16:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100134 if (basetype != SPIRType::Float)
135 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200136 return "unorm float";
137 case ImageFormatRg8:
138 case ImageFormatRg16:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100139 if (basetype != SPIRType::Float)
140 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200141 return "unorm float2";
142 case ImageFormatRgba8:
143 case ImageFormatRgba16:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100144 if (basetype != SPIRType::Float)
145 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200146 return "unorm float4";
147 case ImageFormatRgb10A2:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100148 if (basetype != SPIRType::Float)
149 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200150 return "unorm float4";
151
152 case ImageFormatR8Snorm:
153 case ImageFormatR16Snorm:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100154 if (basetype != SPIRType::Float)
155 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200156 return "snorm float";
157 case ImageFormatRg8Snorm:
158 case ImageFormatRg16Snorm:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100159 if (basetype != SPIRType::Float)
160 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200161 return "snorm float2";
162 case ImageFormatRgba8Snorm:
163 case ImageFormatRgba16Snorm:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100164 if (basetype != SPIRType::Float)
165 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200166 return "snorm float4";
167
168 case ImageFormatR16f:
169 case ImageFormatR32f:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100170 if (basetype != SPIRType::Float)
171 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200172 return "float";
173 case ImageFormatRg16f:
174 case ImageFormatRg32f:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100175 if (basetype != SPIRType::Float)
176 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200177 return "float2";
178 case ImageFormatRgba16f:
179 case ImageFormatRgba32f:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100180 if (basetype != SPIRType::Float)
181 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200182 return "float4";
183
184 case ImageFormatR11fG11fB10f:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100185 if (basetype != SPIRType::Float)
186 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200187 return "float3";
188
189 case ImageFormatR8i:
190 case ImageFormatR16i:
191 case ImageFormatR32i:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100192 if (basetype != SPIRType::Int)
193 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200194 return "int";
195 case ImageFormatRg8i:
196 case ImageFormatRg16i:
197 case ImageFormatRg32i:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100198 if (basetype != SPIRType::Int)
199 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200200 return "int2";
201 case ImageFormatRgba8i:
202 case ImageFormatRgba16i:
203 case ImageFormatRgba32i:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100204 if (basetype != SPIRType::Int)
205 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200206 return "int4";
207
208 case ImageFormatR8ui:
209 case ImageFormatR16ui:
210 case ImageFormatR32ui:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100211 if (basetype != SPIRType::UInt)
212 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200213 return "uint";
214 case ImageFormatRg8ui:
215 case ImageFormatRg16ui:
216 case ImageFormatRg32ui:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100217 if (basetype != SPIRType::UInt)
218 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200219 return "uint2";
220 case ImageFormatRgba8ui:
221 case ImageFormatRgba16ui:
222 case ImageFormatRgba32ui:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100223 if (basetype != SPIRType::UInt)
224 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200225 return "uint4";
226 case ImageFormatRgb10a2ui:
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100227 if (basetype != SPIRType::UInt)
228 SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
229 return "uint4";
230
231 case ImageFormatUnknown:
232 switch (basetype)
233 {
234 case SPIRType::Float:
235 return "float4";
236 case SPIRType::Int:
237 return "int4";
238 case SPIRType::UInt:
239 return "uint4";
240 default:
241 SPIRV_CROSS_THROW("Unsupported base type for image.");
242 }
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200243
244 default:
245 SPIRV_CROSS_THROW("Unrecognized typed image format.");
246 }
247}
248
Chip Davis2eff4202019-08-04 00:07:20 -0500249string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type, uint32_t id)
Hans-Kristian Arntzen59ad0842017-05-07 12:53:51 +0200250{
251 auto &imagetype = get<SPIRType>(type.image.type);
252 const char *dim = nullptr;
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200253 bool typed_load = false;
Hans-Kristian Arntzene8d5d712017-08-21 10:01:03 +0200254 uint32_t components = 4;
255
Hans-Kristian Arntzen28bf9052020-04-03 11:21:41 +0200256 bool force_image_srv = hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(id, DecorationNonWritable);
257
Hans-Kristian Arntzen59ad0842017-05-07 12:53:51 +0200258 switch (type.image.dim)
259 {
260 case Dim1D:
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200261 typed_load = type.image.sampled == 2;
Hans-Kristian Arntzen59ad0842017-05-07 12:53:51 +0200262 dim = "1D";
263 break;
264 case Dim2D:
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200265 typed_load = type.image.sampled == 2;
Hans-Kristian Arntzen59ad0842017-05-07 12:53:51 +0200266 dim = "2D";
267 break;
268 case Dim3D:
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200269 typed_load = type.image.sampled == 2;
Hans-Kristian Arntzen59ad0842017-05-07 12:53:51 +0200270 dim = "3D";
271 break;
272 case DimCube:
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +0200273 if (type.image.sampled == 2)
274 SPIRV_CROSS_THROW("RWTextureCube does not exist in HLSL.");
Hans-Kristian Arntzen59ad0842017-05-07 12:53:51 +0200275 dim = "Cube";
276 break;
277 case DimRect:
Hans-Kristian Arntzene8d5d712017-08-21 10:01:03 +0200278 SPIRV_CROSS_THROW("Rectangle texture support is not yet implemented for HLSL."); // TODO
Hans-Kristian Arntzen59ad0842017-05-07 12:53:51 +0200279 case DimBuffer:
Hans-Kristian Arntzene8d5d712017-08-21 10:01:03 +0200280 if (type.image.sampled == 1)
281 return join("Buffer<", type_to_glsl(imagetype), components, ">");
282 else if (type.image.sampled == 2)
Chip Davis2eff4202019-08-04 00:07:20 -0500283 {
284 if (interlocked_resources.count(id))
285 return join("RasterizerOrderedBuffer<", image_format_to_type(type.image.format, imagetype.basetype),
286 ">");
Hans-Kristian Arntzen28bf9052020-04-03 11:21:41 +0200287
288 typed_load = !force_image_srv && type.image.sampled == 2;
289
290 const char *rw = force_image_srv ? "" : "RW";
291 return join(rw, "Buffer<",
292 typed_load ? image_format_to_type(type.image.format, imagetype.basetype) :
293 join(type_to_glsl(imagetype), components),
294 ">");
Chip Davis2eff4202019-08-04 00:07:20 -0500295 }
Hans-Kristian Arntzene8d5d712017-08-21 10:01:03 +0200296 else
297 SPIRV_CROSS_THROW("Sampler buffers must be either sampled or unsampled. Cannot deduce in runtime.");
Hans-Kristian Arntzen59ad0842017-05-07 12:53:51 +0200298 case DimSubpassData:
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +0100299 dim = "2D";
300 typed_load = false;
301 break;
Hans-Kristian Arntzen08b3c672017-05-09 09:30:30 +0200302 default:
303 SPIRV_CROSS_THROW("Invalid dimension.");
Hans-Kristian Arntzen59ad0842017-05-07 12:53:51 +0200304 }
Hans-Kristian Arntzen59ad0842017-05-07 12:53:51 +0200305 const char *arrayed = type.image.arrayed ? "Array" : "";
Hans-Kristian Arntzen9aa42a82017-09-20 10:31:05 +0200306 const char *ms = type.image.ms ? "MS" : "";
Hans-Kristian Arntzen28bf9052020-04-03 11:21:41 +0200307 const char *rw = typed_load && !force_image_srv ? "RW" : "";
308
309 if (force_image_srv)
310 typed_load = false;
311
Chip Davis2eff4202019-08-04 00:07:20 -0500312 if (typed_load && interlocked_resources.count(id))
313 rw = "RasterizerOrdered";
Hans-Kristian Arntzen28bf9052020-04-03 11:21:41 +0200314
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +0200315 return join(rw, "Texture", dim, ms, arrayed, "<",
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +0100316 typed_load ? image_format_to_type(type.image.format, imagetype.basetype) :
317 join(type_to_glsl(imagetype), components),
318 ">");
Hans-Kristian Arntzen59ad0842017-05-07 12:53:51 +0200319}
320
rdb18893ba2020-10-31 12:06:15 +0100321string CompilerHLSL::image_type_hlsl_legacy(const SPIRType &type, uint32_t /*id*/)
Robert Konrad84466312017-04-21 14:54:03 +0200322{
323 auto &imagetype = get<SPIRType>(type.image.type);
324 string res;
325
326 switch (imagetype.basetype)
327 {
328 case SPIRType::Int:
329 res = "i";
330 break;
331 case SPIRType::UInt:
332 res = "u";
333 break;
334 default:
335 break;
336 }
337
338 if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData)
339 return res + "subpassInput" + (type.image.ms ? "MS" : "");
340
341 // If we're emulating subpassInput with samplers, force sampler2D
342 // so we don't have to specify format.
343 if (type.basetype == SPIRType::Image && type.image.dim != DimSubpassData)
344 {
345 // Sampler buffers are always declared as samplerBuffer even though they might be separate images in the SPIR-V.
346 if (type.image.dim == DimBuffer && type.image.sampled == 1)
347 res += "sampler";
348 else
349 res += type.image.sampled == 2 ? "image" : "texture";
350 }
351 else
352 res += "sampler";
353
354 switch (type.image.dim)
355 {
356 case Dim1D:
357 res += "1D";
358 break;
359 case Dim2D:
360 res += "2D";
361 break;
362 case Dim3D:
363 res += "3D";
364 break;
365 case DimCube:
366 res += "CUBE";
367 break;
368
369 case DimBuffer:
370 res += "Buffer";
371 break;
372
373 case DimSubpassData:
374 res += "2D";
375 break;
376 default:
377 SPIRV_CROSS_THROW("Only 1D, 2D, 3D, Buffer, InputTarget and Cube textures supported.");
378 }
379
380 if (type.image.ms)
381 res += "MS";
382 if (type.image.arrayed)
Robert Konrad84466312017-04-21 14:54:03 +0200383 res += "Array";
Robert Konrad84466312017-04-21 14:54:03 +0200384
385 return res;
386}
387
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +0200388string CompilerHLSL::image_type_hlsl(const SPIRType &type, uint32_t id)
Hans-Kristian Arntzen59ad0842017-05-07 12:53:51 +0200389{
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +0100390 if (hlsl_options.shader_model <= 30)
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +0200391 return image_type_hlsl_legacy(type, id);
Hans-Kristian Arntzen59ad0842017-05-07 12:53:51 +0200392 else
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +0200393 return image_type_hlsl_modern(type, id);
Hans-Kristian Arntzen59ad0842017-05-07 12:53:51 +0200394}
395
Bill Hollingsb41e1482017-05-29 20:45:05 -0400396// The optional id parameter indicates the object whose type we are trying
397// to find the description for. It is optional. Most type descriptions do not
398// depend on a specific object's use of that type.
Hans-Kristian Arntzen978901f2017-06-17 10:54:59 +0200399string CompilerHLSL::type_to_glsl(const SPIRType &type, uint32_t id)
Robert Konrad096d46f2016-08-14 20:28:52 +0200400{
401 // Ignore the pointer type since GLSL doesn't have pointers.
402
403 switch (type.basetype)
404 {
405 case SPIRType::Struct:
406 // Need OpName lookup here to get a "sensible" name for a struct.
407 if (backend.explicit_struct_type)
408 return join("struct ", to_name(type.self));
409 else
410 return to_name(type.self);
411
412 case SPIRType::Image:
413 case SPIRType::SampledImage:
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +0200414 return image_type_hlsl(type, id);
Robert Konrad096d46f2016-08-14 20:28:52 +0200415
416 case SPIRType::Sampler:
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +0200417 return comparison_ids.count(id) ? "SamplerComparisonState" : "SamplerState";
Robert Konrad096d46f2016-08-14 20:28:52 +0200418
419 case SPIRType::Void:
420 return "void";
421
422 default:
423 break;
424 }
425
426 if (type.vecsize == 1 && type.columns == 1) // Scalar builtin
427 {
428 switch (type.basetype)
429 {
430 case SPIRType::Boolean:
431 return "bool";
432 case SPIRType::Int:
433 return backend.basic_int_type;
434 case SPIRType::UInt:
435 return backend.basic_uint_type;
436 case SPIRType::AtomicCounter:
437 return "atomic_uint";
Hans-Kristian Arntzen47d94ff2018-03-07 10:21:25 +0100438 case SPIRType::Half:
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +0200439 if (hlsl_options.enable_16bit_types)
440 return "half";
441 else
442 return "min16float";
443 case SPIRType::Short:
444 if (hlsl_options.enable_16bit_types)
445 return "int16_t";
446 else
447 return "min16int";
448 case SPIRType::UShort:
449 if (hlsl_options.enable_16bit_types)
450 return "uint16_t";
451 else
452 return "min16uint";
Robert Konrad096d46f2016-08-14 20:28:52 +0200453 case SPIRType::Float:
454 return "float";
455 case SPIRType::Double:
456 return "double";
457 case SPIRType::Int64:
Hans-Kristian Arntzene4e47912020-04-21 11:47:45 +0200458 if (hlsl_options.shader_model < 60)
459 SPIRV_CROSS_THROW("64-bit integers only supported in SM 6.0.");
Robert Konrad096d46f2016-08-14 20:28:52 +0200460 return "int64_t";
461 case SPIRType::UInt64:
Hans-Kristian Arntzene4e47912020-04-21 11:47:45 +0200462 if (hlsl_options.shader_model < 60)
463 SPIRV_CROSS_THROW("64-bit integers only supported in SM 6.0.");
Robert Konrad096d46f2016-08-14 20:28:52 +0200464 return "uint64_t";
465 default:
466 return "???";
467 }
468 }
469 else if (type.vecsize > 1 && type.columns == 1) // Vector builtin
470 {
471 switch (type.basetype)
472 {
473 case SPIRType::Boolean:
Robert Konradf7eecd72017-01-27 17:02:59 +0100474 return join("bool", type.vecsize);
Robert Konrad096d46f2016-08-14 20:28:52 +0200475 case SPIRType::Int:
Robert Konradf7eecd72017-01-27 17:02:59 +0100476 return join("int", type.vecsize);
Robert Konrad096d46f2016-08-14 20:28:52 +0200477 case SPIRType::UInt:
Robert Konradf7eecd72017-01-27 17:02:59 +0100478 return join("uint", type.vecsize);
Hans-Kristian Arntzen47d94ff2018-03-07 10:21:25 +0100479 case SPIRType::Half:
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +0200480 return join(hlsl_options.enable_16bit_types ? "half" : "min16float", type.vecsize);
481 case SPIRType::Short:
482 return join(hlsl_options.enable_16bit_types ? "int16_t" : "min16int", type.vecsize);
483 case SPIRType::UShort:
484 return join(hlsl_options.enable_16bit_types ? "uint16_t" : "min16uint", type.vecsize);
Robert Konrad096d46f2016-08-14 20:28:52 +0200485 case SPIRType::Float:
486 return join("float", type.vecsize);
487 case SPIRType::Double:
488 return join("double", type.vecsize);
489 case SPIRType::Int64:
490 return join("i64vec", type.vecsize);
491 case SPIRType::UInt64:
492 return join("u64vec", type.vecsize);
493 default:
494 return "???";
495 }
496 }
Robert Konrad096d46f2016-08-14 20:28:52 +0200497 else
498 {
499 switch (type.basetype)
500 {
501 case SPIRType::Boolean:
Robert Konradda5f99b2016-08-14 23:54:51 +0200502 return join("bool", type.columns, "x", type.vecsize);
Robert Konrad096d46f2016-08-14 20:28:52 +0200503 case SPIRType::Int:
Robert Konradda5f99b2016-08-14 23:54:51 +0200504 return join("int", type.columns, "x", type.vecsize);
Robert Konrad096d46f2016-08-14 20:28:52 +0200505 case SPIRType::UInt:
Robert Konradda5f99b2016-08-14 23:54:51 +0200506 return join("uint", type.columns, "x", type.vecsize);
Hans-Kristian Arntzen47d94ff2018-03-07 10:21:25 +0100507 case SPIRType::Half:
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +0200508 return join(hlsl_options.enable_16bit_types ? "half" : "min16float", type.columns, "x", type.vecsize);
509 case SPIRType::Short:
510 return join(hlsl_options.enable_16bit_types ? "int16_t" : "min16int", type.columns, "x", type.vecsize);
511 case SPIRType::UShort:
512 return join(hlsl_options.enable_16bit_types ? "uint16_t" : "min16uint", type.columns, "x", type.vecsize);
Robert Konrad096d46f2016-08-14 20:28:52 +0200513 case SPIRType::Float:
Robert Konradda5f99b2016-08-14 23:54:51 +0200514 return join("float", type.columns, "x", type.vecsize);
Robert Konrad096d46f2016-08-14 20:28:52 +0200515 case SPIRType::Double:
Robert Konradda5f99b2016-08-14 23:54:51 +0200516 return join("double", type.columns, "x", type.vecsize);
Robert Konrade9cd04e2016-08-14 22:02:38 +0200517 // Matrix types not supported for int64/uint64.
Robert Konrad096d46f2016-08-14 20:28:52 +0200518 default:
519 return "???";
520 }
521 }
522}
523
Robert Konrad02fd8e92016-08-14 17:58:56 +0200524void CompilerHLSL::emit_header()
525{
Robert Konrad02fd8e92016-08-14 17:58:56 +0200526 for (auto &header : header_lines)
527 statement(header);
Robert Konrade9cd04e2016-08-14 22:02:38 +0200528
Robert Konrad1a48d7d2016-08-18 12:54:22 +0200529 if (header_lines.size() > 0)
530 {
531 statement("");
532 }
Robert Konrad02fd8e92016-08-14 17:58:56 +0200533}
534
Robert Konrad80fcf552016-08-14 21:33:32 +0200535void CompilerHLSL::emit_interface_block_globally(const SPIRVariable &var)
536{
Robert Konrad80fcf552016-08-14 21:33:32 +0200537 add_resource_name(var.self);
Hans-Kristian Arntzena0a582d2017-03-20 09:52:18 +0100538
539 // The global copies of I/O variables should not contain interpolation qualifiers.
540 // These are emitted inside the interface structs.
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +0200541 auto &flags = ir.meta[var.self].decoration.decoration_flags;
Hans-Kristian Arntzena0a582d2017-03-20 09:52:18 +0100542 auto old_flags = flags;
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +0100543 flags.reset();
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100544 statement("static ", variable_decl(var), ";");
Hans-Kristian Arntzena0a582d2017-03-20 09:52:18 +0100545 flags = old_flags;
Robert Konrad80fcf552016-08-14 21:33:32 +0200546}
547
Hans-Kristian Arntzenbcf23032017-02-24 11:15:34 +0100548const char *CompilerHLSL::to_storage_qualifiers_glsl(const SPIRVariable &var)
549{
550 // Input and output variables are handled specially in HLSL backend.
551 // The variables are declared as global, private variables, and do not need any qualifiers.
552 if (var.storage == StorageClassUniformConstant || var.storage == StorageClassUniform ||
553 var.storage == StorageClassPushConstant)
554 {
555 return "uniform ";
556 }
557
558 return "";
559}
560
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100561void CompilerHLSL::emit_builtin_outputs_in_struct()
562{
Hans-Kristian Arntzen4e5c8d72018-11-12 10:29:59 +0100563 auto &execution = get_entry_point();
564
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +0100565 bool legacy = hlsl_options.shader_model <= 30;
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +0100566 active_output_builtins.for_each_bit([&](uint32_t i) {
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100567 const char *type = nullptr;
568 const char *semantic = nullptr;
569 auto builtin = static_cast<BuiltIn>(i);
570 switch (builtin)
571 {
572 case BuiltInPosition:
Hans-Kristian Arntzenb8115ff2021-05-07 13:15:55 +0200573 type = is_position_invariant() && backend.support_precise_qualifier ? "precise float4" : "float4";
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100574 semantic = legacy ? "POSITION" : "SV_Position";
575 break;
576
Tomek Ponitkaba58f782020-07-23 19:09:43 +0200577 case BuiltInSampleMask:
578 if (hlsl_options.shader_model < 41 || execution.model != ExecutionModelFragment)
579 SPIRV_CROSS_THROW("Sample Mask output is only supported in PS 4.1 or higher.");
580 type = "uint";
581 semantic = "SV_Coverage";
582 break;
583
Hans-Kristian Arntzena4aa89e2017-03-07 13:51:33 +0100584 case BuiltInFragDepth:
585 type = "float";
Hans-Kristian Arntzen4e5c8d72018-11-12 10:29:59 +0100586 if (legacy)
587 {
588 semantic = "DEPTH";
589 }
590 else
591 {
592 if (hlsl_options.shader_model >= 50 && execution.flags.get(ExecutionModeDepthGreater))
593 semantic = "SV_DepthGreaterEqual";
594 else if (hlsl_options.shader_model >= 50 && execution.flags.get(ExecutionModeDepthLess))
595 semantic = "SV_DepthLessEqual";
596 else
597 semantic = "SV_Depth";
598 }
Hans-Kristian Arntzena4aa89e2017-03-07 13:51:33 +0100599 break;
600
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +0100601 case BuiltInClipDistance:
602 // HLSL is a bit weird here, use SV_ClipDistance0, SV_ClipDistance1 and so on with vectors.
603 for (uint32_t clip = 0; clip < clip_distance_count; clip += 4)
604 {
605 uint32_t to_declare = clip_distance_count - clip;
606 if (to_declare > 4)
607 to_declare = 4;
608
609 uint32_t semantic_index = clip / 4;
610
611 static const char *types[] = { "float", "float2", "float3", "float4" };
Hans-Kristian Arntzen4543dac2018-02-23 13:13:02 +0100612 statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassOutput), semantic_index,
613 " : SV_ClipDistance", semantic_index, ";");
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +0100614 }
615 break;
616
617 case BuiltInCullDistance:
618 // HLSL is a bit weird here, use SV_CullDistance0, SV_CullDistance1 and so on with vectors.
619 for (uint32_t cull = 0; cull < cull_distance_count; cull += 4)
620 {
621 uint32_t to_declare = cull_distance_count - cull;
622 if (to_declare > 4)
623 to_declare = 4;
624
625 uint32_t semantic_index = cull / 4;
626
627 static const char *types[] = { "float", "float2", "float3", "float4" };
Hans-Kristian Arntzen4543dac2018-02-23 13:13:02 +0100628 statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassOutput), semantic_index,
629 " : SV_CullDistance", semantic_index, ";");
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +0100630 }
631 break;
632
Hans-Kristian Arntzen17d88ca2017-05-04 10:10:30 +0200633 case BuiltInPointSize:
634 // If point_size_compat is enabled, just ignore PointSize.
635 // PointSize does not exist in HLSL, but some code bases might want to be able to use these shaders,
636 // even if it means working around the missing feature.
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +0100637 if (hlsl_options.point_size_compat)
Hans-Kristian Arntzen17d88ca2017-05-04 10:10:30 +0200638 break;
639 else
640 SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
641
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100642 default:
643 SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100644 }
645
646 if (type && semantic)
Hans-Kristian Arntzenc8d60912017-07-24 10:07:02 +0200647 statement(type, " ", builtin_to_glsl(builtin, StorageClassOutput), " : ", semantic, ";");
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +0100648 });
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100649}
650
651void CompilerHLSL::emit_builtin_inputs_in_struct()
652{
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +0100653 bool legacy = hlsl_options.shader_model <= 30;
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +0100654 active_input_builtins.for_each_bit([&](uint32_t i) {
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100655 const char *type = nullptr;
656 const char *semantic = nullptr;
657 auto builtin = static_cast<BuiltIn>(i);
658 switch (builtin)
659 {
660 case BuiltInFragCoord:
661 type = "float4";
Hans-Kristian Arntzena4aa89e2017-03-07 13:51:33 +0100662 semantic = legacy ? "VPOS" : "SV_Position";
663 break;
664
Hans-Kristian Arntzen5e9b53e2017-12-06 11:01:32 +0100665 case BuiltInVertexId:
Hans-Kristian Arntzena4aa89e2017-03-07 13:51:33 +0100666 case BuiltInVertexIndex:
667 if (legacy)
668 SPIRV_CROSS_THROW("Vertex index not supported in SM 3.0 or lower.");
669 type = "uint";
670 semantic = "SV_VertexID";
671 break;
672
Hans-Kristian Arntzen5e9b53e2017-12-06 11:01:32 +0100673 case BuiltInInstanceId:
Hans-Kristian Arntzena4aa89e2017-03-07 13:51:33 +0100674 case BuiltInInstanceIndex:
675 if (legacy)
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +0100676 SPIRV_CROSS_THROW("Instance index not supported in SM 3.0 or lower.");
Hans-Kristian Arntzena4aa89e2017-03-07 13:51:33 +0100677 type = "uint";
678 semantic = "SV_InstanceID";
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100679 break;
680
Robert Konrade7b02582017-03-24 13:58:39 +0100681 case BuiltInSampleId:
682 if (legacy)
683 SPIRV_CROSS_THROW("Sample ID not supported in SM 3.0 or lower.");
684 type = "uint";
685 semantic = "SV_SampleIndex";
686 break;
687
Tomek Ponitkaba58f782020-07-23 19:09:43 +0200688 case BuiltInSampleMask:
689 if (hlsl_options.shader_model < 50 || get_entry_point().model != ExecutionModelFragment)
690 SPIRV_CROSS_THROW("Sample Mask input is only supported in PS 5.0 or higher.");
691 type = "uint";
692 semantic = "SV_Coverage";
693 break;
694
Hans-Kristian Arntzene2bb5b82017-08-15 09:34:30 +0200695 case BuiltInGlobalInvocationId:
696 type = "uint3";
697 semantic = "SV_DispatchThreadID";
698 break;
699
700 case BuiltInLocalInvocationId:
701 type = "uint3";
702 semantic = "SV_GroupThreadID";
703 break;
704
705 case BuiltInLocalInvocationIndex:
706 type = "uint";
707 semantic = "SV_GroupIndex";
708 break;
709
710 case BuiltInWorkgroupId:
711 type = "uint3";
712 semantic = "SV_GroupID";
713 break;
714
Hans-Kristian Arntzen843e34b2018-02-15 12:42:56 +0100715 case BuiltInFrontFacing:
716 type = "bool";
717 semantic = "SV_IsFrontFace";
718 break;
719
Hans-Kristian Arntzen18e88332018-02-05 10:27:42 +0100720 case BuiltInNumWorkgroups:
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +0200721 case BuiltInSubgroupSize:
722 case BuiltInSubgroupLocalInvocationId:
723 case BuiltInSubgroupEqMask:
724 case BuiltInSubgroupLtMask:
725 case BuiltInSubgroupLeMask:
726 case BuiltInSubgroupGtMask:
727 case BuiltInSubgroupGeMask:
Hans-Kristian Arntzen18e88332018-02-05 10:27:42 +0100728 // Handled specially.
729 break;
730
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +0100731 case BuiltInClipDistance:
732 // HLSL is a bit weird here, use SV_ClipDistance0, SV_ClipDistance1 and so on with vectors.
733 for (uint32_t clip = 0; clip < clip_distance_count; clip += 4)
734 {
735 uint32_t to_declare = clip_distance_count - clip;
736 if (to_declare > 4)
737 to_declare = 4;
738
739 uint32_t semantic_index = clip / 4;
740
741 static const char *types[] = { "float", "float2", "float3", "float4" };
Hans-Kristian Arntzen4543dac2018-02-23 13:13:02 +0100742 statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassInput), semantic_index,
743 " : SV_ClipDistance", semantic_index, ";");
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +0100744 }
745 break;
746
747 case BuiltInCullDistance:
748 // HLSL is a bit weird here, use SV_CullDistance0, SV_CullDistance1 and so on with vectors.
749 for (uint32_t cull = 0; cull < cull_distance_count; cull += 4)
750 {
751 uint32_t to_declare = cull_distance_count - cull;
752 if (to_declare > 4)
753 to_declare = 4;
754
755 uint32_t semantic_index = cull / 4;
756
757 static const char *types[] = { "float", "float2", "float3", "float4" };
Hans-Kristian Arntzen4543dac2018-02-23 13:13:02 +0100758 statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassInput), semantic_index,
759 " : SV_CullDistance", semantic_index, ";");
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +0100760 }
761 break;
762
Hans-Kristian Arntzenf6d08e62018-02-23 15:56:25 +0100763 case BuiltInPointCoord:
764 // PointCoord is not supported, but provide a way to just ignore that, similar to PointSize.
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +0100765 if (hlsl_options.point_coord_compat)
Hans-Kristian Arntzenf6d08e62018-02-23 15:56:25 +0100766 break;
767 else
768 SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
769
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100770 default:
771 SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100772 }
773
774 if (type && semantic)
Hans-Kristian Arntzenc8d60912017-07-24 10:07:02 +0200775 statement(type, " ", builtin_to_glsl(builtin, StorageClassInput), " : ", semantic, ";");
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +0100776 });
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100777}
778
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100779uint32_t CompilerHLSL::type_to_consumed_locations(const SPIRType &type) const
780{
781 // TODO: Need to verify correctness.
782 uint32_t elements = 0;
783
784 if (type.basetype == SPIRType::Struct)
785 {
786 for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
787 elements += type_to_consumed_locations(get<SPIRType>(type.member_types[i]));
788 }
789 else
790 {
791 uint32_t array_multiplier = 1;
792 for (uint32_t i = 0; i < uint32_t(type.array.size()); i++)
793 {
794 if (type.array_size_literal[i])
795 array_multiplier *= type.array[i];
796 else
Hans-Kristian Arntzen66afe8c2020-09-14 10:42:31 +0200797 array_multiplier *= evaluate_constant_u32(type.array[i]);
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100798 }
799 elements += array_multiplier * type.columns;
800 }
801 return elements;
802}
803
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +0100804string CompilerHLSL::to_interpolation_qualifiers(const Bitset &flags)
Hans-Kristian Arntzenb8bda452017-03-20 09:48:43 +0100805{
806 string res;
807 //if (flags & (1ull << DecorationSmooth))
808 // res += "linear ";
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +0100809 if (flags.get(DecorationFlat))
Hans-Kristian Arntzenb8bda452017-03-20 09:48:43 +0100810 res += "nointerpolation ";
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +0100811 if (flags.get(DecorationNoPerspective))
Hans-Kristian Arntzenb8bda452017-03-20 09:48:43 +0100812 res += "noperspective ";
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +0100813 if (flags.get(DecorationCentroid))
Hans-Kristian Arntzenb8bda452017-03-20 09:48:43 +0100814 res += "centroid ";
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +0100815 if (flags.get(DecorationPatch))
Hans-Kristian Arntzenb8bda452017-03-20 09:48:43 +0100816 res += "patch "; // Seems to be different in actual HLSL.
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +0100817 if (flags.get(DecorationSample))
Hans-Kristian Arntzenb8bda452017-03-20 09:48:43 +0100818 res += "sample ";
Hans-Kristian Arntzenb8115ff2021-05-07 13:15:55 +0200819 if (flags.get(DecorationInvariant) && backend.support_precise_qualifier)
820 res += "precise "; // Not supported?
Hans-Kristian Arntzenb8bda452017-03-20 09:48:43 +0100821
822 return res;
823}
824
Ashley Harriscc2d2902019-04-12 13:54:58 +0930825std::string CompilerHLSL::to_semantic(uint32_t location, ExecutionModel em, StorageClass sc)
Hans-Kristian Arntzen56716a92017-11-13 09:52:00 +0100826{
Ashley Harriscc2d2902019-04-12 13:54:58 +0930827 if (em == ExecutionModelVertex && sc == StorageClassInput)
828 {
829 // We have a vertex attribute - we should look at remapping it if the user provided
830 // vertex attribute hints.
831 for (auto &attribute : remap_vertex_attributes)
832 if (attribute.location == location)
833 return attribute.semantic;
834 }
Hans-Kristian Arntzen56716a92017-11-13 09:52:00 +0100835
Ashley Harriscc2d2902019-04-12 13:54:58 +0930836 // Not a vertex attribute, or no remap_vertex_attributes entry.
837 return join("TEXCOORD", location);
Hans-Kristian Arntzen56716a92017-11-13 09:52:00 +0100838}
839
Hans-Kristian Arntzen9a304fe2021-01-04 11:16:58 +0100840std::string CompilerHLSL::to_initializer_expression(const SPIRVariable &var)
841{
842 // We cannot emit static const initializer for block constants for practical reasons,
843 // so just inline the initializer.
844 // FIXME: There is a theoretical problem here if someone tries to composite extract
845 // into this initializer since we don't declare it properly, but that is somewhat non-sensical.
846 auto &type = get<SPIRType>(var.basetype);
847 bool is_block = has_decoration(type.self, DecorationBlock);
848 auto *c = maybe_get<SPIRConstant>(var.initializer);
849 if (is_block && c)
850 return constant_expression(*c);
851 else
852 return CompilerGLSL::to_initializer_expression(var);
853}
854
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +0200855void CompilerHLSL::emit_interface_block_member_in_struct(const SPIRVariable &var, uint32_t member_index,
856 uint32_t location,
857 std::unordered_set<uint32_t> &active_locations)
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +0100858{
Ashley Harriscc2d2902019-04-12 13:54:58 +0930859 auto &execution = get_entry_point();
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +0200860 auto type = get<SPIRType>(var.basetype);
861 auto semantic = to_semantic(location, execution.model, var.storage);
862 auto mbr_name = join(to_name(type.self), "_", to_member_name(type, member_index));
863 auto &mbr_type = get<SPIRType>(type.member_types[member_index]);
Ashley Harriscc2d2902019-04-12 13:54:58 +0930864
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +0200865 statement(to_interpolation_qualifiers(get_member_decoration_bitset(type.self, member_index)),
866 type_to_glsl(mbr_type),
867 " ", mbr_name, type_to_array_glsl(mbr_type),
868 " : ", semantic, ";");
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +0100869
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +0200870 // Structs and arrays should consume more locations.
871 uint32_t consumed_locations = type_to_consumed_locations(mbr_type);
872 for (uint32_t i = 0; i < consumed_locations; i++)
873 active_locations.insert(location + i);
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +0100874}
875
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100876void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unordered_set<uint32_t> &active_locations)
Robert Konrad02fd8e92016-08-14 17:58:56 +0200877{
878 auto &execution = get_entry_point();
crosire0cdfbe42018-09-11 20:29:24 +0200879 auto type = get<SPIRType>(var.basetype);
Robert Konrad02fd8e92016-08-14 17:58:56 +0200880
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100881 string binding;
Hans-Kristian Arntzen4f88f972017-11-13 09:46:45 +0100882 bool use_location_number = true;
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +0100883 bool legacy = hlsl_options.shader_model <= 30;
Robert Konradd2b29c92016-08-14 23:09:06 +0200884 if (execution.model == ExecutionModelFragment && var.storage == StorageClassOutput)
Robert Konrad02fd8e92016-08-14 17:58:56 +0200885 {
Hans-Kristian Arntzena365ff12019-01-11 10:03:45 +0100886 // Dual-source blending is achieved in HLSL by emitting to SV_Target0 and 1.
887 uint32_t index = get_decoration(var.self, DecorationIndex);
888 uint32_t location = get_decoration(var.self, DecorationLocation);
889
890 if (index != 0 && location != 0)
891 SPIRV_CROSS_THROW("Dual-source blending is only supported on MRT #0 in HLSL.");
892
893 binding = join(legacy ? "COLOR" : "SV_Target", location + index);
Hans-Kristian Arntzen4f88f972017-11-13 09:46:45 +0100894 use_location_number = false;
crosire0cdfbe42018-09-11 20:29:24 +0200895 if (legacy) // COLOR must be a four-component vector on legacy shader model targets (HLSL ERR_COLOR_4COMP)
896 type.vecsize = 4;
Robert Konradda5f99b2016-08-14 23:54:51 +0200897 }
Robert Konrade9cd04e2016-08-14 22:02:38 +0200898
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100899 const auto get_vacant_location = [&]() -> uint32_t {
900 for (uint32_t i = 0; i < 64; i++)
901 if (!active_locations.count(i))
902 return i;
903 SPIRV_CROSS_THROW("All locations from 0 to 63 are exhausted.");
904 };
905
Hans-Kristian Arntzeneb58f672017-10-05 16:31:52 +0200906 bool need_matrix_unroll = var.storage == StorageClassInput && execution.model == ExecutionModelVertex;
907
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100908 auto name = to_name(var.self);
Hans-Kristian Arntzen4f88f972017-11-13 09:46:45 +0100909 if (use_location_number)
Robert Konradd2b29c92016-08-14 23:09:06 +0200910 {
Hans-Kristian Arntzen4f88f972017-11-13 09:46:45 +0100911 uint32_t location_number;
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100912
913 // If an explicit location exists, use it with TEXCOORD[N] semantic.
914 // Otherwise, pick a vacant location.
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +0200915 if (has_decoration(var.self, DecorationLocation))
916 location_number = get_decoration(var.self, DecorationLocation);
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100917 else
Hans-Kristian Arntzen4f88f972017-11-13 09:46:45 +0100918 location_number = get_vacant_location();
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100919
Amer Koleci7216d132017-11-06 20:10:04 +0100920 // Allow semantic remap if specified.
Ashley Harriscc2d2902019-04-12 13:54:58 +0930921 auto semantic = to_semantic(location_number, execution.model, var.storage);
Hans-Kristian Arntzen4f88f972017-11-13 09:46:45 +0100922
Hans-Kristian Arntzeneb58f672017-10-05 16:31:52 +0200923 if (need_matrix_unroll && type.columns > 1)
Robert Konrad4a0267e2016-09-23 23:56:37 +0200924 {
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100925 if (!type.array.empty())
926 SPIRV_CROSS_THROW("Arrays of matrices used as input/output. This is not supported.");
927
928 // Unroll matrices.
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100929 for (uint32_t i = 0; i < type.columns; i++)
Robert Konradffc590e2016-09-24 19:31:05 +0200930 {
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100931 SPIRType newtype = type;
932 newtype.columns = 1;
Hans-Kristian Arntzenb3344172020-11-03 11:18:32 +0100933
934 string effective_semantic;
935 if (hlsl_options.flatten_matrix_vertex_input_semantics)
936 effective_semantic = to_semantic(location_number, execution.model, var.storage);
937 else
938 effective_semantic = join(semantic, "_", i);
939
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +0100940 statement(to_interpolation_qualifiers(get_decoration_bitset(var.self)),
Hans-Kristian Arntzenb3344172020-11-03 11:18:32 +0100941 variable_decl(newtype, join(name, "_", i)), " : ", effective_semantic, ";");
Hans-Kristian Arntzen4f88f972017-11-13 09:46:45 +0100942 active_locations.insert(location_number++);
Robert Konradffc590e2016-09-24 19:31:05 +0200943 }
Robert Konrad4a0267e2016-09-23 23:56:37 +0200944 }
945 else
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100946 {
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +0100947 statement(to_interpolation_qualifiers(get_decoration_bitset(var.self)), variable_decl(type, name), " : ",
Hans-Kristian Arntzenf4861422017-11-13 09:52:35 +0100948 semantic, ";");
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100949
950 // Structs and arrays should consume more locations.
951 uint32_t consumed_locations = type_to_consumed_locations(type);
952 for (uint32_t i = 0; i < consumed_locations; i++)
Hans-Kristian Arntzen4f88f972017-11-13 09:46:45 +0100953 active_locations.insert(location_number + i);
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100954 }
Robert Konradd2b29c92016-08-14 23:09:06 +0200955 }
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100956 else
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +0100957 statement(variable_decl(type, name), " : ", binding, ";");
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100958}
959
Hans-Kristian Arntzen5e9b53e2017-12-06 11:01:32 +0100960std::string CompilerHLSL::builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage)
961{
962 switch (builtin)
963 {
964 case BuiltInVertexId:
965 return "gl_VertexID";
966 case BuiltInInstanceId:
967 return "gl_InstanceID";
Hans-Kristian Arntzen18e88332018-02-05 10:27:42 +0100968 case BuiltInNumWorkgroups:
969 {
970 if (!num_workgroups_builtin)
msiglreithd096f5c2017-11-27 16:00:56 +0100971 SPIRV_CROSS_THROW("NumWorkgroups builtin is used, but remap_num_workgroups_builtin() was not called. "
972 "Cannot emit code for this builtin.");
Hans-Kristian Arntzen18e88332018-02-05 10:27:42 +0100973
974 auto &var = get<SPIRVariable>(num_workgroups_builtin);
975 auto &type = get<SPIRType>(var.basetype);
Hans-Kristian Arntzena0744152020-08-21 16:14:13 +0200976 auto ret = join(to_name(num_workgroups_builtin), "_", get_member_name(type.self, 0));
977 ParsedIR::sanitize_underscores(ret);
978 return ret;
Hans-Kristian Arntzen18e88332018-02-05 10:27:42 +0100979 }
Hans-Kristian Arntzenf6d08e62018-02-23 15:56:25 +0100980 case BuiltInPointCoord:
981 // Crude hack, but there is no real alternative. This path is only enabled if point_coord_compat is set.
982 return "float2(0.5f, 0.5f)";
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +0200983 case BuiltInSubgroupLocalInvocationId:
984 return "WaveGetLaneIndex()";
985 case BuiltInSubgroupSize:
986 return "WaveGetLaneCount()";
987
Hans-Kristian Arntzen5e9b53e2017-12-06 11:01:32 +0100988 default:
989 return CompilerGLSL::builtin_to_glsl(builtin, storage);
990 }
991}
992
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100993void CompilerHLSL::emit_builtin_variables()
994{
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +0100995 Bitset builtins = active_input_builtins;
996 builtins.merge_or(active_output_builtins);
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +0100997
Hans-Kristian Arntzena2a44d92019-01-11 10:32:14 +0100998 bool need_base_vertex_info = false;
999
Hans-Kristian Arntzena1c784f2021-01-04 11:58:50 +01001000 std::unordered_map<uint32_t, ID> builtin_to_initializer;
1001 ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
1002 if (!is_builtin_variable(var) || var.storage != StorageClassOutput || !var.initializer)
1003 return;
1004
1005 auto *c = this->maybe_get<SPIRConstant>(var.initializer);
1006 if (!c)
1007 return;
1008
1009 auto &type = this->get<SPIRType>(var.basetype);
1010 if (type.basetype == SPIRType::Struct)
1011 {
1012 uint32_t member_count = uint32_t(type.member_types.size());
1013 for (uint32_t i = 0; i < member_count; i++)
1014 {
1015 if (has_member_decoration(type.self, i, DecorationBuiltIn))
1016 {
1017 builtin_to_initializer[get_member_decoration(type.self, i, DecorationBuiltIn)] =
1018 c->subconstants[i];
1019 }
1020 }
1021 }
1022 else if (has_decoration(var.self, DecorationBuiltIn))
1023 builtin_to_initializer[get_decoration(var.self, DecorationBuiltIn)] = var.initializer;
1024 });
1025
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01001026 // Emit global variables for the interface variables which are statically used by the shader.
1027 builtins.for_each_bit([&](uint32_t i) {
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01001028 const char *type = nullptr;
1029 auto builtin = static_cast<BuiltIn>(i);
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +01001030 uint32_t array_size = 0;
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01001031
Hans-Kristian Arntzena1c784f2021-01-04 11:58:50 +01001032 string init_expr;
1033 auto init_itr = builtin_to_initializer.find(builtin);
1034 if (init_itr != builtin_to_initializer.end())
1035 init_expr = join(" = ", to_expression(init_itr->second));
1036
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01001037 switch (builtin)
1038 {
1039 case BuiltInFragCoord:
1040 case BuiltInPosition:
1041 type = "float4";
1042 break;
1043
Hans-Kristian Arntzena4aa89e2017-03-07 13:51:33 +01001044 case BuiltInFragDepth:
1045 type = "float";
1046 break;
1047
Hans-Kristian Arntzen5e9b53e2017-12-06 11:01:32 +01001048 case BuiltInVertexId:
Hans-Kristian Arntzena4aa89e2017-03-07 13:51:33 +01001049 case BuiltInVertexIndex:
1050 case BuiltInInstanceIndex:
Hans-Kristian Arntzena2a44d92019-01-11 10:32:14 +01001051 type = "int";
1052 if (hlsl_options.support_nonzero_base_vertex_base_instance)
1053 need_base_vertex_info = true;
1054 break;
1055
1056 case BuiltInInstanceId:
Robert Konrade7b02582017-03-24 13:58:39 +01001057 case BuiltInSampleId:
Hans-Kristian Arntzena4aa89e2017-03-07 13:51:33 +01001058 type = "int";
1059 break;
1060
Hans-Kristian Arntzen17d88ca2017-05-04 10:10:30 +02001061 case BuiltInPointSize:
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01001062 if (hlsl_options.point_size_compat)
Hans-Kristian Arntzen17d88ca2017-05-04 10:10:30 +02001063 {
1064 // Just emit the global variable, it will be ignored.
1065 type = "float";
1066 break;
1067 }
1068 else
1069 SPIRV_CROSS_THROW(join("Unsupported builtin in HLSL: ", unsigned(builtin)));
1070
Hans-Kristian Arntzene2bb5b82017-08-15 09:34:30 +02001071 case BuiltInGlobalInvocationId:
1072 case BuiltInLocalInvocationId:
1073 case BuiltInWorkgroupId:
1074 type = "uint3";
1075 break;
1076
1077 case BuiltInLocalInvocationIndex:
1078 type = "uint";
1079 break;
1080
Hans-Kristian Arntzen843e34b2018-02-15 12:42:56 +01001081 case BuiltInFrontFacing:
1082 type = "bool";
1083 break;
1084
Hans-Kristian Arntzen18e88332018-02-05 10:27:42 +01001085 case BuiltInNumWorkgroups:
Hans-Kristian Arntzenf6d08e62018-02-23 15:56:25 +01001086 case BuiltInPointCoord:
Hans-Kristian Arntzen18e88332018-02-05 10:27:42 +01001087 // Handled specially.
1088 break;
1089
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02001090 case BuiltInSubgroupLocalInvocationId:
1091 case BuiltInSubgroupSize:
1092 if (hlsl_options.shader_model < 60)
1093 SPIRV_CROSS_THROW("Need SM 6.0 for Wave ops.");
1094 break;
1095
1096 case BuiltInSubgroupEqMask:
1097 case BuiltInSubgroupLtMask:
1098 case BuiltInSubgroupLeMask:
1099 case BuiltInSubgroupGtMask:
1100 case BuiltInSubgroupGeMask:
1101 if (hlsl_options.shader_model < 60)
1102 SPIRV_CROSS_THROW("Need SM 6.0 for Wave ops.");
1103 type = "uint4";
1104 break;
1105
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +01001106 case BuiltInClipDistance:
1107 array_size = clip_distance_count;
1108 type = "float";
1109 break;
1110
1111 case BuiltInCullDistance:
1112 array_size = cull_distance_count;
1113 type = "float";
1114 break;
1115
Tomek Ponitkaba58f782020-07-23 19:09:43 +02001116 case BuiltInSampleMask:
1117 type = "int";
1118 break;
1119
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01001120 default:
1121 SPIRV_CROSS_THROW(join("Unsupported builtin in HLSL: ", unsigned(builtin)));
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01001122 }
1123
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01001124 StorageClass storage = active_input_builtins.get(i) ? StorageClassInput : StorageClassOutput;
Hans-Kristian Arntzenc8d60912017-07-24 10:07:02 +02001125
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01001126 if (type)
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +01001127 {
1128 if (array_size)
Hans-Kristian Arntzena1c784f2021-01-04 11:58:50 +01001129 statement("static ", type, " ", builtin_to_glsl(builtin, storage), "[", array_size, "]", init_expr, ";");
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +01001130 else
Hans-Kristian Arntzena1c784f2021-01-04 11:58:50 +01001131 statement("static ", type, " ", builtin_to_glsl(builtin, storage), init_expr, ";");
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +01001132 }
Tomek Ponitkaba58f782020-07-23 19:09:43 +02001133
1134 // SampleMask can be both in and out with sample builtin, in this case we have already
1135 // declared the input variable and we need to add the output one now.
1136 if (builtin == BuiltInSampleMask && storage == StorageClassInput && this->active_output_builtins.get(i))
1137 {
Hans-Kristian Arntzena1c784f2021-01-04 11:58:50 +01001138 statement("static ", type, " ", this->builtin_to_glsl(builtin, StorageClassOutput), init_expr, ";");
Tomek Ponitkaba58f782020-07-23 19:09:43 +02001139 }
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01001140 });
Hans-Kristian Arntzena2a44d92019-01-11 10:32:14 +01001141
1142 if (need_base_vertex_info)
1143 {
1144 statement("cbuffer SPIRV_Cross_VertexInfo");
1145 begin_scope();
1146 statement("int SPIRV_Cross_BaseVertex;");
1147 statement("int SPIRV_Cross_BaseInstance;");
1148 end_scope_decl();
1149 statement("");
1150 }
Robert Konrad02fd8e92016-08-14 17:58:56 +02001151}
1152
Hans-Kristian Arntzen5d9df6a2018-02-02 10:10:17 +01001153void CompilerHLSL::emit_composite_constants()
1154{
1155 // HLSL cannot declare structs or arrays inline, so we must move them out to
1156 // global constants directly.
1157 bool emitted = false;
1158
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001159 ir.for_each_typed_id<SPIRConstant>([&](uint32_t, SPIRConstant &c) {
1160 if (c.specialization)
1161 return;
Hans-Kristian Arntzen5d9df6a2018-02-02 10:10:17 +01001162
Hans-Kristian Arntzen2fb9aa22019-01-11 09:29:28 +01001163 auto &type = this->get<SPIRType>(c.constant_type);
Hans-Kristian Arntzen9a304fe2021-01-04 11:16:58 +01001164
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001165 if (type.basetype == SPIRType::Struct && is_builtin_type(type))
1166 return;
1167
1168 if (type.basetype == SPIRType::Struct || !type.array.empty())
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001169 {
1170 auto name = to_name(c.self);
1171 statement("static const ", variable_decl(type, name), " = ", constant_expression(c), ";");
1172 emitted = true;
Hans-Kristian Arntzen5d9df6a2018-02-02 10:10:17 +01001173 }
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001174 });
Hans-Kristian Arntzen5d9df6a2018-02-02 10:10:17 +01001175
1176 if (emitted)
1177 statement("");
1178}
1179
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001180void CompilerHLSL::emit_specialization_constants_and_structs()
Hans-Kristian Arntzen98c76ee2017-07-29 22:12:19 +02001181{
1182 bool emitted = false;
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02001183 SpecializationConstant wg_x, wg_y, wg_z;
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02001184 ID workgroup_size_id = get_work_group_size_specialization_constants(wg_x, wg_y, wg_z);
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02001185
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001186 std::unordered_set<TypeID> io_block_types;
1187 ir.for_each_typed_id<SPIRVariable>([&](uint32_t, const SPIRVariable &var) {
1188 auto &type = this->get<SPIRType>(var.basetype);
1189 if ((var.storage == StorageClassInput || var.storage == StorageClassOutput) &&
1190 !var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
1191 interface_variable_exists_in_entry_point(var.self) &&
1192 has_decoration(type.self, DecorationBlock))
1193 {
1194 io_block_types.insert(type.self);
1195 }
1196 });
1197
Hans-Kristian Arntzendd7ebaf2019-07-18 17:05:28 +02001198 auto loop_lock = ir.create_loop_hard_lock();
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001199 for (auto &id_ : ir.ids_for_constant_or_type)
Hans-Kristian Arntzen98c76ee2017-07-29 22:12:19 +02001200 {
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001201 auto &id = ir.ids[id_];
1202
Hans-Kristian Arntzen98c76ee2017-07-29 22:12:19 +02001203 if (id.get_type() == TypeConstant)
1204 {
1205 auto &c = id.get<SPIRConstant>();
Hans-Kristian Arntzen62db5352018-11-01 11:22:14 +01001206
Hans-Kristian Arntzen84f8c992017-09-29 10:13:45 +02001207 if (c.self == workgroup_size_id)
Hans-Kristian Arntzen62db5352018-11-01 11:22:14 +01001208 {
1209 statement("static const uint3 gl_WorkGroupSize = ",
Hans-Kristian Arntzen6e99fcf2018-11-01 11:23:33 +01001210 constant_expression(get<SPIRConstant>(workgroup_size_id)), ";");
Hans-Kristian Arntzen62db5352018-11-01 11:22:14 +01001211 emitted = true;
1212 }
1213 else if (c.specialization)
1214 {
1215 auto &type = get<SPIRType>(c.constant_type);
1216 auto name = to_name(c.self);
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02001217
Hans-Kristian Arntzen7c83fc22022-01-05 15:53:51 +01001218 if (has_decoration(c.self, DecorationSpecId))
1219 {
1220 // HLSL does not support specialization constants, so fallback to macros.
1221 c.specialization_constant_macro_name =
1222 constant_value_macro_name(get_decoration(c.self, DecorationSpecId));
Hans-Kristian Arntzen98c76ee2017-07-29 22:12:19 +02001223
Hans-Kristian Arntzen7c83fc22022-01-05 15:53:51 +01001224 statement("#ifndef ", c.specialization_constant_macro_name);
1225 statement("#define ", c.specialization_constant_macro_name, " ", constant_expression(c));
1226 statement("#endif");
1227 statement("static const ", variable_decl(type, name), " = ", c.specialization_constant_macro_name, ";");
1228 }
1229 else
1230 statement("static const ", variable_decl(type, name), " = ", constant_expression(c), ";");
1231
Hans-Kristian Arntzen62db5352018-11-01 11:22:14 +01001232 emitted = true;
1233 }
Hans-Kristian Arntzen98c76ee2017-07-29 22:12:19 +02001234 }
Hans-Kristian Arntzen991b6552018-05-15 14:20:16 +02001235 else if (id.get_type() == TypeConstantOp)
1236 {
1237 auto &c = id.get<SPIRConstantOp>();
1238 auto &type = get<SPIRType>(c.basetype);
1239 auto name = to_name(c.self);
1240 statement("static const ", variable_decl(type, name), " = ", constant_op_expression(c), ";");
Hans-Kristian Arntzen00d542e2018-11-01 11:49:32 +01001241 emitted = true;
Hans-Kristian Arntzen991b6552018-05-15 14:20:16 +02001242 }
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001243 else if (id.get_type() == TypeType)
1244 {
1245 auto &type = id.get<SPIRType>();
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001246 bool is_non_io_block = has_decoration(type.self, DecorationBlock) &&
1247 io_block_types.count(type.self) == 0;
1248 bool is_buffer_block = has_decoration(type.self, DecorationBufferBlock);
1249 if (type.basetype == SPIRType::Struct && type.array.empty() &&
1250 !type.pointer && !is_non_io_block && !is_buffer_block)
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001251 {
1252 if (emitted)
1253 statement("");
1254 emitted = false;
1255
1256 emit_struct(type);
1257 }
1258 }
Hans-Kristian Arntzen98c76ee2017-07-29 22:12:19 +02001259 }
1260
1261 if (emitted)
1262 statement("");
1263}
1264
Hans-Kristian Arntzen0617b982018-05-15 11:16:35 +02001265void CompilerHLSL::replace_illegal_names()
1266{
Pascal Muetschardaced6052018-05-04 14:53:32 -07001267 static const unordered_set<string> keywords = {
1268 // Additional HLSL specific keywords.
Hans-Kristian Arntzen21a93162021-02-15 11:38:14 +01001269 "line", "linear", "matrix", "point", "row_major", "sampler", "vector"
Pascal Muetschardaced6052018-05-04 14:53:32 -07001270 };
1271
Hans-Kristian Arntzenf79c1e22020-01-16 10:28:54 +01001272 CompilerGLSL::replace_illegal_names(keywords);
Pascal Muetschardaced6052018-05-04 14:53:32 -07001273 CompilerGLSL::replace_illegal_names();
1274}
1275
Hans-Kristian Arntzene81c1b12020-02-08 13:39:50 +01001276void CompilerHLSL::declare_undefined_values()
1277{
1278 bool emitted = false;
1279 ir.for_each_typed_id<SPIRUndef>([&](uint32_t, const SPIRUndef &undef) {
Hans-Kristian Arntzen18d03b32020-09-04 09:29:44 +02001280 auto &type = this->get<SPIRType>(undef.basetype);
1281 // OpUndef can be void for some reason ...
1282 if (type.basetype == SPIRType::Void)
1283 return;
1284
Hans-Kristian Arntzenb8905bb2020-03-26 11:21:23 +01001285 string initializer;
Hans-Kristian Arntzen18d03b32020-09-04 09:29:44 +02001286 if (options.force_zero_initialized_variables && type_can_zero_initialize(type))
Hans-Kristian Arntzenb8905bb2020-03-26 11:21:23 +01001287 initializer = join(" = ", to_zero_initialized_expression(undef.basetype));
1288
Hans-Kristian Arntzen6fc2a052020-11-08 13:59:52 +01001289 statement("static ", variable_decl(type, to_name(undef.self), undef.self), initializer, ";");
Hans-Kristian Arntzene81c1b12020-02-08 13:39:50 +01001290 emitted = true;
1291 });
1292
1293 if (emitted)
1294 statement("");
1295}
1296
Robert Konrad02fd8e92016-08-14 17:58:56 +02001297void CompilerHLSL::emit_resources()
1298{
1299 auto &execution = get_entry_point();
Robert Konrade9cd04e2016-08-14 22:02:38 +02001300
Hans-Kristian Arntzen5d9df6a2018-02-02 10:10:17 +01001301 replace_illegal_names();
1302
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001303 emit_specialization_constants_and_structs();
Hans-Kristian Arntzen5d9df6a2018-02-02 10:10:17 +01001304 emit_composite_constants();
1305
Robert Konrad02fd8e92016-08-14 17:58:56 +02001306 bool emitted = false;
1307
Robert Konrad99469f02017-01-24 09:17:43 +01001308 // Output UBOs and SSBOs
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001309 ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
Hans-Kristian Arntzen2fb9aa22019-01-11 09:29:28 +01001310 auto &type = this->get<SPIRType>(var.basetype);
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001311
1312 bool is_block_storage = type.storage == StorageClassStorageBuffer || type.storage == StorageClassUniform;
1313 bool has_block_flags = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) ||
1314 ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock);
1315
1316 if (var.storage != StorageClassFunction && type.pointer && is_block_storage && !is_hidden_variable(var) &&
1317 has_block_flags)
Robert Konrad99469f02017-01-24 09:17:43 +01001318 {
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001319 emit_buffer_block(var);
1320 emitted = true;
Robert Konrad99469f02017-01-24 09:17:43 +01001321 }
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001322 });
Robert Konrad99469f02017-01-24 09:17:43 +01001323
1324 // Output push constant blocks
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001325 ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
Hans-Kristian Arntzen2fb9aa22019-01-11 09:29:28 +01001326 auto &type = this->get<SPIRType>(var.basetype);
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001327 if (var.storage != StorageClassFunction && type.pointer && type.storage == StorageClassPushConstant &&
1328 !is_hidden_variable(var))
Robert Konrad99469f02017-01-24 09:17:43 +01001329 {
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001330 emit_push_constant_block(var);
1331 emitted = true;
Robert Konrad99469f02017-01-24 09:17:43 +01001332 }
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001333 });
Robert Konrad99469f02017-01-24 09:17:43 +01001334
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01001335 if (execution.model == ExecutionModelVertex && hlsl_options.shader_model <= 30)
Robert Konrad95a716f2016-08-16 00:27:39 +02001336 {
1337 statement("uniform float4 gl_HalfPixel;");
1338 emitted = true;
1339 }
1340
Minmin Gonge3ebfda2018-11-10 13:33:15 -08001341 bool skip_separate_image_sampler = !combined_image_samplers.empty() || hlsl_options.shader_model <= 30;
1342
Robert Konrad02fd8e92016-08-14 17:58:56 +02001343 // Output Uniform Constants (values, samplers, images, etc).
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001344 ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
Hans-Kristian Arntzen2fb9aa22019-01-11 09:29:28 +01001345 auto &type = this->get<SPIRType>(var.basetype);
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001346
1347 // If we're remapping separate samplers and images, only emit the combined samplers.
1348 if (skip_separate_image_sampler)
Robert Konrad02fd8e92016-08-14 17:58:56 +02001349 {
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001350 // Sampler buffers are always used without a sampler, and they will also work in regular D3D.
1351 bool sampler_buffer = type.basetype == SPIRType::Image && type.image.dim == DimBuffer;
1352 bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1;
1353 bool separate_sampler = type.basetype == SPIRType::Sampler;
1354 if (!sampler_buffer && (separate_image || separate_sampler))
1355 return;
Robert Konrad02fd8e92016-08-14 17:58:56 +02001356 }
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001357
1358 if (var.storage != StorageClassFunction && !is_builtin_variable(var) && !var.remapped_variable &&
Hans-Kristian Arntzenea02a0c2021-01-22 13:48:16 +01001359 type.pointer && (type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter) &&
1360 !is_hidden_variable(var))
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001361 {
1362 emit_uniform(var);
1363 emitted = true;
1364 }
1365 });
Robert Konrad02fd8e92016-08-14 17:58:56 +02001366
1367 if (emitted)
1368 statement("");
1369 emitted = false;
1370
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01001371 // Emit builtin input and output variables here.
1372 emit_builtin_variables();
1373
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001374 ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
Hans-Kristian Arntzen2fb9aa22019-01-11 09:29:28 +01001375 auto &type = this->get<SPIRType>(var.basetype);
Robert Konrad80fcf552016-08-14 21:33:32 +02001376
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001377 if (var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001378 (var.storage == StorageClassInput || var.storage == StorageClassOutput) && !is_builtin_variable(var) &&
1379 interface_variable_exists_in_entry_point(var.self))
1380 {
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001381 // Builtin variables are handled separately.
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001382 emit_interface_block_globally(var);
1383 emitted = true;
Robert Konrad80fcf552016-08-14 21:33:32 +02001384 }
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001385 });
Robert Konrad80fcf552016-08-14 21:33:32 +02001386
1387 if (emitted)
1388 statement("");
1389 emitted = false;
1390
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01001391 require_input = false;
1392 require_output = false;
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +01001393 unordered_set<uint32_t> active_inputs;
1394 unordered_set<uint32_t> active_outputs;
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001395
1396 struct IOVariable
1397 {
1398 const SPIRVariable *var;
1399 uint32_t location;
1400 uint32_t block_member_index;
1401 bool block;
1402 };
1403
1404 SmallVector<IOVariable> input_variables;
1405 SmallVector<IOVariable> output_variables;
1406
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001407 ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
Hans-Kristian Arntzen2fb9aa22019-01-11 09:29:28 +01001408 auto &type = this->get<SPIRType>(var.basetype);
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001409 bool block = has_decoration(type.self, DecorationBlock);
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001410
1411 if (var.storage != StorageClassInput && var.storage != StorageClassOutput)
1412 return;
1413
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001414 if (!var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001415 interface_variable_exists_in_entry_point(var.self))
Robert Konrad02fd8e92016-08-14 17:58:56 +02001416 {
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001417 if (block)
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001418 {
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001419 for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001420 {
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001421 uint32_t location = get_declared_member_location(var, i, false);
1422 if (var.storage == StorageClassInput)
1423 input_variables.push_back({ &var, location, i, true });
1424 else
1425 output_variables.push_back({ &var, location, i, true });
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001426 }
1427 }
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001428 else
1429 {
1430 uint32_t location = get_decoration(var.self, DecorationLocation);
1431 if (var.storage == StorageClassInput)
1432 input_variables.push_back({ &var, location, 0, false });
1433 else
1434 output_variables.push_back({ &var, location, 0, false });
1435 }
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01001436 }
1437 });
Robert Konrad02fd8e92016-08-14 17:58:56 +02001438
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001439 const auto variable_compare = [&](const IOVariable &a, const IOVariable &b) -> bool {
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +01001440 // Sort input and output variables based on, from more robust to less robust:
1441 // - Location
1442 // - Variable has a location
1443 // - Name comparison
1444 // - Variable has a name
1445 // - Fallback: ID
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001446 bool has_location_a = a.block || has_decoration(a.var->self, DecorationLocation);
1447 bool has_location_b = b.block || has_decoration(b.var->self, DecorationLocation);
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +01001448
1449 if (has_location_a && has_location_b)
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001450 return a.location < b.location;
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +01001451 else if (has_location_a && !has_location_b)
1452 return true;
1453 else if (!has_location_a && has_location_b)
1454 return false;
1455
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001456 const auto &name1 = to_name(a.var->self);
1457 const auto &name2 = to_name(b.var->self);
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +01001458
1459 if (name1.empty() && name2.empty())
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001460 return a.var->self < b.var->self;
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +01001461 else if (name1.empty())
1462 return true;
1463 else if (name2.empty())
1464 return false;
1465
1466 return name1.compare(name2) < 0;
1467 };
1468
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01001469 auto input_builtins = active_input_builtins;
1470 input_builtins.clear(BuiltInNumWorkgroups);
1471 input_builtins.clear(BuiltInPointCoord);
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02001472 input_builtins.clear(BuiltInSubgroupSize);
1473 input_builtins.clear(BuiltInSubgroupLocalInvocationId);
1474 input_builtins.clear(BuiltInSubgroupEqMask);
1475 input_builtins.clear(BuiltInSubgroupLtMask);
1476 input_builtins.clear(BuiltInSubgroupLeMask);
1477 input_builtins.clear(BuiltInSubgroupGtMask);
1478 input_builtins.clear(BuiltInSubgroupGeMask);
1479
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01001480 if (!input_variables.empty() || !input_builtins.empty())
Robert Konrad02fd8e92016-08-14 17:58:56 +02001481 {
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01001482 require_input = true;
1483 statement("struct SPIRV_Cross_Input");
1484
1485 begin_scope();
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +01001486 sort(input_variables.begin(), input_variables.end(), variable_compare);
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001487 for (auto &var : input_variables)
1488 {
1489 if (var.block)
1490 emit_interface_block_member_in_struct(*var.var, var.block_member_index, var.location, active_inputs);
1491 else
1492 emit_interface_block_in_struct(*var.var, active_inputs);
1493 }
Robert Konrade7b02582017-03-24 13:58:39 +01001494 emit_builtin_inputs_in_struct();
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01001495 end_scope_decl();
1496 statement("");
Robert Konrad02fd8e92016-08-14 17:58:56 +02001497 }
Robert Konrad02fd8e92016-08-14 17:58:56 +02001498
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01001499 if (!output_variables.empty() || !active_output_builtins.empty())
Robert Konrada89686822016-08-15 20:33:10 +02001500 {
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01001501 require_output = true;
1502 statement("struct SPIRV_Cross_Output");
1503
1504 begin_scope();
Hans-Kristian Arntzen61c31c62017-03-07 13:27:04 +01001505 sort(output_variables.begin(), output_variables.end(), variable_compare);
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02001506 for (auto &var : output_variables)
1507 {
1508 if (var.block)
1509 emit_interface_block_member_in_struct(*var.var, var.block_member_index, var.location, active_outputs);
1510 else
1511 emit_interface_block_in_struct(*var.var, active_outputs);
1512 }
Robert Konrade7b02582017-03-24 13:58:39 +01001513 emit_builtin_outputs_in_struct();
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01001514 end_scope_decl();
1515 statement("");
Robert Konrad4a0267e2016-09-23 23:56:37 +02001516 }
Robert Konrad02fd8e92016-08-14 17:58:56 +02001517
1518 // Global variables.
1519 for (auto global : global_variables)
1520 {
1521 auto &var = get<SPIRVariable>(global);
Hans-Kristian Arntzenea02a0c2021-01-22 13:48:16 +01001522 if (is_hidden_variable(var, true))
1523 continue;
1524
Robert Konrad02fd8e92016-08-14 17:58:56 +02001525 if (var.storage != StorageClassOutput)
1526 {
Hans-Kristian Arntzen3e584f22019-02-06 10:38:18 +01001527 if (!variable_is_lut(var))
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02001528 {
Hans-Kristian Arntzen3e584f22019-02-06 10:38:18 +01001529 add_resource_name(var.self);
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02001530
Hans-Kristian Arntzen3e584f22019-02-06 10:38:18 +01001531 const char *storage = nullptr;
1532 switch (var.storage)
1533 {
1534 case StorageClassWorkgroup:
1535 storage = "groupshared";
1536 break;
1537
1538 default:
1539 storage = "static";
1540 break;
1541 }
Hans-Kristian Arntzenb8905bb2020-03-26 11:21:23 +01001542
1543 string initializer;
1544 if (options.force_zero_initialized_variables && var.storage == StorageClassPrivate &&
1545 !var.initializer && !var.static_expression && type_can_zero_initialize(get_variable_data_type(var)))
1546 {
1547 initializer = join(" = ", to_zero_initialized_expression(get_variable_data_type_id(var)));
1548 }
1549 statement(storage, " ", variable_decl(var), initializer, ";");
1550
Hans-Kristian Arntzen3e584f22019-02-06 10:38:18 +01001551 emitted = true;
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02001552 }
Robert Konrad02fd8e92016-08-14 17:58:56 +02001553 }
1554 }
1555
1556 if (emitted)
1557 statement("");
Robert Konrad1a48d7d2016-08-18 12:54:22 +02001558
Hans-Kristian Arntzen2abdc132017-08-02 10:33:03 +02001559 declare_undefined_values();
1560
Robert Konrad1a48d7d2016-08-18 12:54:22 +02001561 if (requires_op_fmod)
1562 {
Hans-Kristian Arntzend4727fe2017-10-06 13:21:42 +02001563 static const char *types[] = {
Hans-Kristian Arntzence18d4c2017-11-17 13:38:29 +01001564 "float",
1565 "float2",
1566 "float3",
1567 "float4",
Hans-Kristian Arntzend4727fe2017-10-06 13:21:42 +02001568 };
1569
1570 for (auto &type : types)
1571 {
1572 statement(type, " mod(", type, " x, ", type, " y)");
1573 begin_scope();
1574 statement("return x - y * floor(x / y);");
1575 end_scope();
1576 statement("");
1577 }
Robert Konrad1a48d7d2016-08-18 12:54:22 +02001578 }
Robert Konradfd9b5892017-04-20 13:37:38 +02001579
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02001580 emit_texture_size_variants(required_texture_size_variants.srv, "4", false, "");
1581 for (uint32_t norm = 0; norm < 3; norm++)
Hans-Kristian Arntzen1bc5b702017-09-20 09:59:32 +02001582 {
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02001583 for (uint32_t comp = 0; comp < 4; comp++)
Hans-Kristian Arntzen1bc5b702017-09-20 09:59:32 +02001584 {
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02001585 static const char *qualifiers[] = { "", "unorm ", "snorm " };
1586 static const char *vecsizes[] = { "", "2", "3", "4" };
Hans-Kristian Arntzend573a952020-07-01 11:42:58 +02001587 emit_texture_size_variants(required_texture_size_variants.uav[norm][comp], vecsizes[comp], true,
1588 qualifiers[norm]);
Hans-Kristian Arntzen1bc5b702017-09-20 09:59:32 +02001589 }
1590 }
Hans-Kristian Arntzen6c7c6802017-11-27 14:24:30 +01001591
1592 if (requires_fp16_packing)
1593 {
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01001594 // HLSL does not pack into a single word sadly :(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001595 statement("uint spvPackHalf2x16(float2 value)");
Hans-Kristian Arntzen6c7c6802017-11-27 14:24:30 +01001596 begin_scope();
1597 statement("uint2 Packed = f32tof16(value);");
1598 statement("return Packed.x | (Packed.y << 16);");
1599 end_scope();
1600 statement("");
1601
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001602 statement("float2 spvUnpackHalf2x16(uint value)");
Hans-Kristian Arntzen6c7c6802017-11-27 14:24:30 +01001603 begin_scope();
1604 statement("return f16tof32(uint2(value & 0xffff, value >> 16));");
1605 end_scope();
1606 statement("");
1607 }
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01001608
Asuka55dfbea2020-04-17 22:46:06 +08001609 if (requires_uint2_packing)
1610 {
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001611 statement("uint64_t spvPackUint2x32(uint2 value)");
Asuka55dfbea2020-04-17 22:46:06 +08001612 begin_scope();
Hans-Kristian Arntzen7b9cba72020-04-21 11:48:37 +02001613 statement("return (uint64_t(value.y) << 32) | uint64_t(value.x);");
Asuka55dfbea2020-04-17 22:46:06 +08001614 end_scope();
1615 statement("");
1616
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001617 statement("uint2 spvUnpackUint2x32(uint64_t value)");
Asuka55dfbea2020-04-17 22:46:06 +08001618 begin_scope();
1619 statement("uint2 Unpacked;");
1620 statement("Unpacked.x = uint(value & 0xffffffff);");
1621 statement("Unpacked.y = uint(value >> 32);");
Hans-Kristian Arntzen7b9cba72020-04-21 11:48:37 +02001622 statement("return Unpacked;");
Asuka55dfbea2020-04-17 22:46:06 +08001623 end_scope();
1624 statement("");
1625 }
1626
Hans-Kristian Arntzen47d94ff2018-03-07 10:21:25 +01001627 if (requires_explicit_fp16_packing)
1628 {
1629 // HLSL does not pack into a single word sadly :(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001630 statement("uint spvPackFloat2x16(min16float2 value)");
Hans-Kristian Arntzen47d94ff2018-03-07 10:21:25 +01001631 begin_scope();
1632 statement("uint2 Packed = f32tof16(value);");
1633 statement("return Packed.x | (Packed.y << 16);");
1634 end_scope();
1635 statement("");
1636
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001637 statement("min16float2 spvUnpackFloat2x16(uint value)");
Hans-Kristian Arntzen47d94ff2018-03-07 10:21:25 +01001638 begin_scope();
1639 statement("return min16float2(f16tof32(uint2(value & 0xffff, value >> 16)));");
1640 end_scope();
1641 statement("");
1642 }
1643
Hans-Kristian Arntzen656af7e2017-11-27 15:03:40 +01001644 // HLSL does not seem to have builtins for these operation, so roll them by hand ...
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01001645 if (requires_unorm8_packing)
1646 {
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001647 statement("uint spvPackUnorm4x8(float4 value)");
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01001648 begin_scope();
1649 statement("uint4 Packed = uint4(round(saturate(value) * 255.0));");
1650 statement("return Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24);");
1651 end_scope();
1652 statement("");
1653
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001654 statement("float4 spvUnpackUnorm4x8(uint value)");
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01001655 begin_scope();
1656 statement("uint4 Packed = uint4(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, value >> 24);");
1657 statement("return float4(Packed) / 255.0;");
1658 end_scope();
1659 statement("");
1660 }
1661
1662 if (requires_snorm8_packing)
1663 {
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001664 statement("uint spvPackSnorm4x8(float4 value)");
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01001665 begin_scope();
1666 statement("int4 Packed = int4(round(clamp(value, -1.0, 1.0) * 127.0)) & 0xff;");
1667 statement("return uint(Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24));");
1668 end_scope();
1669 statement("");
1670
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001671 statement("float4 spvUnpackSnorm4x8(uint value)");
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01001672 begin_scope();
1673 statement("int SignedValue = int(value);");
1674 statement("int4 Packed = int4(SignedValue << 24, SignedValue << 16, SignedValue << 8, SignedValue) >> 24;");
1675 statement("return clamp(float4(Packed) / 127.0, -1.0, 1.0);");
1676 end_scope();
1677 statement("");
1678 }
Hans-Kristian Arntzen656af7e2017-11-27 15:03:40 +01001679
1680 if (requires_unorm16_packing)
1681 {
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001682 statement("uint spvPackUnorm2x16(float2 value)");
Hans-Kristian Arntzen656af7e2017-11-27 15:03:40 +01001683 begin_scope();
1684 statement("uint2 Packed = uint2(round(saturate(value) * 65535.0));");
1685 statement("return Packed.x | (Packed.y << 16);");
1686 end_scope();
1687 statement("");
1688
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001689 statement("float2 spvUnpackUnorm2x16(uint value)");
Hans-Kristian Arntzen656af7e2017-11-27 15:03:40 +01001690 begin_scope();
1691 statement("uint2 Packed = uint2(value & 0xffff, value >> 16);");
1692 statement("return float2(Packed) / 65535.0;");
1693 end_scope();
1694 statement("");
1695 }
1696
1697 if (requires_snorm16_packing)
1698 {
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001699 statement("uint spvPackSnorm2x16(float2 value)");
Hans-Kristian Arntzen656af7e2017-11-27 15:03:40 +01001700 begin_scope();
1701 statement("int2 Packed = int2(round(clamp(value, -1.0, 1.0) * 32767.0)) & 0xffff;");
1702 statement("return uint(Packed.x | (Packed.y << 16));");
1703 end_scope();
1704 statement("");
1705
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001706 statement("float2 spvUnpackSnorm2x16(uint value)");
Hans-Kristian Arntzen656af7e2017-11-27 15:03:40 +01001707 begin_scope();
1708 statement("int SignedValue = int(value);");
1709 statement("int2 Packed = int2(SignedValue << 16, SignedValue) >> 16;");
1710 statement("return clamp(float2(Packed) / 32767.0, -1.0, 1.0);");
1711 end_scope();
1712 statement("");
1713 }
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01001714
1715 if (requires_bitfield_insert)
1716 {
1717 static const char *types[] = { "uint", "uint2", "uint3", "uint4" };
1718 for (auto &type : types)
1719 {
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001720 statement(type, " spvBitfieldInsert(", type, " Base, ", type, " Insert, uint Offset, uint Count)");
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01001721 begin_scope();
1722 statement("uint Mask = Count == 32 ? 0xffffffff : (((1u << Count) - 1) << (Offset & 31));");
1723 statement("return (Base & ~Mask) | ((Insert << Offset) & Mask);");
1724 end_scope();
1725 statement("");
1726 }
1727 }
1728
1729 if (requires_bitfield_extract)
1730 {
1731 static const char *unsigned_types[] = { "uint", "uint2", "uint3", "uint4" };
1732 for (auto &type : unsigned_types)
1733 {
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001734 statement(type, " spvBitfieldUExtract(", type, " Base, uint Offset, uint Count)");
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01001735 begin_scope();
1736 statement("uint Mask = Count == 32 ? 0xffffffff : ((1 << Count) - 1);");
1737 statement("return (Base >> Offset) & Mask;");
1738 end_scope();
1739 statement("");
1740 }
1741
1742 // In this overload, we will have to do sign-extension, which we will emulate by shifting up and down.
1743 static const char *signed_types[] = { "int", "int2", "int3", "int4" };
1744 for (auto &type : signed_types)
1745 {
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001746 statement(type, " spvBitfieldSExtract(", type, " Base, int Offset, int Count)");
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01001747 begin_scope();
1748 statement("int Mask = Count == 32 ? -1 : ((1 << Count) - 1);");
1749 statement(type, " Masked = (Base >> Offset) & Mask;");
1750 statement("int ExtendShift = (32 - Count) & 31;");
1751 statement("return (Masked << ExtendShift) >> ExtendShift;");
1752 end_scope();
1753 statement("");
1754 }
1755 }
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001756
1757 if (requires_inverse_2x2)
1758 {
1759 statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical");
1760 statement("// adjoint and dividing by the determinant. The contents of the matrix are changed.");
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001761 statement("float2x2 spvInverse(float2x2 m)");
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001762 begin_scope();
1763 statement("float2x2 adj; // The adjoint matrix (inverse after dividing by determinant)");
1764 statement_no_indent("");
1765 statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix.");
1766 statement("adj[0][0] = m[1][1];");
1767 statement("adj[0][1] = -m[0][1];");
1768 statement_no_indent("");
1769 statement("adj[1][0] = -m[1][0];");
1770 statement("adj[1][1] = m[0][0];");
1771 statement_no_indent("");
1772 statement("// Calculate the determinant as a combination of the cofactors of the first row.");
1773 statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]);");
1774 statement_no_indent("");
1775 statement("// Divide the classical adjoint matrix by the determinant.");
1776 statement("// If determinant is zero, matrix is not invertable, so leave it unchanged.");
1777 statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;");
1778 end_scope();
1779 statement("");
1780 }
1781
1782 if (requires_inverse_3x3)
1783 {
1784 statement("// Returns the determinant of a 2x2 matrix.");
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001785 statement("float spvDet2x2(float a1, float a2, float b1, float b2)");
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001786 begin_scope();
1787 statement("return a1 * b2 - b1 * a2;");
1788 end_scope();
1789 statement_no_indent("");
1790 statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical");
1791 statement("// adjoint and dividing by the determinant. The contents of the matrix are changed.");
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001792 statement("float3x3 spvInverse(float3x3 m)");
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001793 begin_scope();
1794 statement("float3x3 adj; // The adjoint matrix (inverse after dividing by determinant)");
1795 statement_no_indent("");
1796 statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix.");
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001797 statement("adj[0][0] = spvDet2x2(m[1][1], m[1][2], m[2][1], m[2][2]);");
1798 statement("adj[0][1] = -spvDet2x2(m[0][1], m[0][2], m[2][1], m[2][2]);");
1799 statement("adj[0][2] = spvDet2x2(m[0][1], m[0][2], m[1][1], m[1][2]);");
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001800 statement_no_indent("");
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001801 statement("adj[1][0] = -spvDet2x2(m[1][0], m[1][2], m[2][0], m[2][2]);");
1802 statement("adj[1][1] = spvDet2x2(m[0][0], m[0][2], m[2][0], m[2][2]);");
1803 statement("adj[1][2] = -spvDet2x2(m[0][0], m[0][2], m[1][0], m[1][2]);");
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001804 statement_no_indent("");
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001805 statement("adj[2][0] = spvDet2x2(m[1][0], m[1][1], m[2][0], m[2][1]);");
1806 statement("adj[2][1] = -spvDet2x2(m[0][0], m[0][1], m[2][0], m[2][1]);");
1807 statement("adj[2][2] = spvDet2x2(m[0][0], m[0][1], m[1][0], m[1][1]);");
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001808 statement_no_indent("");
1809 statement("// Calculate the determinant as a combination of the cofactors of the first row.");
1810 statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]);");
1811 statement_no_indent("");
1812 statement("// Divide the classical adjoint matrix by the determinant.");
1813 statement("// If determinant is zero, matrix is not invertable, so leave it unchanged.");
1814 statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;");
1815 end_scope();
1816 statement("");
1817 }
1818
1819 if (requires_inverse_4x4)
1820 {
1821 if (!requires_inverse_3x3)
1822 {
1823 statement("// Returns the determinant of a 2x2 matrix.");
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001824 statement("float spvDet2x2(float a1, float a2, float b1, float b2)");
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001825 begin_scope();
1826 statement("return a1 * b2 - b1 * a2;");
1827 end_scope();
1828 statement("");
1829 }
1830
1831 statement("// Returns the determinant of a 3x3 matrix.");
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001832 statement("float spvDet3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001833 "float c2, float c3)");
1834 begin_scope();
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001835 statement("return a1 * spvDet2x2(b2, b3, c2, c3) - b1 * spvDet2x2(a2, a3, c2, c3) + c1 * "
1836 "spvDet2x2(a2, a3, "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001837 "b2, b3);");
1838 end_scope();
1839 statement_no_indent("");
1840 statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical");
1841 statement("// adjoint and dividing by the determinant. The contents of the matrix are changed.");
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001842 statement("float4x4 spvInverse(float4x4 m)");
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001843 begin_scope();
1844 statement("float4x4 adj; // The adjoint matrix (inverse after dividing by determinant)");
1845 statement_no_indent("");
1846 statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix.");
1847 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001848 "adj[0][0] = spvDet3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001849 "m[3][3]);");
1850 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001851 "adj[0][1] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001852 "m[3][3]);");
1853 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001854 "adj[0][2] = spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001855 "m[3][3]);");
1856 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001857 "adj[0][3] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001858 "m[2][3]);");
1859 statement_no_indent("");
1860 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001861 "adj[1][0] = -spvDet3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001862 "m[3][3]);");
1863 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001864 "adj[1][1] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001865 "m[3][3]);");
1866 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001867 "adj[1][2] = -spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001868 "m[3][3]);");
1869 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001870 "adj[1][3] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001871 "m[2][3]);");
1872 statement_no_indent("");
1873 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001874 "adj[2][0] = spvDet3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001875 "m[3][3]);");
1876 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001877 "adj[2][1] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001878 "m[3][3]);");
1879 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001880 "adj[2][2] = spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001881 "m[3][3]);");
1882 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001883 "adj[2][3] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001884 "m[2][3]);");
1885 statement_no_indent("");
1886 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001887 "adj[3][0] = -spvDet3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001888 "m[3][2]);");
1889 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001890 "adj[3][1] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001891 "m[3][2]);");
1892 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001893 "adj[3][2] = -spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001894 "m[3][2]);");
1895 statement(
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001896 "adj[3][3] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], "
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01001897 "m[2][2]);");
1898 statement_no_indent("");
1899 statement("// Calculate the determinant as a combination of the cofactors of the first row.");
1900 statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]) + (adj[0][3] "
1901 "* m[3][0]);");
1902 statement_no_indent("");
1903 statement("// Divide the classical adjoint matrix by the determinant.");
1904 statement("// If determinant is zero, matrix is not invertable, so leave it unchanged.");
1905 statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;");
1906 end_scope();
1907 statement("");
1908 }
Hans-Kristian Arntzen041f1032019-07-03 12:24:58 +02001909
1910 if (requires_scalar_reflect)
1911 {
1912 // FP16/FP64? No templates in HLSL.
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001913 statement("float spvReflect(float i, float n)");
Hans-Kristian Arntzen041f1032019-07-03 12:24:58 +02001914 begin_scope();
1915 statement("return i - 2.0 * dot(n, i) * n;");
1916 end_scope();
1917 statement("");
1918 }
1919
1920 if (requires_scalar_refract)
1921 {
1922 // FP16/FP64? No templates in HLSL.
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001923 statement("float spvRefract(float i, float n, float eta)");
Hans-Kristian Arntzen041f1032019-07-03 12:24:58 +02001924 begin_scope();
Hans-Kristian Arntzen4056d0b2019-07-03 14:32:06 +02001925 statement("float NoI = n * i;");
1926 statement("float NoI2 = NoI * NoI;");
1927 statement("float k = 1.0 - eta * eta * (1.0 - NoI2);");
Hans-Kristian Arntzen041f1032019-07-03 12:24:58 +02001928 statement("if (k < 0.0)");
1929 begin_scope();
1930 statement("return 0.0;");
1931 end_scope();
1932 statement("else");
1933 begin_scope();
Hans-Kristian Arntzen4056d0b2019-07-03 14:32:06 +02001934 statement("return eta * i - (eta * NoI + sqrt(k)) * n;");
Hans-Kristian Arntzen041f1032019-07-03 12:24:58 +02001935 end_scope();
1936 end_scope();
1937 statement("");
1938 }
Hans-Kristian Arntzenc7eda1b2019-07-17 11:24:31 +02001939
1940 if (requires_scalar_faceforward)
1941 {
1942 // FP16/FP64? No templates in HLSL.
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01001943 statement("float spvFaceForward(float n, float i, float nref)");
Hans-Kristian Arntzenc7eda1b2019-07-17 11:24:31 +02001944 begin_scope();
1945 statement("return i * nref < 0.0 ? n : -n;");
1946 end_scope();
1947 statement("");
1948 }
Hans-Kristian Arntzen8216e872021-06-28 11:10:55 +02001949
1950 for (TypeID type_id : composite_selection_workaround_types)
1951 {
1952 // Need out variable since HLSL does not support returning arrays.
1953 auto &type = get<SPIRType>(type_id);
1954 auto type_str = type_to_glsl(type);
1955 auto type_arr_str = type_to_array_glsl(type);
1956 statement("void spvSelectComposite(out ", type_str, " out_value", type_arr_str, ", bool cond, ",
1957 type_str, " true_val", type_arr_str, ", ",
1958 type_str, " false_val", type_arr_str, ")");
1959 begin_scope();
1960 statement("if (cond)");
1961 begin_scope();
1962 statement("out_value = true_val;");
1963 end_scope();
1964 statement("else");
1965 begin_scope();
1966 statement("out_value = false_val;");
1967 end_scope();
1968 end_scope();
1969 statement("");
1970 }
Robert Konrad02fd8e92016-08-14 17:58:56 +02001971}
1972
Hans-Kristian Arntzend573a952020-07-01 11:42:58 +02001973void CompilerHLSL::emit_texture_size_variants(uint64_t variant_mask, const char *vecsize_qualifier, bool uav,
1974 const char *type_qualifier)
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02001975{
1976 if (variant_mask == 0)
1977 return;
1978
1979 static const char *types[QueryTypeCount] = { "float", "int", "uint" };
1980 static const char *dims[QueryDimCount] = { "Texture1D", "Texture1DArray", "Texture2D", "Texture2DArray",
Hans-Kristian Arntzend573a952020-07-01 11:42:58 +02001981 "Texture3D", "Buffer", "TextureCube", "TextureCubeArray",
1982 "Texture2DMS", "Texture2DMSArray" };
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02001983
1984 static const bool has_lod[QueryDimCount] = { true, true, true, true, true, false, true, true, false, false };
1985
1986 static const char *ret_types[QueryDimCount] = {
1987 "uint", "uint2", "uint2", "uint3", "uint3", "uint", "uint2", "uint3", "uint2", "uint3",
1988 };
1989
1990 static const uint32_t return_arguments[QueryDimCount] = {
1991 1, 2, 2, 3, 3, 1, 2, 3, 2, 3,
1992 };
1993
1994 for (uint32_t index = 0; index < QueryDimCount; index++)
1995 {
1996 for (uint32_t type_index = 0; type_index < QueryTypeCount; type_index++)
1997 {
1998 uint32_t bit = 16 * type_index + index;
1999 uint64_t mask = 1ull << bit;
2000
2001 if ((variant_mask & mask) == 0)
2002 continue;
2003
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01002004 statement(ret_types[index], " spv", (uav ? "Image" : "Texture"), "Size(", (uav ? "RW" : ""),
Hans-Kristian Arntzend573a952020-07-01 11:42:58 +02002005 dims[index], "<", type_qualifier, types[type_index], vecsize_qualifier, "> Tex, ",
2006 (uav ? "" : "uint Level, "), "out uint Param)");
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02002007 begin_scope();
2008 statement(ret_types[index], " ret;");
2009 switch (return_arguments[index])
2010 {
2011 case 1:
2012 if (has_lod[index] && !uav)
2013 statement("Tex.GetDimensions(Level, ret.x, Param);");
2014 else
2015 {
2016 statement("Tex.GetDimensions(ret.x);");
2017 statement("Param = 0u;");
2018 }
2019 break;
2020 case 2:
2021 if (has_lod[index] && !uav)
2022 statement("Tex.GetDimensions(Level, ret.x, ret.y, Param);");
2023 else if (!uav)
2024 statement("Tex.GetDimensions(ret.x, ret.y, Param);");
2025 else
2026 {
2027 statement("Tex.GetDimensions(ret.x, ret.y);");
2028 statement("Param = 0u;");
2029 }
2030 break;
2031 case 3:
2032 if (has_lod[index] && !uav)
2033 statement("Tex.GetDimensions(Level, ret.x, ret.y, ret.z, Param);");
2034 else if (!uav)
2035 statement("Tex.GetDimensions(ret.x, ret.y, ret.z, Param);");
2036 else
2037 {
2038 statement("Tex.GetDimensions(ret.x, ret.y, ret.z);");
2039 statement("Param = 0u;");
2040 }
2041 break;
2042 }
2043
2044 statement("return ret;");
2045 end_scope();
2046 statement("");
2047 }
2048 }
2049}
2050
Hans-Kristian Arntzen620d01c2017-10-10 16:11:25 +02002051string CompilerHLSL::layout_for_member(const SPIRType &type, uint32_t index)
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002052{
Hans-Kristian Arntzenf1e85552018-06-05 09:42:07 +02002053 auto &flags = get_member_decoration_bitset(type.self, index);
Hans-Kristian Arntzen620d01c2017-10-10 16:11:25 +02002054
Hans-Kristian Arntzenf1e85552018-06-05 09:42:07 +02002055 // HLSL can emit row_major or column_major decoration in any struct.
2056 // Do not try to merge combined decorations for children like in GLSL.
Hans-Kristian Arntzen620d01c2017-10-10 16:11:25 +02002057
2058 // Flip the convention. HLSL is a bit odd in that the memory layout is column major ... but the language API is "row-major".
2059 // The way to deal with this is to multiply everything in inverse order, and reverse the memory layout.
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002060 if (flags.get(DecorationColMajor))
Hans-Kristian Arntzen620d01c2017-10-10 16:11:25 +02002061 return "row_major ";
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002062 else if (flags.get(DecorationRowMajor))
Hans-Kristian Arntzen620d01c2017-10-10 16:11:25 +02002063 return "column_major ";
2064
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002065 return "";
2066}
2067
Hans-Kristian Arntzenf0200bb2017-10-10 13:15:49 +02002068void CompilerHLSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
msiglreithd096f5c2017-11-27 16:00:56 +01002069 const string &qualifier, uint32_t base_offset)
Hans-Kristian Arntzenf0200bb2017-10-10 13:15:49 +02002070{
2071 auto &membertype = get<SPIRType>(member_type_id);
2072
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002073 Bitset memberflags;
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002074 auto &memb = ir.meta[type.self].members;
Hans-Kristian Arntzenf0200bb2017-10-10 13:15:49 +02002075 if (index < memb.size())
2076 memberflags = memb[index].decoration_flags;
2077
Hans-Kristian Arntzenf0200bb2017-10-10 13:15:49 +02002078 string packing_offset;
msiglreithd096f5c2017-11-27 16:00:56 +01002079 bool is_push_constant = type.storage == StorageClassPushConstant;
2080
Hans-Kristian Arntzena86308b2019-07-18 13:34:47 +02002081 if ((has_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset) || is_push_constant) &&
msiglreithd096f5c2017-11-27 16:00:56 +01002082 has_member_decoration(type.self, index, DecorationOffset))
Hans-Kristian Arntzenf0200bb2017-10-10 13:15:49 +02002083 {
msiglreithd096f5c2017-11-27 16:00:56 +01002084 uint32_t offset = memb[index].offset - base_offset;
Hans-Kristian Arntzenf0200bb2017-10-10 13:15:49 +02002085 if (offset & 3)
2086 SPIRV_CROSS_THROW("Cannot pack on tighter bounds than 4 bytes in HLSL.");
2087
2088 static const char *packing_swizzle[] = { "", ".y", ".z", ".w" };
2089 packing_offset = join(" : packoffset(c", offset / 16, packing_swizzle[(offset & 15) >> 2], ")");
2090 }
2091
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02002092 statement(layout_for_member(type, index), qualifier,
Hans-Kristian Arntzenf0200bb2017-10-10 13:15:49 +02002093 variable_decl(membertype, to_member_name(type, index)), packing_offset, ";");
2094}
2095
Robert Konrad99469f02017-01-24 09:17:43 +01002096void CompilerHLSL::emit_buffer_block(const SPIRVariable &var)
2097{
2098 auto &type = get<SPIRType>(var.basetype);
2099
Hans-Kristian Arntzen153fed02017-09-28 13:28:44 +02002100 bool is_uav = var.storage == StorageClassStorageBuffer || has_decoration(type.self, DecorationBufferBlock);
Hans-Kristian Arntzenecf56cf2017-06-30 10:34:21 +02002101
Hans-Kristian Arntzen247ab1c2017-08-10 17:22:32 +02002102 if (is_uav)
Robert Konrad99469f02017-01-24 09:17:43 +01002103 {
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002104 Bitset flags = ir.get_buffer_block_flags(var);
Bryan Bernhart17bccc92020-05-27 13:08:15 -07002105 bool is_readonly = flags.get(DecorationNonWritable) && !is_hlsl_force_storage_buffer_as_uav(var.self);
Hans-Kristian Arntzen185551b2020-03-05 10:37:36 +01002106 bool is_coherent = flags.get(DecorationCoherent) && !is_readonly;
Chip Davis2eff4202019-08-04 00:07:20 -05002107 bool is_interlocked = interlocked_resources.count(var.self) > 0;
2108 const char *type_name = "ByteAddressBuffer ";
2109 if (!is_readonly)
2110 type_name = is_interlocked ? "RasterizerOrderedByteAddressBuffer " : "RWByteAddressBuffer ";
Hans-Kristian Arntzen247ab1c2017-08-10 17:22:32 +02002111 add_resource_name(var.self);
Chip Davis2eff4202019-08-04 00:07:20 -05002112 statement(is_coherent ? "globallycoherent " : "", type_name, to_name(var.self), type_to_array_glsl(type),
2113 to_resource_binding(var), ";");
Hans-Kristian Arntzenecf56cf2017-06-30 10:34:21 +02002114 }
2115 else
2116 {
Hans-Kristian Arntzen6feff982017-10-10 15:37:53 +02002117 if (type.array.empty())
Hans-Kristian Arntzen247ab1c2017-08-10 17:22:32 +02002118 {
Hans-Kristian Arntzen6feff982017-10-10 15:37:53 +02002119 // Flatten the top-level struct so we can use packoffset,
2120 // this restriction is similar to GLSL where layout(offset) is not possible on sub-structs.
Hans-Kristian Arntzen2d431032020-07-03 13:12:05 +02002121 flattened_structs[var.self] = false;
Hans-Kristian Arntzen6feff982017-10-10 15:37:53 +02002122
Hans-Kristian Arntzen226d8372018-10-22 09:50:04 +02002123 // Prefer the block name if possible.
2124 auto buffer_name = to_name(type.self, false);
Hans-Kristian Arntzen9728f9c2019-01-04 12:15:43 +01002125 if (ir.meta[type.self].decoration.alias.empty() ||
2126 resource_names.find(buffer_name) != end(resource_names) ||
2127 block_names.find(buffer_name) != end(block_names))
2128 {
Hans-Kristian Arntzen226d8372018-10-22 09:50:04 +02002129 buffer_name = get_block_fallback_name(var.self);
Hans-Kristian Arntzen9728f9c2019-01-04 12:15:43 +01002130 }
2131
2132 add_variable(block_names, resource_names, buffer_name);
Hans-Kristian Arntzen226d8372018-10-22 09:50:04 +02002133
2134 // If for some reason buffer_name is an illegal name, make a final fallback to a workaround name.
2135 // This cannot conflict with anything else, so we're safe now.
2136 if (buffer_name.empty())
2137 buffer_name = join("_", get<SPIRType>(var.basetype).self, "_", var.self);
2138
Hans-Kristian Arntzen0b417b52019-11-06 11:20:57 +01002139 uint32_t failed_index = 0;
2140 if (buffer_is_packing_standard(type, BufferPackingHLSLCbufferPackOffset, &failed_index))
Hans-Kristian Arntzene73d9be2019-11-06 11:04:52 +01002141 set_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset);
2142 else
2143 {
Hans-Kristian Arntzen0b417b52019-11-06 11:20:57 +01002144 SPIRV_CROSS_THROW(join("cbuffer ID ", var.self, " (name: ", buffer_name, "), member index ",
2145 failed_index, " (name: ", to_member_name(type, failed_index),
Hans-Kristian Arntzene73d9be2019-11-06 11:04:52 +01002146 ") cannot be expressed with either HLSL packing layout or packoffset."));
2147 }
2148
Hans-Kristian Arntzen9728f9c2019-01-04 12:15:43 +01002149 block_names.insert(buffer_name);
Hans-Kristian Arntzen226d8372018-10-22 09:50:04 +02002150
2151 // Save for post-reflection later.
2152 declared_block_names[var.self] = buffer_name;
2153
Hans-Kristian Arntzen6feff982017-10-10 15:37:53 +02002154 type.member_name_cache.clear();
Hans-Kristian Arntzend7090b82019-02-13 16:39:59 +01002155 // var.self can be used as a backup name for the block name,
2156 // so we need to make sure we don't disturb the name here on a recompile.
2157 // It will need to be reset if we have to recompile.
2158 preserve_alias_on_reset(var.self);
Hans-Kristian Arntzen94ff3552017-10-10 17:32:26 +02002159 add_resource_name(var.self);
Hans-Kristian Arntzen226d8372018-10-22 09:50:04 +02002160 statement("cbuffer ", buffer_name, to_resource_binding(var));
Hans-Kristian Arntzen6feff982017-10-10 15:37:53 +02002161 begin_scope();
2162
2163 uint32_t i = 0;
2164 for (auto &member : type.member_types)
2165 {
2166 add_member_name(type, i);
Hans-Kristian Arntzen94ff3552017-10-10 17:32:26 +02002167 auto backup_name = get_member_name(type.self, i);
2168 auto member_name = to_member_name(type, i);
Hans-Kristian Arntzena0744152020-08-21 16:14:13 +02002169 member_name = join(to_name(var.self), "_", member_name);
2170 ParsedIR::sanitize_underscores(member_name);
2171 set_member_name(type.self, i, member_name);
Hans-Kristian Arntzen6feff982017-10-10 15:37:53 +02002172 emit_struct_member(type, member, i, "");
Hans-Kristian Arntzen94ff3552017-10-10 17:32:26 +02002173 set_member_name(type.self, i, backup_name);
Hans-Kristian Arntzen6feff982017-10-10 15:37:53 +02002174 i++;
2175 }
2176
2177 end_scope_decl();
Hans-Kristian Arntzen226d8372018-10-22 09:50:04 +02002178 statement("");
Hans-Kristian Arntzen6feff982017-10-10 15:37:53 +02002179 }
2180 else
2181 {
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01002182 if (hlsl_options.shader_model < 51)
Hans-Kristian Arntzen74642322017-10-10 16:13:03 +02002183 SPIRV_CROSS_THROW(
2184 "Need ConstantBuffer<T> to use arrays of UBOs, but this is only supported in SM 5.1.");
Hans-Kristian Arntzen6feff982017-10-10 15:37:53 +02002185
Hans-Kristian Arntzen6feff982017-10-10 15:37:53 +02002186 add_resource_name(type.self);
2187 add_resource_name(var.self);
2188
Hans-Kristian Arntzene73d9be2019-11-06 11:04:52 +01002189 // ConstantBuffer<T> does not support packoffset, so it is unuseable unless everything aligns as we expect.
Hans-Kristian Arntzen0b417b52019-11-06 11:20:57 +01002190 uint32_t failed_index = 0;
2191 if (!buffer_is_packing_standard(type, BufferPackingHLSLCbuffer, &failed_index))
Hans-Kristian Arntzene73d9be2019-11-06 11:04:52 +01002192 {
2193 SPIRV_CROSS_THROW(join("HLSL ConstantBuffer<T> ID ", var.self, " (name: ", to_name(type.self),
Hans-Kristian Arntzen0b417b52019-11-06 11:20:57 +01002194 "), member index ", failed_index, " (name: ", to_member_name(type, failed_index),
Hans-Kristian Arntzene73d9be2019-11-06 11:04:52 +01002195 ") cannot be expressed with normal HLSL packing rules."));
2196 }
2197
Hans-Kristian Arntzen6feff982017-10-10 15:37:53 +02002198 emit_struct(get<SPIRType>(type.self));
2199 statement("ConstantBuffer<", to_name(type.self), "> ", to_name(var.self), type_to_array_glsl(type),
2200 to_resource_binding(var), ";");
2201 }
Hans-Kristian Arntzenecf56cf2017-06-30 10:34:21 +02002202 }
Robert Konrad99469f02017-01-24 09:17:43 +01002203}
2204
Hans-Kristian Arntzen620da7b2017-06-17 10:15:32 +02002205void CompilerHLSL::emit_push_constant_block(const SPIRVariable &var)
Robert Konrad99469f02017-01-24 09:17:43 +01002206{
msiglreithd096f5c2017-11-27 16:00:56 +01002207 if (root_constants_layout.empty())
2208 {
2209 emit_buffer_block(var);
2210 }
2211 else
2212 {
2213 for (const auto &layout : root_constants_layout)
2214 {
2215 auto &type = get<SPIRType>(var.basetype);
2216
Hans-Kristian Arntzen0b417b52019-11-06 11:20:57 +01002217 uint32_t failed_index = 0;
2218 if (buffer_is_packing_standard(type, BufferPackingHLSLCbufferPackOffset, &failed_index, layout.start,
2219 layout.end))
Hans-Kristian Arntzena86308b2019-07-18 13:34:47 +02002220 set_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset);
msiglreithd096f5c2017-11-27 16:00:56 +01002221 else
Hans-Kristian Arntzen0b417b52019-11-06 11:20:57 +01002222 {
2223 SPIRV_CROSS_THROW(join("Root constant cbuffer ID ", var.self, " (name: ", to_name(type.self), ")",
2224 ", member index ", failed_index, " (name: ", to_member_name(type, failed_index),
2225 ") cannot be expressed with either HLSL packing layout or packoffset."));
2226 }
msiglreithd096f5c2017-11-27 16:00:56 +01002227
Hans-Kristian Arntzen2d431032020-07-03 13:12:05 +02002228 flattened_structs[var.self] = false;
msiglreithd096f5c2017-11-27 16:00:56 +01002229 type.member_name_cache.clear();
2230 add_resource_name(var.self);
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002231 auto &memb = ir.meta[type.self].members;
msiglreithd096f5c2017-11-27 16:00:56 +01002232
2233 statement("cbuffer SPIRV_CROSS_RootConstant_", to_name(var.self),
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01002234 to_resource_register(HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT, 'b', layout.binding, layout.space));
msiglreithd096f5c2017-11-27 16:00:56 +01002235 begin_scope();
2236
2237 // Index of the next field in the generated root constant constant buffer
2238 auto constant_index = 0u;
2239
2240 // Iterate over all member of the push constant and check which of the fields
2241 // fit into the given root constant layout.
2242 for (auto i = 0u; i < memb.size(); i++)
2243 {
2244 const auto offset = memb[i].offset;
2245 if (layout.start <= offset && offset < layout.end)
2246 {
2247 const auto &member = type.member_types[i];
2248
2249 add_member_name(type, constant_index);
2250 auto backup_name = get_member_name(type.self, i);
2251 auto member_name = to_member_name(type, i);
Hans-Kristian Arntzena0744152020-08-21 16:14:13 +02002252 member_name = join(to_name(var.self), "_", member_name);
2253 ParsedIR::sanitize_underscores(member_name);
2254 set_member_name(type.self, constant_index, member_name);
msiglreithd096f5c2017-11-27 16:00:56 +01002255 emit_struct_member(type, member, i, "", layout.start);
2256 set_member_name(type.self, constant_index, backup_name);
2257
2258 constant_index++;
2259 }
2260 }
2261
2262 end_scope_decl();
2263 }
2264 }
Robert Konrad99469f02017-01-24 09:17:43 +01002265}
2266
Hans-Kristian Arntzen686ac682017-05-07 13:22:16 +02002267string CompilerHLSL::to_sampler_expression(uint32_t id)
2268{
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02002269 auto expr = join("_", to_non_uniform_aware_expression(id));
Hans-Kristian Arntzenec1180f2018-01-04 12:14:18 +01002270 auto index = expr.find_first_of('[');
2271 if (index == string::npos)
2272 {
2273 return expr + "_sampler";
2274 }
2275 else
2276 {
2277 // We have an expression like _ident[array], so we cannot tack on _sampler, insert it inside the string instead.
2278 return expr.insert(index, "_sampler");
2279 }
Hans-Kristian Arntzen686ac682017-05-07 13:22:16 +02002280}
2281
Hans-Kristian Arntzen947f7012017-05-07 13:28:08 +02002282void CompilerHLSL::emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id)
2283{
Minmin Gonge3ebfda2018-11-10 13:33:15 -08002284 if (hlsl_options.shader_model >= 40 && combined_image_samplers.empty())
2285 {
2286 set<SPIRCombinedImageSampler>(result_id, result_type, image_id, samp_id);
2287 }
2288 else
2289 {
2290 // Make sure to suppress usage tracking. It is illegal to create temporaries of opaque types.
2291 emit_op(result_type, result_id, to_combined_image_sampler(image_id, samp_id), true, true);
2292 }
Hans-Kristian Arntzen947f7012017-05-07 13:28:08 +02002293}
2294
Chip Davis39dce882019-08-02 15:11:19 -05002295string CompilerHLSL::to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id)
Hans-Kristian Arntzen686ac682017-05-07 13:22:16 +02002296{
Chip Davis39dce882019-08-02 15:11:19 -05002297 string arg_str = CompilerGLSL::to_func_call_arg(arg, id);
Hans-Kristian Arntzen686ac682017-05-07 13:22:16 +02002298
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01002299 if (hlsl_options.shader_model <= 30)
Hans-Kristian Arntzen686ac682017-05-07 13:22:16 +02002300 return arg_str;
2301
2302 // Manufacture automatic sampler arg if the arg is a SampledImage texture and we're in modern HLSL.
Hans-Kristian Arntzenec1180f2018-01-04 12:14:18 +01002303 auto &type = expression_type(id);
Hans-Kristian Arntzen686ac682017-05-07 13:22:16 +02002304
Hans-Kristian Arntzenec1180f2018-01-04 12:14:18 +01002305 // We don't have to consider combined image samplers here via OpSampledImage because
2306 // those variables cannot be passed as arguments to functions.
2307 // Only global SampledImage variables may be used as arguments.
2308 if (type.basetype == SPIRType::SampledImage && type.image.dim != DimBuffer)
2309 arg_str += ", " + to_sampler_expression(id);
Hans-Kristian Arntzen686ac682017-05-07 13:22:16 +02002310
2311 return arg_str;
2312}
2313
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002314void CompilerHLSL::emit_function_prototype(SPIRFunction &func, const Bitset &return_flags)
Robert Konrad80fcf552016-08-14 21:33:32 +02002315{
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002316 if (func.self != ir.default_entry_point)
Hans-Kristian Arntzena04bdcc2018-02-23 14:13:46 +01002317 add_function_overload(func);
2318
Robert Konrad80fcf552016-08-14 21:33:32 +02002319 auto &execution = get_entry_point();
2320 // Avoid shadow declarations.
2321 local_variable_names = resource_names;
2322
2323 string decl;
2324
2325 auto &type = get<SPIRType>(func.return_type);
Hans-Kristian Arntzen00ccd592018-02-05 12:37:41 +01002326 if (type.array.empty())
2327 {
Hans-Kristian Arntzen2cc374a2019-04-24 14:12:50 +02002328 decl += flags_to_qualifiers_glsl(type, return_flags);
Hans-Kristian Arntzen00ccd592018-02-05 12:37:41 +01002329 decl += type_to_glsl(type);
2330 decl += " ";
2331 }
2332 else
2333 {
2334 // We cannot return arrays in HLSL, so "return" through an out variable.
2335 decl = "void ";
2336 }
Robert Konrad80fcf552016-08-14 21:33:32 +02002337
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002338 if (func.self == ir.default_entry_point)
Robert Konrad80fcf552016-08-14 21:33:32 +02002339 {
2340 if (execution.model == ExecutionModelVertex)
Robert Konrad80fcf552016-08-14 21:33:32 +02002341 decl += "vert_main";
Hans-Kristian Arntzene2bb5b82017-08-15 09:34:30 +02002342 else if (execution.model == ExecutionModelFragment)
Robert Konrad80fcf552016-08-14 21:33:32 +02002343 decl += "frag_main";
Hans-Kristian Arntzene2bb5b82017-08-15 09:34:30 +02002344 else if (execution.model == ExecutionModelGLCompute)
2345 decl += "comp_main";
2346 else
2347 SPIRV_CROSS_THROW("Unsupported execution model.");
Robert Konrad80fcf552016-08-14 21:33:32 +02002348 processing_entry_point = true;
2349 }
2350 else
2351 decl += to_name(func.self);
2352
2353 decl += "(";
Hans-Kristian Arntzena489ba72019-04-02 11:19:03 +02002354 SmallVector<string> arglist;
Hans-Kristian Arntzen00ccd592018-02-05 12:37:41 +01002355
2356 if (!type.array.empty())
2357 {
2358 // Fake array returns by writing to an out array instead.
Hans-Kristian Arntzenb778e162018-11-12 09:58:27 +01002359 string out_argument;
2360 out_argument += "out ";
2361 out_argument += type_to_glsl(type);
2362 out_argument += " ";
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01002363 out_argument += "spvReturnValue";
Hans-Kristian Arntzenb778e162018-11-12 09:58:27 +01002364 out_argument += type_to_array_glsl(type);
2365 arglist.push_back(move(out_argument));
Hans-Kristian Arntzen00ccd592018-02-05 12:37:41 +01002366 }
2367
Robert Konrad80fcf552016-08-14 21:33:32 +02002368 for (auto &arg : func.arguments)
2369 {
Hans-Kristian Arntzenb778e162018-11-12 09:58:27 +01002370 // Do not pass in separate images or samplers if we're remapping
2371 // to combined image samplers.
2372 if (skip_argument(arg.id))
2373 continue;
2374
Robert Konrad80fcf552016-08-14 21:33:32 +02002375 // Might change the variable name if it already exists in this function.
2376 // SPIRV OpName doesn't have any semantic effect, so it's valid for an implementation
2377 // to use same name for variables.
2378 // Since we want to make the GLSL debuggable and somewhat sane, use fallback names for variables which are duplicates.
2379 add_local_variable_name(arg.id);
2380
Hans-Kristian Arntzenb778e162018-11-12 09:58:27 +01002381 arglist.push_back(argument_decl(arg));
Hans-Kristian Arntzen686ac682017-05-07 13:22:16 +02002382
2383 // Flatten a combined sampler to two separate arguments in modern HLSL.
2384 auto &arg_type = get<SPIRType>(arg.type);
Hans-Kristian Arntzen9a527132018-03-09 15:26:36 +01002385 if (hlsl_options.shader_model > 30 && arg_type.basetype == SPIRType::SampledImage &&
2386 arg_type.image.dim != DimBuffer)
Hans-Kristian Arntzen686ac682017-05-07 13:22:16 +02002387 {
2388 // Manufacture automatic sampler arg for SampledImage texture
Bill Hollingsfd252b22021-11-08 15:59:45 -05002389 arglist.push_back(join(is_depth_image(arg_type, arg.id) ? "SamplerComparisonState " : "SamplerState ",
Hans-Kristian Arntzenb778e162018-11-12 09:58:27 +01002390 to_sampler_expression(arg.id), type_to_array_glsl(arg_type)));
Hans-Kristian Arntzen686ac682017-05-07 13:22:16 +02002391 }
2392
Robert Konrad80fcf552016-08-14 21:33:32 +02002393 // Hold a pointer to the parameter so we can invalidate the readonly field if needed.
2394 auto *var = maybe_get<SPIRVariable>(arg.id);
2395 if (var)
2396 var->parameter = &arg;
2397 }
2398
Hans-Kristian Arntzenb778e162018-11-12 09:58:27 +01002399 for (auto &arg : func.shadow_arguments)
2400 {
2401 // Might change the variable name if it already exists in this function.
2402 // SPIRV OpName doesn't have any semantic effect, so it's valid for an implementation
2403 // to use same name for variables.
2404 // Since we want to make the GLSL debuggable and somewhat sane, use fallback names for variables which are duplicates.
2405 add_local_variable_name(arg.id);
2406
2407 arglist.push_back(argument_decl(arg));
2408
2409 // Hold a pointer to the parameter so we can invalidate the readonly field if needed.
2410 auto *var = maybe_get<SPIRVariable>(arg.id);
2411 if (var)
2412 var->parameter = &arg;
2413 }
2414
2415 decl += merge(arglist);
Robert Konrad80fcf552016-08-14 21:33:32 +02002416 decl += ")";
2417 statement(decl);
2418}
2419
2420void CompilerHLSL::emit_hlsl_entry_point()
2421{
Hans-Kristian Arntzena489ba72019-04-02 11:19:03 +02002422 SmallVector<string> arguments;
Robert Konrad80fcf552016-08-14 21:33:32 +02002423
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01002424 if (require_input)
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002425 arguments.push_back("SPIRV_Cross_Input stage_input");
2426
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002427 auto &execution = get_entry_point();
Hans-Kristian Arntzene2bb5b82017-08-15 09:34:30 +02002428
msiglreithf4bde2b2017-11-21 14:51:03 +01002429 switch (execution.model)
2430 {
2431 case ExecutionModelGLCompute:
Hans-Kristian Arntzene2bb5b82017-08-15 09:34:30 +02002432 {
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02002433 SpecializationConstant wg_x, wg_y, wg_z;
2434 get_work_group_size_specialization_constants(wg_x, wg_y, wg_z);
2435
2436 uint32_t x = execution.workgroup_size.x;
2437 uint32_t y = execution.workgroup_size.y;
2438 uint32_t z = execution.workgroup_size.z;
2439
Hans-Kristian Arntzen7c83fc22022-01-05 15:53:51 +01002440 if (!execution.workgroup_size.constant && execution.flags.get(ExecutionModeLocalSizeId))
2441 {
2442 if (execution.workgroup_size.id_x)
2443 x = get<SPIRConstant>(execution.workgroup_size.id_x).scalar();
2444 if (execution.workgroup_size.id_y)
2445 y = get<SPIRConstant>(execution.workgroup_size.id_y).scalar();
2446 if (execution.workgroup_size.id_z)
2447 z = get<SPIRConstant>(execution.workgroup_size.id_z).scalar();
2448 }
2449
Hans-Kristian Arntzenfd6ff362018-11-01 10:53:00 +01002450 auto x_expr = wg_x.id ? get<SPIRConstant>(wg_x.id).specialization_constant_macro_name : to_string(x);
2451 auto y_expr = wg_y.id ? get<SPIRConstant>(wg_y.id).specialization_constant_macro_name : to_string(y);
2452 auto z_expr = wg_z.id ? get<SPIRConstant>(wg_z.id).specialization_constant_macro_name : to_string(z);
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02002453
Hans-Kristian Arntzenfd6ff362018-11-01 10:53:00 +01002454 statement("[numthreads(", x_expr, ", ", y_expr, ", ", z_expr, ")]");
msiglreithf4bde2b2017-11-21 14:51:03 +01002455 break;
2456 }
2457 case ExecutionModelFragment:
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002458 if (execution.flags.get(ExecutionModeEarlyFragmentTests))
msiglreithf4bde2b2017-11-21 14:51:03 +01002459 statement("[earlydepthstencil]");
2460 break;
2461 default:
2462 break;
Hans-Kristian Arntzene2bb5b82017-08-15 09:34:30 +02002463 }
2464
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002465 statement(require_output ? "SPIRV_Cross_Output " : "void ", "main(", merge(arguments), ")");
2466 begin_scope();
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01002467 bool legacy = hlsl_options.shader_model <= 30;
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002468
2469 // Copy builtins from entry point arguments to globals.
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002470 active_input_builtins.for_each_bit([&](uint32_t i) {
Hans-Kristian Arntzenc8d60912017-07-24 10:07:02 +02002471 auto builtin = builtin_to_glsl(static_cast<BuiltIn>(i), StorageClassInput);
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002472 switch (static_cast<BuiltIn>(i))
2473 {
2474 case BuiltInFragCoord:
2475 // VPOS in D3D9 is sampled at integer locations, apply half-pixel offset to be consistent.
2476 // TODO: Do we need an option here? Any reason why a D3D9 shader would be used
2477 // on a D3D10+ system with a different rasterization config?
2478 if (legacy)
2479 statement(builtin, " = stage_input.", builtin, " + float4(0.5f, 0.5f, 0.0f, 0.0f);");
2480 else
Hans-Kristian Arntzenfdbc80d2020-08-20 16:22:48 +02002481 {
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002482 statement(builtin, " = stage_input.", builtin, ";");
Hans-Kristian Arntzenfdbc80d2020-08-20 16:22:48 +02002483 // ZW are undefined in D3D9, only do this fixup here.
2484 statement(builtin, ".w = 1.0 / ", builtin, ".w;");
2485 }
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002486 break;
2487
Hans-Kristian Arntzen5e9b53e2017-12-06 11:01:32 +01002488 case BuiltInVertexId:
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002489 case BuiltInVertexIndex:
2490 case BuiltInInstanceIndex:
2491 // D3D semantics are uint, but shader wants int.
Hans-Kristian Arntzena2a44d92019-01-11 10:32:14 +01002492 if (hlsl_options.support_nonzero_base_vertex_base_instance)
2493 {
2494 if (static_cast<BuiltIn>(i) == BuiltInInstanceIndex)
2495 statement(builtin, " = int(stage_input.", builtin, ") + SPIRV_Cross_BaseInstance;");
2496 else
2497 statement(builtin, " = int(stage_input.", builtin, ") + SPIRV_Cross_BaseVertex;");
2498 }
2499 else
2500 statement(builtin, " = int(stage_input.", builtin, ");");
2501 break;
2502
2503 case BuiltInInstanceId:
2504 // D3D semantics are uint, but shader wants int.
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002505 statement(builtin, " = int(stage_input.", builtin, ");");
2506 break;
2507
Hans-Kristian Arntzen18e88332018-02-05 10:27:42 +01002508 case BuiltInNumWorkgroups:
Hans-Kristian Arntzenf6d08e62018-02-23 15:56:25 +01002509 case BuiltInPointCoord:
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02002510 case BuiltInSubgroupSize:
2511 case BuiltInSubgroupLocalInvocationId:
2512 break;
2513
2514 case BuiltInSubgroupEqMask:
2515 // Emulate these ...
2516 // No 64-bit in HLSL, so have to do it in 32-bit and unroll.
2517 statement("gl_SubgroupEqMask = 1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96));");
2518 statement("if (WaveGetLaneIndex() >= 32) gl_SubgroupEqMask.x = 0;");
2519 statement("if (WaveGetLaneIndex() >= 64 || WaveGetLaneIndex() < 32) gl_SubgroupEqMask.y = 0;");
2520 statement("if (WaveGetLaneIndex() >= 96 || WaveGetLaneIndex() < 64) gl_SubgroupEqMask.z = 0;");
2521 statement("if (WaveGetLaneIndex() < 96) gl_SubgroupEqMask.w = 0;");
2522 break;
2523
2524 case BuiltInSubgroupGeMask:
2525 // Emulate these ...
2526 // No 64-bit in HLSL, so have to do it in 32-bit and unroll.
2527 statement("gl_SubgroupGeMask = ~((1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96))) - 1u);");
2528 statement("if (WaveGetLaneIndex() >= 32) gl_SubgroupGeMask.x = 0u;");
2529 statement("if (WaveGetLaneIndex() >= 64) gl_SubgroupGeMask.y = 0u;");
2530 statement("if (WaveGetLaneIndex() >= 96) gl_SubgroupGeMask.z = 0u;");
2531 statement("if (WaveGetLaneIndex() < 32) gl_SubgroupGeMask.y = ~0u;");
2532 statement("if (WaveGetLaneIndex() < 64) gl_SubgroupGeMask.z = ~0u;");
2533 statement("if (WaveGetLaneIndex() < 96) gl_SubgroupGeMask.w = ~0u;");
2534 break;
2535
2536 case BuiltInSubgroupGtMask:
2537 // Emulate these ...
2538 // No 64-bit in HLSL, so have to do it in 32-bit and unroll.
2539 statement("uint gt_lane_index = WaveGetLaneIndex() + 1;");
2540 statement("gl_SubgroupGtMask = ~((1u << (gt_lane_index - uint4(0, 32, 64, 96))) - 1u);");
2541 statement("if (gt_lane_index >= 32) gl_SubgroupGtMask.x = 0u;");
2542 statement("if (gt_lane_index >= 64) gl_SubgroupGtMask.y = 0u;");
2543 statement("if (gt_lane_index >= 96) gl_SubgroupGtMask.z = 0u;");
2544 statement("if (gt_lane_index >= 128) gl_SubgroupGtMask.w = 0u;");
2545 statement("if (gt_lane_index < 32) gl_SubgroupGtMask.y = ~0u;");
2546 statement("if (gt_lane_index < 64) gl_SubgroupGtMask.z = ~0u;");
2547 statement("if (gt_lane_index < 96) gl_SubgroupGtMask.w = ~0u;");
2548 break;
2549
2550 case BuiltInSubgroupLeMask:
2551 // Emulate these ...
2552 // No 64-bit in HLSL, so have to do it in 32-bit and unroll.
2553 statement("uint le_lane_index = WaveGetLaneIndex() + 1;");
2554 statement("gl_SubgroupLeMask = (1u << (le_lane_index - uint4(0, 32, 64, 96))) - 1u;");
2555 statement("if (le_lane_index >= 32) gl_SubgroupLeMask.x = ~0u;");
2556 statement("if (le_lane_index >= 64) gl_SubgroupLeMask.y = ~0u;");
2557 statement("if (le_lane_index >= 96) gl_SubgroupLeMask.z = ~0u;");
2558 statement("if (le_lane_index >= 128) gl_SubgroupLeMask.w = ~0u;");
2559 statement("if (le_lane_index < 32) gl_SubgroupLeMask.y = 0u;");
2560 statement("if (le_lane_index < 64) gl_SubgroupLeMask.z = 0u;");
2561 statement("if (le_lane_index < 96) gl_SubgroupLeMask.w = 0u;");
2562 break;
2563
2564 case BuiltInSubgroupLtMask:
2565 // Emulate these ...
2566 // No 64-bit in HLSL, so have to do it in 32-bit and unroll.
2567 statement("gl_SubgroupLtMask = (1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96))) - 1u;");
2568 statement("if (WaveGetLaneIndex() >= 32) gl_SubgroupLtMask.x = ~0u;");
2569 statement("if (WaveGetLaneIndex() >= 64) gl_SubgroupLtMask.y = ~0u;");
2570 statement("if (WaveGetLaneIndex() >= 96) gl_SubgroupLtMask.z = ~0u;");
2571 statement("if (WaveGetLaneIndex() < 32) gl_SubgroupLtMask.y = 0u;");
2572 statement("if (WaveGetLaneIndex() < 64) gl_SubgroupLtMask.z = 0u;");
2573 statement("if (WaveGetLaneIndex() < 96) gl_SubgroupLtMask.w = 0u;");
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +01002574 break;
2575
2576 case BuiltInClipDistance:
2577 for (uint32_t clip = 0; clip < clip_distance_count; clip++)
Hans-Kristian Arntzen4543dac2018-02-23 13:13:02 +01002578 statement("gl_ClipDistance[", clip, "] = stage_input.gl_ClipDistance", clip / 4, ".", "xyzw"[clip & 3],
2579 ";");
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +01002580 break;
2581
2582 case BuiltInCullDistance:
Hans-Kristian Arntzen0673f272018-02-22 17:00:41 +01002583 for (uint32_t cull = 0; cull < cull_distance_count; cull++)
Hans-Kristian Arntzen4543dac2018-02-23 13:13:02 +01002584 statement("gl_CullDistance[", cull, "] = stage_input.gl_CullDistance", cull / 4, ".", "xyzw"[cull & 3],
2585 ";");
Hans-Kristian Arntzen18e88332018-02-05 10:27:42 +01002586 break;
2587
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002588 default:
2589 statement(builtin, " = stage_input.", builtin, ";");
2590 break;
2591 }
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002592 });
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002593
2594 // Copy from stage input struct to globals.
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01002595 ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
Hans-Kristian Arntzen2fb9aa22019-01-11 09:29:28 +01002596 auto &type = this->get<SPIRType>(var.basetype);
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02002597 bool block = has_decoration(type.self, DecorationBlock);
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01002598
2599 if (var.storage != StorageClassInput)
2600 return;
2601
2602 bool need_matrix_unroll = var.storage == StorageClassInput && execution.model == ExecutionModelVertex;
2603
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02002604 if (!var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01002605 interface_variable_exists_in_entry_point(var.self))
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002606 {
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02002607 if (block)
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002608 {
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02002609 auto type_name = to_name(type.self);
2610 auto var_name = to_name(var.self);
2611 for (uint32_t mbr_idx = 0; mbr_idx < uint32_t(type.member_types.size()); mbr_idx++)
2612 {
2613 auto mbr_name = to_member_name(type, mbr_idx);
2614 auto flat_name = join(type_name, "_", mbr_name);
2615 statement(var_name, ".", mbr_name, " = stage_input.", flat_name, ";");
2616 }
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002617 }
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01002618 else
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002619 {
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02002620 auto name = to_name(var.self);
2621 auto &mtype = this->get<SPIRType>(var.basetype);
2622 if (need_matrix_unroll && mtype.columns > 1)
2623 {
2624 // Unroll matrices.
2625 for (uint32_t col = 0; col < mtype.columns; col++)
2626 statement(name, "[", col, "] = stage_input.", name, "_", col, ";");
2627 }
2628 else
2629 {
2630 statement(name, " = stage_input.", name, ";");
2631 }
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002632 }
2633 }
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01002634 });
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002635
2636 // Run the shader.
Robert Konrad95a716f2016-08-16 00:27:39 +02002637 if (execution.model == ExecutionModelVertex)
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01002638 statement("vert_main();");
2639 else if (execution.model == ExecutionModelFragment)
2640 statement("frag_main();");
Hans-Kristian Arntzene2bb5b82017-08-15 09:34:30 +02002641 else if (execution.model == ExecutionModelGLCompute)
2642 statement("comp_main();");
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01002643 else
2644 SPIRV_CROSS_THROW("Unsupported shader stage.");
Robert Konrad95a716f2016-08-16 00:27:39 +02002645
Hans-Kristian Arntzen439fa9f2017-03-07 16:18:43 +01002646 // Copy stage outputs.
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01002647 if (require_output)
2648 {
2649 statement("SPIRV_Cross_Output stage_output;");
2650
2651 // Copy builtins from globals to return struct.
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002652 active_output_builtins.for_each_bit([&](uint32_t i) {
Hans-Kristian Arntzen17d88ca2017-05-04 10:10:30 +02002653 // PointSize doesn't exist in HLSL.
2654 if (i == BuiltInPointSize)
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002655 return;
Hans-Kristian Arntzen17d88ca2017-05-04 10:10:30 +02002656
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +01002657 switch (static_cast<BuiltIn>(i))
2658 {
2659 case BuiltInClipDistance:
2660 for (uint32_t clip = 0; clip < clip_distance_count; clip++)
Hans-Kristian Arntzen4543dac2018-02-23 13:13:02 +01002661 statement("stage_output.gl_ClipDistance", clip / 4, ".", "xyzw"[clip & 3], " = gl_ClipDistance[",
2662 clip, "];");
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +01002663 break;
2664
2665 case BuiltInCullDistance:
2666 for (uint32_t cull = 0; cull < cull_distance_count; cull++)
Hans-Kristian Arntzen4543dac2018-02-23 13:13:02 +01002667 statement("stage_output.gl_CullDistance", cull / 4, ".", "xyzw"[cull & 3], " = gl_CullDistance[",
2668 cull, "];");
Hans-Kristian Arntzen114c2c52018-02-22 16:40:04 +01002669 break;
2670
2671 default:
2672 {
2673 auto builtin_expr = builtin_to_glsl(static_cast<BuiltIn>(i), StorageClassOutput);
2674 statement("stage_output.", builtin_expr, " = ", builtin_expr, ";");
2675 break;
2676 }
2677 }
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01002678 });
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01002679
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01002680 ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
Hans-Kristian Arntzen2fb9aa22019-01-11 09:29:28 +01002681 auto &type = this->get<SPIRType>(var.basetype);
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02002682 bool block = has_decoration(type.self, DecorationBlock);
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01002683
2684 if (var.storage != StorageClassOutput)
2685 return;
2686
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02002687 if (!var.remapped_variable && type.pointer &&
2688 !is_builtin_variable(var) &&
2689 interface_variable_exists_in_entry_point(var.self))
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01002690 {
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02002691 if (block)
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01002692 {
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02002693 // I/O blocks need to flatten output.
2694 auto type_name = to_name(type.self);
2695 auto var_name = to_name(var.self);
2696 for (uint32_t mbr_idx = 0; mbr_idx < uint32_t(type.member_types.size()); mbr_idx++)
2697 {
2698 auto mbr_name = to_member_name(type, mbr_idx);
2699 auto flat_name = join(type_name, "_", mbr_name);
2700 statement("stage_output.", flat_name, " = ", var_name, ".", mbr_name, ";");
2701 }
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01002702 }
2703 else
2704 {
Hans-Kristian Arntzend6b29ab2021-06-28 14:24:29 +02002705 auto name = to_name(var.self);
2706
2707 if (legacy && execution.model == ExecutionModelFragment)
2708 {
2709 string output_filler;
2710 for (uint32_t size = type.vecsize; size < 4; ++size)
2711 output_filler += ", 0.0";
2712
2713 statement("stage_output.", name, " = float4(", name, output_filler, ");");
2714 }
2715 else
2716 {
2717 statement("stage_output.", name, " = ", name, ";");
2718 }
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01002719 }
2720 }
Hans-Kristian Arntzend92de002019-01-10 09:49:33 +01002721 });
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01002722
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01002723 statement("return stage_output;");
2724 }
Robert Konrad80fcf552016-08-14 21:33:32 +02002725
2726 end_scope();
Robert Konradd2b29c92016-08-14 23:09:06 +02002727}
Robert Konrad80fcf552016-08-14 21:33:32 +02002728
Hans-Kristian Arntzenbdfa97a2017-08-03 13:02:59 +02002729void CompilerHLSL::emit_fixup()
2730{
Hans-Kristian Arntzen5ea576e2020-09-28 14:09:39 +02002731 if (is_vertex_like_shader())
Hans-Kristian Arntzenbdfa97a2017-08-03 13:02:59 +02002732 {
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02002733 // Do various mangling on the gl_Position.
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01002734 if (hlsl_options.shader_model <= 30)
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02002735 {
2736 statement("gl_Position.x = gl_Position.x - gl_HalfPixel.x * "
Hans-Kristian Arntzen153fed02017-09-28 13:28:44 +02002737 "gl_Position.w;");
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02002738 statement("gl_Position.y = gl_Position.y + gl_HalfPixel.y * "
Hans-Kristian Arntzen153fed02017-09-28 13:28:44 +02002739 "gl_Position.w;");
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02002740 }
Hans-Kristian Arntzenbdfa97a2017-08-03 13:02:59 +02002741
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01002742 if (options.vertex.flip_vert_y)
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02002743 statement("gl_Position.y = -gl_Position.y;");
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01002744 if (options.vertex.fixup_clipspace)
Hans-Kristian Arntzen86eb8742017-09-28 11:33:30 +02002745 statement("gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5;");
2746 }
Hans-Kristian Arntzenbdfa97a2017-08-03 13:02:59 +02002747}
2748
Hans-Kristian Arntzen275974e2020-06-04 15:50:28 +02002749void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse)
Robert Konradd2b29c92016-08-14 23:09:06 +02002750{
Hans-Kristian Arntzen275974e2020-06-04 15:50:28 +02002751 if (sparse)
2752 SPIRV_CROSS_THROW("Sparse feedback not yet supported in HLSL.");
2753
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02002754 auto *ops = stream(i);
Robert Konradd2b29c92016-08-14 23:09:06 +02002755 auto op = static_cast<Op>(i.op);
2756 uint32_t length = i.length;
2757
Hans-Kristian Arntzena489ba72019-04-02 11:19:03 +02002758 SmallVector<uint32_t> inherited_expressions;
Hans-Kristian Arntzene0efa732018-03-09 13:21:38 +01002759
Robert Konradd2b29c92016-08-14 23:09:06 +02002760 uint32_t result_type = ops[0];
2761 uint32_t id = ops[1];
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02002762 VariableID img = ops[2];
Robert Konradd2b29c92016-08-14 23:09:06 +02002763 uint32_t coord = ops[3];
2764 uint32_t dref = 0;
2765 uint32_t comp = 0;
2766 bool gather = false;
2767 bool proj = false;
2768 const uint32_t *opt = nullptr;
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02002769 auto *combined_image = maybe_get<SPIRCombinedImageSampler>(img);
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02002770
2771 if (combined_image && has_decoration(img, DecorationNonUniform))
2772 {
2773 set_decoration(combined_image->image, DecorationNonUniform);
2774 set_decoration(combined_image->sampler, DecorationNonUniform);
2775 }
2776
2777 auto img_expr = to_non_uniform_aware_expression(combined_image ? combined_image->image : img);
Robert Konradd2b29c92016-08-14 23:09:06 +02002778
Hans-Kristian Arntzene0efa732018-03-09 13:21:38 +01002779 inherited_expressions.push_back(coord);
2780
Robert Konradd2b29c92016-08-14 23:09:06 +02002781 switch (op)
2782 {
2783 case OpImageSampleDrefImplicitLod:
2784 case OpImageSampleDrefExplicitLod:
2785 dref = ops[4];
2786 opt = &ops[5];
2787 length -= 5;
2788 break;
2789
2790 case OpImageSampleProjDrefImplicitLod:
2791 case OpImageSampleProjDrefExplicitLod:
2792 dref = ops[4];
2793 proj = true;
2794 opt = &ops[5];
2795 length -= 5;
2796 break;
2797
2798 case OpImageDrefGather:
2799 dref = ops[4];
2800 opt = &ops[5];
2801 gather = true;
2802 length -= 5;
2803 break;
2804
2805 case OpImageGather:
2806 comp = ops[4];
2807 opt = &ops[5];
2808 gather = true;
2809 length -= 5;
2810 break;
2811
2812 case OpImageSampleProjImplicitLod:
2813 case OpImageSampleProjExplicitLod:
2814 opt = &ops[4];
2815 length -= 4;
2816 proj = true;
2817 break;
2818
Hans-Kristian Arntzenaeeb0e32017-12-01 13:28:51 +01002819 case OpImageQueryLod:
2820 opt = &ops[4];
2821 length -= 4;
2822 break;
2823
Robert Konradd2b29c92016-08-14 23:09:06 +02002824 default:
2825 opt = &ops[4];
2826 length -= 4;
2827 break;
2828 }
2829
2830 auto &imgtype = expression_type(img);
2831 uint32_t coord_components = 0;
2832 switch (imgtype.image.dim)
2833 {
2834 case spv::Dim1D:
2835 coord_components = 1;
2836 break;
2837 case spv::Dim2D:
2838 coord_components = 2;
2839 break;
2840 case spv::Dim3D:
2841 coord_components = 3;
2842 break;
2843 case spv::DimCube:
2844 coord_components = 3;
2845 break;
2846 case spv::DimBuffer:
2847 coord_components = 1;
2848 break;
2849 default:
2850 coord_components = 2;
2851 break;
2852 }
2853
Hans-Kristian Arntzene0efa732018-03-09 13:21:38 +01002854 if (dref)
2855 inherited_expressions.push_back(dref);
2856
Robert Konradd2b29c92016-08-14 23:09:06 +02002857 if (imgtype.image.arrayed)
2858 coord_components++;
2859
2860 uint32_t bias = 0;
2861 uint32_t lod = 0;
2862 uint32_t grad_x = 0;
2863 uint32_t grad_y = 0;
2864 uint32_t coffset = 0;
2865 uint32_t offset = 0;
2866 uint32_t coffsets = 0;
2867 uint32_t sample = 0;
Hans-Kristian Arntzenf171d822019-06-11 11:10:16 +02002868 uint32_t minlod = 0;
Robert Konradd2b29c92016-08-14 23:09:06 +02002869 uint32_t flags = 0;
2870
2871 if (length)
2872 {
2873 flags = opt[0];
2874 opt++;
2875 length--;
2876 }
2877
2878 auto test = [&](uint32_t &v, uint32_t flag) {
2879 if (length && (flags & flag))
2880 {
2881 v = *opt++;
Hans-Kristian Arntzene0efa732018-03-09 13:21:38 +01002882 inherited_expressions.push_back(v);
Robert Konradd2b29c92016-08-14 23:09:06 +02002883 length--;
2884 }
2885 };
2886
2887 test(bias, ImageOperandsBiasMask);
2888 test(lod, ImageOperandsLodMask);
2889 test(grad_x, ImageOperandsGradMask);
2890 test(grad_y, ImageOperandsGradMask);
2891 test(coffset, ImageOperandsConstOffsetMask);
2892 test(offset, ImageOperandsOffsetMask);
2893 test(coffsets, ImageOperandsConstOffsetsMask);
2894 test(sample, ImageOperandsSampleMask);
Hans-Kristian Arntzenf171d822019-06-11 11:10:16 +02002895 test(minlod, ImageOperandsMinLodMask);
Robert Konradd2b29c92016-08-14 23:09:06 +02002896
2897 string expr;
2898 string texop;
2899
Hans-Kristian Arntzenf171d822019-06-11 11:10:16 +02002900 if (minlod != 0)
2901 SPIRV_CROSS_THROW("MinLod texture operand not supported in HLSL.");
2902
Robert Konradd2b29c92016-08-14 23:09:06 +02002903 if (op == OpImageFetch)
Robert Konradbb9dbd42017-04-24 11:08:55 +02002904 {
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01002905 if (hlsl_options.shader_model < 40)
Robert Konradbb9dbd42017-04-24 11:08:55 +02002906 {
2907 SPIRV_CROSS_THROW("texelFetch is not supported in HLSL shader model 2/3.");
2908 }
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02002909 texop += img_expr;
Robert Konradbb9dbd42017-04-24 11:08:55 +02002910 texop += ".Load";
2911 }
Hans-Kristian Arntzenaeeb0e32017-12-01 13:28:51 +01002912 else if (op == OpImageQueryLod)
2913 {
2914 texop += img_expr;
2915 texop += ".CalculateLevelOfDetail";
2916 }
Robert Konradd2b29c92016-08-14 23:09:06 +02002917 else
2918 {
Robert Konrad9aaf6b22017-04-21 11:40:24 +02002919 auto &imgformat = get<SPIRType>(imgtype.image.type);
2920 if (imgformat.basetype != SPIRType::Float)
2921 {
2922 SPIRV_CROSS_THROW("Sampling non-float textures is not supported in HLSL.");
2923 }
2924
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01002925 if (hlsl_options.shader_model >= 40)
Robert Konradc5953e02017-04-18 14:14:48 +02002926 {
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02002927 texop += img_expr;
Robert Konradec84e882017-04-18 14:55:38 +02002928
Bill Hollingsfd252b22021-11-08 15:59:45 -05002929 if (is_depth_image(imgtype, img))
Hans-Kristian Arntzendbfa6862017-11-29 12:38:13 +01002930 {
2931 if (gather)
2932 {
2933 SPIRV_CROSS_THROW("GatherCmp does not exist in HLSL.");
2934 }
2935 else if (lod || grad_x || grad_y)
2936 {
2937 // Assume we want a fixed level, and the only thing we can get in HLSL is SampleCmpLevelZero.
2938 texop += ".SampleCmpLevelZero";
2939 }
2940 else
2941 texop += ".SampleCmp";
2942 }
Robert Konrad7d8be832017-04-21 17:52:04 +02002943 else if (gather)
James Ross-Gowan02e6be72017-09-12 07:20:01 +10002944 {
Hans-Kristian Arntzen66afe8c2020-09-14 10:42:31 +02002945 uint32_t comp_num = evaluate_constant_u32(comp);
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01002946 if (hlsl_options.shader_model >= 50)
James Ross-Gowan02e6be72017-09-12 07:20:01 +10002947 {
2948 switch (comp_num)
2949 {
2950 case 0:
2951 texop += ".GatherRed";
2952 break;
2953 case 1:
2954 texop += ".GatherGreen";
2955 break;
2956 case 2:
2957 texop += ".GatherBlue";
2958 break;
2959 case 3:
2960 texop += ".GatherAlpha";
2961 break;
2962 default:
2963 SPIRV_CROSS_THROW("Invalid component.");
2964 }
2965 }
2966 else
2967 {
2968 if (comp_num == 0)
2969 texop += ".Gather";
2970 else
2971 SPIRV_CROSS_THROW("HLSL shader model 4 can only gather from the red component.");
2972 }
2973 }
Robert Konradde41ebf2017-04-20 16:04:02 +02002974 else if (bias)
Robert Konradec84e882017-04-18 14:55:38 +02002975 texop += ".SampleBias";
Robert Konradec84e882017-04-18 14:55:38 +02002976 else if (grad_x || grad_y)
2977 texop += ".SampleGrad";
2978 else if (lod)
2979 texop += ".SampleLevel";
2980 else
2981 texop += ".Sample";
Robert Konradc5953e02017-04-18 14:14:48 +02002982 }
2983 else
2984 {
Robert Konradff12d572017-04-21 14:35:30 +02002985 switch (imgtype.image.dim)
2986 {
2987 case Dim1D:
2988 texop += "tex1D";
2989 break;
2990 case Dim2D:
2991 texop += "tex2D";
2992 break;
2993 case Dim3D:
2994 texop += "tex3D";
2995 break;
2996 case DimCube:
Robert Konrad84466312017-04-21 14:54:03 +02002997 texop += "texCUBE";
2998 break;
Robert Konradff12d572017-04-21 14:35:30 +02002999 case DimRect:
3000 case DimBuffer:
3001 case DimSubpassData:
Robert Konrad84466312017-04-21 14:54:03 +02003002 SPIRV_CROSS_THROW("Buffer texture support is not yet implemented for HLSL"); // TODO
Hans-Kristian Arntzen08b3c672017-05-09 09:30:30 +02003003 default:
3004 SPIRV_CROSS_THROW("Invalid dimension.");
Robert Konradff12d572017-04-21 14:35:30 +02003005 }
Robert Konradd2b29c92016-08-14 23:09:06 +02003006
Robert Konradc5953e02017-04-18 14:14:48 +02003007 if (gather)
Robert Konradec84e882017-04-18 14:55:38 +02003008 SPIRV_CROSS_THROW("textureGather is not supported in HLSL shader model 2/3.");
Robert Konrad61207512017-04-20 16:15:46 +02003009 if (offset || coffset)
Robert Konradec84e882017-04-18 14:55:38 +02003010 SPIRV_CROSS_THROW("textureOffset is not supported in HLSL shader model 2/3.");
rdb18893ba2020-10-31 12:06:15 +01003011
Robert Konradc5953e02017-04-18 14:14:48 +02003012 if (grad_x || grad_y)
Robert Konradec84e882017-04-18 14:55:38 +02003013 texop += "grad";
rdb18893ba2020-10-31 12:06:15 +01003014 else if (lod)
Robert Konradec84e882017-04-18 14:55:38 +02003015 texop += "lod";
rdb18893ba2020-10-31 12:06:15 +01003016 else if (bias)
Robert Konradec84e882017-04-18 14:55:38 +02003017 texop += "bias";
rdb18893ba2020-10-31 12:06:15 +01003018 else if (proj || dref)
3019 texop += "proj";
Robert Konradc5953e02017-04-18 14:14:48 +02003020 }
Robert Konradd2b29c92016-08-14 23:09:06 +02003021 }
3022
Robert Konradd2b29c92016-08-14 23:09:06 +02003023 expr += texop;
Robert Konrad61207512017-04-20 16:15:46 +02003024 expr += "(";
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01003025 if (hlsl_options.shader_model < 40)
Robert Konrad111a30a2017-05-31 16:53:43 +02003026 {
3027 if (combined_image)
3028 SPIRV_CROSS_THROW("Separate images/samplers are not supported in HLSL shader model 2/3.");
3029 expr += to_expression(img);
3030 }
3031 else if (op != OpImageFetch)
Robert Konrad61207512017-04-20 16:15:46 +02003032 {
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003033 string sampler_expr;
3034 if (combined_image)
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02003035 sampler_expr = to_non_uniform_aware_expression(combined_image->sampler);
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003036 else
Hans-Kristian Arntzen686ac682017-05-07 13:22:16 +02003037 sampler_expr = to_sampler_expression(img);
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003038 expr += sampler_expr;
Robert Konradc5953e02017-04-18 14:14:48 +02003039 }
Robert Konradd2b29c92016-08-14 23:09:06 +02003040
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003041 auto swizzle = [](uint32_t comps, uint32_t in_comps) -> const char * {
Robert Konradd2b29c92016-08-14 23:09:06 +02003042 if (comps == in_comps)
3043 return "";
3044
3045 switch (comps)
3046 {
3047 case 1:
3048 return ".x";
3049 case 2:
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003050 return ".xy";
Robert Konradd2b29c92016-08-14 23:09:06 +02003051 case 3:
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003052 return ".xyz";
Robert Konradd2b29c92016-08-14 23:09:06 +02003053 default:
3054 return "";
3055 }
3056 };
3057
3058 bool forward = should_forward(coord);
3059
3060 // The IR can give us more components than we need, so chop them off as needed.
Hans-Kristian Arntzen18a4acc2019-01-28 09:39:45 +01003061 string coord_expr;
Hans-Kristian Arntzene4d5c612019-04-03 10:50:32 +02003062 auto &coord_type = expression_type(coord);
3063 if (coord_components != coord_type.vecsize)
Hans-Kristian Arntzen18a4acc2019-01-28 09:39:45 +01003064 coord_expr = to_enclosed_expression(coord) + swizzle(coord_components, expression_type(coord).vecsize);
3065 else
3066 coord_expr = to_expression(coord);
Robert Konradd2b29c92016-08-14 23:09:06 +02003067
Hans-Kristian Arntzen18a4acc2019-01-28 09:39:45 +01003068 if (proj && hlsl_options.shader_model >= 40) // Legacy HLSL has "proj" operations which do this for us.
3069 coord_expr = coord_expr + " / " + to_extract_component_expression(coord, coord_components);
Robert Konradfd9b5892017-04-20 13:37:38 +02003070
Hans-Kristian Arntzend13dc0c2020-06-16 12:54:22 +02003071 if (hlsl_options.shader_model < 40)
Robert Konrad1fe652d2017-04-21 09:46:58 +02003072 {
rdb18893ba2020-10-31 12:06:15 +01003073 if (dref)
3074 {
3075 if (imgtype.image.dim != spv::Dim1D && imgtype.image.dim != spv::Dim2D)
Hans-Kristian Arntzen6fc2a052020-11-08 13:59:52 +01003076 {
3077 SPIRV_CROSS_THROW(
3078 "Depth comparison is only supported for 1D and 2D textures in HLSL shader model 2/3.");
3079 }
rdb18893ba2020-10-31 12:06:15 +01003080
3081 if (grad_x || grad_y)
3082 SPIRV_CROSS_THROW("Depth comparison is not supported for grad sampling in HLSL shader model 2/3.");
3083
3084 for (uint32_t size = coord_components; size < 2; ++size)
3085 coord_expr += ", 0.0";
3086
3087 forward = forward && should_forward(dref);
3088 coord_expr += ", " + to_expression(dref);
3089 }
3090 else if (lod || bias || proj)
3091 {
3092 for (uint32_t size = coord_components; size < 3; ++size)
3093 coord_expr += ", 0.0";
3094 }
Robert Konrad1fe652d2017-04-21 09:46:58 +02003095
Hans-Kristian Arntzend13dc0c2020-06-16 12:54:22 +02003096 if (lod)
Robert Konradff12d572017-04-21 14:35:30 +02003097 {
rdb18893ba2020-10-31 12:06:15 +01003098 coord_expr = "float4(" + coord_expr + ", " + to_expression(lod) + ")";
Robert Konradff12d572017-04-21 14:35:30 +02003099 }
rdb18893ba2020-10-31 12:06:15 +01003100 else if (bias)
Hans-Kristian Arntzend13dc0c2020-06-16 12:54:22 +02003101 {
rdb18893ba2020-10-31 12:06:15 +01003102 coord_expr = "float4(" + coord_expr + ", " + to_expression(bias) + ")";
Hans-Kristian Arntzend13dc0c2020-06-16 12:54:22 +02003103 }
rdb18893ba2020-10-31 12:06:15 +01003104 else if (proj)
Hans-Kristian Arntzend13dc0c2020-06-16 12:54:22 +02003105 {
Hans-Kristian Arntzen6fc2a052020-11-08 13:59:52 +01003106 coord_expr = "float4(" + coord_expr + ", " + to_extract_component_expression(coord, coord_components) + ")";
rdb18893ba2020-10-31 12:06:15 +01003107 }
3108 else if (dref)
3109 {
3110 // A "normal" sample gets fed into tex2Dproj as well, because the
3111 // regular tex2D accepts only two coordinates.
3112 coord_expr = "float4(" + coord_expr + ", 1.0)";
Hans-Kristian Arntzend13dc0c2020-06-16 12:54:22 +02003113 }
3114
rdb18893ba2020-10-31 12:06:15 +01003115 if (!!lod + !!bias + !!proj > 1)
Hans-Kristian Arntzend13dc0c2020-06-16 12:54:22 +02003116 SPIRV_CROSS_THROW("Legacy HLSL can only use one of lod/bias/proj modifiers.");
Robert Konrad1fe652d2017-04-21 09:46:58 +02003117 }
3118
Robert Konradbb9dbd42017-04-24 11:08:55 +02003119 if (op == OpImageFetch)
3120 {
Hans-Kristian Arntzend93807a2018-04-30 10:53:21 +02003121 if (imgtype.image.dim != DimBuffer && !imgtype.image.ms)
3122 coord_expr =
Hans-Kristian Arntzene4d5c612019-04-03 10:50:32 +02003123 join("int", coord_components + 1, "(", coord_expr, ", ", lod ? to_expression(lod) : string("0"), ")");
Robert Konradbb9dbd42017-04-24 11:08:55 +02003124 }
Hans-Kristian Arntzenaeeb0e32017-12-01 13:28:51 +01003125 else
Robert Konradbb9dbd42017-04-24 11:08:55 +02003126 expr += ", ";
Robert Konrad7d8be832017-04-21 17:52:04 +02003127 expr += coord_expr;
Bill Hollings012cb252017-04-25 11:25:50 -04003128
rdb18893ba2020-10-31 12:06:15 +01003129 if (dref && hlsl_options.shader_model >= 40)
Robert Konradd2b29c92016-08-14 23:09:06 +02003130 {
3131 forward = forward && should_forward(dref);
Robert Konradd2b29c92016-08-14 23:09:06 +02003132 expr += ", ";
Hans-Kristian Arntzen18a4acc2019-01-28 09:39:45 +01003133
3134 if (proj)
3135 expr += to_enclosed_expression(dref) + " / " + to_extract_component_expression(coord, coord_components);
3136 else
3137 expr += to_expression(dref);
Robert Konradd2b29c92016-08-14 23:09:06 +02003138 }
3139
Hans-Kristian Arntzendbfa6862017-11-29 12:38:13 +01003140 if (!dref && (grad_x || grad_y))
Robert Konradd2b29c92016-08-14 23:09:06 +02003141 {
3142 forward = forward && should_forward(grad_x);
3143 forward = forward && should_forward(grad_y);
3144 expr += ", ";
3145 expr += to_expression(grad_x);
3146 expr += ", ";
3147 expr += to_expression(grad_y);
3148 }
3149
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01003150 if (!dref && lod && hlsl_options.shader_model >= 40 && op != OpImageFetch)
Robert Konradd2b29c92016-08-14 23:09:06 +02003151 {
3152 forward = forward && should_forward(lod);
3153 expr += ", ";
3154 expr += to_expression(lod);
3155 }
3156
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01003157 if (!dref && bias && hlsl_options.shader_model >= 40)
Robert Konradec84e882017-04-18 14:55:38 +02003158 {
3159 forward = forward && should_forward(bias);
3160 expr += ", ";
3161 expr += to_expression(bias);
3162 }
3163
Robert Konradd2b29c92016-08-14 23:09:06 +02003164 if (coffset)
3165 {
3166 forward = forward && should_forward(coffset);
3167 expr += ", ";
3168 expr += to_expression(coffset);
3169 }
3170 else if (offset)
3171 {
3172 forward = forward && should_forward(offset);
3173 expr += ", ";
3174 expr += to_expression(offset);
3175 }
3176
Robert Konradd2b29c92016-08-14 23:09:06 +02003177 if (sample)
3178 {
3179 expr += ", ";
3180 expr += to_expression(sample);
3181 }
3182
3183 expr += ")";
3184
rdb18893ba2020-10-31 12:06:15 +01003185 if (dref && hlsl_options.shader_model < 40)
3186 expr += ".x";
3187
Hans-Kristian Arntzenaeeb0e32017-12-01 13:28:51 +01003188 if (op == OpImageQueryLod)
3189 {
3190 // This is rather awkward.
3191 // textureQueryLod returns two values, the "accessed level",
3192 // as well as the actual LOD lambda.
3193 // As far as I can tell, there is no way to get the .x component
3194 // according to GLSL spec, and it depends on the sampler itself.
3195 // Just assume X == Y, so we will need to splat the result to a float2.
3196 statement("float _", id, "_tmp = ", expr, ";");
Hans-Kristian Arntzen4bc87292019-07-24 11:34:28 +02003197 statement("float2 _", id, " = _", id, "_tmp.xx;");
3198 set<SPIRExpression>(id, join("_", id), result_type, true);
Hans-Kristian Arntzenaeeb0e32017-12-01 13:28:51 +01003199 }
3200 else
3201 {
3202 emit_op(result_type, id, expr, forward, false);
3203 }
Hans-Kristian Arntzene0efa732018-03-09 13:21:38 +01003204
3205 for (auto &inherit : inherited_expressions)
3206 inherit_expression_dependencies(id, inherit);
Hans-Kristian Arntzen938c7de2018-03-12 17:34:54 +01003207
3208 switch (op)
3209 {
3210 case OpImageSampleDrefImplicitLod:
3211 case OpImageSampleImplicitLod:
3212 case OpImageSampleProjImplicitLod:
3213 case OpImageSampleProjDrefImplicitLod:
Hans-Kristian Arntzen938c7de2018-03-12 17:34:54 +01003214 register_control_dependent_expression(id);
3215 break;
3216
3217 default:
3218 break;
3219 }
Robert Konrad80fcf552016-08-14 21:33:32 +02003220}
3221
Hans-Kristian Arntzen620da7b2017-06-17 10:15:32 +02003222string CompilerHLSL::to_resource_binding(const SPIRVariable &var)
3223{
Hans-Kristian Arntzencc153f82020-01-09 11:18:14 +01003224 const auto &type = get<SPIRType>(var.basetype);
Hans-Kristian Arntzen620da7b2017-06-17 10:15:32 +02003225
Hans-Kristian Arntzencc153f82020-01-09 11:18:14 +01003226 // We can remap push constant blocks, even if they don't have any binding decoration.
3227 if (type.storage != StorageClassPushConstant && !has_decoration(var.self, DecorationBinding))
Hans-Kristian Arntzen620da7b2017-06-17 10:15:32 +02003228 return "";
3229
msiglreithd096f5c2017-11-27 16:00:56 +01003230 char space = '\0';
Hans-Kristian Arntzen620da7b2017-06-17 10:15:32 +02003231
Hans-Kristian Arntzencc153f82020-01-09 11:18:14 +01003232 HLSLBindingFlagBits resource_flags = HLSL_BINDING_AUTO_NONE_BIT;
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003233
Hans-Kristian Arntzen620da7b2017-06-17 10:15:32 +02003234 switch (type.basetype)
3235 {
3236 case SPIRType::SampledImage:
msiglreithd096f5c2017-11-27 16:00:56 +01003237 space = 't'; // SRV
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003238 resource_flags = HLSL_BINDING_AUTO_SRV_BIT;
Hans-Kristian Arntzen620da7b2017-06-17 10:15:32 +02003239 break;
3240
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02003241 case SPIRType::Image:
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01003242 if (type.image.sampled == 2 && type.image.dim != DimSubpassData)
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003243 {
Hans-Kristian Arntzen28bf9052020-04-03 11:21:41 +02003244 if (has_decoration(var.self, DecorationNonWritable) && hlsl_options.nonwritable_uav_texture_as_srv)
3245 {
3246 space = 't'; // SRV
3247 resource_flags = HLSL_BINDING_AUTO_SRV_BIT;
3248 }
3249 else
3250 {
3251 space = 'u'; // UAV
3252 resource_flags = HLSL_BINDING_AUTO_UAV_BIT;
3253 }
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003254 }
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02003255 else
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003256 {
msiglreithd096f5c2017-11-27 16:00:56 +01003257 space = 't'; // SRV
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003258 resource_flags = HLSL_BINDING_AUTO_SRV_BIT;
3259 }
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02003260 break;
3261
Hans-Kristian Arntzen620da7b2017-06-17 10:15:32 +02003262 case SPIRType::Sampler:
msiglreithd096f5c2017-11-27 16:00:56 +01003263 space = 's';
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003264 resource_flags = HLSL_BINDING_AUTO_SAMPLER_BIT;
Hans-Kristian Arntzen620da7b2017-06-17 10:15:32 +02003265 break;
3266
3267 case SPIRType::Struct:
3268 {
3269 auto storage = type.storage;
3270 if (storage == StorageClassUniform)
3271 {
3272 if (has_decoration(type.self, DecorationBufferBlock))
Hans-Kristian Arntzencc532cb2017-12-11 13:55:26 +01003273 {
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02003274 Bitset flags = ir.get_buffer_block_flags(var);
Bryan Bernhart17bccc92020-05-27 13:08:15 -07003275 bool is_readonly = flags.get(DecorationNonWritable) && !is_hlsl_force_storage_buffer_as_uav(var.self);
msiglreithd096f5c2017-11-27 16:00:56 +01003276 space = is_readonly ? 't' : 'u'; // UAV
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003277 resource_flags = is_readonly ? HLSL_BINDING_AUTO_SRV_BIT : HLSL_BINDING_AUTO_UAV_BIT;
Hans-Kristian Arntzencc532cb2017-12-11 13:55:26 +01003278 }
Hans-Kristian Arntzen620da7b2017-06-17 10:15:32 +02003279 else if (has_decoration(type.self, DecorationBlock))
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003280 {
msiglreithd096f5c2017-11-27 16:00:56 +01003281 space = 'b'; // Constant buffers
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003282 resource_flags = HLSL_BINDING_AUTO_CBV_BIT;
3283 }
Hans-Kristian Arntzen620da7b2017-06-17 10:15:32 +02003284 }
3285 else if (storage == StorageClassPushConstant)
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003286 {
msiglreithd096f5c2017-11-27 16:00:56 +01003287 space = 'b'; // Constant buffers
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003288 resource_flags = HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT;
3289 }
Hans-Kristian Arntzen153fed02017-09-28 13:28:44 +02003290 else if (storage == StorageClassStorageBuffer)
Hans-Kristian Arntzen4e7777c2019-01-30 13:31:17 +01003291 {
3292 // UAV or SRV depending on readonly flag.
3293 Bitset flags = ir.get_buffer_block_flags(var);
Bryan Bernhart17bccc92020-05-27 13:08:15 -07003294 bool is_readonly = flags.get(DecorationNonWritable) && !is_hlsl_force_storage_buffer_as_uav(var.self);
Hans-Kristian Arntzen4e7777c2019-01-30 13:31:17 +01003295 space = is_readonly ? 't' : 'u';
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003296 resource_flags = is_readonly ? HLSL_BINDING_AUTO_SRV_BIT : HLSL_BINDING_AUTO_UAV_BIT;
Hans-Kristian Arntzen4e7777c2019-01-30 13:31:17 +01003297 }
Hans-Kristian Arntzen620da7b2017-06-17 10:15:32 +02003298
3299 break;
3300 }
3301 default:
3302 break;
3303 }
3304
3305 if (!space)
3306 return "";
3307
Hans-Kristian Arntzencc153f82020-01-09 11:18:14 +01003308 uint32_t desc_set =
3309 resource_flags == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT ? ResourceBindingPushConstantDescriptorSet : 0u;
3310 uint32_t binding = resource_flags == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT ? ResourceBindingPushConstantBinding : 0u;
3311
3312 if (has_decoration(var.self, DecorationBinding))
3313 binding = get_decoration(var.self, DecorationBinding);
3314 if (has_decoration(var.self, DecorationDescriptorSet))
3315 desc_set = get_decoration(var.self, DecorationDescriptorSet);
3316
3317 return to_resource_register(resource_flags, space, binding, desc_set);
Hans-Kristian Arntzen620da7b2017-06-17 10:15:32 +02003318}
3319
3320string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var)
3321{
3322 // For combined image samplers.
3323 if (!has_decoration(var.self, DecorationBinding))
3324 return "";
Amer Koleciadebd5e2017-11-15 18:07:13 +01003325
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003326 return to_resource_register(HLSL_BINDING_AUTO_SAMPLER_BIT, 's', get_decoration(var.self, DecorationBinding),
msiglreithd096f5c2017-11-27 16:00:56 +01003327 get_decoration(var.self, DecorationDescriptorSet));
3328}
3329
Hans-Kristian Arntzencc153f82020-01-09 11:18:14 +01003330void CompilerHLSL::remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding)
msiglreithd096f5c2017-11-27 16:00:56 +01003331{
Hans-Kristian Arntzencc153f82020-01-09 11:18:14 +01003332 auto itr = resource_bindings.find({ get_execution_model(), desc_set, binding });
3333 if (itr != end(resource_bindings))
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003334 {
Hans-Kristian Arntzencc153f82020-01-09 11:18:14 +01003335 auto &remap = itr->second;
3336 remap.second = true;
3337
3338 switch (type)
3339 {
3340 case HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT:
3341 case HLSL_BINDING_AUTO_CBV_BIT:
3342 desc_set = remap.first.cbv.register_space;
3343 binding = remap.first.cbv.register_binding;
3344 break;
3345
3346 case HLSL_BINDING_AUTO_SRV_BIT:
3347 desc_set = remap.first.srv.register_space;
3348 binding = remap.first.srv.register_binding;
3349 break;
3350
3351 case HLSL_BINDING_AUTO_SAMPLER_BIT:
3352 desc_set = remap.first.sampler.register_space;
3353 binding = remap.first.sampler.register_binding;
3354 break;
3355
3356 case HLSL_BINDING_AUTO_UAV_BIT:
3357 desc_set = remap.first.uav.register_space;
3358 binding = remap.first.uav.register_binding;
3359 break;
3360
3361 default:
3362 break;
3363 }
3364 }
3365}
3366
3367string CompilerHLSL::to_resource_register(HLSLBindingFlagBits flag, char space, uint32_t binding, uint32_t space_set)
3368{
3369 if ((flag & resource_binding_flags) == 0)
3370 {
3371 remap_hlsl_resource_binding(flag, space_set, binding);
3372
3373 // The push constant block did not have a binding, and there were no remap for it,
3374 // so, declare without register binding.
3375 if (flag == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT && space_set == ResourceBindingPushConstantDescriptorSet)
3376 return "";
3377
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003378 if (hlsl_options.shader_model >= 51)
3379 return join(" : register(", space, binding, ", space", space_set, ")");
3380 else
3381 return join(" : register(", space, binding, ")");
3382 }
Amer Koleciadebd5e2017-11-15 18:07:13 +01003383 else
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01003384 return "";
Hans-Kristian Arntzen620da7b2017-06-17 10:15:32 +02003385}
3386
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003387void CompilerHLSL::emit_modern_uniform(const SPIRVariable &var)
Robert Konradc3268c92017-01-24 09:23:22 +01003388{
Robert Konradc5953e02017-04-18 14:14:48 +02003389 auto &type = get<SPIRType>(var.basetype);
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003390 switch (type.basetype)
3391 {
3392 case SPIRType::SampledImage:
3393 case SPIRType::Image:
Robert Konradc5953e02017-04-18 14:14:48 +02003394 {
Hans-Kristian Arntzen10dfaf72018-06-25 10:04:14 +02003395 bool is_coherent = false;
3396 if (type.basetype == SPIRType::Image && type.image.sampled == 2)
3397 is_coherent = has_decoration(var.self, DecorationCoherent);
3398
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +02003399 statement(is_coherent ? "globallycoherent " : "", image_type_hlsl_modern(type, var.self), " ",
3400 to_name(var.self), type_to_array_glsl(type), to_resource_binding(var), ";");
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003401
Hans-Kristian Arntzene8d5d712017-08-21 10:01:03 +02003402 if (type.basetype == SPIRType::SampledImage && type.image.dim != DimBuffer)
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003403 {
3404 // For combined image samplers, also emit a combined image sampler.
Bill Hollingsfd252b22021-11-08 15:59:45 -05003405 if (is_depth_image(type, var.self))
Hans-Kristian Arntzend9c09482018-01-04 12:15:55 +01003406 statement("SamplerComparisonState ", to_sampler_expression(var.self), type_to_array_glsl(type),
3407 to_resource_binding_sampler(var), ";");
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003408 else
Hans-Kristian Arntzend9c09482018-01-04 12:15:55 +01003409 statement("SamplerState ", to_sampler_expression(var.self), type_to_array_glsl(type),
3410 to_resource_binding_sampler(var), ";");
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003411 }
3412 break;
Robert Konradc5953e02017-04-18 14:14:48 +02003413 }
Robert Konrad1fe652d2017-04-21 09:46:58 +02003414
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003415 case SPIRType::Sampler:
Hans-Kristian Arntzene0447322018-07-04 14:25:10 +02003416 if (comparison_ids.count(var.self))
Hans-Kristian Arntzend9c09482018-01-04 12:15:55 +01003417 statement("SamplerComparisonState ", to_name(var.self), type_to_array_glsl(type), to_resource_binding(var),
3418 ";");
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003419 else
Hans-Kristian Arntzenc7f4b152018-01-04 11:05:40 +01003420 statement("SamplerState ", to_name(var.self), type_to_array_glsl(type), to_resource_binding(var), ";");
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003421 break;
3422
3423 default:
Hans-Kristian Arntzen620da7b2017-06-17 10:15:32 +02003424 statement(variable_decl(var), to_resource_binding(var), ";");
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003425 break;
3426 }
3427}
3428
3429void CompilerHLSL::emit_legacy_uniform(const SPIRVariable &var)
3430{
3431 auto &type = get<SPIRType>(var.basetype);
3432 switch (type.basetype)
3433 {
3434 case SPIRType::Sampler:
3435 case SPIRType::Image:
3436 SPIRV_CROSS_THROW("Separate image and samplers not supported in legacy HLSL.");
3437
3438 default:
3439 statement(variable_decl(var), ";");
3440 break;
3441 }
3442}
3443
3444void CompilerHLSL::emit_uniform(const SPIRVariable &var)
3445{
3446 add_resource_name(var.self);
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01003447 if (hlsl_options.shader_model >= 40)
Hans-Kristian Arntzen100e9d32017-04-25 10:44:55 +02003448 emit_modern_uniform(var);
3449 else
3450 emit_legacy_uniform(var);
Robert Konradc3268c92017-01-24 09:23:22 +01003451}
3452
Hans-Kristian Arntzen5e5d1c22020-04-21 23:27:33 +02003453bool CompilerHLSL::emit_complex_bitcast(uint32_t, uint32_t, uint32_t)
3454{
3455 return false;
3456}
3457
Robert Konrada7e2a692017-03-24 14:13:59 +01003458string CompilerHLSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &in_type)
3459{
3460 if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Int)
3461 return type_to_glsl(out_type);
3462 else if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::Int64)
3463 return type_to_glsl(out_type);
3464 else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Float)
3465 return "asuint";
3466 else if (out_type.basetype == SPIRType::Int && in_type.basetype == SPIRType::UInt)
3467 return type_to_glsl(out_type);
3468 else if (out_type.basetype == SPIRType::Int64 && in_type.basetype == SPIRType::UInt64)
3469 return type_to_glsl(out_type);
3470 else if (out_type.basetype == SPIRType::Int && in_type.basetype == SPIRType::Float)
3471 return "asint";
3472 else if (out_type.basetype == SPIRType::Float && in_type.basetype == SPIRType::UInt)
3473 return "asfloat";
3474 else if (out_type.basetype == SPIRType::Float && in_type.basetype == SPIRType::Int)
3475 return "asfloat";
3476 else if (out_type.basetype == SPIRType::Int64 && in_type.basetype == SPIRType::Double)
3477 SPIRV_CROSS_THROW("Double to Int64 is not supported in HLSL.");
3478 else if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::Double)
3479 SPIRV_CROSS_THROW("Double to UInt64 is not supported in HLSL.");
3480 else if (out_type.basetype == SPIRType::Double && in_type.basetype == SPIRType::Int64)
3481 return "asdouble";
3482 else if (out_type.basetype == SPIRType::Double && in_type.basetype == SPIRType::UInt64)
3483 return "asdouble";
Hans-Kristian Arntzen47d94ff2018-03-07 10:21:25 +01003484 else if (out_type.basetype == SPIRType::Half && in_type.basetype == SPIRType::UInt && in_type.vecsize == 1)
3485 {
3486 if (!requires_explicit_fp16_packing)
3487 {
3488 requires_explicit_fp16_packing = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02003489 force_recompile();
Hans-Kristian Arntzen47d94ff2018-03-07 10:21:25 +01003490 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003491 return "spvUnpackFloat2x16";
Hans-Kristian Arntzen47d94ff2018-03-07 10:21:25 +01003492 }
3493 else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Half && in_type.vecsize == 2)
3494 {
3495 if (!requires_explicit_fp16_packing)
3496 {
3497 requires_explicit_fp16_packing = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02003498 force_recompile();
Hans-Kristian Arntzen47d94ff2018-03-07 10:21:25 +01003499 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003500 return "spvPackFloat2x16";
Hans-Kristian Arntzen47d94ff2018-03-07 10:21:25 +01003501 }
Robert Konrada7e2a692017-03-24 14:13:59 +01003502 else
3503 return "";
3504}
3505
Robert Konrad8e9bf1f2016-08-18 12:40:35 +02003506void CompilerHLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, uint32_t count)
3507{
Hans-Kristian Arntzeneeb3f242019-03-27 11:53:47 +01003508 auto op = static_cast<GLSLstd450>(eop);
3509
3510 // If we need to do implicit bitcasts, make sure we do it with the correct type.
3511 uint32_t integer_width = get_integer_width_for_glsl_instruction(op, args, count);
3512 auto int_type = to_signed_basetype(integer_width);
3513 auto uint_type = to_unsigned_basetype(integer_width);
Robert Konrad8e9bf1f2016-08-18 12:40:35 +02003514
3515 switch (op)
3516 {
3517 case GLSLstd450InverseSqrt:
Robert Konrad8e9bf1f2016-08-18 12:40:35 +02003518 emit_unary_func_op(result_type, id, args[0], "rsqrt");
3519 break;
Robert Konrade7b02582017-03-24 13:58:39 +01003520
Robert Konrad8e9bf1f2016-08-18 12:40:35 +02003521 case GLSLstd450Fract:
Robert Konrad8e9bf1f2016-08-18 12:40:35 +02003522 emit_unary_func_op(result_type, id, args[0], "frac");
3523 break;
Robert Konrade7b02582017-03-24 13:58:39 +01003524
Hans-Kristian Arntzen47d94ff2018-03-07 10:21:25 +01003525 case GLSLstd450RoundEven:
rdb854f5662020-11-03 21:04:22 +01003526 if (hlsl_options.shader_model < 40)
3527 SPIRV_CROSS_THROW("roundEven is not supported in HLSL shader model 2/3.");
3528 emit_unary_func_op(result_type, id, args[0], "round");
3529 break;
Hans-Kristian Arntzen47d94ff2018-03-07 10:21:25 +01003530
3531 case GLSLstd450Acosh:
3532 case GLSLstd450Asinh:
3533 case GLSLstd450Atanh:
3534 SPIRV_CROSS_THROW("Inverse hyperbolics are not supported on HLSL.");
3535
Robert Konrad8e9bf1f2016-08-18 12:40:35 +02003536 case GLSLstd450FMix:
3537 case GLSLstd450IMix:
Robert Konrad8e9bf1f2016-08-18 12:40:35 +02003538 emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "lerp");
3539 break;
Robert Konrade7b02582017-03-24 13:58:39 +01003540
Robert Konrad8e9bf1f2016-08-18 12:40:35 +02003541 case GLSLstd450Atan2:
James Ross-Gowan8805d082017-09-24 02:45:33 +10003542 emit_binary_func_op(result_type, id, args[0], args[1], "atan2");
Robert Konrad8e9bf1f2016-08-18 12:40:35 +02003543 break;
Robert Konrade7b02582017-03-24 13:58:39 +01003544
3545 case GLSLstd450Fma:
3546 emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "mad");
3547 break;
3548
3549 case GLSLstd450InterpolateAtCentroid:
3550 emit_unary_func_op(result_type, id, args[0], "EvaluateAttributeAtCentroid");
3551 break;
3552 case GLSLstd450InterpolateAtSample:
3553 emit_binary_func_op(result_type, id, args[0], args[1], "EvaluateAttributeAtSample");
3554 break;
3555 case GLSLstd450InterpolateAtOffset:
3556 emit_binary_func_op(result_type, id, args[0], args[1], "EvaluateAttributeSnapped");
3557 break;
3558
Hans-Kristian Arntzen6c7c6802017-11-27 14:24:30 +01003559 case GLSLstd450PackHalf2x16:
3560 if (!requires_fp16_packing)
3561 {
3562 requires_fp16_packing = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02003563 force_recompile();
Hans-Kristian Arntzen6c7c6802017-11-27 14:24:30 +01003564 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003565 emit_unary_func_op(result_type, id, args[0], "spvPackHalf2x16");
Hans-Kristian Arntzen6c7c6802017-11-27 14:24:30 +01003566 break;
3567
3568 case GLSLstd450UnpackHalf2x16:
3569 if (!requires_fp16_packing)
3570 {
3571 requires_fp16_packing = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02003572 force_recompile();
Hans-Kristian Arntzen6c7c6802017-11-27 14:24:30 +01003573 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003574 emit_unary_func_op(result_type, id, args[0], "spvUnpackHalf2x16");
Hans-Kristian Arntzen6c7c6802017-11-27 14:24:30 +01003575 break;
3576
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01003577 case GLSLstd450PackSnorm4x8:
3578 if (!requires_snorm8_packing)
3579 {
3580 requires_snorm8_packing = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02003581 force_recompile();
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01003582 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003583 emit_unary_func_op(result_type, id, args[0], "spvPackSnorm4x8");
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01003584 break;
3585
3586 case GLSLstd450UnpackSnorm4x8:
3587 if (!requires_snorm8_packing)
3588 {
3589 requires_snorm8_packing = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02003590 force_recompile();
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01003591 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003592 emit_unary_func_op(result_type, id, args[0], "spvUnpackSnorm4x8");
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01003593 break;
3594
3595 case GLSLstd450PackUnorm4x8:
3596 if (!requires_unorm8_packing)
3597 {
3598 requires_unorm8_packing = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02003599 force_recompile();
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01003600 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003601 emit_unary_func_op(result_type, id, args[0], "spvPackUnorm4x8");
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01003602 break;
3603
3604 case GLSLstd450UnpackUnorm4x8:
3605 if (!requires_unorm8_packing)
3606 {
3607 requires_unorm8_packing = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02003608 force_recompile();
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01003609 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003610 emit_unary_func_op(result_type, id, args[0], "spvUnpackUnorm4x8");
Hans-Kristian Arntzen719ba632017-11-27 14:44:21 +01003611 break;
3612
Hans-Kristian Arntzen656af7e2017-11-27 15:03:40 +01003613 case GLSLstd450PackSnorm2x16:
3614 if (!requires_snorm16_packing)
3615 {
3616 requires_snorm16_packing = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02003617 force_recompile();
Hans-Kristian Arntzen656af7e2017-11-27 15:03:40 +01003618 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003619 emit_unary_func_op(result_type, id, args[0], "spvPackSnorm2x16");
Hans-Kristian Arntzen656af7e2017-11-27 15:03:40 +01003620 break;
3621
3622 case GLSLstd450UnpackSnorm2x16:
3623 if (!requires_snorm16_packing)
3624 {
3625 requires_snorm16_packing = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02003626 force_recompile();
Hans-Kristian Arntzen656af7e2017-11-27 15:03:40 +01003627 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003628 emit_unary_func_op(result_type, id, args[0], "spvUnpackSnorm2x16");
Hans-Kristian Arntzen656af7e2017-11-27 15:03:40 +01003629 break;
3630
3631 case GLSLstd450PackUnorm2x16:
3632 if (!requires_unorm16_packing)
3633 {
3634 requires_unorm16_packing = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02003635 force_recompile();
Hans-Kristian Arntzen656af7e2017-11-27 15:03:40 +01003636 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003637 emit_unary_func_op(result_type, id, args[0], "spvPackUnorm2x16");
Hans-Kristian Arntzen656af7e2017-11-27 15:03:40 +01003638 break;
3639
3640 case GLSLstd450UnpackUnorm2x16:
3641 if (!requires_unorm16_packing)
3642 {
3643 requires_unorm16_packing = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02003644 force_recompile();
Hans-Kristian Arntzen656af7e2017-11-27 15:03:40 +01003645 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003646 emit_unary_func_op(result_type, id, args[0], "spvUnpackUnorm2x16");
Hans-Kristian Arntzen656af7e2017-11-27 15:03:40 +01003647 break;
3648
Hans-Kristian Arntzene27f5772017-11-27 15:06:15 +01003649 case GLSLstd450PackDouble2x32:
3650 case GLSLstd450UnpackDouble2x32:
3651 SPIRV_CROSS_THROW("packDouble2x32/unpackDouble2x32 not supported in HLSL.");
3652
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01003653 case GLSLstd450FindILsb:
Hans-Kristian Arntzen932ee0e2019-07-12 10:57:56 +02003654 {
3655 auto basetype = expression_type(args[0]).basetype;
3656 emit_unary_func_op_cast(result_type, id, args[0], "firstbitlow", basetype, basetype);
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01003657 break;
Hans-Kristian Arntzen932ee0e2019-07-12 10:57:56 +02003658 }
Hans-Kristian Arntzeneeb3f242019-03-27 11:53:47 +01003659
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01003660 case GLSLstd450FindSMsb:
Hans-Kristian Arntzeneeb3f242019-03-27 11:53:47 +01003661 emit_unary_func_op_cast(result_type, id, args[0], "firstbithigh", int_type, int_type);
3662 break;
3663
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01003664 case GLSLstd450FindUMsb:
Hans-Kristian Arntzeneeb3f242019-03-27 11:53:47 +01003665 emit_unary_func_op_cast(result_type, id, args[0], "firstbithigh", uint_type, uint_type);
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01003666 break;
3667
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01003668 case GLSLstd450MatrixInverse:
3669 {
3670 auto &type = get<SPIRType>(result_type);
3671 if (type.vecsize == 2 && type.columns == 2)
3672 {
3673 if (!requires_inverse_2x2)
3674 {
3675 requires_inverse_2x2 = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02003676 force_recompile();
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01003677 }
3678 }
3679 else if (type.vecsize == 3 && type.columns == 3)
3680 {
3681 if (!requires_inverse_3x3)
3682 {
3683 requires_inverse_3x3 = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02003684 force_recompile();
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01003685 }
3686 }
3687 else if (type.vecsize == 4 && type.columns == 4)
3688 {
3689 if (!requires_inverse_4x4)
3690 {
3691 requires_inverse_4x4 = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02003692 force_recompile();
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01003693 }
3694 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003695 emit_unary_func_op(result_type, id, args[0], "spvInverse");
Hans-Kristian Arntzenb380a212018-02-23 16:36:12 +01003696 break;
3697 }
3698
Hans-Kristian Arntzenff874192019-06-28 11:19:19 +02003699 case GLSLstd450Normalize:
3700 // HLSL does not support scalar versions here.
3701 if (expression_type(args[0]).vecsize == 1)
3702 {
3703 // Returns -1 or 1 for valid input, sign() does the job.
3704 emit_unary_func_op(result_type, id, args[0], "sign");
3705 }
3706 else
3707 CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count);
3708 break;
3709
Hans-Kristian Arntzen041f1032019-07-03 12:24:58 +02003710 case GLSLstd450Reflect:
3711 if (get<SPIRType>(result_type).vecsize == 1)
3712 {
3713 if (!requires_scalar_reflect)
3714 {
3715 requires_scalar_reflect = true;
3716 force_recompile();
3717 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003718 emit_binary_func_op(result_type, id, args[0], args[1], "spvReflect");
Hans-Kristian Arntzen041f1032019-07-03 12:24:58 +02003719 }
3720 else
3721 CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count);
3722 break;
3723
3724 case GLSLstd450Refract:
3725 if (get<SPIRType>(result_type).vecsize == 1)
3726 {
3727 if (!requires_scalar_refract)
3728 {
3729 requires_scalar_refract = true;
3730 force_recompile();
3731 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003732 emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "spvRefract");
Hans-Kristian Arntzen041f1032019-07-03 12:24:58 +02003733 }
3734 else
3735 CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count);
3736 break;
3737
Hans-Kristian Arntzenc7eda1b2019-07-17 11:24:31 +02003738 case GLSLstd450FaceForward:
3739 if (get<SPIRType>(result_type).vecsize == 1)
3740 {
3741 if (!requires_scalar_faceforward)
3742 {
3743 requires_scalar_faceforward = true;
3744 force_recompile();
3745 }
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01003746 emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "spvFaceForward");
Hans-Kristian Arntzenc7eda1b2019-07-17 11:24:31 +02003747 }
3748 else
3749 CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count);
3750 break;
3751
Robert Konrad8e9bf1f2016-08-18 12:40:35 +02003752 default:
3753 CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count);
3754 break;
3755 }
3756}
3757
Hans-Kristian Arntzenca9398c2020-01-08 13:05:56 +01003758void CompilerHLSL::read_access_chain_array(const string &lhs, const SPIRAccessChain &chain)
3759{
3760 auto &type = get<SPIRType>(chain.basetype);
3761
3762 // Need to use a reserved identifier here since it might shadow an identifier in the access chain input or other loops.
3763 auto ident = get_unique_identifier();
3764
3765 statement("[unroll]");
Hans-Kristian Arntzenc2565252020-01-08 14:27:34 +01003766 statement("for (int ", ident, " = 0; ", ident, " < ", to_array_size(type, uint32_t(type.array.size() - 1)), "; ",
3767 ident, "++)");
Hans-Kristian Arntzenca9398c2020-01-08 13:05:56 +01003768 begin_scope();
3769 auto subchain = chain;
3770 subchain.dynamic_index = join(ident, " * ", chain.array_stride, " + ", chain.dynamic_index);
3771 subchain.basetype = type.parent_type;
3772 if (!get<SPIRType>(subchain.basetype).array.empty())
3773 subchain.array_stride = get_decoration(subchain.basetype, DecorationArrayStride);
3774 read_access_chain(nullptr, join(lhs, "[", ident, "]"), subchain);
3775 end_scope();
3776}
3777
3778void CompilerHLSL::read_access_chain_struct(const string &lhs, const SPIRAccessChain &chain)
3779{
3780 auto &type = get<SPIRType>(chain.basetype);
3781 auto subchain = chain;
3782 uint32_t member_count = uint32_t(type.member_types.size());
3783
3784 for (uint32_t i = 0; i < member_count; i++)
3785 {
3786 uint32_t offset = type_struct_member_offset(type, i);
3787 subchain.static_index = chain.static_index + offset;
3788 subchain.basetype = type.member_types[i];
3789
Hans-Kristian Arntzen1cbd71b2020-01-08 14:27:02 +01003790 subchain.matrix_stride = 0;
3791 subchain.array_stride = 0;
3792 subchain.row_major_matrix = false;
3793
Hans-Kristian Arntzenca9398c2020-01-08 13:05:56 +01003794 auto &member_type = get<SPIRType>(subchain.basetype);
3795 if (member_type.columns > 1)
3796 {
3797 subchain.matrix_stride = type_struct_member_matrix_stride(type, i);
3798 subchain.row_major_matrix = has_member_decoration(type.self, i, DecorationRowMajor);
3799 }
3800
3801 if (!member_type.array.empty())
3802 subchain.array_stride = type_struct_member_array_stride(type, i);
3803
3804 read_access_chain(nullptr, join(lhs, ".", to_member_name(type, i)), subchain);
3805 }
3806}
3807
3808void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIRAccessChain &chain)
Hans-Kristian Arntzen4375aa32017-08-15 10:12:08 +02003809{
3810 auto &type = get<SPIRType>(chain.basetype);
3811
3812 SPIRType target_type;
3813 target_type.basetype = SPIRType::UInt;
3814 target_type.vecsize = type.vecsize;
3815 target_type.columns = type.columns;
3816
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02003817 if (!type.array.empty())
Hans-Kristian Arntzenca9398c2020-01-08 13:05:56 +01003818 {
3819 read_access_chain_array(lhs, chain);
3820 return;
3821 }
3822 else if (type.basetype == SPIRType::Struct)
3823 {
3824 read_access_chain_struct(lhs, chain);
3825 return;
3826 }
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02003827 else if (type.width != 32 && !hlsl_options.enable_16bit_types)
Hans-Kristian Arntzend573a952020-07-01 11:42:58 +02003828 SPIRV_CROSS_THROW("Reading types other than 32-bit from ByteAddressBuffer not yet supported, unless SM 6.2 and "
3829 "native 16-bit types are enabled.");
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02003830
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02003831 string base = chain.base;
3832 if (has_decoration(chain.self, DecorationNonUniform))
3833 convert_non_uniform_expression(base, chain.self);
3834
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02003835 bool templated_load = hlsl_options.shader_model >= 62;
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02003836 string load_expr;
3837
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02003838 string template_expr;
3839 if (templated_load)
3840 template_expr = join("<", type_to_glsl(type), ">");
3841
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02003842 // Load a vector or scalar.
3843 if (type.columns == 1 && !chain.row_major_matrix)
Hans-Kristian Arntzen4375aa32017-08-15 10:12:08 +02003844 {
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02003845 const char *load_op = nullptr;
3846 switch (type.vecsize)
3847 {
3848 case 1:
3849 load_op = "Load";
3850 break;
3851 case 2:
3852 load_op = "Load2";
3853 break;
3854 case 3:
3855 load_op = "Load3";
3856 break;
3857 case 4:
3858 load_op = "Load4";
3859 break;
3860 default:
3861 SPIRV_CROSS_THROW("Unknown vector size.");
3862 }
3863
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02003864 if (templated_load)
3865 load_op = "Load";
3866
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02003867 load_expr = join(base, ".", load_op, template_expr, "(", chain.dynamic_index, chain.static_index, ")");
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02003868 }
3869 else if (type.columns == 1)
3870 {
3871 // Strided load since we are loading a column from a row-major matrix.
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02003872 if (templated_load)
3873 {
3874 auto scalar_type = type;
3875 scalar_type.vecsize = 1;
3876 scalar_type.columns = 1;
3877 template_expr = join("<", type_to_glsl(scalar_type), ">");
3878 if (type.vecsize > 1)
3879 load_expr += type_to_glsl(type) + "(";
3880 }
3881 else if (type.vecsize > 1)
Hans-Kristian Arntzen22e94912017-10-26 17:40:45 +02003882 {
3883 load_expr = type_to_glsl(target_type);
3884 load_expr += "(";
3885 }
3886
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02003887 for (uint32_t r = 0; r < type.vecsize; r++)
3888 {
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02003889 load_expr += join(base, ".Load", template_expr, "(", chain.dynamic_index,
Hans-Kristian Arntzend573a952020-07-01 11:42:58 +02003890 chain.static_index + r * chain.matrix_stride, ")");
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02003891 if (r + 1 < type.vecsize)
3892 load_expr += ", ";
3893 }
Hans-Kristian Arntzen22e94912017-10-26 17:40:45 +02003894
3895 if (type.vecsize > 1)
3896 load_expr += ")";
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02003897 }
3898 else if (!chain.row_major_matrix)
3899 {
3900 // Load a matrix, column-major, the easy case.
3901 const char *load_op = nullptr;
3902 switch (type.vecsize)
3903 {
3904 case 1:
3905 load_op = "Load";
3906 break;
3907 case 2:
3908 load_op = "Load2";
3909 break;
3910 case 3:
3911 load_op = "Load3";
3912 break;
3913 case 4:
3914 load_op = "Load4";
3915 break;
3916 default:
3917 SPIRV_CROSS_THROW("Unknown vector size.");
3918 }
3919
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02003920 if (templated_load)
3921 {
3922 auto vector_type = type;
3923 vector_type.columns = 1;
3924 template_expr = join("<", type_to_glsl(vector_type), ">");
3925 load_expr = type_to_glsl(type);
3926 load_op = "Load";
3927 }
3928 else
3929 {
3930 // Note, this loading style in HLSL is *actually* row-major, but we always treat matrices as transposed in this backend,
3931 // so row-major is technically column-major ...
3932 load_expr = type_to_glsl(target_type);
3933 }
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02003934 load_expr += "(";
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02003935
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02003936 for (uint32_t c = 0; c < type.columns; c++)
3937 {
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02003938 load_expr += join(base, ".", load_op, template_expr, "(", chain.dynamic_index,
Hans-Kristian Arntzen4a6d7542017-10-26 17:43:03 +02003939 chain.static_index + c * chain.matrix_stride, ")");
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02003940 if (c + 1 < type.columns)
3941 load_expr += ", ";
3942 }
3943 load_expr += ")";
3944 }
3945 else
3946 {
3947 // Pick out elements one by one ... Hopefully compilers are smart enough to recognize this pattern
3948 // considering HLSL is "row-major decl", but "column-major" memory layout (basically implicit transpose model, ugh) ...
3949
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02003950 if (templated_load)
3951 {
3952 load_expr = type_to_glsl(type);
3953 auto scalar_type = type;
3954 scalar_type.vecsize = 1;
3955 scalar_type.columns = 1;
3956 template_expr = join("<", type_to_glsl(scalar_type), ">");
3957 }
3958 else
3959 load_expr = type_to_glsl(target_type);
3960
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02003961 load_expr += "(";
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02003962
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02003963 for (uint32_t c = 0; c < type.columns; c++)
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02003964 {
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02003965 for (uint32_t r = 0; r < type.vecsize; r++)
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02003966 {
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02003967 load_expr += join(base, ".Load", template_expr, "(", chain.dynamic_index,
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02003968 chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ")");
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02003969
3970 if ((r + 1 < type.vecsize) || (c + 1 < type.columns))
3971 load_expr += ", ";
3972 }
3973 }
3974 load_expr += ")";
Hans-Kristian Arntzen4375aa32017-08-15 10:12:08 +02003975 }
3976
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02003977 if (!templated_load)
3978 {
3979 auto bitcast_op = bitcast_glsl_op(type, target_type);
3980 if (!bitcast_op.empty())
3981 load_expr = join(bitcast_op, "(", load_expr, ")");
3982 }
Hans-Kristian Arntzen4375aa32017-08-15 10:12:08 +02003983
Hans-Kristian Arntzenca9398c2020-01-08 13:05:56 +01003984 if (lhs.empty())
3985 {
3986 assert(expr);
3987 *expr = move(load_expr);
3988 }
3989 else
3990 statement(lhs, " = ", load_expr, ";");
Hans-Kristian Arntzen4375aa32017-08-15 10:12:08 +02003991}
3992
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02003993void CompilerHLSL::emit_load(const Instruction &instruction)
3994{
3995 auto ops = stream(instruction);
3996
3997 auto *chain = maybe_get<SPIRAccessChain>(ops[2]);
3998 if (chain)
3999 {
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004000 uint32_t result_type = ops[0];
4001 uint32_t id = ops[1];
4002 uint32_t ptr = ops[2];
4003
Hans-Kristian Arntzen22e94912017-10-26 17:40:45 +02004004 auto &type = get<SPIRType>(result_type);
Hans-Kristian Arntzenca9398c2020-01-08 13:05:56 +01004005 bool composite_load = !type.array.empty() || type.basetype == SPIRType::Struct;
Hans-Kristian Arntzen22e94912017-10-26 17:40:45 +02004006
Hans-Kristian Arntzenca9398c2020-01-08 13:05:56 +01004007 if (composite_load)
4008 {
4009 // We cannot make this work in one single expression as we might have nested structures and arrays,
4010 // so unroll the load to an uninitialized temporary.
4011 emit_uninitialized_temporary_expression(result_type, id);
4012 read_access_chain(nullptr, to_expression(id), *chain);
4013 track_expression_read(chain->self);
4014 }
4015 else
4016 {
4017 string load_expr;
4018 read_access_chain(&load_expr, "", *chain);
4019
4020 bool forward = should_forward(ptr) && forced_temporaries.find(id) == end(forced_temporaries);
4021
4022 // If we are forwarding this load,
4023 // don't register the read to access chain here, defer that to when we actually use the expression,
4024 // using the add_implied_read_expression mechanism.
4025 if (!forward)
4026 track_expression_read(chain->self);
4027
4028 // Do not forward complex load sequences like matrices, structs and arrays.
4029 if (type.columns > 1)
4030 forward = false;
4031
4032 auto &e = emit_op(result_type, id, load_expr, forward, true);
4033 e.need_transpose = false;
4034 register_read(id, ptr, forward);
4035 inherit_expression_dependencies(id, ptr);
4036 if (forward)
4037 add_implied_read_expression(e, chain->self);
4038 }
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004039 }
4040 else
4041 CompilerGLSL::emit_instruction(instruction);
4042}
4043
Hans-Kristian Arntzen151ff1e2020-01-08 14:17:28 +01004044void CompilerHLSL::write_access_chain_array(const SPIRAccessChain &chain, uint32_t value,
4045 const SmallVector<uint32_t> &composite_chain)
4046{
4047 auto &type = get<SPIRType>(chain.basetype);
4048
4049 // Need to use a reserved identifier here since it might shadow an identifier in the access chain input or other loops.
4050 auto ident = get_unique_identifier();
4051
4052 uint32_t id = ir.increase_bound_by(2);
4053 uint32_t int_type_id = id + 1;
4054 SPIRType int_type;
4055 int_type.basetype = SPIRType::Int;
4056 int_type.width = 32;
4057 set<SPIRType>(int_type_id, int_type);
4058 set<SPIRExpression>(id, ident, int_type_id, true);
4059 set_name(id, ident);
4060 suppressed_usage_tracking.insert(id);
4061
4062 statement("[unroll]");
Hans-Kristian Arntzenc2565252020-01-08 14:27:34 +01004063 statement("for (int ", ident, " = 0; ", ident, " < ", to_array_size(type, uint32_t(type.array.size() - 1)), "; ",
4064 ident, "++)");
Hans-Kristian Arntzen151ff1e2020-01-08 14:17:28 +01004065 begin_scope();
4066 auto subchain = chain;
4067 subchain.dynamic_index = join(ident, " * ", chain.array_stride, " + ", chain.dynamic_index);
4068 subchain.basetype = type.parent_type;
4069
4070 // Forcefully allow us to use an ID here by setting MSB.
4071 auto subcomposite_chain = composite_chain;
4072 subcomposite_chain.push_back(0x80000000u | id);
4073
4074 if (!get<SPIRType>(subchain.basetype).array.empty())
4075 subchain.array_stride = get_decoration(subchain.basetype, DecorationArrayStride);
4076
4077 write_access_chain(subchain, value, subcomposite_chain);
4078 end_scope();
4079}
4080
4081void CompilerHLSL::write_access_chain_struct(const SPIRAccessChain &chain, uint32_t value,
4082 const SmallVector<uint32_t> &composite_chain)
4083{
4084 auto &type = get<SPIRType>(chain.basetype);
4085 uint32_t member_count = uint32_t(type.member_types.size());
4086 auto subchain = chain;
4087
4088 auto subcomposite_chain = composite_chain;
4089 subcomposite_chain.push_back(0);
4090
4091 for (uint32_t i = 0; i < member_count; i++)
4092 {
4093 uint32_t offset = type_struct_member_offset(type, i);
4094 subchain.static_index = chain.static_index + offset;
4095 subchain.basetype = type.member_types[i];
4096
Hans-Kristian Arntzen1cbd71b2020-01-08 14:27:02 +01004097 subchain.matrix_stride = 0;
4098 subchain.array_stride = 0;
4099 subchain.row_major_matrix = false;
4100
Hans-Kristian Arntzen151ff1e2020-01-08 14:17:28 +01004101 auto &member_type = get<SPIRType>(subchain.basetype);
4102 if (member_type.columns > 1)
4103 {
4104 subchain.matrix_stride = type_struct_member_matrix_stride(type, i);
4105 subchain.row_major_matrix = has_member_decoration(type.self, i, DecorationRowMajor);
4106 }
4107
4108 if (!member_type.array.empty())
4109 subchain.array_stride = type_struct_member_array_stride(type, i);
4110
4111 subcomposite_chain.back() = i;
4112 write_access_chain(subchain, value, subcomposite_chain);
4113 }
4114}
4115
Hans-Kristian Arntzenc2565252020-01-08 14:27:34 +01004116string CompilerHLSL::write_access_chain_value(uint32_t value, const SmallVector<uint32_t> &composite_chain,
4117 bool enclose)
Hans-Kristian Arntzen151ff1e2020-01-08 14:17:28 +01004118{
4119 string ret;
4120 if (composite_chain.empty())
4121 ret = to_expression(value);
4122 else
4123 {
4124 AccessChainMeta meta;
4125 ret = access_chain_internal(value, composite_chain.data(), uint32_t(composite_chain.size()),
4126 ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_LITERAL_MSB_FORCE_ID, &meta);
4127 }
4128
4129 if (enclose)
4130 ret = enclose_expression(ret);
4131 return ret;
4132}
4133
4134void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t value,
4135 const SmallVector<uint32_t> &composite_chain)
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004136{
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02004137 auto &type = get<SPIRType>(chain.basetype);
4138
Hans-Kristian Arntzenacae6072019-01-04 13:19:50 +01004139 // Make sure we trigger a read of the constituents in the access chain.
4140 track_expression_read(chain.self);
4141
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02004142 SPIRType target_type;
4143 target_type.basetype = SPIRType::UInt;
4144 target_type.vecsize = type.vecsize;
4145 target_type.columns = type.columns;
4146
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02004147 if (!type.array.empty())
Hans-Kristian Arntzen151ff1e2020-01-08 14:17:28 +01004148 {
4149 write_access_chain_array(chain, value, composite_chain);
4150 register_write(chain.self);
4151 return;
4152 }
4153 else if (type.basetype == SPIRType::Struct)
4154 {
4155 write_access_chain_struct(chain, value, composite_chain);
4156 register_write(chain.self);
4157 return;
4158 }
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02004159 else if (type.width != 32 && !hlsl_options.enable_16bit_types)
Hans-Kristian Arntzend573a952020-07-01 11:42:58 +02004160 SPIRV_CROSS_THROW("Writing types other than 32-bit to RWByteAddressBuffer not yet supported, unless SM 6.2 and "
4161 "native 16-bit types are enabled.");
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02004162
4163 bool templated_store = hlsl_options.shader_model >= 62;
4164
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02004165 auto base = chain.base;
4166 if (has_decoration(chain.self, DecorationNonUniform))
4167 convert_non_uniform_expression(base, chain.self);
4168
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02004169 string template_expr;
4170 if (templated_store)
4171 template_expr = join("<", type_to_glsl(type), ">");
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02004172
4173 if (type.columns == 1 && !chain.row_major_matrix)
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004174 {
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004175 const char *store_op = nullptr;
4176 switch (type.vecsize)
4177 {
4178 case 1:
4179 store_op = "Store";
4180 break;
4181 case 2:
4182 store_op = "Store2";
4183 break;
4184 case 3:
4185 store_op = "Store3";
4186 break;
4187 case 4:
4188 store_op = "Store4";
4189 break;
4190 default:
4191 SPIRV_CROSS_THROW("Unknown vector size.");
4192 }
4193
Hans-Kristian Arntzen151ff1e2020-01-08 14:17:28 +01004194 auto store_expr = write_access_chain_value(value, composite_chain, false);
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02004195
4196 if (!templated_store)
4197 {
4198 auto bitcast_op = bitcast_glsl_op(target_type, type);
4199 if (!bitcast_op.empty())
4200 store_expr = join(bitcast_op, "(", store_expr, ")");
4201 }
4202 else
4203 store_op = "Store";
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02004204 statement(base, ".", store_op, template_expr, "(", chain.dynamic_index, chain.static_index, ", ",
Hans-Kristian Arntzend573a952020-07-01 11:42:58 +02004205 store_expr, ");");
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004206 }
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02004207 else if (type.columns == 1)
4208 {
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02004209 if (templated_store)
4210 {
4211 auto scalar_type = type;
4212 scalar_type.vecsize = 1;
4213 scalar_type.columns = 1;
4214 template_expr = join("<", type_to_glsl(scalar_type), ">");
4215 }
4216
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02004217 // Strided store.
4218 for (uint32_t r = 0; r < type.vecsize; r++)
4219 {
Hans-Kristian Arntzen151ff1e2020-01-08 14:17:28 +01004220 auto store_expr = write_access_chain_value(value, composite_chain, true);
Hans-Kristian Arntzen22e94912017-10-26 17:40:45 +02004221 if (type.vecsize > 1)
4222 {
4223 store_expr += ".";
4224 store_expr += index_to_swizzle(r);
4225 }
Hans-Kristian Arntzen4590c632018-01-04 13:16:56 +01004226 remove_duplicate_swizzle(store_expr);
Hans-Kristian Arntzen22e94912017-10-26 17:40:45 +02004227
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02004228 if (!templated_store)
4229 {
4230 auto bitcast_op = bitcast_glsl_op(target_type, type);
4231 if (!bitcast_op.empty())
4232 store_expr = join(bitcast_op, "(", store_expr, ")");
4233 }
4234
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02004235 statement(base, ".Store", template_expr, "(", chain.dynamic_index,
Hans-Kristian Arntzend573a952020-07-01 11:42:58 +02004236 chain.static_index + chain.matrix_stride * r, ", ", store_expr, ");");
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02004237 }
4238 }
4239 else if (!chain.row_major_matrix)
4240 {
4241 const char *store_op = nullptr;
4242 switch (type.vecsize)
4243 {
4244 case 1:
4245 store_op = "Store";
4246 break;
4247 case 2:
4248 store_op = "Store2";
4249 break;
4250 case 3:
4251 store_op = "Store3";
4252 break;
4253 case 4:
4254 store_op = "Store4";
4255 break;
4256 default:
4257 SPIRV_CROSS_THROW("Unknown vector size.");
4258 }
4259
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02004260 if (templated_store)
4261 {
4262 store_op = "Store";
4263 auto vector_type = type;
4264 vector_type.columns = 1;
4265 template_expr = join("<", type_to_glsl(vector_type), ">");
4266 }
4267
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02004268 for (uint32_t c = 0; c < type.columns; c++)
4269 {
Hans-Kristian Arntzen151ff1e2020-01-08 14:17:28 +01004270 auto store_expr = join(write_access_chain_value(value, composite_chain, true), "[", c, "]");
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02004271
4272 if (!templated_store)
4273 {
4274 auto bitcast_op = bitcast_glsl_op(target_type, type);
4275 if (!bitcast_op.empty())
4276 store_expr = join(bitcast_op, "(", store_expr, ")");
4277 }
4278
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02004279 statement(base, ".", store_op, template_expr, "(", chain.dynamic_index,
Hans-Kristian Arntzend573a952020-07-01 11:42:58 +02004280 chain.static_index + c * chain.matrix_stride, ", ", store_expr, ");");
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02004281 }
4282 }
4283 else
4284 {
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02004285 if (templated_store)
4286 {
4287 auto scalar_type = type;
4288 scalar_type.vecsize = 1;
4289 scalar_type.columns = 1;
4290 template_expr = join("<", type_to_glsl(scalar_type), ">");
4291 }
4292
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02004293 for (uint32_t r = 0; r < type.vecsize; r++)
4294 {
4295 for (uint32_t c = 0; c < type.columns; c++)
4296 {
Hans-Kristian Arntzenc2565252020-01-08 14:27:34 +01004297 auto store_expr =
4298 join(write_access_chain_value(value, composite_chain, true), "[", c, "].", index_to_swizzle(r));
Hans-Kristian Arntzen4590c632018-01-04 13:16:56 +01004299 remove_duplicate_swizzle(store_expr);
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02004300 auto bitcast_op = bitcast_glsl_op(target_type, type);
4301 if (!bitcast_op.empty())
4302 store_expr = join(bitcast_op, "(", store_expr, ")");
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02004303 statement(base, ".Store", template_expr, "(", chain.dynamic_index,
Hans-Kristian Arntzenafce0302017-10-26 17:16:32 +02004304 chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ", ", store_expr, ");");
4305 }
4306 }
4307 }
4308
4309 register_write(chain.self);
4310}
4311
4312void CompilerHLSL::emit_store(const Instruction &instruction)
4313{
4314 auto ops = stream(instruction);
4315 auto *chain = maybe_get<SPIRAccessChain>(ops[0]);
4316 if (chain)
Hans-Kristian Arntzen151ff1e2020-01-08 14:17:28 +01004317 write_access_chain(*chain, ops[1], {});
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004318 else
4319 CompilerGLSL::emit_instruction(instruction);
4320}
4321
4322void CompilerHLSL::emit_access_chain(const Instruction &instruction)
4323{
4324 auto ops = stream(instruction);
4325 uint32_t length = instruction.length;
4326
4327 bool need_byte_access_chain = false;
4328 auto &type = expression_type(ops[2]);
Hans-Kristian Arntzen4e7777c2019-01-30 13:31:17 +01004329 const auto *chain = maybe_get<SPIRAccessChain>(ops[2]);
4330
4331 if (chain)
4332 {
4333 // Keep tacking on an existing access chain.
4334 need_byte_access_chain = true;
4335 }
4336 else if (type.storage == StorageClassStorageBuffer || has_decoration(type.self, DecorationBufferBlock))
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004337 {
4338 // If we are starting to poke into an SSBO, we are dealing with ByteAddressBuffers, and we need
4339 // to emit SPIRAccessChain rather than a plain SPIRExpression.
4340 uint32_t chain_arguments = length - 3;
4341 if (chain_arguments > type.array.size())
4342 need_byte_access_chain = true;
4343 }
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004344
4345 if (need_byte_access_chain)
4346 {
Hans-Kristian Arntzenca9398c2020-01-08 13:05:56 +01004347 // If we have a chain variable, we are already inside the SSBO, and any array type will refer to arrays within a block,
4348 // and not array of SSBO.
4349 uint32_t to_plain_buffer_length = chain ? 0u : static_cast<uint32_t>(type.array.size());
4350
Hans-Kristian Arntzen18b82ca2018-07-09 14:02:50 +02004351 auto *backing_variable = maybe_get_backing_variable(ops[2]);
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004352
4353 string base;
4354 if (to_plain_buffer_length != 0)
Hans-Kristian Arntzen816c1162018-11-22 11:55:57 +01004355 base = access_chain(ops[2], &ops[3], to_plain_buffer_length, get<SPIRType>(ops[0]));
Hans-Kristian Arntzenaf672b72018-09-10 10:21:08 +02004356 else if (chain)
4357 base = chain->base;
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004358 else
4359 base = to_expression(ops[2]);
4360
Hans-Kristian Arntzen4375aa32017-08-15 10:12:08 +02004361 // Start traversing type hierarchy at the proper non-pointer types.
Chip Davisfc02b3d2019-01-08 12:54:40 -06004362 auto *basetype = &get_pointee_type(type);
Hans-Kristian Arntzen4375aa32017-08-15 10:12:08 +02004363
4364 // Traverse the type hierarchy down to the actual buffer types.
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004365 for (uint32_t i = 0; i < to_plain_buffer_length; i++)
Hans-Kristian Arntzen4375aa32017-08-15 10:12:08 +02004366 {
4367 assert(basetype->parent_type);
4368 basetype = &get<SPIRType>(basetype->parent_type);
4369 }
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004370
4371 uint32_t matrix_stride = 0;
Hans-Kristian Arntzenca9398c2020-01-08 13:05:56 +01004372 uint32_t array_stride = 0;
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02004373 bool row_major_matrix = false;
4374
4375 // Inherit matrix information.
4376 if (chain)
4377 {
4378 matrix_stride = chain->matrix_stride;
4379 row_major_matrix = chain->row_major_matrix;
Hans-Kristian Arntzenca9398c2020-01-08 13:05:56 +01004380 array_stride = chain->array_stride;
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02004381 }
4382
Hans-Kristian Arntzenc2565252020-01-08 14:27:34 +01004383 auto offsets = flattened_access_chain_offset(*basetype, &ops[3 + to_plain_buffer_length],
4384 length - 3 - to_plain_buffer_length, 0, 1, &row_major_matrix,
4385 &matrix_stride, &array_stride);
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004386
4387 auto &e = set<SPIRAccessChain>(ops[1], ops[0], type.storage, base, offsets.first, offsets.second);
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02004388 e.row_major_matrix = row_major_matrix;
4389 e.matrix_stride = matrix_stride;
Hans-Kristian Arntzenca9398c2020-01-08 13:05:56 +01004390 e.array_stride = array_stride;
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02004391 e.immutable = should_forward(ops[2]);
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02004392 e.loaded_from = backing_variable ? backing_variable->self : ID(0);
Hans-Kristian Arntzen551424c2017-10-26 16:35:18 +02004393
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004394 if (chain)
4395 {
4396 e.dynamic_index += chain->dynamic_index;
4397 e.static_index += chain->static_index;
4398 }
Hans-Kristian Arntzenacae6072019-01-04 13:19:50 +01004399
4400 for (uint32_t i = 2; i < length; i++)
4401 {
4402 inherit_expression_dependencies(ops[1], ops[i]);
4403 add_implied_read_expression(e, ops[i]);
4404 }
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004405 }
4406 else
4407 {
4408 CompilerGLSL::emit_instruction(instruction);
4409 }
4410}
4411
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02004412void CompilerHLSL::emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op)
4413{
4414 const char *atomic_op = nullptr;
Hans-Kristian Arntzen73771522018-09-17 15:46:39 +02004415
4416 string value_expr;
Hans-Kristian Arntzen9b7140e2020-04-27 12:09:59 +02004417 if (op != OpAtomicIDecrement && op != OpAtomicIIncrement && op != OpAtomicLoad && op != OpAtomicStore)
Hans-Kristian Arntzen73771522018-09-17 15:46:39 +02004418 value_expr = to_expression(ops[op == OpAtomicCompareExchange ? 6 : 5]);
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +02004419
Hans-Kristian Arntzen9b7140e2020-04-27 12:09:59 +02004420 bool is_atomic_store = false;
4421
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02004422 switch (op)
4423 {
Hans-Kristian Arntzen73771522018-09-17 15:46:39 +02004424 case OpAtomicIIncrement:
4425 atomic_op = "InterlockedAdd";
4426 value_expr = "1";
4427 break;
4428
4429 case OpAtomicIDecrement:
4430 atomic_op = "InterlockedAdd";
4431 value_expr = "-1";
4432 break;
4433
Hans-Kristian Arntzen9b7140e2020-04-27 12:09:59 +02004434 case OpAtomicLoad:
4435 atomic_op = "InterlockedAdd";
4436 value_expr = "0";
4437 break;
4438
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02004439 case OpAtomicISub:
4440 atomic_op = "InterlockedAdd";
4441 value_expr = join("-", enclose_expression(value_expr));
4442 break;
4443
4444 case OpAtomicSMin:
4445 case OpAtomicUMin:
4446 atomic_op = "InterlockedMin";
4447 break;
4448
4449 case OpAtomicSMax:
4450 case OpAtomicUMax:
4451 atomic_op = "InterlockedMax";
4452 break;
4453
4454 case OpAtomicAnd:
4455 atomic_op = "InterlockedAnd";
4456 break;
4457
4458 case OpAtomicOr:
4459 atomic_op = "InterlockedOr";
4460 break;
4461
4462 case OpAtomicXor:
4463 atomic_op = "InterlockedXor";
4464 break;
4465
4466 case OpAtomicIAdd:
4467 atomic_op = "InterlockedAdd";
4468 break;
4469
4470 case OpAtomicExchange:
4471 atomic_op = "InterlockedExchange";
4472 break;
4473
Hans-Kristian Arntzen9b7140e2020-04-27 12:09:59 +02004474 case OpAtomicStore:
4475 atomic_op = "InterlockedExchange";
4476 is_atomic_store = true;
4477 break;
4478
Hans-Kristian Arntzen85eb9722017-10-20 15:43:45 +02004479 case OpAtomicCompareExchange:
4480 if (length < 8)
4481 SPIRV_CROSS_THROW("Not enough data for opcode.");
4482 atomic_op = "InterlockedCompareExchange";
4483 value_expr = join(to_expression(ops[7]), ", ", value_expr);
4484 break;
4485
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02004486 default:
4487 SPIRV_CROSS_THROW("Unknown atomic opcode.");
4488 }
4489
Hans-Kristian Arntzen9b7140e2020-04-27 12:09:59 +02004490 if (is_atomic_store)
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02004491 {
Hans-Kristian Arntzen9b7140e2020-04-27 12:09:59 +02004492 auto &data_type = expression_type(ops[0]);
4493 auto *chain = maybe_get<SPIRAccessChain>(ops[0]);
4494
4495 auto &tmp_id = extra_sub_expressions[ops[0]];
4496 if (!tmp_id)
4497 {
4498 tmp_id = ir.increase_bound_by(1);
4499 emit_uninitialized_temporary_expression(get_pointee_type(data_type).self, tmp_id);
4500 }
4501
4502 if (data_type.storage == StorageClassImage || !chain)
4503 {
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02004504 statement(atomic_op, "(", to_non_uniform_aware_expression(ops[0]), ", ",
4505 to_expression(ops[3]), ", ", to_expression(tmp_id), ");");
Hans-Kristian Arntzen9b7140e2020-04-27 12:09:59 +02004506 }
4507 else
4508 {
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02004509 string base = chain->base;
4510 if (has_decoration(chain->self, DecorationNonUniform))
4511 convert_non_uniform_expression(base, chain->self);
Hans-Kristian Arntzen9b7140e2020-04-27 12:09:59 +02004512 // RWByteAddress buffer is always uint in its underlying type.
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02004513 statement(base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ",
Hans-Kristian Arntzend573a952020-07-01 11:42:58 +02004514 to_expression(ops[3]), ", ", to_expression(tmp_id), ");");
Hans-Kristian Arntzen9b7140e2020-04-27 12:09:59 +02004515 }
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02004516 }
4517 else
4518 {
Hans-Kristian Arntzen9b7140e2020-04-27 12:09:59 +02004519 uint32_t result_type = ops[0];
4520 uint32_t id = ops[1];
4521 forced_temporaries.insert(ops[1]);
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02004522
Hans-Kristian Arntzen9b7140e2020-04-27 12:09:59 +02004523 auto &type = get<SPIRType>(result_type);
4524 statement(variable_decl(type, to_name(id)), ";");
4525
4526 auto &data_type = expression_type(ops[2]);
4527 auto *chain = maybe_get<SPIRAccessChain>(ops[2]);
4528 SPIRType::BaseType expr_type;
4529 if (data_type.storage == StorageClassImage || !chain)
4530 {
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02004531 statement(atomic_op, "(", to_non_uniform_aware_expression(ops[2]), ", ", value_expr, ", ", to_name(id), ");");
Hans-Kristian Arntzen9b7140e2020-04-27 12:09:59 +02004532 expr_type = data_type.basetype;
4533 }
4534 else
4535 {
4536 // RWByteAddress buffer is always uint in its underlying type.
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02004537 string base = chain->base;
4538 if (has_decoration(chain->self, DecorationNonUniform))
4539 convert_non_uniform_expression(base, chain->self);
Hans-Kristian Arntzen9b7140e2020-04-27 12:09:59 +02004540 expr_type = SPIRType::UInt;
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02004541 statement(base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ", value_expr,
Hans-Kristian Arntzen9b7140e2020-04-27 12:09:59 +02004542 ", ", to_name(id), ");");
4543 }
4544
4545 auto expr = bitcast_expression(type, expr_type, to_name(id));
4546 set<SPIRExpression>(id, expr, result_type, true);
4547 }
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02004548 flush_all_atomic_capable_variables();
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02004549}
4550
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004551void CompilerHLSL::emit_subgroup_op(const Instruction &i)
4552{
4553 if (hlsl_options.shader_model < 60)
4554 SPIRV_CROSS_THROW("Wave ops requires SM 6.0 or higher.");
4555
4556 const uint32_t *ops = stream(i);
4557 auto op = static_cast<Op>(i.op);
4558
4559 uint32_t result_type = ops[0];
4560 uint32_t id = ops[1];
4561
Hans-Kristian Arntzen66afe8c2020-09-14 10:42:31 +02004562 auto scope = static_cast<Scope>(evaluate_constant_u32(ops[2]));
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004563 if (scope != ScopeSubgroup)
4564 SPIRV_CROSS_THROW("Only subgroup scope is supported.");
4565
4566 const auto make_inclusive_Sum = [&](const string &expr) -> string {
4567 return join(expr, " + ", to_expression(ops[4]));
4568 };
4569
4570 const auto make_inclusive_Product = [&](const string &expr) -> string {
4571 return join(expr, " * ", to_expression(ops[4]));
4572 };
4573
Hans-Kristian Arntzen88ddeec2020-01-09 12:10:59 +01004574 // If we need to do implicit bitcasts, make sure we do it with the correct type.
4575 uint32_t integer_width = get_integer_width_for_instruction(i);
4576 auto int_type = to_signed_basetype(integer_width);
4577 auto uint_type = to_unsigned_basetype(integer_width);
4578
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004579#define make_inclusive_BitAnd(expr) ""
4580#define make_inclusive_BitOr(expr) ""
4581#define make_inclusive_BitXor(expr) ""
4582#define make_inclusive_Min(expr) ""
4583#define make_inclusive_Max(expr) ""
4584
4585 switch (op)
4586 {
4587 case OpGroupNonUniformElect:
4588 emit_op(result_type, id, "WaveIsFirstLane()", true);
4589 break;
4590
4591 case OpGroupNonUniformBroadcast:
4592 emit_binary_func_op(result_type, id, ops[3], ops[4], "WaveReadLaneAt");
4593 break;
4594
4595 case OpGroupNonUniformBroadcastFirst:
4596 emit_unary_func_op(result_type, id, ops[3], "WaveReadLaneFirst");
4597 break;
4598
4599 case OpGroupNonUniformBallot:
4600 emit_unary_func_op(result_type, id, ops[3], "WaveActiveBallot");
4601 break;
4602
4603 case OpGroupNonUniformInverseBallot:
4604 SPIRV_CROSS_THROW("Cannot trivially implement InverseBallot in HLSL.");
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004605
4606 case OpGroupNonUniformBallotBitExtract:
4607 SPIRV_CROSS_THROW("Cannot trivially implement BallotBitExtract in HLSL.");
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004608
4609 case OpGroupNonUniformBallotFindLSB:
4610 SPIRV_CROSS_THROW("Cannot trivially implement BallotFindLSB in HLSL.");
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004611
4612 case OpGroupNonUniformBallotFindMSB:
4613 SPIRV_CROSS_THROW("Cannot trivially implement BallotFindMSB in HLSL.");
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004614
4615 case OpGroupNonUniformBallotBitCount:
4616 {
4617 auto operation = static_cast<GroupOperation>(ops[3]);
4618 if (operation == GroupOperationReduce)
4619 {
4620 bool forward = should_forward(ops[4]);
Hans-Kristian Arntzenb9cd3dc2018-04-17 15:01:31 +02004621 auto left = join("countbits(", to_enclosed_expression(ops[4]), ".x) + countbits(",
4622 to_enclosed_expression(ops[4]), ".y)");
4623 auto right = join("countbits(", to_enclosed_expression(ops[4]), ".z) + countbits(",
4624 to_enclosed_expression(ops[4]), ".w)");
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004625 emit_op(result_type, id, join(left, " + ", right), forward);
4626 inherit_expression_dependencies(id, ops[4]);
4627 }
4628 else if (operation == GroupOperationInclusiveScan)
4629 SPIRV_CROSS_THROW("Cannot trivially implement BallotBitCount Inclusive Scan in HLSL.");
4630 else if (operation == GroupOperationExclusiveScan)
4631 SPIRV_CROSS_THROW("Cannot trivially implement BallotBitCount Exclusive Scan in HLSL.");
4632 else
4633 SPIRV_CROSS_THROW("Invalid BitCount operation.");
4634 break;
4635 }
4636
4637 case OpGroupNonUniformShuffle:
Hans-Kristian Arntzen0e963c62021-04-23 13:03:35 +02004638 emit_binary_func_op(result_type, id, ops[3], ops[4], "WaveReadLaneAt");
4639 break;
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004640 case OpGroupNonUniformShuffleXor:
Hans-Kristian Arntzen0e963c62021-04-23 13:03:35 +02004641 {
4642 bool forward = should_forward(ops[3]);
4643 emit_op(ops[0], ops[1],
4644 join("WaveReadLaneAt(", to_unpacked_expression(ops[3]), ", ",
4645 "WaveGetLaneIndex() ^ ", to_enclosed_expression(ops[4]), ")"), forward);
4646 inherit_expression_dependencies(ops[1], ops[3]);
4647 break;
4648 }
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004649 case OpGroupNonUniformShuffleUp:
Hans-Kristian Arntzen0e963c62021-04-23 13:03:35 +02004650 {
4651 bool forward = should_forward(ops[3]);
4652 emit_op(ops[0], ops[1],
4653 join("WaveReadLaneAt(", to_unpacked_expression(ops[3]), ", ",
4654 "WaveGetLaneIndex() - ", to_enclosed_expression(ops[4]), ")"), forward);
4655 inherit_expression_dependencies(ops[1], ops[3]);
4656 break;
4657 }
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004658 case OpGroupNonUniformShuffleDown:
Hans-Kristian Arntzen0e963c62021-04-23 13:03:35 +02004659 {
4660 bool forward = should_forward(ops[3]);
4661 emit_op(ops[0], ops[1],
4662 join("WaveReadLaneAt(", to_unpacked_expression(ops[3]), ", ",
4663 "WaveGetLaneIndex() + ", to_enclosed_expression(ops[4]), ")"), forward);
4664 inherit_expression_dependencies(ops[1], ops[3]);
4665 break;
4666 }
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004667
4668 case OpGroupNonUniformAll:
4669 emit_unary_func_op(result_type, id, ops[3], "WaveActiveAllTrue");
4670 break;
4671
4672 case OpGroupNonUniformAny:
4673 emit_unary_func_op(result_type, id, ops[3], "WaveActiveAnyTrue");
4674 break;
4675
4676 case OpGroupNonUniformAllEqual:
Hans-Kristian Arntzend6c2c1b2021-03-08 12:52:03 +01004677 emit_unary_func_op(result_type, id, ops[3], "WaveActiveAllEqual");
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004678 break;
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004679
Hans-Kristian Arntzenb9cd3dc2018-04-17 15:01:31 +02004680 // clang-format off
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02004681#define HLSL_GROUP_OP(op, hlsl_op, supports_scan) \
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004682case OpGroupNonUniform##op: \
4683 { \
4684 auto operation = static_cast<GroupOperation>(ops[3]); \
4685 if (operation == GroupOperationReduce) \
4686 emit_unary_func_op(result_type, id, ops[4], "WaveActive" #hlsl_op); \
4687 else if (operation == GroupOperationInclusiveScan && supports_scan) \
4688 { \
4689 bool forward = should_forward(ops[4]); \
4690 emit_op(result_type, id, make_inclusive_##hlsl_op (join("WavePrefix" #hlsl_op, "(", to_expression(ops[4]), ")")), forward); \
4691 inherit_expression_dependencies(id, ops[4]); \
4692 } \
4693 else if (operation == GroupOperationExclusiveScan && supports_scan) \
4694 emit_unary_func_op(result_type, id, ops[4], "WavePrefix" #hlsl_op); \
4695 else if (operation == GroupOperationClusteredReduce) \
4696 SPIRV_CROSS_THROW("Cannot trivially implement ClusteredReduce in HLSL."); \
4697 else \
4698 SPIRV_CROSS_THROW("Invalid group operation."); \
4699 break; \
4700 }
Hans-Kristian Arntzen88ddeec2020-01-09 12:10:59 +01004701
4702#define HLSL_GROUP_OP_CAST(op, hlsl_op, type) \
4703case OpGroupNonUniform##op: \
4704 { \
4705 auto operation = static_cast<GroupOperation>(ops[3]); \
4706 if (operation == GroupOperationReduce) \
4707 emit_unary_func_op_cast(result_type, id, ops[4], "WaveActive" #hlsl_op, type, type); \
4708 else \
4709 SPIRV_CROSS_THROW("Invalid group operation."); \
4710 break; \
4711 }
4712
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02004713 HLSL_GROUP_OP(FAdd, Sum, true)
4714 HLSL_GROUP_OP(FMul, Product, true)
4715 HLSL_GROUP_OP(FMin, Min, false)
4716 HLSL_GROUP_OP(FMax, Max, false)
4717 HLSL_GROUP_OP(IAdd, Sum, true)
4718 HLSL_GROUP_OP(IMul, Product, true)
Hans-Kristian Arntzen88ddeec2020-01-09 12:10:59 +01004719 HLSL_GROUP_OP_CAST(SMin, Min, int_type)
4720 HLSL_GROUP_OP_CAST(SMax, Max, int_type)
4721 HLSL_GROUP_OP_CAST(UMin, Min, uint_type)
4722 HLSL_GROUP_OP_CAST(UMax, Max, uint_type)
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02004723 HLSL_GROUP_OP(BitwiseAnd, BitAnd, false)
4724 HLSL_GROUP_OP(BitwiseOr, BitOr, false)
4725 HLSL_GROUP_OP(BitwiseXor, BitXor, false)
Hans-Kristian Arntzend6c2c1b2021-03-08 12:52:03 +01004726 HLSL_GROUP_OP_CAST(LogicalAnd, BitAnd, uint_type)
4727 HLSL_GROUP_OP_CAST(LogicalOr, BitOr, uint_type)
4728 HLSL_GROUP_OP_CAST(LogicalXor, BitXor, uint_type)
Hans-Kristian Arntzen88ddeec2020-01-09 12:10:59 +01004729
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02004730#undef HLSL_GROUP_OP
Hans-Kristian Arntzen88ddeec2020-01-09 12:10:59 +01004731#undef HLSL_GROUP_OP_CAST
Hans-Kristian Arntzenb9cd3dc2018-04-17 15:01:31 +02004732 // clang-format on
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004733
4734 case OpGroupNonUniformQuadSwap:
4735 {
Hans-Kristian Arntzen66afe8c2020-09-14 10:42:31 +02004736 uint32_t direction = evaluate_constant_u32(ops[4]);
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02004737 if (direction == 0)
4738 emit_unary_func_op(result_type, id, ops[3], "QuadReadAcrossX");
4739 else if (direction == 1)
4740 emit_unary_func_op(result_type, id, ops[3], "QuadReadAcrossY");
4741 else if (direction == 2)
4742 emit_unary_func_op(result_type, id, ops[3], "QuadReadAcrossDiagonal");
4743 else
4744 SPIRV_CROSS_THROW("Invalid quad swap direction.");
4745 break;
4746 }
4747
4748 case OpGroupNonUniformQuadBroadcast:
4749 {
4750 emit_binary_func_op(result_type, id, ops[3], ops[4], "QuadReadLaneAt");
4751 break;
4752 }
4753
4754 default:
4755 SPIRV_CROSS_THROW("Invalid opcode for subgroup.");
4756 }
4757
4758 register_control_dependent_expression(id);
4759}
4760
Robert Konradda5f99b2016-08-14 23:54:51 +02004761void CompilerHLSL::emit_instruction(const Instruction &instruction)
4762{
4763 auto ops = stream(instruction);
4764 auto opcode = static_cast<Op>(instruction.op);
Robert Konradda5f99b2016-08-14 23:54:51 +02004765
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02004766#define HLSL_BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op)
4767#define HLSL_BOP_CAST(op, type) \
Hans-Kristian Arntzen2ed171e2019-01-30 14:49:55 +01004768 emit_binary_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode))
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02004769#define HLSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op)
4770#define HLSL_QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op)
4771#define HLSL_TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op)
4772#define HLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op)
4773#define HLSL_BFOP_CAST(op, type) \
Hans-Kristian Arntzen2ed171e2019-01-30 14:49:55 +01004774 emit_binary_func_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode))
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02004775#define HLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op)
4776#define HLSL_UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op)
Robert Konradda5f99b2016-08-14 23:54:51 +02004777
Hans-Kristian Arntzen2ed171e2019-01-30 14:49:55 +01004778 // If we need to do implicit bitcasts, make sure we do it with the correct type.
4779 uint32_t integer_width = get_integer_width_for_instruction(instruction);
4780 auto int_type = to_signed_basetype(integer_width);
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02004781 auto uint_type = to_unsigned_basetype(integer_width);
Hans-Kristian Arntzen2ed171e2019-01-30 14:49:55 +01004782
Robert Konradda5f99b2016-08-14 23:54:51 +02004783 switch (opcode)
4784 {
Hans-Kristian Arntzen3cbdbec2017-08-10 15:36:30 +02004785 case OpAccessChain:
4786 case OpInBoundsAccessChain:
4787 {
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004788 emit_access_chain(instruction);
4789 break;
4790 }
Asuka55dfbea2020-04-17 22:46:06 +08004791 case OpBitcast:
4792 {
4793 auto bitcast_type = get_bitcast_type(ops[0], ops[2]);
4794 if (bitcast_type == CompilerHLSL::TypeNormal)
4795 CompilerGLSL::emit_instruction(instruction);
4796 else
4797 {
4798 if (!requires_uint2_packing)
4799 {
4800 requires_uint2_packing = true;
4801 force_recompile();
4802 }
4803
4804 if (bitcast_type == CompilerHLSL::TypePackUint2x32)
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01004805 emit_unary_func_op(ops[0], ops[1], ops[2], "spvPackUint2x32");
Asuka55dfbea2020-04-17 22:46:06 +08004806 else
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01004807 emit_unary_func_op(ops[0], ops[1], ops[2], "spvUnpackUint2x32");
Asuka55dfbea2020-04-17 22:46:06 +08004808 }
4809
4810 break;
4811 }
Hans-Kristian Arntzen3cbdbec2017-08-10 15:36:30 +02004812
Hans-Kristian Arntzen8216e872021-06-28 11:10:55 +02004813 case OpSelect:
4814 {
4815 auto &value_type = expression_type(ops[3]);
4816 if (value_type.basetype == SPIRType::Struct || is_array(value_type))
4817 {
4818 // HLSL does not support ternary expressions on composites.
4819 // Cannot use branches, since we might be in a continue block
4820 // where explicit control flow is prohibited.
4821 // Emit a helper function where we can use control flow.
4822 TypeID value_type_id = expression_type_id(ops[3]);
4823 auto itr = std::find(composite_selection_workaround_types.begin(),
4824 composite_selection_workaround_types.end(),
4825 value_type_id);
4826 if (itr == composite_selection_workaround_types.end())
4827 {
4828 composite_selection_workaround_types.push_back(value_type_id);
4829 force_recompile();
4830 }
4831 emit_uninitialized_temporary_expression(ops[0], ops[1]);
4832 statement("spvSelectComposite(",
4833 to_expression(ops[1]), ", ", to_expression(ops[2]), ", ",
4834 to_expression(ops[3]), ", ", to_expression(ops[4]), ");");
4835 }
4836 else
4837 CompilerGLSL::emit_instruction(instruction);
4838 break;
4839 }
4840
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004841 case OpStore:
4842 {
4843 emit_store(instruction);
4844 break;
4845 }
Hans-Kristian Arntzen3cbdbec2017-08-10 15:36:30 +02004846
Hans-Kristian Arntzen7d7f4b32017-08-10 17:12:48 +02004847 case OpLoad:
4848 {
4849 emit_load(instruction);
Hans-Kristian Arntzen3cbdbec2017-08-10 15:36:30 +02004850 break;
4851 }
4852
Robert Konradda5f99b2016-08-14 23:54:51 +02004853 case OpMatrixTimesVector:
4854 {
Hans-Kristian Arntzen47a18b92019-07-23 10:56:57 +02004855 // Matrices are kept in a transposed state all the time, flip multiplication order always.
Robert Konrad2da32f72017-01-27 16:51:50 +01004856 emit_binary_func_op(ops[0], ops[1], ops[3], ops[2], "mul");
Robert Konradda5f99b2016-08-14 23:54:51 +02004857 break;
4858 }
Robert Konrade7b02582017-03-24 13:58:39 +01004859
Robert Konrad7534b222016-08-18 11:45:50 +02004860 case OpVectorTimesMatrix:
4861 {
Hans-Kristian Arntzen47a18b92019-07-23 10:56:57 +02004862 // Matrices are kept in a transposed state all the time, flip multiplication order always.
Robert Konrad2da32f72017-01-27 16:51:50 +01004863 emit_binary_func_op(ops[0], ops[1], ops[3], ops[2], "mul");
Robert Konrad7534b222016-08-18 11:45:50 +02004864 break;
4865 }
Robert Konrade7b02582017-03-24 13:58:39 +01004866
Robert Konrad7534b222016-08-18 11:45:50 +02004867 case OpMatrixTimesMatrix:
4868 {
Hans-Kristian Arntzen47a18b92019-07-23 10:56:57 +02004869 // Matrices are kept in a transposed state all the time, flip multiplication order always.
Robert Konrad2da32f72017-01-27 16:51:50 +01004870 emit_binary_func_op(ops[0], ops[1], ops[3], ops[2], "mul");
Robert Konrad7534b222016-08-18 11:45:50 +02004871 break;
4872 }
Robert Konrade7b02582017-03-24 13:58:39 +01004873
Hans-Kristian Arntzenf8b084d2019-07-01 10:57:27 +02004874 case OpOuterProduct:
4875 {
4876 uint32_t result_type = ops[0];
4877 uint32_t id = ops[1];
4878 uint32_t a = ops[2];
4879 uint32_t b = ops[3];
4880
4881 auto &type = get<SPIRType>(result_type);
4882 string expr = type_to_glsl_constructor(type);
4883 expr += "(";
4884 for (uint32_t col = 0; col < type.columns; col++)
4885 {
4886 expr += to_enclosed_expression(a);
4887 expr += " * ";
4888 expr += to_extract_component_expression(b, col);
4889 if (col + 1 < type.columns)
4890 expr += ", ";
4891 }
4892 expr += ")";
4893 emit_op(result_type, id, expr, should_forward(a) && should_forward(b));
4894 inherit_expression_dependencies(id, a);
4895 inherit_expression_dependencies(id, b);
4896 break;
4897 }
4898
Robert Konrad1a48d7d2016-08-18 12:54:22 +02004899 case OpFMod:
4900 {
Robert Konrad31afcbb2017-04-20 15:21:47 +02004901 if (!requires_op_fmod)
4902 {
4903 requires_op_fmod = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02004904 force_recompile();
Robert Konrad31afcbb2017-04-20 15:21:47 +02004905 }
Robert Konrad1a48d7d2016-08-18 12:54:22 +02004906 CompilerGLSL::emit_instruction(instruction);
4907 break;
4908 }
Robert Konrade7b02582017-03-24 13:58:39 +01004909
Hans-Kristian Arntzen3fa6cc82018-02-15 13:31:29 +01004910 case OpFRem:
4911 emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], "fmod");
4912 break;
4913
Robert Konradbb9dbd42017-04-24 11:08:55 +02004914 case OpImage:
4915 {
4916 uint32_t result_type = ops[0];
4917 uint32_t id = ops[1];
Hans-Kristian Arntzen988f00f2018-02-01 09:22:16 +01004918 auto *combined = maybe_get<SPIRCombinedImageSampler>(ops[2]);
Hans-Kristian Arntzena6972992018-10-05 09:49:57 +02004919
Hans-Kristian Arntzen988f00f2018-02-01 09:22:16 +01004920 if (combined)
Hans-Kristian Arntzena6972992018-10-05 09:49:57 +02004921 {
4922 auto &e = emit_op(result_type, id, to_expression(combined->image), true, true);
4923 auto *var = maybe_get_backing_variable(combined->image);
4924 if (var)
4925 e.loaded_from = var->self;
4926 }
Hans-Kristian Arntzen988f00f2018-02-01 09:22:16 +01004927 else
Hans-Kristian Arntzena6972992018-10-05 09:49:57 +02004928 {
4929 auto &e = emit_op(result_type, id, to_expression(ops[2]), true, true);
4930 auto *var = maybe_get_backing_variable(ops[2]);
4931 if (var)
4932 e.loaded_from = var->self;
4933 }
Robert Konradbb9dbd42017-04-24 11:08:55 +02004934 break;
4935 }
4936
Robert Konrade7b02582017-03-24 13:58:39 +01004937 case OpDPdx:
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02004938 HLSL_UFOP(ddx);
Hans-Kristian Arntzen938c7de2018-03-12 17:34:54 +01004939 register_control_dependent_expression(ops[1]);
Robert Konrade7b02582017-03-24 13:58:39 +01004940 break;
4941
4942 case OpDPdy:
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02004943 HLSL_UFOP(ddy);
Hans-Kristian Arntzen938c7de2018-03-12 17:34:54 +01004944 register_control_dependent_expression(ops[1]);
Robert Konrade7b02582017-03-24 13:58:39 +01004945 break;
4946
4947 case OpDPdxFine:
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02004948 HLSL_UFOP(ddx_fine);
Hans-Kristian Arntzen938c7de2018-03-12 17:34:54 +01004949 register_control_dependent_expression(ops[1]);
Robert Konrade7b02582017-03-24 13:58:39 +01004950 break;
4951
4952 case OpDPdyFine:
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02004953 HLSL_UFOP(ddy_fine);
Hans-Kristian Arntzen938c7de2018-03-12 17:34:54 +01004954 register_control_dependent_expression(ops[1]);
Robert Konrade7b02582017-03-24 13:58:39 +01004955 break;
4956
4957 case OpDPdxCoarse:
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02004958 HLSL_UFOP(ddx_coarse);
Hans-Kristian Arntzen938c7de2018-03-12 17:34:54 +01004959 register_control_dependent_expression(ops[1]);
Robert Konrade7b02582017-03-24 13:58:39 +01004960 break;
4961
4962 case OpDPdyCoarse:
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02004963 HLSL_UFOP(ddy_coarse);
Hans-Kristian Arntzen938c7de2018-03-12 17:34:54 +01004964 register_control_dependent_expression(ops[1]);
Robert Konrade7b02582017-03-24 13:58:39 +01004965 break;
4966
Hans-Kristian Arntzen47d94ff2018-03-07 10:21:25 +01004967 case OpFwidth:
4968 case OpFwidthCoarse:
4969 case OpFwidthFine:
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02004970 HLSL_UFOP(fwidth);
Hans-Kristian Arntzen938c7de2018-03-12 17:34:54 +01004971 register_control_dependent_expression(ops[1]);
Hans-Kristian Arntzen47d94ff2018-03-07 10:21:25 +01004972 break;
4973
Robert Konrade7b02582017-03-24 13:58:39 +01004974 case OpLogicalNot:
4975 {
4976 auto result_type = ops[0];
4977 auto id = ops[1];
4978 auto &type = get<SPIRType>(result_type);
4979
4980 if (type.vecsize > 1)
Robert Konradf3a82772017-03-24 15:00:48 +01004981 emit_unrolled_unary_op(result_type, id, ops[2], "!");
Robert Konrade7b02582017-03-24 13:58:39 +01004982 else
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02004983 HLSL_UOP(!);
Robert Konrade7b02582017-03-24 13:58:39 +01004984 break;
4985 }
4986
4987 case OpIEqual:
4988 {
4989 auto result_type = ops[0];
4990 auto id = ops[1];
4991
4992 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02004993 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "==", false, SPIRType::Unknown);
Robert Konrade7b02582017-03-24 13:58:39 +01004994 else
Hans-Kristian Arntzen2ed171e2019-01-30 14:49:55 +01004995 HLSL_BOP_CAST(==, int_type);
Robert Konrade7b02582017-03-24 13:58:39 +01004996 break;
4997 }
4998
4999 case OpLogicalEqual:
5000 case OpFOrdEqual:
Hans-Kristian Arntzenb960ae32019-10-14 15:04:00 +02005001 case OpFUnordEqual:
Robert Konrade7b02582017-03-24 13:58:39 +01005002 {
Hans-Kristian Arntzenb960ae32019-10-14 15:04:00 +02005003 // HLSL != operator is unordered.
5004 // https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-float-rules.
5005 // isnan() is apparently implemented as x != x as well.
5006 // We cannot implement UnordEqual as !(OrdNotEqual), as HLSL cannot express OrdNotEqual.
5007 // HACK: FUnordEqual will be implemented as FOrdEqual.
5008
Robert Konrade7b02582017-03-24 13:58:39 +01005009 auto result_type = ops[0];
5010 auto id = ops[1];
5011
5012 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005013 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "==", false, SPIRType::Unknown);
Robert Konrade7b02582017-03-24 13:58:39 +01005014 else
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02005015 HLSL_BOP(==);
Robert Konrade7b02582017-03-24 13:58:39 +01005016 break;
5017 }
5018
5019 case OpINotEqual:
5020 {
5021 auto result_type = ops[0];
5022 auto id = ops[1];
5023
5024 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005025 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "!=", false, SPIRType::Unknown);
Robert Konrade7b02582017-03-24 13:58:39 +01005026 else
Hans-Kristian Arntzen2ed171e2019-01-30 14:49:55 +01005027 HLSL_BOP_CAST(!=, int_type);
Robert Konrade7b02582017-03-24 13:58:39 +01005028 break;
5029 }
5030
5031 case OpLogicalNotEqual:
5032 case OpFOrdNotEqual:
Hans-Kristian Arntzenb960ae32019-10-14 15:04:00 +02005033 case OpFUnordNotEqual:
Robert Konrade7b02582017-03-24 13:58:39 +01005034 {
Hans-Kristian Arntzenb960ae32019-10-14 15:04:00 +02005035 // HLSL != operator is unordered.
5036 // https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-float-rules.
5037 // isnan() is apparently implemented as x != x as well.
5038
5039 // FIXME: FOrdNotEqual cannot be implemented in a crisp and simple way here.
5040 // We would need to do something like not(UnordEqual), but that cannot be expressed either.
5041 // Adding a lot of NaN checks would be a breaking change from perspective of performance.
5042 // SPIR-V will generally use isnan() checks when this even matters.
5043 // HACK: FOrdNotEqual will be implemented as FUnordEqual.
5044
Robert Konrade7b02582017-03-24 13:58:39 +01005045 auto result_type = ops[0];
5046 auto id = ops[1];
5047
5048 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005049 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "!=", false, SPIRType::Unknown);
Robert Konrade7b02582017-03-24 13:58:39 +01005050 else
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02005051 HLSL_BOP(!=);
Robert Konrade7b02582017-03-24 13:58:39 +01005052 break;
5053 }
5054
5055 case OpUGreaterThan:
5056 case OpSGreaterThan:
5057 {
5058 auto result_type = ops[0];
5059 auto id = ops[1];
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005060 auto type = opcode == OpUGreaterThan ? uint_type : int_type;
Robert Konrade7b02582017-03-24 13:58:39 +01005061
5062 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005063 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">", false, type);
Robert Konrade7b02582017-03-24 13:58:39 +01005064 else
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02005065 HLSL_BOP_CAST(>, type);
Robert Konrade7b02582017-03-24 13:58:39 +01005066 break;
5067 }
5068
5069 case OpFOrdGreaterThan:
5070 {
5071 auto result_type = ops[0];
5072 auto id = ops[1];
5073
5074 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005075 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">", false, SPIRType::Unknown);
Robert Konrade7b02582017-03-24 13:58:39 +01005076 else
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02005077 HLSL_BOP(>);
Robert Konrade7b02582017-03-24 13:58:39 +01005078 break;
5079 }
5080
Hans-Kristian Arntzenb960ae32019-10-14 15:04:00 +02005081 case OpFUnordGreaterThan:
5082 {
5083 auto result_type = ops[0];
5084 auto id = ops[1];
5085
5086 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005087 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<=", true, SPIRType::Unknown);
Hans-Kristian Arntzenb960ae32019-10-14 15:04:00 +02005088 else
5089 CompilerGLSL::emit_instruction(instruction);
5090 break;
5091 }
5092
Robert Konrade7b02582017-03-24 13:58:39 +01005093 case OpUGreaterThanEqual:
5094 case OpSGreaterThanEqual:
5095 {
5096 auto result_type = ops[0];
5097 auto id = ops[1];
5098
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005099 auto type = opcode == OpUGreaterThanEqual ? uint_type : int_type;
Robert Konrade7b02582017-03-24 13:58:39 +01005100 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005101 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">=", false, type);
Robert Konrade7b02582017-03-24 13:58:39 +01005102 else
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02005103 HLSL_BOP_CAST(>=, type);
Robert Konrade7b02582017-03-24 13:58:39 +01005104 break;
5105 }
5106
5107 case OpFOrdGreaterThanEqual:
5108 {
5109 auto result_type = ops[0];
5110 auto id = ops[1];
5111
5112 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005113 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">=", false, SPIRType::Unknown);
Robert Konrade7b02582017-03-24 13:58:39 +01005114 else
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02005115 HLSL_BOP(>=);
Robert Konrade7b02582017-03-24 13:58:39 +01005116 break;
5117 }
5118
Hans-Kristian Arntzenb960ae32019-10-14 15:04:00 +02005119 case OpFUnordGreaterThanEqual:
5120 {
5121 auto result_type = ops[0];
5122 auto id = ops[1];
5123
5124 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005125 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<", true, SPIRType::Unknown);
Hans-Kristian Arntzenb960ae32019-10-14 15:04:00 +02005126 else
5127 CompilerGLSL::emit_instruction(instruction);
5128 break;
5129 }
5130
Robert Konrade7b02582017-03-24 13:58:39 +01005131 case OpULessThan:
5132 case OpSLessThan:
5133 {
5134 auto result_type = ops[0];
5135 auto id = ops[1];
5136
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005137 auto type = opcode == OpULessThan ? uint_type : int_type;
Robert Konrade7b02582017-03-24 13:58:39 +01005138 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005139 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<", false, type);
Robert Konrade7b02582017-03-24 13:58:39 +01005140 else
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02005141 HLSL_BOP_CAST(<, type);
Robert Konrade7b02582017-03-24 13:58:39 +01005142 break;
5143 }
5144
5145 case OpFOrdLessThan:
5146 {
5147 auto result_type = ops[0];
5148 auto id = ops[1];
5149
5150 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005151 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<", false, SPIRType::Unknown);
Robert Konrade7b02582017-03-24 13:58:39 +01005152 else
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02005153 HLSL_BOP(<);
Robert Konrade7b02582017-03-24 13:58:39 +01005154 break;
5155 }
5156
Hans-Kristian Arntzenb960ae32019-10-14 15:04:00 +02005157 case OpFUnordLessThan:
5158 {
5159 auto result_type = ops[0];
5160 auto id = ops[1];
5161
5162 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005163 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">=", true, SPIRType::Unknown);
Hans-Kristian Arntzenb960ae32019-10-14 15:04:00 +02005164 else
5165 CompilerGLSL::emit_instruction(instruction);
5166 break;
5167 }
5168
Robert Konrade7b02582017-03-24 13:58:39 +01005169 case OpULessThanEqual:
5170 case OpSLessThanEqual:
5171 {
5172 auto result_type = ops[0];
5173 auto id = ops[1];
5174
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005175 auto type = opcode == OpULessThanEqual ? uint_type : int_type;
Robert Konrade7b02582017-03-24 13:58:39 +01005176 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005177 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<=", false, type);
Robert Konrade7b02582017-03-24 13:58:39 +01005178 else
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02005179 HLSL_BOP_CAST(<=, type);
Robert Konrade7b02582017-03-24 13:58:39 +01005180 break;
5181 }
5182
5183 case OpFOrdLessThanEqual:
5184 {
5185 auto result_type = ops[0];
5186 auto id = ops[1];
5187
5188 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005189 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<=", false, SPIRType::Unknown);
Robert Konrade7b02582017-03-24 13:58:39 +01005190 else
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02005191 HLSL_BOP(<=);
Robert Konrade7b02582017-03-24 13:58:39 +01005192 break;
5193 }
5194
Hans-Kristian Arntzenb960ae32019-10-14 15:04:00 +02005195 case OpFUnordLessThanEqual:
5196 {
5197 auto result_type = ops[0];
5198 auto id = ops[1];
5199
5200 if (expression_type(ops[2]).vecsize > 1)
Hans-Kristian Arntzena9be9252019-10-14 16:08:39 +02005201 emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">", true, SPIRType::Unknown);
Hans-Kristian Arntzenb960ae32019-10-14 15:04:00 +02005202 else
5203 CompilerGLSL::emit_instruction(instruction);
5204 break;
5205 }
5206
Hans-Kristian Arntzenaeeb0e32017-12-01 13:28:51 +01005207 case OpImageQueryLod:
Hans-Kristian Arntzen275974e2020-06-04 15:50:28 +02005208 emit_texture_op(instruction, false);
Hans-Kristian Arntzenaeeb0e32017-12-01 13:28:51 +01005209 break;
5210
Hans-Kristian Arntzen1bc5b702017-09-20 09:59:32 +02005211 case OpImageQuerySizeLod:
5212 {
5213 auto result_type = ops[0];
5214 auto id = ops[1];
5215
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02005216 require_texture_query_variant(ops[2]);
Hans-Kristian Arntzen1bc5b702017-09-20 09:59:32 +02005217 auto dummy_samples_levels = join(get_fallback_name(id), "_dummy_parameter");
5218 statement("uint ", dummy_samples_levels, ";");
5219
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02005220 auto expr = join("spvTextureSize(", to_non_uniform_aware_expression(ops[2]), ", ",
Hans-Kristian Arntzen1bc5b702017-09-20 09:59:32 +02005221 bitcast_expression(SPIRType::UInt, ops[3]), ", ", dummy_samples_levels, ")");
5222
5223 auto &restype = get<SPIRType>(ops[0]);
5224 expr = bitcast_expression(restype, SPIRType::UInt, expr);
5225 emit_op(result_type, id, expr, true);
5226 break;
5227 }
5228
5229 case OpImageQuerySize:
5230 {
5231 auto result_type = ops[0];
5232 auto id = ops[1];
5233
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02005234 require_texture_query_variant(ops[2]);
5235 bool uav = expression_type(ops[2]).image.sampled == 2;
5236
5237 if (const auto *var = maybe_get_backing_variable(ops[2]))
5238 if (hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(var->self, DecorationNonWritable))
5239 uav = false;
Hans-Kristian Arntzen1bc5b702017-09-20 09:59:32 +02005240
5241 auto dummy_samples_levels = join(get_fallback_name(id), "_dummy_parameter");
5242 statement("uint ", dummy_samples_levels, ";");
5243
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02005244 string expr;
5245 if (uav)
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02005246 expr = join("spvImageSize(", to_non_uniform_aware_expression(ops[2]), ", ", dummy_samples_levels, ")");
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02005247 else
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02005248 expr = join("spvTextureSize(", to_non_uniform_aware_expression(ops[2]), ", 0u, ", dummy_samples_levels, ")");
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02005249
Hans-Kristian Arntzen1bc5b702017-09-20 09:59:32 +02005250 auto &restype = get<SPIRType>(ops[0]);
5251 expr = bitcast_expression(restype, SPIRType::UInt, expr);
5252 emit_op(result_type, id, expr, true);
5253 break;
5254 }
5255
5256 case OpImageQuerySamples:
5257 case OpImageQueryLevels:
5258 {
5259 auto result_type = ops[0];
5260 auto id = ops[1];
5261
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02005262 require_texture_query_variant(ops[2]);
5263 bool uav = expression_type(ops[2]).image.sampled == 2;
5264 if (opcode == OpImageQueryLevels && uav)
5265 SPIRV_CROSS_THROW("Cannot query levels for UAV images.");
5266
5267 if (const auto *var = maybe_get_backing_variable(ops[2]))
5268 if (hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(var->self, DecorationNonWritable))
5269 uav = false;
Hans-Kristian Arntzen1bc5b702017-09-20 09:59:32 +02005270
5271 // Keep it simple and do not emit special variants to make this look nicer ...
5272 // This stuff is barely, if ever, used.
5273 forced_temporaries.insert(id);
5274 auto &type = get<SPIRType>(result_type);
5275 statement(variable_decl(type, to_name(id)), ";");
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02005276
5277 if (uav)
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02005278 statement("spvImageSize(", to_non_uniform_aware_expression(ops[2]), ", ", to_name(id), ");");
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02005279 else
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02005280 statement("spvTextureSize(", to_non_uniform_aware_expression(ops[2]), ", 0u, ", to_name(id), ");");
Hans-Kristian Arntzen1bc5b702017-09-20 09:59:32 +02005281
5282 auto &restype = get<SPIRType>(ops[0]);
5283 auto expr = bitcast_expression(restype, SPIRType::UInt, to_name(id));
5284 set<SPIRExpression>(id, expr, result_type, true);
5285 break;
5286 }
5287
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02005288 case OpImageRead:
5289 {
5290 uint32_t result_type = ops[0];
5291 uint32_t id = ops[1];
5292 auto *var = maybe_get_backing_variable(ops[2]);
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01005293 auto &type = expression_type(ops[2]);
5294 bool subpass_data = type.image.dim == DimSubpassData;
5295 bool pure = false;
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02005296
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01005297 string imgexpr;
5298
5299 if (subpass_data)
5300 {
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01005301 if (hlsl_options.shader_model < 40)
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01005302 SPIRV_CROSS_THROW("Subpass loads are not supported in HLSL shader model 2/3.");
5303
5304 // Similar to GLSL, implement subpass loads using texelFetch.
5305 if (type.image.ms)
5306 {
5307 uint32_t operands = ops[4];
5308 if (operands != ImageOperandsSampleMask || instruction.length != 6)
5309 SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected operand mask was used.");
5310 uint32_t sample = ops[5];
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02005311 imgexpr = join(to_non_uniform_aware_expression(ops[2]), ".Load(int2(gl_FragCoord.xy), ", to_expression(sample), ")");
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01005312 }
5313 else
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02005314 imgexpr = join(to_non_uniform_aware_expression(ops[2]), ".Load(int3(int2(gl_FragCoord.xy), 0))");
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01005315
5316 pure = true;
5317 }
5318 else
5319 {
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02005320 imgexpr = join(to_non_uniform_aware_expression(ops[2]), "[", to_expression(ops[3]), "]");
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01005321 // The underlying image type in HLSL depends on the image format, unlike GLSL, where all images are "vec4",
5322 // except that the underlying type changes how the data is interpreted.
Hans-Kristian Arntzen28bf9052020-04-03 11:21:41 +02005323
5324 bool force_srv =
5325 hlsl_options.nonwritable_uav_texture_as_srv && var && has_decoration(var->self, DecorationNonWritable);
5326 pure = force_srv;
5327
5328 if (var && !subpass_data && !force_srv)
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01005329 imgexpr = remap_swizzle(get<SPIRType>(result_type),
5330 image_format_to_components(get<SPIRType>(var->basetype).image.format), imgexpr);
5331 }
Hans-Kristian Arntzena95295c2017-10-24 09:52:12 +02005332
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02005333 if (var && var->forwardable)
5334 {
Hans-Kristian Arntzenc65248f2017-11-29 12:13:48 +01005335 bool forward = forced_temporaries.find(id) == end(forced_temporaries);
5336 auto &e = emit_op(result_type, id, imgexpr, forward);
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01005337
5338 if (!pure)
5339 {
5340 e.loaded_from = var->self;
5341 if (forward)
5342 var->dependees.push_back(id);
5343 }
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02005344 }
5345 else
5346 emit_op(result_type, id, imgexpr, false);
Hans-Kristian Arntzene0efa732018-03-09 13:21:38 +01005347
5348 inherit_expression_dependencies(id, ops[2]);
5349 if (type.image.ms)
5350 inherit_expression_dependencies(id, ops[5]);
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02005351 break;
5352 }
5353
5354 case OpImageWrite:
5355 {
5356 auto *var = maybe_get_backing_variable(ops[0]);
Hans-Kristian Arntzena95295c2017-10-24 09:52:12 +02005357
5358 // The underlying image type in HLSL depends on the image format, unlike GLSL, where all images are "vec4",
5359 // except that the underlying type changes how the data is interpreted.
5360 auto value_expr = to_expression(ops[2]);
5361 if (var)
5362 {
5363 auto &type = get<SPIRType>(var->basetype);
5364 auto narrowed_type = get<SPIRType>(type.image.type);
5365 narrowed_type.vecsize = image_format_to_components(type.image.format);
5366 value_expr = remap_swizzle(narrowed_type, expression_type(ops[2]).vecsize, value_expr);
5367 }
5368
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02005369 statement(to_non_uniform_aware_expression(ops[0]), "[", to_expression(ops[1]), "] = ", value_expr, ";");
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02005370 if (var && variable_storage_is_aliased(*var))
5371 flush_all_aliased_variables();
5372 break;
5373 }
5374
5375 case OpImageTexelPointer:
5376 {
5377 uint32_t result_type = ops[0];
5378 uint32_t id = ops[1];
Hans-Kristian Arntzena3d3c802020-03-19 11:35:06 +01005379
5380 auto expr = to_expression(ops[2]);
Hans-Kristian Arntzena3d3c802020-03-19 11:35:06 +01005381 expr += join("[", to_expression(ops[3]), "]");
Hans-Kristian Arntzen04e877d2020-03-19 14:20:37 +01005382 auto &e = set<SPIRExpression>(id, expr, result_type, true);
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02005383
5384 // When using the pointer, we need to know which variable it is actually loaded from.
5385 auto *var = maybe_get_backing_variable(ops[2]);
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02005386 e.loaded_from = var ? var->self : ID(0);
Hans-Kristian Arntzen6edbf0c2019-10-24 11:30:20 +02005387 inherit_expression_dependencies(id, ops[3]);
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02005388 break;
5389 }
5390
5391 case OpAtomicCompareExchange:
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02005392 case OpAtomicExchange:
5393 case OpAtomicISub:
5394 case OpAtomicSMin:
5395 case OpAtomicUMin:
5396 case OpAtomicSMax:
5397 case OpAtomicUMax:
5398 case OpAtomicAnd:
5399 case OpAtomicOr:
5400 case OpAtomicXor:
5401 case OpAtomicIAdd:
Hans-Kristian Arntzen73771522018-09-17 15:46:39 +02005402 case OpAtomicIIncrement:
5403 case OpAtomicIDecrement:
Hans-Kristian Arntzen9b7140e2020-04-27 12:09:59 +02005404 case OpAtomicLoad:
5405 case OpAtomicStore:
Hans-Kristian Arntzen044d3c82017-10-20 14:56:37 +02005406 {
5407 emit_atomic(ops, instruction.length, opcode);
5408 break;
5409 }
5410
Hans-Kristian Arntzen9c3d4e72018-01-09 12:41:13 +01005411 case OpControlBarrier:
Hans-Kristian Arntzenae236e72017-10-20 16:18:02 +02005412 case OpMemoryBarrier:
5413 {
Hans-Kristian Arntzen9c3d4e72018-01-09 12:41:13 +01005414 uint32_t memory;
5415 uint32_t semantics;
Hans-Kristian Arntzenae236e72017-10-20 16:18:02 +02005416
Hans-Kristian Arntzen9c3d4e72018-01-09 12:41:13 +01005417 if (opcode == OpMemoryBarrier)
Hans-Kristian Arntzenae236e72017-10-20 16:18:02 +02005418 {
Hans-Kristian Arntzen66afe8c2020-09-14 10:42:31 +02005419 memory = evaluate_constant_u32(ops[0]);
5420 semantics = evaluate_constant_u32(ops[1]);
Hans-Kristian Arntzenae236e72017-10-20 16:18:02 +02005421 }
Hans-Kristian Arntzenae236e72017-10-20 16:18:02 +02005422 else
5423 {
Hans-Kristian Arntzen66afe8c2020-09-14 10:42:31 +02005424 memory = evaluate_constant_u32(ops[1]);
5425 semantics = evaluate_constant_u32(ops[2]);
Hans-Kristian Arntzen9c3d4e72018-01-09 12:41:13 +01005426 }
5427
Hans-Kristian Arntzenc2664292018-04-11 15:02:02 +02005428 if (memory == ScopeSubgroup)
5429 {
5430 // No Wave-barriers in HLSL.
5431 break;
5432 }
5433
Hans-Kristian Arntzen9c3d4e72018-01-09 12:41:13 +01005434 // We only care about these flags, acquire/release and friends are not relevant to GLSL.
5435 semantics = mask_relevant_memory_semantics(semantics);
5436
5437 if (opcode == OpMemoryBarrier)
5438 {
5439 // If we are a memory barrier, and the next instruction is a control barrier, check if that memory barrier
5440 // does what we need, so we avoid redundant barriers.
5441 const Instruction *next = get_next_instruction_in_block(instruction);
5442 if (next && next->op == OpControlBarrier)
5443 {
5444 auto *next_ops = stream(*next);
Hans-Kristian Arntzen66afe8c2020-09-14 10:42:31 +02005445 uint32_t next_memory = evaluate_constant_u32(next_ops[1]);
5446 uint32_t next_semantics = evaluate_constant_u32(next_ops[2]);
Hans-Kristian Arntzen9c3d4e72018-01-09 12:41:13 +01005447 next_semantics = mask_relevant_memory_semantics(next_semantics);
5448
5449 // There is no "just execution barrier" in HLSL.
5450 // If there are no memory semantics for next instruction, we will imply group shared memory is synced.
5451 if (next_semantics == 0)
5452 next_semantics = MemorySemanticsWorkgroupMemoryMask;
5453
5454 bool memory_scope_covered = false;
5455 if (next_memory == memory)
5456 memory_scope_covered = true;
5457 else if (next_semantics == MemorySemanticsWorkgroupMemoryMask)
5458 {
5459 // If we only care about workgroup memory, either Device or Workgroup scope is fine,
5460 // scope does not have to match.
5461 if ((next_memory == ScopeDevice || next_memory == ScopeWorkgroup) &&
5462 (memory == ScopeDevice || memory == ScopeWorkgroup))
5463 {
5464 memory_scope_covered = true;
5465 }
5466 }
5467 else if (memory == ScopeWorkgroup && next_memory == ScopeDevice)
5468 {
5469 // The control barrier has device scope, but the memory barrier just has workgroup scope.
5470 memory_scope_covered = true;
5471 }
5472
5473 // If we have the same memory scope, and all memory types are covered, we're good.
5474 if (memory_scope_covered && (semantics & next_semantics) == semantics)
5475 break;
5476 }
5477 }
5478
5479 // We are synchronizing some memory or syncing execution,
5480 // so we cannot forward any loads beyond the memory barrier.
5481 if (semantics || opcode == OpControlBarrier)
Hans-Kristian Arntzen938c7de2018-03-12 17:34:54 +01005482 {
5483 assert(current_emitting_block);
5484 flush_control_dependent_expressions(current_emitting_block->self);
Hans-Kristian Arntzen9c3d4e72018-01-09 12:41:13 +01005485 flush_all_active_variables();
Hans-Kristian Arntzen938c7de2018-03-12 17:34:54 +01005486 }
Hans-Kristian Arntzen9c3d4e72018-01-09 12:41:13 +01005487
5488 if (opcode == OpControlBarrier)
5489 {
5490 // We cannot emit just execution barrier, for no memory semantics pick the cheapest option.
5491 if (semantics == MemorySemanticsWorkgroupMemoryMask || semantics == 0)
5492 statement("GroupMemoryBarrierWithGroupSync();");
5493 else if (semantics != 0 && (semantics & MemorySemanticsWorkgroupMemoryMask) == 0)
5494 statement("DeviceMemoryBarrierWithGroupSync();");
5495 else
5496 statement("AllMemoryBarrierWithGroupSync();");
5497 }
5498 else
5499 {
5500 if (semantics == MemorySemanticsWorkgroupMemoryMask)
5501 statement("GroupMemoryBarrier();");
5502 else if (semantics != 0 && (semantics & MemorySemanticsWorkgroupMemoryMask) == 0)
5503 statement("DeviceMemoryBarrier();");
5504 else
5505 statement("AllMemoryBarrier();");
Hans-Kristian Arntzenae236e72017-10-20 16:18:02 +02005506 }
5507 break;
5508 }
5509
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01005510 case OpBitFieldInsert:
5511 {
5512 if (!requires_bitfield_insert)
5513 {
5514 requires_bitfield_insert = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02005515 force_recompile();
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01005516 }
5517
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01005518 auto expr = join("spvBitfieldInsert(", to_expression(ops[2]), ", ", to_expression(ops[3]), ", ",
Hans-Kristian Arntzenc4052742017-11-29 12:00:48 +01005519 to_expression(ops[4]), ", ", to_expression(ops[5]), ")");
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01005520
Hans-Kristian Arntzenc4052742017-11-29 12:00:48 +01005521 bool forward =
5522 should_forward(ops[2]) && should_forward(ops[3]) && should_forward(ops[4]) && should_forward(ops[5]);
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01005523
5524 auto &restype = get<SPIRType>(ops[0]);
5525 expr = bitcast_expression(restype, SPIRType::UInt, expr);
5526 emit_op(ops[0], ops[1], expr, forward);
5527 break;
5528 }
5529
5530 case OpBitFieldSExtract:
5531 case OpBitFieldUExtract:
5532 {
5533 if (!requires_bitfield_extract)
5534 {
5535 requires_bitfield_extract = true;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02005536 force_recompile();
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01005537 }
5538
5539 if (opcode == OpBitFieldSExtract)
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01005540 HLSL_TFOP(spvBitfieldSExtract);
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01005541 else
Hans-Kristian Arntzen6a614cc2020-11-23 15:42:27 +01005542 HLSL_TFOP(spvBitfieldUExtract);
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01005543 break;
5544 }
5545
5546 case OpBitCount:
Hans-Kristian Arntzenb3305792019-08-26 11:01:14 +02005547 {
5548 auto basetype = expression_type(ops[2]).basetype;
5549 emit_unary_func_op_cast(ops[0], ops[1], ops[2], "countbits", basetype, basetype);
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01005550 break;
Hans-Kristian Arntzenb3305792019-08-26 11:01:14 +02005551 }
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01005552
5553 case OpBitReverse:
Hans-Kristian Arntzenf1752e52018-06-28 22:57:52 +02005554 HLSL_UFOP(reversebits);
Hans-Kristian Arntzen48f3fa42017-11-29 11:33:44 +01005555 break;
5556
Hans-Kristian Arntzene9da5ed2019-05-07 15:49:38 +02005557 case OpArrayLength:
5558 {
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02005559 auto *var = maybe_get_backing_variable(ops[2]);
Hans-Kristian Arntzene9da5ed2019-05-07 15:49:38 +02005560 if (!var)
5561 SPIRV_CROSS_THROW("Array length must point directly to an SSBO block.");
5562
5563 auto &type = get<SPIRType>(var->basetype);
5564 if (!has_decoration(type.self, DecorationBlock) && !has_decoration(type.self, DecorationBufferBlock))
5565 SPIRV_CROSS_THROW("Array length expression must point to a block type.");
5566
5567 // This must be 32-bit uint, so we're good to go.
5568 emit_uninitialized_temporary_expression(ops[0], ops[1]);
Hans-Kristian Arntzen532f6552021-04-22 13:54:43 +02005569 statement(to_non_uniform_aware_expression(ops[2]), ".GetDimensions(", to_expression(ops[1]), ");");
Hans-Kristian Arntzene9da5ed2019-05-07 15:49:38 +02005570 uint32_t offset = type_struct_member_offset(type, ops[3]);
5571 uint32_t stride = type_struct_member_array_stride(type, ops[3]);
5572 statement(to_expression(ops[1]), " = (", to_expression(ops[1]), " - ", offset, ") / ", stride, ";");
5573 break;
5574 }
5575
Chip Davis50dce102019-07-13 15:57:33 -05005576 case OpIsHelperInvocationEXT:
5577 SPIRV_CROSS_THROW("helperInvocationEXT() is not supported in HLSL.");
5578
Chip Davis2eff4202019-08-04 00:07:20 -05005579 case OpBeginInvocationInterlockEXT:
5580 case OpEndInvocationInterlockEXT:
5581 if (hlsl_options.shader_model < 51)
5582 SPIRV_CROSS_THROW("Rasterizer order views require Shader Model 5.1.");
5583 break; // Nothing to do in the body
5584
Robert Konradda5f99b2016-08-14 23:54:51 +02005585 default:
5586 CompilerGLSL::emit_instruction(instruction);
5587 break;
5588 }
5589}
5590
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02005591void CompilerHLSL::require_texture_query_variant(uint32_t var_id)
Hans-Kristian Arntzen9aa42a82017-09-20 10:31:05 +02005592{
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02005593 if (const auto *var = maybe_get_backing_variable(var_id))
5594 var_id = var->self;
5595
5596 auto &type = expression_type(var_id);
5597 bool uav = type.image.sampled == 2;
5598 if (hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(var_id, DecorationNonWritable))
5599 uav = false;
5600
Hans-Kristian Arntzen9aa42a82017-09-20 10:31:05 +02005601 uint32_t bit = 0;
5602 switch (type.image.dim)
5603 {
5604 case Dim1D:
5605 bit = type.image.arrayed ? Query1DArray : Query1D;
5606 break;
5607
5608 case Dim2D:
5609 if (type.image.ms)
5610 bit = type.image.arrayed ? Query2DMSArray : Query2DMS;
5611 else
5612 bit = type.image.arrayed ? Query2DArray : Query2D;
5613 break;
5614
5615 case Dim3D:
5616 bit = Query3D;
5617 break;
5618
5619 case DimCube:
5620 bit = type.image.arrayed ? QueryCubeArray : QueryCube;
5621 break;
5622
5623 case DimBuffer:
5624 bit = QueryBuffer;
5625 break;
5626
5627 default:
5628 SPIRV_CROSS_THROW("Unsupported query type.");
5629 }
5630
5631 switch (get<SPIRType>(type.image.type).basetype)
5632 {
5633 case SPIRType::Float:
5634 bit += QueryTypeFloat;
5635 break;
5636
5637 case SPIRType::Int:
5638 bit += QueryTypeInt;
5639 break;
5640
5641 case SPIRType::UInt:
5642 bit += QueryTypeUInt;
5643 break;
5644
5645 default:
5646 SPIRV_CROSS_THROW("Unsupported query type.");
5647 }
5648
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02005649 auto norm_state = image_format_to_normalized_state(type.image.format);
Hans-Kristian Arntzend573a952020-07-01 11:42:58 +02005650 auto &variant = uav ? required_texture_size_variants
5651 .uav[uint32_t(norm_state)][image_format_to_components(type.image.format) - 1] :
5652 required_texture_size_variants.srv;
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02005653
Hans-Kristian Arntzen9aa42a82017-09-20 10:31:05 +02005654 uint64_t mask = 1ull << bit;
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02005655 if ((variant & mask) == 0)
Hans-Kristian Arntzen9aa42a82017-09-20 10:31:05 +02005656 {
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02005657 force_recompile();
Hans-Kristian Arntzenf3a362b2020-05-19 13:39:48 +02005658 variant |= mask;
Hans-Kristian Arntzen9aa42a82017-09-20 10:31:05 +02005659 }
5660}
5661
Hans-Kristian Arntzen3fe57d32019-04-09 12:46:23 +02005662void CompilerHLSL::set_root_constant_layouts(std::vector<RootConstants> layout)
Amer Koleci7216d132017-11-06 20:10:04 +01005663{
Hans-Kristian Arntzen9bbdccd2019-02-12 11:11:29 +01005664 root_constants_layout = move(layout);
5665}
5666
5667void CompilerHLSL::add_vertex_attribute_remap(const HLSLVertexAttributeRemap &vertex_attributes)
5668{
5669 remap_vertex_attributes.push_back(vertex_attributes);
Amer Koleci7216d132017-11-06 20:10:04 +01005670}
5671
Hans-Kristian Arntzen333980a2019-09-05 12:43:40 +02005672VariableID CompilerHLSL::remap_num_workgroups_builtin()
Hans-Kristian Arntzen18e88332018-02-05 10:27:42 +01005673{
5674 update_active_builtins();
Hans-Kristian Arntzenf6d08e62018-02-23 15:56:25 +01005675
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01005676 if (!active_input_builtins.get(BuiltInNumWorkgroups))
Hans-Kristian Arntzen18e88332018-02-05 10:27:42 +01005677 return 0;
5678
5679 // Create a new, fake UBO.
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02005680 uint32_t offset = ir.increase_bound_by(4);
Hans-Kristian Arntzen18e88332018-02-05 10:27:42 +01005681
5682 uint32_t uint_type_id = offset;
5683 uint32_t block_type_id = offset + 1;
5684 uint32_t block_pointer_type_id = offset + 2;
5685 uint32_t variable_id = offset + 3;
5686
5687 SPIRType uint_type;
5688 uint_type.basetype = SPIRType::UInt;
Hans-Kristian Arntzen6a0f6982018-02-14 09:22:29 +01005689 uint_type.width = 32;
Hans-Kristian Arntzen18e88332018-02-05 10:27:42 +01005690 uint_type.vecsize = 3;
5691 uint_type.columns = 1;
5692 set<SPIRType>(uint_type_id, uint_type);
5693
5694 SPIRType block_type;
5695 block_type.basetype = SPIRType::Struct;
5696 block_type.member_types.push_back(uint_type_id);
5697 set<SPIRType>(block_type_id, block_type);
5698 set_decoration(block_type_id, DecorationBlock);
5699 set_member_name(block_type_id, 0, "count");
5700 set_member_decoration(block_type_id, 0, DecorationOffset, 0);
5701
5702 SPIRType block_pointer_type = block_type;
5703 block_pointer_type.pointer = true;
5704 block_pointer_type.storage = StorageClassUniform;
5705 block_pointer_type.parent_type = block_type_id;
5706 auto &ptr_type = set<SPIRType>(block_pointer_type_id, block_pointer_type);
5707
5708 // Preserve self.
5709 ptr_type.self = block_type_id;
5710
5711 set<SPIRVariable>(variable_id, block_pointer_type_id, StorageClassUniform);
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02005712 ir.meta[variable_id].decoration.alias = "SPIRV_Cross_NumWorkgroups";
Hans-Kristian Arntzen18e88332018-02-05 10:27:42 +01005713
5714 num_workgroups_builtin = variable_id;
Hans-Kristian Arntzen9b2a8c72021-09-30 14:39:42 +02005715 get_entry_point().interface_variables.push_back(num_workgroups_builtin);
Hans-Kristian Arntzen18e88332018-02-05 10:27:42 +01005716 return variable_id;
5717}
5718
Hans-Kristian Arntzenb9e5fe02019-11-11 11:01:35 +01005719void CompilerHLSL::set_resource_binding_flags(HLSLBindingFlags flags)
5720{
5721 resource_binding_flags = flags;
5722}
5723
Hans-Kristian Arntzen647ddae2019-05-13 14:58:27 +02005724void CompilerHLSL::validate_shader_model()
5725{
5726 // Check for nonuniform qualifier.
5727 // Instead of looping over all decorations to find this, just look at capabilities.
5728 for (auto &cap : ir.declared_capabilities)
5729 {
5730 switch (cap)
5731 {
5732 case CapabilityShaderNonUniformEXT:
5733 case CapabilityRuntimeDescriptorArrayEXT:
5734 if (hlsl_options.shader_model < 51)
Hans-Kristian Arntzen45a36ad2019-05-14 09:54:35 +02005735 SPIRV_CROSS_THROW(
5736 "Shader model 5.1 or higher is required to use bindless resources or NonUniformResourceIndex.");
Hans-Kristian Arntzendc940842020-12-07 12:16:02 +01005737 break;
5738
5739 case CapabilityVariablePointers:
5740 case CapabilityVariablePointersStorageBuffer:
5741 SPIRV_CROSS_THROW("VariablePointers capability is not supported in HLSL.");
5742
Hans-Kristian Arntzen647ddae2019-05-13 14:58:27 +02005743 default:
5744 break;
5745 }
5746 }
5747
5748 if (ir.addressing_model != AddressingModelLogical)
5749 SPIRV_CROSS_THROW("Only Logical addressing model can be used with HLSL.");
Hans-Kristian Arntzen2d520062020-06-04 11:35:21 +02005750
5751 if (hlsl_options.enable_16bit_types && hlsl_options.shader_model < 62)
5752 SPIRV_CROSS_THROW("Need at least shader model 6.2 when enabling native 16-bit type support.");
Hans-Kristian Arntzen647ddae2019-05-13 14:58:27 +02005753}
5754
Robert Konrad45270f62016-08-14 16:21:43 +02005755string CompilerHLSL::compile()
5756{
Hans-Kristian Arntzena0744152020-08-21 16:14:13 +02005757 ir.fixup_reserved_names();
5758
Robert Konrad45270f62016-08-14 16:21:43 +02005759 // Do not deal with ES-isms like precision, older extensions and such.
Hans-Kristian Arntzena803e5a2018-03-09 15:25:25 +01005760 options.es = false;
5761 options.version = 450;
5762 options.vulkan_semantics = true;
Robert Konrad45270f62016-08-14 16:21:43 +02005763 backend.float_literal_suffix = true;
5764 backend.double_literal_suffix = false;
5765 backend.long_long_literal_suffix = true;
5766 backend.uint32_t_literal_suffix = true;
Hans-Kristian Arntzen3fa09f52019-03-29 09:44:32 +01005767 backend.int16_t_literal_suffix = "";
Chip Davisca4744a2018-11-02 14:39:55 -05005768 backend.uint16_t_literal_suffix = "u";
Robert Konradd2b29c92016-08-14 23:09:06 +02005769 backend.basic_int_type = "int";
5770 backend.basic_uint_type = "uint";
Chip Davis50dce102019-07-13 15:57:33 -05005771 backend.demote_literal = "discard";
Chip Davis6628ea62019-07-10 23:46:40 -05005772 backend.boolean_mix_function = "";
Robert Konradd2b29c92016-08-14 23:09:06 +02005773 backend.swizzle_is_function = false;
Robert Konrad45270f62016-08-14 16:21:43 +02005774 backend.shared_is_implied = true;
Hans-Kristian Arntzen647ddae2019-05-13 14:58:27 +02005775 backend.unsized_array_supported = true;
Hans-Kristian Arntzenbdea1a42017-03-06 16:50:15 +01005776 backend.explicit_struct_type = false;
Robert Konrad45270f62016-08-14 16:21:43 +02005777 backend.use_initializer_list = true;
Robert Konradea24ee82016-09-23 18:57:18 +02005778 backend.use_constructor_splatting = false;
Hans-Kristian Arntzen95073252017-12-12 11:03:46 +01005779 backend.can_swizzle_scalar = true;
Hans-Kristian Arntzen06041982018-01-23 16:36:20 +01005780 backend.can_declare_struct_inline = false;
Hans-Kristian Arntzen5d9df6a2018-02-02 10:10:17 +01005781 backend.can_declare_arrays_inline = false;
Hans-Kristian Arntzen00ccd592018-02-05 12:37:41 +01005782 backend.can_return_array = false;
Hans-Kristian Arntzen647ddae2019-05-13 14:58:27 +02005783 backend.nonuniform_qualifier = "NonUniformResourceIndex";
Hans-Kristian Arntzen581ed0f2019-06-27 15:10:17 +02005784 backend.support_case_fallthrough = false;
Robert Konrade9cd04e2016-08-14 22:02:38 +02005785
Hans-Kristian Arntzene47a30e2021-05-07 12:28:08 +02005786 // SM 4.1 does not support precise for some reason.
5787 backend.support_precise_qualifier = hlsl_options.shader_model >= 50 || hlsl_options.shader_model == 40;
5788
Hans-Kristian Arntzen96492642019-05-23 14:54:04 +02005789 fixup_type_alias();
5790 reorder_type_alias();
Hans-Kristian Arntzenb5ed7062018-07-05 10:42:05 +02005791 build_function_control_flow_graphs_and_analyze();
Hans-Kristian Arntzen647ddae2019-05-13 14:58:27 +02005792 validate_shader_model();
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01005793 update_active_builtins();
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01005794 analyze_image_and_sampler_usage();
Hans-Kristian Arntzen261b4692019-09-04 12:18:04 +02005795 analyze_interlocked_resource_usage();
Hans-Kristian Arntzen18a594a2018-02-09 10:26:20 +01005796
5797 // Subpass input needs SV_Position.
5798 if (need_subpass_input)
Hans-Kristian Arntzene8e58842018-03-12 13:09:25 +01005799 active_input_builtins.set(BuiltInFragCoord);
Hans-Kristian Arntzen099f3072017-03-06 15:21:00 +01005800
Robert Konrad45270f62016-08-14 16:21:43 +02005801 uint32_t pass_count = 0;
5802 do
5803 {
Hans-Kristian Arntzen1d13a3e2022-01-17 14:12:01 +01005804 reset(pass_count);
Robert Konrad45270f62016-08-14 16:21:43 +02005805
5806 // Move constructor for this type is broken on GCC 4.9 ...
Hans-Kristian Arntzena489ba72019-04-02 11:19:03 +02005807 buffer.reset();
Robert Konrad45270f62016-08-14 16:21:43 +02005808
5809 emit_header();
5810 emit_resources();
5811
Hans-Kristian Arntzen5bcf02f2018-10-05 11:30:57 +02005812 emit_function(get<SPIRFunction>(ir.default_entry_point), Bitset());
Robert Konrad80fcf552016-08-14 21:33:32 +02005813 emit_hlsl_entry_point();
Robert Konrad45270f62016-08-14 16:21:43 +02005814
5815 pass_count++;
Hans-Kristian Arntzen317144a2019-04-05 12:06:10 +02005816 } while (is_forcing_recompilation());
Robert Konrad45270f62016-08-14 16:21:43 +02005817
Hans-Kristian Arntzen4427cb92017-11-13 13:49:11 +01005818 // Entry point in HLSL is always main() for the time being.
5819 get_entry_point().name = "main";
5820
Hans-Kristian Arntzena489ba72019-04-02 11:19:03 +02005821 return buffer.str();
Robert Konrad45270f62016-08-14 16:21:43 +02005822}
Hans-Kristian Arntzen33c61d22018-06-25 10:33:13 +02005823
5824void CompilerHLSL::emit_block_hints(const SPIRBlock &block)
5825{
5826 switch (block.hint)
5827 {
5828 case SPIRBlock::HintFlatten:
5829 statement("[flatten]");
5830 break;
5831 case SPIRBlock::HintDontFlatten:
5832 statement("[branch]");
5833 break;
5834 case SPIRBlock::HintUnroll:
5835 statement("[unroll]");
5836 break;
5837 case SPIRBlock::HintDontUnroll:
5838 statement("[loop]");
5839 break;
5840 default:
5841 break;
5842 }
5843}
Hans-Kristian Arntzenca9398c2020-01-08 13:05:56 +01005844
5845string CompilerHLSL::get_unique_identifier()
5846{
5847 return join("_", unique_identifier_count++, "ident");
5848}
Hans-Kristian Arntzencc153f82020-01-09 11:18:14 +01005849
5850void CompilerHLSL::add_hlsl_resource_binding(const HLSLResourceBinding &binding)
5851{
5852 StageSetBinding tuple = { binding.stage, binding.desc_set, binding.binding };
5853 resource_bindings[tuple] = { binding, false };
5854}
5855
5856bool CompilerHLSL::is_hlsl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const
5857{
5858 StageSetBinding tuple = { model, desc_set, binding };
5859 auto itr = resource_bindings.find(tuple);
5860 return itr != end(resource_bindings) && itr->second.second;
5861}
Asuka55dfbea2020-04-17 22:46:06 +08005862
5863CompilerHLSL::BitcastType CompilerHLSL::get_bitcast_type(uint32_t result_type, uint32_t op0)
5864{
5865 auto &rslt_type = get<SPIRType>(result_type);
5866 auto &expr_type = expression_type(op0);
5867
5868 if (rslt_type.basetype == SPIRType::BaseType::UInt64 && expr_type.basetype == SPIRType::BaseType::UInt &&
5869 expr_type.vecsize == 2)
5870 return BitcastType::TypePackUint2x32;
5871 else if (rslt_type.basetype == SPIRType::BaseType::UInt && rslt_type.vecsize == 2 &&
5872 expr_type.basetype == SPIRType::BaseType::UInt64)
5873 return BitcastType::TypeUnpackUint64;
5874
5875 return BitcastType::TypeNormal;
5876}
Bryan Bernhart17bccc92020-05-27 13:08:15 -07005877
Bryan Bernhart32bead82020-05-28 10:21:41 -07005878bool CompilerHLSL::is_hlsl_force_storage_buffer_as_uav(ID id) const
Bryan Bernhart17bccc92020-05-27 13:08:15 -07005879{
5880 if (hlsl_options.force_storage_buffer_as_uav)
5881 {
5882 return true;
Bryan Bernhart32bead82020-05-28 10:21:41 -07005883 }
5884
5885 const uint32_t desc_set = get_decoration(id, spv::DecorationDescriptorSet);
5886 const uint32_t binding = get_decoration(id, spv::DecorationBinding);
Hans-Kristian Arntzend573a952020-07-01 11:42:58 +02005887
5888 return (force_uav_buffer_bindings.find({ desc_set, binding }) != force_uav_buffer_bindings.end());
Bryan Bernhart17bccc92020-05-27 13:08:15 -07005889}
5890
Bryan Bernhart32bead82020-05-28 10:21:41 -07005891void CompilerHLSL::set_hlsl_force_storage_buffer_as_uav(uint32_t desc_set, uint32_t binding)
Bryan Bernhart17bccc92020-05-27 13:08:15 -07005892{
Bryan Bernhart32bead82020-05-28 10:21:41 -07005893 SetBindingPair pair = { desc_set, binding };
5894 force_uav_buffer_bindings.insert(pair);
5895}
Tomek Ponitkaba58f782020-07-23 19:09:43 +02005896
5897bool CompilerHLSL::builtin_translates_to_nonarray(spv::BuiltIn builtin) const
5898{
5899 return (builtin == BuiltInSampleMask);
5900}