diff options
Diffstat (limited to 'src/shader_recompiler/backend/spirv')
23 files changed, 6542 insertions, 0 deletions
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp new file mode 100644 index 000000000..2d29d8c14 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_context.cpp | |||
| @@ -0,0 +1,1368 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <array> | ||
| 7 | #include <climits> | ||
| 8 | #include <string_view> | ||
| 9 | |||
| 10 | #include <fmt/format.h> | ||
| 11 | |||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/div_ceil.h" | ||
| 14 | #include "shader_recompiler/backend/spirv/emit_context.h" | ||
| 15 | |||
| 16 | namespace Shader::Backend::SPIRV { | ||
| 17 | namespace { | ||
| 18 | enum class Operation { | ||
| 19 | Increment, | ||
| 20 | Decrement, | ||
| 21 | FPAdd, | ||
| 22 | FPMin, | ||
| 23 | FPMax, | ||
| 24 | }; | ||
| 25 | |||
| 26 | struct AttrInfo { | ||
| 27 | Id pointer; | ||
| 28 | Id id; | ||
| 29 | bool needs_cast; | ||
| 30 | }; | ||
| 31 | |||
| 32 | Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) { | ||
| 33 | const spv::ImageFormat format{spv::ImageFormat::Unknown}; | ||
| 34 | const Id type{ctx.F32[1]}; | ||
| 35 | const bool depth{desc.is_depth}; | ||
| 36 | switch (desc.type) { | ||
| 37 | case TextureType::Color1D: | ||
| 38 | return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format); | ||
| 39 | case TextureType::ColorArray1D: | ||
| 40 | return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format); | ||
| 41 | case TextureType::Color2D: | ||
| 42 | return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, false, 1, format); | ||
| 43 | case TextureType::ColorArray2D: | ||
| 44 | return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, false, 1, format); | ||
| 45 | case TextureType::Color3D: | ||
| 46 | return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format); | ||
| 47 | case TextureType::ColorCube: | ||
| 48 | return ctx.TypeImage(type, spv::Dim::Cube, depth, false, false, 1, format); | ||
| 49 | case TextureType::ColorArrayCube: | ||
| 50 | return ctx.TypeImage(type, spv::Dim::Cube, depth, true, false, 1, format); | ||
| 51 | case TextureType::Buffer: | ||
| 52 | break; | ||
| 53 | } | ||
| 54 | throw InvalidArgument("Invalid texture type {}", desc.type); | ||
| 55 | } | ||
| 56 | |||
| 57 | spv::ImageFormat GetImageFormat(ImageFormat format) { | ||
| 58 | switch (format) { | ||
| 59 | case ImageFormat::Typeless: | ||
| 60 | return spv::ImageFormat::Unknown; | ||
| 61 | case ImageFormat::R8_UINT: | ||
| 62 | return spv::ImageFormat::R8ui; | ||
| 63 | case ImageFormat::R8_SINT: | ||
| 64 | return spv::ImageFormat::R8i; | ||
| 65 | case ImageFormat::R16_UINT: | ||
| 66 | return spv::ImageFormat::R16ui; | ||
| 67 | case ImageFormat::R16_SINT: | ||
| 68 | return spv::ImageFormat::R16i; | ||
| 69 | case ImageFormat::R32_UINT: | ||
| 70 | return spv::ImageFormat::R32ui; | ||
| 71 | case ImageFormat::R32G32_UINT: | ||
| 72 | return spv::ImageFormat::Rg32ui; | ||
| 73 | case ImageFormat::R32G32B32A32_UINT: | ||
| 74 | return spv::ImageFormat::Rgba32ui; | ||
| 75 | } | ||
| 76 | throw InvalidArgument("Invalid image format {}", format); | ||
| 77 | } | ||
| 78 | |||
| 79 | Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) { | ||
| 80 | const spv::ImageFormat format{GetImageFormat(desc.format)}; | ||
| 81 | const Id type{ctx.U32[1]}; | ||
| 82 | switch (desc.type) { | ||
| 83 | case TextureType::Color1D: | ||
| 84 | return ctx.TypeImage(type, spv::Dim::Dim1D, false, false, false, 2, format); | ||
| 85 | case TextureType::ColorArray1D: | ||
| 86 | return ctx.TypeImage(type, spv::Dim::Dim1D, false, true, false, 2, format); | ||
| 87 | case TextureType::Color2D: | ||
| 88 | return ctx.TypeImage(type, spv::Dim::Dim2D, false, false, false, 2, format); | ||
| 89 | case TextureType::ColorArray2D: | ||
| 90 | return ctx.TypeImage(type, spv::Dim::Dim2D, false, true, false, 2, format); | ||
| 91 | case TextureType::Color3D: | ||
| 92 | return ctx.TypeImage(type, spv::Dim::Dim3D, false, false, false, 2, format); | ||
| 93 | case TextureType::Buffer: | ||
| 94 | throw NotImplementedException("Image buffer"); | ||
| 95 | default: | ||
| 96 | break; | ||
| 97 | } | ||
| 98 | throw InvalidArgument("Invalid texture type {}", desc.type); | ||
| 99 | } | ||
| 100 | |||
| 101 | Id DefineVariable(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin, | ||
| 102 | spv::StorageClass storage_class) { | ||
| 103 | const Id pointer_type{ctx.TypePointer(storage_class, type)}; | ||
| 104 | const Id id{ctx.AddGlobalVariable(pointer_type, storage_class)}; | ||
| 105 | if (builtin) { | ||
| 106 | ctx.Decorate(id, spv::Decoration::BuiltIn, *builtin); | ||
| 107 | } | ||
| 108 | ctx.interfaces.push_back(id); | ||
| 109 | return id; | ||
| 110 | } | ||
| 111 | |||
| 112 | u32 NumVertices(InputTopology input_topology) { | ||
| 113 | switch (input_topology) { | ||
| 114 | case InputTopology::Points: | ||
| 115 | return 1; | ||
| 116 | case InputTopology::Lines: | ||
| 117 | return 2; | ||
| 118 | case InputTopology::LinesAdjacency: | ||
| 119 | return 4; | ||
| 120 | case InputTopology::Triangles: | ||
| 121 | return 3; | ||
| 122 | case InputTopology::TrianglesAdjacency: | ||
| 123 | return 6; | ||
| 124 | } | ||
| 125 | throw InvalidArgument("Invalid input topology {}", input_topology); | ||
| 126 | } | ||
| 127 | |||
| 128 | Id DefineInput(EmitContext& ctx, Id type, bool per_invocation, | ||
| 129 | std::optional<spv::BuiltIn> builtin = std::nullopt) { | ||
| 130 | switch (ctx.stage) { | ||
| 131 | case Stage::TessellationControl: | ||
| 132 | case Stage::TessellationEval: | ||
| 133 | if (per_invocation) { | ||
| 134 | type = ctx.TypeArray(type, ctx.Const(32u)); | ||
| 135 | } | ||
| 136 | break; | ||
| 137 | case Stage::Geometry: | ||
| 138 | if (per_invocation) { | ||
| 139 | const u32 num_vertices{NumVertices(ctx.runtime_info.input_topology)}; | ||
| 140 | type = ctx.TypeArray(type, ctx.Const(num_vertices)); | ||
| 141 | } | ||
| 142 | break; | ||
| 143 | default: | ||
| 144 | break; | ||
| 145 | } | ||
| 146 | return DefineVariable(ctx, type, builtin, spv::StorageClass::Input); | ||
| 147 | } | ||
| 148 | |||
| 149 | Id DefineOutput(EmitContext& ctx, Id type, std::optional<u32> invocations, | ||
| 150 | std::optional<spv::BuiltIn> builtin = std::nullopt) { | ||
| 151 | if (invocations && ctx.stage == Stage::TessellationControl) { | ||
| 152 | type = ctx.TypeArray(type, ctx.Const(*invocations)); | ||
| 153 | } | ||
| 154 | return DefineVariable(ctx, type, builtin, spv::StorageClass::Output); | ||
| 155 | } | ||
| 156 | |||
| 157 | void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invocations) { | ||
| 158 | static constexpr std::string_view swizzle{"xyzw"}; | ||
| 159 | const size_t base_attr_index{static_cast<size_t>(IR::Attribute::Generic0X) + index * 4}; | ||
| 160 | u32 element{0}; | ||
| 161 | while (element < 4) { | ||
| 162 | const u32 remainder{4 - element}; | ||
| 163 | const TransformFeedbackVarying* xfb_varying{}; | ||
| 164 | if (!ctx.runtime_info.xfb_varyings.empty()) { | ||
| 165 | xfb_varying = &ctx.runtime_info.xfb_varyings[base_attr_index + element]; | ||
| 166 | xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr; | ||
| 167 | } | ||
| 168 | const u32 num_components{xfb_varying ? xfb_varying->components : remainder}; | ||
| 169 | |||
| 170 | const Id id{DefineOutput(ctx, ctx.F32[num_components], invocations)}; | ||
| 171 | ctx.Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); | ||
| 172 | if (element > 0) { | ||
| 173 | ctx.Decorate(id, spv::Decoration::Component, element); | ||
| 174 | } | ||
| 175 | if (xfb_varying) { | ||
| 176 | ctx.Decorate(id, spv::Decoration::XfbBuffer, xfb_varying->buffer); | ||
| 177 | ctx.Decorate(id, spv::Decoration::XfbStride, xfb_varying->stride); | ||
| 178 | ctx.Decorate(id, spv::Decoration::Offset, xfb_varying->offset); | ||
| 179 | } | ||
| 180 | if (num_components < 4 || element > 0) { | ||
| 181 | const std::string_view subswizzle{swizzle.substr(element, num_components)}; | ||
| 182 | ctx.Name(id, fmt::format("out_attr{}_{}", index, subswizzle)); | ||
| 183 | } else { | ||
| 184 | ctx.Name(id, fmt::format("out_attr{}", index)); | ||
| 185 | } | ||
| 186 | const GenericElementInfo info{ | ||
| 187 | .id = id, | ||
| 188 | .first_element = element, | ||
| 189 | .num_components = num_components, | ||
| 190 | }; | ||
| 191 | std::fill_n(ctx.output_generics[index].begin() + element, num_components, info); | ||
| 192 | element += num_components; | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | Id GetAttributeType(EmitContext& ctx, AttributeType type) { | ||
| 197 | switch (type) { | ||
| 198 | case AttributeType::Float: | ||
| 199 | return ctx.F32[4]; | ||
| 200 | case AttributeType::SignedInt: | ||
| 201 | return ctx.TypeVector(ctx.TypeInt(32, true), 4); | ||
| 202 | case AttributeType::UnsignedInt: | ||
| 203 | return ctx.U32[4]; | ||
| 204 | case AttributeType::Disabled: | ||
| 205 | break; | ||
| 206 | } | ||
| 207 | throw InvalidArgument("Invalid attribute type {}", type); | ||
| 208 | } | ||
| 209 | |||
| 210 | std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) { | ||
| 211 | const AttributeType type{ctx.runtime_info.generic_input_types.at(index)}; | ||
| 212 | switch (type) { | ||
| 213 | case AttributeType::Float: | ||
| 214 | return AttrInfo{ctx.input_f32, ctx.F32[1], false}; | ||
| 215 | case AttributeType::UnsignedInt: | ||
| 216 | return AttrInfo{ctx.input_u32, ctx.U32[1], true}; | ||
| 217 | case AttributeType::SignedInt: | ||
| 218 | return AttrInfo{ctx.input_s32, ctx.TypeInt(32, true), true}; | ||
| 219 | case AttributeType::Disabled: | ||
| 220 | return std::nullopt; | ||
| 221 | } | ||
| 222 | throw InvalidArgument("Invalid attribute type {}", type); | ||
| 223 | } | ||
| 224 | |||
| 225 | std::string_view StageName(Stage stage) { | ||
| 226 | switch (stage) { | ||
| 227 | case Stage::VertexA: | ||
| 228 | return "vs_a"; | ||
| 229 | case Stage::VertexB: | ||
| 230 | return "vs"; | ||
| 231 | case Stage::TessellationControl: | ||
| 232 | return "tcs"; | ||
| 233 | case Stage::TessellationEval: | ||
| 234 | return "tes"; | ||
| 235 | case Stage::Geometry: | ||
| 236 | return "gs"; | ||
| 237 | case Stage::Fragment: | ||
| 238 | return "fs"; | ||
| 239 | case Stage::Compute: | ||
| 240 | return "cs"; | ||
| 241 | } | ||
| 242 | throw InvalidArgument("Invalid stage {}", stage); | ||
| 243 | } | ||
| 244 | |||
| 245 | template <typename... Args> | ||
| 246 | void Name(EmitContext& ctx, Id object, std::string_view format_str, Args&&... args) { | ||
| 247 | ctx.Name(object, fmt::format(fmt::runtime(format_str), StageName(ctx.stage), | ||
| 248 | std::forward<Args>(args)...) | ||
| 249 | .c_str()); | ||
| 250 | } | ||
| 251 | |||
| 252 | void DefineConstBuffers(EmitContext& ctx, const Info& info, Id UniformDefinitions::*member_type, | ||
| 253 | u32 binding, Id type, char type_char, u32 element_size) { | ||
| 254 | const Id array_type{ctx.TypeArray(type, ctx.Const(65536U / element_size))}; | ||
| 255 | ctx.Decorate(array_type, spv::Decoration::ArrayStride, element_size); | ||
| 256 | |||
| 257 | const Id struct_type{ctx.TypeStruct(array_type)}; | ||
| 258 | Name(ctx, struct_type, "{}_cbuf_block_{}{}", ctx.stage, type_char, element_size * CHAR_BIT); | ||
| 259 | ctx.Decorate(struct_type, spv::Decoration::Block); | ||
| 260 | ctx.MemberName(struct_type, 0, "data"); | ||
| 261 | ctx.MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U); | ||
| 262 | |||
| 263 | const Id struct_pointer_type{ctx.TypePointer(spv::StorageClass::Uniform, struct_type)}; | ||
| 264 | const Id uniform_type{ctx.TypePointer(spv::StorageClass::Uniform, type)}; | ||
| 265 | ctx.uniform_types.*member_type = uniform_type; | ||
| 266 | |||
| 267 | for (const ConstantBufferDescriptor& desc : info.constant_buffer_descriptors) { | ||
| 268 | const Id id{ctx.AddGlobalVariable(struct_pointer_type, spv::StorageClass::Uniform)}; | ||
| 269 | ctx.Decorate(id, spv::Decoration::Binding, binding); | ||
| 270 | ctx.Decorate(id, spv::Decoration::DescriptorSet, 0U); | ||
| 271 | ctx.Name(id, fmt::format("c{}", desc.index)); | ||
| 272 | for (size_t i = 0; i < desc.count; ++i) { | ||
| 273 | ctx.cbufs[desc.index + i].*member_type = id; | ||
| 274 | } | ||
| 275 | if (ctx.profile.supported_spirv >= 0x00010400) { | ||
| 276 | ctx.interfaces.push_back(id); | ||
| 277 | } | ||
| 278 | binding += desc.count; | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | void DefineSsbos(EmitContext& ctx, StorageTypeDefinition& type_def, | ||
| 283 | Id StorageDefinitions::*member_type, const Info& info, u32 binding, Id type, | ||
| 284 | u32 stride) { | ||
| 285 | const Id array_type{ctx.TypeRuntimeArray(type)}; | ||
| 286 | ctx.Decorate(array_type, spv::Decoration::ArrayStride, stride); | ||
| 287 | |||
| 288 | const Id struct_type{ctx.TypeStruct(array_type)}; | ||
| 289 | ctx.Decorate(struct_type, spv::Decoration::Block); | ||
| 290 | ctx.MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U); | ||
| 291 | |||
| 292 | const Id struct_pointer{ctx.TypePointer(spv::StorageClass::StorageBuffer, struct_type)}; | ||
| 293 | type_def.array = struct_pointer; | ||
| 294 | type_def.element = ctx.TypePointer(spv::StorageClass::StorageBuffer, type); | ||
| 295 | |||
| 296 | u32 index{}; | ||
| 297 | for (const StorageBufferDescriptor& desc : info.storage_buffers_descriptors) { | ||
| 298 | const Id id{ctx.AddGlobalVariable(struct_pointer, spv::StorageClass::StorageBuffer)}; | ||
| 299 | ctx.Decorate(id, spv::Decoration::Binding, binding); | ||
| 300 | ctx.Decorate(id, spv::Decoration::DescriptorSet, 0U); | ||
| 301 | ctx.Name(id, fmt::format("ssbo{}", index)); | ||
| 302 | if (ctx.profile.supported_spirv >= 0x00010400) { | ||
| 303 | ctx.interfaces.push_back(id); | ||
| 304 | } | ||
| 305 | for (size_t i = 0; i < desc.count; ++i) { | ||
| 306 | ctx.ssbos[index + i].*member_type = id; | ||
| 307 | } | ||
| 308 | index += desc.count; | ||
| 309 | binding += desc.count; | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | Id CasFunction(EmitContext& ctx, Operation operation, Id value_type) { | ||
| 314 | const Id func_type{ctx.TypeFunction(value_type, value_type, value_type)}; | ||
| 315 | const Id func{ctx.OpFunction(value_type, spv::FunctionControlMask::MaskNone, func_type)}; | ||
| 316 | const Id op_a{ctx.OpFunctionParameter(value_type)}; | ||
| 317 | const Id op_b{ctx.OpFunctionParameter(value_type)}; | ||
| 318 | ctx.AddLabel(); | ||
| 319 | Id result{}; | ||
| 320 | switch (operation) { | ||
| 321 | case Operation::Increment: { | ||
| 322 | const Id pred{ctx.OpUGreaterThanEqual(ctx.U1, op_a, op_b)}; | ||
| 323 | const Id incr{ctx.OpIAdd(value_type, op_a, ctx.Constant(value_type, 1))}; | ||
| 324 | result = ctx.OpSelect(value_type, pred, ctx.u32_zero_value, incr); | ||
| 325 | break; | ||
| 326 | } | ||
| 327 | case Operation::Decrement: { | ||
| 328 | const Id lhs{ctx.OpIEqual(ctx.U1, op_a, ctx.Constant(value_type, 0u))}; | ||
| 329 | const Id rhs{ctx.OpUGreaterThan(ctx.U1, op_a, op_b)}; | ||
| 330 | const Id pred{ctx.OpLogicalOr(ctx.U1, lhs, rhs)}; | ||
| 331 | const Id decr{ctx.OpISub(value_type, op_a, ctx.Constant(value_type, 1))}; | ||
| 332 | result = ctx.OpSelect(value_type, pred, op_b, decr); | ||
| 333 | break; | ||
| 334 | } | ||
| 335 | case Operation::FPAdd: | ||
| 336 | result = ctx.OpFAdd(value_type, op_a, op_b); | ||
| 337 | break; | ||
| 338 | case Operation::FPMin: | ||
| 339 | result = ctx.OpFMin(value_type, op_a, op_b); | ||
| 340 | break; | ||
| 341 | case Operation::FPMax: | ||
| 342 | result = ctx.OpFMax(value_type, op_a, op_b); | ||
| 343 | break; | ||
| 344 | default: | ||
| 345 | break; | ||
| 346 | } | ||
| 347 | ctx.OpReturnValue(result); | ||
| 348 | ctx.OpFunctionEnd(); | ||
| 349 | return func; | ||
| 350 | } | ||
| 351 | |||
| 352 | Id CasLoop(EmitContext& ctx, Operation operation, Id array_pointer, Id element_pointer, | ||
| 353 | Id value_type, Id memory_type, spv::Scope scope) { | ||
| 354 | const bool is_shared{scope == spv::Scope::Workgroup}; | ||
| 355 | const bool is_struct{!is_shared || ctx.profile.support_explicit_workgroup_layout}; | ||
| 356 | const Id cas_func{CasFunction(ctx, operation, value_type)}; | ||
| 357 | const Id zero{ctx.u32_zero_value}; | ||
| 358 | const Id scope_id{ctx.Const(static_cast<u32>(scope))}; | ||
| 359 | |||
| 360 | const Id loop_header{ctx.OpLabel()}; | ||
| 361 | const Id continue_block{ctx.OpLabel()}; | ||
| 362 | const Id merge_block{ctx.OpLabel()}; | ||
| 363 | const Id func_type{is_shared | ||
| 364 | ? ctx.TypeFunction(value_type, ctx.U32[1], value_type) | ||
| 365 | : ctx.TypeFunction(value_type, ctx.U32[1], value_type, array_pointer)}; | ||
| 366 | |||
| 367 | const Id func{ctx.OpFunction(value_type, spv::FunctionControlMask::MaskNone, func_type)}; | ||
| 368 | const Id index{ctx.OpFunctionParameter(ctx.U32[1])}; | ||
| 369 | const Id op_b{ctx.OpFunctionParameter(value_type)}; | ||
| 370 | const Id base{is_shared ? ctx.shared_memory_u32 : ctx.OpFunctionParameter(array_pointer)}; | ||
| 371 | ctx.AddLabel(); | ||
| 372 | ctx.OpBranch(loop_header); | ||
| 373 | ctx.AddLabel(loop_header); | ||
| 374 | |||
| 375 | ctx.OpLoopMerge(merge_block, continue_block, spv::LoopControlMask::MaskNone); | ||
| 376 | ctx.OpBranch(continue_block); | ||
| 377 | |||
| 378 | ctx.AddLabel(continue_block); | ||
| 379 | const Id word_pointer{is_struct ? ctx.OpAccessChain(element_pointer, base, zero, index) | ||
| 380 | : ctx.OpAccessChain(element_pointer, base, index)}; | ||
| 381 | if (value_type.value == ctx.F32[2].value) { | ||
| 382 | const Id u32_value{ctx.OpLoad(ctx.U32[1], word_pointer)}; | ||
| 383 | const Id value{ctx.OpUnpackHalf2x16(ctx.F32[2], u32_value)}; | ||
| 384 | const Id new_value{ctx.OpFunctionCall(value_type, cas_func, value, op_b)}; | ||
| 385 | const Id u32_new_value{ctx.OpPackHalf2x16(ctx.U32[1], new_value)}; | ||
| 386 | const Id atomic_res{ctx.OpAtomicCompareExchange(ctx.U32[1], word_pointer, scope_id, zero, | ||
| 387 | zero, u32_new_value, u32_value)}; | ||
| 388 | const Id success{ctx.OpIEqual(ctx.U1, atomic_res, u32_value)}; | ||
| 389 | ctx.OpBranchConditional(success, merge_block, loop_header); | ||
| 390 | |||
| 391 | ctx.AddLabel(merge_block); | ||
| 392 | ctx.OpReturnValue(ctx.OpUnpackHalf2x16(ctx.F32[2], atomic_res)); | ||
| 393 | } else { | ||
| 394 | const Id value{ctx.OpLoad(memory_type, word_pointer)}; | ||
| 395 | const bool matching_type{value_type.value == memory_type.value}; | ||
| 396 | const Id bitcast_value{matching_type ? value : ctx.OpBitcast(value_type, value)}; | ||
| 397 | const Id cal_res{ctx.OpFunctionCall(value_type, cas_func, bitcast_value, op_b)}; | ||
| 398 | const Id new_value{matching_type ? cal_res : ctx.OpBitcast(memory_type, cal_res)}; | ||
| 399 | const Id atomic_res{ctx.OpAtomicCompareExchange(ctx.U32[1], word_pointer, scope_id, zero, | ||
| 400 | zero, new_value, value)}; | ||
| 401 | const Id success{ctx.OpIEqual(ctx.U1, atomic_res, value)}; | ||
| 402 | ctx.OpBranchConditional(success, merge_block, loop_header); | ||
| 403 | |||
| 404 | ctx.AddLabel(merge_block); | ||
| 405 | ctx.OpReturnValue(ctx.OpBitcast(value_type, atomic_res)); | ||
| 406 | } | ||
| 407 | ctx.OpFunctionEnd(); | ||
| 408 | return func; | ||
| 409 | } | ||
| 410 | |||
| 411 | template <typename Desc> | ||
| 412 | std::string NameOf(Stage stage, const Desc& desc, std::string_view prefix) { | ||
| 413 | if (desc.count > 1) { | ||
| 414 | return fmt::format("{}_{}{}_{:02x}x{}", StageName(stage), prefix, desc.cbuf_index, | ||
| 415 | desc.cbuf_offset, desc.count); | ||
| 416 | } else { | ||
| 417 | return fmt::format("{}_{}{}_{:02x}", StageName(stage), prefix, desc.cbuf_index, | ||
| 418 | desc.cbuf_offset); | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | Id DescType(EmitContext& ctx, Id sampled_type, Id pointer_type, u32 count) { | ||
| 423 | if (count > 1) { | ||
| 424 | const Id array_type{ctx.TypeArray(sampled_type, ctx.Const(count))}; | ||
| 425 | return ctx.TypePointer(spv::StorageClass::UniformConstant, array_type); | ||
| 426 | } else { | ||
| 427 | return pointer_type; | ||
| 428 | } | ||
| 429 | } | ||
| 430 | } // Anonymous namespace | ||
| 431 | |||
| 432 | void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) { | ||
| 433 | defs[0] = sirit_ctx.Name(base_type, name); | ||
| 434 | |||
| 435 | std::array<char, 6> def_name; | ||
| 436 | for (int i = 1; i < 4; ++i) { | ||
| 437 | const std::string_view def_name_view( | ||
| 438 | def_name.data(), | ||
| 439 | fmt::format_to_n(def_name.data(), def_name.size(), "{}x{}", name, i + 1).size); | ||
| 440 | defs[static_cast<size_t>(i)] = | ||
| 441 | sirit_ctx.Name(sirit_ctx.TypeVector(base_type, i + 1), def_name_view); | ||
| 442 | } | ||
| 443 | } | ||
| 444 | |||
| 445 | EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_info_, | ||
| 446 | IR::Program& program, Bindings& bindings) | ||
| 447 | : Sirit::Module(profile_.supported_spirv), profile{profile_}, | ||
| 448 | runtime_info{runtime_info_}, stage{program.stage} { | ||
| 449 | const bool is_unified{profile.unified_descriptor_binding}; | ||
| 450 | u32& uniform_binding{is_unified ? bindings.unified : bindings.uniform_buffer}; | ||
| 451 | u32& storage_binding{is_unified ? bindings.unified : bindings.storage_buffer}; | ||
| 452 | u32& texture_binding{is_unified ? bindings.unified : bindings.texture}; | ||
| 453 | u32& image_binding{is_unified ? bindings.unified : bindings.image}; | ||
| 454 | AddCapability(spv::Capability::Shader); | ||
| 455 | DefineCommonTypes(program.info); | ||
| 456 | DefineCommonConstants(); | ||
| 457 | DefineInterfaces(program); | ||
| 458 | DefineLocalMemory(program); | ||
| 459 | DefineSharedMemory(program); | ||
| 460 | DefineSharedMemoryFunctions(program); | ||
| 461 | DefineConstantBuffers(program.info, uniform_binding); | ||
| 462 | DefineStorageBuffers(program.info, storage_binding); | ||
| 463 | DefineTextureBuffers(program.info, texture_binding); | ||
| 464 | DefineImageBuffers(program.info, image_binding); | ||
| 465 | DefineTextures(program.info, texture_binding); | ||
| 466 | DefineImages(program.info, image_binding); | ||
| 467 | DefineAttributeMemAccess(program.info); | ||
| 468 | DefineGlobalMemoryFunctions(program.info); | ||
| 469 | } | ||
| 470 | |||
| 471 | EmitContext::~EmitContext() = default; | ||
| 472 | |||
| 473 | Id EmitContext::Def(const IR::Value& value) { | ||
| 474 | if (!value.IsImmediate()) { | ||
| 475 | return value.InstRecursive()->Definition<Id>(); | ||
| 476 | } | ||
| 477 | switch (value.Type()) { | ||
| 478 | case IR::Type::Void: | ||
| 479 | // Void instructions are used for optional arguments (e.g. texture offsets) | ||
| 480 | // They are not meant to be used in the SPIR-V module | ||
| 481 | return Id{}; | ||
| 482 | case IR::Type::U1: | ||
| 483 | return value.U1() ? true_value : false_value; | ||
| 484 | case IR::Type::U32: | ||
| 485 | return Const(value.U32()); | ||
| 486 | case IR::Type::U64: | ||
| 487 | return Constant(U64, value.U64()); | ||
| 488 | case IR::Type::F32: | ||
| 489 | return Const(value.F32()); | ||
| 490 | case IR::Type::F64: | ||
| 491 | return Constant(F64[1], value.F64()); | ||
| 492 | default: | ||
| 493 | throw NotImplementedException("Immediate type {}", value.Type()); | ||
| 494 | } | ||
| 495 | } | ||
| 496 | |||
| 497 | Id EmitContext::BitOffset8(const IR::Value& offset) { | ||
| 498 | if (offset.IsImmediate()) { | ||
| 499 | return Const((offset.U32() % 4) * 8); | ||
| 500 | } | ||
| 501 | return OpBitwiseAnd(U32[1], OpShiftLeftLogical(U32[1], Def(offset), Const(3u)), Const(24u)); | ||
| 502 | } | ||
| 503 | |||
| 504 | Id EmitContext::BitOffset16(const IR::Value& offset) { | ||
| 505 | if (offset.IsImmediate()) { | ||
| 506 | return Const(((offset.U32() / 2) % 2) * 16); | ||
| 507 | } | ||
| 508 | return OpBitwiseAnd(U32[1], OpShiftLeftLogical(U32[1], Def(offset), Const(3u)), Const(16u)); | ||
| 509 | } | ||
| 510 | |||
| 511 | void EmitContext::DefineCommonTypes(const Info& info) { | ||
| 512 | void_id = TypeVoid(); | ||
| 513 | |||
| 514 | U1 = Name(TypeBool(), "u1"); | ||
| 515 | |||
| 516 | F32.Define(*this, TypeFloat(32), "f32"); | ||
| 517 | U32.Define(*this, TypeInt(32, false), "u32"); | ||
| 518 | S32.Define(*this, TypeInt(32, true), "s32"); | ||
| 519 | |||
| 520 | private_u32 = Name(TypePointer(spv::StorageClass::Private, U32[1]), "private_u32"); | ||
| 521 | |||
| 522 | input_f32 = Name(TypePointer(spv::StorageClass::Input, F32[1]), "input_f32"); | ||
| 523 | input_u32 = Name(TypePointer(spv::StorageClass::Input, U32[1]), "input_u32"); | ||
| 524 | input_s32 = Name(TypePointer(spv::StorageClass::Input, TypeInt(32, true)), "input_s32"); | ||
| 525 | |||
| 526 | output_f32 = Name(TypePointer(spv::StorageClass::Output, F32[1]), "output_f32"); | ||
| 527 | output_u32 = Name(TypePointer(spv::StorageClass::Output, U32[1]), "output_u32"); | ||
| 528 | |||
| 529 | if (info.uses_int8 && profile.support_int8) { | ||
| 530 | AddCapability(spv::Capability::Int8); | ||
| 531 | U8 = Name(TypeInt(8, false), "u8"); | ||
| 532 | S8 = Name(TypeInt(8, true), "s8"); | ||
| 533 | } | ||
| 534 | if (info.uses_int16 && profile.support_int16) { | ||
| 535 | AddCapability(spv::Capability::Int16); | ||
| 536 | U16 = Name(TypeInt(16, false), "u16"); | ||
| 537 | S16 = Name(TypeInt(16, true), "s16"); | ||
| 538 | } | ||
| 539 | if (info.uses_int64) { | ||
| 540 | AddCapability(spv::Capability::Int64); | ||
| 541 | U64 = Name(TypeInt(64, false), "u64"); | ||
| 542 | } | ||
| 543 | if (info.uses_fp16) { | ||
| 544 | AddCapability(spv::Capability::Float16); | ||
| 545 | F16.Define(*this, TypeFloat(16), "f16"); | ||
| 546 | } | ||
| 547 | if (info.uses_fp64) { | ||
| 548 | AddCapability(spv::Capability::Float64); | ||
| 549 | F64.Define(*this, TypeFloat(64), "f64"); | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | void EmitContext::DefineCommonConstants() { | ||
| 554 | true_value = ConstantTrue(U1); | ||
| 555 | false_value = ConstantFalse(U1); | ||
| 556 | u32_zero_value = Const(0U); | ||
| 557 | f32_zero_value = Const(0.0f); | ||
| 558 | } | ||
| 559 | |||
| 560 | void EmitContext::DefineInterfaces(const IR::Program& program) { | ||
| 561 | DefineInputs(program); | ||
| 562 | DefineOutputs(program); | ||
| 563 | } | ||
| 564 | |||
| 565 | void EmitContext::DefineLocalMemory(const IR::Program& program) { | ||
| 566 | if (program.local_memory_size == 0) { | ||
| 567 | return; | ||
| 568 | } | ||
| 569 | const u32 num_elements{Common::DivCeil(program.local_memory_size, 4U)}; | ||
| 570 | const Id type{TypeArray(U32[1], Const(num_elements))}; | ||
| 571 | const Id pointer{TypePointer(spv::StorageClass::Private, type)}; | ||
| 572 | local_memory = AddGlobalVariable(pointer, spv::StorageClass::Private); | ||
| 573 | if (profile.supported_spirv >= 0x00010400) { | ||
| 574 | interfaces.push_back(local_memory); | ||
| 575 | } | ||
| 576 | } | ||
| 577 | |||
| 578 | void EmitContext::DefineSharedMemory(const IR::Program& program) { | ||
| 579 | if (program.shared_memory_size == 0) { | ||
| 580 | return; | ||
| 581 | } | ||
| 582 | const auto make{[&](Id element_type, u32 element_size) { | ||
| 583 | const u32 num_elements{Common::DivCeil(program.shared_memory_size, element_size)}; | ||
| 584 | const Id array_type{TypeArray(element_type, Const(num_elements))}; | ||
| 585 | Decorate(array_type, spv::Decoration::ArrayStride, element_size); | ||
| 586 | |||
| 587 | const Id struct_type{TypeStruct(array_type)}; | ||
| 588 | MemberDecorate(struct_type, 0U, spv::Decoration::Offset, 0U); | ||
| 589 | Decorate(struct_type, spv::Decoration::Block); | ||
| 590 | |||
| 591 | const Id pointer{TypePointer(spv::StorageClass::Workgroup, struct_type)}; | ||
| 592 | const Id element_pointer{TypePointer(spv::StorageClass::Workgroup, element_type)}; | ||
| 593 | const Id variable{AddGlobalVariable(pointer, spv::StorageClass::Workgroup)}; | ||
| 594 | Decorate(variable, spv::Decoration::Aliased); | ||
| 595 | interfaces.push_back(variable); | ||
| 596 | |||
| 597 | return std::make_tuple(variable, element_pointer, pointer); | ||
| 598 | }}; | ||
| 599 | if (profile.support_explicit_workgroup_layout) { | ||
| 600 | AddExtension("SPV_KHR_workgroup_memory_explicit_layout"); | ||
| 601 | AddCapability(spv::Capability::WorkgroupMemoryExplicitLayoutKHR); | ||
| 602 | if (program.info.uses_int8) { | ||
| 603 | AddCapability(spv::Capability::WorkgroupMemoryExplicitLayout8BitAccessKHR); | ||
| 604 | std::tie(shared_memory_u8, shared_u8, std::ignore) = make(U8, 1); | ||
| 605 | } | ||
| 606 | if (program.info.uses_int16) { | ||
| 607 | AddCapability(spv::Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR); | ||
| 608 | std::tie(shared_memory_u16, shared_u16, std::ignore) = make(U16, 2); | ||
| 609 | } | ||
| 610 | if (program.info.uses_int64) { | ||
| 611 | std::tie(shared_memory_u64, shared_u64, std::ignore) = make(U64, 8); | ||
| 612 | } | ||
| 613 | std::tie(shared_memory_u32, shared_u32, shared_memory_u32_type) = make(U32[1], 4); | ||
| 614 | std::tie(shared_memory_u32x2, shared_u32x2, std::ignore) = make(U32[2], 8); | ||
| 615 | std::tie(shared_memory_u32x4, shared_u32x4, std::ignore) = make(U32[4], 16); | ||
| 616 | return; | ||
| 617 | } | ||
| 618 | const u32 num_elements{Common::DivCeil(program.shared_memory_size, 4U)}; | ||
| 619 | const Id type{TypeArray(U32[1], Const(num_elements))}; | ||
| 620 | shared_memory_u32_type = TypePointer(spv::StorageClass::Workgroup, type); | ||
| 621 | |||
| 622 | shared_u32 = TypePointer(spv::StorageClass::Workgroup, U32[1]); | ||
| 623 | shared_memory_u32 = AddGlobalVariable(shared_memory_u32_type, spv::StorageClass::Workgroup); | ||
| 624 | interfaces.push_back(shared_memory_u32); | ||
| 625 | |||
| 626 | const Id func_type{TypeFunction(void_id, U32[1], U32[1])}; | ||
| 627 | const auto make_function{[&](u32 mask, u32 size) { | ||
| 628 | const Id loop_header{OpLabel()}; | ||
| 629 | const Id continue_block{OpLabel()}; | ||
| 630 | const Id merge_block{OpLabel()}; | ||
| 631 | |||
| 632 | const Id func{OpFunction(void_id, spv::FunctionControlMask::MaskNone, func_type)}; | ||
| 633 | const Id offset{OpFunctionParameter(U32[1])}; | ||
| 634 | const Id insert_value{OpFunctionParameter(U32[1])}; | ||
| 635 | AddLabel(); | ||
| 636 | OpBranch(loop_header); | ||
| 637 | |||
| 638 | AddLabel(loop_header); | ||
| 639 | const Id word_offset{OpShiftRightArithmetic(U32[1], offset, Const(2U))}; | ||
| 640 | const Id shift_offset{OpShiftLeftLogical(U32[1], offset, Const(3U))}; | ||
| 641 | const Id bit_offset{OpBitwiseAnd(U32[1], shift_offset, Const(mask))}; | ||
| 642 | const Id count{Const(size)}; | ||
| 643 | OpLoopMerge(merge_block, continue_block, spv::LoopControlMask::MaskNone); | ||
| 644 | OpBranch(continue_block); | ||
| 645 | |||
| 646 | AddLabel(continue_block); | ||
| 647 | const Id word_pointer{OpAccessChain(shared_u32, shared_memory_u32, word_offset)}; | ||
| 648 | const Id old_value{OpLoad(U32[1], word_pointer)}; | ||
| 649 | const Id new_value{OpBitFieldInsert(U32[1], old_value, insert_value, bit_offset, count)}; | ||
| 650 | const Id atomic_res{OpAtomicCompareExchange(U32[1], word_pointer, Const(1U), u32_zero_value, | ||
| 651 | u32_zero_value, new_value, old_value)}; | ||
| 652 | const Id success{OpIEqual(U1, atomic_res, old_value)}; | ||
| 653 | OpBranchConditional(success, merge_block, loop_header); | ||
| 654 | |||
| 655 | AddLabel(merge_block); | ||
| 656 | OpReturn(); | ||
| 657 | OpFunctionEnd(); | ||
| 658 | return func; | ||
| 659 | }}; | ||
| 660 | if (program.info.uses_int8) { | ||
| 661 | shared_store_u8_func = make_function(24, 8); | ||
| 662 | } | ||
| 663 | if (program.info.uses_int16) { | ||
| 664 | shared_store_u16_func = make_function(16, 16); | ||
| 665 | } | ||
| 666 | } | ||
| 667 | |||
| 668 | void EmitContext::DefineSharedMemoryFunctions(const IR::Program& program) { | ||
| 669 | if (program.info.uses_shared_increment) { | ||
| 670 | increment_cas_shared = CasLoop(*this, Operation::Increment, shared_memory_u32_type, | ||
| 671 | shared_u32, U32[1], U32[1], spv::Scope::Workgroup); | ||
| 672 | } | ||
| 673 | if (program.info.uses_shared_decrement) { | ||
| 674 | decrement_cas_shared = CasLoop(*this, Operation::Decrement, shared_memory_u32_type, | ||
| 675 | shared_u32, U32[1], U32[1], spv::Scope::Workgroup); | ||
| 676 | } | ||
| 677 | } | ||
| 678 | |||
| 679 | void EmitContext::DefineAttributeMemAccess(const Info& info) { | ||
| 680 | const auto make_load{[&] { | ||
| 681 | const bool is_array{stage == Stage::Geometry}; | ||
| 682 | const Id end_block{OpLabel()}; | ||
| 683 | const Id default_label{OpLabel()}; | ||
| 684 | |||
| 685 | const Id func_type_load{is_array ? TypeFunction(F32[1], U32[1], U32[1]) | ||
| 686 | : TypeFunction(F32[1], U32[1])}; | ||
| 687 | const Id func{OpFunction(F32[1], spv::FunctionControlMask::MaskNone, func_type_load)}; | ||
| 688 | const Id offset{OpFunctionParameter(U32[1])}; | ||
| 689 | const Id vertex{is_array ? OpFunctionParameter(U32[1]) : Id{}}; | ||
| 690 | |||
| 691 | AddLabel(); | ||
| 692 | const Id base_index{OpShiftRightArithmetic(U32[1], offset, Const(2U))}; | ||
| 693 | const Id masked_index{OpBitwiseAnd(U32[1], base_index, Const(3U))}; | ||
| 694 | const Id compare_index{OpShiftRightArithmetic(U32[1], base_index, Const(2U))}; | ||
| 695 | std::vector<Sirit::Literal> literals; | ||
| 696 | std::vector<Id> labels; | ||
| 697 | if (info.loads.AnyComponent(IR::Attribute::PositionX)) { | ||
| 698 | literals.push_back(static_cast<u32>(IR::Attribute::PositionX) >> 2); | ||
| 699 | labels.push_back(OpLabel()); | ||
| 700 | } | ||
| 701 | const u32 base_attribute_value = static_cast<u32>(IR::Attribute::Generic0X) >> 2; | ||
| 702 | for (u32 index = 0; index < static_cast<u32>(IR::NUM_GENERICS); ++index) { | ||
| 703 | if (!info.loads.Generic(index)) { | ||
| 704 | continue; | ||
| 705 | } | ||
| 706 | literals.push_back(base_attribute_value + index); | ||
| 707 | labels.push_back(OpLabel()); | ||
| 708 | } | ||
| 709 | OpSelectionMerge(end_block, spv::SelectionControlMask::MaskNone); | ||
| 710 | OpSwitch(compare_index, default_label, literals, labels); | ||
| 711 | AddLabel(default_label); | ||
| 712 | OpReturnValue(Const(0.0f)); | ||
| 713 | size_t label_index{0}; | ||
| 714 | if (info.loads.AnyComponent(IR::Attribute::PositionX)) { | ||
| 715 | AddLabel(labels[label_index]); | ||
| 716 | const Id pointer{is_array | ||
| 717 | ? OpAccessChain(input_f32, input_position, vertex, masked_index) | ||
| 718 | : OpAccessChain(input_f32, input_position, masked_index)}; | ||
| 719 | const Id result{OpLoad(F32[1], pointer)}; | ||
| 720 | OpReturnValue(result); | ||
| 721 | ++label_index; | ||
| 722 | } | ||
| 723 | for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { | ||
| 724 | if (!info.loads.Generic(index)) { | ||
| 725 | continue; | ||
| 726 | } | ||
| 727 | AddLabel(labels[label_index]); | ||
| 728 | const auto type{AttrTypes(*this, static_cast<u32>(index))}; | ||
| 729 | if (!type) { | ||
| 730 | OpReturnValue(Const(0.0f)); | ||
| 731 | ++label_index; | ||
| 732 | continue; | ||
| 733 | } | ||
| 734 | const Id generic_id{input_generics.at(index)}; | ||
| 735 | const Id pointer{is_array | ||
| 736 | ? OpAccessChain(type->pointer, generic_id, vertex, masked_index) | ||
| 737 | : OpAccessChain(type->pointer, generic_id, masked_index)}; | ||
| 738 | const Id value{OpLoad(type->id, pointer)}; | ||
| 739 | const Id result{type->needs_cast ? OpBitcast(F32[1], value) : value}; | ||
| 740 | OpReturnValue(result); | ||
| 741 | ++label_index; | ||
| 742 | } | ||
| 743 | AddLabel(end_block); | ||
| 744 | OpUnreachable(); | ||
| 745 | OpFunctionEnd(); | ||
| 746 | return func; | ||
| 747 | }}; | ||
| 748 | const auto make_store{[&] { | ||
| 749 | const Id end_block{OpLabel()}; | ||
| 750 | const Id default_label{OpLabel()}; | ||
| 751 | |||
| 752 | const Id func_type_store{TypeFunction(void_id, U32[1], F32[1])}; | ||
| 753 | const Id func{OpFunction(void_id, spv::FunctionControlMask::MaskNone, func_type_store)}; | ||
| 754 | const Id offset{OpFunctionParameter(U32[1])}; | ||
| 755 | const Id store_value{OpFunctionParameter(F32[1])}; | ||
| 756 | AddLabel(); | ||
| 757 | const Id base_index{OpShiftRightArithmetic(U32[1], offset, Const(2U))}; | ||
| 758 | const Id masked_index{OpBitwiseAnd(U32[1], base_index, Const(3U))}; | ||
| 759 | const Id compare_index{OpShiftRightArithmetic(U32[1], base_index, Const(2U))}; | ||
| 760 | std::vector<Sirit::Literal> literals; | ||
| 761 | std::vector<Id> labels; | ||
| 762 | if (info.stores.AnyComponent(IR::Attribute::PositionX)) { | ||
| 763 | literals.push_back(static_cast<u32>(IR::Attribute::PositionX) >> 2); | ||
| 764 | labels.push_back(OpLabel()); | ||
| 765 | } | ||
| 766 | const u32 base_attribute_value = static_cast<u32>(IR::Attribute::Generic0X) >> 2; | ||
| 767 | for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { | ||
| 768 | if (!info.stores.Generic(index)) { | ||
| 769 | continue; | ||
| 770 | } | ||
| 771 | literals.push_back(base_attribute_value + static_cast<u32>(index)); | ||
| 772 | labels.push_back(OpLabel()); | ||
| 773 | } | ||
| 774 | if (info.stores.ClipDistances()) { | ||
| 775 | literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance0) >> 2); | ||
| 776 | labels.push_back(OpLabel()); | ||
| 777 | literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance4) >> 2); | ||
| 778 | labels.push_back(OpLabel()); | ||
| 779 | } | ||
| 780 | OpSelectionMerge(end_block, spv::SelectionControlMask::MaskNone); | ||
| 781 | OpSwitch(compare_index, default_label, literals, labels); | ||
| 782 | AddLabel(default_label); | ||
| 783 | OpReturn(); | ||
| 784 | size_t label_index{0}; | ||
| 785 | if (info.stores.AnyComponent(IR::Attribute::PositionX)) { | ||
| 786 | AddLabel(labels[label_index]); | ||
| 787 | const Id pointer{OpAccessChain(output_f32, output_position, masked_index)}; | ||
| 788 | OpStore(pointer, store_value); | ||
| 789 | OpReturn(); | ||
| 790 | ++label_index; | ||
| 791 | } | ||
| 792 | for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { | ||
| 793 | if (!info.stores.Generic(index)) { | ||
| 794 | continue; | ||
| 795 | } | ||
| 796 | if (output_generics[index][0].num_components != 4) { | ||
| 797 | throw NotImplementedException("Physical stores and transform feedbacks"); | ||
| 798 | } | ||
| 799 | AddLabel(labels[label_index]); | ||
| 800 | const Id generic_id{output_generics[index][0].id}; | ||
| 801 | const Id pointer{OpAccessChain(output_f32, generic_id, masked_index)}; | ||
| 802 | OpStore(pointer, store_value); | ||
| 803 | OpReturn(); | ||
| 804 | ++label_index; | ||
| 805 | } | ||
| 806 | if (info.stores.ClipDistances()) { | ||
| 807 | AddLabel(labels[label_index]); | ||
| 808 | const Id pointer{OpAccessChain(output_f32, clip_distances, masked_index)}; | ||
| 809 | OpStore(pointer, store_value); | ||
| 810 | OpReturn(); | ||
| 811 | ++label_index; | ||
| 812 | AddLabel(labels[label_index]); | ||
| 813 | const Id fixed_index{OpIAdd(U32[1], masked_index, Const(4U))}; | ||
| 814 | const Id pointer2{OpAccessChain(output_f32, clip_distances, fixed_index)}; | ||
| 815 | OpStore(pointer2, store_value); | ||
| 816 | OpReturn(); | ||
| 817 | ++label_index; | ||
| 818 | } | ||
| 819 | AddLabel(end_block); | ||
| 820 | OpUnreachable(); | ||
| 821 | OpFunctionEnd(); | ||
| 822 | return func; | ||
| 823 | }}; | ||
| 824 | if (info.loads_indexed_attributes) { | ||
| 825 | indexed_load_func = make_load(); | ||
| 826 | } | ||
| 827 | if (info.stores_indexed_attributes) { | ||
| 828 | indexed_store_func = make_store(); | ||
| 829 | } | ||
| 830 | } | ||
| 831 | |||
| 832 | void EmitContext::DefineGlobalMemoryFunctions(const Info& info) { | ||
| 833 | if (!info.uses_global_memory || !profile.support_int64) { | ||
| 834 | return; | ||
| 835 | } | ||
| 836 | using DefPtr = Id StorageDefinitions::*; | ||
| 837 | const Id zero{u32_zero_value}; | ||
| 838 | const auto define_body{[&](DefPtr ssbo_member, Id addr, Id element_pointer, u32 shift, | ||
| 839 | auto&& callback) { | ||
| 840 | AddLabel(); | ||
| 841 | const size_t num_buffers{info.storage_buffers_descriptors.size()}; | ||
| 842 | for (size_t index = 0; index < num_buffers; ++index) { | ||
| 843 | if (!info.nvn_buffer_used[index]) { | ||
| 844 | continue; | ||
| 845 | } | ||
| 846 | const auto& ssbo{info.storage_buffers_descriptors[index]}; | ||
| 847 | const Id ssbo_addr_cbuf_offset{Const(ssbo.cbuf_offset / 8)}; | ||
| 848 | const Id ssbo_size_cbuf_offset{Const(ssbo.cbuf_offset / 4 + 2)}; | ||
| 849 | const Id ssbo_addr_pointer{OpAccessChain( | ||
| 850 | uniform_types.U32x2, cbufs[ssbo.cbuf_index].U32x2, zero, ssbo_addr_cbuf_offset)}; | ||
| 851 | const Id ssbo_size_pointer{OpAccessChain(uniform_types.U32, cbufs[ssbo.cbuf_index].U32, | ||
| 852 | zero, ssbo_size_cbuf_offset)}; | ||
| 853 | |||
| 854 | const Id ssbo_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))}; | ||
| 855 | const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))}; | ||
| 856 | const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)}; | ||
| 857 | const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr), | ||
| 858 | OpULessThan(U1, addr, ssbo_end))}; | ||
| 859 | const Id then_label{OpLabel()}; | ||
| 860 | const Id else_label{OpLabel()}; | ||
| 861 | OpSelectionMerge(else_label, spv::SelectionControlMask::MaskNone); | ||
| 862 | OpBranchConditional(cond, then_label, else_label); | ||
| 863 | AddLabel(then_label); | ||
| 864 | const Id ssbo_id{ssbos[index].*ssbo_member}; | ||
| 865 | const Id ssbo_offset{OpUConvert(U32[1], OpISub(U64, addr, ssbo_addr))}; | ||
| 866 | const Id ssbo_index{OpShiftRightLogical(U32[1], ssbo_offset, Const(shift))}; | ||
| 867 | const Id ssbo_pointer{OpAccessChain(element_pointer, ssbo_id, zero, ssbo_index)}; | ||
| 868 | callback(ssbo_pointer); | ||
| 869 | AddLabel(else_label); | ||
| 870 | } | ||
| 871 | }}; | ||
| 872 | const auto define_load{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) { | ||
| 873 | const Id function_type{TypeFunction(type, U64)}; | ||
| 874 | const Id func_id{OpFunction(type, spv::FunctionControlMask::MaskNone, function_type)}; | ||
| 875 | const Id addr{OpFunctionParameter(U64)}; | ||
| 876 | define_body(ssbo_member, addr, element_pointer, shift, | ||
| 877 | [&](Id ssbo_pointer) { OpReturnValue(OpLoad(type, ssbo_pointer)); }); | ||
| 878 | OpReturnValue(ConstantNull(type)); | ||
| 879 | OpFunctionEnd(); | ||
| 880 | return func_id; | ||
| 881 | }}; | ||
| 882 | const auto define_write{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) { | ||
| 883 | const Id function_type{TypeFunction(void_id, U64, type)}; | ||
| 884 | const Id func_id{OpFunction(void_id, spv::FunctionControlMask::MaskNone, function_type)}; | ||
| 885 | const Id addr{OpFunctionParameter(U64)}; | ||
| 886 | const Id data{OpFunctionParameter(type)}; | ||
| 887 | define_body(ssbo_member, addr, element_pointer, shift, [&](Id ssbo_pointer) { | ||
| 888 | OpStore(ssbo_pointer, data); | ||
| 889 | OpReturn(); | ||
| 890 | }); | ||
| 891 | OpReturn(); | ||
| 892 | OpFunctionEnd(); | ||
| 893 | return func_id; | ||
| 894 | }}; | ||
| 895 | const auto define{ | ||
| 896 | [&](DefPtr ssbo_member, const StorageTypeDefinition& type_def, Id type, size_t size) { | ||
| 897 | const Id element_type{type_def.element}; | ||
| 898 | const u32 shift{static_cast<u32>(std::countr_zero(size))}; | ||
| 899 | const Id load_func{define_load(ssbo_member, element_type, type, shift)}; | ||
| 900 | const Id write_func{define_write(ssbo_member, element_type, type, shift)}; | ||
| 901 | return std::make_pair(load_func, write_func); | ||
| 902 | }}; | ||
| 903 | std::tie(load_global_func_u32, write_global_func_u32) = | ||
| 904 | define(&StorageDefinitions::U32, storage_types.U32, U32[1], sizeof(u32)); | ||
| 905 | std::tie(load_global_func_u32x2, write_global_func_u32x2) = | ||
| 906 | define(&StorageDefinitions::U32x2, storage_types.U32x2, U32[2], sizeof(u32[2])); | ||
| 907 | std::tie(load_global_func_u32x4, write_global_func_u32x4) = | ||
| 908 | define(&StorageDefinitions::U32x4, storage_types.U32x4, U32[4], sizeof(u32[4])); | ||
| 909 | } | ||
| 910 | |||
| 911 | void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { | ||
| 912 | if (info.constant_buffer_descriptors.empty()) { | ||
| 913 | return; | ||
| 914 | } | ||
| 915 | if (!profile.support_descriptor_aliasing) { | ||
| 916 | DefineConstBuffers(*this, info, &UniformDefinitions::U32x4, binding, U32[4], 'u', | ||
| 917 | sizeof(u32[4])); | ||
| 918 | for (const ConstantBufferDescriptor& desc : info.constant_buffer_descriptors) { | ||
| 919 | binding += desc.count; | ||
| 920 | } | ||
| 921 | return; | ||
| 922 | } | ||
| 923 | IR::Type types{info.used_constant_buffer_types}; | ||
| 924 | if (True(types & IR::Type::U8)) { | ||
| 925 | if (profile.support_int8) { | ||
| 926 | DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8)); | ||
| 927 | DefineConstBuffers(*this, info, &UniformDefinitions::S8, binding, S8, 's', sizeof(s8)); | ||
| 928 | } else { | ||
| 929 | types |= IR::Type::U32; | ||
| 930 | } | ||
| 931 | } | ||
| 932 | if (True(types & IR::Type::U16)) { | ||
| 933 | if (profile.support_int16) { | ||
| 934 | DefineConstBuffers(*this, info, &UniformDefinitions::U16, binding, U16, 'u', | ||
| 935 | sizeof(u16)); | ||
| 936 | DefineConstBuffers(*this, info, &UniformDefinitions::S16, binding, S16, 's', | ||
| 937 | sizeof(s16)); | ||
| 938 | } else { | ||
| 939 | types |= IR::Type::U32; | ||
| 940 | } | ||
| 941 | } | ||
| 942 | if (True(types & IR::Type::U32)) { | ||
| 943 | DefineConstBuffers(*this, info, &UniformDefinitions::U32, binding, U32[1], 'u', | ||
| 944 | sizeof(u32)); | ||
| 945 | } | ||
| 946 | if (True(types & IR::Type::F32)) { | ||
| 947 | DefineConstBuffers(*this, info, &UniformDefinitions::F32, binding, F32[1], 'f', | ||
| 948 | sizeof(f32)); | ||
| 949 | } | ||
| 950 | if (True(types & IR::Type::U32x2)) { | ||
| 951 | DefineConstBuffers(*this, info, &UniformDefinitions::U32x2, binding, U32[2], 'u', | ||
| 952 | sizeof(u32[2])); | ||
| 953 | } | ||
| 954 | binding += static_cast<u32>(info.constant_buffer_descriptors.size()); | ||
| 955 | } | ||
| 956 | |||
| 957 | void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) { | ||
| 958 | if (info.storage_buffers_descriptors.empty()) { | ||
| 959 | return; | ||
| 960 | } | ||
| 961 | AddExtension("SPV_KHR_storage_buffer_storage_class"); | ||
| 962 | |||
| 963 | const IR::Type used_types{profile.support_descriptor_aliasing ? info.used_storage_buffer_types | ||
| 964 | : IR::Type::U32}; | ||
| 965 | if (profile.support_int8 && True(used_types & IR::Type::U8)) { | ||
| 966 | DefineSsbos(*this, storage_types.U8, &StorageDefinitions::U8, info, binding, U8, | ||
| 967 | sizeof(u8)); | ||
| 968 | DefineSsbos(*this, storage_types.S8, &StorageDefinitions::S8, info, binding, S8, | ||
| 969 | sizeof(u8)); | ||
| 970 | } | ||
| 971 | if (profile.support_int16 && True(used_types & IR::Type::U16)) { | ||
| 972 | DefineSsbos(*this, storage_types.U16, &StorageDefinitions::U16, info, binding, U16, | ||
| 973 | sizeof(u16)); | ||
| 974 | DefineSsbos(*this, storage_types.S16, &StorageDefinitions::S16, info, binding, S16, | ||
| 975 | sizeof(u16)); | ||
| 976 | } | ||
| 977 | if (True(used_types & IR::Type::U32)) { | ||
| 978 | DefineSsbos(*this, storage_types.U32, &StorageDefinitions::U32, info, binding, U32[1], | ||
| 979 | sizeof(u32)); | ||
| 980 | } | ||
| 981 | if (True(used_types & IR::Type::F32)) { | ||
| 982 | DefineSsbos(*this, storage_types.F32, &StorageDefinitions::F32, info, binding, F32[1], | ||
| 983 | sizeof(f32)); | ||
| 984 | } | ||
| 985 | if (True(used_types & IR::Type::U64)) { | ||
| 986 | DefineSsbos(*this, storage_types.U64, &StorageDefinitions::U64, info, binding, U64, | ||
| 987 | sizeof(u64)); | ||
| 988 | } | ||
| 989 | if (True(used_types & IR::Type::U32x2)) { | ||
| 990 | DefineSsbos(*this, storage_types.U32x2, &StorageDefinitions::U32x2, info, binding, U32[2], | ||
| 991 | sizeof(u32[2])); | ||
| 992 | } | ||
| 993 | if (True(used_types & IR::Type::U32x4)) { | ||
| 994 | DefineSsbos(*this, storage_types.U32x4, &StorageDefinitions::U32x4, info, binding, U32[4], | ||
| 995 | sizeof(u32[4])); | ||
| 996 | } | ||
| 997 | for (const StorageBufferDescriptor& desc : info.storage_buffers_descriptors) { | ||
| 998 | binding += desc.count; | ||
| 999 | } | ||
| 1000 | const bool needs_function{ | ||
| 1001 | info.uses_global_increment || info.uses_global_decrement || info.uses_atomic_f32_add || | ||
| 1002 | info.uses_atomic_f16x2_add || info.uses_atomic_f16x2_min || info.uses_atomic_f16x2_max || | ||
| 1003 | info.uses_atomic_f32x2_add || info.uses_atomic_f32x2_min || info.uses_atomic_f32x2_max}; | ||
| 1004 | if (needs_function) { | ||
| 1005 | AddCapability(spv::Capability::VariablePointersStorageBuffer); | ||
| 1006 | } | ||
| 1007 | if (info.uses_global_increment) { | ||
| 1008 | increment_cas_ssbo = CasLoop(*this, Operation::Increment, storage_types.U32.array, | ||
| 1009 | storage_types.U32.element, U32[1], U32[1], spv::Scope::Device); | ||
| 1010 | } | ||
| 1011 | if (info.uses_global_decrement) { | ||
| 1012 | decrement_cas_ssbo = CasLoop(*this, Operation::Decrement, storage_types.U32.array, | ||
| 1013 | storage_types.U32.element, U32[1], U32[1], spv::Scope::Device); | ||
| 1014 | } | ||
| 1015 | if (info.uses_atomic_f32_add) { | ||
| 1016 | f32_add_cas = CasLoop(*this, Operation::FPAdd, storage_types.U32.array, | ||
| 1017 | storage_types.U32.element, F32[1], U32[1], spv::Scope::Device); | ||
| 1018 | } | ||
| 1019 | if (info.uses_atomic_f16x2_add) { | ||
| 1020 | f16x2_add_cas = CasLoop(*this, Operation::FPAdd, storage_types.U32.array, | ||
| 1021 | storage_types.U32.element, F16[2], F16[2], spv::Scope::Device); | ||
| 1022 | } | ||
| 1023 | if (info.uses_atomic_f16x2_min) { | ||
| 1024 | f16x2_min_cas = CasLoop(*this, Operation::FPMin, storage_types.U32.array, | ||
| 1025 | storage_types.U32.element, F16[2], F16[2], spv::Scope::Device); | ||
| 1026 | } | ||
| 1027 | if (info.uses_atomic_f16x2_max) { | ||
| 1028 | f16x2_max_cas = CasLoop(*this, Operation::FPMax, storage_types.U32.array, | ||
| 1029 | storage_types.U32.element, F16[2], F16[2], spv::Scope::Device); | ||
| 1030 | } | ||
| 1031 | if (info.uses_atomic_f32x2_add) { | ||
| 1032 | f32x2_add_cas = CasLoop(*this, Operation::FPAdd, storage_types.U32.array, | ||
| 1033 | storage_types.U32.element, F32[2], F32[2], spv::Scope::Device); | ||
| 1034 | } | ||
| 1035 | if (info.uses_atomic_f32x2_min) { | ||
| 1036 | f32x2_min_cas = CasLoop(*this, Operation::FPMin, storage_types.U32.array, | ||
| 1037 | storage_types.U32.element, F32[2], F32[2], spv::Scope::Device); | ||
| 1038 | } | ||
| 1039 | if (info.uses_atomic_f32x2_max) { | ||
| 1040 | f32x2_max_cas = CasLoop(*this, Operation::FPMax, storage_types.U32.array, | ||
| 1041 | storage_types.U32.element, F32[2], F32[2], spv::Scope::Device); | ||
| 1042 | } | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | void EmitContext::DefineTextureBuffers(const Info& info, u32& binding) { | ||
| 1046 | if (info.texture_buffer_descriptors.empty()) { | ||
| 1047 | return; | ||
| 1048 | } | ||
| 1049 | const spv::ImageFormat format{spv::ImageFormat::Unknown}; | ||
| 1050 | image_buffer_type = TypeImage(F32[1], spv::Dim::Buffer, 0U, false, false, 1, format); | ||
| 1051 | sampled_texture_buffer_type = TypeSampledImage(image_buffer_type); | ||
| 1052 | |||
| 1053 | const Id type{TypePointer(spv::StorageClass::UniformConstant, sampled_texture_buffer_type)}; | ||
| 1054 | texture_buffers.reserve(info.texture_buffer_descriptors.size()); | ||
| 1055 | for (const TextureBufferDescriptor& desc : info.texture_buffer_descriptors) { | ||
| 1056 | if (desc.count != 1) { | ||
| 1057 | throw NotImplementedException("Array of texture buffers"); | ||
| 1058 | } | ||
| 1059 | const Id id{AddGlobalVariable(type, spv::StorageClass::UniformConstant)}; | ||
| 1060 | Decorate(id, spv::Decoration::Binding, binding); | ||
| 1061 | Decorate(id, spv::Decoration::DescriptorSet, 0U); | ||
| 1062 | Name(id, NameOf(stage, desc, "texbuf")); | ||
| 1063 | texture_buffers.push_back({ | ||
| 1064 | .id = id, | ||
| 1065 | .count = desc.count, | ||
| 1066 | }); | ||
| 1067 | if (profile.supported_spirv >= 0x00010400) { | ||
| 1068 | interfaces.push_back(id); | ||
| 1069 | } | ||
| 1070 | ++binding; | ||
| 1071 | } | ||
| 1072 | } | ||
| 1073 | |||
| 1074 | void EmitContext::DefineImageBuffers(const Info& info, u32& binding) { | ||
| 1075 | image_buffers.reserve(info.image_buffer_descriptors.size()); | ||
| 1076 | for (const ImageBufferDescriptor& desc : info.image_buffer_descriptors) { | ||
| 1077 | if (desc.count != 1) { | ||
| 1078 | throw NotImplementedException("Array of image buffers"); | ||
| 1079 | } | ||
| 1080 | const spv::ImageFormat format{GetImageFormat(desc.format)}; | ||
| 1081 | const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)}; | ||
| 1082 | const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; | ||
| 1083 | const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; | ||
| 1084 | Decorate(id, spv::Decoration::Binding, binding); | ||
| 1085 | Decorate(id, spv::Decoration::DescriptorSet, 0U); | ||
| 1086 | Name(id, NameOf(stage, desc, "imgbuf")); | ||
| 1087 | image_buffers.push_back({ | ||
| 1088 | .id = id, | ||
| 1089 | .image_type = image_type, | ||
| 1090 | .count = desc.count, | ||
| 1091 | }); | ||
| 1092 | if (profile.supported_spirv >= 0x00010400) { | ||
| 1093 | interfaces.push_back(id); | ||
| 1094 | } | ||
| 1095 | ++binding; | ||
| 1096 | } | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | void EmitContext::DefineTextures(const Info& info, u32& binding) { | ||
| 1100 | textures.reserve(info.texture_descriptors.size()); | ||
| 1101 | for (const TextureDescriptor& desc : info.texture_descriptors) { | ||
| 1102 | const Id image_type{ImageType(*this, desc)}; | ||
| 1103 | const Id sampled_type{TypeSampledImage(image_type)}; | ||
| 1104 | const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, sampled_type)}; | ||
| 1105 | const Id desc_type{DescType(*this, sampled_type, pointer_type, desc.count)}; | ||
| 1106 | const Id id{AddGlobalVariable(desc_type, spv::StorageClass::UniformConstant)}; | ||
| 1107 | Decorate(id, spv::Decoration::Binding, binding); | ||
| 1108 | Decorate(id, spv::Decoration::DescriptorSet, 0U); | ||
| 1109 | Name(id, NameOf(stage, desc, "tex")); | ||
| 1110 | textures.push_back({ | ||
| 1111 | .id = id, | ||
| 1112 | .sampled_type = sampled_type, | ||
| 1113 | .pointer_type = pointer_type, | ||
| 1114 | .image_type = image_type, | ||
| 1115 | .count = desc.count, | ||
| 1116 | }); | ||
| 1117 | if (profile.supported_spirv >= 0x00010400) { | ||
| 1118 | interfaces.push_back(id); | ||
| 1119 | } | ||
| 1120 | ++binding; | ||
| 1121 | } | ||
| 1122 | if (info.uses_atomic_image_u32) { | ||
| 1123 | image_u32 = TypePointer(spv::StorageClass::Image, U32[1]); | ||
| 1124 | } | ||
| 1125 | } | ||
| 1126 | |||
| 1127 | void EmitContext::DefineImages(const Info& info, u32& binding) { | ||
| 1128 | images.reserve(info.image_descriptors.size()); | ||
| 1129 | for (const ImageDescriptor& desc : info.image_descriptors) { | ||
| 1130 | if (desc.count != 1) { | ||
| 1131 | throw NotImplementedException("Array of images"); | ||
| 1132 | } | ||
| 1133 | const Id image_type{ImageType(*this, desc)}; | ||
| 1134 | const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; | ||
| 1135 | const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; | ||
| 1136 | Decorate(id, spv::Decoration::Binding, binding); | ||
| 1137 | Decorate(id, spv::Decoration::DescriptorSet, 0U); | ||
| 1138 | Name(id, NameOf(stage, desc, "img")); | ||
| 1139 | images.push_back({ | ||
| 1140 | .id = id, | ||
| 1141 | .image_type = image_type, | ||
| 1142 | .count = desc.count, | ||
| 1143 | }); | ||
| 1144 | if (profile.supported_spirv >= 0x00010400) { | ||
| 1145 | interfaces.push_back(id); | ||
| 1146 | } | ||
| 1147 | ++binding; | ||
| 1148 | } | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | void EmitContext::DefineInputs(const IR::Program& program) { | ||
| 1152 | const Info& info{program.info}; | ||
| 1153 | const VaryingState loads{info.loads.mask | info.passthrough.mask}; | ||
| 1154 | |||
| 1155 | if (info.uses_workgroup_id) { | ||
| 1156 | workgroup_id = DefineInput(*this, U32[3], false, spv::BuiltIn::WorkgroupId); | ||
| 1157 | } | ||
| 1158 | if (info.uses_local_invocation_id) { | ||
| 1159 | local_invocation_id = DefineInput(*this, U32[3], false, spv::BuiltIn::LocalInvocationId); | ||
| 1160 | } | ||
| 1161 | if (info.uses_invocation_id) { | ||
| 1162 | invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::InvocationId); | ||
| 1163 | } | ||
| 1164 | if (info.uses_sample_id) { | ||
| 1165 | sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId); | ||
| 1166 | } | ||
| 1167 | if (info.uses_is_helper_invocation) { | ||
| 1168 | is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation); | ||
| 1169 | } | ||
| 1170 | if (info.uses_subgroup_mask) { | ||
| 1171 | subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR); | ||
| 1172 | subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR); | ||
| 1173 | subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR); | ||
| 1174 | subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR); | ||
| 1175 | subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR); | ||
| 1176 | } | ||
| 1177 | if (info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || | ||
| 1178 | (profile.warp_size_potentially_larger_than_guest && | ||
| 1179 | (info.uses_subgroup_vote || info.uses_subgroup_mask))) { | ||
| 1180 | subgroup_local_invocation_id = | ||
| 1181 | DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId); | ||
| 1182 | } | ||
| 1183 | if (info.uses_fswzadd) { | ||
| 1184 | const Id f32_one{Const(1.0f)}; | ||
| 1185 | const Id f32_minus_one{Const(-1.0f)}; | ||
| 1186 | const Id f32_zero{Const(0.0f)}; | ||
| 1187 | fswzadd_lut_a = ConstantComposite(F32[4], f32_minus_one, f32_one, f32_minus_one, f32_zero); | ||
| 1188 | fswzadd_lut_b = | ||
| 1189 | ConstantComposite(F32[4], f32_minus_one, f32_minus_one, f32_one, f32_minus_one); | ||
| 1190 | } | ||
| 1191 | if (loads[IR::Attribute::PrimitiveId]) { | ||
| 1192 | primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId); | ||
| 1193 | } | ||
| 1194 | if (loads.AnyComponent(IR::Attribute::PositionX)) { | ||
| 1195 | const bool is_fragment{stage != Stage::Fragment}; | ||
| 1196 | const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord}; | ||
| 1197 | input_position = DefineInput(*this, F32[4], true, built_in); | ||
| 1198 | if (profile.support_geometry_shader_passthrough) { | ||
| 1199 | if (info.passthrough.AnyComponent(IR::Attribute::PositionX)) { | ||
| 1200 | Decorate(input_position, spv::Decoration::PassthroughNV); | ||
| 1201 | } | ||
| 1202 | } | ||
| 1203 | } | ||
| 1204 | if (loads[IR::Attribute::InstanceId]) { | ||
| 1205 | if (profile.support_vertex_instance_id) { | ||
| 1206 | instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId); | ||
| 1207 | } else { | ||
| 1208 | instance_index = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceIndex); | ||
| 1209 | base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance); | ||
| 1210 | } | ||
| 1211 | } | ||
| 1212 | if (loads[IR::Attribute::VertexId]) { | ||
| 1213 | if (profile.support_vertex_instance_id) { | ||
| 1214 | vertex_id = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexId); | ||
| 1215 | } else { | ||
| 1216 | vertex_index = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexIndex); | ||
| 1217 | base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex); | ||
| 1218 | } | ||
| 1219 | } | ||
| 1220 | if (loads[IR::Attribute::FrontFace]) { | ||
| 1221 | front_face = DefineInput(*this, U1, true, spv::BuiltIn::FrontFacing); | ||
| 1222 | } | ||
| 1223 | if (loads[IR::Attribute::PointSpriteS] || loads[IR::Attribute::PointSpriteT]) { | ||
| 1224 | point_coord = DefineInput(*this, F32[2], true, spv::BuiltIn::PointCoord); | ||
| 1225 | } | ||
| 1226 | if (loads[IR::Attribute::TessellationEvaluationPointU] || | ||
| 1227 | loads[IR::Attribute::TessellationEvaluationPointV]) { | ||
| 1228 | tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord); | ||
| 1229 | } | ||
| 1230 | for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { | ||
| 1231 | const AttributeType input_type{runtime_info.generic_input_types[index]}; | ||
| 1232 | if (!runtime_info.previous_stage_stores.Generic(index)) { | ||
| 1233 | continue; | ||
| 1234 | } | ||
| 1235 | if (!loads.Generic(index)) { | ||
| 1236 | continue; | ||
| 1237 | } | ||
| 1238 | if (input_type == AttributeType::Disabled) { | ||
| 1239 | continue; | ||
| 1240 | } | ||
| 1241 | const Id type{GetAttributeType(*this, input_type)}; | ||
| 1242 | const Id id{DefineInput(*this, type, true)}; | ||
| 1243 | Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); | ||
| 1244 | Name(id, fmt::format("in_attr{}", index)); | ||
| 1245 | input_generics[index] = id; | ||
| 1246 | |||
| 1247 | if (info.passthrough.Generic(index) && profile.support_geometry_shader_passthrough) { | ||
| 1248 | Decorate(id, spv::Decoration::PassthroughNV); | ||
| 1249 | } | ||
| 1250 | if (stage != Stage::Fragment) { | ||
| 1251 | continue; | ||
| 1252 | } | ||
| 1253 | switch (info.interpolation[index]) { | ||
| 1254 | case Interpolation::Smooth: | ||
| 1255 | // Default | ||
| 1256 | // Decorate(id, spv::Decoration::Smooth); | ||
| 1257 | break; | ||
| 1258 | case Interpolation::NoPerspective: | ||
| 1259 | Decorate(id, spv::Decoration::NoPerspective); | ||
| 1260 | break; | ||
| 1261 | case Interpolation::Flat: | ||
| 1262 | Decorate(id, spv::Decoration::Flat); | ||
| 1263 | break; | ||
| 1264 | } | ||
| 1265 | } | ||
| 1266 | if (stage == Stage::TessellationEval) { | ||
| 1267 | for (size_t index = 0; index < info.uses_patches.size(); ++index) { | ||
| 1268 | if (!info.uses_patches[index]) { | ||
| 1269 | continue; | ||
| 1270 | } | ||
| 1271 | const Id id{DefineInput(*this, F32[4], false)}; | ||
| 1272 | Decorate(id, spv::Decoration::Patch); | ||
| 1273 | Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); | ||
| 1274 | patches[index] = id; | ||
| 1275 | } | ||
| 1276 | } | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | void EmitContext::DefineOutputs(const IR::Program& program) { | ||
| 1280 | const Info& info{program.info}; | ||
| 1281 | const std::optional<u32> invocations{program.invocations}; | ||
| 1282 | if (info.stores.AnyComponent(IR::Attribute::PositionX) || stage == Stage::VertexB) { | ||
| 1283 | output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position); | ||
| 1284 | } | ||
| 1285 | if (info.stores[IR::Attribute::PointSize] || runtime_info.fixed_state_point_size) { | ||
| 1286 | if (stage == Stage::Fragment) { | ||
| 1287 | throw NotImplementedException("Storing PointSize in fragment stage"); | ||
| 1288 | } | ||
| 1289 | output_point_size = DefineOutput(*this, F32[1], invocations, spv::BuiltIn::PointSize); | ||
| 1290 | } | ||
| 1291 | if (info.stores.ClipDistances()) { | ||
| 1292 | if (stage == Stage::Fragment) { | ||
| 1293 | throw NotImplementedException("Storing ClipDistance in fragment stage"); | ||
| 1294 | } | ||
| 1295 | const Id type{TypeArray(F32[1], Const(8U))}; | ||
| 1296 | clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance); | ||
| 1297 | } | ||
| 1298 | if (info.stores[IR::Attribute::Layer] && | ||
| 1299 | (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) { | ||
| 1300 | if (stage == Stage::Fragment) { | ||
| 1301 | throw NotImplementedException("Storing Layer in fragment stage"); | ||
| 1302 | } | ||
| 1303 | layer = DefineOutput(*this, U32[1], invocations, spv::BuiltIn::Layer); | ||
| 1304 | } | ||
| 1305 | if (info.stores[IR::Attribute::ViewportIndex] && | ||
| 1306 | (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) { | ||
| 1307 | if (stage == Stage::Fragment) { | ||
| 1308 | throw NotImplementedException("Storing ViewportIndex in fragment stage"); | ||
| 1309 | } | ||
| 1310 | viewport_index = DefineOutput(*this, U32[1], invocations, spv::BuiltIn::ViewportIndex); | ||
| 1311 | } | ||
| 1312 | if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) { | ||
| 1313 | viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt, | ||
| 1314 | spv::BuiltIn::ViewportMaskNV); | ||
| 1315 | } | ||
| 1316 | for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { | ||
| 1317 | if (info.stores.Generic(index)) { | ||
| 1318 | DefineGenericOutput(*this, index, invocations); | ||
| 1319 | } | ||
| 1320 | } | ||
| 1321 | switch (stage) { | ||
| 1322 | case Stage::TessellationControl: | ||
| 1323 | if (info.stores_tess_level_outer) { | ||
| 1324 | const Id type{TypeArray(F32[1], Const(4U))}; | ||
| 1325 | output_tess_level_outer = | ||
| 1326 | DefineOutput(*this, type, std::nullopt, spv::BuiltIn::TessLevelOuter); | ||
| 1327 | Decorate(output_tess_level_outer, spv::Decoration::Patch); | ||
| 1328 | } | ||
| 1329 | if (info.stores_tess_level_inner) { | ||
| 1330 | const Id type{TypeArray(F32[1], Const(2U))}; | ||
| 1331 | output_tess_level_inner = | ||
| 1332 | DefineOutput(*this, type, std::nullopt, spv::BuiltIn::TessLevelInner); | ||
| 1333 | Decorate(output_tess_level_inner, spv::Decoration::Patch); | ||
| 1334 | } | ||
| 1335 | for (size_t index = 0; index < info.uses_patches.size(); ++index) { | ||
| 1336 | if (!info.uses_patches[index]) { | ||
| 1337 | continue; | ||
| 1338 | } | ||
| 1339 | const Id id{DefineOutput(*this, F32[4], std::nullopt)}; | ||
| 1340 | Decorate(id, spv::Decoration::Patch); | ||
| 1341 | Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); | ||
| 1342 | patches[index] = id; | ||
| 1343 | } | ||
| 1344 | break; | ||
| 1345 | case Stage::Fragment: | ||
| 1346 | for (u32 index = 0; index < 8; ++index) { | ||
| 1347 | if (!info.stores_frag_color[index] && !profile.need_declared_frag_colors) { | ||
| 1348 | continue; | ||
| 1349 | } | ||
| 1350 | frag_color[index] = DefineOutput(*this, F32[4], std::nullopt); | ||
| 1351 | Decorate(frag_color[index], spv::Decoration::Location, index); | ||
| 1352 | Name(frag_color[index], fmt::format("frag_color{}", index)); | ||
| 1353 | } | ||
| 1354 | if (info.stores_frag_depth) { | ||
| 1355 | frag_depth = DefineOutput(*this, F32[1], std::nullopt); | ||
| 1356 | Decorate(frag_depth, spv::Decoration::BuiltIn, spv::BuiltIn::FragDepth); | ||
| 1357 | } | ||
| 1358 | if (info.stores_sample_mask) { | ||
| 1359 | sample_mask = DefineOutput(*this, U32[1], std::nullopt); | ||
| 1360 | Decorate(sample_mask, spv::Decoration::BuiltIn, spv::BuiltIn::SampleMask); | ||
| 1361 | } | ||
| 1362 | break; | ||
| 1363 | default: | ||
| 1364 | break; | ||
| 1365 | } | ||
| 1366 | } | ||
| 1367 | |||
| 1368 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h new file mode 100644 index 000000000..e277bc358 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_context.h | |||
| @@ -0,0 +1,307 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <string_view> | ||
| 9 | |||
| 10 | #include <sirit/sirit.h> | ||
| 11 | |||
| 12 | #include "shader_recompiler/backend/bindings.h" | ||
| 13 | #include "shader_recompiler/frontend/ir/program.h" | ||
| 14 | #include "shader_recompiler/profile.h" | ||
| 15 | #include "shader_recompiler/runtime_info.h" | ||
| 16 | #include "shader_recompiler/shader_info.h" | ||
| 17 | |||
| 18 | namespace Shader::Backend::SPIRV { | ||
| 19 | |||
| 20 | using Sirit::Id; | ||
| 21 | |||
| 22 | class VectorTypes { | ||
| 23 | public: | ||
| 24 | void Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name); | ||
| 25 | |||
| 26 | [[nodiscard]] Id operator[](size_t size) const noexcept { | ||
| 27 | return defs[size - 1]; | ||
| 28 | } | ||
| 29 | |||
| 30 | private: | ||
| 31 | std::array<Id, 4> defs{}; | ||
| 32 | }; | ||
| 33 | |||
| 34 | struct TextureDefinition { | ||
| 35 | Id id; | ||
| 36 | Id sampled_type; | ||
| 37 | Id pointer_type; | ||
| 38 | Id image_type; | ||
| 39 | u32 count; | ||
| 40 | }; | ||
| 41 | |||
| 42 | struct TextureBufferDefinition { | ||
| 43 | Id id; | ||
| 44 | u32 count; | ||
| 45 | }; | ||
| 46 | |||
| 47 | struct ImageBufferDefinition { | ||
| 48 | Id id; | ||
| 49 | Id image_type; | ||
| 50 | u32 count; | ||
| 51 | }; | ||
| 52 | |||
| 53 | struct ImageDefinition { | ||
| 54 | Id id; | ||
| 55 | Id image_type; | ||
| 56 | u32 count; | ||
| 57 | }; | ||
| 58 | |||
| 59 | struct UniformDefinitions { | ||
| 60 | Id U8{}; | ||
| 61 | Id S8{}; | ||
| 62 | Id U16{}; | ||
| 63 | Id S16{}; | ||
| 64 | Id U32{}; | ||
| 65 | Id F32{}; | ||
| 66 | Id U32x2{}; | ||
| 67 | Id U32x4{}; | ||
| 68 | }; | ||
| 69 | |||
| 70 | struct StorageTypeDefinition { | ||
| 71 | Id array{}; | ||
| 72 | Id element{}; | ||
| 73 | }; | ||
| 74 | |||
| 75 | struct StorageTypeDefinitions { | ||
| 76 | StorageTypeDefinition U8{}; | ||
| 77 | StorageTypeDefinition S8{}; | ||
| 78 | StorageTypeDefinition U16{}; | ||
| 79 | StorageTypeDefinition S16{}; | ||
| 80 | StorageTypeDefinition U32{}; | ||
| 81 | StorageTypeDefinition U64{}; | ||
| 82 | StorageTypeDefinition F32{}; | ||
| 83 | StorageTypeDefinition U32x2{}; | ||
| 84 | StorageTypeDefinition U32x4{}; | ||
| 85 | }; | ||
| 86 | |||
| 87 | struct StorageDefinitions { | ||
| 88 | Id U8{}; | ||
| 89 | Id S8{}; | ||
| 90 | Id U16{}; | ||
| 91 | Id S16{}; | ||
| 92 | Id U32{}; | ||
| 93 | Id F32{}; | ||
| 94 | Id U64{}; | ||
| 95 | Id U32x2{}; | ||
| 96 | Id U32x4{}; | ||
| 97 | }; | ||
| 98 | |||
| 99 | struct GenericElementInfo { | ||
| 100 | Id id{}; | ||
| 101 | u32 first_element{}; | ||
| 102 | u32 num_components{}; | ||
| 103 | }; | ||
| 104 | |||
| 105 | class EmitContext final : public Sirit::Module { | ||
| 106 | public: | ||
| 107 | explicit EmitContext(const Profile& profile, const RuntimeInfo& runtime_info, | ||
| 108 | IR::Program& program, Bindings& binding); | ||
| 109 | ~EmitContext(); | ||
| 110 | |||
| 111 | [[nodiscard]] Id Def(const IR::Value& value); | ||
| 112 | |||
| 113 | [[nodiscard]] Id BitOffset8(const IR::Value& offset); | ||
| 114 | [[nodiscard]] Id BitOffset16(const IR::Value& offset); | ||
| 115 | |||
| 116 | Id Const(u32 value) { | ||
| 117 | return Constant(U32[1], value); | ||
| 118 | } | ||
| 119 | |||
| 120 | Id Const(u32 element_1, u32 element_2) { | ||
| 121 | return ConstantComposite(U32[2], Const(element_1), Const(element_2)); | ||
| 122 | } | ||
| 123 | |||
| 124 | Id Const(u32 element_1, u32 element_2, u32 element_3) { | ||
| 125 | return ConstantComposite(U32[3], Const(element_1), Const(element_2), Const(element_3)); | ||
| 126 | } | ||
| 127 | |||
| 128 | Id Const(u32 element_1, u32 element_2, u32 element_3, u32 element_4) { | ||
| 129 | return ConstantComposite(U32[4], Const(element_1), Const(element_2), Const(element_3), | ||
| 130 | Const(element_4)); | ||
| 131 | } | ||
| 132 | |||
| 133 | Id SConst(s32 value) { | ||
| 134 | return Constant(S32[1], value); | ||
| 135 | } | ||
| 136 | |||
| 137 | Id SConst(s32 element_1, s32 element_2) { | ||
| 138 | return ConstantComposite(S32[2], SConst(element_1), SConst(element_2)); | ||
| 139 | } | ||
| 140 | |||
| 141 | Id SConst(s32 element_1, s32 element_2, s32 element_3) { | ||
| 142 | return ConstantComposite(S32[3], SConst(element_1), SConst(element_2), SConst(element_3)); | ||
| 143 | } | ||
| 144 | |||
| 145 | Id SConst(s32 element_1, s32 element_2, s32 element_3, s32 element_4) { | ||
| 146 | return ConstantComposite(S32[4], SConst(element_1), SConst(element_2), SConst(element_3), | ||
| 147 | SConst(element_4)); | ||
| 148 | } | ||
| 149 | |||
| 150 | Id Const(f32 value) { | ||
| 151 | return Constant(F32[1], value); | ||
| 152 | } | ||
| 153 | |||
| 154 | const Profile& profile; | ||
| 155 | const RuntimeInfo& runtime_info; | ||
| 156 | Stage stage{}; | ||
| 157 | |||
| 158 | Id void_id{}; | ||
| 159 | Id U1{}; | ||
| 160 | Id U8{}; | ||
| 161 | Id S8{}; | ||
| 162 | Id U16{}; | ||
| 163 | Id S16{}; | ||
| 164 | Id U64{}; | ||
| 165 | VectorTypes F32; | ||
| 166 | VectorTypes U32; | ||
| 167 | VectorTypes S32; | ||
| 168 | VectorTypes F16; | ||
| 169 | VectorTypes F64; | ||
| 170 | |||
| 171 | Id true_value{}; | ||
| 172 | Id false_value{}; | ||
| 173 | Id u32_zero_value{}; | ||
| 174 | Id f32_zero_value{}; | ||
| 175 | |||
| 176 | UniformDefinitions uniform_types; | ||
| 177 | StorageTypeDefinitions storage_types; | ||
| 178 | |||
| 179 | Id private_u32{}; | ||
| 180 | |||
| 181 | Id shared_u8{}; | ||
| 182 | Id shared_u16{}; | ||
| 183 | Id shared_u32{}; | ||
| 184 | Id shared_u64{}; | ||
| 185 | Id shared_u32x2{}; | ||
| 186 | Id shared_u32x4{}; | ||
| 187 | |||
| 188 | Id input_f32{}; | ||
| 189 | Id input_u32{}; | ||
| 190 | Id input_s32{}; | ||
| 191 | |||
| 192 | Id output_f32{}; | ||
| 193 | Id output_u32{}; | ||
| 194 | |||
| 195 | Id image_buffer_type{}; | ||
| 196 | Id sampled_texture_buffer_type{}; | ||
| 197 | Id image_u32{}; | ||
| 198 | |||
| 199 | std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{}; | ||
| 200 | std::array<StorageDefinitions, Info::MAX_SSBOS> ssbos{}; | ||
| 201 | std::vector<TextureBufferDefinition> texture_buffers; | ||
| 202 | std::vector<ImageBufferDefinition> image_buffers; | ||
| 203 | std::vector<TextureDefinition> textures; | ||
| 204 | std::vector<ImageDefinition> images; | ||
| 205 | |||
| 206 | Id workgroup_id{}; | ||
| 207 | Id local_invocation_id{}; | ||
| 208 | Id invocation_id{}; | ||
| 209 | Id sample_id{}; | ||
| 210 | Id is_helper_invocation{}; | ||
| 211 | Id subgroup_local_invocation_id{}; | ||
| 212 | Id subgroup_mask_eq{}; | ||
| 213 | Id subgroup_mask_lt{}; | ||
| 214 | Id subgroup_mask_le{}; | ||
| 215 | Id subgroup_mask_gt{}; | ||
| 216 | Id subgroup_mask_ge{}; | ||
| 217 | Id instance_id{}; | ||
| 218 | Id instance_index{}; | ||
| 219 | Id base_instance{}; | ||
| 220 | Id vertex_id{}; | ||
| 221 | Id vertex_index{}; | ||
| 222 | Id base_vertex{}; | ||
| 223 | Id front_face{}; | ||
| 224 | Id point_coord{}; | ||
| 225 | Id tess_coord{}; | ||
| 226 | Id clip_distances{}; | ||
| 227 | Id layer{}; | ||
| 228 | Id viewport_index{}; | ||
| 229 | Id viewport_mask{}; | ||
| 230 | Id primitive_id{}; | ||
| 231 | |||
| 232 | Id fswzadd_lut_a{}; | ||
| 233 | Id fswzadd_lut_b{}; | ||
| 234 | |||
| 235 | Id indexed_load_func{}; | ||
| 236 | Id indexed_store_func{}; | ||
| 237 | |||
| 238 | Id local_memory{}; | ||
| 239 | |||
| 240 | Id shared_memory_u8{}; | ||
| 241 | Id shared_memory_u16{}; | ||
| 242 | Id shared_memory_u32{}; | ||
| 243 | Id shared_memory_u64{}; | ||
| 244 | Id shared_memory_u32x2{}; | ||
| 245 | Id shared_memory_u32x4{}; | ||
| 246 | |||
| 247 | Id shared_memory_u32_type{}; | ||
| 248 | |||
| 249 | Id shared_store_u8_func{}; | ||
| 250 | Id shared_store_u16_func{}; | ||
| 251 | Id increment_cas_shared{}; | ||
| 252 | Id increment_cas_ssbo{}; | ||
| 253 | Id decrement_cas_shared{}; | ||
| 254 | Id decrement_cas_ssbo{}; | ||
| 255 | Id f32_add_cas{}; | ||
| 256 | Id f16x2_add_cas{}; | ||
| 257 | Id f16x2_min_cas{}; | ||
| 258 | Id f16x2_max_cas{}; | ||
| 259 | Id f32x2_add_cas{}; | ||
| 260 | Id f32x2_min_cas{}; | ||
| 261 | Id f32x2_max_cas{}; | ||
| 262 | |||
| 263 | Id load_global_func_u32{}; | ||
| 264 | Id load_global_func_u32x2{}; | ||
| 265 | Id load_global_func_u32x4{}; | ||
| 266 | Id write_global_func_u32{}; | ||
| 267 | Id write_global_func_u32x2{}; | ||
| 268 | Id write_global_func_u32x4{}; | ||
| 269 | |||
| 270 | Id input_position{}; | ||
| 271 | std::array<Id, 32> input_generics{}; | ||
| 272 | |||
| 273 | Id output_point_size{}; | ||
| 274 | Id output_position{}; | ||
| 275 | std::array<std::array<GenericElementInfo, 4>, 32> output_generics{}; | ||
| 276 | |||
| 277 | Id output_tess_level_outer{}; | ||
| 278 | Id output_tess_level_inner{}; | ||
| 279 | std::array<Id, 30> patches{}; | ||
| 280 | |||
| 281 | std::array<Id, 8> frag_color{}; | ||
| 282 | Id sample_mask{}; | ||
| 283 | Id frag_depth{}; | ||
| 284 | |||
| 285 | std::vector<Id> interfaces; | ||
| 286 | |||
| 287 | private: | ||
| 288 | void DefineCommonTypes(const Info& info); | ||
| 289 | void DefineCommonConstants(); | ||
| 290 | void DefineInterfaces(const IR::Program& program); | ||
| 291 | void DefineLocalMemory(const IR::Program& program); | ||
| 292 | void DefineSharedMemory(const IR::Program& program); | ||
| 293 | void DefineSharedMemoryFunctions(const IR::Program& program); | ||
| 294 | void DefineConstantBuffers(const Info& info, u32& binding); | ||
| 295 | void DefineStorageBuffers(const Info& info, u32& binding); | ||
| 296 | void DefineTextureBuffers(const Info& info, u32& binding); | ||
| 297 | void DefineImageBuffers(const Info& info, u32& binding); | ||
| 298 | void DefineTextures(const Info& info, u32& binding); | ||
| 299 | void DefineImages(const Info& info, u32& binding); | ||
| 300 | void DefineAttributeMemAccess(const Info& info); | ||
| 301 | void DefineGlobalMemoryFunctions(const Info& info); | ||
| 302 | |||
| 303 | void DefineInputs(const IR::Program& program); | ||
| 304 | void DefineOutputs(const IR::Program& program); | ||
| 305 | }; | ||
| 306 | |||
| 307 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp new file mode 100644 index 000000000..d7a86e270 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp | |||
| @@ -0,0 +1,541 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <span> | ||
| 6 | #include <tuple> | ||
| 7 | #include <type_traits> | ||
| 8 | #include <utility> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "common/settings.h" | ||
| 12 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 13 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 14 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 15 | #include "shader_recompiler/frontend/ir/program.h" | ||
| 16 | |||
| 17 | namespace Shader::Backend::SPIRV { | ||
| 18 | namespace { | ||
| 19 | template <class Func> | ||
| 20 | struct FuncTraits {}; | ||
| 21 | |||
| 22 | template <class ReturnType_, class... Args> | ||
| 23 | struct FuncTraits<ReturnType_ (*)(Args...)> { | ||
| 24 | using ReturnType = ReturnType_; | ||
| 25 | |||
| 26 | static constexpr size_t NUM_ARGS = sizeof...(Args); | ||
| 27 | |||
| 28 | template <size_t I> | ||
| 29 | using ArgType = std::tuple_element_t<I, std::tuple<Args...>>; | ||
| 30 | }; | ||
| 31 | |||
| 32 | template <auto func, typename... Args> | ||
| 33 | void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) { | ||
| 34 | inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...)); | ||
| 35 | } | ||
| 36 | |||
| 37 | template <typename ArgType> | ||
| 38 | ArgType Arg(EmitContext& ctx, const IR::Value& arg) { | ||
| 39 | if constexpr (std::is_same_v<ArgType, Id>) { | ||
| 40 | return ctx.Def(arg); | ||
| 41 | } else if constexpr (std::is_same_v<ArgType, const IR::Value&>) { | ||
| 42 | return arg; | ||
| 43 | } else if constexpr (std::is_same_v<ArgType, u32>) { | ||
| 44 | return arg.U32(); | ||
| 45 | } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) { | ||
| 46 | return arg.Attribute(); | ||
| 47 | } else if constexpr (std::is_same_v<ArgType, IR::Patch>) { | ||
| 48 | return arg.Patch(); | ||
| 49 | } else if constexpr (std::is_same_v<ArgType, IR::Reg>) { | ||
| 50 | return arg.Reg(); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | template <auto func, bool is_first_arg_inst, size_t... I> | ||
| 55 | void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) { | ||
| 56 | using Traits = FuncTraits<decltype(func)>; | ||
| 57 | if constexpr (std::is_same_v<typename Traits::ReturnType, Id>) { | ||
| 58 | if constexpr (is_first_arg_inst) { | ||
| 59 | SetDefinition<func>( | ||
| 60 | ctx, inst, inst, | ||
| 61 | Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...); | ||
| 62 | } else { | ||
| 63 | SetDefinition<func>( | ||
| 64 | ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...); | ||
| 65 | } | ||
| 66 | } else { | ||
| 67 | if constexpr (is_first_arg_inst) { | ||
| 68 | func(ctx, inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...); | ||
| 69 | } else { | ||
| 70 | func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | template <auto func> | ||
| 76 | void Invoke(EmitContext& ctx, IR::Inst* inst) { | ||
| 77 | using Traits = FuncTraits<decltype(func)>; | ||
| 78 | static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments"); | ||
| 79 | if constexpr (Traits::NUM_ARGS == 1) { | ||
| 80 | Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{}); | ||
| 81 | } else { | ||
| 82 | using FirstArgType = typename Traits::template ArgType<1>; | ||
| 83 | static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst*>; | ||
| 84 | using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>; | ||
| 85 | Invoke<func, is_first_arg_inst>(ctx, inst, Indices{}); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | void EmitInst(EmitContext& ctx, IR::Inst* inst) { | ||
| 90 | switch (inst->GetOpcode()) { | ||
| 91 | #define OPCODE(name, result_type, ...) \ | ||
| 92 | case IR::Opcode::name: \ | ||
| 93 | return Invoke<&Emit##name>(ctx, inst); | ||
| 94 | #include "shader_recompiler/frontend/ir/opcodes.inc" | ||
| 95 | #undef OPCODE | ||
| 96 | } | ||
| 97 | throw LogicError("Invalid opcode {}", inst->GetOpcode()); | ||
| 98 | } | ||
| 99 | |||
| 100 | Id TypeId(const EmitContext& ctx, IR::Type type) { | ||
| 101 | switch (type) { | ||
| 102 | case IR::Type::U1: | ||
| 103 | return ctx.U1; | ||
| 104 | case IR::Type::U32: | ||
| 105 | return ctx.U32[1]; | ||
| 106 | default: | ||
| 107 | throw NotImplementedException("Phi node type {}", type); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | void Traverse(EmitContext& ctx, IR::Program& program) { | ||
| 112 | IR::Block* current_block{}; | ||
| 113 | for (const IR::AbstractSyntaxNode& node : program.syntax_list) { | ||
| 114 | switch (node.type) { | ||
| 115 | case IR::AbstractSyntaxNode::Type::Block: { | ||
| 116 | const Id label{node.data.block->Definition<Id>()}; | ||
| 117 | if (current_block) { | ||
| 118 | ctx.OpBranch(label); | ||
| 119 | } | ||
| 120 | current_block = node.data.block; | ||
| 121 | ctx.AddLabel(label); | ||
| 122 | for (IR::Inst& inst : node.data.block->Instructions()) { | ||
| 123 | EmitInst(ctx, &inst); | ||
| 124 | } | ||
| 125 | break; | ||
| 126 | } | ||
| 127 | case IR::AbstractSyntaxNode::Type::If: { | ||
| 128 | const Id if_label{node.data.if_node.body->Definition<Id>()}; | ||
| 129 | const Id endif_label{node.data.if_node.merge->Definition<Id>()}; | ||
| 130 | ctx.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone); | ||
| 131 | ctx.OpBranchConditional(ctx.Def(node.data.if_node.cond), if_label, endif_label); | ||
| 132 | break; | ||
| 133 | } | ||
| 134 | case IR::AbstractSyntaxNode::Type::Loop: { | ||
| 135 | const Id body_label{node.data.loop.body->Definition<Id>()}; | ||
| 136 | const Id continue_label{node.data.loop.continue_block->Definition<Id>()}; | ||
| 137 | const Id endloop_label{node.data.loop.merge->Definition<Id>()}; | ||
| 138 | |||
| 139 | ctx.OpLoopMerge(endloop_label, continue_label, spv::LoopControlMask::MaskNone); | ||
| 140 | ctx.OpBranch(body_label); | ||
| 141 | break; | ||
| 142 | } | ||
| 143 | case IR::AbstractSyntaxNode::Type::Break: { | ||
| 144 | const Id break_label{node.data.break_node.merge->Definition<Id>()}; | ||
| 145 | const Id skip_label{node.data.break_node.skip->Definition<Id>()}; | ||
| 146 | ctx.OpBranchConditional(ctx.Def(node.data.break_node.cond), break_label, skip_label); | ||
| 147 | break; | ||
| 148 | } | ||
| 149 | case IR::AbstractSyntaxNode::Type::EndIf: | ||
| 150 | if (current_block) { | ||
| 151 | ctx.OpBranch(node.data.end_if.merge->Definition<Id>()); | ||
| 152 | } | ||
| 153 | break; | ||
| 154 | case IR::AbstractSyntaxNode::Type::Repeat: { | ||
| 155 | Id cond{ctx.Def(node.data.repeat.cond)}; | ||
| 156 | if (!Settings::values.disable_shader_loop_safety_checks) { | ||
| 157 | const Id pointer_type{ctx.TypePointer(spv::StorageClass::Private, ctx.U32[1])}; | ||
| 158 | const Id safety_counter{ctx.AddGlobalVariable( | ||
| 159 | pointer_type, spv::StorageClass::Private, ctx.Const(0x2000u))}; | ||
| 160 | if (ctx.profile.supported_spirv >= 0x00010400) { | ||
| 161 | ctx.interfaces.push_back(safety_counter); | ||
| 162 | } | ||
| 163 | const Id old_counter{ctx.OpLoad(ctx.U32[1], safety_counter)}; | ||
| 164 | const Id new_counter{ctx.OpISub(ctx.U32[1], old_counter, ctx.Const(1u))}; | ||
| 165 | ctx.OpStore(safety_counter, new_counter); | ||
| 166 | |||
| 167 | const Id safety_cond{ | ||
| 168 | ctx.OpSGreaterThanEqual(ctx.U1, new_counter, ctx.u32_zero_value)}; | ||
| 169 | cond = ctx.OpLogicalAnd(ctx.U1, cond, safety_cond); | ||
| 170 | } | ||
| 171 | const Id loop_header_label{node.data.repeat.loop_header->Definition<Id>()}; | ||
| 172 | const Id merge_label{node.data.repeat.merge->Definition<Id>()}; | ||
| 173 | ctx.OpBranchConditional(cond, loop_header_label, merge_label); | ||
| 174 | break; | ||
| 175 | } | ||
| 176 | case IR::AbstractSyntaxNode::Type::Return: | ||
| 177 | ctx.OpReturn(); | ||
| 178 | break; | ||
| 179 | case IR::AbstractSyntaxNode::Type::Unreachable: | ||
| 180 | ctx.OpUnreachable(); | ||
| 181 | break; | ||
| 182 | } | ||
| 183 | if (node.type != IR::AbstractSyntaxNode::Type::Block) { | ||
| 184 | current_block = nullptr; | ||
| 185 | } | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | Id DefineMain(EmitContext& ctx, IR::Program& program) { | ||
| 190 | const Id void_function{ctx.TypeFunction(ctx.void_id)}; | ||
| 191 | const Id main{ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function)}; | ||
| 192 | for (IR::Block* const block : program.blocks) { | ||
| 193 | block->SetDefinition(ctx.OpLabel()); | ||
| 194 | } | ||
| 195 | Traverse(ctx, program); | ||
| 196 | ctx.OpFunctionEnd(); | ||
| 197 | return main; | ||
| 198 | } | ||
| 199 | |||
| 200 | spv::ExecutionMode ExecutionMode(TessPrimitive primitive) { | ||
| 201 | switch (primitive) { | ||
| 202 | case TessPrimitive::Isolines: | ||
| 203 | return spv::ExecutionMode::Isolines; | ||
| 204 | case TessPrimitive::Triangles: | ||
| 205 | return spv::ExecutionMode::Triangles; | ||
| 206 | case TessPrimitive::Quads: | ||
| 207 | return spv::ExecutionMode::Quads; | ||
| 208 | } | ||
| 209 | throw InvalidArgument("Tessellation primitive {}", primitive); | ||
| 210 | } | ||
| 211 | |||
| 212 | spv::ExecutionMode ExecutionMode(TessSpacing spacing) { | ||
| 213 | switch (spacing) { | ||
| 214 | case TessSpacing::Equal: | ||
| 215 | return spv::ExecutionMode::SpacingEqual; | ||
| 216 | case TessSpacing::FractionalOdd: | ||
| 217 | return spv::ExecutionMode::SpacingFractionalOdd; | ||
| 218 | case TessSpacing::FractionalEven: | ||
| 219 | return spv::ExecutionMode::SpacingFractionalEven; | ||
| 220 | } | ||
| 221 | throw InvalidArgument("Tessellation spacing {}", spacing); | ||
| 222 | } | ||
| 223 | |||
| 224 | void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { | ||
| 225 | const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size()); | ||
| 226 | spv::ExecutionModel execution_model{}; | ||
| 227 | switch (program.stage) { | ||
| 228 | case Stage::Compute: { | ||
| 229 | const std::array<u32, 3> workgroup_size{program.workgroup_size}; | ||
| 230 | execution_model = spv::ExecutionModel::GLCompute; | ||
| 231 | ctx.AddExecutionMode(main, spv::ExecutionMode::LocalSize, workgroup_size[0], | ||
| 232 | workgroup_size[1], workgroup_size[2]); | ||
| 233 | break; | ||
| 234 | } | ||
| 235 | case Stage::VertexB: | ||
| 236 | execution_model = spv::ExecutionModel::Vertex; | ||
| 237 | break; | ||
| 238 | case Stage::TessellationControl: | ||
| 239 | execution_model = spv::ExecutionModel::TessellationControl; | ||
| 240 | ctx.AddCapability(spv::Capability::Tessellation); | ||
| 241 | ctx.AddExecutionMode(main, spv::ExecutionMode::OutputVertices, program.invocations); | ||
| 242 | break; | ||
| 243 | case Stage::TessellationEval: | ||
| 244 | execution_model = spv::ExecutionModel::TessellationEvaluation; | ||
| 245 | ctx.AddCapability(spv::Capability::Tessellation); | ||
| 246 | ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_primitive)); | ||
| 247 | ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_spacing)); | ||
| 248 | ctx.AddExecutionMode(main, ctx.runtime_info.tess_clockwise | ||
| 249 | ? spv::ExecutionMode::VertexOrderCw | ||
| 250 | : spv::ExecutionMode::VertexOrderCcw); | ||
| 251 | break; | ||
| 252 | case Stage::Geometry: | ||
| 253 | execution_model = spv::ExecutionModel::Geometry; | ||
| 254 | ctx.AddCapability(spv::Capability::Geometry); | ||
| 255 | ctx.AddCapability(spv::Capability::GeometryStreams); | ||
| 256 | switch (ctx.runtime_info.input_topology) { | ||
| 257 | case InputTopology::Points: | ||
| 258 | ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints); | ||
| 259 | break; | ||
| 260 | case InputTopology::Lines: | ||
| 261 | ctx.AddExecutionMode(main, spv::ExecutionMode::InputLines); | ||
| 262 | break; | ||
| 263 | case InputTopology::LinesAdjacency: | ||
| 264 | ctx.AddExecutionMode(main, spv::ExecutionMode::InputLinesAdjacency); | ||
| 265 | break; | ||
| 266 | case InputTopology::Triangles: | ||
| 267 | ctx.AddExecutionMode(main, spv::ExecutionMode::Triangles); | ||
| 268 | break; | ||
| 269 | case InputTopology::TrianglesAdjacency: | ||
| 270 | ctx.AddExecutionMode(main, spv::ExecutionMode::InputTrianglesAdjacency); | ||
| 271 | break; | ||
| 272 | } | ||
| 273 | switch (program.output_topology) { | ||
| 274 | case OutputTopology::PointList: | ||
| 275 | ctx.AddExecutionMode(main, spv::ExecutionMode::OutputPoints); | ||
| 276 | break; | ||
| 277 | case OutputTopology::LineStrip: | ||
| 278 | ctx.AddExecutionMode(main, spv::ExecutionMode::OutputLineStrip); | ||
| 279 | break; | ||
| 280 | case OutputTopology::TriangleStrip: | ||
| 281 | ctx.AddExecutionMode(main, spv::ExecutionMode::OutputTriangleStrip); | ||
| 282 | break; | ||
| 283 | } | ||
| 284 | if (program.info.stores[IR::Attribute::PointSize]) { | ||
| 285 | ctx.AddCapability(spv::Capability::GeometryPointSize); | ||
| 286 | } | ||
| 287 | ctx.AddExecutionMode(main, spv::ExecutionMode::OutputVertices, program.output_vertices); | ||
| 288 | ctx.AddExecutionMode(main, spv::ExecutionMode::Invocations, program.invocations); | ||
| 289 | if (program.is_geometry_passthrough) { | ||
| 290 | if (ctx.profile.support_geometry_shader_passthrough) { | ||
| 291 | ctx.AddExtension("SPV_NV_geometry_shader_passthrough"); | ||
| 292 | ctx.AddCapability(spv::Capability::GeometryShaderPassthroughNV); | ||
| 293 | } else { | ||
| 294 | LOG_WARNING(Shader_SPIRV, "Geometry shader passthrough used with no support"); | ||
| 295 | } | ||
| 296 | } | ||
| 297 | break; | ||
| 298 | case Stage::Fragment: | ||
| 299 | execution_model = spv::ExecutionModel::Fragment; | ||
| 300 | if (ctx.profile.lower_left_origin_mode) { | ||
| 301 | ctx.AddExecutionMode(main, spv::ExecutionMode::OriginLowerLeft); | ||
| 302 | } else { | ||
| 303 | ctx.AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft); | ||
| 304 | } | ||
| 305 | if (program.info.stores_frag_depth) { | ||
| 306 | ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing); | ||
| 307 | } | ||
| 308 | if (ctx.runtime_info.force_early_z) { | ||
| 309 | ctx.AddExecutionMode(main, spv::ExecutionMode::EarlyFragmentTests); | ||
| 310 | } | ||
| 311 | break; | ||
| 312 | default: | ||
| 313 | throw NotImplementedException("Stage {}", program.stage); | ||
| 314 | } | ||
| 315 | ctx.AddEntryPoint(execution_model, main, "main", interfaces); | ||
| 316 | } | ||
| 317 | |||
| 318 | void SetupDenormControl(const Profile& profile, const IR::Program& program, EmitContext& ctx, | ||
| 319 | Id main_func) { | ||
| 320 | const Info& info{program.info}; | ||
| 321 | if (info.uses_fp32_denorms_flush && info.uses_fp32_denorms_preserve) { | ||
| 322 | LOG_DEBUG(Shader_SPIRV, "Fp32 denorm flush and preserve on the same shader"); | ||
| 323 | } else if (info.uses_fp32_denorms_flush) { | ||
| 324 | if (profile.support_fp32_denorm_flush) { | ||
| 325 | ctx.AddCapability(spv::Capability::DenormFlushToZero); | ||
| 326 | ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 32U); | ||
| 327 | } else { | ||
| 328 | // Drivers will most likely flush denorms by default, no need to warn | ||
| 329 | } | ||
| 330 | } else if (info.uses_fp32_denorms_preserve) { | ||
| 331 | if (profile.support_fp32_denorm_preserve) { | ||
| 332 | ctx.AddCapability(spv::Capability::DenormPreserve); | ||
| 333 | ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormPreserve, 32U); | ||
| 334 | } else { | ||
| 335 | LOG_DEBUG(Shader_SPIRV, "Fp32 denorm preserve used in shader without host support"); | ||
| 336 | } | ||
| 337 | } | ||
| 338 | if (!profile.support_separate_denorm_behavior || profile.has_broken_fp16_float_controls) { | ||
| 339 | // No separate denorm behavior | ||
| 340 | return; | ||
| 341 | } | ||
| 342 | if (info.uses_fp16_denorms_flush && info.uses_fp16_denorms_preserve) { | ||
| 343 | LOG_DEBUG(Shader_SPIRV, "Fp16 denorm flush and preserve on the same shader"); | ||
| 344 | } else if (info.uses_fp16_denorms_flush) { | ||
| 345 | if (profile.support_fp16_denorm_flush) { | ||
| 346 | ctx.AddCapability(spv::Capability::DenormFlushToZero); | ||
| 347 | ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 16U); | ||
| 348 | } else { | ||
| 349 | // Same as fp32, no need to warn as most drivers will flush by default | ||
| 350 | } | ||
| 351 | } else if (info.uses_fp16_denorms_preserve) { | ||
| 352 | if (profile.support_fp16_denorm_preserve) { | ||
| 353 | ctx.AddCapability(spv::Capability::DenormPreserve); | ||
| 354 | ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormPreserve, 16U); | ||
| 355 | } else { | ||
| 356 | LOG_DEBUG(Shader_SPIRV, "Fp16 denorm preserve used in shader without host support"); | ||
| 357 | } | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | void SetupSignedNanCapabilities(const Profile& profile, const IR::Program& program, | ||
| 362 | EmitContext& ctx, Id main_func) { | ||
| 363 | if (profile.has_broken_fp16_float_controls && program.info.uses_fp16) { | ||
| 364 | return; | ||
| 365 | } | ||
| 366 | if (program.info.uses_fp16 && profile.support_fp16_signed_zero_nan_preserve) { | ||
| 367 | ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve); | ||
| 368 | ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 16U); | ||
| 369 | } | ||
| 370 | if (profile.support_fp32_signed_zero_nan_preserve) { | ||
| 371 | ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve); | ||
| 372 | ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 32U); | ||
| 373 | } | ||
| 374 | if (program.info.uses_fp64 && profile.support_fp64_signed_zero_nan_preserve) { | ||
| 375 | ctx.AddCapability(spv::Capability::SignedZeroInfNanPreserve); | ||
| 376 | ctx.AddExecutionMode(main_func, spv::ExecutionMode::SignedZeroInfNanPreserve, 64U); | ||
| 377 | } | ||
| 378 | } | ||
| 379 | |||
| 380 | void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ctx) { | ||
| 381 | if (info.uses_sampled_1d) { | ||
| 382 | ctx.AddCapability(spv::Capability::Sampled1D); | ||
| 383 | } | ||
| 384 | if (info.uses_sparse_residency) { | ||
| 385 | ctx.AddCapability(spv::Capability::SparseResidency); | ||
| 386 | } | ||
| 387 | if (info.uses_demote_to_helper_invocation && profile.support_demote_to_helper_invocation) { | ||
| 388 | ctx.AddExtension("SPV_EXT_demote_to_helper_invocation"); | ||
| 389 | ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT); | ||
| 390 | } | ||
| 391 | if (info.stores[IR::Attribute::ViewportIndex]) { | ||
| 392 | ctx.AddCapability(spv::Capability::MultiViewport); | ||
| 393 | } | ||
| 394 | if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) { | ||
| 395 | ctx.AddExtension("SPV_NV_viewport_array2"); | ||
| 396 | ctx.AddCapability(spv::Capability::ShaderViewportMaskNV); | ||
| 397 | } | ||
| 398 | if (info.stores[IR::Attribute::Layer] || info.stores[IR::Attribute::ViewportIndex]) { | ||
| 399 | if (profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) { | ||
| 400 | ctx.AddExtension("SPV_EXT_shader_viewport_index_layer"); | ||
| 401 | ctx.AddCapability(spv::Capability::ShaderViewportIndexLayerEXT); | ||
| 402 | } | ||
| 403 | } | ||
| 404 | if (!profile.support_vertex_instance_id && | ||
| 405 | (info.loads[IR::Attribute::InstanceId] || info.loads[IR::Attribute::VertexId])) { | ||
| 406 | ctx.AddExtension("SPV_KHR_shader_draw_parameters"); | ||
| 407 | ctx.AddCapability(spv::Capability::DrawParameters); | ||
| 408 | } | ||
| 409 | if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id || | ||
| 410 | info.uses_subgroup_shuffles) && | ||
| 411 | profile.support_vote) { | ||
| 412 | ctx.AddExtension("SPV_KHR_shader_ballot"); | ||
| 413 | ctx.AddCapability(spv::Capability::SubgroupBallotKHR); | ||
| 414 | if (!profile.warp_size_potentially_larger_than_guest) { | ||
| 415 | // vote ops are only used when not taking the long path | ||
| 416 | ctx.AddExtension("SPV_KHR_subgroup_vote"); | ||
| 417 | ctx.AddCapability(spv::Capability::SubgroupVoteKHR); | ||
| 418 | } | ||
| 419 | } | ||
| 420 | if (info.uses_int64_bit_atomics && profile.support_int64_atomics) { | ||
| 421 | ctx.AddCapability(spv::Capability::Int64Atomics); | ||
| 422 | } | ||
| 423 | if (info.uses_typeless_image_reads && profile.support_typeless_image_loads) { | ||
| 424 | ctx.AddCapability(spv::Capability::StorageImageReadWithoutFormat); | ||
| 425 | } | ||
| 426 | if (info.uses_typeless_image_writes) { | ||
| 427 | ctx.AddCapability(spv::Capability::StorageImageWriteWithoutFormat); | ||
| 428 | } | ||
| 429 | if (info.uses_image_buffers) { | ||
| 430 | ctx.AddCapability(spv::Capability::ImageBuffer); | ||
| 431 | } | ||
| 432 | if (info.uses_sample_id) { | ||
| 433 | ctx.AddCapability(spv::Capability::SampleRateShading); | ||
| 434 | } | ||
| 435 | if (!ctx.runtime_info.xfb_varyings.empty()) { | ||
| 436 | ctx.AddCapability(spv::Capability::TransformFeedback); | ||
| 437 | } | ||
| 438 | if (info.uses_derivatives) { | ||
| 439 | ctx.AddCapability(spv::Capability::DerivativeControl); | ||
| 440 | } | ||
| 441 | // TODO: Track this usage | ||
| 442 | ctx.AddCapability(spv::Capability::ImageGatherExtended); | ||
| 443 | ctx.AddCapability(spv::Capability::ImageQuery); | ||
| 444 | ctx.AddCapability(spv::Capability::SampledBuffer); | ||
| 445 | } | ||
| 446 | |||
| 447 | void PatchPhiNodes(IR::Program& program, EmitContext& ctx) { | ||
| 448 | auto inst{program.blocks.front()->begin()}; | ||
| 449 | size_t block_index{0}; | ||
| 450 | ctx.PatchDeferredPhi([&](size_t phi_arg) { | ||
| 451 | if (phi_arg == 0) { | ||
| 452 | ++inst; | ||
| 453 | if (inst == program.blocks[block_index]->end() || | ||
| 454 | inst->GetOpcode() != IR::Opcode::Phi) { | ||
| 455 | do { | ||
| 456 | ++block_index; | ||
| 457 | inst = program.blocks[block_index]->begin(); | ||
| 458 | } while (inst->GetOpcode() != IR::Opcode::Phi); | ||
| 459 | } | ||
| 460 | } | ||
| 461 | return ctx.Def(inst->Arg(phi_arg)); | ||
| 462 | }); | ||
| 463 | } | ||
| 464 | } // Anonymous namespace | ||
| 465 | |||
| 466 | std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, | ||
| 467 | IR::Program& program, Bindings& bindings) { | ||
| 468 | EmitContext ctx{profile, runtime_info, program, bindings}; | ||
| 469 | const Id main{DefineMain(ctx, program)}; | ||
| 470 | DefineEntryPoint(program, ctx, main); | ||
| 471 | if (profile.support_float_controls) { | ||
| 472 | ctx.AddExtension("SPV_KHR_float_controls"); | ||
| 473 | SetupDenormControl(profile, program, ctx, main); | ||
| 474 | SetupSignedNanCapabilities(profile, program, ctx, main); | ||
| 475 | } | ||
| 476 | SetupCapabilities(profile, program.info, ctx); | ||
| 477 | PatchPhiNodes(program, ctx); | ||
| 478 | return ctx.Assemble(); | ||
| 479 | } | ||
| 480 | |||
| 481 | Id EmitPhi(EmitContext& ctx, IR::Inst* inst) { | ||
| 482 | const size_t num_args{inst->NumArgs()}; | ||
| 483 | boost::container::small_vector<Id, 32> blocks; | ||
| 484 | blocks.reserve(num_args); | ||
| 485 | for (size_t index = 0; index < num_args; ++index) { | ||
| 486 | blocks.push_back(inst->PhiBlock(index)->Definition<Id>()); | ||
| 487 | } | ||
| 488 | // The type of a phi instruction is stored in its flags | ||
| 489 | const Id result_type{TypeId(ctx, inst->Flags<IR::Type>())}; | ||
| 490 | return ctx.DeferredOpPhi(result_type, std::span(blocks.data(), blocks.size())); | ||
| 491 | } | ||
| 492 | |||
| 493 | void EmitVoid(EmitContext&) {} | ||
| 494 | |||
| 495 | Id EmitIdentity(EmitContext& ctx, const IR::Value& value) { | ||
| 496 | const Id id{ctx.Def(value)}; | ||
| 497 | if (!Sirit::ValidId(id)) { | ||
| 498 | throw NotImplementedException("Forward identity declaration"); | ||
| 499 | } | ||
| 500 | return id; | ||
| 501 | } | ||
| 502 | |||
| 503 | Id EmitConditionRef(EmitContext& ctx, const IR::Value& value) { | ||
| 504 | const Id id{ctx.Def(value)}; | ||
| 505 | if (!Sirit::ValidId(id)) { | ||
| 506 | throw NotImplementedException("Forward identity declaration"); | ||
| 507 | } | ||
| 508 | return id; | ||
| 509 | } | ||
| 510 | |||
| 511 | void EmitReference(EmitContext&) {} | ||
| 512 | |||
| 513 | void EmitPhiMove(EmitContext&) { | ||
| 514 | throw LogicError("Unreachable instruction"); | ||
| 515 | } | ||
| 516 | |||
| 517 | void EmitGetZeroFromOp(EmitContext&) { | ||
| 518 | throw LogicError("Unreachable instruction"); | ||
| 519 | } | ||
| 520 | |||
| 521 | void EmitGetSignFromOp(EmitContext&) { | ||
| 522 | throw LogicError("Unreachable instruction"); | ||
| 523 | } | ||
| 524 | |||
| 525 | void EmitGetCarryFromOp(EmitContext&) { | ||
| 526 | throw LogicError("Unreachable instruction"); | ||
| 527 | } | ||
| 528 | |||
| 529 | void EmitGetOverflowFromOp(EmitContext&) { | ||
| 530 | throw LogicError("Unreachable instruction"); | ||
| 531 | } | ||
| 532 | |||
| 533 | void EmitGetSparseFromOp(EmitContext&) { | ||
| 534 | throw LogicError("Unreachable instruction"); | ||
| 535 | } | ||
| 536 | |||
| 537 | void EmitGetInBoundsFromOp(EmitContext&) { | ||
| 538 | throw LogicError("Unreachable instruction"); | ||
| 539 | } | ||
| 540 | |||
| 541 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h new file mode 100644 index 000000000..db0c935fe --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include <sirit/sirit.h> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "shader_recompiler/backend/bindings.h" | ||
| 13 | #include "shader_recompiler/backend/spirv/emit_context.h" | ||
| 14 | #include "shader_recompiler/frontend/ir/program.h" | ||
| 15 | #include "shader_recompiler/profile.h" | ||
| 16 | |||
| 17 | namespace Shader::Backend::SPIRV { | ||
| 18 | |||
| 19 | [[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, | ||
| 20 | IR::Program& program, Bindings& bindings); | ||
| 21 | |||
| 22 | [[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) { | ||
| 23 | Bindings binding; | ||
| 24 | return EmitSPIRV(profile, {}, program, binding); | ||
| 25 | } | ||
| 26 | |||
| 27 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp new file mode 100644 index 000000000..9af8bb9e1 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp | |||
| @@ -0,0 +1,448 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 7 | |||
| 8 | namespace Shader::Backend::SPIRV { | ||
| 9 | namespace { | ||
| 10 | Id SharedPointer(EmitContext& ctx, Id offset, u32 index_offset = 0) { | ||
| 11 | const Id shift_id{ctx.Const(2U)}; | ||
| 12 | Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)}; | ||
| 13 | if (index_offset > 0) { | ||
| 14 | index = ctx.OpIAdd(ctx.U32[1], index, ctx.Const(index_offset)); | ||
| 15 | } | ||
| 16 | return ctx.profile.support_explicit_workgroup_layout | ||
| 17 | ? ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, ctx.u32_zero_value, index) | ||
| 18 | : ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index); | ||
| 19 | } | ||
| 20 | |||
| 21 | Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size) { | ||
| 22 | if (offset.IsImmediate()) { | ||
| 23 | const u32 imm_offset{static_cast<u32>(offset.U32() / element_size)}; | ||
| 24 | return ctx.Const(imm_offset); | ||
| 25 | } | ||
| 26 | const u32 shift{static_cast<u32>(std::countr_zero(element_size))}; | ||
| 27 | const Id index{ctx.Def(offset)}; | ||
| 28 | if (shift == 0) { | ||
| 29 | return index; | ||
| 30 | } | ||
| 31 | const Id shift_id{ctx.Const(shift)}; | ||
| 32 | return ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id); | ||
| 33 | } | ||
| 34 | |||
| 35 | Id StoragePointer(EmitContext& ctx, const StorageTypeDefinition& type_def, | ||
| 36 | Id StorageDefinitions::*member_ptr, const IR::Value& binding, | ||
| 37 | const IR::Value& offset, size_t element_size) { | ||
| 38 | if (!binding.IsImmediate()) { | ||
| 39 | throw NotImplementedException("Dynamic storage buffer indexing"); | ||
| 40 | } | ||
| 41 | const Id ssbo{ctx.ssbos[binding.U32()].*member_ptr}; | ||
| 42 | const Id index{StorageIndex(ctx, offset, element_size)}; | ||
| 43 | return ctx.OpAccessChain(type_def.element, ssbo, ctx.u32_zero_value, index); | ||
| 44 | } | ||
| 45 | |||
| 46 | std::pair<Id, Id> AtomicArgs(EmitContext& ctx) { | ||
| 47 | const Id scope{ctx.Const(static_cast<u32>(spv::Scope::Device))}; | ||
| 48 | const Id semantics{ctx.u32_zero_value}; | ||
| 49 | return {scope, semantics}; | ||
| 50 | } | ||
| 51 | |||
| 52 | Id SharedAtomicU32(EmitContext& ctx, Id offset, Id value, | ||
| 53 | Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) { | ||
| 54 | const Id pointer{SharedPointer(ctx, offset)}; | ||
| 55 | const auto [scope, semantics]{AtomicArgs(ctx)}; | ||
| 56 | return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value); | ||
| 57 | } | ||
| 58 | |||
| 59 | Id StorageAtomicU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value, | ||
| 60 | Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) { | ||
| 61 | const Id pointer{StoragePointer(ctx, ctx.storage_types.U32, &StorageDefinitions::U32, binding, | ||
| 62 | offset, sizeof(u32))}; | ||
| 63 | const auto [scope, semantics]{AtomicArgs(ctx)}; | ||
| 64 | return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value); | ||
| 65 | } | ||
| 66 | |||
| 67 | Id StorageAtomicU64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value, | ||
| 68 | Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id), | ||
| 69 | Id (Sirit::Module::*non_atomic_func)(Id, Id, Id)) { | ||
| 70 | if (ctx.profile.support_int64_atomics) { | ||
| 71 | const Id pointer{StoragePointer(ctx, ctx.storage_types.U64, &StorageDefinitions::U64, | ||
| 72 | binding, offset, sizeof(u64))}; | ||
| 73 | const auto [scope, semantics]{AtomicArgs(ctx)}; | ||
| 74 | return (ctx.*atomic_func)(ctx.U64, pointer, scope, semantics, value); | ||
| 75 | } | ||
| 76 | LOG_ERROR(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic"); | ||
| 77 | const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2, | ||
| 78 | binding, offset, sizeof(u32[2]))}; | ||
| 79 | const Id original_value{ctx.OpBitcast(ctx.U64, ctx.OpLoad(ctx.U32[2], pointer))}; | ||
| 80 | const Id result{(ctx.*non_atomic_func)(ctx.U64, value, original_value)}; | ||
| 81 | ctx.OpStore(pointer, ctx.OpBitcast(ctx.U32[2], result)); | ||
| 82 | return original_value; | ||
| 83 | } | ||
| 84 | } // Anonymous namespace | ||
| 85 | |||
| 86 | Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value) { | ||
| 87 | return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicIAdd); | ||
| 88 | } | ||
| 89 | |||
| 90 | Id EmitSharedAtomicSMin32(EmitContext& ctx, Id offset, Id value) { | ||
| 91 | return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicSMin); | ||
| 92 | } | ||
| 93 | |||
| 94 | Id EmitSharedAtomicUMin32(EmitContext& ctx, Id offset, Id value) { | ||
| 95 | return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicUMin); | ||
| 96 | } | ||
| 97 | |||
| 98 | Id EmitSharedAtomicSMax32(EmitContext& ctx, Id offset, Id value) { | ||
| 99 | return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicSMax); | ||
| 100 | } | ||
| 101 | |||
| 102 | Id EmitSharedAtomicUMax32(EmitContext& ctx, Id offset, Id value) { | ||
| 103 | return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicUMax); | ||
| 104 | } | ||
| 105 | |||
| 106 | Id EmitSharedAtomicInc32(EmitContext& ctx, Id offset, Id value) { | ||
| 107 | const Id shift_id{ctx.Const(2U)}; | ||
| 108 | const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)}; | ||
| 109 | return ctx.OpFunctionCall(ctx.U32[1], ctx.increment_cas_shared, index, value); | ||
| 110 | } | ||
| 111 | |||
| 112 | Id EmitSharedAtomicDec32(EmitContext& ctx, Id offset, Id value) { | ||
| 113 | const Id shift_id{ctx.Const(2U)}; | ||
| 114 | const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)}; | ||
| 115 | return ctx.OpFunctionCall(ctx.U32[1], ctx.decrement_cas_shared, index, value); | ||
| 116 | } | ||
| 117 | |||
| 118 | Id EmitSharedAtomicAnd32(EmitContext& ctx, Id offset, Id value) { | ||
| 119 | return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicAnd); | ||
| 120 | } | ||
| 121 | |||
| 122 | Id EmitSharedAtomicOr32(EmitContext& ctx, Id offset, Id value) { | ||
| 123 | return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicOr); | ||
| 124 | } | ||
| 125 | |||
| 126 | Id EmitSharedAtomicXor32(EmitContext& ctx, Id offset, Id value) { | ||
| 127 | return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicXor); | ||
| 128 | } | ||
| 129 | |||
| 130 | Id EmitSharedAtomicExchange32(EmitContext& ctx, Id offset, Id value) { | ||
| 131 | return SharedAtomicU32(ctx, offset, value, &Sirit::Module::OpAtomicExchange); | ||
| 132 | } | ||
| 133 | |||
| 134 | Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) { | ||
| 135 | if (ctx.profile.support_int64_atomics && ctx.profile.support_explicit_workgroup_layout) { | ||
| 136 | const Id shift_id{ctx.Const(3U)}; | ||
| 137 | const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)}; | ||
| 138 | const Id pointer{ | ||
| 139 | ctx.OpAccessChain(ctx.shared_u64, ctx.shared_memory_u64, ctx.u32_zero_value, index)}; | ||
| 140 | const auto [scope, semantics]{AtomicArgs(ctx)}; | ||
| 141 | return ctx.OpAtomicExchange(ctx.U64, pointer, scope, semantics, value); | ||
| 142 | } | ||
| 143 | LOG_ERROR(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic"); | ||
| 144 | const Id pointer_1{SharedPointer(ctx, offset, 0)}; | ||
| 145 | const Id pointer_2{SharedPointer(ctx, offset, 1)}; | ||
| 146 | const Id value_1{ctx.OpLoad(ctx.U32[1], pointer_1)}; | ||
| 147 | const Id value_2{ctx.OpLoad(ctx.U32[1], pointer_2)}; | ||
| 148 | const Id new_vector{ctx.OpBitcast(ctx.U32[2], value)}; | ||
| 149 | ctx.OpStore(pointer_1, ctx.OpCompositeExtract(ctx.U32[1], new_vector, 0U)); | ||
| 150 | ctx.OpStore(pointer_2, ctx.OpCompositeExtract(ctx.U32[1], new_vector, 1U)); | ||
| 151 | return ctx.OpBitcast(ctx.U64, ctx.OpCompositeConstruct(ctx.U32[2], value_1, value_2)); | ||
| 152 | } | ||
| 153 | |||
| 154 | Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 155 | Id value) { | ||
| 156 | return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicIAdd); | ||
| 157 | } | ||
| 158 | |||
| 159 | Id EmitStorageAtomicSMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 160 | Id value) { | ||
| 161 | return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicSMin); | ||
| 162 | } | ||
| 163 | |||
| 164 | Id EmitStorageAtomicUMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 165 | Id value) { | ||
| 166 | return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicUMin); | ||
| 167 | } | ||
| 168 | |||
| 169 | Id EmitStorageAtomicSMax32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 170 | Id value) { | ||
| 171 | return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicSMax); | ||
| 172 | } | ||
| 173 | |||
| 174 | Id EmitStorageAtomicUMax32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 175 | Id value) { | ||
| 176 | return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicUMax); | ||
| 177 | } | ||
| 178 | |||
| 179 | Id EmitStorageAtomicInc32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 180 | Id value) { | ||
| 181 | const Id ssbo{ctx.ssbos[binding.U32()].U32}; | ||
| 182 | const Id base_index{StorageIndex(ctx, offset, sizeof(u32))}; | ||
| 183 | return ctx.OpFunctionCall(ctx.U32[1], ctx.increment_cas_ssbo, base_index, value, ssbo); | ||
| 184 | } | ||
| 185 | |||
| 186 | Id EmitStorageAtomicDec32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 187 | Id value) { | ||
| 188 | const Id ssbo{ctx.ssbos[binding.U32()].U32}; | ||
| 189 | const Id base_index{StorageIndex(ctx, offset, sizeof(u32))}; | ||
| 190 | return ctx.OpFunctionCall(ctx.U32[1], ctx.decrement_cas_ssbo, base_index, value, ssbo); | ||
| 191 | } | ||
| 192 | |||
| 193 | Id EmitStorageAtomicAnd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 194 | Id value) { | ||
| 195 | return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicAnd); | ||
| 196 | } | ||
| 197 | |||
| 198 | Id EmitStorageAtomicOr32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 199 | Id value) { | ||
| 200 | return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicOr); | ||
| 201 | } | ||
| 202 | |||
| 203 | Id EmitStorageAtomicXor32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 204 | Id value) { | ||
| 205 | return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicXor); | ||
| 206 | } | ||
| 207 | |||
| 208 | Id EmitStorageAtomicExchange32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 209 | Id value) { | ||
| 210 | return StorageAtomicU32(ctx, binding, offset, value, &Sirit::Module::OpAtomicExchange); | ||
| 211 | } | ||
| 212 | |||
| 213 | Id EmitStorageAtomicIAdd64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 214 | Id value) { | ||
| 215 | return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicIAdd, | ||
| 216 | &Sirit::Module::OpIAdd); | ||
| 217 | } | ||
| 218 | |||
| 219 | Id EmitStorageAtomicSMin64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 220 | Id value) { | ||
| 221 | return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicSMin, | ||
| 222 | &Sirit::Module::OpSMin); | ||
| 223 | } | ||
| 224 | |||
| 225 | Id EmitStorageAtomicUMin64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 226 | Id value) { | ||
| 227 | return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicUMin, | ||
| 228 | &Sirit::Module::OpUMin); | ||
| 229 | } | ||
| 230 | |||
| 231 | Id EmitStorageAtomicSMax64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 232 | Id value) { | ||
| 233 | return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicSMax, | ||
| 234 | &Sirit::Module::OpSMax); | ||
| 235 | } | ||
| 236 | |||
| 237 | Id EmitStorageAtomicUMax64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 238 | Id value) { | ||
| 239 | return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicUMax, | ||
| 240 | &Sirit::Module::OpUMax); | ||
| 241 | } | ||
| 242 | |||
| 243 | Id EmitStorageAtomicAnd64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 244 | Id value) { | ||
| 245 | return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicAnd, | ||
| 246 | &Sirit::Module::OpBitwiseAnd); | ||
| 247 | } | ||
| 248 | |||
| 249 | Id EmitStorageAtomicOr64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 250 | Id value) { | ||
| 251 | return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicOr, | ||
| 252 | &Sirit::Module::OpBitwiseOr); | ||
| 253 | } | ||
| 254 | |||
| 255 | Id EmitStorageAtomicXor64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 256 | Id value) { | ||
| 257 | return StorageAtomicU64(ctx, binding, offset, value, &Sirit::Module::OpAtomicXor, | ||
| 258 | &Sirit::Module::OpBitwiseXor); | ||
| 259 | } | ||
| 260 | |||
| 261 | Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 262 | Id value) { | ||
| 263 | if (ctx.profile.support_int64_atomics) { | ||
| 264 | const Id pointer{StoragePointer(ctx, ctx.storage_types.U64, &StorageDefinitions::U64, | ||
| 265 | binding, offset, sizeof(u64))}; | ||
| 266 | const auto [scope, semantics]{AtomicArgs(ctx)}; | ||
| 267 | return ctx.OpAtomicExchange(ctx.U64, pointer, scope, semantics, value); | ||
| 268 | } | ||
| 269 | LOG_ERROR(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic"); | ||
| 270 | const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2, | ||
| 271 | binding, offset, sizeof(u32[2]))}; | ||
| 272 | const Id original{ctx.OpBitcast(ctx.U64, ctx.OpLoad(ctx.U32[2], pointer))}; | ||
| 273 | ctx.OpStore(pointer, value); | ||
| 274 | return original; | ||
| 275 | } | ||
| 276 | |||
| 277 | Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 278 | Id value) { | ||
| 279 | const Id ssbo{ctx.ssbos[binding.U32()].U32}; | ||
| 280 | const Id base_index{StorageIndex(ctx, offset, sizeof(u32))}; | ||
| 281 | return ctx.OpFunctionCall(ctx.F32[1], ctx.f32_add_cas, base_index, value, ssbo); | ||
| 282 | } | ||
| 283 | |||
| 284 | Id EmitStorageAtomicAddF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 285 | Id value) { | ||
| 286 | const Id ssbo{ctx.ssbos[binding.U32()].U32}; | ||
| 287 | const Id base_index{StorageIndex(ctx, offset, sizeof(u32))}; | ||
| 288 | const Id result{ctx.OpFunctionCall(ctx.F16[2], ctx.f16x2_add_cas, base_index, value, ssbo)}; | ||
| 289 | return ctx.OpBitcast(ctx.U32[1], result); | ||
| 290 | } | ||
| 291 | |||
| 292 | Id EmitStorageAtomicAddF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 293 | Id value) { | ||
| 294 | const Id ssbo{ctx.ssbos[binding.U32()].U32}; | ||
| 295 | const Id base_index{StorageIndex(ctx, offset, sizeof(u32))}; | ||
| 296 | const Id result{ctx.OpFunctionCall(ctx.F32[2], ctx.f32x2_add_cas, base_index, value, ssbo)}; | ||
| 297 | return ctx.OpPackHalf2x16(ctx.U32[1], result); | ||
| 298 | } | ||
| 299 | |||
| 300 | Id EmitStorageAtomicMinF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 301 | Id value) { | ||
| 302 | const Id ssbo{ctx.ssbos[binding.U32()].U32}; | ||
| 303 | const Id base_index{StorageIndex(ctx, offset, sizeof(u32))}; | ||
| 304 | const Id result{ctx.OpFunctionCall(ctx.F16[2], ctx.f16x2_min_cas, base_index, value, ssbo)}; | ||
| 305 | return ctx.OpBitcast(ctx.U32[1], result); | ||
| 306 | } | ||
| 307 | |||
| 308 | Id EmitStorageAtomicMinF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 309 | Id value) { | ||
| 310 | const Id ssbo{ctx.ssbos[binding.U32()].U32}; | ||
| 311 | const Id base_index{StorageIndex(ctx, offset, sizeof(u32))}; | ||
| 312 | const Id result{ctx.OpFunctionCall(ctx.F32[2], ctx.f32x2_min_cas, base_index, value, ssbo)}; | ||
| 313 | return ctx.OpPackHalf2x16(ctx.U32[1], result); | ||
| 314 | } | ||
| 315 | |||
| 316 | Id EmitStorageAtomicMaxF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 317 | Id value) { | ||
| 318 | const Id ssbo{ctx.ssbos[binding.U32()].U32}; | ||
| 319 | const Id base_index{StorageIndex(ctx, offset, sizeof(u32))}; | ||
| 320 | const Id result{ctx.OpFunctionCall(ctx.F16[2], ctx.f16x2_max_cas, base_index, value, ssbo)}; | ||
| 321 | return ctx.OpBitcast(ctx.U32[1], result); | ||
| 322 | } | ||
| 323 | |||
| 324 | Id EmitStorageAtomicMaxF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 325 | Id value) { | ||
| 326 | const Id ssbo{ctx.ssbos[binding.U32()].U32}; | ||
| 327 | const Id base_index{StorageIndex(ctx, offset, sizeof(u32))}; | ||
| 328 | const Id result{ctx.OpFunctionCall(ctx.F32[2], ctx.f32x2_max_cas, base_index, value, ssbo)}; | ||
| 329 | return ctx.OpPackHalf2x16(ctx.U32[1], result); | ||
| 330 | } | ||
| 331 | |||
| 332 | Id EmitGlobalAtomicIAdd32(EmitContext&) { | ||
| 333 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 334 | } | ||
| 335 | |||
| 336 | Id EmitGlobalAtomicSMin32(EmitContext&) { | ||
| 337 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 338 | } | ||
| 339 | |||
| 340 | Id EmitGlobalAtomicUMin32(EmitContext&) { | ||
| 341 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 342 | } | ||
| 343 | |||
| 344 | Id EmitGlobalAtomicSMax32(EmitContext&) { | ||
| 345 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 346 | } | ||
| 347 | |||
| 348 | Id EmitGlobalAtomicUMax32(EmitContext&) { | ||
| 349 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 350 | } | ||
| 351 | |||
| 352 | Id EmitGlobalAtomicInc32(EmitContext&) { | ||
| 353 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 354 | } | ||
| 355 | |||
| 356 | Id EmitGlobalAtomicDec32(EmitContext&) { | ||
| 357 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 358 | } | ||
| 359 | |||
| 360 | Id EmitGlobalAtomicAnd32(EmitContext&) { | ||
| 361 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 362 | } | ||
| 363 | |||
| 364 | Id EmitGlobalAtomicOr32(EmitContext&) { | ||
| 365 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 366 | } | ||
| 367 | |||
| 368 | Id EmitGlobalAtomicXor32(EmitContext&) { | ||
| 369 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 370 | } | ||
| 371 | |||
| 372 | Id EmitGlobalAtomicExchange32(EmitContext&) { | ||
| 373 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 374 | } | ||
| 375 | |||
| 376 | Id EmitGlobalAtomicIAdd64(EmitContext&) { | ||
| 377 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 378 | } | ||
| 379 | |||
| 380 | Id EmitGlobalAtomicSMin64(EmitContext&) { | ||
| 381 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 382 | } | ||
| 383 | |||
| 384 | Id EmitGlobalAtomicUMin64(EmitContext&) { | ||
| 385 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 386 | } | ||
| 387 | |||
| 388 | Id EmitGlobalAtomicSMax64(EmitContext&) { | ||
| 389 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 390 | } | ||
| 391 | |||
| 392 | Id EmitGlobalAtomicUMax64(EmitContext&) { | ||
| 393 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 394 | } | ||
| 395 | |||
| 396 | Id EmitGlobalAtomicInc64(EmitContext&) { | ||
| 397 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 398 | } | ||
| 399 | |||
| 400 | Id EmitGlobalAtomicDec64(EmitContext&) { | ||
| 401 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 402 | } | ||
| 403 | |||
| 404 | Id EmitGlobalAtomicAnd64(EmitContext&) { | ||
| 405 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 406 | } | ||
| 407 | |||
| 408 | Id EmitGlobalAtomicOr64(EmitContext&) { | ||
| 409 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 410 | } | ||
| 411 | |||
| 412 | Id EmitGlobalAtomicXor64(EmitContext&) { | ||
| 413 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 414 | } | ||
| 415 | |||
| 416 | Id EmitGlobalAtomicExchange64(EmitContext&) { | ||
| 417 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 418 | } | ||
| 419 | |||
| 420 | Id EmitGlobalAtomicAddF32(EmitContext&) { | ||
| 421 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 422 | } | ||
| 423 | |||
| 424 | Id EmitGlobalAtomicAddF16x2(EmitContext&) { | ||
| 425 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 426 | } | ||
| 427 | |||
| 428 | Id EmitGlobalAtomicAddF32x2(EmitContext&) { | ||
| 429 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 430 | } | ||
| 431 | |||
| 432 | Id EmitGlobalAtomicMinF16x2(EmitContext&) { | ||
| 433 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 434 | } | ||
| 435 | |||
| 436 | Id EmitGlobalAtomicMinF32x2(EmitContext&) { | ||
| 437 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 438 | } | ||
| 439 | |||
| 440 | Id EmitGlobalAtomicMaxF16x2(EmitContext&) { | ||
| 441 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 442 | } | ||
| 443 | |||
| 444 | Id EmitGlobalAtomicMaxF32x2(EmitContext&) { | ||
| 445 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 446 | } | ||
| 447 | |||
| 448 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp new file mode 100644 index 000000000..e0b52a001 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_barriers.cpp | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 7 | #include "shader_recompiler/frontend/ir/modifiers.h" | ||
| 8 | |||
| 9 | namespace Shader::Backend::SPIRV { | ||
| 10 | namespace { | ||
| 11 | void MemoryBarrier(EmitContext& ctx, spv::Scope scope) { | ||
| 12 | const auto semantics{ | ||
| 13 | spv::MemorySemanticsMask::AcquireRelease | spv::MemorySemanticsMask::UniformMemory | | ||
| 14 | spv::MemorySemanticsMask::WorkgroupMemory | spv::MemorySemanticsMask::AtomicCounterMemory | | ||
| 15 | spv::MemorySemanticsMask::ImageMemory}; | ||
| 16 | ctx.OpMemoryBarrier(ctx.Const(static_cast<u32>(scope)), ctx.Const(static_cast<u32>(semantics))); | ||
| 17 | } | ||
| 18 | } // Anonymous namespace | ||
| 19 | |||
| 20 | void EmitBarrier(EmitContext& ctx) { | ||
| 21 | const auto execution{spv::Scope::Workgroup}; | ||
| 22 | const auto memory{spv::Scope::Workgroup}; | ||
| 23 | const auto memory_semantics{spv::MemorySemanticsMask::AcquireRelease | | ||
| 24 | spv::MemorySemanticsMask::WorkgroupMemory}; | ||
| 25 | ctx.OpControlBarrier(ctx.Const(static_cast<u32>(execution)), | ||
| 26 | ctx.Const(static_cast<u32>(memory)), | ||
| 27 | ctx.Const(static_cast<u32>(memory_semantics))); | ||
| 28 | } | ||
| 29 | |||
| 30 | void EmitWorkgroupMemoryBarrier(EmitContext& ctx) { | ||
| 31 | MemoryBarrier(ctx, spv::Scope::Workgroup); | ||
| 32 | } | ||
| 33 | |||
| 34 | void EmitDeviceMemoryBarrier(EmitContext& ctx) { | ||
| 35 | MemoryBarrier(ctx, spv::Scope::Device); | ||
| 36 | } | ||
| 37 | |||
| 38 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp new file mode 100644 index 000000000..bb11f4f4e --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_bitwise_conversion.cpp | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 7 | |||
| 8 | namespace Shader::Backend::SPIRV { | ||
| 9 | |||
| 10 | void EmitBitCastU16F16(EmitContext&) { | ||
| 11 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 12 | } | ||
| 13 | |||
| 14 | Id EmitBitCastU32F32(EmitContext& ctx, Id value) { | ||
| 15 | return ctx.OpBitcast(ctx.U32[1], value); | ||
| 16 | } | ||
| 17 | |||
| 18 | void EmitBitCastU64F64(EmitContext&) { | ||
| 19 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 20 | } | ||
| 21 | |||
| 22 | void EmitBitCastF16U16(EmitContext&) { | ||
| 23 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 24 | } | ||
| 25 | |||
| 26 | Id EmitBitCastF32U32(EmitContext& ctx, Id value) { | ||
| 27 | return ctx.OpBitcast(ctx.F32[1], value); | ||
| 28 | } | ||
| 29 | |||
| 30 | void EmitBitCastF64U64(EmitContext&) { | ||
| 31 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 32 | } | ||
| 33 | |||
| 34 | Id EmitPackUint2x32(EmitContext& ctx, Id value) { | ||
| 35 | return ctx.OpBitcast(ctx.U64, value); | ||
| 36 | } | ||
| 37 | |||
| 38 | Id EmitUnpackUint2x32(EmitContext& ctx, Id value) { | ||
| 39 | return ctx.OpBitcast(ctx.U32[2], value); | ||
| 40 | } | ||
| 41 | |||
| 42 | Id EmitPackFloat2x16(EmitContext& ctx, Id value) { | ||
| 43 | return ctx.OpBitcast(ctx.U32[1], value); | ||
| 44 | } | ||
| 45 | |||
| 46 | Id EmitUnpackFloat2x16(EmitContext& ctx, Id value) { | ||
| 47 | return ctx.OpBitcast(ctx.F16[2], value); | ||
| 48 | } | ||
| 49 | |||
| 50 | Id EmitPackHalf2x16(EmitContext& ctx, Id value) { | ||
| 51 | return ctx.OpPackHalf2x16(ctx.U32[1], value); | ||
| 52 | } | ||
| 53 | |||
| 54 | Id EmitUnpackHalf2x16(EmitContext& ctx, Id value) { | ||
| 55 | return ctx.OpUnpackHalf2x16(ctx.F32[2], value); | ||
| 56 | } | ||
| 57 | |||
| 58 | Id EmitPackDouble2x32(EmitContext& ctx, Id value) { | ||
| 59 | return ctx.OpBitcast(ctx.F64[1], value); | ||
| 60 | } | ||
| 61 | |||
| 62 | Id EmitUnpackDouble2x32(EmitContext& ctx, Id value) { | ||
| 63 | return ctx.OpBitcast(ctx.U32[2], value); | ||
| 64 | } | ||
| 65 | |||
| 66 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp new file mode 100644 index 000000000..10ff4ecab --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 7 | #include "shader_recompiler/frontend/ir/modifiers.h" | ||
| 8 | |||
| 9 | namespace Shader::Backend::SPIRV { | ||
| 10 | |||
| 11 | Id EmitCompositeConstructU32x2(EmitContext& ctx, Id e1, Id e2) { | ||
| 12 | return ctx.OpCompositeConstruct(ctx.U32[2], e1, e2); | ||
| 13 | } | ||
| 14 | |||
| 15 | Id EmitCompositeConstructU32x3(EmitContext& ctx, Id e1, Id e2, Id e3) { | ||
| 16 | return ctx.OpCompositeConstruct(ctx.U32[3], e1, e2, e3); | ||
| 17 | } | ||
| 18 | |||
| 19 | Id EmitCompositeConstructU32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) { | ||
| 20 | return ctx.OpCompositeConstruct(ctx.U32[4], e1, e2, e3, e4); | ||
| 21 | } | ||
| 22 | |||
| 23 | Id EmitCompositeExtractU32x2(EmitContext& ctx, Id composite, u32 index) { | ||
| 24 | return ctx.OpCompositeExtract(ctx.U32[1], composite, index); | ||
| 25 | } | ||
| 26 | |||
| 27 | Id EmitCompositeExtractU32x3(EmitContext& ctx, Id composite, u32 index) { | ||
| 28 | return ctx.OpCompositeExtract(ctx.U32[1], composite, index); | ||
| 29 | } | ||
| 30 | |||
| 31 | Id EmitCompositeExtractU32x4(EmitContext& ctx, Id composite, u32 index) { | ||
| 32 | return ctx.OpCompositeExtract(ctx.U32[1], composite, index); | ||
| 33 | } | ||
| 34 | |||
| 35 | Id EmitCompositeInsertU32x2(EmitContext& ctx, Id composite, Id object, u32 index) { | ||
| 36 | return ctx.OpCompositeInsert(ctx.U32[2], object, composite, index); | ||
| 37 | } | ||
| 38 | |||
| 39 | Id EmitCompositeInsertU32x3(EmitContext& ctx, Id composite, Id object, u32 index) { | ||
| 40 | return ctx.OpCompositeInsert(ctx.U32[3], object, composite, index); | ||
| 41 | } | ||
| 42 | |||
| 43 | Id EmitCompositeInsertU32x4(EmitContext& ctx, Id composite, Id object, u32 index) { | ||
| 44 | return ctx.OpCompositeInsert(ctx.U32[4], object, composite, index); | ||
| 45 | } | ||
| 46 | |||
| 47 | Id EmitCompositeConstructF16x2(EmitContext& ctx, Id e1, Id e2) { | ||
| 48 | return ctx.OpCompositeConstruct(ctx.F16[2], e1, e2); | ||
| 49 | } | ||
| 50 | |||
| 51 | Id EmitCompositeConstructF16x3(EmitContext& ctx, Id e1, Id e2, Id e3) { | ||
| 52 | return ctx.OpCompositeConstruct(ctx.F16[3], e1, e2, e3); | ||
| 53 | } | ||
| 54 | |||
| 55 | Id EmitCompositeConstructF16x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) { | ||
| 56 | return ctx.OpCompositeConstruct(ctx.F16[4], e1, e2, e3, e4); | ||
| 57 | } | ||
| 58 | |||
| 59 | Id EmitCompositeExtractF16x2(EmitContext& ctx, Id composite, u32 index) { | ||
| 60 | return ctx.OpCompositeExtract(ctx.F16[1], composite, index); | ||
| 61 | } | ||
| 62 | |||
| 63 | Id EmitCompositeExtractF16x3(EmitContext& ctx, Id composite, u32 index) { | ||
| 64 | return ctx.OpCompositeExtract(ctx.F16[1], composite, index); | ||
| 65 | } | ||
| 66 | |||
| 67 | Id EmitCompositeExtractF16x4(EmitContext& ctx, Id composite, u32 index) { | ||
| 68 | return ctx.OpCompositeExtract(ctx.F16[1], composite, index); | ||
| 69 | } | ||
| 70 | |||
| 71 | Id EmitCompositeInsertF16x2(EmitContext& ctx, Id composite, Id object, u32 index) { | ||
| 72 | return ctx.OpCompositeInsert(ctx.F16[2], object, composite, index); | ||
| 73 | } | ||
| 74 | |||
| 75 | Id EmitCompositeInsertF16x3(EmitContext& ctx, Id composite, Id object, u32 index) { | ||
| 76 | return ctx.OpCompositeInsert(ctx.F16[3], object, composite, index); | ||
| 77 | } | ||
| 78 | |||
| 79 | Id EmitCompositeInsertF16x4(EmitContext& ctx, Id composite, Id object, u32 index) { | ||
| 80 | return ctx.OpCompositeInsert(ctx.F16[4], object, composite, index); | ||
| 81 | } | ||
| 82 | |||
| 83 | Id EmitCompositeConstructF32x2(EmitContext& ctx, Id e1, Id e2) { | ||
| 84 | return ctx.OpCompositeConstruct(ctx.F32[2], e1, e2); | ||
| 85 | } | ||
| 86 | |||
| 87 | Id EmitCompositeConstructF32x3(EmitContext& ctx, Id e1, Id e2, Id e3) { | ||
| 88 | return ctx.OpCompositeConstruct(ctx.F32[3], e1, e2, e3); | ||
| 89 | } | ||
| 90 | |||
| 91 | Id EmitCompositeConstructF32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4) { | ||
| 92 | return ctx.OpCompositeConstruct(ctx.F32[4], e1, e2, e3, e4); | ||
| 93 | } | ||
| 94 | |||
| 95 | Id EmitCompositeExtractF32x2(EmitContext& ctx, Id composite, u32 index) { | ||
| 96 | return ctx.OpCompositeExtract(ctx.F32[1], composite, index); | ||
| 97 | } | ||
| 98 | |||
| 99 | Id EmitCompositeExtractF32x3(EmitContext& ctx, Id composite, u32 index) { | ||
| 100 | return ctx.OpCompositeExtract(ctx.F32[1], composite, index); | ||
| 101 | } | ||
| 102 | |||
| 103 | Id EmitCompositeExtractF32x4(EmitContext& ctx, Id composite, u32 index) { | ||
| 104 | return ctx.OpCompositeExtract(ctx.F32[1], composite, index); | ||
| 105 | } | ||
| 106 | |||
| 107 | Id EmitCompositeInsertF32x2(EmitContext& ctx, Id composite, Id object, u32 index) { | ||
| 108 | return ctx.OpCompositeInsert(ctx.F32[2], object, composite, index); | ||
| 109 | } | ||
| 110 | |||
| 111 | Id EmitCompositeInsertF32x3(EmitContext& ctx, Id composite, Id object, u32 index) { | ||
| 112 | return ctx.OpCompositeInsert(ctx.F32[3], object, composite, index); | ||
| 113 | } | ||
| 114 | |||
| 115 | Id EmitCompositeInsertF32x4(EmitContext& ctx, Id composite, Id object, u32 index) { | ||
| 116 | return ctx.OpCompositeInsert(ctx.F32[4], object, composite, index); | ||
| 117 | } | ||
| 118 | |||
| 119 | void EmitCompositeConstructF64x2(EmitContext&) { | ||
| 120 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 121 | } | ||
| 122 | |||
| 123 | void EmitCompositeConstructF64x3(EmitContext&) { | ||
| 124 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 125 | } | ||
| 126 | |||
| 127 | void EmitCompositeConstructF64x4(EmitContext&) { | ||
| 128 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 129 | } | ||
| 130 | |||
| 131 | void EmitCompositeExtractF64x2(EmitContext&) { | ||
| 132 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 133 | } | ||
| 134 | |||
| 135 | void EmitCompositeExtractF64x3(EmitContext&) { | ||
| 136 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 137 | } | ||
| 138 | |||
| 139 | void EmitCompositeExtractF64x4(EmitContext&) { | ||
| 140 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 141 | } | ||
| 142 | |||
| 143 | Id EmitCompositeInsertF64x2(EmitContext& ctx, Id composite, Id object, u32 index) { | ||
| 144 | return ctx.OpCompositeInsert(ctx.F64[2], object, composite, index); | ||
| 145 | } | ||
| 146 | |||
| 147 | Id EmitCompositeInsertF64x3(EmitContext& ctx, Id composite, Id object, u32 index) { | ||
| 148 | return ctx.OpCompositeInsert(ctx.F64[3], object, composite, index); | ||
| 149 | } | ||
| 150 | |||
| 151 | Id EmitCompositeInsertF64x4(EmitContext& ctx, Id composite, Id object, u32 index) { | ||
| 152 | return ctx.OpCompositeInsert(ctx.F64[4], object, composite, index); | ||
| 153 | } | ||
| 154 | |||
| 155 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp new file mode 100644 index 000000000..fb8c02a77 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp | |||
| @@ -0,0 +1,505 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <tuple> | ||
| 6 | #include <utility> | ||
| 7 | |||
| 8 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 9 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 10 | |||
| 11 | namespace Shader::Backend::SPIRV { | ||
| 12 | namespace { | ||
| 13 | struct AttrInfo { | ||
| 14 | Id pointer; | ||
| 15 | Id id; | ||
| 16 | bool needs_cast; | ||
| 17 | }; | ||
| 18 | |||
| 19 | std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) { | ||
| 20 | const AttributeType type{ctx.runtime_info.generic_input_types.at(index)}; | ||
| 21 | switch (type) { | ||
| 22 | case AttributeType::Float: | ||
| 23 | return AttrInfo{ctx.input_f32, ctx.F32[1], false}; | ||
| 24 | case AttributeType::UnsignedInt: | ||
| 25 | return AttrInfo{ctx.input_u32, ctx.U32[1], true}; | ||
| 26 | case AttributeType::SignedInt: | ||
| 27 | return AttrInfo{ctx.input_s32, ctx.TypeInt(32, true), true}; | ||
| 28 | case AttributeType::Disabled: | ||
| 29 | return std::nullopt; | ||
| 30 | } | ||
| 31 | throw InvalidArgument("Invalid attribute type {}", type); | ||
| 32 | } | ||
| 33 | |||
| 34 | template <typename... Args> | ||
| 35 | Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... args) { | ||
| 36 | switch (ctx.stage) { | ||
| 37 | case Stage::TessellationControl: | ||
| 38 | case Stage::TessellationEval: | ||
| 39 | case Stage::Geometry: | ||
| 40 | return ctx.OpAccessChain(pointer_type, base, vertex, std::forward<Args>(args)...); | ||
| 41 | default: | ||
| 42 | return ctx.OpAccessChain(pointer_type, base, std::forward<Args>(args)...); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | template <typename... Args> | ||
| 47 | Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) { | ||
| 48 | if (ctx.stage == Stage::TessellationControl) { | ||
| 49 | const Id invocation_id{ctx.OpLoad(ctx.U32[1], ctx.invocation_id)}; | ||
| 50 | return ctx.OpAccessChain(result_type, base, invocation_id, std::forward<Args>(args)...); | ||
| 51 | } else { | ||
| 52 | return ctx.OpAccessChain(result_type, base, std::forward<Args>(args)...); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | struct OutAttr { | ||
| 57 | OutAttr(Id pointer_) : pointer{pointer_} {} | ||
| 58 | OutAttr(Id pointer_, Id type_) : pointer{pointer_}, type{type_} {} | ||
| 59 | |||
| 60 | Id pointer{}; | ||
| 61 | Id type{}; | ||
| 62 | }; | ||
| 63 | |||
| 64 | std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { | ||
| 65 | if (IR::IsGeneric(attr)) { | ||
| 66 | const u32 index{IR::GenericAttributeIndex(attr)}; | ||
| 67 | const u32 element{IR::GenericAttributeElement(attr)}; | ||
| 68 | const GenericElementInfo& info{ctx.output_generics.at(index).at(element)}; | ||
| 69 | if (info.num_components == 1) { | ||
| 70 | return info.id; | ||
| 71 | } else { | ||
| 72 | const u32 index_element{element - info.first_element}; | ||
| 73 | const Id index_id{ctx.Const(index_element)}; | ||
| 74 | return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id); | ||
| 75 | } | ||
| 76 | } | ||
| 77 | switch (attr) { | ||
| 78 | case IR::Attribute::PointSize: | ||
| 79 | return ctx.output_point_size; | ||
| 80 | case IR::Attribute::PositionX: | ||
| 81 | case IR::Attribute::PositionY: | ||
| 82 | case IR::Attribute::PositionZ: | ||
| 83 | case IR::Attribute::PositionW: { | ||
| 84 | const u32 element{static_cast<u32>(attr) % 4}; | ||
| 85 | const Id element_id{ctx.Const(element)}; | ||
| 86 | return OutputAccessChain(ctx, ctx.output_f32, ctx.output_position, element_id); | ||
| 87 | } | ||
| 88 | case IR::Attribute::ClipDistance0: | ||
| 89 | case IR::Attribute::ClipDistance1: | ||
| 90 | case IR::Attribute::ClipDistance2: | ||
| 91 | case IR::Attribute::ClipDistance3: | ||
| 92 | case IR::Attribute::ClipDistance4: | ||
| 93 | case IR::Attribute::ClipDistance5: | ||
| 94 | case IR::Attribute::ClipDistance6: | ||
| 95 | case IR::Attribute::ClipDistance7: { | ||
| 96 | const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)}; | ||
| 97 | const u32 index{static_cast<u32>(attr) - base}; | ||
| 98 | const Id clip_num{ctx.Const(index)}; | ||
| 99 | return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num); | ||
| 100 | } | ||
| 101 | case IR::Attribute::Layer: | ||
| 102 | if (ctx.profile.support_viewport_index_layer_non_geometry || | ||
| 103 | ctx.stage == Shader::Stage::Geometry) { | ||
| 104 | return OutAttr{ctx.layer, ctx.U32[1]}; | ||
| 105 | } | ||
| 106 | return std::nullopt; | ||
| 107 | case IR::Attribute::ViewportIndex: | ||
| 108 | if (ctx.profile.support_viewport_index_layer_non_geometry || | ||
| 109 | ctx.stage == Shader::Stage::Geometry) { | ||
| 110 | return OutAttr{ctx.viewport_index, ctx.U32[1]}; | ||
| 111 | } | ||
| 112 | return std::nullopt; | ||
| 113 | case IR::Attribute::ViewportMask: | ||
| 114 | if (!ctx.profile.support_viewport_mask) { | ||
| 115 | return std::nullopt; | ||
| 116 | } | ||
| 117 | return OutAttr{ctx.OpAccessChain(ctx.output_u32, ctx.viewport_mask, ctx.u32_zero_value), | ||
| 118 | ctx.U32[1]}; | ||
| 119 | default: | ||
| 120 | throw NotImplementedException("Read attribute {}", attr); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, u32 element_size, | ||
| 125 | const IR::Value& binding, const IR::Value& offset) { | ||
| 126 | if (!binding.IsImmediate()) { | ||
| 127 | throw NotImplementedException("Constant buffer indexing"); | ||
| 128 | } | ||
| 129 | const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr}; | ||
| 130 | const Id uniform_type{ctx.uniform_types.*member_ptr}; | ||
| 131 | if (!offset.IsImmediate()) { | ||
| 132 | Id index{ctx.Def(offset)}; | ||
| 133 | if (element_size > 1) { | ||
| 134 | const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))}; | ||
| 135 | const Id shift{ctx.Const(log2_element_size)}; | ||
| 136 | index = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift); | ||
| 137 | } | ||
| 138 | const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, index)}; | ||
| 139 | return ctx.OpLoad(result_type, access_chain); | ||
| 140 | } | ||
| 141 | // Hardware been proved to read the aligned offset (e.g. LDC.U32 at 6 will read offset 4) | ||
| 142 | const Id imm_offset{ctx.Const(offset.U32() / element_size)}; | ||
| 143 | const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, imm_offset)}; | ||
| 144 | return ctx.OpLoad(result_type, access_chain); | ||
| 145 | } | ||
| 146 | |||
| 147 | Id GetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 148 | return GetCbuf(ctx, ctx.U32[1], &UniformDefinitions::U32, sizeof(u32), binding, offset); | ||
| 149 | } | ||
| 150 | |||
| 151 | Id GetCbufU32x4(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 152 | return GetCbuf(ctx, ctx.U32[4], &UniformDefinitions::U32x4, sizeof(u32[4]), binding, offset); | ||
| 153 | } | ||
| 154 | |||
| 155 | Id GetCbufElement(EmitContext& ctx, Id vector, const IR::Value& offset, u32 index_offset) { | ||
| 156 | if (offset.IsImmediate()) { | ||
| 157 | const u32 element{(offset.U32() / 4) % 4 + index_offset}; | ||
| 158 | return ctx.OpCompositeExtract(ctx.U32[1], vector, element); | ||
| 159 | } | ||
| 160 | const Id shift{ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), ctx.Const(2u))}; | ||
| 161 | Id element{ctx.OpBitwiseAnd(ctx.U32[1], shift, ctx.Const(3u))}; | ||
| 162 | if (index_offset > 0) { | ||
| 163 | element = ctx.OpIAdd(ctx.U32[1], element, ctx.Const(index_offset)); | ||
| 164 | } | ||
| 165 | return ctx.OpVectorExtractDynamic(ctx.U32[1], vector, element); | ||
| 166 | } | ||
| 167 | } // Anonymous namespace | ||
| 168 | |||
| 169 | void EmitGetRegister(EmitContext&) { | ||
| 170 | throw LogicError("Unreachable instruction"); | ||
| 171 | } | ||
| 172 | |||
| 173 | void EmitSetRegister(EmitContext&) { | ||
| 174 | throw LogicError("Unreachable instruction"); | ||
| 175 | } | ||
| 176 | |||
| 177 | void EmitGetPred(EmitContext&) { | ||
| 178 | throw LogicError("Unreachable instruction"); | ||
| 179 | } | ||
| 180 | |||
| 181 | void EmitSetPred(EmitContext&) { | ||
| 182 | throw LogicError("Unreachable instruction"); | ||
| 183 | } | ||
| 184 | |||
| 185 | void EmitSetGotoVariable(EmitContext&) { | ||
| 186 | throw LogicError("Unreachable instruction"); | ||
| 187 | } | ||
| 188 | |||
| 189 | void EmitGetGotoVariable(EmitContext&) { | ||
| 190 | throw LogicError("Unreachable instruction"); | ||
| 191 | } | ||
| 192 | |||
| 193 | void EmitSetIndirectBranchVariable(EmitContext&) { | ||
| 194 | throw LogicError("Unreachable instruction"); | ||
| 195 | } | ||
| 196 | |||
| 197 | void EmitGetIndirectBranchVariable(EmitContext&) { | ||
| 198 | throw LogicError("Unreachable instruction"); | ||
| 199 | } | ||
| 200 | |||
| 201 | Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 202 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) { | ||
| 203 | const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset)}; | ||
| 204 | return ctx.OpUConvert(ctx.U32[1], load); | ||
| 205 | } | ||
| 206 | Id element{}; | ||
| 207 | if (ctx.profile.support_descriptor_aliasing) { | ||
| 208 | element = GetCbufU32(ctx, binding, offset); | ||
| 209 | } else { | ||
| 210 | const Id vector{GetCbufU32x4(ctx, binding, offset)}; | ||
| 211 | element = GetCbufElement(ctx, vector, offset, 0u); | ||
| 212 | } | ||
| 213 | const Id bit_offset{ctx.BitOffset8(offset)}; | ||
| 214 | return ctx.OpBitFieldUExtract(ctx.U32[1], element, bit_offset, ctx.Const(8u)); | ||
| 215 | } | ||
| 216 | |||
| 217 | Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 218 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) { | ||
| 219 | const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset)}; | ||
| 220 | return ctx.OpSConvert(ctx.U32[1], load); | ||
| 221 | } | ||
| 222 | Id element{}; | ||
| 223 | if (ctx.profile.support_descriptor_aliasing) { | ||
| 224 | element = GetCbufU32(ctx, binding, offset); | ||
| 225 | } else { | ||
| 226 | const Id vector{GetCbufU32x4(ctx, binding, offset)}; | ||
| 227 | element = GetCbufElement(ctx, vector, offset, 0u); | ||
| 228 | } | ||
| 229 | const Id bit_offset{ctx.BitOffset8(offset)}; | ||
| 230 | return ctx.OpBitFieldSExtract(ctx.U32[1], element, bit_offset, ctx.Const(8u)); | ||
| 231 | } | ||
| 232 | |||
| 233 | Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 234 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) { | ||
| 235 | const Id load{ | ||
| 236 | GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset)}; | ||
| 237 | return ctx.OpUConvert(ctx.U32[1], load); | ||
| 238 | } | ||
| 239 | Id element{}; | ||
| 240 | if (ctx.profile.support_descriptor_aliasing) { | ||
| 241 | element = GetCbufU32(ctx, binding, offset); | ||
| 242 | } else { | ||
| 243 | const Id vector{GetCbufU32x4(ctx, binding, offset)}; | ||
| 244 | element = GetCbufElement(ctx, vector, offset, 0u); | ||
| 245 | } | ||
| 246 | const Id bit_offset{ctx.BitOffset16(offset)}; | ||
| 247 | return ctx.OpBitFieldUExtract(ctx.U32[1], element, bit_offset, ctx.Const(16u)); | ||
| 248 | } | ||
| 249 | |||
| 250 | Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 251 | if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) { | ||
| 252 | const Id load{ | ||
| 253 | GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset)}; | ||
| 254 | return ctx.OpSConvert(ctx.U32[1], load); | ||
| 255 | } | ||
| 256 | Id element{}; | ||
| 257 | if (ctx.profile.support_descriptor_aliasing) { | ||
| 258 | element = GetCbufU32(ctx, binding, offset); | ||
| 259 | } else { | ||
| 260 | const Id vector{GetCbufU32x4(ctx, binding, offset)}; | ||
| 261 | element = GetCbufElement(ctx, vector, offset, 0u); | ||
| 262 | } | ||
| 263 | const Id bit_offset{ctx.BitOffset16(offset)}; | ||
| 264 | return ctx.OpBitFieldSExtract(ctx.U32[1], element, bit_offset, ctx.Const(16u)); | ||
| 265 | } | ||
| 266 | |||
| 267 | Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 268 | if (ctx.profile.support_descriptor_aliasing) { | ||
| 269 | return GetCbufU32(ctx, binding, offset); | ||
| 270 | } else { | ||
| 271 | const Id vector{GetCbufU32x4(ctx, binding, offset)}; | ||
| 272 | return GetCbufElement(ctx, vector, offset, 0u); | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 277 | if (ctx.profile.support_descriptor_aliasing) { | ||
| 278 | return GetCbuf(ctx, ctx.F32[1], &UniformDefinitions::F32, sizeof(f32), binding, offset); | ||
| 279 | } else { | ||
| 280 | const Id vector{GetCbufU32x4(ctx, binding, offset)}; | ||
| 281 | return ctx.OpBitcast(ctx.F32[1], GetCbufElement(ctx, vector, offset, 0u)); | ||
| 282 | } | ||
| 283 | } | ||
| 284 | |||
| 285 | Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 286 | if (ctx.profile.support_descriptor_aliasing) { | ||
| 287 | return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding, | ||
| 288 | offset); | ||
| 289 | } else { | ||
| 290 | const Id vector{GetCbufU32x4(ctx, binding, offset)}; | ||
| 291 | return ctx.OpCompositeConstruct(ctx.U32[2], GetCbufElement(ctx, vector, offset, 0u), | ||
| 292 | GetCbufElement(ctx, vector, offset, 1u)); | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { | ||
| 297 | const u32 element{static_cast<u32>(attr) % 4}; | ||
| 298 | if (IR::IsGeneric(attr)) { | ||
| 299 | const u32 index{IR::GenericAttributeIndex(attr)}; | ||
| 300 | const std::optional<AttrInfo> type{AttrTypes(ctx, index)}; | ||
| 301 | if (!type) { | ||
| 302 | // Attribute is disabled | ||
| 303 | return ctx.Const(element == 3 ? 1.0f : 0.0f); | ||
| 304 | } | ||
| 305 | if (!ctx.runtime_info.previous_stage_stores.Generic(index, element)) { | ||
| 306 | // Varying component is not written | ||
| 307 | return ctx.Const(type && element == 3 ? 1.0f : 0.0f); | ||
| 308 | } | ||
| 309 | const Id generic_id{ctx.input_generics.at(index)}; | ||
| 310 | const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))}; | ||
| 311 | const Id value{ctx.OpLoad(type->id, pointer)}; | ||
| 312 | return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value; | ||
| 313 | } | ||
| 314 | switch (attr) { | ||
| 315 | case IR::Attribute::PrimitiveId: | ||
| 316 | return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id)); | ||
| 317 | case IR::Attribute::PositionX: | ||
| 318 | case IR::Attribute::PositionY: | ||
| 319 | case IR::Attribute::PositionZ: | ||
| 320 | case IR::Attribute::PositionW: | ||
| 321 | return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, | ||
| 322 | ctx.Const(element))); | ||
| 323 | case IR::Attribute::InstanceId: | ||
| 324 | if (ctx.profile.support_vertex_instance_id) { | ||
| 325 | return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id)); | ||
| 326 | } else { | ||
| 327 | const Id index{ctx.OpLoad(ctx.U32[1], ctx.instance_index)}; | ||
| 328 | const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_instance)}; | ||
| 329 | return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base)); | ||
| 330 | } | ||
| 331 | case IR::Attribute::VertexId: | ||
| 332 | if (ctx.profile.support_vertex_instance_id) { | ||
| 333 | return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.vertex_id)); | ||
| 334 | } else { | ||
| 335 | const Id index{ctx.OpLoad(ctx.U32[1], ctx.vertex_index)}; | ||
| 336 | const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)}; | ||
| 337 | return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base)); | ||
| 338 | } | ||
| 339 | case IR::Attribute::FrontFace: | ||
| 340 | return ctx.OpSelect(ctx.U32[1], ctx.OpLoad(ctx.U1, ctx.front_face), | ||
| 341 | ctx.Const(std::numeric_limits<u32>::max()), ctx.u32_zero_value); | ||
| 342 | case IR::Attribute::PointSpriteS: | ||
| 343 | return ctx.OpLoad(ctx.F32[1], | ||
| 344 | ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.u32_zero_value)); | ||
| 345 | case IR::Attribute::PointSpriteT: | ||
| 346 | return ctx.OpLoad(ctx.F32[1], | ||
| 347 | ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.Const(1U))); | ||
| 348 | case IR::Attribute::TessellationEvaluationPointU: | ||
| 349 | return ctx.OpLoad(ctx.F32[1], | ||
| 350 | ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.u32_zero_value)); | ||
| 351 | case IR::Attribute::TessellationEvaluationPointV: | ||
| 352 | return ctx.OpLoad(ctx.F32[1], | ||
| 353 | ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.Const(1U))); | ||
| 354 | |||
| 355 | default: | ||
| 356 | throw NotImplementedException("Read attribute {}", attr); | ||
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 360 | void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, [[maybe_unused]] Id vertex) { | ||
| 361 | const std::optional<OutAttr> output{OutputAttrPointer(ctx, attr)}; | ||
| 362 | if (!output) { | ||
| 363 | return; | ||
| 364 | } | ||
| 365 | if (Sirit::ValidId(output->type)) { | ||
| 366 | value = ctx.OpBitcast(output->type, value); | ||
| 367 | } | ||
| 368 | ctx.OpStore(output->pointer, value); | ||
| 369 | } | ||
| 370 | |||
| 371 | Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex) { | ||
| 372 | switch (ctx.stage) { | ||
| 373 | case Stage::TessellationControl: | ||
| 374 | case Stage::TessellationEval: | ||
| 375 | case Stage::Geometry: | ||
| 376 | return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset, vertex); | ||
| 377 | default: | ||
| 378 | return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset); | ||
| 379 | } | ||
| 380 | } | ||
| 381 | |||
| 382 | void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, [[maybe_unused]] Id vertex) { | ||
| 383 | ctx.OpFunctionCall(ctx.void_id, ctx.indexed_store_func, offset, value); | ||
| 384 | } | ||
| 385 | |||
| 386 | Id EmitGetPatch(EmitContext& ctx, IR::Patch patch) { | ||
| 387 | if (!IR::IsGeneric(patch)) { | ||
| 388 | throw NotImplementedException("Non-generic patch load"); | ||
| 389 | } | ||
| 390 | const u32 index{IR::GenericPatchIndex(patch)}; | ||
| 391 | const Id element{ctx.Const(IR::GenericPatchElement(patch))}; | ||
| 392 | const Id type{ctx.stage == Stage::TessellationControl ? ctx.output_f32 : ctx.input_f32}; | ||
| 393 | const Id pointer{ctx.OpAccessChain(type, ctx.patches.at(index), element)}; | ||
| 394 | return ctx.OpLoad(ctx.F32[1], pointer); | ||
| 395 | } | ||
| 396 | |||
| 397 | void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value) { | ||
| 398 | const Id pointer{[&] { | ||
| 399 | if (IR::IsGeneric(patch)) { | ||
| 400 | const u32 index{IR::GenericPatchIndex(patch)}; | ||
| 401 | const Id element{ctx.Const(IR::GenericPatchElement(patch))}; | ||
| 402 | return ctx.OpAccessChain(ctx.output_f32, ctx.patches.at(index), element); | ||
| 403 | } | ||
| 404 | switch (patch) { | ||
| 405 | case IR::Patch::TessellationLodLeft: | ||
| 406 | case IR::Patch::TessellationLodRight: | ||
| 407 | case IR::Patch::TessellationLodTop: | ||
| 408 | case IR::Patch::TessellationLodBottom: { | ||
| 409 | const u32 index{static_cast<u32>(patch) - u32(IR::Patch::TessellationLodLeft)}; | ||
| 410 | const Id index_id{ctx.Const(index)}; | ||
| 411 | return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_outer, index_id); | ||
| 412 | } | ||
| 413 | case IR::Patch::TessellationLodInteriorU: | ||
| 414 | return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_inner, | ||
| 415 | ctx.u32_zero_value); | ||
| 416 | case IR::Patch::TessellationLodInteriorV: | ||
| 417 | return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_inner, ctx.Const(1u)); | ||
| 418 | default: | ||
| 419 | throw NotImplementedException("Patch {}", patch); | ||
| 420 | } | ||
| 421 | }()}; | ||
| 422 | ctx.OpStore(pointer, value); | ||
| 423 | } | ||
| 424 | |||
| 425 | void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value) { | ||
| 426 | const Id component_id{ctx.Const(component)}; | ||
| 427 | const Id pointer{ctx.OpAccessChain(ctx.output_f32, ctx.frag_color.at(index), component_id)}; | ||
| 428 | ctx.OpStore(pointer, value); | ||
| 429 | } | ||
| 430 | |||
| 431 | void EmitSetSampleMask(EmitContext& ctx, Id value) { | ||
| 432 | ctx.OpStore(ctx.sample_mask, value); | ||
| 433 | } | ||
| 434 | |||
| 435 | void EmitSetFragDepth(EmitContext& ctx, Id value) { | ||
| 436 | ctx.OpStore(ctx.frag_depth, value); | ||
| 437 | } | ||
| 438 | |||
| 439 | void EmitGetZFlag(EmitContext&) { | ||
| 440 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 441 | } | ||
| 442 | |||
| 443 | void EmitGetSFlag(EmitContext&) { | ||
| 444 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 445 | } | ||
| 446 | |||
| 447 | void EmitGetCFlag(EmitContext&) { | ||
| 448 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 449 | } | ||
| 450 | |||
| 451 | void EmitGetOFlag(EmitContext&) { | ||
| 452 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 453 | } | ||
| 454 | |||
| 455 | void EmitSetZFlag(EmitContext&) { | ||
| 456 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 457 | } | ||
| 458 | |||
| 459 | void EmitSetSFlag(EmitContext&) { | ||
| 460 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 461 | } | ||
| 462 | |||
| 463 | void EmitSetCFlag(EmitContext&) { | ||
| 464 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 465 | } | ||
| 466 | |||
| 467 | void EmitSetOFlag(EmitContext&) { | ||
| 468 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 469 | } | ||
| 470 | |||
| 471 | Id EmitWorkgroupId(EmitContext& ctx) { | ||
| 472 | return ctx.OpLoad(ctx.U32[3], ctx.workgroup_id); | ||
| 473 | } | ||
| 474 | |||
| 475 | Id EmitLocalInvocationId(EmitContext& ctx) { | ||
| 476 | return ctx.OpLoad(ctx.U32[3], ctx.local_invocation_id); | ||
| 477 | } | ||
| 478 | |||
| 479 | Id EmitInvocationId(EmitContext& ctx) { | ||
| 480 | return ctx.OpLoad(ctx.U32[1], ctx.invocation_id); | ||
| 481 | } | ||
| 482 | |||
| 483 | Id EmitSampleId(EmitContext& ctx) { | ||
| 484 | return ctx.OpLoad(ctx.U32[1], ctx.sample_id); | ||
| 485 | } | ||
| 486 | |||
| 487 | Id EmitIsHelperInvocation(EmitContext& ctx) { | ||
| 488 | return ctx.OpLoad(ctx.U1, ctx.is_helper_invocation); | ||
| 489 | } | ||
| 490 | |||
| 491 | Id EmitYDirection(EmitContext& ctx) { | ||
| 492 | return ctx.Const(ctx.runtime_info.y_negate ? -1.0f : 1.0f); | ||
| 493 | } | ||
| 494 | |||
| 495 | Id EmitLoadLocal(EmitContext& ctx, Id word_offset) { | ||
| 496 | const Id pointer{ctx.OpAccessChain(ctx.private_u32, ctx.local_memory, word_offset)}; | ||
| 497 | return ctx.OpLoad(ctx.U32[1], pointer); | ||
| 498 | } | ||
| 499 | |||
| 500 | void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value) { | ||
| 501 | const Id pointer{ctx.OpAccessChain(ctx.private_u32, ctx.local_memory, word_offset)}; | ||
| 502 | ctx.OpStore(pointer, value); | ||
| 503 | } | ||
| 504 | |||
| 505 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp new file mode 100644 index 000000000..d33486f28 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 7 | |||
| 8 | namespace Shader::Backend::SPIRV { | ||
| 9 | |||
| 10 | void EmitJoin(EmitContext&) { | ||
| 11 | throw NotImplementedException("Join shouldn't be emitted"); | ||
| 12 | } | ||
| 13 | |||
| 14 | void EmitDemoteToHelperInvocation(EmitContext& ctx) { | ||
| 15 | if (ctx.profile.support_demote_to_helper_invocation) { | ||
| 16 | ctx.OpDemoteToHelperInvocationEXT(); | ||
| 17 | } else { | ||
| 18 | const Id kill_label{ctx.OpLabel()}; | ||
| 19 | const Id impossible_label{ctx.OpLabel()}; | ||
| 20 | ctx.OpSelectionMerge(impossible_label, spv::SelectionControlMask::MaskNone); | ||
| 21 | ctx.OpBranchConditional(ctx.true_value, kill_label, impossible_label); | ||
| 22 | ctx.AddLabel(kill_label); | ||
| 23 | ctx.OpKill(); | ||
| 24 | ctx.AddLabel(impossible_label); | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp new file mode 100644 index 000000000..fd42b7a16 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_convert.cpp | |||
| @@ -0,0 +1,269 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 7 | |||
| 8 | namespace Shader::Backend::SPIRV { | ||
| 9 | namespace { | ||
| 10 | Id ExtractU16(EmitContext& ctx, Id value) { | ||
| 11 | if (ctx.profile.support_int16) { | ||
| 12 | return ctx.OpUConvert(ctx.U16, value); | ||
| 13 | } else { | ||
| 14 | return ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(16u)); | ||
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 18 | Id ExtractS16(EmitContext& ctx, Id value) { | ||
| 19 | if (ctx.profile.support_int16) { | ||
| 20 | return ctx.OpSConvert(ctx.S16, value); | ||
| 21 | } else { | ||
| 22 | return ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(16u)); | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | Id ExtractU8(EmitContext& ctx, Id value) { | ||
| 27 | if (ctx.profile.support_int8) { | ||
| 28 | return ctx.OpUConvert(ctx.U8, value); | ||
| 29 | } else { | ||
| 30 | return ctx.OpBitFieldUExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(8u)); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | Id ExtractS8(EmitContext& ctx, Id value) { | ||
| 35 | if (ctx.profile.support_int8) { | ||
| 36 | return ctx.OpSConvert(ctx.S8, value); | ||
| 37 | } else { | ||
| 38 | return ctx.OpBitFieldSExtract(ctx.U32[1], value, ctx.u32_zero_value, ctx.Const(8u)); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } // Anonymous namespace | ||
| 42 | |||
| 43 | Id EmitConvertS16F16(EmitContext& ctx, Id value) { | ||
| 44 | if (ctx.profile.support_int16) { | ||
| 45 | return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value)); | ||
| 46 | } else { | ||
| 47 | return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value)); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | Id EmitConvertS16F32(EmitContext& ctx, Id value) { | ||
| 52 | if (ctx.profile.support_int16) { | ||
| 53 | return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value)); | ||
| 54 | } else { | ||
| 55 | return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value)); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | Id EmitConvertS16F64(EmitContext& ctx, Id value) { | ||
| 60 | if (ctx.profile.support_int16) { | ||
| 61 | return ctx.OpSConvert(ctx.U32[1], ctx.OpConvertFToS(ctx.U16, value)); | ||
| 62 | } else { | ||
| 63 | return ExtractS16(ctx, ctx.OpConvertFToS(ctx.U32[1], value)); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | Id EmitConvertS32F16(EmitContext& ctx, Id value) { | ||
| 68 | return ctx.OpConvertFToS(ctx.U32[1], value); | ||
| 69 | } | ||
| 70 | |||
| 71 | Id EmitConvertS32F32(EmitContext& ctx, Id value) { | ||
| 72 | if (ctx.profile.has_broken_signed_operations) { | ||
| 73 | return ctx.OpBitcast(ctx.U32[1], ctx.OpConvertFToS(ctx.S32[1], value)); | ||
| 74 | } else { | ||
| 75 | return ctx.OpConvertFToS(ctx.U32[1], value); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | Id EmitConvertS32F64(EmitContext& ctx, Id value) { | ||
| 80 | return ctx.OpConvertFToS(ctx.U32[1], value); | ||
| 81 | } | ||
| 82 | |||
| 83 | Id EmitConvertS64F16(EmitContext& ctx, Id value) { | ||
| 84 | return ctx.OpConvertFToS(ctx.U64, value); | ||
| 85 | } | ||
| 86 | |||
| 87 | Id EmitConvertS64F32(EmitContext& ctx, Id value) { | ||
| 88 | return ctx.OpConvertFToS(ctx.U64, value); | ||
| 89 | } | ||
| 90 | |||
| 91 | Id EmitConvertS64F64(EmitContext& ctx, Id value) { | ||
| 92 | return ctx.OpConvertFToS(ctx.U64, value); | ||
| 93 | } | ||
| 94 | |||
| 95 | Id EmitConvertU16F16(EmitContext& ctx, Id value) { | ||
| 96 | if (ctx.profile.support_int16) { | ||
| 97 | return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value)); | ||
| 98 | } else { | ||
| 99 | return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value)); | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | Id EmitConvertU16F32(EmitContext& ctx, Id value) { | ||
| 104 | if (ctx.profile.support_int16) { | ||
| 105 | return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value)); | ||
| 106 | } else { | ||
| 107 | return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value)); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | Id EmitConvertU16F64(EmitContext& ctx, Id value) { | ||
| 112 | if (ctx.profile.support_int16) { | ||
| 113 | return ctx.OpUConvert(ctx.U32[1], ctx.OpConvertFToU(ctx.U16, value)); | ||
| 114 | } else { | ||
| 115 | return ExtractU16(ctx, ctx.OpConvertFToU(ctx.U32[1], value)); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | Id EmitConvertU32F16(EmitContext& ctx, Id value) { | ||
| 120 | return ctx.OpConvertFToU(ctx.U32[1], value); | ||
| 121 | } | ||
| 122 | |||
| 123 | Id EmitConvertU32F32(EmitContext& ctx, Id value) { | ||
| 124 | return ctx.OpConvertFToU(ctx.U32[1], value); | ||
| 125 | } | ||
| 126 | |||
| 127 | Id EmitConvertU32F64(EmitContext& ctx, Id value) { | ||
| 128 | return ctx.OpConvertFToU(ctx.U32[1], value); | ||
| 129 | } | ||
| 130 | |||
| 131 | Id EmitConvertU64F16(EmitContext& ctx, Id value) { | ||
| 132 | return ctx.OpConvertFToU(ctx.U64, value); | ||
| 133 | } | ||
| 134 | |||
| 135 | Id EmitConvertU64F32(EmitContext& ctx, Id value) { | ||
| 136 | return ctx.OpConvertFToU(ctx.U64, value); | ||
| 137 | } | ||
| 138 | |||
| 139 | Id EmitConvertU64F64(EmitContext& ctx, Id value) { | ||
| 140 | return ctx.OpConvertFToU(ctx.U64, value); | ||
| 141 | } | ||
| 142 | |||
| 143 | Id EmitConvertU64U32(EmitContext& ctx, Id value) { | ||
| 144 | return ctx.OpUConvert(ctx.U64, value); | ||
| 145 | } | ||
| 146 | |||
| 147 | Id EmitConvertU32U64(EmitContext& ctx, Id value) { | ||
| 148 | return ctx.OpUConvert(ctx.U32[1], value); | ||
| 149 | } | ||
| 150 | |||
| 151 | Id EmitConvertF16F32(EmitContext& ctx, Id value) { | ||
| 152 | return ctx.OpFConvert(ctx.F16[1], value); | ||
| 153 | } | ||
| 154 | |||
| 155 | Id EmitConvertF32F16(EmitContext& ctx, Id value) { | ||
| 156 | return ctx.OpFConvert(ctx.F32[1], value); | ||
| 157 | } | ||
| 158 | |||
| 159 | Id EmitConvertF32F64(EmitContext& ctx, Id value) { | ||
| 160 | return ctx.OpFConvert(ctx.F32[1], value); | ||
| 161 | } | ||
| 162 | |||
| 163 | Id EmitConvertF64F32(EmitContext& ctx, Id value) { | ||
| 164 | return ctx.OpFConvert(ctx.F64[1], value); | ||
| 165 | } | ||
| 166 | |||
| 167 | Id EmitConvertF16S8(EmitContext& ctx, Id value) { | ||
| 168 | return ctx.OpConvertSToF(ctx.F16[1], ExtractS8(ctx, value)); | ||
| 169 | } | ||
| 170 | |||
| 171 | Id EmitConvertF16S16(EmitContext& ctx, Id value) { | ||
| 172 | return ctx.OpConvertSToF(ctx.F16[1], ExtractS16(ctx, value)); | ||
| 173 | } | ||
| 174 | |||
| 175 | Id EmitConvertF16S32(EmitContext& ctx, Id value) { | ||
| 176 | return ctx.OpConvertSToF(ctx.F16[1], value); | ||
| 177 | } | ||
| 178 | |||
| 179 | Id EmitConvertF16S64(EmitContext& ctx, Id value) { | ||
| 180 | return ctx.OpConvertSToF(ctx.F16[1], value); | ||
| 181 | } | ||
| 182 | |||
| 183 | Id EmitConvertF16U8(EmitContext& ctx, Id value) { | ||
| 184 | return ctx.OpConvertUToF(ctx.F16[1], ExtractU8(ctx, value)); | ||
| 185 | } | ||
| 186 | |||
| 187 | Id EmitConvertF16U16(EmitContext& ctx, Id value) { | ||
| 188 | return ctx.OpConvertUToF(ctx.F16[1], ExtractU16(ctx, value)); | ||
| 189 | } | ||
| 190 | |||
| 191 | Id EmitConvertF16U32(EmitContext& ctx, Id value) { | ||
| 192 | return ctx.OpConvertUToF(ctx.F16[1], value); | ||
| 193 | } | ||
| 194 | |||
| 195 | Id EmitConvertF16U64(EmitContext& ctx, Id value) { | ||
| 196 | return ctx.OpConvertUToF(ctx.F16[1], value); | ||
| 197 | } | ||
| 198 | |||
| 199 | Id EmitConvertF32S8(EmitContext& ctx, Id value) { | ||
| 200 | return ctx.OpConvertSToF(ctx.F32[1], ExtractS8(ctx, value)); | ||
| 201 | } | ||
| 202 | |||
| 203 | Id EmitConvertF32S16(EmitContext& ctx, Id value) { | ||
| 204 | return ctx.OpConvertSToF(ctx.F32[1], ExtractS16(ctx, value)); | ||
| 205 | } | ||
| 206 | |||
| 207 | Id EmitConvertF32S32(EmitContext& ctx, Id value) { | ||
| 208 | if (ctx.profile.has_broken_signed_operations) { | ||
| 209 | value = ctx.OpBitcast(ctx.S32[1], value); | ||
| 210 | } | ||
| 211 | return ctx.OpConvertSToF(ctx.F32[1], value); | ||
| 212 | } | ||
| 213 | |||
| 214 | Id EmitConvertF32S64(EmitContext& ctx, Id value) { | ||
| 215 | return ctx.OpConvertSToF(ctx.F32[1], value); | ||
| 216 | } | ||
| 217 | |||
| 218 | Id EmitConvertF32U8(EmitContext& ctx, Id value) { | ||
| 219 | return ctx.OpConvertUToF(ctx.F32[1], ExtractU8(ctx, value)); | ||
| 220 | } | ||
| 221 | |||
| 222 | Id EmitConvertF32U16(EmitContext& ctx, Id value) { | ||
| 223 | return ctx.OpConvertUToF(ctx.F32[1], ExtractU16(ctx, value)); | ||
| 224 | } | ||
| 225 | |||
| 226 | Id EmitConvertF32U32(EmitContext& ctx, Id value) { | ||
| 227 | return ctx.OpConvertUToF(ctx.F32[1], value); | ||
| 228 | } | ||
| 229 | |||
| 230 | Id EmitConvertF32U64(EmitContext& ctx, Id value) { | ||
| 231 | return ctx.OpConvertUToF(ctx.F32[1], value); | ||
| 232 | } | ||
| 233 | |||
| 234 | Id EmitConvertF64S8(EmitContext& ctx, Id value) { | ||
| 235 | return ctx.OpConvertSToF(ctx.F64[1], ExtractS8(ctx, value)); | ||
| 236 | } | ||
| 237 | |||
| 238 | Id EmitConvertF64S16(EmitContext& ctx, Id value) { | ||
| 239 | return ctx.OpConvertSToF(ctx.F64[1], ExtractS16(ctx, value)); | ||
| 240 | } | ||
| 241 | |||
| 242 | Id EmitConvertF64S32(EmitContext& ctx, Id value) { | ||
| 243 | if (ctx.profile.has_broken_signed_operations) { | ||
| 244 | value = ctx.OpBitcast(ctx.S32[1], value); | ||
| 245 | } | ||
| 246 | return ctx.OpConvertSToF(ctx.F64[1], value); | ||
| 247 | } | ||
| 248 | |||
| 249 | Id EmitConvertF64S64(EmitContext& ctx, Id value) { | ||
| 250 | return ctx.OpConvertSToF(ctx.F64[1], value); | ||
| 251 | } | ||
| 252 | |||
| 253 | Id EmitConvertF64U8(EmitContext& ctx, Id value) { | ||
| 254 | return ctx.OpConvertUToF(ctx.F64[1], ExtractU8(ctx, value)); | ||
| 255 | } | ||
| 256 | |||
| 257 | Id EmitConvertF64U16(EmitContext& ctx, Id value) { | ||
| 258 | return ctx.OpConvertUToF(ctx.F64[1], ExtractU16(ctx, value)); | ||
| 259 | } | ||
| 260 | |||
| 261 | Id EmitConvertF64U32(EmitContext& ctx, Id value) { | ||
| 262 | return ctx.OpConvertUToF(ctx.F64[1], value); | ||
| 263 | } | ||
| 264 | |||
| 265 | Id EmitConvertF64U64(EmitContext& ctx, Id value) { | ||
| 266 | return ctx.OpConvertUToF(ctx.F64[1], value); | ||
| 267 | } | ||
| 268 | |||
| 269 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp new file mode 100644 index 000000000..61cf25f9c --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp | |||
| @@ -0,0 +1,396 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 7 | #include "shader_recompiler/frontend/ir/modifiers.h" | ||
| 8 | |||
| 9 | namespace Shader::Backend::SPIRV { | ||
| 10 | namespace { | ||
| 11 | Id Decorate(EmitContext& ctx, IR::Inst* inst, Id op) { | ||
| 12 | const auto flags{inst->Flags<IR::FpControl>()}; | ||
| 13 | if (flags.no_contraction) { | ||
| 14 | ctx.Decorate(op, spv::Decoration::NoContraction); | ||
| 15 | } | ||
| 16 | return op; | ||
| 17 | } | ||
| 18 | |||
| 19 | Id Clamp(EmitContext& ctx, Id type, Id value, Id zero, Id one) { | ||
| 20 | if (ctx.profile.has_broken_spirv_clamp) { | ||
| 21 | return ctx.OpFMin(type, ctx.OpFMax(type, value, zero), one); | ||
| 22 | } else { | ||
| 23 | return ctx.OpFClamp(type, value, zero, one); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | Id FPOrdNotEqual(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 28 | if (ctx.profile.ignore_nan_fp_comparisons) { | ||
| 29 | const Id comp{ctx.OpFOrdEqual(ctx.U1, lhs, rhs)}; | ||
| 30 | const Id lhs_not_nan{ctx.OpLogicalNot(ctx.U1, ctx.OpIsNan(ctx.U1, lhs))}; | ||
| 31 | const Id rhs_not_nan{ctx.OpLogicalNot(ctx.U1, ctx.OpIsNan(ctx.U1, rhs))}; | ||
| 32 | return ctx.OpLogicalAnd(ctx.U1, ctx.OpLogicalAnd(ctx.U1, comp, lhs_not_nan), rhs_not_nan); | ||
| 33 | } else { | ||
| 34 | return ctx.OpFOrdNotEqual(ctx.U1, lhs, rhs); | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | Id FPUnordCompare(Id (EmitContext::*comp_func)(Id, Id, Id), EmitContext& ctx, Id lhs, Id rhs) { | ||
| 39 | if (ctx.profile.ignore_nan_fp_comparisons) { | ||
| 40 | const Id lhs_nan{ctx.OpIsNan(ctx.U1, lhs)}; | ||
| 41 | const Id rhs_nan{ctx.OpIsNan(ctx.U1, rhs)}; | ||
| 42 | const Id comp{(ctx.*comp_func)(ctx.U1, lhs, rhs)}; | ||
| 43 | return ctx.OpLogicalOr(ctx.U1, ctx.OpLogicalOr(ctx.U1, comp, lhs_nan), rhs_nan); | ||
| 44 | } else { | ||
| 45 | return (ctx.*comp_func)(ctx.U1, lhs, rhs); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | } // Anonymous namespace | ||
| 49 | |||
| 50 | Id EmitFPAbs16(EmitContext& ctx, Id value) { | ||
| 51 | return ctx.OpFAbs(ctx.F16[1], value); | ||
| 52 | } | ||
| 53 | |||
| 54 | Id EmitFPAbs32(EmitContext& ctx, Id value) { | ||
| 55 | return ctx.OpFAbs(ctx.F32[1], value); | ||
| 56 | } | ||
| 57 | |||
| 58 | Id EmitFPAbs64(EmitContext& ctx, Id value) { | ||
| 59 | return ctx.OpFAbs(ctx.F64[1], value); | ||
| 60 | } | ||
| 61 | |||
| 62 | Id EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | ||
| 63 | return Decorate(ctx, inst, ctx.OpFAdd(ctx.F16[1], a, b)); | ||
| 64 | } | ||
| 65 | |||
| 66 | Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | ||
| 67 | return Decorate(ctx, inst, ctx.OpFAdd(ctx.F32[1], a, b)); | ||
| 68 | } | ||
| 69 | |||
| 70 | Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | ||
| 71 | return Decorate(ctx, inst, ctx.OpFAdd(ctx.F64[1], a, b)); | ||
| 72 | } | ||
| 73 | |||
| 74 | Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { | ||
| 75 | return Decorate(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c)); | ||
| 76 | } | ||
| 77 | |||
| 78 | Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { | ||
| 79 | return Decorate(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c)); | ||
| 80 | } | ||
| 81 | |||
| 82 | Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { | ||
| 83 | return Decorate(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c)); | ||
| 84 | } | ||
| 85 | |||
| 86 | Id EmitFPMax32(EmitContext& ctx, Id a, Id b) { | ||
| 87 | return ctx.OpFMax(ctx.F32[1], a, b); | ||
| 88 | } | ||
| 89 | |||
| 90 | Id EmitFPMax64(EmitContext& ctx, Id a, Id b) { | ||
| 91 | return ctx.OpFMax(ctx.F64[1], a, b); | ||
| 92 | } | ||
| 93 | |||
| 94 | Id EmitFPMin32(EmitContext& ctx, Id a, Id b) { | ||
| 95 | return ctx.OpFMin(ctx.F32[1], a, b); | ||
| 96 | } | ||
| 97 | |||
| 98 | Id EmitFPMin64(EmitContext& ctx, Id a, Id b) { | ||
| 99 | return ctx.OpFMin(ctx.F64[1], a, b); | ||
| 100 | } | ||
| 101 | |||
| 102 | Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | ||
| 103 | return Decorate(ctx, inst, ctx.OpFMul(ctx.F16[1], a, b)); | ||
| 104 | } | ||
| 105 | |||
| 106 | Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | ||
| 107 | return Decorate(ctx, inst, ctx.OpFMul(ctx.F32[1], a, b)); | ||
| 108 | } | ||
| 109 | |||
| 110 | Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | ||
| 111 | return Decorate(ctx, inst, ctx.OpFMul(ctx.F64[1], a, b)); | ||
| 112 | } | ||
| 113 | |||
| 114 | Id EmitFPNeg16(EmitContext& ctx, Id value) { | ||
| 115 | return ctx.OpFNegate(ctx.F16[1], value); | ||
| 116 | } | ||
| 117 | |||
| 118 | Id EmitFPNeg32(EmitContext& ctx, Id value) { | ||
| 119 | return ctx.OpFNegate(ctx.F32[1], value); | ||
| 120 | } | ||
| 121 | |||
| 122 | Id EmitFPNeg64(EmitContext& ctx, Id value) { | ||
| 123 | return ctx.OpFNegate(ctx.F64[1], value); | ||
| 124 | } | ||
| 125 | |||
| 126 | Id EmitFPSin(EmitContext& ctx, Id value) { | ||
| 127 | return ctx.OpSin(ctx.F32[1], value); | ||
| 128 | } | ||
| 129 | |||
| 130 | Id EmitFPCos(EmitContext& ctx, Id value) { | ||
| 131 | return ctx.OpCos(ctx.F32[1], value); | ||
| 132 | } | ||
| 133 | |||
| 134 | Id EmitFPExp2(EmitContext& ctx, Id value) { | ||
| 135 | return ctx.OpExp2(ctx.F32[1], value); | ||
| 136 | } | ||
| 137 | |||
| 138 | Id EmitFPLog2(EmitContext& ctx, Id value) { | ||
| 139 | return ctx.OpLog2(ctx.F32[1], value); | ||
| 140 | } | ||
| 141 | |||
| 142 | Id EmitFPRecip32(EmitContext& ctx, Id value) { | ||
| 143 | return ctx.OpFDiv(ctx.F32[1], ctx.Const(1.0f), value); | ||
| 144 | } | ||
| 145 | |||
| 146 | Id EmitFPRecip64(EmitContext& ctx, Id value) { | ||
| 147 | return ctx.OpFDiv(ctx.F64[1], ctx.Constant(ctx.F64[1], 1.0f), value); | ||
| 148 | } | ||
| 149 | |||
| 150 | Id EmitFPRecipSqrt32(EmitContext& ctx, Id value) { | ||
| 151 | return ctx.OpInverseSqrt(ctx.F32[1], value); | ||
| 152 | } | ||
| 153 | |||
| 154 | Id EmitFPRecipSqrt64(EmitContext& ctx, Id value) { | ||
| 155 | return ctx.OpInverseSqrt(ctx.F64[1], value); | ||
| 156 | } | ||
| 157 | |||
| 158 | Id EmitFPSqrt(EmitContext& ctx, Id value) { | ||
| 159 | return ctx.OpSqrt(ctx.F32[1], value); | ||
| 160 | } | ||
| 161 | |||
| 162 | Id EmitFPSaturate16(EmitContext& ctx, Id value) { | ||
| 163 | const Id zero{ctx.Constant(ctx.F16[1], u16{0})}; | ||
| 164 | const Id one{ctx.Constant(ctx.F16[1], u16{0x3c00})}; | ||
| 165 | return Clamp(ctx, ctx.F16[1], value, zero, one); | ||
| 166 | } | ||
| 167 | |||
| 168 | Id EmitFPSaturate32(EmitContext& ctx, Id value) { | ||
| 169 | const Id zero{ctx.Const(f32{0.0})}; | ||
| 170 | const Id one{ctx.Const(f32{1.0})}; | ||
| 171 | return Clamp(ctx, ctx.F32[1], value, zero, one); | ||
| 172 | } | ||
| 173 | |||
| 174 | Id EmitFPSaturate64(EmitContext& ctx, Id value) { | ||
| 175 | const Id zero{ctx.Constant(ctx.F64[1], f64{0.0})}; | ||
| 176 | const Id one{ctx.Constant(ctx.F64[1], f64{1.0})}; | ||
| 177 | return Clamp(ctx, ctx.F64[1], value, zero, one); | ||
| 178 | } | ||
| 179 | |||
| 180 | Id EmitFPClamp16(EmitContext& ctx, Id value, Id min_value, Id max_value) { | ||
| 181 | return Clamp(ctx, ctx.F16[1], value, min_value, max_value); | ||
| 182 | } | ||
| 183 | |||
| 184 | Id EmitFPClamp32(EmitContext& ctx, Id value, Id min_value, Id max_value) { | ||
| 185 | return Clamp(ctx, ctx.F32[1], value, min_value, max_value); | ||
| 186 | } | ||
| 187 | |||
| 188 | Id EmitFPClamp64(EmitContext& ctx, Id value, Id min_value, Id max_value) { | ||
| 189 | return Clamp(ctx, ctx.F64[1], value, min_value, max_value); | ||
| 190 | } | ||
| 191 | |||
| 192 | Id EmitFPRoundEven16(EmitContext& ctx, Id value) { | ||
| 193 | return ctx.OpRoundEven(ctx.F16[1], value); | ||
| 194 | } | ||
| 195 | |||
| 196 | Id EmitFPRoundEven32(EmitContext& ctx, Id value) { | ||
| 197 | return ctx.OpRoundEven(ctx.F32[1], value); | ||
| 198 | } | ||
| 199 | |||
| 200 | Id EmitFPRoundEven64(EmitContext& ctx, Id value) { | ||
| 201 | return ctx.OpRoundEven(ctx.F64[1], value); | ||
| 202 | } | ||
| 203 | |||
| 204 | Id EmitFPFloor16(EmitContext& ctx, Id value) { | ||
| 205 | return ctx.OpFloor(ctx.F16[1], value); | ||
| 206 | } | ||
| 207 | |||
| 208 | Id EmitFPFloor32(EmitContext& ctx, Id value) { | ||
| 209 | return ctx.OpFloor(ctx.F32[1], value); | ||
| 210 | } | ||
| 211 | |||
| 212 | Id EmitFPFloor64(EmitContext& ctx, Id value) { | ||
| 213 | return ctx.OpFloor(ctx.F64[1], value); | ||
| 214 | } | ||
| 215 | |||
| 216 | Id EmitFPCeil16(EmitContext& ctx, Id value) { | ||
| 217 | return ctx.OpCeil(ctx.F16[1], value); | ||
| 218 | } | ||
| 219 | |||
| 220 | Id EmitFPCeil32(EmitContext& ctx, Id value) { | ||
| 221 | return ctx.OpCeil(ctx.F32[1], value); | ||
| 222 | } | ||
| 223 | |||
| 224 | Id EmitFPCeil64(EmitContext& ctx, Id value) { | ||
| 225 | return ctx.OpCeil(ctx.F64[1], value); | ||
| 226 | } | ||
| 227 | |||
| 228 | Id EmitFPTrunc16(EmitContext& ctx, Id value) { | ||
| 229 | return ctx.OpTrunc(ctx.F16[1], value); | ||
| 230 | } | ||
| 231 | |||
| 232 | Id EmitFPTrunc32(EmitContext& ctx, Id value) { | ||
| 233 | return ctx.OpTrunc(ctx.F32[1], value); | ||
| 234 | } | ||
| 235 | |||
| 236 | Id EmitFPTrunc64(EmitContext& ctx, Id value) { | ||
| 237 | return ctx.OpTrunc(ctx.F64[1], value); | ||
| 238 | } | ||
| 239 | |||
| 240 | Id EmitFPOrdEqual16(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 241 | return ctx.OpFOrdEqual(ctx.U1, lhs, rhs); | ||
| 242 | } | ||
| 243 | |||
| 244 | Id EmitFPOrdEqual32(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 245 | return ctx.OpFOrdEqual(ctx.U1, lhs, rhs); | ||
| 246 | } | ||
| 247 | |||
| 248 | Id EmitFPOrdEqual64(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 249 | return ctx.OpFOrdEqual(ctx.U1, lhs, rhs); | ||
| 250 | } | ||
| 251 | |||
| 252 | Id EmitFPUnordEqual16(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 253 | return FPUnordCompare(&EmitContext::OpFUnordEqual, ctx, lhs, rhs); | ||
| 254 | } | ||
| 255 | |||
| 256 | Id EmitFPUnordEqual32(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 257 | return FPUnordCompare(&EmitContext::OpFUnordEqual, ctx, lhs, rhs); | ||
| 258 | } | ||
| 259 | |||
| 260 | Id EmitFPUnordEqual64(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 261 | return FPUnordCompare(&EmitContext::OpFUnordEqual, ctx, lhs, rhs); | ||
| 262 | } | ||
| 263 | |||
| 264 | Id EmitFPOrdNotEqual16(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 265 | return FPOrdNotEqual(ctx, lhs, rhs); | ||
| 266 | } | ||
| 267 | |||
| 268 | Id EmitFPOrdNotEqual32(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 269 | return FPOrdNotEqual(ctx, lhs, rhs); | ||
| 270 | } | ||
| 271 | |||
| 272 | Id EmitFPOrdNotEqual64(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 273 | return FPOrdNotEqual(ctx, lhs, rhs); | ||
| 274 | } | ||
| 275 | |||
| 276 | Id EmitFPUnordNotEqual16(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 277 | return ctx.OpFUnordNotEqual(ctx.U1, lhs, rhs); | ||
| 278 | } | ||
| 279 | |||
| 280 | Id EmitFPUnordNotEqual32(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 281 | return ctx.OpFUnordNotEqual(ctx.U1, lhs, rhs); | ||
| 282 | } | ||
| 283 | |||
| 284 | Id EmitFPUnordNotEqual64(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 285 | return ctx.OpFUnordNotEqual(ctx.U1, lhs, rhs); | ||
| 286 | } | ||
| 287 | |||
| 288 | Id EmitFPOrdLessThan16(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 289 | return ctx.OpFOrdLessThan(ctx.U1, lhs, rhs); | ||
| 290 | } | ||
| 291 | |||
| 292 | Id EmitFPOrdLessThan32(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 293 | return ctx.OpFOrdLessThan(ctx.U1, lhs, rhs); | ||
| 294 | } | ||
| 295 | |||
| 296 | Id EmitFPOrdLessThan64(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 297 | return ctx.OpFOrdLessThan(ctx.U1, lhs, rhs); | ||
| 298 | } | ||
| 299 | |||
| 300 | Id EmitFPUnordLessThan16(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 301 | return FPUnordCompare(&EmitContext::OpFUnordLessThan, ctx, lhs, rhs); | ||
| 302 | } | ||
| 303 | |||
| 304 | Id EmitFPUnordLessThan32(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 305 | return FPUnordCompare(&EmitContext::OpFUnordLessThan, ctx, lhs, rhs); | ||
| 306 | } | ||
| 307 | |||
| 308 | Id EmitFPUnordLessThan64(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 309 | return FPUnordCompare(&EmitContext::OpFUnordLessThan, ctx, lhs, rhs); | ||
| 310 | } | ||
| 311 | |||
| 312 | Id EmitFPOrdGreaterThan16(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 313 | return ctx.OpFOrdGreaterThan(ctx.U1, lhs, rhs); | ||
| 314 | } | ||
| 315 | |||
| 316 | Id EmitFPOrdGreaterThan32(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 317 | return ctx.OpFOrdGreaterThan(ctx.U1, lhs, rhs); | ||
| 318 | } | ||
| 319 | |||
| 320 | Id EmitFPOrdGreaterThan64(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 321 | return ctx.OpFOrdGreaterThan(ctx.U1, lhs, rhs); | ||
| 322 | } | ||
| 323 | |||
| 324 | Id EmitFPUnordGreaterThan16(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 325 | return FPUnordCompare(&EmitContext::OpFUnordGreaterThan, ctx, lhs, rhs); | ||
| 326 | } | ||
| 327 | |||
| 328 | Id EmitFPUnordGreaterThan32(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 329 | return FPUnordCompare(&EmitContext::OpFUnordGreaterThan, ctx, lhs, rhs); | ||
| 330 | } | ||
| 331 | |||
| 332 | Id EmitFPUnordGreaterThan64(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 333 | return FPUnordCompare(&EmitContext::OpFUnordGreaterThan, ctx, lhs, rhs); | ||
| 334 | } | ||
| 335 | |||
| 336 | Id EmitFPOrdLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 337 | return ctx.OpFOrdLessThanEqual(ctx.U1, lhs, rhs); | ||
| 338 | } | ||
| 339 | |||
| 340 | Id EmitFPOrdLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 341 | return ctx.OpFOrdLessThanEqual(ctx.U1, lhs, rhs); | ||
| 342 | } | ||
| 343 | |||
| 344 | Id EmitFPOrdLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 345 | return ctx.OpFOrdLessThanEqual(ctx.U1, lhs, rhs); | ||
| 346 | } | ||
| 347 | |||
| 348 | Id EmitFPUnordLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 349 | return FPUnordCompare(&EmitContext::OpFUnordLessThanEqual, ctx, lhs, rhs); | ||
| 350 | } | ||
| 351 | |||
| 352 | Id EmitFPUnordLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 353 | return FPUnordCompare(&EmitContext::OpFUnordLessThanEqual, ctx, lhs, rhs); | ||
| 354 | } | ||
| 355 | |||
| 356 | Id EmitFPUnordLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 357 | return FPUnordCompare(&EmitContext::OpFUnordLessThanEqual, ctx, lhs, rhs); | ||
| 358 | } | ||
| 359 | |||
| 360 | Id EmitFPOrdGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 361 | return ctx.OpFOrdGreaterThanEqual(ctx.U1, lhs, rhs); | ||
| 362 | } | ||
| 363 | |||
| 364 | Id EmitFPOrdGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 365 | return ctx.OpFOrdGreaterThanEqual(ctx.U1, lhs, rhs); | ||
| 366 | } | ||
| 367 | |||
| 368 | Id EmitFPOrdGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 369 | return ctx.OpFOrdGreaterThanEqual(ctx.U1, lhs, rhs); | ||
| 370 | } | ||
| 371 | |||
| 372 | Id EmitFPUnordGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 373 | return FPUnordCompare(&EmitContext::OpFUnordGreaterThanEqual, ctx, lhs, rhs); | ||
| 374 | } | ||
| 375 | |||
| 376 | Id EmitFPUnordGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 377 | return FPUnordCompare(&EmitContext::OpFUnordGreaterThanEqual, ctx, lhs, rhs); | ||
| 378 | } | ||
| 379 | |||
| 380 | Id EmitFPUnordGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 381 | return FPUnordCompare(&EmitContext::OpFUnordGreaterThanEqual, ctx, lhs, rhs); | ||
| 382 | } | ||
| 383 | |||
| 384 | Id EmitFPIsNan16(EmitContext& ctx, Id value) { | ||
| 385 | return ctx.OpIsNan(ctx.U1, value); | ||
| 386 | } | ||
| 387 | |||
| 388 | Id EmitFPIsNan32(EmitContext& ctx, Id value) { | ||
| 389 | return ctx.OpIsNan(ctx.U1, value); | ||
| 390 | } | ||
| 391 | |||
| 392 | Id EmitFPIsNan64(EmitContext& ctx, Id value) { | ||
| 393 | return ctx.OpIsNan(ctx.U1, value); | ||
| 394 | } | ||
| 395 | |||
| 396 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp new file mode 100644 index 000000000..3588f052b --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp | |||
| @@ -0,0 +1,462 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <boost/container/static_vector.hpp> | ||
| 6 | |||
| 7 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 8 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/modifiers.h" | ||
| 10 | |||
| 11 | namespace Shader::Backend::SPIRV { | ||
| 12 | namespace { | ||
| 13 | class ImageOperands { | ||
| 14 | public: | ||
| 15 | explicit ImageOperands(EmitContext& ctx, bool has_bias, bool has_lod, bool has_lod_clamp, | ||
| 16 | Id lod, const IR::Value& offset) { | ||
| 17 | if (has_bias) { | ||
| 18 | const Id bias{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod}; | ||
| 19 | Add(spv::ImageOperandsMask::Bias, bias); | ||
| 20 | } | ||
| 21 | if (has_lod) { | ||
| 22 | const Id lod_value{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod}; | ||
| 23 | Add(spv::ImageOperandsMask::Lod, lod_value); | ||
| 24 | } | ||
| 25 | AddOffset(ctx, offset); | ||
| 26 | if (has_lod_clamp) { | ||
| 27 | const Id lod_clamp{has_bias ? ctx.OpCompositeExtract(ctx.F32[1], lod, 1) : lod}; | ||
| 28 | Add(spv::ImageOperandsMask::MinLod, lod_clamp); | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | explicit ImageOperands(EmitContext& ctx, const IR::Value& offset, const IR::Value& offset2) { | ||
| 33 | if (offset2.IsEmpty()) { | ||
| 34 | if (offset.IsEmpty()) { | ||
| 35 | return; | ||
| 36 | } | ||
| 37 | Add(spv::ImageOperandsMask::Offset, ctx.Def(offset)); | ||
| 38 | return; | ||
| 39 | } | ||
| 40 | const std::array values{offset.InstRecursive(), offset2.InstRecursive()}; | ||
| 41 | if (!values[0]->AreAllArgsImmediates() || !values[1]->AreAllArgsImmediates()) { | ||
| 42 | LOG_WARNING(Shader_SPIRV, "Not all arguments in PTP are immediate, ignoring"); | ||
| 43 | return; | ||
| 44 | } | ||
| 45 | const IR::Opcode opcode{values[0]->GetOpcode()}; | ||
| 46 | if (opcode != values[1]->GetOpcode() || opcode != IR::Opcode::CompositeConstructU32x4) { | ||
| 47 | throw LogicError("Invalid PTP arguments"); | ||
| 48 | } | ||
| 49 | auto read{[&](unsigned int a, unsigned int b) { return values[a]->Arg(b).U32(); }}; | ||
| 50 | |||
| 51 | const Id offsets{ctx.ConstantComposite( | ||
| 52 | ctx.TypeArray(ctx.U32[2], ctx.Const(4U)), ctx.Const(read(0, 0), read(0, 1)), | ||
| 53 | ctx.Const(read(0, 2), read(0, 3)), ctx.Const(read(1, 0), read(1, 1)), | ||
| 54 | ctx.Const(read(1, 2), read(1, 3)))}; | ||
| 55 | Add(spv::ImageOperandsMask::ConstOffsets, offsets); | ||
| 56 | } | ||
| 57 | |||
| 58 | explicit ImageOperands(Id offset, Id lod, Id ms) { | ||
| 59 | if (Sirit::ValidId(lod)) { | ||
| 60 | Add(spv::ImageOperandsMask::Lod, lod); | ||
| 61 | } | ||
| 62 | if (Sirit::ValidId(offset)) { | ||
| 63 | Add(spv::ImageOperandsMask::Offset, offset); | ||
| 64 | } | ||
| 65 | if (Sirit::ValidId(ms)) { | ||
| 66 | Add(spv::ImageOperandsMask::Sample, ms); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates, u32 num_derivates, | ||
| 71 | Id offset, Id lod_clamp) { | ||
| 72 | if (!Sirit::ValidId(derivates)) { | ||
| 73 | throw LogicError("Derivates must be present"); | ||
| 74 | } | ||
| 75 | boost::container::static_vector<Id, 3> deriv_x_accum; | ||
| 76 | boost::container::static_vector<Id, 3> deriv_y_accum; | ||
| 77 | for (u32 i = 0; i < num_derivates; ++i) { | ||
| 78 | deriv_x_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2)); | ||
| 79 | deriv_y_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2 + 1)); | ||
| 80 | } | ||
| 81 | const Id derivates_X{ctx.OpCompositeConstruct( | ||
| 82 | ctx.F32[num_derivates], std::span{deriv_x_accum.data(), deriv_x_accum.size()})}; | ||
| 83 | const Id derivates_Y{ctx.OpCompositeConstruct( | ||
| 84 | ctx.F32[num_derivates], std::span{deriv_y_accum.data(), deriv_y_accum.size()})}; | ||
| 85 | Add(spv::ImageOperandsMask::Grad, derivates_X, derivates_Y); | ||
| 86 | if (Sirit::ValidId(offset)) { | ||
| 87 | Add(spv::ImageOperandsMask::Offset, offset); | ||
| 88 | } | ||
| 89 | if (has_lod_clamp) { | ||
| 90 | Add(spv::ImageOperandsMask::MinLod, lod_clamp); | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | std::span<const Id> Span() const noexcept { | ||
| 95 | return std::span{operands.data(), operands.size()}; | ||
| 96 | } | ||
| 97 | |||
| 98 | std::optional<spv::ImageOperandsMask> MaskOptional() const noexcept { | ||
| 99 | return mask != spv::ImageOperandsMask{} ? std::make_optional(mask) : std::nullopt; | ||
| 100 | } | ||
| 101 | |||
| 102 | spv::ImageOperandsMask Mask() const noexcept { | ||
| 103 | return mask; | ||
| 104 | } | ||
| 105 | |||
| 106 | private: | ||
| 107 | void AddOffset(EmitContext& ctx, const IR::Value& offset) { | ||
| 108 | if (offset.IsEmpty()) { | ||
| 109 | return; | ||
| 110 | } | ||
| 111 | if (offset.IsImmediate()) { | ||
| 112 | Add(spv::ImageOperandsMask::ConstOffset, ctx.SConst(static_cast<s32>(offset.U32()))); | ||
| 113 | return; | ||
| 114 | } | ||
| 115 | IR::Inst* const inst{offset.InstRecursive()}; | ||
| 116 | if (inst->AreAllArgsImmediates()) { | ||
| 117 | switch (inst->GetOpcode()) { | ||
| 118 | case IR::Opcode::CompositeConstructU32x2: | ||
| 119 | Add(spv::ImageOperandsMask::ConstOffset, | ||
| 120 | ctx.SConst(static_cast<s32>(inst->Arg(0).U32()), | ||
| 121 | static_cast<s32>(inst->Arg(1).U32()))); | ||
| 122 | return; | ||
| 123 | case IR::Opcode::CompositeConstructU32x3: | ||
| 124 | Add(spv::ImageOperandsMask::ConstOffset, | ||
| 125 | ctx.SConst(static_cast<s32>(inst->Arg(0).U32()), | ||
| 126 | static_cast<s32>(inst->Arg(1).U32()), | ||
| 127 | static_cast<s32>(inst->Arg(2).U32()))); | ||
| 128 | return; | ||
| 129 | case IR::Opcode::CompositeConstructU32x4: | ||
| 130 | Add(spv::ImageOperandsMask::ConstOffset, | ||
| 131 | ctx.SConst(static_cast<s32>(inst->Arg(0).U32()), | ||
| 132 | static_cast<s32>(inst->Arg(1).U32()), | ||
| 133 | static_cast<s32>(inst->Arg(2).U32()), | ||
| 134 | static_cast<s32>(inst->Arg(3).U32()))); | ||
| 135 | return; | ||
| 136 | default: | ||
| 137 | break; | ||
| 138 | } | ||
| 139 | } | ||
| 140 | Add(spv::ImageOperandsMask::Offset, ctx.Def(offset)); | ||
| 141 | } | ||
| 142 | |||
| 143 | void Add(spv::ImageOperandsMask new_mask, Id value) { | ||
| 144 | mask = static_cast<spv::ImageOperandsMask>(static_cast<unsigned>(mask) | | ||
| 145 | static_cast<unsigned>(new_mask)); | ||
| 146 | operands.push_back(value); | ||
| 147 | } | ||
| 148 | |||
| 149 | void Add(spv::ImageOperandsMask new_mask, Id value_1, Id value_2) { | ||
| 150 | mask = static_cast<spv::ImageOperandsMask>(static_cast<unsigned>(mask) | | ||
| 151 | static_cast<unsigned>(new_mask)); | ||
| 152 | operands.push_back(value_1); | ||
| 153 | operands.push_back(value_2); | ||
| 154 | } | ||
| 155 | |||
| 156 | boost::container::static_vector<Id, 4> operands; | ||
| 157 | spv::ImageOperandsMask mask{}; | ||
| 158 | }; | ||
| 159 | |||
| 160 | Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) { | ||
| 161 | const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; | ||
| 162 | if (def.count > 1) { | ||
| 163 | const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, ctx.Def(index))}; | ||
| 164 | return ctx.OpLoad(def.sampled_type, pointer); | ||
| 165 | } else { | ||
| 166 | return ctx.OpLoad(def.sampled_type, def.id); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& index) { | ||
| 171 | if (!index.IsImmediate() || index.U32() != 0) { | ||
| 172 | throw NotImplementedException("Indirect image indexing"); | ||
| 173 | } | ||
| 174 | if (info.type == TextureType::Buffer) { | ||
| 175 | const TextureBufferDefinition& def{ctx.texture_buffers.at(info.descriptor_index)}; | ||
| 176 | if (def.count > 1) { | ||
| 177 | throw NotImplementedException("Indirect texture sample"); | ||
| 178 | } | ||
| 179 | const Id sampler_id{def.id}; | ||
| 180 | const Id id{ctx.OpLoad(ctx.sampled_texture_buffer_type, sampler_id)}; | ||
| 181 | return ctx.OpImage(ctx.image_buffer_type, id); | ||
| 182 | } else { | ||
| 183 | const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; | ||
| 184 | if (def.count > 1) { | ||
| 185 | throw NotImplementedException("Indirect texture sample"); | ||
| 186 | } | ||
| 187 | return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id)); | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) { | ||
| 192 | if (!index.IsImmediate() || index.U32() != 0) { | ||
| 193 | throw NotImplementedException("Indirect image indexing"); | ||
| 194 | } | ||
| 195 | if (info.type == TextureType::Buffer) { | ||
| 196 | const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)}; | ||
| 197 | return ctx.OpLoad(def.image_type, def.id); | ||
| 198 | } else { | ||
| 199 | const ImageDefinition def{ctx.images.at(info.descriptor_index)}; | ||
| 200 | return ctx.OpLoad(def.image_type, def.id); | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) { | ||
| 205 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||
| 206 | if (info.relaxed_precision != 0) { | ||
| 207 | ctx.Decorate(sample, spv::Decoration::RelaxedPrecision); | ||
| 208 | } | ||
| 209 | return sample; | ||
| 210 | } | ||
| 211 | |||
| 212 | template <typename MethodPtrType, typename... Args> | ||
| 213 | Id Emit(MethodPtrType sparse_ptr, MethodPtrType non_sparse_ptr, EmitContext& ctx, IR::Inst* inst, | ||
| 214 | Id result_type, Args&&... args) { | ||
| 215 | IR::Inst* const sparse{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)}; | ||
| 216 | if (!sparse) { | ||
| 217 | return Decorate(ctx, inst, (ctx.*non_sparse_ptr)(result_type, std::forward<Args>(args)...)); | ||
| 218 | } | ||
| 219 | const Id struct_type{ctx.TypeStruct(ctx.U32[1], result_type)}; | ||
| 220 | const Id sample{(ctx.*sparse_ptr)(struct_type, std::forward<Args>(args)...)}; | ||
| 221 | const Id resident_code{ctx.OpCompositeExtract(ctx.U32[1], sample, 0U)}; | ||
| 222 | sparse->SetDefinition(ctx.OpImageSparseTexelsResident(ctx.U1, resident_code)); | ||
| 223 | sparse->Invalidate(); | ||
| 224 | Decorate(ctx, inst, sample); | ||
| 225 | return ctx.OpCompositeExtract(result_type, sample, 1U); | ||
| 226 | } | ||
| 227 | } // Anonymous namespace | ||
| 228 | |||
| 229 | Id EmitBindlessImageSampleImplicitLod(EmitContext&) { | ||
| 230 | throw LogicError("Unreachable instruction"); | ||
| 231 | } | ||
| 232 | |||
| 233 | Id EmitBindlessImageSampleExplicitLod(EmitContext&) { | ||
| 234 | throw LogicError("Unreachable instruction"); | ||
| 235 | } | ||
| 236 | |||
| 237 | Id EmitBindlessImageSampleDrefImplicitLod(EmitContext&) { | ||
| 238 | throw LogicError("Unreachable instruction"); | ||
| 239 | } | ||
| 240 | |||
| 241 | Id EmitBindlessImageSampleDrefExplicitLod(EmitContext&) { | ||
| 242 | throw LogicError("Unreachable instruction"); | ||
| 243 | } | ||
| 244 | |||
| 245 | Id EmitBindlessImageGather(EmitContext&) { | ||
| 246 | throw LogicError("Unreachable instruction"); | ||
| 247 | } | ||
| 248 | |||
| 249 | Id EmitBindlessImageGatherDref(EmitContext&) { | ||
| 250 | throw LogicError("Unreachable instruction"); | ||
| 251 | } | ||
| 252 | |||
| 253 | Id EmitBindlessImageFetch(EmitContext&) { | ||
| 254 | throw LogicError("Unreachable instruction"); | ||
| 255 | } | ||
| 256 | |||
| 257 | Id EmitBindlessImageQueryDimensions(EmitContext&) { | ||
| 258 | throw LogicError("Unreachable instruction"); | ||
| 259 | } | ||
| 260 | |||
| 261 | Id EmitBindlessImageQueryLod(EmitContext&) { | ||
| 262 | throw LogicError("Unreachable instruction"); | ||
| 263 | } | ||
| 264 | |||
| 265 | Id EmitBindlessImageGradient(EmitContext&) { | ||
| 266 | throw LogicError("Unreachable instruction"); | ||
| 267 | } | ||
| 268 | |||
| 269 | Id EmitBindlessImageRead(EmitContext&) { | ||
| 270 | throw LogicError("Unreachable instruction"); | ||
| 271 | } | ||
| 272 | |||
| 273 | Id EmitBindlessImageWrite(EmitContext&) { | ||
| 274 | throw LogicError("Unreachable instruction"); | ||
| 275 | } | ||
| 276 | |||
| 277 | Id EmitBoundImageSampleImplicitLod(EmitContext&) { | ||
| 278 | throw LogicError("Unreachable instruction"); | ||
| 279 | } | ||
| 280 | |||
| 281 | Id EmitBoundImageSampleExplicitLod(EmitContext&) { | ||
| 282 | throw LogicError("Unreachable instruction"); | ||
| 283 | } | ||
| 284 | |||
| 285 | Id EmitBoundImageSampleDrefImplicitLod(EmitContext&) { | ||
| 286 | throw LogicError("Unreachable instruction"); | ||
| 287 | } | ||
| 288 | |||
| 289 | Id EmitBoundImageSampleDrefExplicitLod(EmitContext&) { | ||
| 290 | throw LogicError("Unreachable instruction"); | ||
| 291 | } | ||
| 292 | |||
| 293 | Id EmitBoundImageGather(EmitContext&) { | ||
| 294 | throw LogicError("Unreachable instruction"); | ||
| 295 | } | ||
| 296 | |||
| 297 | Id EmitBoundImageGatherDref(EmitContext&) { | ||
| 298 | throw LogicError("Unreachable instruction"); | ||
| 299 | } | ||
| 300 | |||
| 301 | Id EmitBoundImageFetch(EmitContext&) { | ||
| 302 | throw LogicError("Unreachable instruction"); | ||
| 303 | } | ||
| 304 | |||
| 305 | Id EmitBoundImageQueryDimensions(EmitContext&) { | ||
| 306 | throw LogicError("Unreachable instruction"); | ||
| 307 | } | ||
| 308 | |||
| 309 | Id EmitBoundImageQueryLod(EmitContext&) { | ||
| 310 | throw LogicError("Unreachable instruction"); | ||
| 311 | } | ||
| 312 | |||
| 313 | Id EmitBoundImageGradient(EmitContext&) { | ||
| 314 | throw LogicError("Unreachable instruction"); | ||
| 315 | } | ||
| 316 | |||
| 317 | Id EmitBoundImageRead(EmitContext&) { | ||
| 318 | throw LogicError("Unreachable instruction"); | ||
| 319 | } | ||
| 320 | |||
| 321 | Id EmitBoundImageWrite(EmitContext&) { | ||
| 322 | throw LogicError("Unreachable instruction"); | ||
| 323 | } | ||
| 324 | |||
| 325 | Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 326 | Id bias_lc, const IR::Value& offset) { | ||
| 327 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||
| 328 | if (ctx.stage == Stage::Fragment) { | ||
| 329 | const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0, | ||
| 330 | bias_lc, offset); | ||
| 331 | return Emit(&EmitContext::OpImageSparseSampleImplicitLod, | ||
| 332 | &EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4], | ||
| 333 | Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); | ||
| 334 | } else { | ||
| 335 | // We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as | ||
| 336 | // if the lod was explicitly zero. This may change on Turing with implicit compute | ||
| 337 | // derivatives | ||
| 338 | const Id lod{ctx.Const(0.0f)}; | ||
| 339 | const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset); | ||
| 340 | return Emit(&EmitContext::OpImageSparseSampleExplicitLod, | ||
| 341 | &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], | ||
| 342 | Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); | ||
| 343 | } | ||
| 344 | } | ||
| 345 | |||
| 346 | Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 347 | Id lod, const IR::Value& offset) { | ||
| 348 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||
| 349 | const ImageOperands operands(ctx, false, true, false, lod, offset); | ||
| 350 | return Emit(&EmitContext::OpImageSparseSampleExplicitLod, | ||
| 351 | &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], | ||
| 352 | Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); | ||
| 353 | } | ||
| 354 | |||
| 355 | Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, | ||
| 356 | Id coords, Id dref, Id bias_lc, const IR::Value& offset) { | ||
| 357 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||
| 358 | const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0, bias_lc, | ||
| 359 | offset); | ||
| 360 | return Emit(&EmitContext::OpImageSparseSampleDrefImplicitLod, | ||
| 361 | &EmitContext::OpImageSampleDrefImplicitLod, ctx, inst, ctx.F32[1], | ||
| 362 | Texture(ctx, info, index), coords, dref, operands.MaskOptional(), operands.Span()); | ||
| 363 | } | ||
| 364 | |||
| 365 | Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, | ||
| 366 | Id coords, Id dref, Id lod, const IR::Value& offset) { | ||
| 367 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||
| 368 | const ImageOperands operands(ctx, false, true, false, lod, offset); | ||
| 369 | return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod, | ||
| 370 | &EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1], | ||
| 371 | Texture(ctx, info, index), coords, dref, operands.Mask(), operands.Span()); | ||
| 372 | } | ||
| 373 | |||
| 374 | Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 375 | const IR::Value& offset, const IR::Value& offset2) { | ||
| 376 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||
| 377 | const ImageOperands operands(ctx, offset, offset2); | ||
| 378 | return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst, | ||
| 379 | ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component), | ||
| 380 | operands.MaskOptional(), operands.Span()); | ||
| 381 | } | ||
| 382 | |||
| 383 | Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 384 | const IR::Value& offset, const IR::Value& offset2, Id dref) { | ||
| 385 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||
| 386 | const ImageOperands operands(ctx, offset, offset2); | ||
| 387 | return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst, | ||
| 388 | ctx.F32[4], Texture(ctx, info, index), coords, dref, operands.MaskOptional(), | ||
| 389 | operands.Span()); | ||
| 390 | } | ||
| 391 | |||
| 392 | Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, | ||
| 393 | Id lod, Id ms) { | ||
| 394 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||
| 395 | if (info.type == TextureType::Buffer) { | ||
| 396 | lod = Id{}; | ||
| 397 | } | ||
| 398 | const ImageOperands operands(offset, lod, ms); | ||
| 399 | return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], | ||
| 400 | TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); | ||
| 401 | } | ||
| 402 | |||
| 403 | Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) { | ||
| 404 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||
| 405 | const Id image{TextureImage(ctx, info, index)}; | ||
| 406 | const Id zero{ctx.u32_zero_value}; | ||
| 407 | const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }}; | ||
| 408 | switch (info.type) { | ||
| 409 | case TextureType::Color1D: | ||
| 410 | return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[1], image, lod), | ||
| 411 | zero, zero, mips()); | ||
| 412 | case TextureType::ColorArray1D: | ||
| 413 | case TextureType::Color2D: | ||
| 414 | case TextureType::ColorCube: | ||
| 415 | return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[2], image, lod), | ||
| 416 | zero, mips()); | ||
| 417 | case TextureType::ColorArray2D: | ||
| 418 | case TextureType::Color3D: | ||
| 419 | case TextureType::ColorArrayCube: | ||
| 420 | return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[3], image, lod), | ||
| 421 | mips()); | ||
| 422 | case TextureType::Buffer: | ||
| 423 | return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySize(ctx.U32[1], image), zero, | ||
| 424 | zero, mips()); | ||
| 425 | } | ||
| 426 | throw LogicError("Unspecified image type {}", info.type.Value()); | ||
| 427 | } | ||
| 428 | |||
| 429 | Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) { | ||
| 430 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||
| 431 | const Id zero{ctx.f32_zero_value}; | ||
| 432 | const Id sampler{Texture(ctx, info, index)}; | ||
| 433 | return ctx.OpCompositeConstruct(ctx.F32[4], ctx.OpImageQueryLod(ctx.F32[2], sampler, coords), | ||
| 434 | zero, zero); | ||
| 435 | } | ||
| 436 | |||
| 437 | Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 438 | Id derivates, Id offset, Id lod_clamp) { | ||
| 439 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||
| 440 | const ImageOperands operands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates, | ||
| 441 | offset, lod_clamp); | ||
| 442 | return Emit(&EmitContext::OpImageSparseSampleExplicitLod, | ||
| 443 | &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], | ||
| 444 | Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); | ||
| 445 | } | ||
| 446 | |||
| 447 | Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) { | ||
| 448 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||
| 449 | if (info.image_format == ImageFormat::Typeless && !ctx.profile.support_typeless_image_loads) { | ||
| 450 | LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host"); | ||
| 451 | return ctx.ConstantNull(ctx.U32[4]); | ||
| 452 | } | ||
| 453 | return Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst, ctx.U32[4], | ||
| 454 | Image(ctx, index, info), coords, std::nullopt, std::span<const Id>{}); | ||
| 455 | } | ||
| 456 | |||
| 457 | void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) { | ||
| 458 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||
| 459 | ctx.OpImageWrite(Image(ctx, index, info), coords, color); | ||
| 460 | } | ||
| 461 | |||
| 462 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp new file mode 100644 index 000000000..d7f1a365a --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image_atomic.cpp | |||
| @@ -0,0 +1,183 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 7 | #include "shader_recompiler/frontend/ir/modifiers.h" | ||
| 8 | |||
| 9 | namespace Shader::Backend::SPIRV { | ||
| 10 | namespace { | ||
| 11 | Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) { | ||
| 12 | if (!index.IsImmediate()) { | ||
| 13 | throw NotImplementedException("Indirect image indexing"); | ||
| 14 | } | ||
| 15 | if (info.type == TextureType::Buffer) { | ||
| 16 | const ImageBufferDefinition def{ctx.image_buffers.at(index.U32())}; | ||
| 17 | return def.id; | ||
| 18 | } else { | ||
| 19 | const ImageDefinition def{ctx.images.at(index.U32())}; | ||
| 20 | return def.id; | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | std::pair<Id, Id> AtomicArgs(EmitContext& ctx) { | ||
| 25 | const Id scope{ctx.Const(static_cast<u32>(spv::Scope::Device))}; | ||
| 26 | const Id semantics{ctx.u32_zero_value}; | ||
| 27 | return {scope, semantics}; | ||
| 28 | } | ||
| 29 | |||
| 30 | Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id value, | ||
| 31 | Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) { | ||
| 32 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | ||
| 33 | const Id image{Image(ctx, index, info)}; | ||
| 34 | const Id pointer{ctx.OpImageTexelPointer(ctx.image_u32, image, coords, ctx.Const(0U))}; | ||
| 35 | const auto [scope, semantics]{AtomicArgs(ctx)}; | ||
| 36 | return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value); | ||
| 37 | } | ||
| 38 | } // Anonymous namespace | ||
| 39 | |||
| 40 | Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 41 | Id value) { | ||
| 42 | return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicIAdd); | ||
| 43 | } | ||
| 44 | |||
| 45 | Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 46 | Id value) { | ||
| 47 | return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMin); | ||
| 48 | } | ||
| 49 | |||
| 50 | Id EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 51 | Id value) { | ||
| 52 | return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMin); | ||
| 53 | } | ||
| 54 | |||
| 55 | Id EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 56 | Id value) { | ||
| 57 | return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicSMax); | ||
| 58 | } | ||
| 59 | |||
| 60 | Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 61 | Id value) { | ||
| 62 | return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicUMax); | ||
| 63 | } | ||
| 64 | |||
| 65 | Id EmitImageAtomicInc32(EmitContext&, IR::Inst*, const IR::Value&, Id, Id) { | ||
| 66 | // TODO: This is not yet implemented | ||
| 67 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 68 | } | ||
| 69 | |||
| 70 | Id EmitImageAtomicDec32(EmitContext&, IR::Inst*, const IR::Value&, Id, Id) { | ||
| 71 | // TODO: This is not yet implemented | ||
| 72 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 73 | } | ||
| 74 | |||
| 75 | Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 76 | Id value) { | ||
| 77 | return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicAnd); | ||
| 78 | } | ||
| 79 | |||
| 80 | Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 81 | Id value) { | ||
| 82 | return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicOr); | ||
| 83 | } | ||
| 84 | |||
| 85 | Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 86 | Id value) { | ||
| 87 | return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicXor); | ||
| 88 | } | ||
| 89 | |||
| 90 | Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 91 | Id value) { | ||
| 92 | return ImageAtomicU32(ctx, inst, index, coords, value, &Sirit::Module::OpAtomicExchange); | ||
| 93 | } | ||
| 94 | |||
| 95 | Id EmitBindlessImageAtomicIAdd32(EmitContext&) { | ||
| 96 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 97 | } | ||
| 98 | |||
| 99 | Id EmitBindlessImageAtomicSMin32(EmitContext&) { | ||
| 100 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 101 | } | ||
| 102 | |||
| 103 | Id EmitBindlessImageAtomicUMin32(EmitContext&) { | ||
| 104 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 105 | } | ||
| 106 | |||
| 107 | Id EmitBindlessImageAtomicSMax32(EmitContext&) { | ||
| 108 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 109 | } | ||
| 110 | |||
| 111 | Id EmitBindlessImageAtomicUMax32(EmitContext&) { | ||
| 112 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 113 | } | ||
| 114 | |||
| 115 | Id EmitBindlessImageAtomicInc32(EmitContext&) { | ||
| 116 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 117 | } | ||
| 118 | |||
| 119 | Id EmitBindlessImageAtomicDec32(EmitContext&) { | ||
| 120 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 121 | } | ||
| 122 | |||
| 123 | Id EmitBindlessImageAtomicAnd32(EmitContext&) { | ||
| 124 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 125 | } | ||
| 126 | |||
| 127 | Id EmitBindlessImageAtomicOr32(EmitContext&) { | ||
| 128 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 129 | } | ||
| 130 | |||
| 131 | Id EmitBindlessImageAtomicXor32(EmitContext&) { | ||
| 132 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 133 | } | ||
| 134 | |||
| 135 | Id EmitBindlessImageAtomicExchange32(EmitContext&) { | ||
| 136 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 137 | } | ||
| 138 | |||
| 139 | Id EmitBoundImageAtomicIAdd32(EmitContext&) { | ||
| 140 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 141 | } | ||
| 142 | |||
| 143 | Id EmitBoundImageAtomicSMin32(EmitContext&) { | ||
| 144 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 145 | } | ||
| 146 | |||
| 147 | Id EmitBoundImageAtomicUMin32(EmitContext&) { | ||
| 148 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 149 | } | ||
| 150 | |||
| 151 | Id EmitBoundImageAtomicSMax32(EmitContext&) { | ||
| 152 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 153 | } | ||
| 154 | |||
| 155 | Id EmitBoundImageAtomicUMax32(EmitContext&) { | ||
| 156 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 157 | } | ||
| 158 | |||
| 159 | Id EmitBoundImageAtomicInc32(EmitContext&) { | ||
| 160 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 161 | } | ||
| 162 | |||
| 163 | Id EmitBoundImageAtomicDec32(EmitContext&) { | ||
| 164 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 165 | } | ||
| 166 | |||
| 167 | Id EmitBoundImageAtomicAnd32(EmitContext&) { | ||
| 168 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 169 | } | ||
| 170 | |||
| 171 | Id EmitBoundImageAtomicOr32(EmitContext&) { | ||
| 172 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 173 | } | ||
| 174 | |||
| 175 | Id EmitBoundImageAtomicXor32(EmitContext&) { | ||
| 176 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 177 | } | ||
| 178 | |||
| 179 | Id EmitBoundImageAtomicExchange32(EmitContext&) { | ||
| 180 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 181 | } | ||
| 182 | |||
| 183 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h new file mode 100644 index 000000000..f99c02848 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h | |||
| @@ -0,0 +1,579 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <sirit/sirit.h> | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace Shader::IR { | ||
| 10 | enum class Attribute : u64; | ||
| 11 | enum class Patch : u64; | ||
| 12 | class Inst; | ||
| 13 | class Value; | ||
| 14 | } // namespace Shader::IR | ||
| 15 | |||
| 16 | namespace Shader::Backend::SPIRV { | ||
| 17 | |||
| 18 | using Sirit::Id; | ||
| 19 | |||
| 20 | class EmitContext; | ||
| 21 | |||
| 22 | // Microinstruction emitters | ||
| 23 | Id EmitPhi(EmitContext& ctx, IR::Inst* inst); | ||
| 24 | void EmitVoid(EmitContext& ctx); | ||
| 25 | Id EmitIdentity(EmitContext& ctx, const IR::Value& value); | ||
| 26 | Id EmitConditionRef(EmitContext& ctx, const IR::Value& value); | ||
| 27 | void EmitReference(EmitContext&); | ||
| 28 | void EmitPhiMove(EmitContext&); | ||
| 29 | void EmitJoin(EmitContext& ctx); | ||
| 30 | void EmitDemoteToHelperInvocation(EmitContext& ctx); | ||
| 31 | void EmitBarrier(EmitContext& ctx); | ||
| 32 | void EmitWorkgroupMemoryBarrier(EmitContext& ctx); | ||
| 33 | void EmitDeviceMemoryBarrier(EmitContext& ctx); | ||
| 34 | void EmitPrologue(EmitContext& ctx); | ||
| 35 | void EmitEpilogue(EmitContext& ctx); | ||
| 36 | void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream); | ||
| 37 | void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream); | ||
| 38 | void EmitGetRegister(EmitContext& ctx); | ||
| 39 | void EmitSetRegister(EmitContext& ctx); | ||
| 40 | void EmitGetPred(EmitContext& ctx); | ||
| 41 | void EmitSetPred(EmitContext& ctx); | ||
| 42 | void EmitSetGotoVariable(EmitContext& ctx); | ||
| 43 | void EmitGetGotoVariable(EmitContext& ctx); | ||
| 44 | void EmitSetIndirectBranchVariable(EmitContext& ctx); | ||
| 45 | void EmitGetIndirectBranchVariable(EmitContext& ctx); | ||
| 46 | Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||
| 47 | Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||
| 48 | Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||
| 49 | Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||
| 50 | Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||
| 51 | Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||
| 52 | Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||
| 53 | Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex); | ||
| 54 | void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, Id vertex); | ||
| 55 | Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex); | ||
| 56 | void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, Id vertex); | ||
| 57 | Id EmitGetPatch(EmitContext& ctx, IR::Patch patch); | ||
| 58 | void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value); | ||
| 59 | void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value); | ||
| 60 | void EmitSetSampleMask(EmitContext& ctx, Id value); | ||
| 61 | void EmitSetFragDepth(EmitContext& ctx, Id value); | ||
| 62 | void EmitGetZFlag(EmitContext& ctx); | ||
| 63 | void EmitGetSFlag(EmitContext& ctx); | ||
| 64 | void EmitGetCFlag(EmitContext& ctx); | ||
| 65 | void EmitGetOFlag(EmitContext& ctx); | ||
| 66 | void EmitSetZFlag(EmitContext& ctx); | ||
| 67 | void EmitSetSFlag(EmitContext& ctx); | ||
| 68 | void EmitSetCFlag(EmitContext& ctx); | ||
| 69 | void EmitSetOFlag(EmitContext& ctx); | ||
| 70 | Id EmitWorkgroupId(EmitContext& ctx); | ||
| 71 | Id EmitLocalInvocationId(EmitContext& ctx); | ||
| 72 | Id EmitInvocationId(EmitContext& ctx); | ||
| 73 | Id EmitSampleId(EmitContext& ctx); | ||
| 74 | Id EmitIsHelperInvocation(EmitContext& ctx); | ||
| 75 | Id EmitYDirection(EmitContext& ctx); | ||
| 76 | Id EmitLoadLocal(EmitContext& ctx, Id word_offset); | ||
| 77 | void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value); | ||
| 78 | Id EmitUndefU1(EmitContext& ctx); | ||
| 79 | Id EmitUndefU8(EmitContext& ctx); | ||
| 80 | Id EmitUndefU16(EmitContext& ctx); | ||
| 81 | Id EmitUndefU32(EmitContext& ctx); | ||
| 82 | Id EmitUndefU64(EmitContext& ctx); | ||
| 83 | void EmitLoadGlobalU8(EmitContext& ctx); | ||
| 84 | void EmitLoadGlobalS8(EmitContext& ctx); | ||
| 85 | void EmitLoadGlobalU16(EmitContext& ctx); | ||
| 86 | void EmitLoadGlobalS16(EmitContext& ctx); | ||
| 87 | Id EmitLoadGlobal32(EmitContext& ctx, Id address); | ||
| 88 | Id EmitLoadGlobal64(EmitContext& ctx, Id address); | ||
| 89 | Id EmitLoadGlobal128(EmitContext& ctx, Id address); | ||
| 90 | void EmitWriteGlobalU8(EmitContext& ctx); | ||
| 91 | void EmitWriteGlobalS8(EmitContext& ctx); | ||
| 92 | void EmitWriteGlobalU16(EmitContext& ctx); | ||
| 93 | void EmitWriteGlobalS16(EmitContext& ctx); | ||
| 94 | void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value); | ||
| 95 | void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value); | ||
| 96 | void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value); | ||
| 97 | Id EmitLoadStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||
| 98 | Id EmitLoadStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||
| 99 | Id EmitLoadStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||
| 100 | Id EmitLoadStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||
| 101 | Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||
| 102 | Id EmitLoadStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||
| 103 | Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||
| 104 | void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 105 | Id value); | ||
| 106 | void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 107 | Id value); | ||
| 108 | void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 109 | Id value); | ||
| 110 | void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 111 | Id value); | ||
| 112 | void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 113 | Id value); | ||
| 114 | void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 115 | Id value); | ||
| 116 | void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 117 | Id value); | ||
| 118 | Id EmitLoadSharedU8(EmitContext& ctx, Id offset); | ||
| 119 | Id EmitLoadSharedS8(EmitContext& ctx, Id offset); | ||
| 120 | Id EmitLoadSharedU16(EmitContext& ctx, Id offset); | ||
| 121 | Id EmitLoadSharedS16(EmitContext& ctx, Id offset); | ||
| 122 | Id EmitLoadSharedU32(EmitContext& ctx, Id offset); | ||
| 123 | Id EmitLoadSharedU64(EmitContext& ctx, Id offset); | ||
| 124 | Id EmitLoadSharedU128(EmitContext& ctx, Id offset); | ||
| 125 | void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value); | ||
| 126 | void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value); | ||
| 127 | void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value); | ||
| 128 | void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value); | ||
| 129 | void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value); | ||
| 130 | Id EmitCompositeConstructU32x2(EmitContext& ctx, Id e1, Id e2); | ||
| 131 | Id EmitCompositeConstructU32x3(EmitContext& ctx, Id e1, Id e2, Id e3); | ||
| 132 | Id EmitCompositeConstructU32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4); | ||
| 133 | Id EmitCompositeExtractU32x2(EmitContext& ctx, Id composite, u32 index); | ||
| 134 | Id EmitCompositeExtractU32x3(EmitContext& ctx, Id composite, u32 index); | ||
| 135 | Id EmitCompositeExtractU32x4(EmitContext& ctx, Id composite, u32 index); | ||
| 136 | Id EmitCompositeInsertU32x2(EmitContext& ctx, Id composite, Id object, u32 index); | ||
| 137 | Id EmitCompositeInsertU32x3(EmitContext& ctx, Id composite, Id object, u32 index); | ||
| 138 | Id EmitCompositeInsertU32x4(EmitContext& ctx, Id composite, Id object, u32 index); | ||
| 139 | Id EmitCompositeConstructF16x2(EmitContext& ctx, Id e1, Id e2); | ||
| 140 | Id EmitCompositeConstructF16x3(EmitContext& ctx, Id e1, Id e2, Id e3); | ||
| 141 | Id EmitCompositeConstructF16x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4); | ||
| 142 | Id EmitCompositeExtractF16x2(EmitContext& ctx, Id composite, u32 index); | ||
| 143 | Id EmitCompositeExtractF16x3(EmitContext& ctx, Id composite, u32 index); | ||
| 144 | Id EmitCompositeExtractF16x4(EmitContext& ctx, Id composite, u32 index); | ||
| 145 | Id EmitCompositeInsertF16x2(EmitContext& ctx, Id composite, Id object, u32 index); | ||
| 146 | Id EmitCompositeInsertF16x3(EmitContext& ctx, Id composite, Id object, u32 index); | ||
| 147 | Id EmitCompositeInsertF16x4(EmitContext& ctx, Id composite, Id object, u32 index); | ||
| 148 | Id EmitCompositeConstructF32x2(EmitContext& ctx, Id e1, Id e2); | ||
| 149 | Id EmitCompositeConstructF32x3(EmitContext& ctx, Id e1, Id e2, Id e3); | ||
| 150 | Id EmitCompositeConstructF32x4(EmitContext& ctx, Id e1, Id e2, Id e3, Id e4); | ||
| 151 | Id EmitCompositeExtractF32x2(EmitContext& ctx, Id composite, u32 index); | ||
| 152 | Id EmitCompositeExtractF32x3(EmitContext& ctx, Id composite, u32 index); | ||
| 153 | Id EmitCompositeExtractF32x4(EmitContext& ctx, Id composite, u32 index); | ||
| 154 | Id EmitCompositeInsertF32x2(EmitContext& ctx, Id composite, Id object, u32 index); | ||
| 155 | Id EmitCompositeInsertF32x3(EmitContext& ctx, Id composite, Id object, u32 index); | ||
| 156 | Id EmitCompositeInsertF32x4(EmitContext& ctx, Id composite, Id object, u32 index); | ||
| 157 | void EmitCompositeConstructF64x2(EmitContext& ctx); | ||
| 158 | void EmitCompositeConstructF64x3(EmitContext& ctx); | ||
| 159 | void EmitCompositeConstructF64x4(EmitContext& ctx); | ||
| 160 | void EmitCompositeExtractF64x2(EmitContext& ctx); | ||
| 161 | void EmitCompositeExtractF64x3(EmitContext& ctx); | ||
| 162 | void EmitCompositeExtractF64x4(EmitContext& ctx); | ||
| 163 | Id EmitCompositeInsertF64x2(EmitContext& ctx, Id composite, Id object, u32 index); | ||
| 164 | Id EmitCompositeInsertF64x3(EmitContext& ctx, Id composite, Id object, u32 index); | ||
| 165 | Id EmitCompositeInsertF64x4(EmitContext& ctx, Id composite, Id object, u32 index); | ||
| 166 | Id EmitSelectU1(EmitContext& ctx, Id cond, Id true_value, Id false_value); | ||
| 167 | Id EmitSelectU8(EmitContext& ctx, Id cond, Id true_value, Id false_value); | ||
| 168 | Id EmitSelectU16(EmitContext& ctx, Id cond, Id true_value, Id false_value); | ||
| 169 | Id EmitSelectU32(EmitContext& ctx, Id cond, Id true_value, Id false_value); | ||
| 170 | Id EmitSelectU64(EmitContext& ctx, Id cond, Id true_value, Id false_value); | ||
| 171 | Id EmitSelectF16(EmitContext& ctx, Id cond, Id true_value, Id false_value); | ||
| 172 | Id EmitSelectF32(EmitContext& ctx, Id cond, Id true_value, Id false_value); | ||
| 173 | Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value); | ||
| 174 | void EmitBitCastU16F16(EmitContext& ctx); | ||
| 175 | Id EmitBitCastU32F32(EmitContext& ctx, Id value); | ||
| 176 | void EmitBitCastU64F64(EmitContext& ctx); | ||
| 177 | void EmitBitCastF16U16(EmitContext& ctx); | ||
| 178 | Id EmitBitCastF32U32(EmitContext& ctx, Id value); | ||
| 179 | void EmitBitCastF64U64(EmitContext& ctx); | ||
| 180 | Id EmitPackUint2x32(EmitContext& ctx, Id value); | ||
| 181 | Id EmitUnpackUint2x32(EmitContext& ctx, Id value); | ||
| 182 | Id EmitPackFloat2x16(EmitContext& ctx, Id value); | ||
| 183 | Id EmitUnpackFloat2x16(EmitContext& ctx, Id value); | ||
| 184 | Id EmitPackHalf2x16(EmitContext& ctx, Id value); | ||
| 185 | Id EmitUnpackHalf2x16(EmitContext& ctx, Id value); | ||
| 186 | Id EmitPackDouble2x32(EmitContext& ctx, Id value); | ||
| 187 | Id EmitUnpackDouble2x32(EmitContext& ctx, Id value); | ||
| 188 | void EmitGetZeroFromOp(EmitContext& ctx); | ||
| 189 | void EmitGetSignFromOp(EmitContext& ctx); | ||
| 190 | void EmitGetCarryFromOp(EmitContext& ctx); | ||
| 191 | void EmitGetOverflowFromOp(EmitContext& ctx); | ||
| 192 | void EmitGetSparseFromOp(EmitContext& ctx); | ||
| 193 | void EmitGetInBoundsFromOp(EmitContext& ctx); | ||
| 194 | Id EmitFPAbs16(EmitContext& ctx, Id value); | ||
| 195 | Id EmitFPAbs32(EmitContext& ctx, Id value); | ||
| 196 | Id EmitFPAbs64(EmitContext& ctx, Id value); | ||
| 197 | Id EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b); | ||
| 198 | Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b); | ||
| 199 | Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b); | ||
| 200 | Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c); | ||
| 201 | Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c); | ||
| 202 | Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c); | ||
| 203 | Id EmitFPMax32(EmitContext& ctx, Id a, Id b); | ||
| 204 | Id EmitFPMax64(EmitContext& ctx, Id a, Id b); | ||
| 205 | Id EmitFPMin32(EmitContext& ctx, Id a, Id b); | ||
| 206 | Id EmitFPMin64(EmitContext& ctx, Id a, Id b); | ||
| 207 | Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b); | ||
| 208 | Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b); | ||
| 209 | Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b); | ||
| 210 | Id EmitFPNeg16(EmitContext& ctx, Id value); | ||
| 211 | Id EmitFPNeg32(EmitContext& ctx, Id value); | ||
| 212 | Id EmitFPNeg64(EmitContext& ctx, Id value); | ||
| 213 | Id EmitFPSin(EmitContext& ctx, Id value); | ||
| 214 | Id EmitFPCos(EmitContext& ctx, Id value); | ||
| 215 | Id EmitFPExp2(EmitContext& ctx, Id value); | ||
| 216 | Id EmitFPLog2(EmitContext& ctx, Id value); | ||
| 217 | Id EmitFPRecip32(EmitContext& ctx, Id value); | ||
| 218 | Id EmitFPRecip64(EmitContext& ctx, Id value); | ||
| 219 | Id EmitFPRecipSqrt32(EmitContext& ctx, Id value); | ||
| 220 | Id EmitFPRecipSqrt64(EmitContext& ctx, Id value); | ||
| 221 | Id EmitFPSqrt(EmitContext& ctx, Id value); | ||
| 222 | Id EmitFPSaturate16(EmitContext& ctx, Id value); | ||
| 223 | Id EmitFPSaturate32(EmitContext& ctx, Id value); | ||
| 224 | Id EmitFPSaturate64(EmitContext& ctx, Id value); | ||
| 225 | Id EmitFPClamp16(EmitContext& ctx, Id value, Id min_value, Id max_value); | ||
| 226 | Id EmitFPClamp32(EmitContext& ctx, Id value, Id min_value, Id max_value); | ||
| 227 | Id EmitFPClamp64(EmitContext& ctx, Id value, Id min_value, Id max_value); | ||
| 228 | Id EmitFPRoundEven16(EmitContext& ctx, Id value); | ||
| 229 | Id EmitFPRoundEven32(EmitContext& ctx, Id value); | ||
| 230 | Id EmitFPRoundEven64(EmitContext& ctx, Id value); | ||
| 231 | Id EmitFPFloor16(EmitContext& ctx, Id value); | ||
| 232 | Id EmitFPFloor32(EmitContext& ctx, Id value); | ||
| 233 | Id EmitFPFloor64(EmitContext& ctx, Id value); | ||
| 234 | Id EmitFPCeil16(EmitContext& ctx, Id value); | ||
| 235 | Id EmitFPCeil32(EmitContext& ctx, Id value); | ||
| 236 | Id EmitFPCeil64(EmitContext& ctx, Id value); | ||
| 237 | Id EmitFPTrunc16(EmitContext& ctx, Id value); | ||
| 238 | Id EmitFPTrunc32(EmitContext& ctx, Id value); | ||
| 239 | Id EmitFPTrunc64(EmitContext& ctx, Id value); | ||
| 240 | Id EmitFPOrdEqual16(EmitContext& ctx, Id lhs, Id rhs); | ||
| 241 | Id EmitFPOrdEqual32(EmitContext& ctx, Id lhs, Id rhs); | ||
| 242 | Id EmitFPOrdEqual64(EmitContext& ctx, Id lhs, Id rhs); | ||
| 243 | Id EmitFPUnordEqual16(EmitContext& ctx, Id lhs, Id rhs); | ||
| 244 | Id EmitFPUnordEqual32(EmitContext& ctx, Id lhs, Id rhs); | ||
| 245 | Id EmitFPUnordEqual64(EmitContext& ctx, Id lhs, Id rhs); | ||
| 246 | Id EmitFPOrdNotEqual16(EmitContext& ctx, Id lhs, Id rhs); | ||
| 247 | Id EmitFPOrdNotEqual32(EmitContext& ctx, Id lhs, Id rhs); | ||
| 248 | Id EmitFPOrdNotEqual64(EmitContext& ctx, Id lhs, Id rhs); | ||
| 249 | Id EmitFPUnordNotEqual16(EmitContext& ctx, Id lhs, Id rhs); | ||
| 250 | Id EmitFPUnordNotEqual32(EmitContext& ctx, Id lhs, Id rhs); | ||
| 251 | Id EmitFPUnordNotEqual64(EmitContext& ctx, Id lhs, Id rhs); | ||
| 252 | Id EmitFPOrdLessThan16(EmitContext& ctx, Id lhs, Id rhs); | ||
| 253 | Id EmitFPOrdLessThan32(EmitContext& ctx, Id lhs, Id rhs); | ||
| 254 | Id EmitFPOrdLessThan64(EmitContext& ctx, Id lhs, Id rhs); | ||
| 255 | Id EmitFPUnordLessThan16(EmitContext& ctx, Id lhs, Id rhs); | ||
| 256 | Id EmitFPUnordLessThan32(EmitContext& ctx, Id lhs, Id rhs); | ||
| 257 | Id EmitFPUnordLessThan64(EmitContext& ctx, Id lhs, Id rhs); | ||
| 258 | Id EmitFPOrdGreaterThan16(EmitContext& ctx, Id lhs, Id rhs); | ||
| 259 | Id EmitFPOrdGreaterThan32(EmitContext& ctx, Id lhs, Id rhs); | ||
| 260 | Id EmitFPOrdGreaterThan64(EmitContext& ctx, Id lhs, Id rhs); | ||
| 261 | Id EmitFPUnordGreaterThan16(EmitContext& ctx, Id lhs, Id rhs); | ||
| 262 | Id EmitFPUnordGreaterThan32(EmitContext& ctx, Id lhs, Id rhs); | ||
| 263 | Id EmitFPUnordGreaterThan64(EmitContext& ctx, Id lhs, Id rhs); | ||
| 264 | Id EmitFPOrdLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs); | ||
| 265 | Id EmitFPOrdLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs); | ||
| 266 | Id EmitFPOrdLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs); | ||
| 267 | Id EmitFPUnordLessThanEqual16(EmitContext& ctx, Id lhs, Id rhs); | ||
| 268 | Id EmitFPUnordLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs); | ||
| 269 | Id EmitFPUnordLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs); | ||
| 270 | Id EmitFPOrdGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs); | ||
| 271 | Id EmitFPOrdGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs); | ||
| 272 | Id EmitFPOrdGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs); | ||
| 273 | Id EmitFPUnordGreaterThanEqual16(EmitContext& ctx, Id lhs, Id rhs); | ||
| 274 | Id EmitFPUnordGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs); | ||
| 275 | Id EmitFPUnordGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs); | ||
| 276 | Id EmitFPIsNan16(EmitContext& ctx, Id value); | ||
| 277 | Id EmitFPIsNan32(EmitContext& ctx, Id value); | ||
| 278 | Id EmitFPIsNan64(EmitContext& ctx, Id value); | ||
| 279 | Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b); | ||
| 280 | Id EmitIAdd64(EmitContext& ctx, Id a, Id b); | ||
| 281 | Id EmitISub32(EmitContext& ctx, Id a, Id b); | ||
| 282 | Id EmitISub64(EmitContext& ctx, Id a, Id b); | ||
| 283 | Id EmitIMul32(EmitContext& ctx, Id a, Id b); | ||
| 284 | Id EmitINeg32(EmitContext& ctx, Id value); | ||
| 285 | Id EmitINeg64(EmitContext& ctx, Id value); | ||
| 286 | Id EmitIAbs32(EmitContext& ctx, Id value); | ||
| 287 | Id EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift); | ||
| 288 | Id EmitShiftLeftLogical64(EmitContext& ctx, Id base, Id shift); | ||
| 289 | Id EmitShiftRightLogical32(EmitContext& ctx, Id base, Id shift); | ||
| 290 | Id EmitShiftRightLogical64(EmitContext& ctx, Id base, Id shift); | ||
| 291 | Id EmitShiftRightArithmetic32(EmitContext& ctx, Id base, Id shift); | ||
| 292 | Id EmitShiftRightArithmetic64(EmitContext& ctx, Id base, Id shift); | ||
| 293 | Id EmitBitwiseAnd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b); | ||
| 294 | Id EmitBitwiseOr32(EmitContext& ctx, IR::Inst* inst, Id a, Id b); | ||
| 295 | Id EmitBitwiseXor32(EmitContext& ctx, IR::Inst* inst, Id a, Id b); | ||
| 296 | Id EmitBitFieldInsert(EmitContext& ctx, Id base, Id insert, Id offset, Id count); | ||
| 297 | Id EmitBitFieldSExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count); | ||
| 298 | Id EmitBitFieldUExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count); | ||
| 299 | Id EmitBitReverse32(EmitContext& ctx, Id value); | ||
| 300 | Id EmitBitCount32(EmitContext& ctx, Id value); | ||
| 301 | Id EmitBitwiseNot32(EmitContext& ctx, Id value); | ||
| 302 | Id EmitFindSMsb32(EmitContext& ctx, Id value); | ||
| 303 | Id EmitFindUMsb32(EmitContext& ctx, Id value); | ||
| 304 | Id EmitSMin32(EmitContext& ctx, Id a, Id b); | ||
| 305 | Id EmitUMin32(EmitContext& ctx, Id a, Id b); | ||
| 306 | Id EmitSMax32(EmitContext& ctx, Id a, Id b); | ||
| 307 | Id EmitUMax32(EmitContext& ctx, Id a, Id b); | ||
| 308 | Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max); | ||
| 309 | Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max); | ||
| 310 | Id EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs); | ||
| 311 | Id EmitULessThan(EmitContext& ctx, Id lhs, Id rhs); | ||
| 312 | Id EmitIEqual(EmitContext& ctx, Id lhs, Id rhs); | ||
| 313 | Id EmitSLessThanEqual(EmitContext& ctx, Id lhs, Id rhs); | ||
| 314 | Id EmitULessThanEqual(EmitContext& ctx, Id lhs, Id rhs); | ||
| 315 | Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs); | ||
| 316 | Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs); | ||
| 317 | Id EmitINotEqual(EmitContext& ctx, Id lhs, Id rhs); | ||
| 318 | Id EmitSGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs); | ||
| 319 | Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs); | ||
| 320 | Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id pointer_offset, Id value); | ||
| 321 | Id EmitSharedAtomicSMin32(EmitContext& ctx, Id pointer_offset, Id value); | ||
| 322 | Id EmitSharedAtomicUMin32(EmitContext& ctx, Id pointer_offset, Id value); | ||
| 323 | Id EmitSharedAtomicSMax32(EmitContext& ctx, Id pointer_offset, Id value); | ||
| 324 | Id EmitSharedAtomicUMax32(EmitContext& ctx, Id pointer_offset, Id value); | ||
| 325 | Id EmitSharedAtomicInc32(EmitContext& ctx, Id pointer_offset, Id value); | ||
| 326 | Id EmitSharedAtomicDec32(EmitContext& ctx, Id pointer_offset, Id value); | ||
| 327 | Id EmitSharedAtomicAnd32(EmitContext& ctx, Id pointer_offset, Id value); | ||
| 328 | Id EmitSharedAtomicOr32(EmitContext& ctx, Id pointer_offset, Id value); | ||
| 329 | Id EmitSharedAtomicXor32(EmitContext& ctx, Id pointer_offset, Id value); | ||
| 330 | Id EmitSharedAtomicExchange32(EmitContext& ctx, Id pointer_offset, Id value); | ||
| 331 | Id EmitSharedAtomicExchange64(EmitContext& ctx, Id pointer_offset, Id value); | ||
| 332 | Id EmitStorageAtomicIAdd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 333 | Id value); | ||
| 334 | Id EmitStorageAtomicSMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 335 | Id value); | ||
| 336 | Id EmitStorageAtomicUMin32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 337 | Id value); | ||
| 338 | Id EmitStorageAtomicSMax32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 339 | Id value); | ||
| 340 | Id EmitStorageAtomicUMax32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 341 | Id value); | ||
| 342 | Id EmitStorageAtomicInc32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 343 | Id value); | ||
| 344 | Id EmitStorageAtomicDec32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 345 | Id value); | ||
| 346 | Id EmitStorageAtomicAnd32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 347 | Id value); | ||
| 348 | Id EmitStorageAtomicOr32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 349 | Id value); | ||
| 350 | Id EmitStorageAtomicXor32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 351 | Id value); | ||
| 352 | Id EmitStorageAtomicExchange32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 353 | Id value); | ||
| 354 | Id EmitStorageAtomicIAdd64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 355 | Id value); | ||
| 356 | Id EmitStorageAtomicSMin64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 357 | Id value); | ||
| 358 | Id EmitStorageAtomicUMin64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 359 | Id value); | ||
| 360 | Id EmitStorageAtomicSMax64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 361 | Id value); | ||
| 362 | Id EmitStorageAtomicUMax64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 363 | Id value); | ||
| 364 | Id EmitStorageAtomicAnd64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 365 | Id value); | ||
| 366 | Id EmitStorageAtomicOr64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 367 | Id value); | ||
| 368 | Id EmitStorageAtomicXor64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 369 | Id value); | ||
| 370 | Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 371 | Id value); | ||
| 372 | Id EmitStorageAtomicAddF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 373 | Id value); | ||
| 374 | Id EmitStorageAtomicAddF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 375 | Id value); | ||
| 376 | Id EmitStorageAtomicAddF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 377 | Id value); | ||
| 378 | Id EmitStorageAtomicMinF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 379 | Id value); | ||
| 380 | Id EmitStorageAtomicMinF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 381 | Id value); | ||
| 382 | Id EmitStorageAtomicMaxF16x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 383 | Id value); | ||
| 384 | Id EmitStorageAtomicMaxF32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 385 | Id value); | ||
| 386 | Id EmitGlobalAtomicIAdd32(EmitContext& ctx); | ||
| 387 | Id EmitGlobalAtomicSMin32(EmitContext& ctx); | ||
| 388 | Id EmitGlobalAtomicUMin32(EmitContext& ctx); | ||
| 389 | Id EmitGlobalAtomicSMax32(EmitContext& ctx); | ||
| 390 | Id EmitGlobalAtomicUMax32(EmitContext& ctx); | ||
| 391 | Id EmitGlobalAtomicInc32(EmitContext& ctx); | ||
| 392 | Id EmitGlobalAtomicDec32(EmitContext& ctx); | ||
| 393 | Id EmitGlobalAtomicAnd32(EmitContext& ctx); | ||
| 394 | Id EmitGlobalAtomicOr32(EmitContext& ctx); | ||
| 395 | Id EmitGlobalAtomicXor32(EmitContext& ctx); | ||
| 396 | Id EmitGlobalAtomicExchange32(EmitContext& ctx); | ||
| 397 | Id EmitGlobalAtomicIAdd64(EmitContext& ctx); | ||
| 398 | Id EmitGlobalAtomicSMin64(EmitContext& ctx); | ||
| 399 | Id EmitGlobalAtomicUMin64(EmitContext& ctx); | ||
| 400 | Id EmitGlobalAtomicSMax64(EmitContext& ctx); | ||
| 401 | Id EmitGlobalAtomicUMax64(EmitContext& ctx); | ||
| 402 | Id EmitGlobalAtomicInc64(EmitContext& ctx); | ||
| 403 | Id EmitGlobalAtomicDec64(EmitContext& ctx); | ||
| 404 | Id EmitGlobalAtomicAnd64(EmitContext& ctx); | ||
| 405 | Id EmitGlobalAtomicOr64(EmitContext& ctx); | ||
| 406 | Id EmitGlobalAtomicXor64(EmitContext& ctx); | ||
| 407 | Id EmitGlobalAtomicExchange64(EmitContext& ctx); | ||
| 408 | Id EmitGlobalAtomicAddF32(EmitContext& ctx); | ||
| 409 | Id EmitGlobalAtomicAddF16x2(EmitContext& ctx); | ||
| 410 | Id EmitGlobalAtomicAddF32x2(EmitContext& ctx); | ||
| 411 | Id EmitGlobalAtomicMinF16x2(EmitContext& ctx); | ||
| 412 | Id EmitGlobalAtomicMinF32x2(EmitContext& ctx); | ||
| 413 | Id EmitGlobalAtomicMaxF16x2(EmitContext& ctx); | ||
| 414 | Id EmitGlobalAtomicMaxF32x2(EmitContext& ctx); | ||
| 415 | Id EmitLogicalOr(EmitContext& ctx, Id a, Id b); | ||
| 416 | Id EmitLogicalAnd(EmitContext& ctx, Id a, Id b); | ||
| 417 | Id EmitLogicalXor(EmitContext& ctx, Id a, Id b); | ||
| 418 | Id EmitLogicalNot(EmitContext& ctx, Id value); | ||
| 419 | Id EmitConvertS16F16(EmitContext& ctx, Id value); | ||
| 420 | Id EmitConvertS16F32(EmitContext& ctx, Id value); | ||
| 421 | Id EmitConvertS16F64(EmitContext& ctx, Id value); | ||
| 422 | Id EmitConvertS32F16(EmitContext& ctx, Id value); | ||
| 423 | Id EmitConvertS32F32(EmitContext& ctx, Id value); | ||
| 424 | Id EmitConvertS32F64(EmitContext& ctx, Id value); | ||
| 425 | Id EmitConvertS64F16(EmitContext& ctx, Id value); | ||
| 426 | Id EmitConvertS64F32(EmitContext& ctx, Id value); | ||
| 427 | Id EmitConvertS64F64(EmitContext& ctx, Id value); | ||
| 428 | Id EmitConvertU16F16(EmitContext& ctx, Id value); | ||
| 429 | Id EmitConvertU16F32(EmitContext& ctx, Id value); | ||
| 430 | Id EmitConvertU16F64(EmitContext& ctx, Id value); | ||
| 431 | Id EmitConvertU32F16(EmitContext& ctx, Id value); | ||
| 432 | Id EmitConvertU32F32(EmitContext& ctx, Id value); | ||
| 433 | Id EmitConvertU32F64(EmitContext& ctx, Id value); | ||
| 434 | Id EmitConvertU64F16(EmitContext& ctx, Id value); | ||
| 435 | Id EmitConvertU64F32(EmitContext& ctx, Id value); | ||
| 436 | Id EmitConvertU64F64(EmitContext& ctx, Id value); | ||
| 437 | Id EmitConvertU64U32(EmitContext& ctx, Id value); | ||
| 438 | Id EmitConvertU32U64(EmitContext& ctx, Id value); | ||
| 439 | Id EmitConvertF16F32(EmitContext& ctx, Id value); | ||
| 440 | Id EmitConvertF32F16(EmitContext& ctx, Id value); | ||
| 441 | Id EmitConvertF32F64(EmitContext& ctx, Id value); | ||
| 442 | Id EmitConvertF64F32(EmitContext& ctx, Id value); | ||
| 443 | Id EmitConvertF16S8(EmitContext& ctx, Id value); | ||
| 444 | Id EmitConvertF16S16(EmitContext& ctx, Id value); | ||
| 445 | Id EmitConvertF16S32(EmitContext& ctx, Id value); | ||
| 446 | Id EmitConvertF16S64(EmitContext& ctx, Id value); | ||
| 447 | Id EmitConvertF16U8(EmitContext& ctx, Id value); | ||
| 448 | Id EmitConvertF16U16(EmitContext& ctx, Id value); | ||
| 449 | Id EmitConvertF16U32(EmitContext& ctx, Id value); | ||
| 450 | Id EmitConvertF16U64(EmitContext& ctx, Id value); | ||
| 451 | Id EmitConvertF32S8(EmitContext& ctx, Id value); | ||
| 452 | Id EmitConvertF32S16(EmitContext& ctx, Id value); | ||
| 453 | Id EmitConvertF32S32(EmitContext& ctx, Id value); | ||
| 454 | Id EmitConvertF32S64(EmitContext& ctx, Id value); | ||
| 455 | Id EmitConvertF32U8(EmitContext& ctx, Id value); | ||
| 456 | Id EmitConvertF32U16(EmitContext& ctx, Id value); | ||
| 457 | Id EmitConvertF32U32(EmitContext& ctx, Id value); | ||
| 458 | Id EmitConvertF32U64(EmitContext& ctx, Id value); | ||
| 459 | Id EmitConvertF64S8(EmitContext& ctx, Id value); | ||
| 460 | Id EmitConvertF64S16(EmitContext& ctx, Id value); | ||
| 461 | Id EmitConvertF64S32(EmitContext& ctx, Id value); | ||
| 462 | Id EmitConvertF64S64(EmitContext& ctx, Id value); | ||
| 463 | Id EmitConvertF64U8(EmitContext& ctx, Id value); | ||
| 464 | Id EmitConvertF64U16(EmitContext& ctx, Id value); | ||
| 465 | Id EmitConvertF64U32(EmitContext& ctx, Id value); | ||
| 466 | Id EmitConvertF64U64(EmitContext& ctx, Id value); | ||
| 467 | Id EmitBindlessImageSampleImplicitLod(EmitContext&); | ||
| 468 | Id EmitBindlessImageSampleExplicitLod(EmitContext&); | ||
| 469 | Id EmitBindlessImageSampleDrefImplicitLod(EmitContext&); | ||
| 470 | Id EmitBindlessImageSampleDrefExplicitLod(EmitContext&); | ||
| 471 | Id EmitBindlessImageGather(EmitContext&); | ||
| 472 | Id EmitBindlessImageGatherDref(EmitContext&); | ||
| 473 | Id EmitBindlessImageFetch(EmitContext&); | ||
| 474 | Id EmitBindlessImageQueryDimensions(EmitContext&); | ||
| 475 | Id EmitBindlessImageQueryLod(EmitContext&); | ||
| 476 | Id EmitBindlessImageGradient(EmitContext&); | ||
| 477 | Id EmitBindlessImageRead(EmitContext&); | ||
| 478 | Id EmitBindlessImageWrite(EmitContext&); | ||
| 479 | Id EmitBoundImageSampleImplicitLod(EmitContext&); | ||
| 480 | Id EmitBoundImageSampleExplicitLod(EmitContext&); | ||
| 481 | Id EmitBoundImageSampleDrefImplicitLod(EmitContext&); | ||
| 482 | Id EmitBoundImageSampleDrefExplicitLod(EmitContext&); | ||
| 483 | Id EmitBoundImageGather(EmitContext&); | ||
| 484 | Id EmitBoundImageGatherDref(EmitContext&); | ||
| 485 | Id EmitBoundImageFetch(EmitContext&); | ||
| 486 | Id EmitBoundImageQueryDimensions(EmitContext&); | ||
| 487 | Id EmitBoundImageQueryLod(EmitContext&); | ||
| 488 | Id EmitBoundImageGradient(EmitContext&); | ||
| 489 | Id EmitBoundImageRead(EmitContext&); | ||
| 490 | Id EmitBoundImageWrite(EmitContext&); | ||
| 491 | Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 492 | Id bias_lc, const IR::Value& offset); | ||
| 493 | Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 494 | Id lod, const IR::Value& offset); | ||
| 495 | Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, | ||
| 496 | Id coords, Id dref, Id bias_lc, const IR::Value& offset); | ||
| 497 | Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, | ||
| 498 | Id coords, Id dref, Id lod, const IR::Value& offset); | ||
| 499 | Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 500 | const IR::Value& offset, const IR::Value& offset2); | ||
| 501 | Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 502 | const IR::Value& offset, const IR::Value& offset2, Id dref); | ||
| 503 | Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, | ||
| 504 | Id lod, Id ms); | ||
| 505 | Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod); | ||
| 506 | Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); | ||
| 507 | Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 508 | Id derivates, Id offset, Id lod_clamp); | ||
| 509 | Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); | ||
| 510 | void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color); | ||
| 511 | Id EmitBindlessImageAtomicIAdd32(EmitContext&); | ||
| 512 | Id EmitBindlessImageAtomicSMin32(EmitContext&); | ||
| 513 | Id EmitBindlessImageAtomicUMin32(EmitContext&); | ||
| 514 | Id EmitBindlessImageAtomicSMax32(EmitContext&); | ||
| 515 | Id EmitBindlessImageAtomicUMax32(EmitContext&); | ||
| 516 | Id EmitBindlessImageAtomicInc32(EmitContext&); | ||
| 517 | Id EmitBindlessImageAtomicDec32(EmitContext&); | ||
| 518 | Id EmitBindlessImageAtomicAnd32(EmitContext&); | ||
| 519 | Id EmitBindlessImageAtomicOr32(EmitContext&); | ||
| 520 | Id EmitBindlessImageAtomicXor32(EmitContext&); | ||
| 521 | Id EmitBindlessImageAtomicExchange32(EmitContext&); | ||
| 522 | Id EmitBoundImageAtomicIAdd32(EmitContext&); | ||
| 523 | Id EmitBoundImageAtomicSMin32(EmitContext&); | ||
| 524 | Id EmitBoundImageAtomicUMin32(EmitContext&); | ||
| 525 | Id EmitBoundImageAtomicSMax32(EmitContext&); | ||
| 526 | Id EmitBoundImageAtomicUMax32(EmitContext&); | ||
| 527 | Id EmitBoundImageAtomicInc32(EmitContext&); | ||
| 528 | Id EmitBoundImageAtomicDec32(EmitContext&); | ||
| 529 | Id EmitBoundImageAtomicAnd32(EmitContext&); | ||
| 530 | Id EmitBoundImageAtomicOr32(EmitContext&); | ||
| 531 | Id EmitBoundImageAtomicXor32(EmitContext&); | ||
| 532 | Id EmitBoundImageAtomicExchange32(EmitContext&); | ||
| 533 | Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 534 | Id value); | ||
| 535 | Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 536 | Id value); | ||
| 537 | Id EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 538 | Id value); | ||
| 539 | Id EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 540 | Id value); | ||
| 541 | Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 542 | Id value); | ||
| 543 | Id EmitImageAtomicInc32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 544 | Id value); | ||
| 545 | Id EmitImageAtomicDec32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 546 | Id value); | ||
| 547 | Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 548 | Id value); | ||
| 549 | Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 550 | Id value); | ||
| 551 | Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 552 | Id value); | ||
| 553 | Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | ||
| 554 | Id value); | ||
| 555 | Id EmitLaneId(EmitContext& ctx); | ||
| 556 | Id EmitVoteAll(EmitContext& ctx, Id pred); | ||
| 557 | Id EmitVoteAny(EmitContext& ctx, Id pred); | ||
| 558 | Id EmitVoteEqual(EmitContext& ctx, Id pred); | ||
| 559 | Id EmitSubgroupBallot(EmitContext& ctx, Id pred); | ||
| 560 | Id EmitSubgroupEqMask(EmitContext& ctx); | ||
| 561 | Id EmitSubgroupLtMask(EmitContext& ctx); | ||
| 562 | Id EmitSubgroupLeMask(EmitContext& ctx); | ||
| 563 | Id EmitSubgroupGtMask(EmitContext& ctx); | ||
| 564 | Id EmitSubgroupGeMask(EmitContext& ctx); | ||
| 565 | Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | ||
| 566 | Id segmentation_mask); | ||
| 567 | Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | ||
| 568 | Id segmentation_mask); | ||
| 569 | Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | ||
| 570 | Id segmentation_mask); | ||
| 571 | Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | ||
| 572 | Id segmentation_mask); | ||
| 573 | Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle); | ||
| 574 | Id EmitDPdxFine(EmitContext& ctx, Id op_a); | ||
| 575 | Id EmitDPdyFine(EmitContext& ctx, Id op_a); | ||
| 576 | Id EmitDPdxCoarse(EmitContext& ctx, Id op_a); | ||
| 577 | Id EmitDPdyCoarse(EmitContext& ctx, Id op_a); | ||
| 578 | |||
| 579 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp new file mode 100644 index 000000000..3501d7495 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp | |||
| @@ -0,0 +1,270 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 7 | |||
| 8 | namespace Shader::Backend::SPIRV { | ||
| 9 | namespace { | ||
| 10 | void SetZeroFlag(EmitContext& ctx, IR::Inst* inst, Id result) { | ||
| 11 | IR::Inst* const zero{inst->GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp)}; | ||
| 12 | if (!zero) { | ||
| 13 | return; | ||
| 14 | } | ||
| 15 | zero->SetDefinition(ctx.OpIEqual(ctx.U1, result, ctx.u32_zero_value)); | ||
| 16 | zero->Invalidate(); | ||
| 17 | } | ||
| 18 | |||
| 19 | void SetSignFlag(EmitContext& ctx, IR::Inst* inst, Id result) { | ||
| 20 | IR::Inst* const sign{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp)}; | ||
| 21 | if (!sign) { | ||
| 22 | return; | ||
| 23 | } | ||
| 24 | sign->SetDefinition(ctx.OpSLessThan(ctx.U1, result, ctx.u32_zero_value)); | ||
| 25 | sign->Invalidate(); | ||
| 26 | } | ||
| 27 | } // Anonymous namespace | ||
| 28 | |||
| 29 | Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | ||
| 30 | Id result{}; | ||
| 31 | if (IR::Inst* const carry{inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)}) { | ||
| 32 | const Id carry_type{ctx.TypeStruct(ctx.U32[1], ctx.U32[1])}; | ||
| 33 | const Id carry_result{ctx.OpIAddCarry(carry_type, a, b)}; | ||
| 34 | result = ctx.OpCompositeExtract(ctx.U32[1], carry_result, 0U); | ||
| 35 | |||
| 36 | const Id carry_value{ctx.OpCompositeExtract(ctx.U32[1], carry_result, 1U)}; | ||
| 37 | carry->SetDefinition(ctx.OpINotEqual(ctx.U1, carry_value, ctx.u32_zero_value)); | ||
| 38 | carry->Invalidate(); | ||
| 39 | } else { | ||
| 40 | result = ctx.OpIAdd(ctx.U32[1], a, b); | ||
| 41 | } | ||
| 42 | SetZeroFlag(ctx, inst, result); | ||
| 43 | SetSignFlag(ctx, inst, result); | ||
| 44 | if (IR::Inst * overflow{inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp)}) { | ||
| 45 | // https://stackoverflow.com/questions/55468823/how-to-detect-integer-overflow-in-c | ||
| 46 | constexpr u32 s32_max{static_cast<u32>(std::numeric_limits<s32>::max())}; | ||
| 47 | const Id is_positive{ctx.OpSGreaterThanEqual(ctx.U1, a, ctx.u32_zero_value)}; | ||
| 48 | const Id sub_a{ctx.OpISub(ctx.U32[1], ctx.Const(s32_max), a)}; | ||
| 49 | |||
| 50 | const Id positive_test{ctx.OpSGreaterThan(ctx.U1, b, sub_a)}; | ||
| 51 | const Id negative_test{ctx.OpSLessThan(ctx.U1, b, sub_a)}; | ||
| 52 | const Id carry_flag{ctx.OpSelect(ctx.U1, is_positive, positive_test, negative_test)}; | ||
| 53 | overflow->SetDefinition(carry_flag); | ||
| 54 | overflow->Invalidate(); | ||
| 55 | } | ||
| 56 | return result; | ||
| 57 | } | ||
| 58 | |||
| 59 | Id EmitIAdd64(EmitContext& ctx, Id a, Id b) { | ||
| 60 | return ctx.OpIAdd(ctx.U64, a, b); | ||
| 61 | } | ||
| 62 | |||
| 63 | Id EmitISub32(EmitContext& ctx, Id a, Id b) { | ||
| 64 | return ctx.OpISub(ctx.U32[1], a, b); | ||
| 65 | } | ||
| 66 | |||
| 67 | Id EmitISub64(EmitContext& ctx, Id a, Id b) { | ||
| 68 | return ctx.OpISub(ctx.U64, a, b); | ||
| 69 | } | ||
| 70 | |||
| 71 | Id EmitIMul32(EmitContext& ctx, Id a, Id b) { | ||
| 72 | return ctx.OpIMul(ctx.U32[1], a, b); | ||
| 73 | } | ||
| 74 | |||
| 75 | Id EmitINeg32(EmitContext& ctx, Id value) { | ||
| 76 | return ctx.OpSNegate(ctx.U32[1], value); | ||
| 77 | } | ||
| 78 | |||
| 79 | Id EmitINeg64(EmitContext& ctx, Id value) { | ||
| 80 | return ctx.OpSNegate(ctx.U64, value); | ||
| 81 | } | ||
| 82 | |||
| 83 | Id EmitIAbs32(EmitContext& ctx, Id value) { | ||
| 84 | return ctx.OpSAbs(ctx.U32[1], value); | ||
| 85 | } | ||
| 86 | |||
| 87 | Id EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift) { | ||
| 88 | return ctx.OpShiftLeftLogical(ctx.U32[1], base, shift); | ||
| 89 | } | ||
| 90 | |||
| 91 | Id EmitShiftLeftLogical64(EmitContext& ctx, Id base, Id shift) { | ||
| 92 | return ctx.OpShiftLeftLogical(ctx.U64, base, shift); | ||
| 93 | } | ||
| 94 | |||
| 95 | Id EmitShiftRightLogical32(EmitContext& ctx, Id base, Id shift) { | ||
| 96 | return ctx.OpShiftRightLogical(ctx.U32[1], base, shift); | ||
| 97 | } | ||
| 98 | |||
| 99 | Id EmitShiftRightLogical64(EmitContext& ctx, Id base, Id shift) { | ||
| 100 | return ctx.OpShiftRightLogical(ctx.U64, base, shift); | ||
| 101 | } | ||
| 102 | |||
| 103 | Id EmitShiftRightArithmetic32(EmitContext& ctx, Id base, Id shift) { | ||
| 104 | return ctx.OpShiftRightArithmetic(ctx.U32[1], base, shift); | ||
| 105 | } | ||
| 106 | |||
| 107 | Id EmitShiftRightArithmetic64(EmitContext& ctx, Id base, Id shift) { | ||
| 108 | return ctx.OpShiftRightArithmetic(ctx.U64, base, shift); | ||
| 109 | } | ||
| 110 | |||
| 111 | Id EmitBitwiseAnd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | ||
| 112 | const Id result{ctx.OpBitwiseAnd(ctx.U32[1], a, b)}; | ||
| 113 | SetZeroFlag(ctx, inst, result); | ||
| 114 | SetSignFlag(ctx, inst, result); | ||
| 115 | return result; | ||
| 116 | } | ||
| 117 | |||
| 118 | Id EmitBitwiseOr32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | ||
| 119 | const Id result{ctx.OpBitwiseOr(ctx.U32[1], a, b)}; | ||
| 120 | SetZeroFlag(ctx, inst, result); | ||
| 121 | SetSignFlag(ctx, inst, result); | ||
| 122 | return result; | ||
| 123 | } | ||
| 124 | |||
| 125 | Id EmitBitwiseXor32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { | ||
| 126 | const Id result{ctx.OpBitwiseXor(ctx.U32[1], a, b)}; | ||
| 127 | SetZeroFlag(ctx, inst, result); | ||
| 128 | SetSignFlag(ctx, inst, result); | ||
| 129 | return result; | ||
| 130 | } | ||
| 131 | |||
| 132 | Id EmitBitFieldInsert(EmitContext& ctx, Id base, Id insert, Id offset, Id count) { | ||
| 133 | return ctx.OpBitFieldInsert(ctx.U32[1], base, insert, offset, count); | ||
| 134 | } | ||
| 135 | |||
| 136 | Id EmitBitFieldSExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count) { | ||
| 137 | const Id result{ctx.OpBitFieldSExtract(ctx.U32[1], base, offset, count)}; | ||
| 138 | SetZeroFlag(ctx, inst, result); | ||
| 139 | SetSignFlag(ctx, inst, result); | ||
| 140 | return result; | ||
| 141 | } | ||
| 142 | |||
| 143 | Id EmitBitFieldUExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count) { | ||
| 144 | const Id result{ctx.OpBitFieldUExtract(ctx.U32[1], base, offset, count)}; | ||
| 145 | SetZeroFlag(ctx, inst, result); | ||
| 146 | SetSignFlag(ctx, inst, result); | ||
| 147 | return result; | ||
| 148 | } | ||
| 149 | |||
| 150 | Id EmitBitReverse32(EmitContext& ctx, Id value) { | ||
| 151 | return ctx.OpBitReverse(ctx.U32[1], value); | ||
| 152 | } | ||
| 153 | |||
| 154 | Id EmitBitCount32(EmitContext& ctx, Id value) { | ||
| 155 | return ctx.OpBitCount(ctx.U32[1], value); | ||
| 156 | } | ||
| 157 | |||
| 158 | Id EmitBitwiseNot32(EmitContext& ctx, Id value) { | ||
| 159 | return ctx.OpNot(ctx.U32[1], value); | ||
| 160 | } | ||
| 161 | |||
| 162 | Id EmitFindSMsb32(EmitContext& ctx, Id value) { | ||
| 163 | return ctx.OpFindSMsb(ctx.U32[1], value); | ||
| 164 | } | ||
| 165 | |||
| 166 | Id EmitFindUMsb32(EmitContext& ctx, Id value) { | ||
| 167 | return ctx.OpFindUMsb(ctx.U32[1], value); | ||
| 168 | } | ||
| 169 | |||
| 170 | Id EmitSMin32(EmitContext& ctx, Id a, Id b) { | ||
| 171 | const bool is_broken{ctx.profile.has_broken_signed_operations}; | ||
| 172 | if (is_broken) { | ||
| 173 | a = ctx.OpBitcast(ctx.S32[1], a); | ||
| 174 | b = ctx.OpBitcast(ctx.S32[1], b); | ||
| 175 | } | ||
| 176 | const Id result{ctx.OpSMin(ctx.U32[1], a, b)}; | ||
| 177 | return is_broken ? ctx.OpBitcast(ctx.U32[1], result) : result; | ||
| 178 | } | ||
| 179 | |||
| 180 | Id EmitUMin32(EmitContext& ctx, Id a, Id b) { | ||
| 181 | return ctx.OpUMin(ctx.U32[1], a, b); | ||
| 182 | } | ||
| 183 | |||
| 184 | Id EmitSMax32(EmitContext& ctx, Id a, Id b) { | ||
| 185 | const bool is_broken{ctx.profile.has_broken_signed_operations}; | ||
| 186 | if (is_broken) { | ||
| 187 | a = ctx.OpBitcast(ctx.S32[1], a); | ||
| 188 | b = ctx.OpBitcast(ctx.S32[1], b); | ||
| 189 | } | ||
| 190 | const Id result{ctx.OpSMax(ctx.U32[1], a, b)}; | ||
| 191 | return is_broken ? ctx.OpBitcast(ctx.U32[1], result) : result; | ||
| 192 | } | ||
| 193 | |||
| 194 | Id EmitUMax32(EmitContext& ctx, Id a, Id b) { | ||
| 195 | return ctx.OpUMax(ctx.U32[1], a, b); | ||
| 196 | } | ||
| 197 | |||
| 198 | Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) { | ||
| 199 | Id result{}; | ||
| 200 | if (ctx.profile.has_broken_signed_operations || ctx.profile.has_broken_spirv_clamp) { | ||
| 201 | value = ctx.OpBitcast(ctx.S32[1], value); | ||
| 202 | min = ctx.OpBitcast(ctx.S32[1], min); | ||
| 203 | max = ctx.OpBitcast(ctx.S32[1], max); | ||
| 204 | if (ctx.profile.has_broken_spirv_clamp) { | ||
| 205 | result = ctx.OpSMax(ctx.S32[1], ctx.OpSMin(ctx.S32[1], value, max), min); | ||
| 206 | } else { | ||
| 207 | result = ctx.OpSClamp(ctx.S32[1], value, min, max); | ||
| 208 | } | ||
| 209 | result = ctx.OpBitcast(ctx.U32[1], result); | ||
| 210 | } else { | ||
| 211 | result = ctx.OpSClamp(ctx.U32[1], value, min, max); | ||
| 212 | } | ||
| 213 | SetZeroFlag(ctx, inst, result); | ||
| 214 | SetSignFlag(ctx, inst, result); | ||
| 215 | return result; | ||
| 216 | } | ||
| 217 | |||
| 218 | Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) { | ||
| 219 | Id result{}; | ||
| 220 | if (ctx.profile.has_broken_spirv_clamp) { | ||
| 221 | result = ctx.OpUMax(ctx.U32[1], ctx.OpUMin(ctx.U32[1], value, max), min); | ||
| 222 | } else { | ||
| 223 | result = ctx.OpUClamp(ctx.U32[1], value, min, max); | ||
| 224 | } | ||
| 225 | SetZeroFlag(ctx, inst, result); | ||
| 226 | SetSignFlag(ctx, inst, result); | ||
| 227 | return result; | ||
| 228 | } | ||
| 229 | |||
| 230 | Id EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 231 | return ctx.OpSLessThan(ctx.U1, lhs, rhs); | ||
| 232 | } | ||
| 233 | |||
| 234 | Id EmitULessThan(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 235 | return ctx.OpULessThan(ctx.U1, lhs, rhs); | ||
| 236 | } | ||
| 237 | |||
| 238 | Id EmitIEqual(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 239 | return ctx.OpIEqual(ctx.U1, lhs, rhs); | ||
| 240 | } | ||
| 241 | |||
| 242 | Id EmitSLessThanEqual(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 243 | return ctx.OpSLessThanEqual(ctx.U1, lhs, rhs); | ||
| 244 | } | ||
| 245 | |||
| 246 | Id EmitULessThanEqual(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 247 | return ctx.OpULessThanEqual(ctx.U1, lhs, rhs); | ||
| 248 | } | ||
| 249 | |||
| 250 | Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 251 | return ctx.OpSGreaterThan(ctx.U1, lhs, rhs); | ||
| 252 | } | ||
| 253 | |||
| 254 | Id EmitUGreaterThan(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 255 | return ctx.OpUGreaterThan(ctx.U1, lhs, rhs); | ||
| 256 | } | ||
| 257 | |||
| 258 | Id EmitINotEqual(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 259 | return ctx.OpINotEqual(ctx.U1, lhs, rhs); | ||
| 260 | } | ||
| 261 | |||
| 262 | Id EmitSGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 263 | return ctx.OpSGreaterThanEqual(ctx.U1, lhs, rhs); | ||
| 264 | } | ||
| 265 | |||
| 266 | Id EmitUGreaterThanEqual(EmitContext& ctx, Id lhs, Id rhs) { | ||
| 267 | return ctx.OpUGreaterThanEqual(ctx.U1, lhs, rhs); | ||
| 268 | } | ||
| 269 | |||
| 270 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp new file mode 100644 index 000000000..b9a9500fc --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_logical.cpp | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 7 | |||
| 8 | namespace Shader::Backend::SPIRV { | ||
| 9 | |||
| 10 | Id EmitLogicalOr(EmitContext& ctx, Id a, Id b) { | ||
| 11 | return ctx.OpLogicalOr(ctx.U1, a, b); | ||
| 12 | } | ||
| 13 | |||
| 14 | Id EmitLogicalAnd(EmitContext& ctx, Id a, Id b) { | ||
| 15 | return ctx.OpLogicalAnd(ctx.U1, a, b); | ||
| 16 | } | ||
| 17 | |||
| 18 | Id EmitLogicalXor(EmitContext& ctx, Id a, Id b) { | ||
| 19 | return ctx.OpLogicalNotEqual(ctx.U1, a, b); | ||
| 20 | } | ||
| 21 | |||
| 22 | Id EmitLogicalNot(EmitContext& ctx, Id value) { | ||
| 23 | return ctx.OpLogicalNot(ctx.U1, value); | ||
| 24 | } | ||
| 25 | |||
| 26 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp new file mode 100644 index 000000000..679ee2684 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp | |||
| @@ -0,0 +1,275 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <bit> | ||
| 6 | |||
| 7 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 8 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 9 | |||
| 10 | namespace Shader::Backend::SPIRV { | ||
| 11 | namespace { | ||
| 12 | Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size, | ||
| 13 | u32 index_offset = 0) { | ||
| 14 | if (offset.IsImmediate()) { | ||
| 15 | const u32 imm_offset{static_cast<u32>(offset.U32() / element_size) + index_offset}; | ||
| 16 | return ctx.Const(imm_offset); | ||
| 17 | } | ||
| 18 | const u32 shift{static_cast<u32>(std::countr_zero(element_size))}; | ||
| 19 | Id index{ctx.Def(offset)}; | ||
| 20 | if (shift != 0) { | ||
| 21 | const Id shift_id{ctx.Const(shift)}; | ||
| 22 | index = ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id); | ||
| 23 | } | ||
| 24 | if (index_offset != 0) { | ||
| 25 | index = ctx.OpIAdd(ctx.U32[1], index, ctx.Const(index_offset)); | ||
| 26 | } | ||
| 27 | return index; | ||
| 28 | } | ||
| 29 | |||
| 30 | Id StoragePointer(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 31 | const StorageTypeDefinition& type_def, size_t element_size, | ||
| 32 | Id StorageDefinitions::*member_ptr, u32 index_offset = 0) { | ||
| 33 | if (!binding.IsImmediate()) { | ||
| 34 | throw NotImplementedException("Dynamic storage buffer indexing"); | ||
| 35 | } | ||
| 36 | const Id ssbo{ctx.ssbos[binding.U32()].*member_ptr}; | ||
| 37 | const Id index{StorageIndex(ctx, offset, element_size, index_offset)}; | ||
| 38 | return ctx.OpAccessChain(type_def.element, ssbo, ctx.u32_zero_value, index); | ||
| 39 | } | ||
| 40 | |||
| 41 | Id LoadStorage(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id result_type, | ||
| 42 | const StorageTypeDefinition& type_def, size_t element_size, | ||
| 43 | Id StorageDefinitions::*member_ptr, u32 index_offset = 0) { | ||
| 44 | const Id pointer{ | ||
| 45 | StoragePointer(ctx, binding, offset, type_def, element_size, member_ptr, index_offset)}; | ||
| 46 | return ctx.OpLoad(result_type, pointer); | ||
| 47 | } | ||
| 48 | |||
| 49 | Id LoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 50 | u32 index_offset = 0) { | ||
| 51 | return LoadStorage(ctx, binding, offset, ctx.U32[1], ctx.storage_types.U32, sizeof(u32), | ||
| 52 | &StorageDefinitions::U32, index_offset); | ||
| 53 | } | ||
| 54 | |||
| 55 | void WriteStorage(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value, | ||
| 56 | const StorageTypeDefinition& type_def, size_t element_size, | ||
| 57 | Id StorageDefinitions::*member_ptr, u32 index_offset = 0) { | ||
| 58 | const Id pointer{ | ||
| 59 | StoragePointer(ctx, binding, offset, type_def, element_size, member_ptr, index_offset)}; | ||
| 60 | ctx.OpStore(pointer, value); | ||
| 61 | } | ||
| 62 | |||
| 63 | void WriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value, | ||
| 64 | u32 index_offset = 0) { | ||
| 65 | WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32, sizeof(u32), | ||
| 66 | &StorageDefinitions::U32, index_offset); | ||
| 67 | } | ||
| 68 | } // Anonymous namespace | ||
| 69 | |||
| 70 | void EmitLoadGlobalU8(EmitContext&) { | ||
| 71 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 72 | } | ||
| 73 | |||
| 74 | void EmitLoadGlobalS8(EmitContext&) { | ||
| 75 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 76 | } | ||
| 77 | |||
| 78 | void EmitLoadGlobalU16(EmitContext&) { | ||
| 79 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 80 | } | ||
| 81 | |||
| 82 | void EmitLoadGlobalS16(EmitContext&) { | ||
| 83 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 84 | } | ||
| 85 | |||
| 86 | Id EmitLoadGlobal32(EmitContext& ctx, Id address) { | ||
| 87 | if (ctx.profile.support_int64) { | ||
| 88 | return ctx.OpFunctionCall(ctx.U32[1], ctx.load_global_func_u32, address); | ||
| 89 | } | ||
| 90 | LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation"); | ||
| 91 | return ctx.Const(0u); | ||
| 92 | } | ||
| 93 | |||
| 94 | Id EmitLoadGlobal64(EmitContext& ctx, Id address) { | ||
| 95 | if (ctx.profile.support_int64) { | ||
| 96 | return ctx.OpFunctionCall(ctx.U32[2], ctx.load_global_func_u32x2, address); | ||
| 97 | } | ||
| 98 | LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation"); | ||
| 99 | return ctx.Const(0u, 0u); | ||
| 100 | } | ||
| 101 | |||
| 102 | Id EmitLoadGlobal128(EmitContext& ctx, Id address) { | ||
| 103 | if (ctx.profile.support_int64) { | ||
| 104 | return ctx.OpFunctionCall(ctx.U32[4], ctx.load_global_func_u32x4, address); | ||
| 105 | } | ||
| 106 | LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation"); | ||
| 107 | return ctx.Const(0u, 0u, 0u, 0u); | ||
| 108 | } | ||
| 109 | |||
| 110 | void EmitWriteGlobalU8(EmitContext&) { | ||
| 111 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 112 | } | ||
| 113 | |||
| 114 | void EmitWriteGlobalS8(EmitContext&) { | ||
| 115 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 116 | } | ||
| 117 | |||
| 118 | void EmitWriteGlobalU16(EmitContext&) { | ||
| 119 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 120 | } | ||
| 121 | |||
| 122 | void EmitWriteGlobalS16(EmitContext&) { | ||
| 123 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 124 | } | ||
| 125 | |||
| 126 | void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value) { | ||
| 127 | if (ctx.profile.support_int64) { | ||
| 128 | ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32, address, value); | ||
| 129 | return; | ||
| 130 | } | ||
| 131 | LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation"); | ||
| 132 | } | ||
| 133 | |||
| 134 | void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value) { | ||
| 135 | if (ctx.profile.support_int64) { | ||
| 136 | ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x2, address, value); | ||
| 137 | return; | ||
| 138 | } | ||
| 139 | LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation"); | ||
| 140 | } | ||
| 141 | |||
| 142 | void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value) { | ||
| 143 | if (ctx.profile.support_int64) { | ||
| 144 | ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x4, address, value); | ||
| 145 | return; | ||
| 146 | } | ||
| 147 | LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation"); | ||
| 148 | } | ||
| 149 | |||
| 150 | Id EmitLoadStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 151 | if (ctx.profile.support_int8 && ctx.profile.support_descriptor_aliasing) { | ||
| 152 | return ctx.OpUConvert(ctx.U32[1], | ||
| 153 | LoadStorage(ctx, binding, offset, ctx.U8, ctx.storage_types.U8, | ||
| 154 | sizeof(u8), &StorageDefinitions::U8)); | ||
| 155 | } else { | ||
| 156 | return ctx.OpBitFieldUExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset), | ||
| 157 | ctx.BitOffset8(offset), ctx.Const(8u)); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | Id EmitLoadStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 162 | if (ctx.profile.support_int8 && ctx.profile.support_descriptor_aliasing) { | ||
| 163 | return ctx.OpSConvert(ctx.U32[1], | ||
| 164 | LoadStorage(ctx, binding, offset, ctx.S8, ctx.storage_types.S8, | ||
| 165 | sizeof(s8), &StorageDefinitions::S8)); | ||
| 166 | } else { | ||
| 167 | return ctx.OpBitFieldSExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset), | ||
| 168 | ctx.BitOffset8(offset), ctx.Const(8u)); | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | Id EmitLoadStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 173 | if (ctx.profile.support_int16 && ctx.profile.support_descriptor_aliasing) { | ||
| 174 | return ctx.OpUConvert(ctx.U32[1], | ||
| 175 | LoadStorage(ctx, binding, offset, ctx.U16, ctx.storage_types.U16, | ||
| 176 | sizeof(u16), &StorageDefinitions::U16)); | ||
| 177 | } else { | ||
| 178 | return ctx.OpBitFieldUExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset), | ||
| 179 | ctx.BitOffset16(offset), ctx.Const(16u)); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | Id EmitLoadStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 184 | if (ctx.profile.support_int16 && ctx.profile.support_descriptor_aliasing) { | ||
| 185 | return ctx.OpSConvert(ctx.U32[1], | ||
| 186 | LoadStorage(ctx, binding, offset, ctx.S16, ctx.storage_types.S16, | ||
| 187 | sizeof(s16), &StorageDefinitions::S16)); | ||
| 188 | } else { | ||
| 189 | return ctx.OpBitFieldSExtract(ctx.U32[1], LoadStorage32(ctx, binding, offset), | ||
| 190 | ctx.BitOffset16(offset), ctx.Const(16u)); | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 195 | return LoadStorage32(ctx, binding, offset); | ||
| 196 | } | ||
| 197 | |||
| 198 | Id EmitLoadStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 199 | if (ctx.profile.support_descriptor_aliasing) { | ||
| 200 | return LoadStorage(ctx, binding, offset, ctx.U32[2], ctx.storage_types.U32x2, | ||
| 201 | sizeof(u32[2]), &StorageDefinitions::U32x2); | ||
| 202 | } else { | ||
| 203 | return ctx.OpCompositeConstruct(ctx.U32[2], LoadStorage32(ctx, binding, offset, 0), | ||
| 204 | LoadStorage32(ctx, binding, offset, 1)); | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||
| 209 | if (ctx.profile.support_descriptor_aliasing) { | ||
| 210 | return LoadStorage(ctx, binding, offset, ctx.U32[4], ctx.storage_types.U32x4, | ||
| 211 | sizeof(u32[4]), &StorageDefinitions::U32x4); | ||
| 212 | } else { | ||
| 213 | return ctx.OpCompositeConstruct(ctx.U32[4], LoadStorage32(ctx, binding, offset, 0), | ||
| 214 | LoadStorage32(ctx, binding, offset, 1), | ||
| 215 | LoadStorage32(ctx, binding, offset, 2), | ||
| 216 | LoadStorage32(ctx, binding, offset, 3)); | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 221 | Id value) { | ||
| 222 | WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U8, value), ctx.storage_types.U8, | ||
| 223 | sizeof(u8), &StorageDefinitions::U8); | ||
| 224 | } | ||
| 225 | |||
| 226 | void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 227 | Id value) { | ||
| 228 | WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S8, value), ctx.storage_types.S8, | ||
| 229 | sizeof(s8), &StorageDefinitions::S8); | ||
| 230 | } | ||
| 231 | |||
| 232 | void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 233 | Id value) { | ||
| 234 | WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.U16, value), ctx.storage_types.U16, | ||
| 235 | sizeof(u16), &StorageDefinitions::U16); | ||
| 236 | } | ||
| 237 | |||
| 238 | void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 239 | Id value) { | ||
| 240 | WriteStorage(ctx, binding, offset, ctx.OpSConvert(ctx.S16, value), ctx.storage_types.S16, | ||
| 241 | sizeof(s16), &StorageDefinitions::S16); | ||
| 242 | } | ||
| 243 | |||
| 244 | void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 245 | Id value) { | ||
| 246 | WriteStorage32(ctx, binding, offset, value); | ||
| 247 | } | ||
| 248 | |||
| 249 | void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 250 | Id value) { | ||
| 251 | if (ctx.profile.support_descriptor_aliasing) { | ||
| 252 | WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32x2, sizeof(u32[2]), | ||
| 253 | &StorageDefinitions::U32x2); | ||
| 254 | } else { | ||
| 255 | for (u32 index = 0; index < 2; ++index) { | ||
| 256 | const Id element{ctx.OpCompositeExtract(ctx.U32[1], value, index)}; | ||
| 257 | WriteStorage32(ctx, binding, offset, element, index); | ||
| 258 | } | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 263 | Id value) { | ||
| 264 | if (ctx.profile.support_descriptor_aliasing) { | ||
| 265 | WriteStorage(ctx, binding, offset, value, ctx.storage_types.U32x4, sizeof(u32[4]), | ||
| 266 | &StorageDefinitions::U32x4); | ||
| 267 | } else { | ||
| 268 | for (u32 index = 0; index < 4; ++index) { | ||
| 269 | const Id element{ctx.OpCompositeExtract(ctx.U32[1], value, index)}; | ||
| 270 | WriteStorage32(ctx, binding, offset, element, index); | ||
| 271 | } | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp new file mode 100644 index 000000000..c5b4f4720 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_select.cpp | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 7 | |||
| 8 | namespace Shader::Backend::SPIRV { | ||
| 9 | |||
| 10 | Id EmitSelectU1(EmitContext& ctx, Id cond, Id true_value, Id false_value) { | ||
| 11 | return ctx.OpSelect(ctx.U1, cond, true_value, false_value); | ||
| 12 | } | ||
| 13 | |||
| 14 | Id EmitSelectU8(EmitContext&, Id, Id, Id) { | ||
| 15 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 16 | } | ||
| 17 | |||
| 18 | Id EmitSelectU16(EmitContext& ctx, Id cond, Id true_value, Id false_value) { | ||
| 19 | return ctx.OpSelect(ctx.U16, cond, true_value, false_value); | ||
| 20 | } | ||
| 21 | |||
| 22 | Id EmitSelectU32(EmitContext& ctx, Id cond, Id true_value, Id false_value) { | ||
| 23 | return ctx.OpSelect(ctx.U32[1], cond, true_value, false_value); | ||
| 24 | } | ||
| 25 | |||
| 26 | Id EmitSelectU64(EmitContext& ctx, Id cond, Id true_value, Id false_value) { | ||
| 27 | return ctx.OpSelect(ctx.U64, cond, true_value, false_value); | ||
| 28 | } | ||
| 29 | |||
| 30 | Id EmitSelectF16(EmitContext& ctx, Id cond, Id true_value, Id false_value) { | ||
| 31 | return ctx.OpSelect(ctx.F16[1], cond, true_value, false_value); | ||
| 32 | } | ||
| 33 | |||
| 34 | Id EmitSelectF32(EmitContext& ctx, Id cond, Id true_value, Id false_value) { | ||
| 35 | return ctx.OpSelect(ctx.F32[1], cond, true_value, false_value); | ||
| 36 | } | ||
| 37 | |||
| 38 | Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value) { | ||
| 39 | return ctx.OpSelect(ctx.F64[1], cond, true_value, false_value); | ||
| 40 | } | ||
| 41 | |||
| 42 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp new file mode 100644 index 000000000..9a79fc7a2 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp | |||
| @@ -0,0 +1,174 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 7 | |||
| 8 | namespace Shader::Backend::SPIRV { | ||
| 9 | namespace { | ||
| 10 | Id Pointer(EmitContext& ctx, Id pointer_type, Id array, Id offset, u32 shift) { | ||
| 11 | const Id shift_id{ctx.Const(shift)}; | ||
| 12 | const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)}; | ||
| 13 | return ctx.OpAccessChain(pointer_type, array, ctx.u32_zero_value, index); | ||
| 14 | } | ||
| 15 | |||
| 16 | Id Word(EmitContext& ctx, Id offset) { | ||
| 17 | const Id shift_id{ctx.Const(2U)}; | ||
| 18 | const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)}; | ||
| 19 | const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)}; | ||
| 20 | return ctx.OpLoad(ctx.U32[1], pointer); | ||
| 21 | } | ||
| 22 | |||
| 23 | std::pair<Id, Id> ExtractArgs(EmitContext& ctx, Id offset, u32 mask, u32 count) { | ||
| 24 | const Id shift{ctx.OpShiftLeftLogical(ctx.U32[1], offset, ctx.Const(3U))}; | ||
| 25 | const Id bit{ctx.OpBitwiseAnd(ctx.U32[1], shift, ctx.Const(mask))}; | ||
| 26 | const Id count_id{ctx.Const(count)}; | ||
| 27 | return {bit, count_id}; | ||
| 28 | } | ||
| 29 | } // Anonymous namespace | ||
| 30 | |||
| 31 | Id EmitLoadSharedU8(EmitContext& ctx, Id offset) { | ||
| 32 | if (ctx.profile.support_explicit_workgroup_layout) { | ||
| 33 | const Id pointer{ | ||
| 34 | ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)}; | ||
| 35 | return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer)); | ||
| 36 | } else { | ||
| 37 | const auto [bit, count]{ExtractArgs(ctx, offset, 24, 8)}; | ||
| 38 | return ctx.OpBitFieldUExtract(ctx.U32[1], Word(ctx, offset), bit, count); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | Id EmitLoadSharedS8(EmitContext& ctx, Id offset) { | ||
| 43 | if (ctx.profile.support_explicit_workgroup_layout) { | ||
| 44 | const Id pointer{ | ||
| 45 | ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)}; | ||
| 46 | return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer)); | ||
| 47 | } else { | ||
| 48 | const auto [bit, count]{ExtractArgs(ctx, offset, 24, 8)}; | ||
| 49 | return ctx.OpBitFieldSExtract(ctx.U32[1], Word(ctx, offset), bit, count); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | Id EmitLoadSharedU16(EmitContext& ctx, Id offset) { | ||
| 54 | if (ctx.profile.support_explicit_workgroup_layout) { | ||
| 55 | const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)}; | ||
| 56 | return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer)); | ||
| 57 | } else { | ||
| 58 | const auto [bit, count]{ExtractArgs(ctx, offset, 16, 16)}; | ||
| 59 | return ctx.OpBitFieldUExtract(ctx.U32[1], Word(ctx, offset), bit, count); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | Id EmitLoadSharedS16(EmitContext& ctx, Id offset) { | ||
| 64 | if (ctx.profile.support_explicit_workgroup_layout) { | ||
| 65 | const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)}; | ||
| 66 | return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer)); | ||
| 67 | } else { | ||
| 68 | const auto [bit, count]{ExtractArgs(ctx, offset, 16, 16)}; | ||
| 69 | return ctx.OpBitFieldSExtract(ctx.U32[1], Word(ctx, offset), bit, count); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | Id EmitLoadSharedU32(EmitContext& ctx, Id offset) { | ||
| 74 | if (ctx.profile.support_explicit_workgroup_layout) { | ||
| 75 | const Id pointer{Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2)}; | ||
| 76 | return ctx.OpLoad(ctx.U32[1], pointer); | ||
| 77 | } else { | ||
| 78 | return Word(ctx, offset); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | Id EmitLoadSharedU64(EmitContext& ctx, Id offset) { | ||
| 83 | if (ctx.profile.support_explicit_workgroup_layout) { | ||
| 84 | const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)}; | ||
| 85 | return ctx.OpLoad(ctx.U32[2], pointer); | ||
| 86 | } else { | ||
| 87 | const Id shift_id{ctx.Const(2U)}; | ||
| 88 | const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)}; | ||
| 89 | const Id next_index{ctx.OpIAdd(ctx.U32[1], base_index, ctx.Const(1U))}; | ||
| 90 | const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, base_index)}; | ||
| 91 | const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, next_index)}; | ||
| 92 | return ctx.OpCompositeConstruct(ctx.U32[2], ctx.OpLoad(ctx.U32[1], lhs_pointer), | ||
| 93 | ctx.OpLoad(ctx.U32[1], rhs_pointer)); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | Id EmitLoadSharedU128(EmitContext& ctx, Id offset) { | ||
| 98 | if (ctx.profile.support_explicit_workgroup_layout) { | ||
| 99 | const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)}; | ||
| 100 | return ctx.OpLoad(ctx.U32[4], pointer); | ||
| 101 | } | ||
| 102 | const Id shift_id{ctx.Const(2U)}; | ||
| 103 | const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)}; | ||
| 104 | std::array<Id, 4> values{}; | ||
| 105 | for (u32 i = 0; i < 4; ++i) { | ||
| 106 | const Id index{i == 0 ? base_index : ctx.OpIAdd(ctx.U32[1], base_index, ctx.Const(i))}; | ||
| 107 | const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)}; | ||
| 108 | values[i] = ctx.OpLoad(ctx.U32[1], pointer); | ||
| 109 | } | ||
| 110 | return ctx.OpCompositeConstruct(ctx.U32[4], values); | ||
| 111 | } | ||
| 112 | |||
| 113 | void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) { | ||
| 114 | if (ctx.profile.support_explicit_workgroup_layout) { | ||
| 115 | const Id pointer{ | ||
| 116 | ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)}; | ||
| 117 | ctx.OpStore(pointer, ctx.OpUConvert(ctx.U8, value)); | ||
| 118 | } else { | ||
| 119 | ctx.OpFunctionCall(ctx.void_id, ctx.shared_store_u8_func, offset, value); | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) { | ||
| 124 | if (ctx.profile.support_explicit_workgroup_layout) { | ||
| 125 | const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)}; | ||
| 126 | ctx.OpStore(pointer, ctx.OpUConvert(ctx.U16, value)); | ||
| 127 | } else { | ||
| 128 | ctx.OpFunctionCall(ctx.void_id, ctx.shared_store_u16_func, offset, value); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) { | ||
| 133 | Id pointer{}; | ||
| 134 | if (ctx.profile.support_explicit_workgroup_layout) { | ||
| 135 | pointer = Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2); | ||
| 136 | } else { | ||
| 137 | const Id shift{ctx.Const(2U)}; | ||
| 138 | const Id word_offset{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)}; | ||
| 139 | pointer = ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, word_offset); | ||
| 140 | } | ||
| 141 | ctx.OpStore(pointer, value); | ||
| 142 | } | ||
| 143 | |||
| 144 | void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) { | ||
| 145 | if (ctx.profile.support_explicit_workgroup_layout) { | ||
| 146 | const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)}; | ||
| 147 | ctx.OpStore(pointer, value); | ||
| 148 | return; | ||
| 149 | } | ||
| 150 | const Id shift{ctx.Const(2U)}; | ||
| 151 | const Id word_offset{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)}; | ||
| 152 | const Id next_offset{ctx.OpIAdd(ctx.U32[1], word_offset, ctx.Const(1U))}; | ||
| 153 | const Id lhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, word_offset)}; | ||
| 154 | const Id rhs_pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, next_offset)}; | ||
| 155 | ctx.OpStore(lhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 0U)); | ||
| 156 | ctx.OpStore(rhs_pointer, ctx.OpCompositeExtract(ctx.U32[1], value, 1U)); | ||
| 157 | } | ||
| 158 | |||
| 159 | void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value) { | ||
| 160 | if (ctx.profile.support_explicit_workgroup_layout) { | ||
| 161 | const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)}; | ||
| 162 | ctx.OpStore(pointer, value); | ||
| 163 | return; | ||
| 164 | } | ||
| 165 | const Id shift{ctx.Const(2U)}; | ||
| 166 | const Id base_index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift)}; | ||
| 167 | for (u32 i = 0; i < 4; ++i) { | ||
| 168 | const Id index{i == 0 ? base_index : ctx.OpIAdd(ctx.U32[1], base_index, ctx.Const(i))}; | ||
| 169 | const Id pointer{ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index)}; | ||
| 170 | ctx.OpStore(pointer, ctx.OpCompositeExtract(ctx.U32[1], value, i)); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp new file mode 100644 index 000000000..9e7eb3cb1 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp | |||
| @@ -0,0 +1,150 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 7 | |||
| 8 | namespace Shader::Backend::SPIRV { | ||
| 9 | namespace { | ||
| 10 | void ConvertDepthMode(EmitContext& ctx) { | ||
| 11 | const Id type{ctx.F32[1]}; | ||
| 12 | const Id position{ctx.OpLoad(ctx.F32[4], ctx.output_position)}; | ||
| 13 | const Id z{ctx.OpCompositeExtract(type, position, 2u)}; | ||
| 14 | const Id w{ctx.OpCompositeExtract(type, position, 3u)}; | ||
| 15 | const Id screen_depth{ctx.OpFMul(type, ctx.OpFAdd(type, z, w), ctx.Constant(type, 0.5f))}; | ||
| 16 | const Id vector{ctx.OpCompositeInsert(ctx.F32[4], screen_depth, position, 2u)}; | ||
| 17 | ctx.OpStore(ctx.output_position, vector); | ||
| 18 | } | ||
| 19 | |||
| 20 | void SetFixedPipelinePointSize(EmitContext& ctx) { | ||
| 21 | if (ctx.runtime_info.fixed_state_point_size) { | ||
| 22 | const float point_size{*ctx.runtime_info.fixed_state_point_size}; | ||
| 23 | ctx.OpStore(ctx.output_point_size, ctx.Const(point_size)); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | Id DefaultVarying(EmitContext& ctx, u32 num_components, u32 element, Id zero, Id one, | ||
| 28 | Id default_vector) { | ||
| 29 | switch (num_components) { | ||
| 30 | case 1: | ||
| 31 | return element == 3 ? one : zero; | ||
| 32 | case 2: | ||
| 33 | return ctx.ConstantComposite(ctx.F32[2], zero, element + 1 == 3 ? one : zero); | ||
| 34 | case 3: | ||
| 35 | return ctx.ConstantComposite(ctx.F32[3], zero, zero, element + 2 == 3 ? one : zero); | ||
| 36 | case 4: | ||
| 37 | return default_vector; | ||
| 38 | } | ||
| 39 | throw InvalidArgument("Bad element"); | ||
| 40 | } | ||
| 41 | |||
| 42 | Id ComparisonFunction(EmitContext& ctx, CompareFunction comparison, Id operand_1, Id operand_2) { | ||
| 43 | switch (comparison) { | ||
| 44 | case CompareFunction::Never: | ||
| 45 | return ctx.false_value; | ||
| 46 | case CompareFunction::Less: | ||
| 47 | return ctx.OpFOrdLessThan(ctx.U1, operand_1, operand_2); | ||
| 48 | case CompareFunction::Equal: | ||
| 49 | return ctx.OpFOrdEqual(ctx.U1, operand_1, operand_2); | ||
| 50 | case CompareFunction::LessThanEqual: | ||
| 51 | return ctx.OpFOrdLessThanEqual(ctx.U1, operand_1, operand_2); | ||
| 52 | case CompareFunction::Greater: | ||
| 53 | return ctx.OpFOrdGreaterThan(ctx.U1, operand_1, operand_2); | ||
| 54 | case CompareFunction::NotEqual: | ||
| 55 | return ctx.OpFOrdNotEqual(ctx.U1, operand_1, operand_2); | ||
| 56 | case CompareFunction::GreaterThanEqual: | ||
| 57 | return ctx.OpFOrdGreaterThanEqual(ctx.U1, operand_1, operand_2); | ||
| 58 | case CompareFunction::Always: | ||
| 59 | return ctx.true_value; | ||
| 60 | } | ||
| 61 | throw InvalidArgument("Comparison function {}", comparison); | ||
| 62 | } | ||
| 63 | |||
| 64 | void AlphaTest(EmitContext& ctx) { | ||
| 65 | if (!ctx.runtime_info.alpha_test_func) { | ||
| 66 | return; | ||
| 67 | } | ||
| 68 | const auto comparison{*ctx.runtime_info.alpha_test_func}; | ||
| 69 | if (comparison == CompareFunction::Always) { | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | if (!Sirit::ValidId(ctx.frag_color[0])) { | ||
| 73 | return; | ||
| 74 | } | ||
| 75 | |||
| 76 | const Id type{ctx.F32[1]}; | ||
| 77 | const Id rt0_color{ctx.OpLoad(ctx.F32[4], ctx.frag_color[0])}; | ||
| 78 | const Id alpha{ctx.OpCompositeExtract(type, rt0_color, 3u)}; | ||
| 79 | |||
| 80 | const Id true_label{ctx.OpLabel()}; | ||
| 81 | const Id discard_label{ctx.OpLabel()}; | ||
| 82 | const Id alpha_reference{ctx.Const(ctx.runtime_info.alpha_test_reference)}; | ||
| 83 | const Id condition{ComparisonFunction(ctx, comparison, alpha, alpha_reference)}; | ||
| 84 | |||
| 85 | ctx.OpSelectionMerge(true_label, spv::SelectionControlMask::MaskNone); | ||
| 86 | ctx.OpBranchConditional(condition, true_label, discard_label); | ||
| 87 | ctx.AddLabel(discard_label); | ||
| 88 | ctx.OpKill(); | ||
| 89 | ctx.AddLabel(true_label); | ||
| 90 | } | ||
| 91 | } // Anonymous namespace | ||
| 92 | |||
| 93 | void EmitPrologue(EmitContext& ctx) { | ||
| 94 | if (ctx.stage == Stage::VertexB) { | ||
| 95 | const Id zero{ctx.Const(0.0f)}; | ||
| 96 | const Id one{ctx.Const(1.0f)}; | ||
| 97 | const Id default_vector{ctx.ConstantComposite(ctx.F32[4], zero, zero, zero, one)}; | ||
| 98 | ctx.OpStore(ctx.output_position, default_vector); | ||
| 99 | for (const auto& info : ctx.output_generics) { | ||
| 100 | if (info[0].num_components == 0) { | ||
| 101 | continue; | ||
| 102 | } | ||
| 103 | u32 element{0}; | ||
| 104 | while (element < 4) { | ||
| 105 | const auto& element_info{info[element]}; | ||
| 106 | const u32 num{element_info.num_components}; | ||
| 107 | const Id value{DefaultVarying(ctx, num, element, zero, one, default_vector)}; | ||
| 108 | ctx.OpStore(element_info.id, value); | ||
| 109 | element += num; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | if (ctx.stage == Stage::VertexB || ctx.stage == Stage::Geometry) { | ||
| 114 | SetFixedPipelinePointSize(ctx); | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | void EmitEpilogue(EmitContext& ctx) { | ||
| 119 | if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode) { | ||
| 120 | ConvertDepthMode(ctx); | ||
| 121 | } | ||
| 122 | if (ctx.stage == Stage::Fragment) { | ||
| 123 | AlphaTest(ctx); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) { | ||
| 128 | if (ctx.runtime_info.convert_depth_mode) { | ||
| 129 | ConvertDepthMode(ctx); | ||
| 130 | } | ||
| 131 | if (stream.IsImmediate()) { | ||
| 132 | ctx.OpEmitStreamVertex(ctx.Def(stream)); | ||
| 133 | } else { | ||
| 134 | LOG_WARNING(Shader_SPIRV, "Stream is not immediate"); | ||
| 135 | ctx.OpEmitStreamVertex(ctx.u32_zero_value); | ||
| 136 | } | ||
| 137 | // Restore fixed pipeline point size after emitting the vertex | ||
| 138 | SetFixedPipelinePointSize(ctx); | ||
| 139 | } | ||
| 140 | |||
| 141 | void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) { | ||
| 142 | if (stream.IsImmediate()) { | ||
| 143 | ctx.OpEndStreamPrimitive(ctx.Def(stream)); | ||
| 144 | } else { | ||
| 145 | LOG_WARNING(Shader_SPIRV, "Stream is not immediate"); | ||
| 146 | ctx.OpEndStreamPrimitive(ctx.u32_zero_value); | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp new file mode 100644 index 000000000..c9f469e90 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 7 | |||
| 8 | namespace Shader::Backend::SPIRV { | ||
| 9 | |||
| 10 | Id EmitUndefU1(EmitContext& ctx) { | ||
| 11 | return ctx.OpUndef(ctx.U1); | ||
| 12 | } | ||
| 13 | |||
| 14 | Id EmitUndefU8(EmitContext&) { | ||
| 15 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 16 | } | ||
| 17 | |||
| 18 | Id EmitUndefU16(EmitContext&) { | ||
| 19 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 20 | } | ||
| 21 | |||
| 22 | Id EmitUndefU32(EmitContext& ctx) { | ||
| 23 | return ctx.OpUndef(ctx.U32[1]); | ||
| 24 | } | ||
| 25 | |||
| 26 | Id EmitUndefU64(EmitContext&) { | ||
| 27 | throw NotImplementedException("SPIR-V Instruction"); | ||
| 28 | } | ||
| 29 | |||
| 30 | } // namespace Shader::Backend::SPIRV | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp new file mode 100644 index 000000000..78b1e1ba7 --- /dev/null +++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp | |||
| @@ -0,0 +1,203 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||
| 6 | #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||
| 7 | |||
| 8 | namespace Shader::Backend::SPIRV { | ||
| 9 | namespace { | ||
| 10 | Id WarpExtract(EmitContext& ctx, Id value) { | ||
| 11 | const Id local_index{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; | ||
| 12 | return ctx.OpVectorExtractDynamic(ctx.U32[1], value, local_index); | ||
| 13 | } | ||
| 14 | |||
| 15 | Id LoadMask(EmitContext& ctx, Id mask) { | ||
| 16 | const Id value{ctx.OpLoad(ctx.U32[4], mask)}; | ||
| 17 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { | ||
| 18 | return ctx.OpCompositeExtract(ctx.U32[1], value, 0U); | ||
| 19 | } | ||
| 20 | return WarpExtract(ctx, value); | ||
| 21 | } | ||
| 22 | |||
| 23 | void SetInBoundsFlag(IR::Inst* inst, Id result) { | ||
| 24 | IR::Inst* const in_bounds{inst->GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)}; | ||
| 25 | if (!in_bounds) { | ||
| 26 | return; | ||
| 27 | } | ||
| 28 | in_bounds->SetDefinition(result); | ||
| 29 | in_bounds->Invalidate(); | ||
| 30 | } | ||
| 31 | |||
| 32 | Id ComputeMinThreadId(EmitContext& ctx, Id thread_id, Id segmentation_mask) { | ||
| 33 | return ctx.OpBitwiseAnd(ctx.U32[1], thread_id, segmentation_mask); | ||
| 34 | } | ||
| 35 | |||
| 36 | Id ComputeMaxThreadId(EmitContext& ctx, Id min_thread_id, Id clamp, Id not_seg_mask) { | ||
| 37 | return ctx.OpBitwiseOr(ctx.U32[1], min_thread_id, | ||
| 38 | ctx.OpBitwiseAnd(ctx.U32[1], clamp, not_seg_mask)); | ||
| 39 | } | ||
| 40 | |||
| 41 | Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask) { | ||
| 42 | const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)}; | ||
| 43 | const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)}; | ||
| 44 | return ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask); | ||
| 45 | } | ||
| 46 | |||
| 47 | Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) { | ||
| 48 | return ctx.OpSelect(ctx.U32[1], in_range, | ||
| 49 | ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value); | ||
| 50 | } | ||
| 51 | } // Anonymous namespace | ||
| 52 | |||
| 53 | Id EmitLaneId(EmitContext& ctx) { | ||
| 54 | const Id id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; | ||
| 55 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { | ||
| 56 | return id; | ||
| 57 | } | ||
| 58 | return ctx.OpBitwiseAnd(ctx.U32[1], id, ctx.Const(31U)); | ||
| 59 | } | ||
| 60 | |||
| 61 | Id EmitVoteAll(EmitContext& ctx, Id pred) { | ||
| 62 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { | ||
| 63 | return ctx.OpSubgroupAllKHR(ctx.U1, pred); | ||
| 64 | } | ||
| 65 | const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; | ||
| 66 | const Id active_mask{WarpExtract(ctx, mask_ballot)}; | ||
| 67 | const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; | ||
| 68 | const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)}; | ||
| 69 | return ctx.OpIEqual(ctx.U1, lhs, active_mask); | ||
| 70 | } | ||
| 71 | |||
| 72 | Id EmitVoteAny(EmitContext& ctx, Id pred) { | ||
| 73 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { | ||
| 74 | return ctx.OpSubgroupAnyKHR(ctx.U1, pred); | ||
| 75 | } | ||
| 76 | const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; | ||
| 77 | const Id active_mask{WarpExtract(ctx, mask_ballot)}; | ||
| 78 | const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; | ||
| 79 | const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)}; | ||
| 80 | return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value); | ||
| 81 | } | ||
| 82 | |||
| 83 | Id EmitVoteEqual(EmitContext& ctx, Id pred) { | ||
| 84 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { | ||
| 85 | return ctx.OpSubgroupAllEqualKHR(ctx.U1, pred); | ||
| 86 | } | ||
| 87 | const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; | ||
| 88 | const Id active_mask{WarpExtract(ctx, mask_ballot)}; | ||
| 89 | const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; | ||
| 90 | const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)}; | ||
| 91 | return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value), | ||
| 92 | ctx.OpIEqual(ctx.U1, lhs, active_mask)); | ||
| 93 | } | ||
| 94 | |||
| 95 | Id EmitSubgroupBallot(EmitContext& ctx, Id pred) { | ||
| 96 | const Id ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], pred)}; | ||
| 97 | if (!ctx.profile.warp_size_potentially_larger_than_guest) { | ||
| 98 | return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U); | ||
| 99 | } | ||
| 100 | return WarpExtract(ctx, ballot); | ||
| 101 | } | ||
| 102 | |||
| 103 | Id EmitSubgroupEqMask(EmitContext& ctx) { | ||
| 104 | return LoadMask(ctx, ctx.subgroup_mask_eq); | ||
| 105 | } | ||
| 106 | |||
| 107 | Id EmitSubgroupLtMask(EmitContext& ctx) { | ||
| 108 | return LoadMask(ctx, ctx.subgroup_mask_lt); | ||
| 109 | } | ||
| 110 | |||
| 111 | Id EmitSubgroupLeMask(EmitContext& ctx) { | ||
| 112 | return LoadMask(ctx, ctx.subgroup_mask_le); | ||
| 113 | } | ||
| 114 | |||
| 115 | Id EmitSubgroupGtMask(EmitContext& ctx) { | ||
| 116 | return LoadMask(ctx, ctx.subgroup_mask_gt); | ||
| 117 | } | ||
| 118 | |||
| 119 | Id EmitSubgroupGeMask(EmitContext& ctx) { | ||
| 120 | return LoadMask(ctx, ctx.subgroup_mask_ge); | ||
| 121 | } | ||
| 122 | |||
| 123 | Id EmitShuffleIndex(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | ||
| 124 | Id segmentation_mask) { | ||
| 125 | const Id not_seg_mask{ctx.OpNot(ctx.U32[1], segmentation_mask)}; | ||
| 126 | const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; | ||
| 127 | const Id min_thread_id{ComputeMinThreadId(ctx, thread_id, segmentation_mask)}; | ||
| 128 | const Id max_thread_id{ComputeMaxThreadId(ctx, min_thread_id, clamp, not_seg_mask)}; | ||
| 129 | |||
| 130 | const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], index, not_seg_mask)}; | ||
| 131 | const Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], lhs, min_thread_id)}; | ||
| 132 | const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)}; | ||
| 133 | |||
| 134 | SetInBoundsFlag(inst, in_range); | ||
| 135 | return SelectValue(ctx, in_range, value, src_thread_id); | ||
| 136 | } | ||
| 137 | |||
| 138 | Id EmitShuffleUp(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | ||
| 139 | Id segmentation_mask) { | ||
| 140 | const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; | ||
| 141 | const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; | ||
| 142 | const Id src_thread_id{ctx.OpISub(ctx.U32[1], thread_id, index)}; | ||
| 143 | const Id in_range{ctx.OpSGreaterThanEqual(ctx.U1, src_thread_id, max_thread_id)}; | ||
| 144 | |||
| 145 | SetInBoundsFlag(inst, in_range); | ||
| 146 | return SelectValue(ctx, in_range, value, src_thread_id); | ||
| 147 | } | ||
| 148 | |||
| 149 | Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | ||
| 150 | Id segmentation_mask) { | ||
| 151 | const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; | ||
| 152 | const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; | ||
| 153 | const Id src_thread_id{ctx.OpIAdd(ctx.U32[1], thread_id, index)}; | ||
| 154 | const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)}; | ||
| 155 | |||
| 156 | SetInBoundsFlag(inst, in_range); | ||
| 157 | return SelectValue(ctx, in_range, value, src_thread_id); | ||
| 158 | } | ||
| 159 | |||
| 160 | Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp, | ||
| 161 | Id segmentation_mask) { | ||
| 162 | const Id thread_id{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; | ||
| 163 | const Id max_thread_id{GetMaxThreadId(ctx, thread_id, clamp, segmentation_mask)}; | ||
| 164 | const Id src_thread_id{ctx.OpBitwiseXor(ctx.U32[1], thread_id, index)}; | ||
| 165 | const Id in_range{ctx.OpSLessThanEqual(ctx.U1, src_thread_id, max_thread_id)}; | ||
| 166 | |||
| 167 | SetInBoundsFlag(inst, in_range); | ||
| 168 | return SelectValue(ctx, in_range, value, src_thread_id); | ||
| 169 | } | ||
| 170 | |||
| 171 | Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) { | ||
| 172 | const Id three{ctx.Const(3U)}; | ||
| 173 | Id mask{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; | ||
| 174 | mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three); | ||
| 175 | mask = ctx.OpShiftLeftLogical(ctx.U32[1], mask, ctx.Const(1U)); | ||
| 176 | mask = ctx.OpShiftRightLogical(ctx.U32[1], swizzle, mask); | ||
| 177 | mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three); | ||
| 178 | |||
| 179 | const Id modifier_a{ctx.OpVectorExtractDynamic(ctx.F32[1], ctx.fswzadd_lut_a, mask)}; | ||
| 180 | const Id modifier_b{ctx.OpVectorExtractDynamic(ctx.F32[1], ctx.fswzadd_lut_b, mask)}; | ||
| 181 | |||
| 182 | const Id result_a{ctx.OpFMul(ctx.F32[1], op_a, modifier_a)}; | ||
| 183 | const Id result_b{ctx.OpFMul(ctx.F32[1], op_b, modifier_b)}; | ||
| 184 | return ctx.OpFAdd(ctx.F32[1], result_a, result_b); | ||
| 185 | } | ||
| 186 | |||
| 187 | Id EmitDPdxFine(EmitContext& ctx, Id op_a) { | ||
| 188 | return ctx.OpDPdxFine(ctx.F32[1], op_a); | ||
| 189 | } | ||
| 190 | |||
| 191 | Id EmitDPdyFine(EmitContext& ctx, Id op_a) { | ||
| 192 | return ctx.OpDPdyFine(ctx.F32[1], op_a); | ||
| 193 | } | ||
| 194 | |||
| 195 | Id EmitDPdxCoarse(EmitContext& ctx, Id op_a) { | ||
| 196 | return ctx.OpDPdxCoarse(ctx.F32[1], op_a); | ||
| 197 | } | ||
| 198 | |||
| 199 | Id EmitDPdyCoarse(EmitContext& ctx, Id op_a) { | ||
| 200 | return ctx.OpDPdyCoarse(ctx.F32[1], op_a); | ||
| 201 | } | ||
| 202 | |||
| 203 | } // namespace Shader::Backend::SPIRV | ||