2019-03-14 06:19:02 +01:00
|
|
|
// Copyright 2019 yuzu Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2019-03-14 06:23:37 +01:00
|
|
|
#include <functional>
|
2019-12-10 03:44:29 +01:00
|
|
|
#include <limits>
|
2019-03-14 06:23:37 +01:00
|
|
|
#include <map>
|
2020-03-06 09:10:39 +01:00
|
|
|
#include <optional>
|
2019-12-10 03:44:29 +01:00
|
|
|
#include <type_traits>
|
2020-03-06 09:10:39 +01:00
|
|
|
#include <unordered_map>
|
2019-12-10 03:44:29 +01:00
|
|
|
#include <utility>
|
2019-03-14 06:23:37 +01:00
|
|
|
|
|
|
|
#include <fmt/format.h>
|
|
|
|
|
2019-03-14 06:19:02 +01:00
|
|
|
#include <sirit/sirit.h>
|
|
|
|
|
2019-03-14 06:23:37 +01:00
|
|
|
#include "common/alignment.h"
|
2019-03-14 06:19:02 +01:00
|
|
|
#include "common/assert.h"
|
2019-03-14 06:23:37 +01:00
|
|
|
#include "common/common_types.h"
|
|
|
|
#include "common/logging/log.h"
|
2019-03-14 06:19:02 +01:00
|
|
|
#include "video_core/engines/maxwell_3d.h"
|
2019-03-14 06:23:37 +01:00
|
|
|
#include "video_core/engines/shader_bytecode.h"
|
2019-03-14 06:19:02 +01:00
|
|
|
#include "video_core/engines/shader_header.h"
|
2019-11-18 22:35:21 +01:00
|
|
|
#include "video_core/engines/shader_type.h"
|
2019-05-26 06:43:07 +02:00
|
|
|
#include "video_core/renderer_vulkan/vk_device.h"
|
2019-03-14 06:19:02 +01:00
|
|
|
#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
|
2019-09-18 06:07:01 +02:00
|
|
|
#include "video_core/shader/node.h"
|
2019-03-14 06:19:02 +01:00
|
|
|
#include "video_core/shader/shader_ir.h"
|
2020-03-03 01:36:25 +01:00
|
|
|
#include "video_core/shader/transform_feedback.h"
|
2019-03-14 06:19:02 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
namespace Vulkan {
|
|
|
|
|
|
|
|
namespace {
|
2019-03-14 06:19:02 +01:00
|
|
|
|
2019-03-14 06:23:37 +01:00
|
|
|
using Sirit::Id;
|
2019-11-18 22:35:21 +01:00
|
|
|
using Tegra::Engines::ShaderType;
|
2019-03-14 06:23:37 +01:00
|
|
|
using Tegra::Shader::Attribute;
|
2020-04-02 02:37:32 +02:00
|
|
|
using Tegra::Shader::PixelImap;
|
2019-03-14 06:23:37 +01:00
|
|
|
using Tegra::Shader::Register;
|
2019-03-14 06:19:02 +01:00
|
|
|
using namespace VideoCommon::Shader;
|
|
|
|
|
2019-03-14 06:23:37 +01:00
|
|
|
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
2019-03-14 06:36:14 +01:00
|
|
|
using Operation = const OperationNode&;
|
2019-03-14 06:19:02 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
class ASTDecompiler;
|
|
|
|
class ExprDecompiler;
|
|
|
|
|
2019-03-14 06:23:37 +01:00
|
|
|
// TODO(Rodrigo): Use rasterizer's value
|
2019-12-10 03:44:29 +01:00
|
|
|
constexpr u32 MaxConstBufferFloats = 0x4000;
|
|
|
|
constexpr u32 MaxConstBufferElements = MaxConstBufferFloats / 4;
|
|
|
|
|
|
|
|
constexpr u32 NumInputPatches = 32; // This value seems to be the standard
|
|
|
|
|
|
|
|
enum class Type { Void, Bool, Bool2, Float, Int, Uint, HalfFloat };
|
|
|
|
|
|
|
|
class Expression final {
|
|
|
|
public:
|
|
|
|
Expression(Id id, Type type) : id{id}, type{type} {
|
|
|
|
ASSERT(type != Type::Void);
|
|
|
|
}
|
|
|
|
Expression() : type{Type::Void} {}
|
2019-03-14 06:23:37 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Id id{};
|
|
|
|
Type type{};
|
|
|
|
};
|
|
|
|
static_assert(std::is_standard_layout_v<Expression>);
|
2019-03-14 06:23:37 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
struct TexelBuffer {
|
|
|
|
Id image_type{};
|
|
|
|
Id image{};
|
2019-03-14 06:23:37 +01:00
|
|
|
};
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
struct SampledImage {
|
|
|
|
Id image_type{};
|
2020-02-23 06:35:16 +01:00
|
|
|
Id sampler_type{};
|
|
|
|
Id sampler_pointer_type{};
|
|
|
|
Id variable{};
|
2019-12-10 03:44:29 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct StorageImage {
|
|
|
|
Id image_type{};
|
|
|
|
Id image{};
|
|
|
|
};
|
|
|
|
|
|
|
|
struct AttributeType {
|
|
|
|
Type type;
|
|
|
|
Id scalar;
|
|
|
|
Id vector;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct VertexIndices {
|
|
|
|
std::optional<u32> position;
|
2020-02-16 06:40:17 +01:00
|
|
|
std::optional<u32> layer;
|
2019-12-10 03:44:29 +01:00
|
|
|
std::optional<u32> viewport;
|
|
|
|
std::optional<u32> point_size;
|
|
|
|
std::optional<u32> clip_distances;
|
|
|
|
};
|
2019-03-14 06:23:37 +01:00
|
|
|
|
2020-03-06 09:10:39 +01:00
|
|
|
struct GenericVaryingDescription {
|
|
|
|
Id id = nullptr;
|
|
|
|
u32 first_element = 0;
|
|
|
|
bool is_scalar = false;
|
|
|
|
};
|
|
|
|
|
2019-03-14 06:23:37 +01:00
|
|
|
spv::Dim GetSamplerDim(const Sampler& sampler) {
|
2019-12-10 03:44:29 +01:00
|
|
|
ASSERT(!sampler.IsBuffer());
|
2019-03-14 06:23:37 +01:00
|
|
|
switch (sampler.GetType()) {
|
|
|
|
case Tegra::Shader::TextureType::Texture1D:
|
|
|
|
return spv::Dim::Dim1D;
|
|
|
|
case Tegra::Shader::TextureType::Texture2D:
|
|
|
|
return spv::Dim::Dim2D;
|
|
|
|
case Tegra::Shader::TextureType::Texture3D:
|
|
|
|
return spv::Dim::Dim3D;
|
|
|
|
case Tegra::Shader::TextureType::TextureCube:
|
|
|
|
return spv::Dim::Cube;
|
|
|
|
default:
|
|
|
|
UNIMPLEMENTED_MSG("Unimplemented sampler type={}", static_cast<u32>(sampler.GetType()));
|
|
|
|
return spv::Dim::Dim2D;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
std::pair<spv::Dim, bool> GetImageDim(const Image& image) {
|
|
|
|
switch (image.GetType()) {
|
|
|
|
case Tegra::Shader::ImageType::Texture1D:
|
|
|
|
return {spv::Dim::Dim1D, false};
|
|
|
|
case Tegra::Shader::ImageType::TextureBuffer:
|
|
|
|
return {spv::Dim::Buffer, false};
|
|
|
|
case Tegra::Shader::ImageType::Texture1DArray:
|
|
|
|
return {spv::Dim::Dim1D, true};
|
|
|
|
case Tegra::Shader::ImageType::Texture2D:
|
|
|
|
return {spv::Dim::Dim2D, false};
|
|
|
|
case Tegra::Shader::ImageType::Texture2DArray:
|
|
|
|
return {spv::Dim::Dim2D, true};
|
|
|
|
case Tegra::Shader::ImageType::Texture3D:
|
|
|
|
return {spv::Dim::Dim3D, false};
|
|
|
|
default:
|
|
|
|
UNIMPLEMENTED_MSG("Unimplemented image type={}", static_cast<u32>(image.GetType()));
|
|
|
|
return {spv::Dim::Dim2D, false};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the number of vertices present in a primitive topology.
|
|
|
|
u32 GetNumPrimitiveTopologyVertices(Maxwell::PrimitiveTopology primitive_topology) {
|
|
|
|
switch (primitive_topology) {
|
|
|
|
case Maxwell::PrimitiveTopology::Points:
|
|
|
|
return 1;
|
|
|
|
case Maxwell::PrimitiveTopology::Lines:
|
|
|
|
case Maxwell::PrimitiveTopology::LineLoop:
|
|
|
|
case Maxwell::PrimitiveTopology::LineStrip:
|
|
|
|
return 2;
|
|
|
|
case Maxwell::PrimitiveTopology::Triangles:
|
|
|
|
case Maxwell::PrimitiveTopology::TriangleStrip:
|
|
|
|
case Maxwell::PrimitiveTopology::TriangleFan:
|
|
|
|
return 3;
|
|
|
|
case Maxwell::PrimitiveTopology::LinesAdjacency:
|
|
|
|
case Maxwell::PrimitiveTopology::LineStripAdjacency:
|
|
|
|
return 4;
|
|
|
|
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
|
|
|
|
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
|
|
|
|
return 6;
|
|
|
|
case Maxwell::PrimitiveTopology::Quads:
|
|
|
|
UNIMPLEMENTED_MSG("Quads");
|
|
|
|
return 3;
|
|
|
|
case Maxwell::PrimitiveTopology::QuadStrip:
|
|
|
|
UNIMPLEMENTED_MSG("QuadStrip");
|
|
|
|
return 3;
|
|
|
|
case Maxwell::PrimitiveTopology::Polygon:
|
|
|
|
UNIMPLEMENTED_MSG("Polygon");
|
|
|
|
return 3;
|
|
|
|
case Maxwell::PrimitiveTopology::Patches:
|
|
|
|
UNIMPLEMENTED_MSG("Patches");
|
|
|
|
return 3;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
spv::ExecutionMode GetExecutionMode(Maxwell::TessellationPrimitive primitive) {
|
|
|
|
switch (primitive) {
|
|
|
|
case Maxwell::TessellationPrimitive::Isolines:
|
|
|
|
return spv::ExecutionMode::Isolines;
|
|
|
|
case Maxwell::TessellationPrimitive::Triangles:
|
|
|
|
return spv::ExecutionMode::Triangles;
|
|
|
|
case Maxwell::TessellationPrimitive::Quads:
|
|
|
|
return spv::ExecutionMode::Quads;
|
|
|
|
}
|
|
|
|
UNREACHABLE();
|
|
|
|
return spv::ExecutionMode::Triangles;
|
|
|
|
}
|
|
|
|
|
|
|
|
spv::ExecutionMode GetExecutionMode(Maxwell::TessellationSpacing spacing) {
|
|
|
|
switch (spacing) {
|
|
|
|
case Maxwell::TessellationSpacing::Equal:
|
|
|
|
return spv::ExecutionMode::SpacingEqual;
|
|
|
|
case Maxwell::TessellationSpacing::FractionalOdd:
|
|
|
|
return spv::ExecutionMode::SpacingFractionalOdd;
|
|
|
|
case Maxwell::TessellationSpacing::FractionalEven:
|
|
|
|
return spv::ExecutionMode::SpacingFractionalEven;
|
|
|
|
}
|
|
|
|
UNREACHABLE();
|
|
|
|
return spv::ExecutionMode::SpacingEqual;
|
|
|
|
}
|
|
|
|
|
|
|
|
spv::ExecutionMode GetExecutionMode(Maxwell::PrimitiveTopology input_topology) {
|
|
|
|
switch (input_topology) {
|
|
|
|
case Maxwell::PrimitiveTopology::Points:
|
|
|
|
return spv::ExecutionMode::InputPoints;
|
|
|
|
case Maxwell::PrimitiveTopology::Lines:
|
|
|
|
case Maxwell::PrimitiveTopology::LineLoop:
|
|
|
|
case Maxwell::PrimitiveTopology::LineStrip:
|
|
|
|
return spv::ExecutionMode::InputLines;
|
|
|
|
case Maxwell::PrimitiveTopology::Triangles:
|
|
|
|
case Maxwell::PrimitiveTopology::TriangleStrip:
|
|
|
|
case Maxwell::PrimitiveTopology::TriangleFan:
|
|
|
|
return spv::ExecutionMode::Triangles;
|
|
|
|
case Maxwell::PrimitiveTopology::LinesAdjacency:
|
|
|
|
case Maxwell::PrimitiveTopology::LineStripAdjacency:
|
|
|
|
return spv::ExecutionMode::InputLinesAdjacency;
|
|
|
|
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
|
|
|
|
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
|
|
|
|
return spv::ExecutionMode::InputTrianglesAdjacency;
|
|
|
|
case Maxwell::PrimitiveTopology::Quads:
|
|
|
|
UNIMPLEMENTED_MSG("Quads");
|
|
|
|
return spv::ExecutionMode::Triangles;
|
|
|
|
case Maxwell::PrimitiveTopology::QuadStrip:
|
|
|
|
UNIMPLEMENTED_MSG("QuadStrip");
|
|
|
|
return spv::ExecutionMode::Triangles;
|
|
|
|
case Maxwell::PrimitiveTopology::Polygon:
|
|
|
|
UNIMPLEMENTED_MSG("Polygon");
|
|
|
|
return spv::ExecutionMode::Triangles;
|
|
|
|
case Maxwell::PrimitiveTopology::Patches:
|
|
|
|
UNIMPLEMENTED_MSG("Patches");
|
|
|
|
return spv::ExecutionMode::Triangles;
|
|
|
|
}
|
|
|
|
UNREACHABLE();
|
|
|
|
return spv::ExecutionMode::Triangles;
|
|
|
|
}
|
|
|
|
|
|
|
|
spv::ExecutionMode GetExecutionMode(Tegra::Shader::OutputTopology output_topology) {
|
|
|
|
switch (output_topology) {
|
|
|
|
case Tegra::Shader::OutputTopology::PointList:
|
|
|
|
return spv::ExecutionMode::OutputPoints;
|
|
|
|
case Tegra::Shader::OutputTopology::LineStrip:
|
|
|
|
return spv::ExecutionMode::OutputLineStrip;
|
|
|
|
case Tegra::Shader::OutputTopology::TriangleStrip:
|
|
|
|
return spv::ExecutionMode::OutputTriangleStrip;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return spv::ExecutionMode::OutputPoints;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-14 06:23:37 +01:00
|
|
|
/// Returns true if an attribute index is one of the 32 generic attributes
|
|
|
|
constexpr bool IsGenericAttribute(Attribute::Index attribute) {
|
|
|
|
return attribute >= Attribute::Index::Attribute_0 &&
|
|
|
|
attribute <= Attribute::Index::Attribute_31;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the location of a generic attribute
|
2019-12-10 03:44:29 +01:00
|
|
|
u32 GetGenericAttributeLocation(Attribute::Index attribute) {
|
2019-03-14 06:23:37 +01:00
|
|
|
ASSERT(IsGenericAttribute(attribute));
|
|
|
|
return static_cast<u32>(attribute) - static_cast<u32>(Attribute::Index::Attribute_0);
|
|
|
|
}
|
|
|
|
|
2019-03-14 06:36:14 +01:00
|
|
|
/// Returns true if an object has to be treated as precise
|
|
|
|
bool IsPrecise(Operation operand) {
|
2019-04-16 00:48:11 +02:00
|
|
|
const auto& meta{operand.GetMeta()};
|
2019-03-14 06:36:14 +01:00
|
|
|
if (std::holds_alternative<MetaArithmetic>(meta)) {
|
|
|
|
return std::get<MetaArithmetic>(meta).precise;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
class SPIRVDecompiler final : public Sirit::Module {
|
2019-03-14 06:19:02 +01:00
|
|
|
public:
|
2019-12-10 03:44:29 +01:00
|
|
|
explicit SPIRVDecompiler(const VKDevice& device, const ShaderIR& ir, ShaderType stage,
|
2020-03-03 01:36:25 +01:00
|
|
|
const Registry& registry, const Specialization& specialization)
|
2019-12-10 03:44:29 +01:00
|
|
|
: Module(0x00010300), device{device}, ir{ir}, stage{stage}, header{ir.GetHeader()},
|
2020-03-12 07:27:29 +01:00
|
|
|
registry{registry}, specialization{specialization} {
|
|
|
|
if (stage != ShaderType::Compute) {
|
|
|
|
transform_feedback = BuildTransformFeedback(registry.GetGraphicsInfo());
|
|
|
|
}
|
|
|
|
|
2019-03-14 06:23:37 +01:00
|
|
|
AddCapability(spv::Capability::Shader);
|
2019-12-10 03:44:29 +01:00
|
|
|
AddCapability(spv::Capability::UniformAndStorageBuffer16BitAccess);
|
|
|
|
AddCapability(spv::Capability::ImageQuery);
|
|
|
|
AddCapability(spv::Capability::Image1D);
|
|
|
|
AddCapability(spv::Capability::ImageBuffer);
|
|
|
|
AddCapability(spv::Capability::ImageGatherExtended);
|
|
|
|
AddCapability(spv::Capability::SampledBuffer);
|
|
|
|
AddCapability(spv::Capability::StorageImageWriteWithoutFormat);
|
2020-02-14 00:25:28 +01:00
|
|
|
AddCapability(spv::Capability::DrawParameters);
|
2019-12-10 03:44:29 +01:00
|
|
|
AddCapability(spv::Capability::SubgroupBallotKHR);
|
|
|
|
AddCapability(spv::Capability::SubgroupVoteKHR);
|
|
|
|
AddExtension("SPV_KHR_shader_ballot");
|
|
|
|
AddExtension("SPV_KHR_subgroup_vote");
|
2019-03-14 06:23:37 +01:00
|
|
|
AddExtension("SPV_KHR_storage_buffer_storage_class");
|
|
|
|
AddExtension("SPV_KHR_variable_pointers");
|
2020-02-14 00:25:28 +01:00
|
|
|
AddExtension("SPV_KHR_shader_draw_parameters");
|
2019-12-10 03:44:29 +01:00
|
|
|
|
2020-03-06 09:10:39 +01:00
|
|
|
if (!transform_feedback.empty()) {
|
|
|
|
if (device.IsExtTransformFeedbackSupported()) {
|
|
|
|
AddCapability(spv::Capability::TransformFeedback);
|
|
|
|
} else {
|
|
|
|
LOG_ERROR(Render_Vulkan, "Shader requires transform feedbacks but these are not "
|
|
|
|
"supported on this device");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-16 06:40:17 +01:00
|
|
|
if (ir.UsesLayer() || ir.UsesViewportIndex()) {
|
|
|
|
if (ir.UsesViewportIndex()) {
|
|
|
|
AddCapability(spv::Capability::MultiViewport);
|
|
|
|
}
|
|
|
|
if (stage != ShaderType::Geometry && device.IsExtShaderViewportIndexLayerSupported()) {
|
2019-12-10 03:44:29 +01:00
|
|
|
AddExtension("SPV_EXT_shader_viewport_index_layer");
|
|
|
|
AddCapability(spv::Capability::ShaderViewportIndexLayerEXT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-06 09:06:02 +01:00
|
|
|
if (device.IsFormatlessImageLoadSupported()) {
|
2020-02-20 03:28:13 +01:00
|
|
|
AddCapability(spv::Capability::StorageImageReadWithoutFormat);
|
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
|
|
|
|
if (device.IsFloat16Supported()) {
|
|
|
|
AddCapability(spv::Capability::Float16);
|
|
|
|
}
|
|
|
|
t_scalar_half = Name(TypeFloat(device.IsFloat16Supported() ? 16 : 32), "scalar_half");
|
|
|
|
t_half = Name(TypeVector(t_scalar_half, 2), "half");
|
|
|
|
|
|
|
|
const Id main = Decompile();
|
|
|
|
|
|
|
|
switch (stage) {
|
|
|
|
case ShaderType::Vertex:
|
|
|
|
AddEntryPoint(spv::ExecutionModel::Vertex, main, "main", interfaces);
|
|
|
|
break;
|
|
|
|
case ShaderType::TesselationControl:
|
|
|
|
AddCapability(spv::Capability::Tessellation);
|
|
|
|
AddEntryPoint(spv::ExecutionModel::TessellationControl, main, "main", interfaces);
|
|
|
|
AddExecutionMode(main, spv::ExecutionMode::OutputVertices,
|
|
|
|
header.common2.threads_per_input_primitive);
|
|
|
|
break;
|
2020-03-03 01:36:25 +01:00
|
|
|
case ShaderType::TesselationEval: {
|
|
|
|
const auto& info = registry.GetGraphicsInfo();
|
2019-12-10 03:44:29 +01:00
|
|
|
AddCapability(spv::Capability::Tessellation);
|
|
|
|
AddEntryPoint(spv::ExecutionModel::TessellationEvaluation, main, "main", interfaces);
|
2020-03-03 01:36:25 +01:00
|
|
|
AddExecutionMode(main, GetExecutionMode(info.tessellation_primitive));
|
|
|
|
AddExecutionMode(main, GetExecutionMode(info.tessellation_spacing));
|
|
|
|
AddExecutionMode(main, info.tessellation_clockwise
|
2019-12-10 03:44:29 +01:00
|
|
|
? spv::ExecutionMode::VertexOrderCw
|
|
|
|
: spv::ExecutionMode::VertexOrderCcw);
|
|
|
|
break;
|
2020-03-03 01:36:25 +01:00
|
|
|
}
|
|
|
|
case ShaderType::Geometry: {
|
|
|
|
const auto& info = registry.GetGraphicsInfo();
|
2019-12-10 03:44:29 +01:00
|
|
|
AddCapability(spv::Capability::Geometry);
|
|
|
|
AddEntryPoint(spv::ExecutionModel::Geometry, main, "main", interfaces);
|
2020-03-03 01:36:25 +01:00
|
|
|
AddExecutionMode(main, GetExecutionMode(info.primitive_topology));
|
2019-12-10 03:44:29 +01:00
|
|
|
AddExecutionMode(main, GetExecutionMode(header.common3.output_topology));
|
|
|
|
AddExecutionMode(main, spv::ExecutionMode::OutputVertices,
|
|
|
|
header.common4.max_output_vertices);
|
|
|
|
// TODO(Rodrigo): Where can we get this info from?
|
|
|
|
AddExecutionMode(main, spv::ExecutionMode::Invocations, 1U);
|
|
|
|
break;
|
2020-03-03 01:36:25 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
case ShaderType::Fragment:
|
|
|
|
AddEntryPoint(spv::ExecutionModel::Fragment, main, "main", interfaces);
|
|
|
|
AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft);
|
|
|
|
if (header.ps.omap.depth) {
|
|
|
|
AddExecutionMode(main, spv::ExecutionMode::DepthReplacing);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ShaderType::Compute:
|
|
|
|
const auto workgroup_size = specialization.workgroup_size;
|
|
|
|
AddExecutionMode(main, spv::ExecutionMode::LocalSize, workgroup_size[0],
|
|
|
|
workgroup_size[1], workgroup_size[2]);
|
|
|
|
AddEntryPoint(spv::ExecutionModel::GLCompute, main, "main", interfaces);
|
|
|
|
break;
|
|
|
|
}
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
2019-03-14 06:19:02 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
private:
|
|
|
|
Id Decompile() {
|
|
|
|
DeclareCommon();
|
|
|
|
DeclareVertex();
|
|
|
|
DeclareTessControl();
|
|
|
|
DeclareTessEval();
|
|
|
|
DeclareGeometry();
|
|
|
|
DeclareFragment();
|
|
|
|
DeclareCompute();
|
|
|
|
DeclareRegisters();
|
2020-01-07 19:53:46 +01:00
|
|
|
DeclareCustomVariables();
|
2019-12-10 03:44:29 +01:00
|
|
|
DeclarePredicates();
|
|
|
|
DeclareLocalMemory();
|
|
|
|
DeclareSharedMemory();
|
|
|
|
DeclareInternalFlags();
|
|
|
|
DeclareInputAttributes();
|
|
|
|
DeclareOutputAttributes();
|
|
|
|
|
|
|
|
u32 binding = specialization.base_binding;
|
|
|
|
binding = DeclareConstantBuffers(binding);
|
|
|
|
binding = DeclareGlobalBuffers(binding);
|
|
|
|
binding = DeclareTexelBuffers(binding);
|
|
|
|
binding = DeclareSamplers(binding);
|
|
|
|
binding = DeclareImages(binding);
|
|
|
|
|
|
|
|
const Id main = OpFunction(t_void, {}, TypeFunction(t_void));
|
|
|
|
AddLabel();
|
|
|
|
|
|
|
|
if (ir.IsDecompiled()) {
|
|
|
|
DeclareFlowVariables();
|
|
|
|
DecompileAST();
|
|
|
|
} else {
|
|
|
|
AllocateLabels();
|
|
|
|
DecompileBranchMode();
|
|
|
|
}
|
|
|
|
|
|
|
|
OpReturn();
|
|
|
|
OpFunctionEnd();
|
|
|
|
|
|
|
|
return main;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefinePrologue() {
|
|
|
|
if (stage == ShaderType::Vertex) {
|
|
|
|
// Clear Position to avoid reading trash on the Z conversion.
|
|
|
|
const auto position_index = out_indices.position.value();
|
|
|
|
const Id position = AccessElement(t_out_float4, out_vertex, position_index);
|
2020-03-12 06:35:31 +01:00
|
|
|
OpStore(position, v_varying_default);
|
2019-12-10 03:44:29 +01:00
|
|
|
|
|
|
|
if (specialization.point_size) {
|
|
|
|
const u32 point_size_index = out_indices.point_size.value();
|
|
|
|
const Id out_point_size = AccessElement(t_out_float, out_vertex, point_size_index);
|
|
|
|
OpStore(out_point_size, Constant(t_float, *specialization.point_size));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DecompileAST();
|
|
|
|
|
2019-08-25 21:32:00 +02:00
|
|
|
void DecompileBranchMode() {
|
2019-03-14 06:27:06 +01:00
|
|
|
const u32 first_address = ir.GetBasicBlocks().begin()->first;
|
|
|
|
const Id loop_label = OpLabel("loop");
|
|
|
|
const Id merge_label = OpLabel("merge");
|
|
|
|
const Id dummy_label = OpLabel();
|
|
|
|
const Id jump_label = OpLabel();
|
|
|
|
continue_label = OpLabel("continue");
|
|
|
|
|
|
|
|
std::vector<Sirit::Literal> literals;
|
|
|
|
std::vector<Id> branch_labels;
|
2019-12-10 03:44:29 +01:00
|
|
|
for (const auto& [literal, label] : labels) {
|
2019-03-14 06:27:06 +01:00
|
|
|
literals.push_back(literal);
|
|
|
|
branch_labels.push_back(label);
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
jmp_to = OpVariable(TypePointer(spv::StorageClass::Function, t_uint),
|
|
|
|
spv::StorageClass::Function, Constant(t_uint, first_address));
|
|
|
|
AddLocalVariable(jmp_to);
|
|
|
|
|
2019-06-02 23:52:07 +02:00
|
|
|
std::tie(ssy_flow_stack, ssy_flow_stack_top) = CreateFlowStack();
|
|
|
|
std::tie(pbk_flow_stack, pbk_flow_stack_top) = CreateFlowStack();
|
2019-03-14 06:27:06 +01:00
|
|
|
|
|
|
|
Name(jmp_to, "jmp_to");
|
2019-06-02 23:52:07 +02:00
|
|
|
Name(ssy_flow_stack, "ssy_flow_stack");
|
|
|
|
Name(ssy_flow_stack_top, "ssy_flow_stack_top");
|
|
|
|
Name(pbk_flow_stack, "pbk_flow_stack");
|
|
|
|
Name(pbk_flow_stack_top, "pbk_flow_stack_top");
|
2019-03-14 06:27:06 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
DefinePrologue();
|
|
|
|
|
|
|
|
OpBranch(loop_label);
|
|
|
|
AddLabel(loop_label);
|
|
|
|
OpLoopMerge(merge_label, continue_label, spv::LoopControlMask::MaskNone);
|
|
|
|
OpBranch(dummy_label);
|
2019-03-14 06:27:06 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
AddLabel(dummy_label);
|
2019-03-14 06:27:06 +01:00
|
|
|
const Id default_branch = OpLabel();
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id jmp_to_load = OpLoad(t_uint, jmp_to);
|
|
|
|
OpSelectionMerge(jump_label, spv::SelectionControlMask::MaskNone);
|
|
|
|
OpSwitch(jmp_to_load, default_branch, literals, branch_labels);
|
2019-03-14 06:27:06 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
AddLabel(default_branch);
|
|
|
|
OpReturn();
|
2019-03-14 06:27:06 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
for (const auto& [address, bb] : ir.GetBasicBlocks()) {
|
|
|
|
AddLabel(labels.at(address));
|
2019-03-14 06:30:19 +01:00
|
|
|
|
|
|
|
VisitBasicBlock(bb);
|
|
|
|
|
|
|
|
const auto next_it = labels.lower_bound(address + 1);
|
|
|
|
const Id next_label = next_it != labels.end() ? next_it->second : default_branch;
|
2019-12-10 03:44:29 +01:00
|
|
|
OpBranch(next_label);
|
2019-03-14 06:30:19 +01:00
|
|
|
}
|
2019-03-14 06:27:06 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
AddLabel(jump_label);
|
|
|
|
OpBranch(continue_label);
|
|
|
|
AddLabel(continue_label);
|
|
|
|
OpBranch(loop_label);
|
|
|
|
AddLabel(merge_label);
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
private:
|
|
|
|
friend class ASTDecompiler;
|
|
|
|
friend class ExprDecompiler;
|
2019-08-25 21:32:00 +02:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount);
|
2019-08-25 21:32:00 +02:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
void AllocateLabels() {
|
|
|
|
for (const auto& pair : ir.GetBasicBlocks()) {
|
|
|
|
const u32 address = pair.first;
|
|
|
|
labels.emplace(address, OpLabel(fmt::format("label_0x{:x}", address)));
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
}
|
2019-08-25 21:32:00 +02:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
void DeclareCommon() {
|
|
|
|
thread_id =
|
|
|
|
DeclareInputBuiltIn(spv::BuiltIn::SubgroupLocalInvocationId, t_in_uint, "thread_id");
|
|
|
|
}
|
2019-08-25 21:32:00 +02:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
void DeclareVertex() {
|
|
|
|
if (stage != ShaderType::Vertex) {
|
|
|
|
return;
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
Id out_vertex_struct;
|
|
|
|
std::tie(out_vertex_struct, out_indices) = DeclareVertexStruct();
|
|
|
|
const Id vertex_ptr = TypePointer(spv::StorageClass::Output, out_vertex_struct);
|
|
|
|
out_vertex = OpVariable(vertex_ptr, spv::StorageClass::Output);
|
|
|
|
interfaces.push_back(AddGlobalVariable(Name(out_vertex, "out_vertex")));
|
2019-08-25 21:32:00 +02:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
// Declare input attributes
|
2020-02-14 00:25:28 +01:00
|
|
|
vertex_index = DeclareInputBuiltIn(spv::BuiltIn::VertexIndex, t_in_int, "vertex_index");
|
2019-12-10 03:44:29 +01:00
|
|
|
instance_index =
|
2020-02-14 00:25:28 +01:00
|
|
|
DeclareInputBuiltIn(spv::BuiltIn::InstanceIndex, t_in_int, "instance_index");
|
|
|
|
base_vertex = DeclareInputBuiltIn(spv::BuiltIn::BaseVertex, t_in_int, "base_vertex");
|
|
|
|
base_instance = DeclareInputBuiltIn(spv::BuiltIn::BaseInstance, t_in_int, "base_instance");
|
2019-03-14 06:19:02 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
void DeclareTessControl() {
|
|
|
|
if (stage != ShaderType::TesselationControl) {
|
|
|
|
return;
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
DeclareInputVertexArray(NumInputPatches);
|
|
|
|
DeclareOutputVertexArray(header.common2.threads_per_input_primitive);
|
2019-03-14 06:23:37 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
tess_level_outer = DeclareBuiltIn(
|
|
|
|
spv::BuiltIn::TessLevelOuter, spv::StorageClass::Output,
|
|
|
|
TypePointer(spv::StorageClass::Output, TypeArray(t_float, Constant(t_uint, 4U))),
|
|
|
|
"tess_level_outer");
|
|
|
|
Decorate(tess_level_outer, spv::Decoration::Patch);
|
2019-03-14 06:23:37 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
tess_level_inner = DeclareBuiltIn(
|
|
|
|
spv::BuiltIn::TessLevelInner, spv::StorageClass::Output,
|
|
|
|
TypePointer(spv::StorageClass::Output, TypeArray(t_float, Constant(t_uint, 2U))),
|
|
|
|
"tess_level_inner");
|
|
|
|
Decorate(tess_level_inner, spv::Decoration::Patch);
|
2019-03-14 06:23:37 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
invocation_id = DeclareInputBuiltIn(spv::BuiltIn::InvocationId, t_in_int, "invocation_id");
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
void DeclareTessEval() {
|
|
|
|
if (stage != ShaderType::TesselationEval) {
|
2019-03-14 06:23:37 +01:00
|
|
|
return;
|
2019-12-10 03:44:29 +01:00
|
|
|
}
|
|
|
|
DeclareInputVertexArray(NumInputPatches);
|
|
|
|
DeclareOutputVertex();
|
2019-03-14 06:23:37 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
tess_coord = DeclareInputBuiltIn(spv::BuiltIn::TessCoord, t_in_float3, "tess_coord");
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareGeometry() {
|
2019-12-10 03:44:29 +01:00
|
|
|
if (stage != ShaderType::Geometry) {
|
2019-03-14 06:23:37 +01:00
|
|
|
return;
|
2019-12-10 03:44:29 +01:00
|
|
|
}
|
2020-03-03 01:36:25 +01:00
|
|
|
const auto& info = registry.GetGraphicsInfo();
|
|
|
|
const u32 num_input = GetNumPrimitiveTopologyVertices(info.primitive_topology);
|
2019-12-10 03:44:29 +01:00
|
|
|
DeclareInputVertexArray(num_input);
|
|
|
|
DeclareOutputVertex();
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareFragment() {
|
2019-12-10 03:44:29 +01:00
|
|
|
if (stage != ShaderType::Fragment) {
|
2019-03-14 06:23:37 +01:00
|
|
|
return;
|
2019-12-10 03:44:29 +01:00
|
|
|
}
|
2019-03-14 06:23:37 +01:00
|
|
|
|
2020-01-24 05:15:50 +01:00
|
|
|
for (u32 rt = 0; rt < static_cast<u32>(std::size(frag_colors)); ++rt) {
|
|
|
|
if (!IsRenderTargetEnabled(rt)) {
|
2019-03-14 06:23:37 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const Id id = AddGlobalVariable(OpVariable(t_out_float4, spv::StorageClass::Output));
|
|
|
|
Name(id, fmt::format("frag_color{}", rt));
|
|
|
|
Decorate(id, spv::Decoration::Location, rt);
|
|
|
|
|
|
|
|
frag_colors[rt] = id;
|
|
|
|
interfaces.push_back(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (header.ps.omap.depth) {
|
|
|
|
frag_depth = AddGlobalVariable(OpVariable(t_out_float, spv::StorageClass::Output));
|
|
|
|
Name(frag_depth, "frag_depth");
|
|
|
|
Decorate(frag_depth, spv::Decoration::BuiltIn,
|
|
|
|
static_cast<u32>(spv::BuiltIn::FragDepth));
|
|
|
|
|
|
|
|
interfaces.push_back(frag_depth);
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
frag_coord = DeclareInputBuiltIn(spv::BuiltIn::FragCoord, t_in_float4, "frag_coord");
|
|
|
|
front_facing = DeclareInputBuiltIn(spv::BuiltIn::FrontFacing, t_in_bool, "front_facing");
|
|
|
|
point_coord = DeclareInputBuiltIn(spv::BuiltIn::PointCoord, t_in_float2, "point_coord");
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareCompute() {
|
|
|
|
if (stage != ShaderType::Compute) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
workgroup_id = DeclareInputBuiltIn(spv::BuiltIn::WorkgroupId, t_in_uint3, "workgroup_id");
|
|
|
|
local_invocation_id =
|
|
|
|
DeclareInputBuiltIn(spv::BuiltIn::LocalInvocationId, t_in_uint3, "local_invocation_id");
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareRegisters() {
|
|
|
|
for (const u32 gpr : ir.GetRegisters()) {
|
|
|
|
const Id id = OpVariable(t_prv_float, spv::StorageClass::Private, v_float_zero);
|
|
|
|
Name(id, fmt::format("gpr_{}", gpr));
|
|
|
|
registers.emplace(gpr, AddGlobalVariable(id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-07 19:53:46 +01:00
|
|
|
void DeclareCustomVariables() {
|
2020-01-24 15:44:34 +01:00
|
|
|
const u32 num_custom_variables = ir.GetNumCustomVariables();
|
|
|
|
for (u32 i = 0; i < num_custom_variables; ++i) {
|
2020-01-07 19:53:46 +01:00
|
|
|
const Id id = OpVariable(t_prv_float, spv::StorageClass::Private, v_float_zero);
|
|
|
|
Name(id, fmt::format("custom_var_{}", i));
|
|
|
|
custom_variables.emplace(i, AddGlobalVariable(id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-14 06:23:37 +01:00
|
|
|
void DeclarePredicates() {
|
|
|
|
for (const auto pred : ir.GetPredicates()) {
|
|
|
|
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
|
|
|
Name(id, fmt::format("pred_{}", static_cast<u32>(pred)));
|
|
|
|
predicates.emplace(pred, AddGlobalVariable(id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-25 21:32:00 +02:00
|
|
|
void DeclareFlowVariables() {
|
|
|
|
for (u32 i = 0; i < ir.GetASTNumVariables(); i++) {
|
|
|
|
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
|
|
|
Name(id, fmt::format("flow_var_{}", static_cast<u32>(i)));
|
|
|
|
flow_variables.emplace(i, AddGlobalVariable(id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-14 06:23:37 +01:00
|
|
|
void DeclareLocalMemory() {
|
2019-12-10 03:44:29 +01:00
|
|
|
// TODO(Rodrigo): Unstub kernel local memory size and pass it from a register at
|
|
|
|
// specialization time.
|
|
|
|
const u64 lmem_size = stage == ShaderType::Compute ? 0x400 : header.GetLocalMemorySize();
|
|
|
|
if (lmem_size == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const auto element_count = static_cast<u32>(Common::AlignUp(lmem_size, 4) / 4);
|
|
|
|
const Id type_array = TypeArray(t_float, Constant(t_uint, element_count));
|
|
|
|
const Id type_pointer = TypePointer(spv::StorageClass::Private, type_array);
|
|
|
|
Name(type_pointer, "LocalMemory");
|
|
|
|
|
|
|
|
local_memory =
|
|
|
|
OpVariable(type_pointer, spv::StorageClass::Private, ConstantNull(type_array));
|
|
|
|
AddGlobalVariable(Name(local_memory, "local_memory"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareSharedMemory() {
|
|
|
|
if (stage != ShaderType::Compute) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
t_smem_uint = TypePointer(spv::StorageClass::Workgroup, t_uint);
|
2019-03-14 06:23:37 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
const u32 smem_size = specialization.shared_memory_size;
|
|
|
|
if (smem_size == 0) {
|
|
|
|
// Avoid declaring an empty array.
|
|
|
|
return;
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
const auto element_count = static_cast<u32>(Common::AlignUp(smem_size, 4) / 4);
|
|
|
|
const Id type_array = TypeArray(t_uint, Constant(t_uint, element_count));
|
|
|
|
const Id type_pointer = TypePointer(spv::StorageClass::Workgroup, type_array);
|
|
|
|
Name(type_pointer, "SharedMemory");
|
|
|
|
|
|
|
|
shared_memory = OpVariable(type_pointer, spv::StorageClass::Workgroup);
|
|
|
|
AddGlobalVariable(Name(shared_memory, "shared_memory"));
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareInternalFlags() {
|
2019-12-10 03:44:29 +01:00
|
|
|
constexpr std::array names = {"zero", "sign", "carry", "overflow"};
|
2019-03-14 06:23:37 +01:00
|
|
|
for (std::size_t flag = 0; flag < INTERNAL_FLAGS_COUNT; ++flag) {
|
2019-05-26 06:43:07 +02:00
|
|
|
const auto flag_code = static_cast<InternalFlag>(flag);
|
2019-03-14 06:23:37 +01:00
|
|
|
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
|
|
|
internal_flags[flag] = AddGlobalVariable(Name(id, names[flag]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
void DeclareInputVertexArray(u32 length) {
|
|
|
|
constexpr auto storage = spv::StorageClass::Input;
|
|
|
|
std::tie(in_indices, in_vertex) = DeclareVertexArray(storage, "in_indices", length);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareOutputVertexArray(u32 length) {
|
|
|
|
constexpr auto storage = spv::StorageClass::Output;
|
|
|
|
std::tie(out_indices, out_vertex) = DeclareVertexArray(storage, "out_indices", length);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::tuple<VertexIndices, Id> DeclareVertexArray(spv::StorageClass storage_class,
|
|
|
|
std::string name, u32 length) {
|
|
|
|
const auto [struct_id, indices] = DeclareVertexStruct();
|
|
|
|
const Id vertex_array = TypeArray(struct_id, Constant(t_uint, length));
|
|
|
|
const Id vertex_ptr = TypePointer(storage_class, vertex_array);
|
|
|
|
const Id vertex = OpVariable(vertex_ptr, storage_class);
|
|
|
|
AddGlobalVariable(Name(vertex, std::move(name)));
|
|
|
|
interfaces.push_back(vertex);
|
|
|
|
return {indices, vertex};
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareOutputVertex() {
|
|
|
|
Id out_vertex_struct;
|
|
|
|
std::tie(out_vertex_struct, out_indices) = DeclareVertexStruct();
|
|
|
|
const Id out_vertex_ptr = TypePointer(spv::StorageClass::Output, out_vertex_struct);
|
|
|
|
out_vertex = OpVariable(out_vertex_ptr, spv::StorageClass::Output);
|
|
|
|
interfaces.push_back(AddGlobalVariable(Name(out_vertex, "out_vertex")));
|
|
|
|
}
|
|
|
|
|
2019-03-14 06:23:37 +01:00
|
|
|
void DeclareInputAttributes() {
|
2019-04-30 04:37:09 +02:00
|
|
|
for (const auto index : ir.GetInputAttributes()) {
|
2019-03-14 06:23:37 +01:00
|
|
|
if (!IsGenericAttribute(index)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const u32 location = GetGenericAttributeLocation(index);
|
2019-12-10 03:44:29 +01:00
|
|
|
const auto type_descriptor = GetAttributeType(location);
|
|
|
|
Id type;
|
|
|
|
if (IsInputAttributeArray()) {
|
|
|
|
type = GetTypeVectorDefinitionLut(type_descriptor.type).at(3);
|
|
|
|
type = TypeArray(type, Constant(t_uint, GetNumInputVertices()));
|
|
|
|
type = TypePointer(spv::StorageClass::Input, type);
|
|
|
|
} else {
|
|
|
|
type = type_descriptor.vector;
|
|
|
|
}
|
|
|
|
const Id id = OpVariable(type, spv::StorageClass::Input);
|
|
|
|
AddGlobalVariable(Name(id, fmt::format("in_attr{}", location)));
|
2019-03-14 06:23:37 +01:00
|
|
|
input_attributes.emplace(index, id);
|
|
|
|
interfaces.push_back(id);
|
|
|
|
|
|
|
|
Decorate(id, spv::Decoration::Location, location);
|
|
|
|
|
2019-11-18 22:35:21 +01:00
|
|
|
if (stage != ShaderType::Fragment) {
|
2019-03-14 06:23:37 +01:00
|
|
|
continue;
|
|
|
|
}
|
2020-04-02 02:37:32 +02:00
|
|
|
switch (header.ps.GetPixelImap(location)) {
|
|
|
|
case PixelImap::Constant:
|
2019-03-14 06:23:37 +01:00
|
|
|
Decorate(id, spv::Decoration::Flat);
|
|
|
|
break;
|
2020-04-02 02:37:32 +02:00
|
|
|
case PixelImap::Perspective:
|
2019-03-14 06:23:37 +01:00
|
|
|
// Default
|
|
|
|
break;
|
2020-04-02 02:37:32 +02:00
|
|
|
case PixelImap::ScreenLinear:
|
|
|
|
Decorate(id, spv::Decoration::NoPerspective);
|
|
|
|
break;
|
2019-03-14 06:23:37 +01:00
|
|
|
default:
|
|
|
|
UNREACHABLE_MSG("Unused attribute being fetched");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareOutputAttributes() {
|
2020-03-06 09:10:39 +01:00
|
|
|
if (stage == ShaderType::Compute || stage == ShaderType::Fragment) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
UNIMPLEMENTED_IF(registry.GetGraphicsInfo().tfb_enabled && stage != ShaderType::Vertex);
|
2019-03-14 06:23:37 +01:00
|
|
|
for (const auto index : ir.GetOutputAttributes()) {
|
|
|
|
if (!IsGenericAttribute(index)) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-03-06 09:10:39 +01:00
|
|
|
DeclareOutputAttribute(index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareOutputAttribute(Attribute::Index index) {
|
|
|
|
static constexpr std::string_view swizzle = "xyzw";
|
|
|
|
|
|
|
|
const u32 location = GetGenericAttributeLocation(index);
|
|
|
|
u8 element = 0;
|
|
|
|
while (element < 4) {
|
|
|
|
const std::size_t remainder = 4 - element;
|
|
|
|
|
|
|
|
std::size_t num_components = remainder;
|
|
|
|
const std::optional tfb = GetTransformFeedbackInfo(index, element);
|
|
|
|
if (tfb) {
|
|
|
|
num_components = tfb->components;
|
|
|
|
}
|
|
|
|
|
|
|
|
Id type = GetTypeVectorDefinitionLut(Type::Float).at(num_components - 1);
|
2020-03-12 06:35:31 +01:00
|
|
|
Id varying_default = v_varying_default;
|
2019-12-10 03:44:29 +01:00
|
|
|
if (IsOutputAttributeArray()) {
|
|
|
|
const u32 num = GetNumOutputVertices();
|
|
|
|
type = TypeArray(type, Constant(t_uint, num));
|
|
|
|
if (device.GetDriverID() != vk::DriverIdKHR::eIntelProprietaryWindows) {
|
|
|
|
// Intel's proprietary driver fails to setup defaults for arrayed output
|
|
|
|
// attributes.
|
|
|
|
varying_default = ConstantComposite(type, std::vector(num, varying_default));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
type = TypePointer(spv::StorageClass::Output, type);
|
|
|
|
|
2020-03-06 09:10:39 +01:00
|
|
|
std::string name = fmt::format("out_attr{}", location);
|
|
|
|
if (num_components < 4 || element > 0) {
|
|
|
|
name = fmt::format("{}_{}", name, swizzle.substr(element, num_components));
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id id = OpVariable(type, spv::StorageClass::Output, varying_default);
|
2020-03-06 09:10:39 +01:00
|
|
|
Name(AddGlobalVariable(id), name);
|
|
|
|
|
|
|
|
GenericVaryingDescription description;
|
|
|
|
description.id = id;
|
|
|
|
description.first_element = element;
|
|
|
|
description.is_scalar = num_components == 1;
|
|
|
|
for (u32 i = 0; i < num_components; ++i) {
|
|
|
|
const u8 offset = static_cast<u8>(static_cast<u32>(index) * 4 + element + i);
|
|
|
|
output_attributes.emplace(offset, description);
|
|
|
|
}
|
2019-03-14 06:23:37 +01:00
|
|
|
interfaces.push_back(id);
|
|
|
|
|
|
|
|
Decorate(id, spv::Decoration::Location, location);
|
2020-03-06 09:10:39 +01:00
|
|
|
if (element > 0) {
|
|
|
|
Decorate(id, spv::Decoration::Component, static_cast<u32>(element));
|
|
|
|
}
|
|
|
|
if (tfb && device.IsExtTransformFeedbackSupported()) {
|
|
|
|
Decorate(id, spv::Decoration::XfbBuffer, static_cast<u32>(tfb->buffer));
|
|
|
|
Decorate(id, spv::Decoration::XfbStride, static_cast<u32>(tfb->stride));
|
|
|
|
Decorate(id, spv::Decoration::Offset, static_cast<u32>(tfb->offset));
|
|
|
|
}
|
|
|
|
|
2020-03-15 18:00:14 +01:00
|
|
|
element = static_cast<u8>(static_cast<std::size_t>(element) + num_components);
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-06 09:10:39 +01:00
|
|
|
std::optional<VaryingTFB> GetTransformFeedbackInfo(Attribute::Index index, u8 element = 0) {
|
2020-03-10 00:08:48 +01:00
|
|
|
const u8 location = static_cast<u8>(static_cast<u32>(index) * 4 + element);
|
2020-03-06 09:10:39 +01:00
|
|
|
const auto it = transform_feedback.find(location);
|
|
|
|
if (it == transform_feedback.end()) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
return it->second;
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
u32 DeclareConstantBuffers(u32 binding) {
|
|
|
|
for (const auto& [index, size] : ir.GetConstantBuffers()) {
|
2019-09-13 06:55:28 +02:00
|
|
|
const Id type = device.IsKhrUniformBufferStandardLayoutSupported() ? t_cbuf_scalar_ubo
|
|
|
|
: t_cbuf_std140_ubo;
|
2019-05-26 06:43:07 +02:00
|
|
|
const Id id = OpVariable(type, spv::StorageClass::Uniform);
|
2019-03-14 06:23:37 +01:00
|
|
|
AddGlobalVariable(Name(id, fmt::format("cbuf_{}", index)));
|
|
|
|
|
|
|
|
Decorate(id, spv::Decoration::Binding, binding++);
|
|
|
|
Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET);
|
|
|
|
constant_buffers.emplace(index, id);
|
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
return binding;
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
u32 DeclareGlobalBuffers(u32 binding) {
|
|
|
|
for (const auto& [base, usage] : ir.GetGlobalMemory()) {
|
2019-03-14 06:23:37 +01:00
|
|
|
const Id id = OpVariable(t_gmem_ssbo, spv::StorageClass::StorageBuffer);
|
|
|
|
AddGlobalVariable(
|
2019-02-07 04:05:41 +01:00
|
|
|
Name(id, fmt::format("gmem_{}_{}", base.cbuf_index, base.cbuf_offset)));
|
2019-03-14 06:23:37 +01:00
|
|
|
|
|
|
|
Decorate(id, spv::Decoration::Binding, binding++);
|
|
|
|
Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET);
|
2019-02-07 04:05:41 +01:00
|
|
|
global_buffers.emplace(base, id);
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
return binding;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 DeclareTexelBuffers(u32 binding) {
|
|
|
|
for (const auto& sampler : ir.GetSamplers()) {
|
|
|
|
if (!sampler.IsBuffer()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ASSERT(!sampler.IsArray());
|
|
|
|
ASSERT(!sampler.IsShadow());
|
|
|
|
|
|
|
|
constexpr auto dim = spv::Dim::Buffer;
|
|
|
|
constexpr int depth = 0;
|
|
|
|
constexpr int arrayed = 0;
|
|
|
|
constexpr bool ms = false;
|
|
|
|
constexpr int sampled = 1;
|
|
|
|
constexpr auto format = spv::ImageFormat::Unknown;
|
|
|
|
const Id image_type = TypeImage(t_float, dim, depth, arrayed, ms, sampled, format);
|
|
|
|
const Id pointer_type = TypePointer(spv::StorageClass::UniformConstant, image_type);
|
|
|
|
const Id id = OpVariable(pointer_type, spv::StorageClass::UniformConstant);
|
|
|
|
AddGlobalVariable(Name(id, fmt::format("sampler_{}", sampler.GetIndex())));
|
|
|
|
Decorate(id, spv::Decoration::Binding, binding++);
|
|
|
|
Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET);
|
|
|
|
|
|
|
|
texel_buffers.emplace(sampler.GetIndex(), TexelBuffer{image_type, id});
|
|
|
|
}
|
|
|
|
return binding;
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
u32 DeclareSamplers(u32 binding) {
|
2019-03-14 06:23:37 +01:00
|
|
|
for (const auto& sampler : ir.GetSamplers()) {
|
2019-12-10 03:44:29 +01:00
|
|
|
if (sampler.IsBuffer()) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-03-14 06:23:37 +01:00
|
|
|
const auto dim = GetSamplerDim(sampler);
|
|
|
|
const int depth = sampler.IsShadow() ? 1 : 0;
|
|
|
|
const int arrayed = sampler.IsArray() ? 1 : 0;
|
2019-12-10 03:44:29 +01:00
|
|
|
constexpr bool ms = false;
|
|
|
|
constexpr int sampled = 1;
|
|
|
|
constexpr auto format = spv::ImageFormat::Unknown;
|
|
|
|
const Id image_type = TypeImage(t_float, dim, depth, arrayed, ms, sampled, format);
|
2020-02-23 06:35:16 +01:00
|
|
|
const Id sampler_type = TypeSampledImage(image_type);
|
|
|
|
const Id sampler_pointer_type =
|
|
|
|
TypePointer(spv::StorageClass::UniformConstant, sampler_type);
|
|
|
|
const Id type = sampler.IsIndexed()
|
|
|
|
? TypeArray(sampler_type, Constant(t_uint, sampler.Size()))
|
|
|
|
: sampler_type;
|
|
|
|
const Id pointer_type = TypePointer(spv::StorageClass::UniformConstant, type);
|
2019-03-14 06:23:37 +01:00
|
|
|
const Id id = OpVariable(pointer_type, spv::StorageClass::UniformConstant);
|
|
|
|
AddGlobalVariable(Name(id, fmt::format("sampler_{}", sampler.GetIndex())));
|
2019-12-10 03:44:29 +01:00
|
|
|
Decorate(id, spv::Decoration::Binding, binding++);
|
|
|
|
Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET);
|
2019-03-14 06:23:37 +01:00
|
|
|
|
2020-02-23 06:35:16 +01:00
|
|
|
sampled_images.emplace(sampler.GetIndex(), SampledImage{image_type, sampler_type,
|
|
|
|
sampler_pointer_type, id});
|
2019-12-10 03:44:29 +01:00
|
|
|
}
|
|
|
|
return binding;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 DeclareImages(u32 binding) {
|
|
|
|
for (const auto& image : ir.GetImages()) {
|
|
|
|
const auto [dim, arrayed] = GetImageDim(image);
|
|
|
|
constexpr int depth = 0;
|
|
|
|
constexpr bool ms = false;
|
|
|
|
constexpr int sampled = 2; // This won't be accessed with a sampler
|
|
|
|
constexpr auto format = spv::ImageFormat::Unknown;
|
|
|
|
const Id image_type = TypeImage(t_uint, dim, depth, arrayed, ms, sampled, format, {});
|
|
|
|
const Id pointer_type = TypePointer(spv::StorageClass::UniformConstant, image_type);
|
|
|
|
const Id id = OpVariable(pointer_type, spv::StorageClass::UniformConstant);
|
|
|
|
AddGlobalVariable(Name(id, fmt::format("image_{}", image.GetIndex())));
|
2019-03-14 06:23:37 +01:00
|
|
|
|
|
|
|
Decorate(id, spv::Decoration::Binding, binding++);
|
|
|
|
Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET);
|
2019-12-10 03:44:29 +01:00
|
|
|
if (image.IsRead() && !image.IsWritten()) {
|
|
|
|
Decorate(id, spv::Decoration::NonWritable);
|
|
|
|
} else if (image.IsWritten() && !image.IsRead()) {
|
|
|
|
Decorate(id, spv::Decoration::NonReadable);
|
|
|
|
}
|
|
|
|
|
|
|
|
images.emplace(static_cast<u32>(image.GetIndex()), StorageImage{image_type, id});
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
return binding;
|
|
|
|
}
|
|
|
|
|
2020-01-24 05:15:50 +01:00
|
|
|
bool IsRenderTargetEnabled(u32 rt) const {
|
|
|
|
for (u32 component = 0; component < 4; ++component) {
|
|
|
|
if (header.ps.IsColorComponentOutputEnabled(rt, component)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
bool IsInputAttributeArray() const {
|
|
|
|
return stage == ShaderType::TesselationControl || stage == ShaderType::TesselationEval ||
|
|
|
|
stage == ShaderType::Geometry;
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
bool IsOutputAttributeArray() const {
|
|
|
|
return stage == ShaderType::TesselationControl;
|
|
|
|
}
|
2019-03-14 06:23:37 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
u32 GetNumInputVertices() const {
|
|
|
|
switch (stage) {
|
|
|
|
case ShaderType::Geometry:
|
2020-03-03 01:36:25 +01:00
|
|
|
return GetNumPrimitiveTopologyVertices(registry.GetGraphicsInfo().primitive_topology);
|
2019-12-10 03:44:29 +01:00
|
|
|
case ShaderType::TesselationControl:
|
|
|
|
case ShaderType::TesselationEval:
|
|
|
|
return NumInputPatches;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return 1;
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
}
|
2019-03-14 06:23:37 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
u32 GetNumOutputVertices() const {
|
|
|
|
switch (stage) {
|
|
|
|
case ShaderType::TesselationControl:
|
|
|
|
return header.common2.threads_per_input_primitive;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::tuple<Id, VertexIndices> DeclareVertexStruct() {
|
|
|
|
struct BuiltIn {
|
|
|
|
Id type;
|
|
|
|
spv::BuiltIn builtin;
|
|
|
|
const char* name;
|
|
|
|
};
|
|
|
|
std::vector<BuiltIn> members;
|
|
|
|
members.reserve(4);
|
|
|
|
|
|
|
|
const auto AddBuiltIn = [&](Id type, spv::BuiltIn builtin, const char* name) {
|
|
|
|
const auto index = static_cast<u32>(members.size());
|
|
|
|
members.push_back(BuiltIn{type, builtin, name});
|
|
|
|
return index;
|
2019-03-14 06:23:37 +01:00
|
|
|
};
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
VertexIndices indices;
|
|
|
|
indices.position = AddBuiltIn(t_float4, spv::BuiltIn::Position, "position");
|
|
|
|
|
2020-02-16 06:40:17 +01:00
|
|
|
if (ir.UsesLayer()) {
|
|
|
|
if (stage != ShaderType::Vertex || device.IsExtShaderViewportIndexLayerSupported()) {
|
|
|
|
indices.layer = AddBuiltIn(t_int, spv::BuiltIn::Layer, "layer");
|
|
|
|
} else {
|
|
|
|
LOG_ERROR(
|
|
|
|
Render_Vulkan,
|
|
|
|
"Shader requires Layer but it's not supported on this stage with this device.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
if (ir.UsesViewportIndex()) {
|
|
|
|
if (stage != ShaderType::Vertex || device.IsExtShaderViewportIndexLayerSupported()) {
|
|
|
|
indices.viewport = AddBuiltIn(t_int, spv::BuiltIn::ViewportIndex, "viewport_index");
|
|
|
|
} else {
|
2020-02-16 06:40:17 +01:00
|
|
|
LOG_ERROR(Render_Vulkan, "Shader requires ViewportIndex but it's not supported on "
|
|
|
|
"this stage with this device.");
|
2019-12-10 03:44:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ir.UsesPointSize() || specialization.point_size) {
|
|
|
|
indices.point_size = AddBuiltIn(t_float, spv::BuiltIn::PointSize, "point_size");
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto& output_attributes = ir.GetOutputAttributes();
|
|
|
|
const bool declare_clip_distances =
|
|
|
|
std::any_of(output_attributes.begin(), output_attributes.end(), [](const auto& index) {
|
|
|
|
return index == Attribute::Index::ClipDistances0123 ||
|
|
|
|
index == Attribute::Index::ClipDistances4567;
|
|
|
|
});
|
|
|
|
if (declare_clip_distances) {
|
|
|
|
indices.clip_distances = AddBuiltIn(TypeArray(t_float, Constant(t_uint, 8)),
|
|
|
|
spv::BuiltIn::ClipDistance, "clip_distances");
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<Id> member_types;
|
|
|
|
member_types.reserve(members.size());
|
|
|
|
for (std::size_t i = 0; i < members.size(); ++i) {
|
|
|
|
member_types.push_back(members[i].type);
|
|
|
|
}
|
|
|
|
const Id per_vertex_struct = Name(TypeStruct(member_types), "PerVertex");
|
|
|
|
Decorate(per_vertex_struct, spv::Decoration::Block);
|
|
|
|
|
|
|
|
for (std::size_t index = 0; index < members.size(); ++index) {
|
|
|
|
const auto& member = members[index];
|
|
|
|
MemberName(per_vertex_struct, static_cast<u32>(index), member.name);
|
|
|
|
MemberDecorate(per_vertex_struct, static_cast<u32>(index), spv::Decoration::BuiltIn,
|
|
|
|
static_cast<u32>(member.builtin));
|
|
|
|
}
|
2019-03-14 06:23:37 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
return {per_vertex_struct, indices};
|
2019-03-14 06:23:37 +01:00
|
|
|
}
|
|
|
|
|
2019-03-14 06:30:19 +01:00
|
|
|
void VisitBasicBlock(const NodeBlock& bb) {
|
2019-06-05 03:44:06 +02:00
|
|
|
for (const auto& node : bb) {
|
2019-12-10 03:44:29 +01:00
|
|
|
[[maybe_unused]] const Type type = Visit(node).type;
|
|
|
|
ASSERT(type == Type::Void);
|
2019-03-14 06:30:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression Visit(const Node& node) {
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto operation = std::get_if<OperationNode>(&*node)) {
|
2020-01-04 19:40:57 +01:00
|
|
|
if (const auto amend_index = operation->GetAmendIndex()) {
|
|
|
|
[[maybe_unused]] const Type type = Visit(ir.GetAmendNode(*amend_index)).type;
|
2019-12-30 18:54:53 +01:00
|
|
|
ASSERT(type == Type::Void);
|
|
|
|
}
|
2019-03-14 06:36:14 +01:00
|
|
|
const auto operation_index = static_cast<std::size_t>(operation->GetCode());
|
|
|
|
const auto decompiler = operation_decompilers[operation_index];
|
|
|
|
if (decompiler == nullptr) {
|
|
|
|
UNREACHABLE_MSG("Operation decompiler {} not defined", operation_index);
|
|
|
|
}
|
|
|
|
return (this->*decompiler)(*operation);
|
2019-12-10 03:44:29 +01:00
|
|
|
}
|
2019-03-14 06:30:19 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
if (const auto gpr = std::get_if<GprNode>(&*node)) {
|
2019-03-14 06:43:40 +01:00
|
|
|
const u32 index = gpr->GetIndex();
|
|
|
|
if (index == Register::ZeroIndex) {
|
2019-12-10 03:44:29 +01:00
|
|
|
return {v_float_zero, Type::Float};
|
2019-03-14 06:43:40 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
return {OpLoad(t_float, registers.at(index)), Type::Float};
|
|
|
|
}
|
2019-03-14 06:30:19 +01:00
|
|
|
|
2020-01-07 19:53:46 +01:00
|
|
|
if (const auto cv = std::get_if<CustomVarNode>(&*node)) {
|
|
|
|
const u32 index = cv->GetIndex();
|
|
|
|
return {OpLoad(t_float, custom_variables.at(index)), Type::Float};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
if (const auto immediate = std::get_if<ImmediateNode>(&*node)) {
|
|
|
|
return {Constant(t_uint, immediate->GetValue()), Type::Uint};
|
|
|
|
}
|
2019-03-14 06:30:19 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
if (const auto predicate = std::get_if<PredicateNode>(&*node)) {
|
2019-03-14 06:43:40 +01:00
|
|
|
const auto value = [&]() -> Id {
|
|
|
|
switch (const auto index = predicate->GetIndex(); index) {
|
|
|
|
case Tegra::Shader::Pred::UnusedIndex:
|
|
|
|
return v_true;
|
|
|
|
case Tegra::Shader::Pred::NeverExecute:
|
|
|
|
return v_false;
|
|
|
|
default:
|
2019-12-10 03:44:29 +01:00
|
|
|
return OpLoad(t_bool, predicates.at(index));
|
2019-03-14 06:43:40 +01:00
|
|
|
}
|
|
|
|
}();
|
|
|
|
if (predicate->IsNegated()) {
|
2019-12-10 03:44:29 +01:00
|
|
|
return {OpLogicalNot(t_bool, value), Type::Bool};
|
2019-03-14 06:43:40 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
return {value, Type::Bool};
|
|
|
|
}
|
2019-03-14 06:30:19 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
if (const auto abuf = std::get_if<AbufNode>(&*node)) {
|
2019-03-14 06:43:40 +01:00
|
|
|
const auto attribute = abuf->GetIndex();
|
2019-12-10 03:44:29 +01:00
|
|
|
const u32 element = abuf->GetElement();
|
|
|
|
const auto& buffer = abuf->GetBuffer();
|
|
|
|
|
|
|
|
const auto ArrayPass = [&](Id pointer_type, Id composite, std::vector<u32> indices) {
|
|
|
|
std::vector<Id> members;
|
|
|
|
members.reserve(std::size(indices) + 1);
|
|
|
|
|
|
|
|
if (buffer && IsInputAttributeArray()) {
|
|
|
|
members.push_back(AsUint(Visit(buffer)));
|
|
|
|
}
|
|
|
|
for (const u32 index : indices) {
|
|
|
|
members.push_back(Constant(t_uint, index));
|
|
|
|
}
|
|
|
|
return OpAccessChain(pointer_type, composite, members);
|
|
|
|
};
|
2019-03-14 06:43:40 +01:00
|
|
|
|
|
|
|
switch (attribute) {
|
2019-12-10 03:44:29 +01:00
|
|
|
case Attribute::Index::Position: {
|
|
|
|
if (stage == ShaderType::Fragment) {
|
|
|
|
return {OpLoad(t_float, AccessElement(t_in_float, frag_coord, element)),
|
|
|
|
Type::Float};
|
|
|
|
}
|
2019-12-10 04:55:21 +01:00
|
|
|
const std::vector elements = {in_indices.position.value(), element};
|
2019-12-10 03:44:29 +01:00
|
|
|
return {OpLoad(t_float, ArrayPass(t_in_float, in_vertex, elements)), Type::Float};
|
|
|
|
}
|
|
|
|
case Attribute::Index::PointCoord: {
|
|
|
|
switch (element) {
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
return {OpCompositeExtract(t_float, OpLoad(t_float2, point_coord), element),
|
|
|
|
Type::Float};
|
2019-03-14 06:43:40 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
UNIMPLEMENTED_MSG("Unimplemented point coord element={}", element);
|
|
|
|
return {v_float_zero, Type::Float};
|
|
|
|
}
|
2019-03-14 06:43:40 +01:00
|
|
|
case Attribute::Index::TessCoordInstanceIDVertexID:
|
|
|
|
// TODO(Subv): Find out what the values are for the first two elements when inside a
|
|
|
|
// vertex shader, and what's the value of the fourth element when inside a Tess Eval
|
|
|
|
// shader.
|
|
|
|
switch (element) {
|
2019-12-10 03:44:29 +01:00
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
return {OpLoad(t_float, AccessElement(t_in_float, tess_coord, element)),
|
|
|
|
Type::Float};
|
2019-03-14 06:43:40 +01:00
|
|
|
case 2:
|
2020-02-14 00:25:28 +01:00
|
|
|
return {
|
|
|
|
OpISub(t_int, OpLoad(t_int, instance_index), OpLoad(t_int, base_instance)),
|
|
|
|
Type::Int};
|
2019-03-14 06:43:40 +01:00
|
|
|
case 3:
|
2020-02-14 00:25:28 +01:00
|
|
|
return {OpISub(t_int, OpLoad(t_int, vertex_index), OpLoad(t_int, base_vertex)),
|
|
|
|
Type::Int};
|
2019-03-14 06:43:40 +01:00
|
|
|
}
|
|
|
|
UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element);
|
2019-12-10 03:44:29 +01:00
|
|
|
return {Constant(t_uint, 0U), Type::Uint};
|
2019-03-14 06:43:40 +01:00
|
|
|
case Attribute::Index::FrontFacing:
|
|
|
|
// TODO(Subv): Find out what the values are for the other elements.
|
2019-11-18 22:35:21 +01:00
|
|
|
ASSERT(stage == ShaderType::Fragment);
|
2019-03-14 06:43:40 +01:00
|
|
|
if (element == 3) {
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id is_front_facing = OpLoad(t_bool, front_facing);
|
|
|
|
const Id true_value = Constant(t_int, static_cast<s32>(-1));
|
|
|
|
const Id false_value = Constant(t_int, 0);
|
|
|
|
return {OpSelect(t_int, is_front_facing, true_value, false_value), Type::Int};
|
2019-03-14 06:43:40 +01:00
|
|
|
}
|
|
|
|
UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element);
|
2019-12-10 03:44:29 +01:00
|
|
|
return {v_float_zero, Type::Float};
|
2019-03-14 06:43:40 +01:00
|
|
|
default:
|
|
|
|
if (IsGenericAttribute(attribute)) {
|
2019-12-10 03:44:29 +01:00
|
|
|
const u32 location = GetGenericAttributeLocation(attribute);
|
|
|
|
const auto type_descriptor = GetAttributeType(location);
|
|
|
|
const Type type = type_descriptor.type;
|
|
|
|
const Id attribute_id = input_attributes.at(attribute);
|
2019-12-10 04:55:21 +01:00
|
|
|
const std::vector elements = {element};
|
|
|
|
const Id pointer = ArrayPass(type_descriptor.scalar, attribute_id, elements);
|
2019-12-10 03:44:29 +01:00
|
|
|
return {OpLoad(GetTypeDefinition(type), pointer), type};
|
2019-03-14 06:43:40 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
|
2019-12-10 03:44:29 +01:00
|
|
|
return {v_float_zero, Type::Float};
|
|
|
|
}
|
2019-03-14 06:30:19 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
if (const auto cbuf = std::get_if<CbufNode>(&*node)) {
|
2019-06-05 03:44:06 +02:00
|
|
|
const Node& offset = cbuf->GetOffset();
|
2019-03-14 06:43:40 +01:00
|
|
|
const Id buffer_id = constant_buffers.at(cbuf->GetIndex());
|
|
|
|
|
2019-05-26 06:43:07 +02:00
|
|
|
Id pointer{};
|
2019-09-13 06:55:28 +02:00
|
|
|
if (device.IsKhrUniformBufferStandardLayoutSupported()) {
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id buffer_offset =
|
|
|
|
OpShiftRightLogical(t_uint, AsUint(Visit(offset)), Constant(t_uint, 2U));
|
|
|
|
pointer =
|
|
|
|
OpAccessChain(t_cbuf_float, buffer_id, Constant(t_uint, 0U), buffer_offset);
|
2019-03-14 06:43:40 +01:00
|
|
|
} else {
|
2019-05-26 06:43:07 +02:00
|
|
|
Id buffer_index{};
|
|
|
|
Id buffer_element{};
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
|
2019-05-26 06:43:07 +02:00
|
|
|
// Direct access
|
|
|
|
const u32 offset_imm = immediate->GetValue();
|
|
|
|
ASSERT(offset_imm % 4 == 0);
|
|
|
|
buffer_index = Constant(t_uint, offset_imm / 16);
|
|
|
|
buffer_element = Constant(t_uint, (offset_imm / 4) % 4);
|
|
|
|
} else if (std::holds_alternative<OperationNode>(*offset)) {
|
|
|
|
// Indirect access
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id offset_id = AsUint(Visit(offset));
|
|
|
|
const Id unsafe_offset = OpUDiv(t_uint, offset_id, Constant(t_uint, 4));
|
|
|
|
const Id final_offset =
|
|
|
|
OpUMod(t_uint, unsafe_offset, Constant(t_uint, MaxConstBufferElements - 1));
|
|
|
|
buffer_index = OpUDiv(t_uint, final_offset, Constant(t_uint, 4));
|
|
|
|
buffer_element = OpUMod(t_uint, final_offset, Constant(t_uint, 4));
|
2019-05-26 06:43:07 +02:00
|
|
|
} else {
|
|
|
|
UNREACHABLE_MSG("Unmanaged offset node type");
|
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
pointer = OpAccessChain(t_cbuf_float, buffer_id, Constant(t_uint, 0), buffer_index,
|
|
|
|
buffer_element);
|
2019-03-14 06:43:40 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
return {OpLoad(t_float, pointer), Type::Float};
|
|
|
|
}
|
2019-03-14 06:30:19 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
if (const auto gmem = std::get_if<GmemNode>(&*node)) {
|
2020-01-26 01:03:02 +01:00
|
|
|
return {OpLoad(t_uint, GetGlobalMemoryPointer(*gmem)), Type::Uint};
|
2019-12-10 03:44:29 +01:00
|
|
|
}
|
2019-03-14 06:43:40 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
if (const auto lmem = std::get_if<LmemNode>(&*node)) {
|
|
|
|
Id address = AsUint(Visit(lmem->GetAddress()));
|
|
|
|
address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U));
|
|
|
|
const Id pointer = OpAccessChain(t_prv_float, local_memory, address);
|
|
|
|
return {OpLoad(t_float, pointer), Type::Float};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (const auto smem = std::get_if<SmemNode>(&*node)) {
|
2020-01-26 01:03:02 +01:00
|
|
|
return {OpLoad(t_uint, GetSharedMemoryPointer(*smem)), Type::Uint};
|
2019-12-10 03:44:29 +01:00
|
|
|
}
|
2019-03-14 06:30:19 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) {
|
|
|
|
const Id flag = internal_flags.at(static_cast<std::size_t>(internal_flag->GetFlag()));
|
|
|
|
return {OpLoad(t_bool, flag), Type::Bool};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
|
2020-01-04 19:40:57 +01:00
|
|
|
if (const auto amend_index = conditional->GetAmendIndex()) {
|
|
|
|
[[maybe_unused]] const Type type = Visit(ir.GetAmendNode(*amend_index)).type;
|
2019-12-30 18:54:53 +01:00
|
|
|
ASSERT(type == Type::Void);
|
|
|
|
}
|
2019-03-14 06:43:40 +01:00
|
|
|
// It's invalid to call conditional on nested nodes, use an operation instead
|
|
|
|
const Id true_label = OpLabel();
|
|
|
|
const Id skip_label = OpLabel();
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id condition = AsBool(Visit(conditional->GetCondition()));
|
|
|
|
OpSelectionMerge(skip_label, spv::SelectionControlMask::MaskNone);
|
|
|
|
OpBranchConditional(condition, true_label, skip_label);
|
|
|
|
AddLabel(true_label);
|
2019-03-14 06:43:40 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
conditional_branch_set = true;
|
|
|
|
inside_branch = false;
|
2019-03-14 06:43:40 +01:00
|
|
|
VisitBasicBlock(conditional->GetCode());
|
2019-12-10 03:44:29 +01:00
|
|
|
conditional_branch_set = false;
|
|
|
|
if (!inside_branch) {
|
|
|
|
OpBranch(skip_label);
|
2019-09-25 20:34:08 +02:00
|
|
|
} else {
|
2019-12-10 03:44:29 +01:00
|
|
|
inside_branch = false;
|
2019-09-25 20:34:08 +02:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
AddLabel(skip_label);
|
2019-03-14 06:43:40 +01:00
|
|
|
return {};
|
2019-12-10 03:44:29 +01:00
|
|
|
}
|
2019-03-14 06:30:19 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
if (const auto comment = std::get_if<CommentNode>(&*node)) {
|
|
|
|
Name(OpUndef(t_void), comment->GetText());
|
2019-03-14 06:30:19 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
UNREACHABLE();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-03-14 06:36:14 +01:00
|
|
|
template <Id (Module::*func)(Id, Id), Type result_type, Type type_a = result_type>
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression Unary(Operation operation) {
|
2019-03-14 06:36:14 +01:00
|
|
|
const Id type_def = GetTypeDefinition(result_type);
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id op_a = As(Visit(operation[0]), type_a);
|
2019-03-14 06:36:14 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id value = (this->*func)(type_def, op_a);
|
2019-03-14 06:36:14 +01:00
|
|
|
if (IsPrecise(operation)) {
|
|
|
|
Decorate(value, spv::Decoration::NoContraction);
|
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
return {value, result_type};
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template <Id (Module::*func)(Id, Id, Id), Type result_type, Type type_a = result_type,
|
|
|
|
Type type_b = type_a>
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression Binary(Operation operation) {
|
2019-03-14 06:36:14 +01:00
|
|
|
const Id type_def = GetTypeDefinition(result_type);
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id op_a = As(Visit(operation[0]), type_a);
|
|
|
|
const Id op_b = As(Visit(operation[1]), type_b);
|
2019-03-14 06:36:14 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id value = (this->*func)(type_def, op_a, op_b);
|
2019-03-14 06:36:14 +01:00
|
|
|
if (IsPrecise(operation)) {
|
|
|
|
Decorate(value, spv::Decoration::NoContraction);
|
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
return {value, result_type};
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template <Id (Module::*func)(Id, Id, Id, Id), Type result_type, Type type_a = result_type,
|
|
|
|
Type type_b = type_a, Type type_c = type_b>
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression Ternary(Operation operation) {
|
2019-03-14 06:36:14 +01:00
|
|
|
const Id type_def = GetTypeDefinition(result_type);
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id op_a = As(Visit(operation[0]), type_a);
|
|
|
|
const Id op_b = As(Visit(operation[1]), type_b);
|
|
|
|
const Id op_c = As(Visit(operation[2]), type_c);
|
2019-03-14 06:36:14 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id value = (this->*func)(type_def, op_a, op_b, op_c);
|
2019-03-14 06:36:14 +01:00
|
|
|
if (IsPrecise(operation)) {
|
|
|
|
Decorate(value, spv::Decoration::NoContraction);
|
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
return {value, result_type};
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template <Id (Module::*func)(Id, Id, Id, Id, Id), Type result_type, Type type_a = result_type,
|
|
|
|
Type type_b = type_a, Type type_c = type_b, Type type_d = type_c>
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression Quaternary(Operation operation) {
|
2019-03-14 06:36:14 +01:00
|
|
|
const Id type_def = GetTypeDefinition(result_type);
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id op_a = As(Visit(operation[0]), type_a);
|
|
|
|
const Id op_b = As(Visit(operation[1]), type_b);
|
|
|
|
const Id op_c = As(Visit(operation[2]), type_c);
|
|
|
|
const Id op_d = As(Visit(operation[3]), type_d);
|
2019-03-14 06:36:14 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id value = (this->*func)(type_def, op_a, op_b, op_c, op_d);
|
2019-03-14 06:36:14 +01:00
|
|
|
if (IsPrecise(operation)) {
|
|
|
|
Decorate(value, spv::Decoration::NoContraction);
|
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
return {value, result_type};
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression Assign(Operation operation) {
|
2019-06-05 03:44:06 +02:00
|
|
|
const Node& dest = operation[0];
|
|
|
|
const Node& src = operation[1];
|
2019-03-14 06:44:23 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression target{};
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto gpr = std::get_if<GprNode>(&*dest)) {
|
2019-03-14 06:44:23 +01:00
|
|
|
if (gpr->GetIndex() == Register::ZeroIndex) {
|
|
|
|
// Writing to Register::ZeroIndex is a no op
|
|
|
|
return {};
|
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
target = {registers.at(gpr->GetIndex()), Type::Float};
|
2019-03-14 06:44:23 +01:00
|
|
|
|
2019-06-05 03:44:06 +02:00
|
|
|
} else if (const auto abuf = std::get_if<AbufNode>(&*dest)) {
|
2019-12-10 03:44:29 +01:00
|
|
|
const auto& buffer = abuf->GetBuffer();
|
|
|
|
const auto ArrayPass = [&](Id pointer_type, Id composite, std::vector<u32> indices) {
|
|
|
|
std::vector<Id> members;
|
|
|
|
members.reserve(std::size(indices) + 1);
|
|
|
|
|
|
|
|
if (buffer && IsOutputAttributeArray()) {
|
|
|
|
members.push_back(AsUint(Visit(buffer)));
|
|
|
|
}
|
|
|
|
for (const u32 index : indices) {
|
|
|
|
members.push_back(Constant(t_uint, index));
|
|
|
|
}
|
|
|
|
return OpAccessChain(pointer_type, composite, members);
|
|
|
|
};
|
|
|
|
|
|
|
|
target = [&]() -> Expression {
|
|
|
|
const u32 element = abuf->GetElement();
|
2019-03-14 06:44:23 +01:00
|
|
|
switch (const auto attribute = abuf->GetIndex(); attribute) {
|
2019-12-10 03:44:29 +01:00
|
|
|
case Attribute::Index::Position: {
|
|
|
|
const u32 index = out_indices.position.value();
|
|
|
|
return {ArrayPass(t_out_float, out_vertex, {index, element}), Type::Float};
|
|
|
|
}
|
2019-07-08 01:36:42 +02:00
|
|
|
case Attribute::Index::LayerViewportPointSize:
|
2019-12-10 03:44:29 +01:00
|
|
|
switch (element) {
|
2020-02-16 06:40:17 +01:00
|
|
|
case 1: {
|
|
|
|
if (!out_indices.layer) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
const u32 index = out_indices.layer.value();
|
|
|
|
return {AccessElement(t_out_int, out_vertex, index), Type::Int};
|
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
case 2: {
|
|
|
|
if (!out_indices.viewport) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
const u32 index = out_indices.viewport.value();
|
|
|
|
return {AccessElement(t_out_int, out_vertex, index), Type::Int};
|
|
|
|
}
|
|
|
|
case 3: {
|
|
|
|
const auto index = out_indices.point_size.value();
|
|
|
|
return {AccessElement(t_out_float, out_vertex, index), Type::Float};
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
UNIMPLEMENTED_MSG("LayerViewportPoint element={}", abuf->GetElement());
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
case Attribute::Index::ClipDistances0123: {
|
|
|
|
const u32 index = out_indices.clip_distances.value();
|
|
|
|
return {AccessElement(t_out_float, out_vertex, index, element), Type::Float};
|
|
|
|
}
|
|
|
|
case Attribute::Index::ClipDistances4567: {
|
|
|
|
const u32 index = out_indices.clip_distances.value();
|
|
|
|
return {AccessElement(t_out_float, out_vertex, index, element + 4),
|
|
|
|
Type::Float};
|
|
|
|
}
|
2019-03-14 06:44:23 +01:00
|
|
|
default:
|
|
|
|
if (IsGenericAttribute(attribute)) {
|
2020-03-06 09:10:39 +01:00
|
|
|
const u8 offset = static_cast<u8>(static_cast<u8>(attribute) * 4 + element);
|
|
|
|
const GenericVaryingDescription description = output_attributes.at(offset);
|
|
|
|
const Id composite = description.id;
|
|
|
|
std::vector<u32> indices;
|
|
|
|
if (!description.is_scalar) {
|
|
|
|
indices.push_back(element - description.first_element);
|
|
|
|
}
|
|
|
|
return {ArrayPass(t_out_float, composite, indices), Type::Float};
|
2019-03-14 06:44:23 +01:00
|
|
|
}
|
|
|
|
UNIMPLEMENTED_MSG("Unhandled output attribute: {}",
|
|
|
|
static_cast<u32>(attribute));
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
} else if (const auto patch = std::get_if<PatchNode>(&*dest)) {
|
|
|
|
target = [&]() -> Expression {
|
|
|
|
const u32 offset = patch->GetOffset();
|
|
|
|
switch (offset) {
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
return {AccessElement(t_out_float, tess_level_outer, offset % 4), Type::Float};
|
|
|
|
case 4:
|
|
|
|
case 5:
|
|
|
|
return {AccessElement(t_out_float, tess_level_inner, offset % 4), Type::Float};
|
|
|
|
}
|
|
|
|
UNIMPLEMENTED_MSG("Unhandled patch output offset: {}", offset);
|
|
|
|
return {};
|
|
|
|
}();
|
|
|
|
|
2019-06-05 03:44:06 +02:00
|
|
|
} else if (const auto lmem = std::get_if<LmemNode>(&*dest)) {
|
2019-12-10 03:44:29 +01:00
|
|
|
Id address = AsUint(Visit(lmem->GetAddress()));
|
|
|
|
address = OpUDiv(t_uint, address, Constant(t_uint, 4));
|
|
|
|
target = {OpAccessChain(t_prv_float, local_memory, address), Type::Float};
|
|
|
|
|
|
|
|
} else if (const auto smem = std::get_if<SmemNode>(&*dest)) {
|
2020-01-26 01:03:02 +01:00
|
|
|
target = {GetSharedMemoryPointer(*smem), Type::Uint};
|
2019-12-10 03:44:29 +01:00
|
|
|
|
|
|
|
} else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
|
2020-01-26 01:03:02 +01:00
|
|
|
target = {GetGlobalMemoryPointer(*gmem), Type::Uint};
|
2019-12-10 03:44:29 +01:00
|
|
|
|
2020-01-07 23:56:03 +01:00
|
|
|
} else if (const auto cv = std::get_if<CustomVarNode>(&*dest)) {
|
|
|
|
target = {custom_variables.at(cv->GetIndex()), Type::Float};
|
2020-01-24 15:44:34 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
} else {
|
|
|
|
UNIMPLEMENTED();
|
2019-03-14 06:44:23 +01:00
|
|
|
}
|
|
|
|
|
2020-02-16 06:40:17 +01:00
|
|
|
if (!target.id) {
|
|
|
|
// On failure we return a nullptr target.id, skip these stores.
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
OpStore(target.id, As(Visit(src), target.type));
|
2019-03-14 06:36:14 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
template <u32 offset>
|
|
|
|
Expression FCastHalf(Operation operation) {
|
|
|
|
const Id value = AsHalfFloat(Visit(operation[0]));
|
|
|
|
return {GetFloatFromHalfScalar(OpCompositeExtract(t_scalar_half, value, offset)),
|
|
|
|
Type::Float};
|
2019-07-20 23:38:25 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression FSwizzleAdd(Operation operation) {
|
|
|
|
const Id minus = Constant(t_float, -1.0f);
|
|
|
|
const Id plus = v_float_one;
|
|
|
|
const Id zero = v_float_zero;
|
|
|
|
const Id lut_a = ConstantComposite(t_float4, minus, plus, minus, zero);
|
|
|
|
const Id lut_b = ConstantComposite(t_float4, minus, minus, plus, minus);
|
2019-07-20 23:38:25 +02:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Id mask = OpLoad(t_uint, thread_id);
|
|
|
|
mask = OpBitwiseAnd(t_uint, mask, Constant(t_uint, 3));
|
|
|
|
mask = OpShiftLeftLogical(t_uint, mask, Constant(t_uint, 1));
|
|
|
|
mask = OpShiftRightLogical(t_uint, AsUint(Visit(operation[2])), mask);
|
|
|
|
mask = OpBitwiseAnd(t_uint, mask, Constant(t_uint, 3));
|
2019-11-03 03:44:46 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id modifier_a = OpVectorExtractDynamic(t_float, lut_a, mask);
|
|
|
|
const Id modifier_b = OpVectorExtractDynamic(t_float, lut_b, mask);
|
|
|
|
|
|
|
|
const Id op_a = OpFMul(t_float, AsFloat(Visit(operation[0])), modifier_a);
|
|
|
|
const Id op_b = OpFMul(t_float, AsFloat(Visit(operation[1])), modifier_b);
|
|
|
|
return {OpFAdd(t_float, op_a, op_b), Type::Float};
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression HNegate(Operation operation) {
|
|
|
|
const bool is_f16 = device.IsFloat16Supported();
|
|
|
|
const Id minus_one = Constant(t_scalar_half, is_f16 ? 0xbc00 : 0xbf800000);
|
|
|
|
const Id one = Constant(t_scalar_half, is_f16 ? 0x3c00 : 0x3f800000);
|
|
|
|
const auto GetNegate = [&](std::size_t index) {
|
|
|
|
return OpSelect(t_scalar_half, AsBool(Visit(operation[index])), minus_one, one);
|
|
|
|
};
|
|
|
|
const Id negation = OpCompositeConstruct(t_half, GetNegate(1), GetNegate(2));
|
|
|
|
return {OpFMul(t_half, AsHalfFloat(Visit(operation[0])), negation), Type::HalfFloat};
|
2019-04-09 23:41:41 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression HClamp(Operation operation) {
|
|
|
|
const auto Pack = [&](std::size_t index) {
|
|
|
|
const Id scalar = GetHalfScalarFromFloat(AsFloat(Visit(operation[index])));
|
|
|
|
return OpCompositeConstruct(t_half, scalar, scalar);
|
|
|
|
};
|
|
|
|
const Id value = AsHalfFloat(Visit(operation[0]));
|
|
|
|
const Id min = Pack(1);
|
|
|
|
const Id max = Pack(2);
|
|
|
|
|
|
|
|
const Id clamped = OpFClamp(t_half, value, min, max);
|
|
|
|
if (IsPrecise(operation)) {
|
|
|
|
Decorate(clamped, spv::Decoration::NoContraction);
|
|
|
|
}
|
|
|
|
return {clamped, Type::HalfFloat};
|
2019-07-20 23:38:25 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression HCastFloat(Operation operation) {
|
|
|
|
const Id value = GetHalfScalarFromFloat(AsFloat(Visit(operation[0])));
|
|
|
|
return {OpCompositeConstruct(t_half, value, Constant(t_scalar_half, 0)), Type::HalfFloat};
|
2019-04-16 00:48:11 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression HUnpack(Operation operation) {
|
|
|
|
Expression operand = Visit(operation[0]);
|
|
|
|
const auto type = std::get<Tegra::Shader::HalfType>(operation.GetMeta());
|
|
|
|
if (type == Tegra::Shader::HalfType::H0_H1) {
|
|
|
|
return operand;
|
|
|
|
}
|
|
|
|
const auto value = [&] {
|
|
|
|
switch (std::get<Tegra::Shader::HalfType>(operation.GetMeta())) {
|
|
|
|
case Tegra::Shader::HalfType::F32:
|
|
|
|
return GetHalfScalarFromFloat(AsFloat(operand));
|
|
|
|
case Tegra::Shader::HalfType::H0_H0:
|
|
|
|
return OpCompositeExtract(t_scalar_half, AsHalfFloat(operand), 0);
|
|
|
|
case Tegra::Shader::HalfType::H1_H1:
|
|
|
|
return OpCompositeExtract(t_scalar_half, AsHalfFloat(operand), 1);
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return ConstantNull(t_half);
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
return {OpCompositeConstruct(t_half, value, value), Type::HalfFloat};
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression HMergeF32(Operation operation) {
|
|
|
|
const Id value = AsHalfFloat(Visit(operation[0]));
|
|
|
|
return {GetFloatFromHalfScalar(OpCompositeExtract(t_scalar_half, value, 0)), Type::Float};
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
template <u32 offset>
|
|
|
|
Expression HMergeHN(Operation operation) {
|
|
|
|
const Id target = AsHalfFloat(Visit(operation[0]));
|
|
|
|
const Id source = AsHalfFloat(Visit(operation[1]));
|
|
|
|
const Id object = OpCompositeExtract(t_scalar_half, source, offset);
|
|
|
|
return {OpCompositeInsert(t_half, object, target, offset), Type::HalfFloat};
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression HPack2(Operation operation) {
|
|
|
|
const Id low = GetHalfScalarFromFloat(AsFloat(Visit(operation[0])));
|
|
|
|
const Id high = GetHalfScalarFromFloat(AsFloat(Visit(operation[1])));
|
|
|
|
return {OpCompositeConstruct(t_half, low, high), Type::HalfFloat};
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression LogicalAssign(Operation operation) {
|
2019-06-05 03:44:06 +02:00
|
|
|
const Node& dest = operation[0];
|
|
|
|
const Node& src = operation[1];
|
2019-03-14 06:44:23 +01:00
|
|
|
|
|
|
|
Id target{};
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto pred = std::get_if<PredicateNode>(&*dest)) {
|
2019-03-14 06:44:23 +01:00
|
|
|
ASSERT_MSG(!pred->IsNegated(), "Negating logical assignment");
|
|
|
|
|
|
|
|
const auto index = pred->GetIndex();
|
|
|
|
switch (index) {
|
|
|
|
case Tegra::Shader::Pred::NeverExecute:
|
|
|
|
case Tegra::Shader::Pred::UnusedIndex:
|
|
|
|
// Writing to these predicates is a no-op
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
target = predicates.at(index);
|
|
|
|
|
2019-06-05 03:44:06 +02:00
|
|
|
} else if (const auto flag = std::get_if<InternalFlagNode>(&*dest)) {
|
2019-03-14 06:44:23 +01:00
|
|
|
target = internal_flags.at(static_cast<u32>(flag->GetFlag()));
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
OpStore(target, AsBool(Visit(src)));
|
2019-03-14 06:36:14 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Id GetTextureSampler(Operation operation) {
|
|
|
|
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
|
|
|
|
ASSERT(!meta.sampler.IsBuffer());
|
|
|
|
|
|
|
|
const auto& entry = sampled_images.at(meta.sampler.GetIndex());
|
2020-02-23 06:35:16 +01:00
|
|
|
Id sampler = entry.variable;
|
|
|
|
if (meta.sampler.IsIndexed()) {
|
|
|
|
const Id index = AsInt(Visit(meta.index));
|
|
|
|
sampler = OpAccessChain(entry.sampler_pointer_type, sampler, index);
|
|
|
|
}
|
|
|
|
return OpLoad(entry.sampler_type, sampler);
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Id GetTextureImage(Operation operation) {
|
|
|
|
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
|
|
|
|
const u32 index = meta.sampler.GetIndex();
|
|
|
|
if (meta.sampler.IsBuffer()) {
|
|
|
|
const auto& entry = texel_buffers.at(index);
|
|
|
|
return OpLoad(entry.image_type, entry.image);
|
|
|
|
} else {
|
|
|
|
const auto& entry = sampled_images.at(index);
|
|
|
|
return OpImage(entry.image_type, GetTextureSampler(operation));
|
|
|
|
}
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Id GetImage(Operation operation) {
|
|
|
|
const auto& meta = std::get<MetaImage>(operation.GetMeta());
|
|
|
|
const auto entry = images.at(meta.image.GetIndex());
|
|
|
|
return OpLoad(entry.image_type, entry.image);
|
2019-03-14 06:45:51 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Id AssembleVector(const std::vector<Id>& coords, Type type) {
|
|
|
|
const Id coords_type = GetTypeVectorDefinitionLut(type).at(coords.size() - 1);
|
|
|
|
return coords.size() == 1 ? coords[0] : OpCompositeConstruct(coords_type, coords);
|
2019-03-14 06:45:51 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Id GetCoordinates(Operation operation, Type type) {
|
2019-03-14 06:45:51 +01:00
|
|
|
std::vector<Id> coords;
|
|
|
|
for (std::size_t i = 0; i < operation.GetOperandsCount(); ++i) {
|
2019-12-10 03:44:29 +01:00
|
|
|
coords.push_back(As(Visit(operation[i]), type));
|
2019-03-14 06:45:51 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
if (const auto meta = std::get_if<MetaTexture>(&operation.GetMeta())) {
|
|
|
|
// Add array coordinate for textures
|
|
|
|
if (meta->sampler.IsArray()) {
|
|
|
|
Id array = AsInt(Visit(meta->array));
|
|
|
|
if (type == Type::Float) {
|
|
|
|
array = OpConvertSToF(t_float, array);
|
|
|
|
}
|
|
|
|
coords.push_back(array);
|
|
|
|
}
|
2019-03-14 06:45:51 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
return AssembleVector(coords, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
Id GetOffsetCoordinates(Operation operation) {
|
|
|
|
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
|
|
|
|
std::vector<Id> coords;
|
|
|
|
coords.reserve(meta.aoffi.size());
|
|
|
|
for (const auto& coord : meta.aoffi) {
|
|
|
|
coords.push_back(AsInt(Visit(coord)));
|
2019-03-14 06:45:51 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
return AssembleVector(coords, Type::Int);
|
|
|
|
}
|
2019-03-14 06:45:51 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
std::pair<Id, Id> GetDerivatives(Operation operation) {
|
|
|
|
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
|
|
|
|
const auto& derivatives = meta.derivates;
|
|
|
|
ASSERT(derivatives.size() % 2 == 0);
|
|
|
|
|
|
|
|
const std::size_t components = derivatives.size() / 2;
|
|
|
|
std::vector<Id> dx, dy;
|
|
|
|
dx.reserve(components);
|
|
|
|
dy.reserve(components);
|
|
|
|
for (std::size_t index = 0; index < components; ++index) {
|
|
|
|
dx.push_back(AsFloat(Visit(derivatives.at(index * 2 + 0))));
|
|
|
|
dy.push_back(AsFloat(Visit(derivatives.at(index * 2 + 1))));
|
|
|
|
}
|
|
|
|
return {AssembleVector(dx, Type::Float), AssembleVector(dy, Type::Float)};
|
2019-03-14 06:45:51 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression GetTextureElement(Operation operation, Id sample_value, Type type) {
|
|
|
|
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
|
|
|
|
const auto type_def = GetTypeDefinition(type);
|
|
|
|
return {OpCompositeExtract(type_def, sample_value, meta.element), type};
|
2019-03-14 06:47:09 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression Texture(Operation operation) {
|
|
|
|
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
|
|
|
|
|
|
|
|
const bool can_implicit = stage == ShaderType::Fragment;
|
|
|
|
const Id sampler = GetTextureSampler(operation);
|
|
|
|
const Id coords = GetCoordinates(operation, Type::Float);
|
|
|
|
|
2019-12-19 05:41:22 +01:00
|
|
|
std::vector<Id> operands;
|
|
|
|
spv::ImageOperandsMask mask{};
|
|
|
|
if (meta.bias) {
|
|
|
|
mask = mask | spv::ImageOperandsMask::Bias;
|
|
|
|
operands.push_back(AsFloat(Visit(meta.bias)));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!can_implicit) {
|
|
|
|
mask = mask | spv::ImageOperandsMask::Lod;
|
|
|
|
operands.push_back(v_float_zero);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!meta.aoffi.empty()) {
|
|
|
|
mask = mask | spv::ImageOperandsMask::Offset;
|
|
|
|
operands.push_back(GetOffsetCoordinates(operation));
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
if (meta.depth_compare) {
|
|
|
|
// Depth sampling
|
|
|
|
UNIMPLEMENTED_IF(meta.bias);
|
|
|
|
const Id dref = AsFloat(Visit(meta.depth_compare));
|
|
|
|
if (can_implicit) {
|
2019-12-19 05:41:22 +01:00
|
|
|
return {
|
|
|
|
OpImageSampleDrefImplicitLod(t_float, sampler, coords, dref, mask, operands),
|
|
|
|
Type::Float};
|
2019-12-10 03:44:29 +01:00
|
|
|
} else {
|
2019-12-19 05:41:22 +01:00
|
|
|
return {
|
|
|
|
OpImageSampleDrefExplicitLod(t_float, sampler, coords, dref, mask, operands),
|
|
|
|
Type::Float};
|
2019-12-10 03:44:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Id texture;
|
|
|
|
if (can_implicit) {
|
|
|
|
texture = OpImageSampleImplicitLod(t_float4, sampler, coords, mask, operands);
|
|
|
|
} else {
|
2019-12-19 05:41:22 +01:00
|
|
|
texture = OpImageSampleExplicitLod(t_float4, sampler, coords, mask, operands);
|
2019-12-10 03:44:29 +01:00
|
|
|
}
|
|
|
|
return GetTextureElement(operation, texture, Type::Float);
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression TextureLod(Operation operation) {
|
|
|
|
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
|
|
|
|
|
|
|
|
const Id sampler = GetTextureSampler(operation);
|
|
|
|
const Id coords = GetCoordinates(operation, Type::Float);
|
|
|
|
const Id lod = AsFloat(Visit(meta.lod));
|
|
|
|
|
|
|
|
spv::ImageOperandsMask mask = spv::ImageOperandsMask::Lod;
|
2019-12-19 05:41:22 +01:00
|
|
|
std::vector<Id> operands{lod};
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
if (!meta.aoffi.empty()) {
|
|
|
|
mask = mask | spv::ImageOperandsMask::Offset;
|
|
|
|
operands.push_back(GetOffsetCoordinates(operation));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (meta.sampler.IsShadow()) {
|
|
|
|
const Id dref = AsFloat(Visit(meta.depth_compare));
|
2019-12-19 05:41:22 +01:00
|
|
|
return {OpImageSampleDrefExplicitLod(t_float, sampler, coords, dref, mask, operands),
|
|
|
|
Type::Float};
|
2019-12-10 03:44:29 +01:00
|
|
|
}
|
2019-12-19 05:41:22 +01:00
|
|
|
const Id texture = OpImageSampleExplicitLod(t_float4, sampler, coords, mask, operands);
|
2019-12-10 03:44:29 +01:00
|
|
|
return GetTextureElement(operation, texture, Type::Float);
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression TextureGather(Operation operation) {
|
|
|
|
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
|
|
|
|
UNIMPLEMENTED_IF(!meta.aoffi.empty());
|
2019-03-14 06:47:09 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id coords = GetCoordinates(operation, Type::Float);
|
|
|
|
Id texture{};
|
|
|
|
if (meta.sampler.IsShadow()) {
|
|
|
|
texture = OpImageDrefGather(t_float4, GetTextureSampler(operation), coords,
|
|
|
|
AsFloat(Visit(meta.depth_compare)));
|
2019-03-14 06:47:09 +01:00
|
|
|
} else {
|
|
|
|
u32 component_value = 0;
|
2019-12-10 03:44:29 +01:00
|
|
|
if (meta.component) {
|
|
|
|
const auto component = std::get_if<ImmediateNode>(&*meta.component);
|
2019-03-14 06:47:09 +01:00
|
|
|
ASSERT_MSG(component, "Component is not an immediate value");
|
|
|
|
component_value = component->GetValue();
|
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
texture = OpImageGather(t_float4, GetTextureSampler(operation), coords,
|
|
|
|
Constant(t_uint, component_value));
|
2019-03-14 06:47:09 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
return GetTextureElement(operation, texture, Type::Float);
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression TextureQueryDimensions(Operation operation) {
|
|
|
|
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
|
|
|
|
UNIMPLEMENTED_IF(!meta.aoffi.empty());
|
|
|
|
UNIMPLEMENTED_IF(meta.depth_compare);
|
2019-03-14 06:47:09 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
const auto image_id = GetTextureImage(operation);
|
|
|
|
if (meta.element == 3) {
|
|
|
|
return {OpImageQueryLevels(t_int, image_id), Type::Int};
|
2019-03-14 06:47:09 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id lod = AsUint(Visit(operation[0]));
|
2019-03-14 06:47:09 +01:00
|
|
|
const std::size_t coords_count = [&]() {
|
2019-12-10 03:44:29 +01:00
|
|
|
switch (const auto type = meta.sampler.GetType(); type) {
|
2019-03-14 06:47:09 +01:00
|
|
|
case Tegra::Shader::TextureType::Texture1D:
|
|
|
|
return 1;
|
|
|
|
case Tegra::Shader::TextureType::Texture2D:
|
|
|
|
case Tegra::Shader::TextureType::TextureCube:
|
|
|
|
return 2;
|
|
|
|
case Tegra::Shader::TextureType::Texture3D:
|
|
|
|
return 3;
|
|
|
|
default:
|
|
|
|
UNREACHABLE_MSG("Invalid texture type={}", static_cast<u32>(type));
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
if (meta.element >= coords_count) {
|
|
|
|
return {v_float_zero, Type::Float};
|
2019-03-14 06:47:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const std::array<Id, 3> types = {t_int, t_int2, t_int3};
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id sizes = OpImageQuerySizeLod(types.at(coords_count - 1), image_id, lod);
|
|
|
|
const Id size = OpCompositeExtract(t_int, sizes, meta.element);
|
|
|
|
return {size, Type::Int};
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression TextureQueryLod(Operation operation) {
|
|
|
|
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
|
|
|
|
UNIMPLEMENTED_IF(!meta.aoffi.empty());
|
|
|
|
UNIMPLEMENTED_IF(meta.depth_compare);
|
|
|
|
|
|
|
|
if (meta.element >= 2) {
|
|
|
|
UNREACHABLE_MSG("Invalid element");
|
|
|
|
return {v_float_zero, Type::Float};
|
|
|
|
}
|
|
|
|
const auto sampler_id = GetTextureSampler(operation);
|
|
|
|
|
|
|
|
const Id multiplier = Constant(t_float, 256.0f);
|
|
|
|
const Id multipliers = ConstantComposite(t_float2, multiplier, multiplier);
|
|
|
|
|
|
|
|
const Id coords = GetCoordinates(operation, Type::Float);
|
|
|
|
Id size = OpImageQueryLod(t_float2, sampler_id, coords);
|
|
|
|
size = OpFMul(t_float2, size, multipliers);
|
|
|
|
size = OpConvertFToS(t_int2, size);
|
|
|
|
return GetTextureElement(operation, size, Type::Int);
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression TexelFetch(Operation operation) {
|
|
|
|
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
|
|
|
|
UNIMPLEMENTED_IF(meta.depth_compare);
|
|
|
|
|
|
|
|
const Id image = GetTextureImage(operation);
|
|
|
|
const Id coords = GetCoordinates(operation, Type::Int);
|
|
|
|
Id fetch;
|
|
|
|
if (meta.lod && !meta.sampler.IsBuffer()) {
|
|
|
|
fetch = OpImageFetch(t_float4, image, coords, spv::ImageOperandsMask::Lod,
|
|
|
|
AsInt(Visit(meta.lod)));
|
|
|
|
} else {
|
|
|
|
fetch = OpImageFetch(t_float4, image, coords);
|
|
|
|
}
|
|
|
|
return GetTextureElement(operation, fetch, Type::Float);
|
|
|
|
}
|
|
|
|
|
|
|
|
Expression TextureGradient(Operation operation) {
|
|
|
|
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
|
|
|
|
UNIMPLEMENTED_IF(!meta.aoffi.empty());
|
|
|
|
|
|
|
|
const Id sampler = GetTextureSampler(operation);
|
|
|
|
const Id coords = GetCoordinates(operation, Type::Float);
|
|
|
|
const auto [dx, dy] = GetDerivatives(operation);
|
|
|
|
const std::vector grad = {dx, dy};
|
|
|
|
|
|
|
|
static constexpr auto mask = spv::ImageOperandsMask::Grad;
|
2019-12-19 05:41:22 +01:00
|
|
|
const Id texture = OpImageSampleExplicitLod(t_float4, sampler, coords, mask, grad);
|
2019-12-10 03:44:29 +01:00
|
|
|
return GetTextureElement(operation, texture, Type::Float);
|
|
|
|
}
|
|
|
|
|
|
|
|
Expression ImageLoad(Operation operation) {
|
2020-03-06 09:06:02 +01:00
|
|
|
if (!device.IsFormatlessImageLoadSupported()) {
|
2020-02-19 06:57:22 +01:00
|
|
|
return {v_float_zero, Type::Float};
|
|
|
|
}
|
|
|
|
|
2020-02-18 15:34:01 +01:00
|
|
|
const auto& meta{std::get<MetaImage>(operation.GetMeta())};
|
|
|
|
|
|
|
|
const Id coords = GetCoordinates(operation, Type::Int);
|
|
|
|
const Id texel = OpImageRead(t_uint4, GetImage(operation), coords);
|
|
|
|
|
|
|
|
return {OpCompositeExtract(t_uint, texel, meta.element), Type::Uint};
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression ImageStore(Operation operation) {
|
|
|
|
const auto meta{std::get<MetaImage>(operation.GetMeta())};
|
|
|
|
std::vector<Id> colors;
|
|
|
|
for (const auto& value : meta.values) {
|
|
|
|
colors.push_back(AsUint(Visit(value)));
|
|
|
|
}
|
|
|
|
|
|
|
|
const Id coords = GetCoordinates(operation, Type::Int);
|
|
|
|
const Id texel = OpCompositeConstruct(t_uint4, colors);
|
|
|
|
|
|
|
|
OpImageWrite(GetImage(operation), coords, texel, {});
|
2019-11-12 18:43:08 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression AtomicImageAdd(Operation operation) {
|
2019-09-18 06:07:01 +02:00
|
|
|
UNIMPLEMENTED();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression AtomicImageMin(Operation operation) {
|
2019-04-27 07:07:18 +02:00
|
|
|
UNIMPLEMENTED();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression AtomicImageMax(Operation operation) {
|
2019-07-18 02:03:53 +02:00
|
|
|
UNIMPLEMENTED();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression AtomicImageAnd(Operation operation) {
|
2019-07-18 02:03:53 +02:00
|
|
|
UNIMPLEMENTED();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression AtomicImageOr(Operation operation) {
|
2019-07-18 02:03:53 +02:00
|
|
|
UNIMPLEMENTED();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression AtomicImageXor(Operation operation) {
|
2019-07-18 02:03:53 +02:00
|
|
|
UNIMPLEMENTED();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression AtomicImageExchange(Operation operation) {
|
2019-07-18 02:03:53 +02:00
|
|
|
UNIMPLEMENTED();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2020-03-13 17:08:04 +01:00
|
|
|
template <Id (Module::*func)(Id, Id, Id, Id, Id), Type result_type,
|
|
|
|
Type value_type = result_type>
|
|
|
|
Expression Atomic(Operation operation) {
|
|
|
|
const Id type_def = GetTypeDefinition(result_type);
|
|
|
|
|
2020-01-26 01:03:02 +01:00
|
|
|
Id pointer;
|
|
|
|
if (const auto smem = std::get_if<SmemNode>(&*operation[0])) {
|
|
|
|
pointer = GetSharedMemoryPointer(*smem);
|
|
|
|
} else if (const auto gmem = std::get_if<GmemNode>(&*operation[0])) {
|
|
|
|
pointer = GetGlobalMemoryPointer(*gmem);
|
|
|
|
} else {
|
|
|
|
UNREACHABLE();
|
2020-03-13 17:08:04 +01:00
|
|
|
return {Constant(type_def, 0), result_type};
|
2020-01-26 01:03:02 +01:00
|
|
|
}
|
2020-01-19 20:37:39 +01:00
|
|
|
|
2020-03-13 17:08:04 +01:00
|
|
|
const Id value = As(Visit(operation[1]), value_type);
|
|
|
|
|
2020-01-19 20:37:39 +01:00
|
|
|
const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device));
|
2020-03-13 17:08:04 +01:00
|
|
|
const Id semantics = Constant(type_def, 0);
|
2020-01-19 20:37:39 +01:00
|
|
|
|
2020-03-13 17:08:04 +01:00
|
|
|
return {(this->*func)(type_def, pointer, scope, semantics, value), result_type};
|
2020-01-16 08:00:52 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression Branch(Operation operation) {
|
|
|
|
const auto& target = std::get<ImmediateNode>(*operation[0]);
|
|
|
|
OpStore(jmp_to, Constant(t_uint, target.GetValue()));
|
|
|
|
OpBranch(continue_label);
|
|
|
|
inside_branch = true;
|
|
|
|
if (!conditional_branch_set) {
|
|
|
|
AddLabel();
|
2019-09-23 02:15:09 +02:00
|
|
|
}
|
2019-03-14 06:36:14 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression BranchIndirect(Operation operation) {
|
|
|
|
const Id op_a = AsUint(Visit(operation[0]));
|
2019-06-25 03:25:38 +02:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
OpStore(jmp_to, op_a);
|
|
|
|
OpBranch(continue_label);
|
|
|
|
inside_branch = true;
|
|
|
|
if (!conditional_branch_set) {
|
|
|
|
AddLabel();
|
2019-09-23 02:15:09 +02:00
|
|
|
}
|
2019-06-25 03:25:38 +02:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression PushFlowStack(Operation operation) {
|
|
|
|
const auto& target = std::get<ImmediateNode>(*operation[0]);
|
2019-06-02 23:52:07 +02:00
|
|
|
const auto [flow_stack, flow_stack_top] = GetFlowStack(operation);
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id current = OpLoad(t_uint, flow_stack_top);
|
|
|
|
const Id next = OpIAdd(t_uint, current, Constant(t_uint, 1));
|
|
|
|
const Id access = OpAccessChain(t_func_uint, flow_stack, current);
|
2019-03-14 06:48:16 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
OpStore(access, Constant(t_uint, target.GetValue()));
|
|
|
|
OpStore(flow_stack_top, next);
|
2019-03-14 06:36:14 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression PopFlowStack(Operation operation) {
|
2019-06-02 23:52:07 +02:00
|
|
|
const auto [flow_stack, flow_stack_top] = GetFlowStack(operation);
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id current = OpLoad(t_uint, flow_stack_top);
|
|
|
|
const Id previous = OpISub(t_uint, current, Constant(t_uint, 1));
|
|
|
|
const Id access = OpAccessChain(t_func_uint, flow_stack, previous);
|
|
|
|
const Id target = OpLoad(t_uint, access);
|
|
|
|
|
|
|
|
OpStore(flow_stack_top, previous);
|
|
|
|
OpStore(jmp_to, target);
|
|
|
|
OpBranch(continue_label);
|
|
|
|
inside_branch = true;
|
|
|
|
if (!conditional_branch_set) {
|
|
|
|
AddLabel();
|
2019-09-23 02:15:09 +02:00
|
|
|
}
|
2019-03-14 06:36:14 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
void PreExit() {
|
2019-12-19 05:45:09 +01:00
|
|
|
if (stage == ShaderType::Vertex && specialization.ndc_minus_one_to_one) {
|
2019-12-10 03:44:29 +01:00
|
|
|
const u32 position_index = out_indices.position.value();
|
|
|
|
const Id z_pointer = AccessElement(t_out_float, out_vertex, position_index, 2U);
|
|
|
|
const Id w_pointer = AccessElement(t_out_float, out_vertex, position_index, 3U);
|
|
|
|
Id depth = OpLoad(t_float, z_pointer);
|
|
|
|
depth = OpFAdd(t_float, depth, OpLoad(t_float, w_pointer));
|
|
|
|
depth = OpFMul(t_float, depth, Constant(t_float, 0.5f));
|
|
|
|
OpStore(z_pointer, depth);
|
2019-03-14 06:48:16 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
if (stage == ShaderType::Fragment) {
|
2019-03-14 06:48:16 +01:00
|
|
|
const auto SafeGetRegister = [&](u32 reg) {
|
|
|
|
// TODO(Rodrigo): Replace with contains once C++20 releases
|
|
|
|
if (const auto it = registers.find(reg); it != registers.end()) {
|
2019-12-10 03:44:29 +01:00
|
|
|
return OpLoad(t_float, it->second);
|
2019-03-14 06:48:16 +01:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
return v_float_zero;
|
2019-03-14 06:48:16 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0,
|
|
|
|
"Sample mask write is unimplemented");
|
|
|
|
|
|
|
|
// TODO(Rodrigo): Alpha testing
|
|
|
|
|
|
|
|
// Write the color outputs using the data in the shader registers, disabled
|
|
|
|
// rendertargets/components are skipped in the register assignment.
|
|
|
|
u32 current_reg = 0;
|
|
|
|
for (u32 rt = 0; rt < Maxwell::NumRenderTargets; ++rt) {
|
|
|
|
// TODO(Subv): Figure out how dual-source blending is configured in the Switch.
|
|
|
|
for (u32 component = 0; component < 4; ++component) {
|
2020-01-24 05:15:50 +01:00
|
|
|
if (!header.ps.IsColorComponentOutputEnabled(rt, component)) {
|
|
|
|
continue;
|
2019-03-14 06:48:16 +01:00
|
|
|
}
|
2020-01-24 05:15:50 +01:00
|
|
|
const Id pointer = AccessElement(t_out_float, frag_colors[rt], component);
|
|
|
|
OpStore(pointer, SafeGetRegister(current_reg));
|
|
|
|
++current_reg;
|
2019-03-14 06:48:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (header.ps.omap.depth) {
|
|
|
|
// The depth output is always 2 registers after the last color output, and
|
|
|
|
// current_reg already contains one past the last color register.
|
2019-12-10 03:44:29 +01:00
|
|
|
OpStore(frag_depth, SafeGetRegister(current_reg + 1));
|
2019-03-14 06:48:16 +01:00
|
|
|
}
|
|
|
|
}
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression Exit(Operation operation) {
|
2019-08-25 21:32:00 +02:00
|
|
|
PreExit();
|
2019-12-10 03:44:29 +01:00
|
|
|
inside_branch = true;
|
|
|
|
if (conditional_branch_set) {
|
|
|
|
OpReturn();
|
2019-09-23 02:15:09 +02:00
|
|
|
} else {
|
|
|
|
const Id dummy = OpLabel();
|
2019-12-10 03:44:29 +01:00
|
|
|
OpBranch(dummy);
|
|
|
|
AddLabel(dummy);
|
|
|
|
OpReturn();
|
|
|
|
AddLabel();
|
2019-09-23 02:15:09 +02:00
|
|
|
}
|
2019-03-14 06:36:14 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression Discard(Operation operation) {
|
|
|
|
inside_branch = true;
|
|
|
|
if (conditional_branch_set) {
|
|
|
|
OpKill();
|
2019-09-23 02:15:09 +02:00
|
|
|
} else {
|
|
|
|
const Id dummy = OpLabel();
|
2019-12-10 03:44:29 +01:00
|
|
|
OpBranch(dummy);
|
|
|
|
AddLabel(dummy);
|
|
|
|
OpKill();
|
|
|
|
AddLabel();
|
2019-09-23 02:15:09 +02:00
|
|
|
}
|
2019-03-14 06:36:14 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression EmitVertex(Operation) {
|
|
|
|
OpEmitVertex();
|
2019-03-14 06:36:14 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression EndPrimitive(Operation operation) {
|
|
|
|
OpEndPrimitive();
|
2019-03-14 06:36:14 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression InvocationId(Operation) {
|
|
|
|
return {OpLoad(t_int, invocation_id), Type::Int};
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression YNegate(Operation) {
|
2019-12-10 03:51:43 +01:00
|
|
|
LOG_WARNING(Render_Vulkan, "(STUBBED)");
|
2019-12-10 03:44:29 +01:00
|
|
|
return {Constant(t_float, 1.0f), Type::Float};
|
2019-05-03 08:00:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template <u32 element>
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression LocalInvocationId(Operation) {
|
|
|
|
const Id id = OpLoad(t_uint3, local_invocation_id);
|
|
|
|
return {OpCompositeExtract(t_uint, id, element), Type::Uint};
|
2019-05-03 08:00:51 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
template <u32 element>
|
|
|
|
Expression WorkGroupId(Operation operation) {
|
|
|
|
const Id id = OpLoad(t_uint3, workgroup_id);
|
|
|
|
return {OpCompositeExtract(t_uint, id, element), Type::Uint};
|
2019-08-10 04:50:21 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression BallotThread(Operation operation) {
|
|
|
|
const Id predicate = AsBool(Visit(operation[0]));
|
|
|
|
const Id ballot = OpSubgroupBallotKHR(t_uint4, predicate);
|
2019-08-10 04:50:21 +02:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
if (!device.IsWarpSizePotentiallyBiggerThanGuest()) {
|
|
|
|
// Guest-like devices can just return the first index.
|
|
|
|
return {OpCompositeExtract(t_uint, ballot, 0U), Type::Uint};
|
|
|
|
}
|
|
|
|
|
|
|
|
// The others will have to return what is local to the current thread.
|
|
|
|
// For instance a device with a warp size of 64 will return the upper uint when the current
|
|
|
|
// thread is 38.
|
|
|
|
const Id tid = OpLoad(t_uint, thread_id);
|
|
|
|
const Id thread_index = OpShiftRightLogical(t_uint, tid, Constant(t_uint, 5));
|
|
|
|
return {OpVectorExtractDynamic(t_uint, ballot, thread_index), Type::Uint};
|
2019-08-10 04:50:21 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
template <Id (Module::*func)(Id, Id)>
|
|
|
|
Expression Vote(Operation operation) {
|
|
|
|
// TODO(Rodrigo): Handle devices with different warp sizes
|
|
|
|
const Id predicate = AsBool(Visit(operation[0]));
|
|
|
|
return {(this->*func)(t_bool, predicate), Type::Bool};
|
2019-08-10 04:50:21 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression ThreadId(Operation) {
|
|
|
|
return {OpLoad(t_uint, thread_id), Type::Uint};
|
2019-08-27 03:09:12 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Expression ShuffleIndexed(Operation operation) {
|
|
|
|
const Id value = AsFloat(Visit(operation[0]));
|
|
|
|
const Id index = AsUint(Visit(operation[1]));
|
|
|
|
return {OpSubgroupReadInvocationKHR(t_float, value, index), Type::Float};
|
2019-08-27 03:09:12 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 01:46:12 +01:00
|
|
|
Expression MemoryBarrierGL(Operation) {
|
|
|
|
const auto scope = spv::Scope::Device;
|
|
|
|
const auto semantics =
|
|
|
|
spv::MemorySemanticsMask::AcquireRelease | spv::MemorySemanticsMask::UniformMemory |
|
|
|
|
spv::MemorySemanticsMask::WorkgroupMemory |
|
|
|
|
spv::MemorySemanticsMask::AtomicCounterMemory | spv::MemorySemanticsMask::ImageMemory;
|
|
|
|
|
|
|
|
OpMemoryBarrier(Constant(t_uint, static_cast<u32>(scope)),
|
|
|
|
Constant(t_uint, static_cast<u32>(semantics)));
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Id DeclareBuiltIn(spv::BuiltIn builtin, spv::StorageClass storage, Id type, std::string name) {
|
2019-03-14 06:23:37 +01:00
|
|
|
const Id id = OpVariable(type, storage);
|
|
|
|
Decorate(id, spv::Decoration::BuiltIn, static_cast<u32>(builtin));
|
2019-12-10 03:44:29 +01:00
|
|
|
AddGlobalVariable(Name(id, std::move(name)));
|
2019-03-14 06:23:37 +01:00
|
|
|
interfaces.push_back(id);
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Id DeclareInputBuiltIn(spv::BuiltIn builtin, Id type, std::string name) {
|
|
|
|
return DeclareBuiltIn(builtin, spv::StorageClass::Input, type, std::move(name));
|
|
|
|
}
|
|
|
|
|
2019-03-14 06:43:40 +01:00
|
|
|
template <typename... Args>
|
|
|
|
Id AccessElement(Id pointer_type, Id composite, Args... elements_) {
|
|
|
|
std::vector<Id> members;
|
|
|
|
auto elements = {elements_...};
|
|
|
|
for (const auto element : elements) {
|
|
|
|
members.push_back(Constant(t_uint, element));
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
return OpAccessChain(pointer_type, composite, members);
|
2019-03-14 06:43:40 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Id As(Expression expr, Type wanted_type) {
|
|
|
|
switch (wanted_type) {
|
2019-03-14 06:36:14 +01:00
|
|
|
case Type::Bool:
|
2019-12-10 03:44:29 +01:00
|
|
|
return AsBool(expr);
|
2019-03-14 06:36:14 +01:00
|
|
|
case Type::Bool2:
|
2019-12-10 03:44:29 +01:00
|
|
|
return AsBool2(expr);
|
2019-03-14 06:36:14 +01:00
|
|
|
case Type::Float:
|
2019-12-10 03:44:29 +01:00
|
|
|
return AsFloat(expr);
|
2019-03-14 06:36:14 +01:00
|
|
|
case Type::Int:
|
2019-12-10 03:44:29 +01:00
|
|
|
return AsInt(expr);
|
2019-03-14 06:36:14 +01:00
|
|
|
case Type::Uint:
|
2019-12-10 03:44:29 +01:00
|
|
|
return AsUint(expr);
|
2019-03-14 06:36:14 +01:00
|
|
|
case Type::HalfFloat:
|
2019-12-10 03:44:29 +01:00
|
|
|
return AsHalfFloat(expr);
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return expr.id;
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Id AsBool(Expression expr) {
|
|
|
|
ASSERT(expr.type == Type::Bool);
|
|
|
|
return expr.id;
|
|
|
|
}
|
|
|
|
|
|
|
|
Id AsBool2(Expression expr) {
|
|
|
|
ASSERT(expr.type == Type::Bool2);
|
|
|
|
return expr.id;
|
|
|
|
}
|
|
|
|
|
|
|
|
Id AsFloat(Expression expr) {
|
|
|
|
switch (expr.type) {
|
2019-03-14 06:36:14 +01:00
|
|
|
case Type::Float:
|
2019-12-10 03:44:29 +01:00
|
|
|
return expr.id;
|
2019-03-14 06:36:14 +01:00
|
|
|
case Type::Int:
|
|
|
|
case Type::Uint:
|
2019-12-10 03:44:29 +01:00
|
|
|
return OpBitcast(t_float, expr.id);
|
2019-03-14 06:36:14 +01:00
|
|
|
case Type::HalfFloat:
|
2019-12-10 03:44:29 +01:00
|
|
|
if (device.IsFloat16Supported()) {
|
|
|
|
return OpBitcast(t_float, expr.id);
|
|
|
|
}
|
|
|
|
return OpBitcast(t_float, OpPackHalf2x16(t_uint, expr.id));
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return expr.id;
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Id AsInt(Expression expr) {
|
|
|
|
switch (expr.type) {
|
|
|
|
case Type::Int:
|
|
|
|
return expr.id;
|
|
|
|
case Type::Float:
|
|
|
|
case Type::Uint:
|
|
|
|
return OpBitcast(t_int, expr.id);
|
|
|
|
case Type::HalfFloat:
|
|
|
|
if (device.IsFloat16Supported()) {
|
|
|
|
return OpBitcast(t_int, expr.id);
|
|
|
|
}
|
|
|
|
return OpPackHalf2x16(t_int, expr.id);
|
|
|
|
default:
|
2019-03-14 06:36:14 +01:00
|
|
|
UNREACHABLE();
|
2019-12-10 03:44:29 +01:00
|
|
|
return expr.id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Id AsUint(Expression expr) {
|
|
|
|
switch (expr.type) {
|
|
|
|
case Type::Uint:
|
|
|
|
return expr.id;
|
2019-03-14 06:36:14 +01:00
|
|
|
case Type::Float:
|
|
|
|
case Type::Int:
|
2019-12-10 03:44:29 +01:00
|
|
|
return OpBitcast(t_uint, expr.id);
|
2019-03-14 06:36:14 +01:00
|
|
|
case Type::HalfFloat:
|
2019-12-10 03:44:29 +01:00
|
|
|
if (device.IsFloat16Supported()) {
|
|
|
|
return OpBitcast(t_uint, expr.id);
|
|
|
|
}
|
|
|
|
return OpPackHalf2x16(t_uint, expr.id);
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return expr.id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Id AsHalfFloat(Expression expr) {
|
|
|
|
switch (expr.type) {
|
|
|
|
case Type::HalfFloat:
|
|
|
|
return expr.id;
|
|
|
|
case Type::Float:
|
|
|
|
case Type::Int:
|
|
|
|
case Type::Uint:
|
|
|
|
if (device.IsFloat16Supported()) {
|
|
|
|
return OpBitcast(t_half, expr.id);
|
|
|
|
}
|
|
|
|
return OpUnpackHalf2x16(t_half, AsUint(expr));
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return expr.id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Id GetHalfScalarFromFloat(Id value) {
|
|
|
|
if (device.IsFloat16Supported()) {
|
|
|
|
return OpFConvert(t_scalar_half, value);
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Id GetFloatFromHalfScalar(Id value) {
|
|
|
|
if (device.IsFloat16Supported()) {
|
|
|
|
return OpFConvert(t_float, value);
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
AttributeType GetAttributeType(u32 location) const {
|
|
|
|
if (stage != ShaderType::Vertex) {
|
|
|
|
return {Type::Float, t_in_float, t_in_float4};
|
|
|
|
}
|
|
|
|
switch (specialization.attribute_types.at(location)) {
|
|
|
|
case Maxwell::VertexAttribute::Type::SignedNorm:
|
|
|
|
case Maxwell::VertexAttribute::Type::UnsignedNorm:
|
2020-02-23 21:45:58 +01:00
|
|
|
case Maxwell::VertexAttribute::Type::UnsignedScaled:
|
|
|
|
case Maxwell::VertexAttribute::Type::SignedScaled:
|
2019-12-10 03:44:29 +01:00
|
|
|
case Maxwell::VertexAttribute::Type::Float:
|
|
|
|
return {Type::Float, t_in_float, t_in_float4};
|
|
|
|
case Maxwell::VertexAttribute::Type::SignedInt:
|
|
|
|
return {Type::Int, t_in_int, t_in_int4};
|
|
|
|
case Maxwell::VertexAttribute::Type::UnsignedInt:
|
|
|
|
return {Type::Uint, t_in_uint, t_in_uint4};
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return {Type::Float, t_in_float, t_in_float4};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Id GetTypeDefinition(Type type) const {
|
2019-03-14 06:36:14 +01:00
|
|
|
switch (type) {
|
|
|
|
case Type::Bool:
|
|
|
|
return t_bool;
|
|
|
|
case Type::Bool2:
|
|
|
|
return t_bool2;
|
|
|
|
case Type::Float:
|
|
|
|
return t_float;
|
|
|
|
case Type::Int:
|
|
|
|
return t_int;
|
|
|
|
case Type::Uint:
|
|
|
|
return t_uint;
|
|
|
|
case Type::HalfFloat:
|
2019-12-10 03:44:29 +01:00
|
|
|
return t_half;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::array<Id, 4> GetTypeVectorDefinitionLut(Type type) const {
|
|
|
|
switch (type) {
|
|
|
|
case Type::Float:
|
2020-03-06 09:10:39 +01:00
|
|
|
return {t_float, t_float2, t_float3, t_float4};
|
2019-12-10 03:44:29 +01:00
|
|
|
case Type::Int:
|
2020-03-06 09:10:39 +01:00
|
|
|
return {t_int, t_int2, t_int3, t_int4};
|
2019-12-10 03:44:29 +01:00
|
|
|
case Type::Uint:
|
2020-03-06 09:10:39 +01:00
|
|
|
return {t_uint, t_uint2, t_uint3, t_uint4};
|
2019-12-10 03:44:29 +01:00
|
|
|
default:
|
2019-03-14 06:36:14 +01:00
|
|
|
UNIMPLEMENTED();
|
2019-12-10 03:44:29 +01:00
|
|
|
return {};
|
2019-03-14 06:36:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-02 23:52:07 +02:00
|
|
|
std::tuple<Id, Id> CreateFlowStack() {
|
|
|
|
// TODO(Rodrigo): Figure out the actual depth of the flow stack, for now it seems unlikely
|
|
|
|
// that shaders will use 20 nested SSYs and PBKs.
|
|
|
|
constexpr u32 FLOW_STACK_SIZE = 20;
|
|
|
|
constexpr auto storage_class = spv::StorageClass::Function;
|
|
|
|
|
|
|
|
const Id flow_stack_type = TypeArray(t_uint, Constant(t_uint, FLOW_STACK_SIZE));
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id stack = OpVariable(TypePointer(storage_class, flow_stack_type), storage_class,
|
|
|
|
ConstantNull(flow_stack_type));
|
|
|
|
const Id top = OpVariable(t_func_uint, storage_class, Constant(t_uint, 0));
|
|
|
|
AddLocalVariable(stack);
|
|
|
|
AddLocalVariable(top);
|
2019-06-02 23:52:07 +02:00
|
|
|
return std::tie(stack, top);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<Id, Id> GetFlowStack(Operation operation) {
|
|
|
|
const auto stack_class = std::get<MetaStackClass>(operation.GetMeta());
|
|
|
|
switch (stack_class) {
|
|
|
|
case MetaStackClass::Ssy:
|
|
|
|
return {ssy_flow_stack, ssy_flow_stack_top};
|
|
|
|
case MetaStackClass::Pbk:
|
|
|
|
return {pbk_flow_stack, pbk_flow_stack_top};
|
|
|
|
}
|
|
|
|
UNREACHABLE();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2020-01-26 01:03:02 +01:00
|
|
|
Id GetGlobalMemoryPointer(const GmemNode& gmem) {
|
|
|
|
const Id real = AsUint(Visit(gmem.GetRealAddress()));
|
|
|
|
const Id base = AsUint(Visit(gmem.GetBaseAddress()));
|
|
|
|
const Id diff = OpISub(t_uint, real, base);
|
|
|
|
const Id offset = OpShiftRightLogical(t_uint, diff, Constant(t_uint, 2));
|
|
|
|
const Id buffer = global_buffers.at(gmem.GetDescriptor());
|
|
|
|
return OpAccessChain(t_gmem_uint, buffer, Constant(t_uint, 0), offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
Id GetSharedMemoryPointer(const SmemNode& smem) {
|
|
|
|
ASSERT(stage == ShaderType::Compute);
|
|
|
|
Id address = AsUint(Visit(smem.GetAddress()));
|
|
|
|
address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U));
|
|
|
|
return OpAccessChain(t_smem_uint, shared_memory, address);
|
|
|
|
}
|
|
|
|
|
2019-07-20 03:20:34 +02:00
|
|
|
static constexpr std::array operation_decompilers = {
|
2019-03-14 06:36:14 +01:00
|
|
|
&SPIRVDecompiler::Assign,
|
|
|
|
|
|
|
|
&SPIRVDecompiler::Ternary<&Module::OpSelect, Type::Float, Type::Bool, Type::Float,
|
|
|
|
Type::Float>,
|
|
|
|
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFAdd, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFMul, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFDiv, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Ternary<&Module::OpFma, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpFNegate, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpFAbs, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Ternary<&Module::OpFClamp, Type::Float>,
|
2019-12-10 03:44:29 +01:00
|
|
|
&SPIRVDecompiler::FCastHalf<0>,
|
|
|
|
&SPIRVDecompiler::FCastHalf<1>,
|
2019-03-14 06:36:14 +01:00
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFMin, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFMax, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpCos, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpSin, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpExp2, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpLog2, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpInverseSqrt, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpSqrt, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpRoundEven, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpFloor, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpCeil, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpTrunc, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpConvertSToF, Type::Float, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpConvertUToF, Type::Float, Type::Uint>,
|
2019-11-03 03:44:46 +01:00
|
|
|
&SPIRVDecompiler::FSwizzleAdd,
|
2019-03-14 06:36:14 +01:00
|
|
|
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpIAdd, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpIMul, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpSDiv, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpSNegate, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpSAbs, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpSMin, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpSMax, Type::Int>,
|
|
|
|
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpConvertFToS, Type::Int, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpBitcast, Type::Int, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpShiftLeftLogical, Type::Int, Type::Int, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpShiftRightLogical, Type::Int, Type::Int, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpShiftRightArithmetic, Type::Int, Type::Int, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpBitwiseAnd, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpBitwiseOr, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpBitwiseXor, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpNot, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Quaternary<&Module::OpBitFieldInsert, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Ternary<&Module::OpBitFieldSExtract, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpBitCount, Type::Int>,
|
2019-11-12 15:07:22 +01:00
|
|
|
&SPIRVDecompiler::Unary<&Module::OpFindSMsb, Type::Int>,
|
2019-03-14 06:36:14 +01:00
|
|
|
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpIAdd, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpIMul, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpUDiv, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpUMin, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpUMax, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpConvertFToU, Type::Uint, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpBitcast, Type::Uint, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpShiftLeftLogical, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpShiftRightLogical, Type::Uint>,
|
2019-12-10 03:44:29 +01:00
|
|
|
&SPIRVDecompiler::Binary<&Module::OpShiftRightLogical, Type::Uint>,
|
2019-03-14 06:36:14 +01:00
|
|
|
&SPIRVDecompiler::Binary<&Module::OpBitwiseAnd, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpBitwiseOr, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpBitwiseXor, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpNot, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Quaternary<&Module::OpBitFieldInsert, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Ternary<&Module::OpBitFieldUExtract, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpBitCount, Type::Uint>,
|
2019-11-12 15:07:22 +01:00
|
|
|
&SPIRVDecompiler::Unary<&Module::OpFindUMsb, Type::Uint>,
|
2019-03-14 06:36:14 +01:00
|
|
|
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFAdd, Type::HalfFloat>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFMul, Type::HalfFloat>,
|
|
|
|
&SPIRVDecompiler::Ternary<&Module::OpFma, Type::HalfFloat>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpFAbs, Type::HalfFloat>,
|
|
|
|
&SPIRVDecompiler::HNegate,
|
2019-04-09 23:41:41 +02:00
|
|
|
&SPIRVDecompiler::HClamp,
|
2019-07-20 23:38:25 +02:00
|
|
|
&SPIRVDecompiler::HCastFloat,
|
2019-04-16 00:48:11 +02:00
|
|
|
&SPIRVDecompiler::HUnpack,
|
2019-03-14 06:36:14 +01:00
|
|
|
&SPIRVDecompiler::HMergeF32,
|
2019-12-10 03:44:29 +01:00
|
|
|
&SPIRVDecompiler::HMergeHN<0>,
|
|
|
|
&SPIRVDecompiler::HMergeHN<1>,
|
2019-03-14 06:36:14 +01:00
|
|
|
&SPIRVDecompiler::HPack2,
|
|
|
|
|
|
|
|
&SPIRVDecompiler::LogicalAssign,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpLogicalAnd, Type::Bool>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpLogicalOr, Type::Bool>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpLogicalNotEqual, Type::Bool>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpLogicalNot, Type::Bool>,
|
2019-12-10 03:44:29 +01:00
|
|
|
&SPIRVDecompiler::Binary<&Module::OpVectorExtractDynamic, Type::Bool, Type::Bool2,
|
|
|
|
Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Unary<&Module::OpAll, Type::Bool, Type::Bool2>,
|
2019-03-14 06:36:14 +01:00
|
|
|
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::Float>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::Float>,
|
2019-12-10 03:44:29 +01:00
|
|
|
&SPIRVDecompiler::Unary<&Module::OpIsNan, Type::Bool, Type::Float>,
|
2019-03-14 06:36:14 +01:00
|
|
|
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpSLessThan, Type::Bool, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpIEqual, Type::Bool, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpSLessThanEqual, Type::Bool, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpSGreaterThan, Type::Bool, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpINotEqual, Type::Bool, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpSGreaterThanEqual, Type::Bool, Type::Int>,
|
|
|
|
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpULessThan, Type::Bool, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpIEqual, Type::Bool, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpULessThanEqual, Type::Bool, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpUGreaterThan, Type::Bool, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpINotEqual, Type::Bool, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpUGreaterThanEqual, Type::Bool, Type::Uint>,
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool2, Type::HalfFloat>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool2, Type::HalfFloat>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool2, Type::HalfFloat>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool2, Type::HalfFloat>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool2, Type::HalfFloat>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool2, Type::HalfFloat>,
|
2019-04-16 02:32:24 +02:00
|
|
|
// TODO(Rodrigo): Should these use the OpFUnord* variants?
|
2019-12-10 03:44:29 +01:00
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool2, Type::HalfFloat>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool2, Type::HalfFloat>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool2, Type::HalfFloat>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool2, Type::HalfFloat>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool2, Type::HalfFloat>,
|
|
|
|
&SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool2, Type::HalfFloat>,
|
2019-03-14 06:36:14 +01:00
|
|
|
|
|
|
|
&SPIRVDecompiler::Texture,
|
|
|
|
&SPIRVDecompiler::TextureLod,
|
|
|
|
&SPIRVDecompiler::TextureGather,
|
|
|
|
&SPIRVDecompiler::TextureQueryDimensions,
|
|
|
|
&SPIRVDecompiler::TextureQueryLod,
|
|
|
|
&SPIRVDecompiler::TexelFetch,
|
2019-11-12 18:43:08 +01:00
|
|
|
&SPIRVDecompiler::TextureGradient,
|
2019-03-14 06:36:14 +01:00
|
|
|
|
2019-09-18 06:07:01 +02:00
|
|
|
&SPIRVDecompiler::ImageLoad,
|
2019-04-27 07:07:18 +02:00
|
|
|
&SPIRVDecompiler::ImageStore,
|
2019-07-18 02:03:53 +02:00
|
|
|
&SPIRVDecompiler::AtomicImageAdd,
|
|
|
|
&SPIRVDecompiler::AtomicImageAnd,
|
|
|
|
&SPIRVDecompiler::AtomicImageOr,
|
|
|
|
&SPIRVDecompiler::AtomicImageXor,
|
|
|
|
&SPIRVDecompiler::AtomicImageExchange,
|
2019-04-27 07:07:18 +02:00
|
|
|
|
2020-03-13 17:08:04 +01:00
|
|
|
&SPIRVDecompiler::Atomic<&Module::OpAtomicExchange, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Atomic<&Module::OpAtomicUMin, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Atomic<&Module::OpAtomicUMax, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Atomic<&Module::OpAtomicAnd, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Atomic<&Module::OpAtomicOr, Type::Uint>,
|
|
|
|
&SPIRVDecompiler::Atomic<&Module::OpAtomicXor, Type::Uint>,
|
|
|
|
|
|
|
|
&SPIRVDecompiler::Atomic<&Module::OpAtomicExchange, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Atomic<&Module::OpAtomicSMin, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Atomic<&Module::OpAtomicSMax, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Atomic<&Module::OpAtomicAnd, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Atomic<&Module::OpAtomicOr, Type::Int>,
|
|
|
|
&SPIRVDecompiler::Atomic<&Module::OpAtomicXor, Type::Int>,
|
2020-01-16 08:00:52 +01:00
|
|
|
|
2019-03-14 06:36:14 +01:00
|
|
|
&SPIRVDecompiler::Branch,
|
2019-06-25 03:25:38 +02:00
|
|
|
&SPIRVDecompiler::BranchIndirect,
|
2019-03-14 06:36:14 +01:00
|
|
|
&SPIRVDecompiler::PushFlowStack,
|
|
|
|
&SPIRVDecompiler::PopFlowStack,
|
|
|
|
&SPIRVDecompiler::Exit,
|
|
|
|
&SPIRVDecompiler::Discard,
|
|
|
|
|
|
|
|
&SPIRVDecompiler::EmitVertex,
|
|
|
|
&SPIRVDecompiler::EndPrimitive,
|
|
|
|
|
2019-10-30 22:17:27 +01:00
|
|
|
&SPIRVDecompiler::InvocationId,
|
2019-03-14 06:36:14 +01:00
|
|
|
&SPIRVDecompiler::YNegate,
|
2019-05-03 08:00:51 +02:00
|
|
|
&SPIRVDecompiler::LocalInvocationId<0>,
|
|
|
|
&SPIRVDecompiler::LocalInvocationId<1>,
|
|
|
|
&SPIRVDecompiler::LocalInvocationId<2>,
|
|
|
|
&SPIRVDecompiler::WorkGroupId<0>,
|
|
|
|
&SPIRVDecompiler::WorkGroupId<1>,
|
|
|
|
&SPIRVDecompiler::WorkGroupId<2>,
|
2019-08-10 04:50:21 +02:00
|
|
|
|
|
|
|
&SPIRVDecompiler::BallotThread,
|
2019-12-10 03:44:29 +01:00
|
|
|
&SPIRVDecompiler::Vote<&Module::OpSubgroupAllKHR>,
|
|
|
|
&SPIRVDecompiler::Vote<&Module::OpSubgroupAnyKHR>,
|
|
|
|
&SPIRVDecompiler::Vote<&Module::OpSubgroupAllEqualKHR>,
|
2019-08-27 03:09:12 +02:00
|
|
|
|
2019-11-03 03:44:13 +01:00
|
|
|
&SPIRVDecompiler::ThreadId,
|
2019-08-27 03:09:12 +02:00
|
|
|
&SPIRVDecompiler::ShuffleIndexed,
|
2019-12-10 01:46:12 +01:00
|
|
|
|
|
|
|
&SPIRVDecompiler::MemoryBarrierGL,
|
2019-03-14 06:36:14 +01:00
|
|
|
};
|
2019-07-20 03:20:34 +02:00
|
|
|
static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount));
|
2019-03-14 06:36:14 +01:00
|
|
|
|
2019-05-26 06:43:07 +02:00
|
|
|
const VKDevice& device;
|
2019-03-14 06:19:02 +01:00
|
|
|
const ShaderIR& ir;
|
2019-11-18 22:35:21 +01:00
|
|
|
const ShaderType stage;
|
2019-03-14 06:19:02 +01:00
|
|
|
const Tegra::Shader::Header header;
|
2020-03-03 01:36:25 +01:00
|
|
|
const Registry& registry;
|
2019-12-10 03:44:29 +01:00
|
|
|
const Specialization& specialization;
|
2020-03-12 07:27:29 +01:00
|
|
|
std::unordered_map<u8, VaryingTFB> transform_feedback;
|
2019-03-14 06:23:37 +01:00
|
|
|
|
|
|
|
const Id t_void = Name(TypeVoid(), "void");
|
|
|
|
|
|
|
|
const Id t_bool = Name(TypeBool(), "bool");
|
|
|
|
const Id t_bool2 = Name(TypeVector(t_bool, 2), "bool2");
|
|
|
|
|
|
|
|
const Id t_int = Name(TypeInt(32, true), "int");
|
|
|
|
const Id t_int2 = Name(TypeVector(t_int, 2), "int2");
|
|
|
|
const Id t_int3 = Name(TypeVector(t_int, 3), "int3");
|
|
|
|
const Id t_int4 = Name(TypeVector(t_int, 4), "int4");
|
|
|
|
|
|
|
|
const Id t_uint = Name(TypeInt(32, false), "uint");
|
|
|
|
const Id t_uint2 = Name(TypeVector(t_uint, 2), "uint2");
|
|
|
|
const Id t_uint3 = Name(TypeVector(t_uint, 3), "uint3");
|
|
|
|
const Id t_uint4 = Name(TypeVector(t_uint, 4), "uint4");
|
|
|
|
|
|
|
|
const Id t_float = Name(TypeFloat(32), "float");
|
|
|
|
const Id t_float2 = Name(TypeVector(t_float, 2), "float2");
|
|
|
|
const Id t_float3 = Name(TypeVector(t_float, 3), "float3");
|
|
|
|
const Id t_float4 = Name(TypeVector(t_float, 4), "float4");
|
|
|
|
|
|
|
|
const Id t_prv_bool = Name(TypePointer(spv::StorageClass::Private, t_bool), "prv_bool");
|
|
|
|
const Id t_prv_float = Name(TypePointer(spv::StorageClass::Private, t_float), "prv_float");
|
|
|
|
|
2019-03-14 06:27:06 +01:00
|
|
|
const Id t_func_uint = Name(TypePointer(spv::StorageClass::Function, t_uint), "func_uint");
|
|
|
|
|
2019-03-14 06:23:37 +01:00
|
|
|
const Id t_in_bool = Name(TypePointer(spv::StorageClass::Input, t_bool), "in_bool");
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id t_in_int = Name(TypePointer(spv::StorageClass::Input, t_int), "in_int");
|
|
|
|
const Id t_in_int4 = Name(TypePointer(spv::StorageClass::Input, t_int4), "in_int4");
|
2019-03-14 06:23:37 +01:00
|
|
|
const Id t_in_uint = Name(TypePointer(spv::StorageClass::Input, t_uint), "in_uint");
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id t_in_uint3 = Name(TypePointer(spv::StorageClass::Input, t_uint3), "in_uint3");
|
|
|
|
const Id t_in_uint4 = Name(TypePointer(spv::StorageClass::Input, t_uint4), "in_uint4");
|
2019-03-14 06:23:37 +01:00
|
|
|
const Id t_in_float = Name(TypePointer(spv::StorageClass::Input, t_float), "in_float");
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id t_in_float2 = Name(TypePointer(spv::StorageClass::Input, t_float2), "in_float2");
|
|
|
|
const Id t_in_float3 = Name(TypePointer(spv::StorageClass::Input, t_float3), "in_float3");
|
2019-03-14 06:23:37 +01:00
|
|
|
const Id t_in_float4 = Name(TypePointer(spv::StorageClass::Input, t_float4), "in_float4");
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id t_out_int = Name(TypePointer(spv::StorageClass::Output, t_int), "out_int");
|
|
|
|
|
2019-03-14 06:23:37 +01:00
|
|
|
const Id t_out_float = Name(TypePointer(spv::StorageClass::Output, t_float), "out_float");
|
|
|
|
const Id t_out_float4 = Name(TypePointer(spv::StorageClass::Output, t_float4), "out_float4");
|
|
|
|
|
|
|
|
const Id t_cbuf_float = TypePointer(spv::StorageClass::Uniform, t_float);
|
2019-05-26 06:43:07 +02:00
|
|
|
const Id t_cbuf_std140 = Decorate(
|
2019-12-10 03:44:29 +01:00
|
|
|
Name(TypeArray(t_float4, Constant(t_uint, MaxConstBufferElements)), "CbufStd140Array"),
|
|
|
|
spv::Decoration::ArrayStride, 16U);
|
2019-05-26 06:43:07 +02:00
|
|
|
const Id t_cbuf_scalar = Decorate(
|
2019-12-10 03:44:29 +01:00
|
|
|
Name(TypeArray(t_float, Constant(t_uint, MaxConstBufferFloats)), "CbufScalarArray"),
|
|
|
|
spv::Decoration::ArrayStride, 4U);
|
2019-05-26 06:43:07 +02:00
|
|
|
const Id t_cbuf_std140_struct = MemberDecorate(
|
|
|
|
Decorate(TypeStruct(t_cbuf_std140), spv::Decoration::Block), 0, spv::Decoration::Offset, 0);
|
|
|
|
const Id t_cbuf_scalar_struct = MemberDecorate(
|
|
|
|
Decorate(TypeStruct(t_cbuf_scalar), spv::Decoration::Block), 0, spv::Decoration::Offset, 0);
|
|
|
|
const Id t_cbuf_std140_ubo = TypePointer(spv::StorageClass::Uniform, t_cbuf_std140_struct);
|
|
|
|
const Id t_cbuf_scalar_ubo = TypePointer(spv::StorageClass::Uniform, t_cbuf_scalar_struct);
|
2019-03-14 06:23:37 +01:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Id t_smem_uint{};
|
|
|
|
|
2020-01-26 01:03:02 +01:00
|
|
|
const Id t_gmem_uint = TypePointer(spv::StorageClass::StorageBuffer, t_uint);
|
2019-03-14 06:23:37 +01:00
|
|
|
const Id t_gmem_array =
|
2020-01-26 01:03:02 +01:00
|
|
|
Name(Decorate(TypeRuntimeArray(t_uint), spv::Decoration::ArrayStride, 4U), "GmemArray");
|
2019-03-14 06:23:37 +01:00
|
|
|
const Id t_gmem_struct = MemberDecorate(
|
|
|
|
Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0);
|
|
|
|
const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct);
|
|
|
|
|
|
|
|
const Id v_float_zero = Constant(t_float, 0.0f);
|
2019-12-10 03:44:29 +01:00
|
|
|
const Id v_float_one = Constant(t_float, 1.0f);
|
|
|
|
|
2020-03-12 06:35:31 +01:00
|
|
|
// Nvidia uses these defaults for varyings (e.g. position and generic attributes)
|
|
|
|
const Id v_varying_default =
|
|
|
|
ConstantComposite(t_float4, v_float_zero, v_float_zero, v_float_zero, v_float_one);
|
|
|
|
|
2019-03-14 06:23:37 +01:00
|
|
|
const Id v_true = ConstantTrue(t_bool);
|
|
|
|
const Id v_false = ConstantFalse(t_bool);
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
Id t_scalar_half{};
|
|
|
|
Id t_half{};
|
|
|
|
|
|
|
|
Id out_vertex{};
|
|
|
|
Id in_vertex{};
|
2019-03-14 06:23:37 +01:00
|
|
|
std::map<u32, Id> registers;
|
2020-01-07 19:53:46 +01:00
|
|
|
std::map<u32, Id> custom_variables;
|
2019-03-14 06:23:37 +01:00
|
|
|
std::map<Tegra::Shader::Pred, Id> predicates;
|
2019-08-25 21:32:00 +02:00
|
|
|
std::map<u32, Id> flow_variables;
|
2019-03-14 06:23:37 +01:00
|
|
|
Id local_memory{};
|
2019-12-10 03:44:29 +01:00
|
|
|
Id shared_memory{};
|
2019-03-14 06:23:37 +01:00
|
|
|
std::array<Id, INTERNAL_FLAGS_COUNT> internal_flags{};
|
|
|
|
std::map<Attribute::Index, Id> input_attributes;
|
2020-03-06 09:10:39 +01:00
|
|
|
std::unordered_map<u8, GenericVaryingDescription> output_attributes;
|
2019-03-14 06:23:37 +01:00
|
|
|
std::map<u32, Id> constant_buffers;
|
|
|
|
std::map<GlobalMemoryBase, Id> global_buffers;
|
2019-12-10 03:44:29 +01:00
|
|
|
std::map<u32, TexelBuffer> texel_buffers;
|
|
|
|
std::map<u32, SampledImage> sampled_images;
|
|
|
|
std::map<u32, StorageImage> images;
|
2019-03-14 06:23:37 +01:00
|
|
|
|
|
|
|
Id instance_index{};
|
|
|
|
Id vertex_index{};
|
2020-02-14 00:25:28 +01:00
|
|
|
Id base_instance{};
|
|
|
|
Id base_vertex{};
|
2019-03-14 06:23:37 +01:00
|
|
|
std::array<Id, Maxwell::NumRenderTargets> frag_colors{};
|
|
|
|
Id frag_depth{};
|
|
|
|
Id frag_coord{};
|
|
|
|
Id front_facing{};
|
2019-12-10 03:44:29 +01:00
|
|
|
Id point_coord{};
|
|
|
|
Id tess_level_outer{};
|
|
|
|
Id tess_level_inner{};
|
|
|
|
Id tess_coord{};
|
|
|
|
Id invocation_id{};
|
|
|
|
Id workgroup_id{};
|
|
|
|
Id local_invocation_id{};
|
|
|
|
Id thread_id{};
|
|
|
|
|
|
|
|
VertexIndices in_indices;
|
|
|
|
VertexIndices out_indices;
|
2019-03-14 06:23:37 +01:00
|
|
|
|
|
|
|
std::vector<Id> interfaces;
|
|
|
|
|
2019-03-14 06:27:06 +01:00
|
|
|
Id jmp_to{};
|
2019-06-02 23:52:07 +02:00
|
|
|
Id ssy_flow_stack_top{};
|
|
|
|
Id pbk_flow_stack_top{};
|
|
|
|
Id ssy_flow_stack{};
|
|
|
|
Id pbk_flow_stack{};
|
2019-03-14 06:27:06 +01:00
|
|
|
Id continue_label{};
|
|
|
|
std::map<u32, Id> labels;
|
2019-12-10 03:44:29 +01:00
|
|
|
|
|
|
|
bool conditional_branch_set{};
|
|
|
|
bool inside_branch{};
|
2019-03-14 06:19:02 +01:00
|
|
|
};
|
|
|
|
|
2019-08-25 21:32:00 +02:00
|
|
|
class ExprDecompiler {
|
|
|
|
public:
|
2019-09-28 21:16:19 +02:00
|
|
|
explicit ExprDecompiler(SPIRVDecompiler& decomp) : decomp{decomp} {}
|
2019-08-25 21:32:00 +02:00
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
Id operator()(const ExprAnd& expr) {
|
2019-08-25 21:32:00 +02:00
|
|
|
const Id type_def = decomp.GetTypeDefinition(Type::Bool);
|
|
|
|
const Id op1 = Visit(expr.operand1);
|
|
|
|
const Id op2 = Visit(expr.operand2);
|
2019-12-10 03:44:29 +01:00
|
|
|
return decomp.OpLogicalAnd(type_def, op1, op2);
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
Id operator()(const ExprOr& expr) {
|
2019-08-25 21:32:00 +02:00
|
|
|
const Id type_def = decomp.GetTypeDefinition(Type::Bool);
|
|
|
|
const Id op1 = Visit(expr.operand1);
|
|
|
|
const Id op2 = Visit(expr.operand2);
|
2019-12-10 03:44:29 +01:00
|
|
|
return decomp.OpLogicalOr(type_def, op1, op2);
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
Id operator()(const ExprNot& expr) {
|
2019-08-25 21:32:00 +02:00
|
|
|
const Id type_def = decomp.GetTypeDefinition(Type::Bool);
|
|
|
|
const Id op1 = Visit(expr.operand1);
|
2019-12-10 03:44:29 +01:00
|
|
|
return decomp.OpLogicalNot(type_def, op1);
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
Id operator()(const ExprPredicate& expr) {
|
2019-09-24 16:57:45 +02:00
|
|
|
const auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate);
|
2019-12-10 03:44:29 +01:00
|
|
|
return decomp.OpLoad(decomp.t_bool, decomp.predicates.at(pred));
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
Id operator()(const ExprCondCode& expr) {
|
2019-12-21 01:36:25 +01:00
|
|
|
return decomp.AsBool(decomp.Visit(decomp.ir.GetConditionCode(expr.cc)));
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
Id operator()(const ExprVar& expr) {
|
2019-12-10 03:44:29 +01:00
|
|
|
return decomp.OpLoad(decomp.t_bool, decomp.flow_variables.at(expr.var_index));
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
Id operator()(const ExprBoolean& expr) {
|
2019-09-28 21:16:19 +02:00
|
|
|
return expr.value ? decomp.v_true : decomp.v_false;
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
|
2019-09-24 04:55:25 +02:00
|
|
|
Id operator()(const ExprGprEqual& expr) {
|
|
|
|
const Id target = decomp.Constant(decomp.t_uint, expr.value);
|
2019-12-10 03:44:29 +01:00
|
|
|
Id gpr = decomp.OpLoad(decomp.t_float, decomp.registers.at(expr.gpr));
|
|
|
|
gpr = decomp.OpBitcast(decomp.t_uint, gpr);
|
2019-12-19 05:46:20 +01:00
|
|
|
return decomp.OpIEqual(decomp.t_bool, gpr, target);
|
2019-09-24 04:55:25 +02:00
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
Id Visit(const Expr& node) {
|
2019-09-28 21:16:19 +02:00
|
|
|
return std::visit(*this, *node);
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
SPIRVDecompiler& decomp;
|
|
|
|
};
|
|
|
|
|
|
|
|
class ASTDecompiler {
|
|
|
|
public:
|
2019-09-28 21:16:19 +02:00
|
|
|
explicit ASTDecompiler(SPIRVDecompiler& decomp) : decomp{decomp} {}
|
2019-08-25 21:32:00 +02:00
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
void operator()(const ASTProgram& ast) {
|
2019-08-25 21:32:00 +02:00
|
|
|
ASTNode current = ast.nodes.GetFirst();
|
|
|
|
while (current) {
|
|
|
|
Visit(current);
|
|
|
|
current = current->GetNext();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
void operator()(const ASTIfThen& ast) {
|
2019-08-25 21:32:00 +02:00
|
|
|
ExprDecompiler expr_parser{decomp};
|
|
|
|
const Id condition = expr_parser.Visit(ast.condition);
|
|
|
|
const Id then_label = decomp.OpLabel();
|
|
|
|
const Id endif_label = decomp.OpLabel();
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone);
|
|
|
|
decomp.OpBranchConditional(condition, then_label, endif_label);
|
|
|
|
decomp.AddLabel(then_label);
|
2019-08-25 21:32:00 +02:00
|
|
|
ASTNode current = ast.nodes.GetFirst();
|
|
|
|
while (current) {
|
|
|
|
Visit(current);
|
|
|
|
current = current->GetNext();
|
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.OpBranch(endif_label);
|
|
|
|
decomp.AddLabel(endif_label);
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
void operator()([[maybe_unused]] const ASTIfElse& ast) {
|
2019-08-25 21:32:00 +02:00
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
void operator()([[maybe_unused]] const ASTBlockEncoded& ast) {
|
2019-08-25 21:32:00 +02:00
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
void operator()(const ASTBlockDecoded& ast) {
|
2019-08-25 21:32:00 +02:00
|
|
|
decomp.VisitBasicBlock(ast.nodes);
|
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
void operator()(const ASTVarSet& ast) {
|
2019-08-25 21:32:00 +02:00
|
|
|
ExprDecompiler expr_parser{decomp};
|
|
|
|
const Id condition = expr_parser.Visit(ast.condition);
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.OpStore(decomp.flow_variables.at(ast.index), condition);
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
void operator()([[maybe_unused]] const ASTLabel& ast) {
|
2019-08-25 21:32:00 +02:00
|
|
|
// Do nothing
|
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
void operator()([[maybe_unused]] const ASTGoto& ast) {
|
2019-08-25 21:32:00 +02:00
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
void operator()(const ASTDoWhile& ast) {
|
2019-08-25 21:32:00 +02:00
|
|
|
const Id loop_label = decomp.OpLabel();
|
|
|
|
const Id endloop_label = decomp.OpLabel();
|
|
|
|
const Id loop_start_block = decomp.OpLabel();
|
2019-12-19 05:46:20 +01:00
|
|
|
const Id loop_continue_block = decomp.OpLabel();
|
2019-08-25 21:32:00 +02:00
|
|
|
current_loop_exit = endloop_label;
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.OpBranch(loop_label);
|
|
|
|
decomp.AddLabel(loop_label);
|
2019-12-19 05:46:20 +01:00
|
|
|
decomp.OpLoopMerge(endloop_label, loop_continue_block, spv::LoopControlMask::MaskNone);
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.OpBranch(loop_start_block);
|
|
|
|
decomp.AddLabel(loop_start_block);
|
2019-08-25 21:32:00 +02:00
|
|
|
ASTNode current = ast.nodes.GetFirst();
|
|
|
|
while (current) {
|
|
|
|
Visit(current);
|
|
|
|
current = current->GetNext();
|
|
|
|
}
|
2019-12-19 05:46:20 +01:00
|
|
|
decomp.OpBranch(loop_continue_block);
|
|
|
|
decomp.AddLabel(loop_continue_block);
|
2019-08-25 21:32:00 +02:00
|
|
|
ExprDecompiler expr_parser{decomp};
|
|
|
|
const Id condition = expr_parser.Visit(ast.condition);
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.OpBranchConditional(condition, loop_label, endloop_label);
|
|
|
|
decomp.AddLabel(endloop_label);
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
void operator()(const ASTReturn& ast) {
|
2019-09-24 16:57:45 +02:00
|
|
|
if (!VideoCommon::Shader::ExprIsTrue(ast.condition)) {
|
2019-08-25 21:32:00 +02:00
|
|
|
ExprDecompiler expr_parser{decomp};
|
|
|
|
const Id condition = expr_parser.Visit(ast.condition);
|
|
|
|
const Id then_label = decomp.OpLabel();
|
|
|
|
const Id endif_label = decomp.OpLabel();
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone);
|
|
|
|
decomp.OpBranchConditional(condition, then_label, endif_label);
|
|
|
|
decomp.AddLabel(then_label);
|
2019-08-25 21:32:00 +02:00
|
|
|
if (ast.kills) {
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.OpKill();
|
2019-08-25 21:32:00 +02:00
|
|
|
} else {
|
|
|
|
decomp.PreExit();
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.OpReturn();
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.AddLabel(endif_label);
|
2019-08-25 21:32:00 +02:00
|
|
|
} else {
|
2019-09-21 06:45:13 +02:00
|
|
|
const Id next_block = decomp.OpLabel();
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.OpBranch(next_block);
|
|
|
|
decomp.AddLabel(next_block);
|
2019-08-25 21:32:00 +02:00
|
|
|
if (ast.kills) {
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.OpKill();
|
2019-08-25 21:32:00 +02:00
|
|
|
} else {
|
|
|
|
decomp.PreExit();
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.OpReturn();
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.AddLabel(decomp.OpLabel());
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
void operator()(const ASTBreak& ast) {
|
2019-09-24 16:57:45 +02:00
|
|
|
if (!VideoCommon::Shader::ExprIsTrue(ast.condition)) {
|
2019-08-25 21:32:00 +02:00
|
|
|
ExprDecompiler expr_parser{decomp};
|
|
|
|
const Id condition = expr_parser.Visit(ast.condition);
|
|
|
|
const Id then_label = decomp.OpLabel();
|
|
|
|
const Id endif_label = decomp.OpLabel();
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone);
|
|
|
|
decomp.OpBranchConditional(condition, then_label, endif_label);
|
|
|
|
decomp.AddLabel(then_label);
|
|
|
|
decomp.OpBranch(current_loop_exit);
|
|
|
|
decomp.AddLabel(endif_label);
|
2019-08-25 21:32:00 +02:00
|
|
|
} else {
|
2019-09-21 06:45:13 +02:00
|
|
|
const Id next_block = decomp.OpLabel();
|
2019-12-10 03:44:29 +01:00
|
|
|
decomp.OpBranch(next_block);
|
|
|
|
decomp.AddLabel(next_block);
|
|
|
|
decomp.OpBranch(current_loop_exit);
|
|
|
|
decomp.AddLabel(decomp.OpLabel());
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
void Visit(const ASTNode& node) {
|
2019-08-25 21:32:00 +02:00
|
|
|
std::visit(*this, *node->GetInnerData());
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
SPIRVDecompiler& decomp;
|
2019-09-28 21:16:19 +02:00
|
|
|
Id current_loop_exit{};
|
2019-08-25 21:32:00 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
void SPIRVDecompiler::DecompileAST() {
|
2019-09-24 16:57:45 +02:00
|
|
|
const u32 num_flow_variables = ir.GetASTNumVariables();
|
2019-08-25 21:32:00 +02:00
|
|
|
for (u32 i = 0; i < num_flow_variables; i++) {
|
|
|
|
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
|
|
|
Name(id, fmt::format("flow_var_{}", i));
|
|
|
|
flow_variables.emplace(i, AddGlobalVariable(id));
|
|
|
|
}
|
2019-10-18 01:36:55 +02:00
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
DefinePrologue();
|
|
|
|
|
2019-10-18 01:36:55 +02:00
|
|
|
const ASTNode program = ir.GetASTProgram();
|
2019-08-25 21:32:00 +02:00
|
|
|
ASTDecompiler decompiler{*this};
|
|
|
|
decompiler.Visit(program);
|
2019-10-18 01:36:55 +02:00
|
|
|
|
2019-09-21 06:45:13 +02:00
|
|
|
const Id next_block = OpLabel();
|
2019-12-10 03:44:29 +01:00
|
|
|
OpBranch(next_block);
|
|
|
|
AddLabel(next_block);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // Anonymous namespace
|
|
|
|
|
|
|
|
ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir) {
|
|
|
|
ShaderEntries entries;
|
|
|
|
for (const auto& cbuf : ir.GetConstantBuffers()) {
|
|
|
|
entries.const_buffers.emplace_back(cbuf.second, cbuf.first);
|
|
|
|
}
|
|
|
|
for (const auto& [base, usage] : ir.GetGlobalMemory()) {
|
|
|
|
entries.global_buffers.emplace_back(base.cbuf_index, base.cbuf_offset, usage.is_written);
|
|
|
|
}
|
|
|
|
for (const auto& sampler : ir.GetSamplers()) {
|
|
|
|
if (sampler.IsBuffer()) {
|
|
|
|
entries.texel_buffers.emplace_back(sampler);
|
|
|
|
} else {
|
|
|
|
entries.samplers.emplace_back(sampler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const auto& image : ir.GetImages()) {
|
|
|
|
entries.images.emplace_back(image);
|
|
|
|
}
|
|
|
|
for (const auto& attribute : ir.GetInputAttributes()) {
|
|
|
|
if (IsGenericAttribute(attribute)) {
|
|
|
|
entries.attributes.insert(GetGenericAttributeLocation(attribute));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
entries.clip_distances = ir.GetClipDistances();
|
|
|
|
entries.shader_length = ir.GetLength();
|
|
|
|
entries.uses_warps = ir.UsesWarps();
|
|
|
|
return entries;
|
2019-08-25 21:32:00 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
std::vector<u32> Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir,
|
2020-03-03 01:36:25 +01:00
|
|
|
ShaderType stage, const VideoCommon::Shader::Registry& registry,
|
|
|
|
const Specialization& specialization) {
|
|
|
|
return SPIRVDecompiler(device, ir, stage, registry, specialization).Assemble();
|
2019-03-14 06:19:02 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 03:44:29 +01:00
|
|
|
} // namespace Vulkan
|