2018-12-21 01:45:49 +01:00
|
|
|
// Copyright 2018 yuzu Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2018-12-27 05:50:22 +01:00
|
|
|
#include <array>
|
2018-12-21 01:45:49 +01:00
|
|
|
#include <string>
|
|
|
|
#include <string_view>
|
2019-02-22 07:30:12 +01:00
|
|
|
#include <utility>
|
2018-12-21 01:45:49 +01:00
|
|
|
#include <variant>
|
2019-02-22 07:30:12 +01:00
|
|
|
#include <vector>
|
2018-12-21 01:45:49 +01:00
|
|
|
|
|
|
|
#include <fmt/format.h>
|
|
|
|
|
|
|
|
#include "common/alignment.h"
|
|
|
|
#include "common/assert.h"
|
|
|
|
#include "common/common_types.h"
|
2019-07-08 01:36:42 +02:00
|
|
|
#include "common/logging/log.h"
|
2018-12-21 01:45:49 +01:00
|
|
|
#include "video_core/engines/maxwell_3d.h"
|
2019-04-10 23:03:52 +02:00
|
|
|
#include "video_core/renderer_opengl/gl_device.h"
|
2018-12-26 06:14:06 +01:00
|
|
|
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
2018-12-26 05:57:14 +01:00
|
|
|
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
2018-12-21 01:45:49 +01:00
|
|
|
#include "video_core/shader/shader_ir.h"
|
|
|
|
|
|
|
|
namespace OpenGL::GLShader {
|
|
|
|
|
2019-03-31 05:24:10 +02:00
|
|
|
namespace {
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
using Tegra::Shader::Attribute;
|
2019-02-13 02:14:39 +01:00
|
|
|
using Tegra::Shader::AttributeUse;
|
2018-12-21 01:45:49 +01:00
|
|
|
using Tegra::Shader::Header;
|
|
|
|
using Tegra::Shader::IpaInterpMode;
|
|
|
|
using Tegra::Shader::IpaMode;
|
|
|
|
using Tegra::Shader::IpaSampleMode;
|
2018-12-26 05:49:32 +01:00
|
|
|
using Tegra::Shader::Register;
|
2019-05-03 07:59:25 +02:00
|
|
|
|
|
|
|
using namespace std::string_literals;
|
2018-12-21 01:45:49 +01:00
|
|
|
using namespace VideoCommon::Shader;
|
|
|
|
|
|
|
|
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
|
|
|
using Operation = const OperationNode&;
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
enum class Type { Void, Bool, Bool2, Float, Int, Uint, HalfFloat };
|
2019-03-29 22:37:37 +01:00
|
|
|
|
|
|
|
struct TextureAoffi {};
|
|
|
|
using TextureArgument = std::pair<Type, Node>;
|
|
|
|
using TextureIR = std::variant<TextureAoffi, TextureArgument>;
|
|
|
|
|
2018-12-26 06:14:06 +01:00
|
|
|
constexpr u32 MAX_CONSTBUFFER_ELEMENTS =
|
2019-07-06 04:11:58 +02:00
|
|
|
static_cast<u32>(Maxwell::MaxConstBufferSize) / (4 * sizeof(float));
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
class ShaderWriter final {
|
2018-12-21 01:45:49 +01:00
|
|
|
public:
|
|
|
|
void AddExpression(std::string_view text) {
|
|
|
|
DEBUG_ASSERT(scope >= 0);
|
|
|
|
if (!text.empty()) {
|
|
|
|
AppendIndentation();
|
|
|
|
}
|
|
|
|
shader_source += text;
|
|
|
|
}
|
|
|
|
|
2019-05-14 23:59:25 +02:00
|
|
|
// Forwards all arguments directly to libfmt.
|
2019-05-14 18:12:57 +02:00
|
|
|
// Note that all formatting requirements for fmt must be
|
|
|
|
// obeyed when using this function. (e.g. {{ must be used
|
|
|
|
// printing the character '{' is desirable. Ditto for }} and '}',
|
|
|
|
// etc).
|
|
|
|
template <typename... Args>
|
|
|
|
void AddLine(std::string_view text, Args&&... args) {
|
|
|
|
AddExpression(fmt::format(text, std::forward<Args>(args)...));
|
|
|
|
AddNewLine();
|
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
void AddNewLine() {
|
|
|
|
DEBUG_ASSERT(scope >= 0);
|
|
|
|
shader_source += '\n';
|
|
|
|
}
|
|
|
|
|
2019-04-05 01:35:01 +02:00
|
|
|
std::string GenerateTemporary() {
|
2019-05-15 00:12:29 +02:00
|
|
|
return fmt::format("tmp{}", temporary_index++);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetResult() {
|
|
|
|
return std::move(shader_source);
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 scope = 0;
|
|
|
|
|
|
|
|
private:
|
|
|
|
void AppendIndentation() {
|
|
|
|
shader_source.append(static_cast<std::size_t>(scope) * 4, ' ');
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string shader_source;
|
2019-04-05 01:35:01 +02:00
|
|
|
u32 temporary_index = 1;
|
2018-12-21 01:45:49 +01:00
|
|
|
};
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
class Expression final {
|
|
|
|
public:
|
|
|
|
Expression(std::string code, Type type) : code{std::move(code)}, type{type} {
|
|
|
|
ASSERT(type != Type::Void);
|
|
|
|
}
|
|
|
|
Expression() : type{Type::Void} {}
|
|
|
|
|
|
|
|
Type GetType() const {
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetCode() const {
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckVoid() const {
|
|
|
|
ASSERT(type == Type::Void);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string As(Type type) const {
|
|
|
|
switch (type) {
|
|
|
|
case Type::Bool:
|
|
|
|
return AsBool();
|
|
|
|
case Type::Bool2:
|
|
|
|
return AsBool2();
|
|
|
|
case Type::Float:
|
|
|
|
return AsFloat();
|
|
|
|
case Type::Int:
|
|
|
|
return AsInt();
|
|
|
|
case Type::Uint:
|
|
|
|
return AsUint();
|
|
|
|
case Type::HalfFloat:
|
|
|
|
return AsHalfFloat();
|
|
|
|
default:
|
|
|
|
UNREACHABLE_MSG("Invalid type");
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string AsBool() const {
|
|
|
|
switch (type) {
|
|
|
|
case Type::Bool:
|
|
|
|
return code;
|
|
|
|
default:
|
|
|
|
UNREACHABLE_MSG("Incompatible types");
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string AsBool2() const {
|
|
|
|
switch (type) {
|
|
|
|
case Type::Bool2:
|
|
|
|
return code;
|
|
|
|
default:
|
|
|
|
UNREACHABLE_MSG("Incompatible types");
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string AsFloat() const {
|
|
|
|
switch (type) {
|
|
|
|
case Type::Float:
|
|
|
|
return code;
|
|
|
|
case Type::Uint:
|
|
|
|
return fmt::format("utof({})", code);
|
|
|
|
case Type::Int:
|
|
|
|
return fmt::format("itof({})", code);
|
|
|
|
case Type::HalfFloat:
|
|
|
|
return fmt::format("utof(packHalf2x16({}))", code);
|
|
|
|
default:
|
|
|
|
UNREACHABLE_MSG("Incompatible types");
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string AsInt() const {
|
|
|
|
switch (type) {
|
|
|
|
case Type::Float:
|
|
|
|
return fmt::format("ftoi({})", code);
|
|
|
|
case Type::Uint:
|
|
|
|
return fmt::format("int({})", code);
|
|
|
|
case Type::Int:
|
|
|
|
return code;
|
|
|
|
case Type::HalfFloat:
|
|
|
|
return fmt::format("int(packHalf2x16({}))", code);
|
|
|
|
default:
|
|
|
|
UNREACHABLE_MSG("Incompatible types");
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string AsUint() const {
|
|
|
|
switch (type) {
|
|
|
|
case Type::Float:
|
|
|
|
return fmt::format("ftou({})", code);
|
|
|
|
case Type::Uint:
|
|
|
|
return code;
|
|
|
|
case Type::Int:
|
|
|
|
return fmt::format("uint({})", code);
|
|
|
|
case Type::HalfFloat:
|
|
|
|
return fmt::format("packHalf2x16({})", code);
|
|
|
|
default:
|
|
|
|
UNREACHABLE_MSG("Incompatible types");
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string AsHalfFloat() const {
|
|
|
|
switch (type) {
|
|
|
|
case Type::Float:
|
|
|
|
return fmt::format("unpackHalf2x16(ftou({}))", code);
|
|
|
|
case Type::Uint:
|
|
|
|
return fmt::format("unpackHalf2x16({})", code);
|
|
|
|
case Type::Int:
|
|
|
|
return fmt::format("unpackHalf2x16(int({}))", code);
|
|
|
|
case Type::HalfFloat:
|
|
|
|
return code;
|
|
|
|
default:
|
|
|
|
UNREACHABLE_MSG("Incompatible types");
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::string code;
|
|
|
|
Type type{};
|
|
|
|
};
|
|
|
|
|
|
|
|
constexpr const char* GetTypeString(Type type) {
|
|
|
|
switch (type) {
|
|
|
|
case Type::Bool:
|
|
|
|
return "bool";
|
|
|
|
case Type::Bool2:
|
|
|
|
return "bvec2";
|
|
|
|
case Type::Float:
|
|
|
|
return "float";
|
|
|
|
case Type::Int:
|
|
|
|
return "int";
|
|
|
|
case Type::Uint:
|
|
|
|
return "uint";
|
|
|
|
case Type::HalfFloat:
|
|
|
|
return "vec2";
|
|
|
|
default:
|
|
|
|
UNREACHABLE_MSG("Invalid type");
|
|
|
|
return "<invalid type>";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
/// Generates code to use for a swizzle operation.
|
2019-05-03 07:59:25 +02:00
|
|
|
constexpr const char* GetSwizzle(u32 element) {
|
2019-08-24 22:29:19 +02:00
|
|
|
constexpr std::array swizzle = {".x", ".y", ".z", ".w"};
|
2019-05-03 07:59:25 +02:00
|
|
|
return swizzle.at(element);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2018-12-24 06:24:38 +01:00
|
|
|
/// Translate topology
|
2019-03-31 05:24:10 +02:00
|
|
|
std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
|
2018-12-24 06:24:38 +01:00
|
|
|
switch (topology) {
|
|
|
|
case Tegra::Shader::OutputTopology::PointList:
|
|
|
|
return "points";
|
|
|
|
case Tegra::Shader::OutputTopology::LineStrip:
|
|
|
|
return "line_strip";
|
|
|
|
case Tegra::Shader::OutputTopology::TriangleStrip:
|
|
|
|
return "triangle_strip";
|
|
|
|
default:
|
|
|
|
UNIMPLEMENTED_MSG("Unknown output topology: {}", static_cast<u32>(topology));
|
|
|
|
return "points";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns true if an object has to be treated as precise
|
2019-03-31 05:24:10 +02:00
|
|
|
bool IsPrecise(Operation operand) {
|
2019-04-16 00:48:11 +02:00
|
|
|
const auto& meta{operand.GetMeta()};
|
2019-01-15 21:52:49 +01:00
|
|
|
if (const auto arithmetic = std::get_if<MetaArithmetic>(&meta)) {
|
|
|
|
return arithmetic->precise;
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-06-05 03:44:06 +02:00
|
|
|
bool IsPrecise(const Node& node) {
|
|
|
|
if (const auto operation = std::get_if<OperationNode>(&*node)) {
|
2019-01-15 21:52:49 +01:00
|
|
|
return IsPrecise(*operation);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
2019-01-15 21:52:49 +01:00
|
|
|
return false;
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-04-30 05:18:28 +02:00
|
|
|
constexpr bool IsGenericAttribute(Attribute::Index index) {
|
|
|
|
return index >= Attribute::Index::Attribute_0 && index <= Attribute::Index::Attribute_31;
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
constexpr Attribute::Index ToGenericAttribute(u64 value) {
|
|
|
|
return static_cast<Attribute::Index>(value + static_cast<u64>(Attribute::Index::Attribute_0));
|
2019-04-30 05:18:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
u32 GetGenericAttributeIndex(Attribute::Index index) {
|
|
|
|
ASSERT(IsGenericAttribute(index));
|
|
|
|
return static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0);
|
|
|
|
}
|
|
|
|
|
2019-06-02 23:52:07 +02:00
|
|
|
constexpr const char* GetFlowStackPrefix(MetaStackClass stack) {
|
|
|
|
switch (stack) {
|
|
|
|
case MetaStackClass::Ssy:
|
|
|
|
return "ssy";
|
|
|
|
case MetaStackClass::Pbk:
|
|
|
|
return "pbk";
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string FlowStackName(MetaStackClass stack) {
|
|
|
|
return fmt::format("{}_flow_stack", GetFlowStackPrefix(stack));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string FlowStackTopName(MetaStackClass stack) {
|
|
|
|
return fmt::format("{}_flow_stack_top", GetFlowStackPrefix(stack));
|
|
|
|
}
|
|
|
|
|
2019-07-15 03:25:13 +02:00
|
|
|
constexpr bool IsVertexShader(ProgramType stage) {
|
|
|
|
return stage == ProgramType::VertexA || stage == ProgramType::VertexB;
|
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
class GLSLDecompiler final {
|
|
|
|
public:
|
2019-07-15 03:25:13 +02:00
|
|
|
explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ProgramType stage,
|
2019-04-10 23:03:52 +02:00
|
|
|
std::string suffix)
|
|
|
|
: device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {}
|
2018-12-21 01:45:49 +01:00
|
|
|
|
|
|
|
void Decompile() {
|
|
|
|
DeclareVertex();
|
2018-12-24 06:24:38 +01:00
|
|
|
DeclareGeometry();
|
2018-12-21 01:45:49 +01:00
|
|
|
DeclareRegisters();
|
|
|
|
DeclarePredicates();
|
|
|
|
DeclareLocalMemory();
|
2019-08-29 22:18:04 +02:00
|
|
|
DeclareSharedMemory();
|
2018-12-21 01:45:49 +01:00
|
|
|
DeclareInternalFlags();
|
|
|
|
DeclareInputAttributes();
|
|
|
|
DeclareOutputAttributes();
|
|
|
|
DeclareConstantBuffers();
|
2018-12-29 06:44:54 +01:00
|
|
|
DeclareGlobalMemory();
|
2018-12-21 01:45:49 +01:00
|
|
|
DeclareSamplers();
|
2019-05-01 00:36:18 +02:00
|
|
|
DeclarePhysicalAttributeReader();
|
2019-04-27 07:07:18 +02:00
|
|
|
DeclareImages();
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("void execute_{}() {{", suffix);
|
2018-12-21 01:45:49 +01:00
|
|
|
++code.scope;
|
|
|
|
|
|
|
|
// VM's program counter
|
|
|
|
const auto first_address = ir.GetBasicBlocks().begin()->first;
|
2019-08-24 22:29:19 +02:00
|
|
|
code.AddLine("uint jmp_to = {}U;", first_address);
|
2018-12-21 01:45:49 +01:00
|
|
|
|
|
|
|
// TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
|
|
|
|
// unlikely that shaders will use 20 nested SSYs and PBKs.
|
2019-06-25 19:03:51 +02:00
|
|
|
if (!ir.IsFlowStackDisabled()) {
|
|
|
|
constexpr u32 FLOW_STACK_SIZE = 20;
|
|
|
|
for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) {
|
|
|
|
code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE);
|
2019-08-24 22:29:19 +02:00
|
|
|
code.AddLine("uint {} = 0U;", FlowStackTopName(stack));
|
2019-06-25 19:03:51 +02:00
|
|
|
}
|
2019-06-02 23:52:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-05-14 23:59:25 +02:00
|
|
|
code.AddLine("while (true) {{");
|
2018-12-21 01:45:49 +01:00
|
|
|
++code.scope;
|
|
|
|
|
2019-05-14 23:59:25 +02:00
|
|
|
code.AddLine("switch (jmp_to) {{");
|
2018-12-21 01:45:49 +01:00
|
|
|
|
|
|
|
for (const auto& pair : ir.GetBasicBlocks()) {
|
|
|
|
const auto [address, bb] = pair;
|
2019-08-24 22:29:19 +02:00
|
|
|
code.AddLine("case 0x{:X}U: {{", address);
|
2018-12-21 01:45:49 +01:00
|
|
|
++code.scope;
|
|
|
|
|
2019-01-30 06:09:40 +01:00
|
|
|
VisitBlock(bb);
|
2018-12-21 01:45:49 +01:00
|
|
|
|
|
|
|
--code.scope;
|
2019-05-14 23:59:25 +02:00
|
|
|
code.AddLine("}}");
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
code.AddLine("default: return;");
|
2019-05-14 23:59:25 +02:00
|
|
|
code.AddLine("}}");
|
2018-12-21 01:45:49 +01:00
|
|
|
|
|
|
|
for (std::size_t i = 0; i < 2; ++i) {
|
|
|
|
--code.scope;
|
2019-05-14 23:59:25 +02:00
|
|
|
code.AddLine("}}");
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetResult() {
|
|
|
|
return code.GetResult();
|
|
|
|
}
|
|
|
|
|
|
|
|
ShaderEntries GetShaderEntries() const {
|
|
|
|
ShaderEntries entries;
|
|
|
|
for (const auto& cbuf : ir.GetConstantBuffers()) {
|
2019-01-15 05:07:57 +01:00
|
|
|
entries.const_buffers.emplace_back(cbuf.second.GetMaxOffset(), cbuf.second.IsIndirect(),
|
|
|
|
cbuf.first);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
for (const auto& sampler : ir.GetSamplers()) {
|
2019-01-15 05:07:57 +01:00
|
|
|
entries.samplers.emplace_back(sampler);
|
2018-12-29 06:44:54 +01:00
|
|
|
}
|
2019-09-06 04:26:05 +02:00
|
|
|
for (const auto& [offset, image] : ir.GetImages()) {
|
2019-04-27 07:37:15 +02:00
|
|
|
entries.images.emplace_back(image);
|
|
|
|
}
|
2019-09-06 04:26:05 +02:00
|
|
|
for (const auto& [base, usage] : ir.GetGlobalMemory()) {
|
2019-02-07 04:05:41 +01:00
|
|
|
entries.global_memory_entries.emplace_back(base.cbuf_index, base.cbuf_offset,
|
|
|
|
usage.is_read, usage.is_written);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
entries.clip_distances = ir.GetClipDistances();
|
2019-07-08 01:36:42 +02:00
|
|
|
entries.shader_viewport_layer_array =
|
2019-07-15 03:25:13 +02:00
|
|
|
IsVertexShader(stage) && (ir.UsesLayer() || ir.UsesViewportIndex());
|
2018-12-21 01:45:49 +01:00
|
|
|
entries.shader_length = ir.GetLength();
|
|
|
|
return entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void DeclareVertex() {
|
2019-07-15 03:25:13 +02:00
|
|
|
if (!IsVertexShader(stage))
|
2018-12-21 01:45:49 +01:00
|
|
|
return;
|
|
|
|
|
2018-12-25 01:28:44 +01:00
|
|
|
DeclareVertexRedeclarations();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareGeometry() {
|
2019-07-15 03:25:13 +02:00
|
|
|
if (stage != ProgramType::Geometry) {
|
2018-12-25 01:28:44 +01:00
|
|
|
return;
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-25 01:28:44 +01:00
|
|
|
|
|
|
|
const auto topology = GetTopologyName(header.common3.output_topology);
|
2019-05-14 18:18:07 +02:00
|
|
|
const auto max_vertices = header.common4.max_output_vertices.Value();
|
|
|
|
code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_vertices);
|
2018-12-25 01:28:44 +01:00
|
|
|
code.AddNewLine();
|
|
|
|
|
2019-06-03 06:01:34 +02:00
|
|
|
code.AddLine("in gl_PerVertex {{");
|
|
|
|
++code.scope;
|
|
|
|
code.AddLine("vec4 gl_Position;");
|
|
|
|
--code.scope;
|
|
|
|
code.AddLine("}} gl_in[];");
|
|
|
|
|
2018-12-25 01:28:44 +01:00
|
|
|
DeclareVertexRedeclarations();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareVertexRedeclarations() {
|
2019-05-14 23:59:25 +02:00
|
|
|
code.AddLine("out gl_PerVertex {{");
|
2018-12-21 01:45:49 +01:00
|
|
|
++code.scope;
|
|
|
|
|
|
|
|
code.AddLine("vec4 gl_Position;");
|
|
|
|
|
2019-07-08 01:36:42 +02:00
|
|
|
for (const auto attribute : ir.GetOutputAttributes()) {
|
|
|
|
if (attribute == Attribute::Index::ClipDistances0123 ||
|
|
|
|
attribute == Attribute::Index::ClipDistances4567) {
|
2018-12-21 01:45:49 +01:00
|
|
|
code.AddLine("float gl_ClipDistance[];");
|
2019-07-08 01:36:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-07-15 03:25:13 +02:00
|
|
|
if (!IsVertexShader(stage) || device.HasVertexViewportLayer()) {
|
2019-07-08 01:36:42 +02:00
|
|
|
if (ir.UsesLayer()) {
|
|
|
|
code.AddLine("int gl_Layer;");
|
|
|
|
}
|
|
|
|
if (ir.UsesViewportIndex()) {
|
|
|
|
code.AddLine("int gl_ViewportIndex;");
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
2019-07-15 03:25:13 +02:00
|
|
|
} else if ((ir.UsesLayer() || ir.UsesViewportIndex()) && IsVertexShader(stage) &&
|
2019-07-11 09:24:57 +02:00
|
|
|
!device.HasVertexViewportLayer()) {
|
2019-07-08 01:36:42 +02:00
|
|
|
LOG_ERROR(
|
|
|
|
Render_OpenGL,
|
|
|
|
"GL_ARB_shader_viewport_layer_array is not available and its required by a shader");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ir.UsesPointSize()) {
|
2019-07-11 21:10:59 +02:00
|
|
|
code.AddLine("float gl_PointSize;");
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
--code.scope;
|
2019-05-14 23:59:25 +02:00
|
|
|
code.AddLine("}};");
|
2018-12-21 01:45:49 +01:00
|
|
|
code.AddNewLine();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareRegisters() {
|
|
|
|
const auto& registers = ir.GetRegisters();
|
|
|
|
for (const u32 gpr : registers) {
|
2019-08-24 22:29:19 +02:00
|
|
|
code.AddLine("float {} = 0.0f;", GetRegister(gpr));
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
2019-05-14 18:18:07 +02:00
|
|
|
if (!registers.empty()) {
|
2018-12-21 01:45:49 +01:00
|
|
|
code.AddNewLine();
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DeclarePredicates() {
|
|
|
|
const auto& predicates = ir.GetPredicates();
|
|
|
|
for (const auto pred : predicates) {
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("bool {} = false;", GetPredicate(pred));
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
2019-05-14 18:18:07 +02:00
|
|
|
if (!predicates.empty()) {
|
2018-12-21 01:45:49 +01:00
|
|
|
code.AddNewLine();
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareLocalMemory() {
|
2019-07-15 22:28:27 +02:00
|
|
|
// TODO(Rodrigo): Unstub kernel local memory size and pass it from a register at
|
|
|
|
// specialization time.
|
|
|
|
const u64 local_memory_size =
|
|
|
|
stage == ProgramType::Compute ? 0x400 : header.GetLocalMemorySize();
|
|
|
|
if (local_memory_size == 0) {
|
2019-07-15 03:25:13 +02:00
|
|
|
return;
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
2019-07-15 22:28:27 +02:00
|
|
|
const auto element_count = Common::AlignUp(local_memory_size, 4) / 4;
|
2019-08-24 22:29:19 +02:00
|
|
|
code.AddLine("uint {}[{}];", GetLocalMemory(), element_count);
|
2019-07-15 22:28:27 +02:00
|
|
|
code.AddNewLine();
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-29 22:18:04 +02:00
|
|
|
void DeclareSharedMemory() {
|
|
|
|
if (stage != ProgramType::Compute) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
code.AddLine("shared uint {}[];", GetSharedMemory());
|
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
void DeclareInternalFlags() {
|
|
|
|
for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) {
|
2019-05-14 18:18:07 +02:00
|
|
|
const auto flag_code = static_cast<InternalFlag>(flag);
|
|
|
|
code.AddLine("bool {} = false;", GetInternalFlag(flag_code));
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
code.AddNewLine();
|
|
|
|
}
|
|
|
|
|
2019-02-13 02:14:39 +01:00
|
|
|
std::string GetInputFlags(AttributeUse attribute) {
|
|
|
|
switch (attribute) {
|
|
|
|
case AttributeUse::Perspective:
|
2018-12-21 01:45:49 +01:00
|
|
|
// Default, Smooth
|
2019-05-02 21:06:56 +02:00
|
|
|
return {};
|
|
|
|
case AttributeUse::Constant:
|
|
|
|
return "flat ";
|
|
|
|
case AttributeUse::ScreenLinear:
|
|
|
|
return "noperspective ";
|
2018-12-21 01:45:49 +01:00
|
|
|
default:
|
2019-05-02 21:06:56 +02:00
|
|
|
case AttributeUse::Unused:
|
|
|
|
UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<u32>(attribute));
|
|
|
|
return {};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareInputAttributes() {
|
2019-04-30 05:09:51 +02:00
|
|
|
if (ir.HasPhysicalAttributes()) {
|
2019-05-01 00:36:18 +02:00
|
|
|
const u32 num_inputs{GetNumPhysicalInputAttributes()};
|
2019-04-30 05:09:51 +02:00
|
|
|
for (u32 i = 0; i < num_inputs; ++i) {
|
2019-05-02 21:06:56 +02:00
|
|
|
DeclareInputAttribute(ToGenericAttribute(i), true);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
2019-04-30 05:09:51 +02:00
|
|
|
code.AddNewLine();
|
|
|
|
return;
|
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
|
|
|
|
const auto& attributes = ir.GetInputAttributes();
|
2019-04-30 04:37:09 +02:00
|
|
|
for (const auto index : attributes) {
|
2019-04-30 05:18:28 +02:00
|
|
|
if (IsGenericAttribute(index)) {
|
2019-05-02 21:06:56 +02:00
|
|
|
DeclareInputAttribute(index, false);
|
2019-02-13 02:14:39 +01:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
2019-05-14 18:18:07 +02:00
|
|
|
if (!attributes.empty()) {
|
2018-12-21 01:45:49 +01:00
|
|
|
code.AddNewLine();
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-05-02 21:06:56 +02:00
|
|
|
void DeclareInputAttribute(Attribute::Index index, bool skip_unused) {
|
2019-06-03 06:01:34 +02:00
|
|
|
const u32 location{GetGenericAttributeIndex(index)};
|
2019-04-30 05:09:51 +02:00
|
|
|
|
|
|
|
std::string name{GetInputAttribute(index)};
|
2019-07-15 03:25:13 +02:00
|
|
|
if (stage == ProgramType::Geometry) {
|
2019-04-30 05:09:51 +02:00
|
|
|
name = "gs_" + name + "[]";
|
|
|
|
}
|
2019-04-30 05:18:28 +02:00
|
|
|
|
2019-04-30 05:09:51 +02:00
|
|
|
std::string suffix;
|
2019-07-15 03:25:13 +02:00
|
|
|
if (stage == ProgramType::Fragment) {
|
2019-06-03 06:01:34 +02:00
|
|
|
const auto input_mode{header.ps.GetAttributeUse(location)};
|
2019-05-02 21:06:56 +02:00
|
|
|
if (skip_unused && input_mode == AttributeUse::Unused) {
|
|
|
|
return;
|
|
|
|
}
|
2019-04-30 05:09:51 +02:00
|
|
|
suffix = GetInputFlags(input_mode);
|
|
|
|
}
|
|
|
|
|
2019-05-21 15:45:39 +02:00
|
|
|
code.AddLine("layout (location = {}) {} in vec4 {};", location, suffix, name);
|
2019-04-30 05:09:51 +02:00
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
void DeclareOutputAttributes() {
|
2019-07-15 03:25:13 +02:00
|
|
|
if (ir.HasPhysicalAttributes() && stage != ProgramType::Fragment) {
|
2019-04-30 05:09:51 +02:00
|
|
|
for (u32 i = 0; i < GetNumPhysicalVaryings(); ++i) {
|
2019-04-30 05:18:28 +02:00
|
|
|
DeclareOutputAttribute(ToGenericAttribute(i));
|
2019-04-30 05:09:51 +02:00
|
|
|
}
|
|
|
|
code.AddNewLine();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
const auto& attributes = ir.GetOutputAttributes();
|
|
|
|
for (const auto index : attributes) {
|
2019-04-30 05:18:28 +02:00
|
|
|
if (IsGenericAttribute(index)) {
|
|
|
|
DeclareOutputAttribute(index);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
}
|
2019-05-14 18:18:07 +02:00
|
|
|
if (!attributes.empty()) {
|
2018-12-21 01:45:49 +01:00
|
|
|
code.AddNewLine();
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-04-30 05:09:51 +02:00
|
|
|
void DeclareOutputAttribute(Attribute::Index index) {
|
2019-06-03 06:01:34 +02:00
|
|
|
const u32 location{GetGenericAttributeIndex(index)};
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("layout (location = {}) out vec4 {};", location, GetOutputAttribute(index));
|
2019-04-30 05:09:51 +02:00
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
void DeclareConstantBuffers() {
|
|
|
|
for (const auto& entry : ir.GetConstantBuffers()) {
|
|
|
|
const auto [index, size] = entry;
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("layout (std140, binding = CBUF_BINDING_{}) uniform {} {{", index,
|
|
|
|
GetConstBufferBlock(index));
|
2019-08-24 22:29:19 +02:00
|
|
|
code.AddLine(" uvec4 {}[{}];", GetConstBuffer(index), MAX_CONSTBUFFER_ELEMENTS);
|
2019-05-14 23:59:25 +02:00
|
|
|
code.AddLine("}};");
|
2018-12-21 01:45:49 +01:00
|
|
|
code.AddNewLine();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-29 06:44:54 +01:00
|
|
|
void DeclareGlobalMemory() {
|
2019-02-07 04:05:41 +01:00
|
|
|
for (const auto& gmem : ir.GetGlobalMemory()) {
|
|
|
|
const auto& [base, usage] = gmem;
|
|
|
|
|
|
|
|
// Since we don't know how the shader will use the shader, hint the driver to disable as
|
|
|
|
// much optimizations as possible
|
|
|
|
std::string qualifier = "coherent volatile";
|
2019-05-14 18:18:07 +02:00
|
|
|
if (usage.is_read && !usage.is_written) {
|
2019-02-07 04:05:41 +01:00
|
|
|
qualifier += " readonly";
|
2019-05-14 18:18:07 +02:00
|
|
|
} else if (usage.is_written && !usage.is_read) {
|
2019-02-07 04:05:41 +01:00
|
|
|
qualifier += " writeonly";
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2019-02-07 04:05:41 +01:00
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("layout (std430, binding = GMEM_BINDING_{}_{}) {} buffer {} {{",
|
|
|
|
base.cbuf_index, base.cbuf_offset, qualifier, GetGlobalMemoryBlock(base));
|
2019-08-24 22:29:19 +02:00
|
|
|
code.AddLine(" uint {}[];", GetGlobalMemory(base));
|
2019-05-14 23:59:25 +02:00
|
|
|
code.AddLine("}};");
|
2018-12-29 06:44:54 +01:00
|
|
|
code.AddNewLine();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
void DeclareSamplers() {
|
|
|
|
const auto& samplers = ir.GetSamplers();
|
|
|
|
for (const auto& sampler : samplers) {
|
2019-04-28 02:45:59 +02:00
|
|
|
const std::string name{GetSampler(sampler)};
|
|
|
|
const std::string description{"layout (binding = SAMPLER_BINDING_" +
|
2019-06-24 06:56:38 +02:00
|
|
|
std::to_string(sampler.GetIndex()) + ") uniform"};
|
2019-04-28 02:45:59 +02:00
|
|
|
std::string sampler_type = [&]() {
|
2018-12-21 01:45:49 +01:00
|
|
|
switch (sampler.GetType()) {
|
|
|
|
case Tegra::Shader::TextureType::Texture1D:
|
2019-04-28 02:45:59 +02:00
|
|
|
// Special cased, read below.
|
2018-12-21 01:45:49 +01:00
|
|
|
return "sampler1D";
|
|
|
|
case Tegra::Shader::TextureType::Texture2D:
|
|
|
|
return "sampler2D";
|
|
|
|
case Tegra::Shader::TextureType::Texture3D:
|
|
|
|
return "sampler3D";
|
|
|
|
case Tegra::Shader::TextureType::TextureCube:
|
|
|
|
return "samplerCube";
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
2018-12-21 22:47:22 +01:00
|
|
|
return "sampler2D";
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
}();
|
2019-05-14 18:18:07 +02:00
|
|
|
if (sampler.IsArray()) {
|
2018-12-21 01:45:49 +01:00
|
|
|
sampler_type += "Array";
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
|
|
|
if (sampler.IsShadow()) {
|
2018-12-21 01:45:49 +01:00
|
|
|
sampler_type += "Shadow";
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-04-28 02:45:59 +02:00
|
|
|
if (sampler.GetType() == Tegra::Shader::TextureType::Texture1D) {
|
|
|
|
// 1D textures can be aliased to texture buffers, hide the declarations behind a
|
|
|
|
// preprocessor flag and use one or the other from the GPU state. This has to be
|
|
|
|
// done because shaders don't have enough information to determine the texture type.
|
|
|
|
EmitIfdefIsBuffer(sampler);
|
2019-06-24 06:56:38 +02:00
|
|
|
code.AddLine("{} samplerBuffer {};", description, name);
|
2019-04-28 02:45:59 +02:00
|
|
|
code.AddLine("#else");
|
2019-06-24 06:56:38 +02:00
|
|
|
code.AddLine("{} {} {};", description, sampler_type, name);
|
2019-04-28 02:45:59 +02:00
|
|
|
code.AddLine("#endif");
|
|
|
|
} else {
|
|
|
|
// The other texture types (2D, 3D and cubes) don't have this issue.
|
2019-06-24 06:56:38 +02:00
|
|
|
code.AddLine("{} {} {};", description, sampler_type, name);
|
2019-04-28 02:45:59 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
2019-05-14 18:18:07 +02:00
|
|
|
if (!samplers.empty()) {
|
2018-12-21 01:45:49 +01:00
|
|
|
code.AddNewLine();
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-05-01 00:36:18 +02:00
|
|
|
void DeclarePhysicalAttributeReader() {
|
|
|
|
if (!ir.HasPhysicalAttributes()) {
|
|
|
|
return;
|
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
code.AddLine("float ReadPhysicalAttribute(uint physical_address) {{");
|
2019-05-01 00:36:18 +02:00
|
|
|
++code.scope;
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("switch (physical_address) {{");
|
2019-05-01 00:36:18 +02:00
|
|
|
|
|
|
|
// Just declare generic attributes for now.
|
2019-05-02 21:06:56 +02:00
|
|
|
const auto num_attributes{static_cast<u32>(GetNumPhysicalInputAttributes())};
|
2019-05-01 00:36:18 +02:00
|
|
|
for (u32 index = 0; index < num_attributes; ++index) {
|
2019-05-02 21:06:56 +02:00
|
|
|
const auto attribute{ToGenericAttribute(index)};
|
2019-05-01 00:36:18 +02:00
|
|
|
for (u32 element = 0; element < 4; ++element) {
|
2019-08-24 22:29:19 +02:00
|
|
|
constexpr u32 generic_base = 0x80;
|
|
|
|
constexpr u32 generic_stride = 16;
|
|
|
|
constexpr u32 element_stride = 4;
|
2019-05-02 21:06:56 +02:00
|
|
|
const u32 address{generic_base + index * generic_stride + element * element_stride};
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
const bool declared = stage != ProgramType::Fragment ||
|
|
|
|
header.ps.GetAttributeUse(index) != AttributeUse::Unused;
|
|
|
|
const std::string value =
|
|
|
|
declared ? ReadAttribute(attribute, element).AsFloat() : "0.0f";
|
|
|
|
code.AddLine("case 0x{:X}U: return {};", address, value);
|
2019-05-01 00:36:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
code.AddLine("default: return 0;");
|
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("}}");
|
2019-05-01 00:36:18 +02:00
|
|
|
--code.scope;
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("}}");
|
2019-05-01 00:36:18 +02:00
|
|
|
code.AddNewLine();
|
|
|
|
}
|
|
|
|
|
2019-04-27 07:07:18 +02:00
|
|
|
void DeclareImages() {
|
|
|
|
const auto& images{ir.GetImages()};
|
2019-09-06 04:26:05 +02:00
|
|
|
for (const auto& [offset, image] : images) {
|
2019-07-18 02:03:53 +02:00
|
|
|
const char* image_type = [&] {
|
2019-04-27 07:07:18 +02:00
|
|
|
switch (image.GetType()) {
|
|
|
|
case Tegra::Shader::ImageType::Texture1D:
|
|
|
|
return "image1D";
|
|
|
|
case Tegra::Shader::ImageType::TextureBuffer:
|
2019-07-18 06:16:44 +02:00
|
|
|
return "imageBuffer";
|
2019-04-27 07:07:18 +02:00
|
|
|
case Tegra::Shader::ImageType::Texture1DArray:
|
|
|
|
return "image1DArray";
|
|
|
|
case Tegra::Shader::ImageType::Texture2D:
|
|
|
|
return "image2D";
|
|
|
|
case Tegra::Shader::ImageType::Texture2DArray:
|
|
|
|
return "image2DArray";
|
|
|
|
case Tegra::Shader::ImageType::Texture3D:
|
|
|
|
return "image3D";
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return "image1D";
|
|
|
|
}
|
|
|
|
}();
|
2019-07-18 02:03:53 +02:00
|
|
|
|
|
|
|
const auto [type_prefix, format] = [&]() -> std::pair<const char*, const char*> {
|
|
|
|
if (!image.IsSizeKnown()) {
|
|
|
|
return {"", ""};
|
|
|
|
}
|
|
|
|
switch (image.GetSize()) {
|
|
|
|
case Tegra::Shader::ImageAtomicSize::U32:
|
|
|
|
return {"u", "r32ui, "};
|
|
|
|
case Tegra::Shader::ImageAtomicSize::S32:
|
|
|
|
return {"i", "r32i, "};
|
|
|
|
default:
|
|
|
|
UNIMPLEMENTED_MSG("Unimplemented atomic size={}",
|
|
|
|
static_cast<u32>(image.GetSize()));
|
|
|
|
return {"", ""};
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
2019-09-06 04:26:05 +02:00
|
|
|
std::string qualifier = "coherent volatile";
|
|
|
|
if (image.IsRead() && !image.IsWritten()) {
|
|
|
|
qualifier += " readonly";
|
|
|
|
} else if (image.IsWritten() && !image.IsRead()) {
|
|
|
|
qualifier += " writeonly";
|
|
|
|
}
|
|
|
|
|
|
|
|
code.AddLine("layout (binding = IMAGE_BINDING_{}) {} uniform "
|
2019-06-24 06:56:38 +02:00
|
|
|
"{} {};",
|
2019-09-06 04:26:05 +02:00
|
|
|
image.GetIndex(), qualifier, image_type, GetImage(image));
|
2019-04-27 07:07:18 +02:00
|
|
|
}
|
2019-06-24 06:56:38 +02:00
|
|
|
if (!images.empty()) {
|
2019-04-27 07:07:18 +02:00
|
|
|
code.AddNewLine();
|
2019-06-24 06:56:38 +02:00
|
|
|
}
|
2019-04-27 07:07:18 +02:00
|
|
|
}
|
|
|
|
|
2019-01-30 06:09:40 +01:00
|
|
|
void VisitBlock(const NodeBlock& bb) {
|
2019-06-05 03:44:06 +02:00
|
|
|
for (const auto& node : bb) {
|
2019-08-24 22:29:19 +02:00
|
|
|
Visit(node).CheckVoid();
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Visit(const Node& node) {
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto operation = std::get_if<OperationNode>(&*node)) {
|
2018-12-21 01:45:49 +01:00
|
|
|
const auto operation_index = static_cast<std::size_t>(operation->GetCode());
|
2019-04-03 21:01:53 +02:00
|
|
|
if (operation_index >= operation_decompilers.size()) {
|
|
|
|
UNREACHABLE_MSG("Out of bounds operation: {}", operation_index);
|
|
|
|
return {};
|
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
const auto decompiler = operation_decompilers[operation_index];
|
|
|
|
if (decompiler == nullptr) {
|
2019-04-03 21:01:53 +02:00
|
|
|
UNREACHABLE_MSG("Undefined operation: {}", operation_index);
|
|
|
|
return {};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
return (this->*decompiler)(*operation);
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto gpr = std::get_if<GprNode>(&*node)) {
|
2018-12-21 01:45:49 +01:00
|
|
|
const u32 index = gpr->GetIndex();
|
2018-12-26 05:49:32 +01:00
|
|
|
if (index == Register::ZeroIndex) {
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"0U", Type::Uint};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
return {GetRegister(index), Type::Float};
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto immediate = std::get_if<ImmediateNode>(&*node)) {
|
2018-12-21 01:45:49 +01:00
|
|
|
const u32 value = immediate->GetValue();
|
|
|
|
if (value < 10) {
|
|
|
|
// For eyecandy avoid using hex numbers on single digits
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("{}U", immediate->GetValue()), Type::Uint};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("0x{:X}U", immediate->GetValue()), Type::Uint};
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto predicate = std::get_if<PredicateNode>(&*node)) {
|
2018-12-21 01:45:49 +01:00
|
|
|
const auto value = [&]() -> std::string {
|
|
|
|
switch (const auto index = predicate->GetIndex(); index) {
|
|
|
|
case Tegra::Shader::Pred::UnusedIndex:
|
|
|
|
return "true";
|
|
|
|
case Tegra::Shader::Pred::NeverExecute:
|
|
|
|
return "false";
|
|
|
|
default:
|
|
|
|
return GetPredicate(index);
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
if (predicate->IsNegated()) {
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("!({})", value), Type::Bool};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
return {value, Type::Bool};
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto abuf = std::get_if<AbufNode>(&*node)) {
|
2019-07-15 03:25:13 +02:00
|
|
|
UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ProgramType::Geometry,
|
2019-05-01 00:36:18 +02:00
|
|
|
"Physical attributes in geometry shaders are not implemented");
|
|
|
|
if (abuf->IsPhysicalBuffer()) {
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("ReadPhysicalAttribute({})",
|
|
|
|
Visit(abuf->GetPhysicalAddress()).AsUint()),
|
|
|
|
Type::Float};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
2019-05-01 00:36:18 +02:00
|
|
|
return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer());
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto cbuf = std::get_if<CbufNode>(&*node)) {
|
2018-12-21 01:45:49 +01:00
|
|
|
const Node offset = cbuf->GetOffset();
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
|
2018-12-21 01:45:49 +01:00
|
|
|
// Direct access
|
|
|
|
const u32 offset_imm = immediate->GetValue();
|
2019-01-28 22:11:23 +01:00
|
|
|
ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access");
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()),
|
|
|
|
offset_imm / (4 * 4), (offset_imm / 4) % 4),
|
|
|
|
Type::Uint};
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
if (std::holds_alternative<OperationNode>(*offset)) {
|
2018-12-21 01:45:49 +01:00
|
|
|
// Indirect access
|
2019-04-05 01:35:01 +02:00
|
|
|
const std::string final_offset = code.GenerateTemporary();
|
2019-08-24 22:29:19 +02:00
|
|
|
code.AddLine("uint {} = {} >> 2;", final_offset, Visit(offset).AsUint());
|
2019-05-24 01:27:54 +02:00
|
|
|
|
|
|
|
if (!device.HasComponentIndexingBug()) {
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("{}[{} >> 2][{} & 3]", GetConstBuffer(cbuf->GetIndex()),
|
|
|
|
final_offset, final_offset),
|
|
|
|
Type::Uint};
|
2019-05-24 01:27:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// AMD's proprietary GLSL compiler emits ill code for variable component access.
|
|
|
|
// To bypass this driver bug generate 4 ifs, one per each component.
|
|
|
|
const std::string pack = code.GenerateTemporary();
|
2019-09-04 20:03:51 +02:00
|
|
|
code.AddLine("uvec4 {} = {}[{} >> 2];", pack, GetConstBuffer(cbuf->GetIndex()),
|
2019-05-24 01:27:54 +02:00
|
|
|
final_offset);
|
|
|
|
|
|
|
|
const std::string result = code.GenerateTemporary();
|
2019-08-30 23:32:17 +02:00
|
|
|
code.AddLine("uint {};", result);
|
2019-05-24 01:27:54 +02:00
|
|
|
for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
|
|
|
|
code.AddLine("if (({} & 3) == {}) {} = {}{};", final_offset, swizzle, result,
|
|
|
|
pack, GetSwizzle(swizzle));
|
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
return {result, Type::Uint};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
UNREACHABLE_MSG("Unmanaged offset node type");
|
|
|
|
}
|
|
|
|
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto gmem = std::get_if<GmemNode>(&*node)) {
|
2019-08-24 22:29:19 +02:00
|
|
|
const std::string real = Visit(gmem->GetRealAddress()).AsUint();
|
|
|
|
const std::string base = Visit(gmem->GetBaseAddress()).AsUint();
|
|
|
|
const std::string final_offset = fmt::format("({} - {}) >> 2", real, base);
|
|
|
|
return {fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset),
|
|
|
|
Type::Uint};
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-29 06:44:54 +01:00
|
|
|
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto lmem = std::get_if<LmemNode>(&*node)) {
|
2019-07-15 22:28:27 +02:00
|
|
|
if (stage == ProgramType::Compute) {
|
|
|
|
LOG_WARNING(Render_OpenGL, "Local memory is stubbed on compute shaders");
|
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
return {
|
|
|
|
fmt::format("{}[{} >> 2]", GetLocalMemory(), Visit(lmem->GetAddress()).AsUint()),
|
|
|
|
Type::Uint};
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-08-29 22:18:04 +02:00
|
|
|
if (const auto smem = std::get_if<SmemNode>(&*node)) {
|
|
|
|
return {
|
|
|
|
fmt::format("{}[{} >> 2]", GetSharedMemory(), Visit(smem->GetAddress()).AsUint()),
|
|
|
|
Type::Uint};
|
|
|
|
}
|
|
|
|
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) {
|
2019-08-24 22:29:19 +02:00
|
|
|
return {GetInternalFlag(internal_flag->GetFlag()), Type::Bool};
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
|
2018-12-21 01:45:49 +01:00
|
|
|
// It's invalid to call conditional on nested nodes, use an operation instead
|
2019-08-24 22:29:19 +02:00
|
|
|
code.AddLine("if ({}) {{", Visit(conditional->GetCondition()).AsBool());
|
2018-12-21 01:45:49 +01:00
|
|
|
++code.scope;
|
|
|
|
|
2019-01-30 06:09:40 +01:00
|
|
|
VisitBlock(conditional->GetCode());
|
2018-12-21 01:45:49 +01:00
|
|
|
|
|
|
|
--code.scope;
|
2019-05-14 23:59:25 +02:00
|
|
|
code.AddLine("}}");
|
2018-12-21 01:45:49 +01:00
|
|
|
return {};
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto comment = std::get_if<CommentNode>(&*node)) {
|
2019-08-24 22:29:19 +02:00
|
|
|
code.AddLine("// " + comment->GetText());
|
|
|
|
return {};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
2019-05-14 18:18:07 +02:00
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
UNREACHABLE();
|
2018-12-21 22:47:22 +01:00
|
|
|
return {};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression ReadAttribute(Attribute::Index attribute, u32 element, const Node& buffer = {}) {
|
2019-05-14 18:18:07 +02:00
|
|
|
const auto GeometryPass = [&](std::string_view name) {
|
2019-07-15 03:25:13 +02:00
|
|
|
if (stage == ProgramType::Geometry && buffer) {
|
2019-05-01 00:36:18 +02:00
|
|
|
// TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
|
|
|
|
// set an 0x80000000 index for those and the shader fails to build. Find out why
|
|
|
|
// this happens and what's its intent.
|
2019-08-24 22:29:19 +02:00
|
|
|
return fmt::format("gs_{}[{} % MAX_VERTEX_INPUT]", name, Visit(buffer).AsUint());
|
2019-05-01 00:36:18 +02:00
|
|
|
}
|
2019-05-14 18:18:07 +02:00
|
|
|
return std::string(name);
|
2019-05-01 00:36:18 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
switch (attribute) {
|
|
|
|
case Attribute::Index::Position:
|
2019-06-03 06:01:34 +02:00
|
|
|
switch (stage) {
|
2019-07-15 03:25:13 +02:00
|
|
|
case ProgramType::Geometry:
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("gl_in[{}].gl_Position{}", Visit(buffer).AsUint(),
|
|
|
|
GetSwizzle(element)),
|
|
|
|
Type::Float};
|
2019-07-15 03:25:13 +02:00
|
|
|
case ProgramType::Fragment:
|
2019-08-24 22:29:19 +02:00
|
|
|
return {element == 3 ? "1.0f" : ("gl_FragCoord"s + GetSwizzle(element)),
|
|
|
|
Type::Float};
|
2019-06-03 06:01:34 +02:00
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
2019-05-01 00:36:18 +02:00
|
|
|
}
|
|
|
|
case Attribute::Index::PointCoord:
|
|
|
|
switch (element) {
|
|
|
|
case 0:
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"gl_PointCoord.x", Type::Float};
|
2019-05-01 00:36:18 +02:00
|
|
|
case 1:
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"gl_PointCoord.y", Type::Float};
|
2019-05-01 00:36:18 +02:00
|
|
|
case 2:
|
|
|
|
case 3:
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"0.0f", Type::Float};
|
2019-05-01 00:36:18 +02:00
|
|
|
}
|
|
|
|
UNREACHABLE();
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"0", Type::Int};
|
2019-05-01 00:36:18 +02: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.
|
2019-07-15 03:25:13 +02:00
|
|
|
ASSERT(IsVertexShader(stage));
|
2019-05-01 00:36:18 +02:00
|
|
|
switch (element) {
|
|
|
|
case 2:
|
|
|
|
// Config pack's first value is instance_id.
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"config_pack[0]", Type::Uint};
|
2019-05-01 00:36:18 +02:00
|
|
|
case 3:
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"gl_VertexID", Type::Int};
|
2019-05-01 00:36:18 +02:00
|
|
|
}
|
|
|
|
UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element);
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"0", Type::Int};
|
2019-05-01 00:36:18 +02:00
|
|
|
case Attribute::Index::FrontFacing:
|
|
|
|
// TODO(Subv): Find out what the values are for the other elements.
|
2019-07-15 03:25:13 +02:00
|
|
|
ASSERT(stage == ProgramType::Fragment);
|
2019-05-01 00:36:18 +02:00
|
|
|
switch (element) {
|
|
|
|
case 3:
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"(gl_FrontFacing ? -1 : 0)", Type::Int};
|
2019-05-01 00:36:18 +02:00
|
|
|
}
|
|
|
|
UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element);
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"0", Type::Int};
|
2019-05-01 00:36:18 +02:00
|
|
|
default:
|
|
|
|
if (IsGenericAttribute(attribute)) {
|
2019-08-24 22:29:19 +02:00
|
|
|
return {GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element),
|
|
|
|
Type::Float};
|
2019-05-01 00:36:18 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"0", Type::Int};
|
2019-05-01 00:36:18 +02:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression ApplyPrecise(Operation operation, std::string value, Type type) {
|
2018-12-21 01:45:49 +01:00
|
|
|
if (!IsPrecise(operation)) {
|
2019-08-24 22:29:19 +02:00
|
|
|
return {std::move(value), type};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
2019-08-31 08:03:35 +02:00
|
|
|
// Old Nvidia drivers have a bug with precise and texture sampling. These are more likely to
|
|
|
|
// be found in fragment shaders, so we disable precise there. There are vertex shaders that
|
|
|
|
// also fail to build but nobody seems to care about those.
|
|
|
|
// Note: Only bugged drivers will skip precise.
|
|
|
|
const bool disable_precise = device.HasPreciseBug() && stage == ProgramType::Fragment;
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
std::string temporary = code.GenerateTemporary();
|
2019-08-31 08:03:35 +02:00
|
|
|
code.AddLine("{}{} {} = {};", disable_precise ? "" : "precise ", GetTypeString(type),
|
|
|
|
temporary, value);
|
2019-08-24 22:29:19 +02:00
|
|
|
return {std::move(temporary), type};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression VisitOperand(Operation operation, std::size_t operand_index) {
|
2018-12-21 01:45:49 +01:00
|
|
|
const auto& operand = operation[operand_index];
|
|
|
|
const bool parent_precise = IsPrecise(operation);
|
|
|
|
const bool child_precise = IsPrecise(operand);
|
|
|
|
const bool child_trivial = !std::holds_alternative<OperationNode>(*operand);
|
|
|
|
if (!parent_precise || child_precise || child_trivial) {
|
|
|
|
return Visit(operand);
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression value = Visit(operand);
|
|
|
|
std::string temporary = code.GenerateTemporary();
|
|
|
|
code.AddLine("{} {} = {};", GetTypeString(value.GetType()), temporary, value.GetCode());
|
|
|
|
return {std::move(temporary), value.GetType()};
|
2019-02-12 21:02:59 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression GetOutputAttribute(const AbufNode* abuf) {
|
2019-07-08 01:36:42 +02:00
|
|
|
switch (const auto attribute = abuf->GetIndex()) {
|
|
|
|
case Attribute::Index::Position:
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"gl_Position"s + GetSwizzle(abuf->GetElement()), Type::Float};
|
2019-07-08 01:36:42 +02:00
|
|
|
case Attribute::Index::LayerViewportPointSize:
|
|
|
|
switch (abuf->GetElement()) {
|
|
|
|
case 0:
|
|
|
|
UNIMPLEMENTED();
|
|
|
|
return {};
|
|
|
|
case 1:
|
2019-07-15 03:25:13 +02:00
|
|
|
if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) {
|
2019-07-08 01:36:42 +02:00
|
|
|
return {};
|
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"gl_Layer", Type::Int};
|
2019-07-08 01:36:42 +02:00
|
|
|
case 2:
|
2019-07-15 03:25:13 +02:00
|
|
|
if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) {
|
2019-07-08 01:36:42 +02:00
|
|
|
return {};
|
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"gl_ViewportIndex", Type::Int};
|
2019-07-08 01:36:42 +02:00
|
|
|
case 3:
|
|
|
|
UNIMPLEMENTED_MSG("Requires some state changes for gl_PointSize to work in shader");
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"gl_PointSize", Type::Float};
|
2019-07-08 01:36:42 +02:00
|
|
|
}
|
|
|
|
return {};
|
|
|
|
case Attribute::Index::ClipDistances0123:
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("gl_ClipDistance[{}]", abuf->GetElement()), Type::Float};
|
2019-07-08 01:36:42 +02:00
|
|
|
case Attribute::Index::ClipDistances4567:
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4), Type::Float};
|
2019-07-08 01:36:42 +02:00
|
|
|
default:
|
|
|
|
if (IsGenericAttribute(attribute)) {
|
2019-08-24 22:29:19 +02:00
|
|
|
return {GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()),
|
|
|
|
Type::Float};
|
2019-07-08 01:36:42 +02:00
|
|
|
}
|
|
|
|
UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute));
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression GenerateUnary(Operation operation, std::string_view func, Type result_type,
|
|
|
|
Type type_a) {
|
|
|
|
std::string op_str = fmt::format("{}({})", func, VisitOperand(operation, 0).As(type_a));
|
|
|
|
return ApplyPrecise(operation, std::move(op_str), result_type);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression GenerateBinaryInfix(Operation operation, std::string_view func, Type result_type,
|
|
|
|
Type type_a, Type type_b) {
|
|
|
|
const std::string op_a = VisitOperand(operation, 0).As(type_a);
|
|
|
|
const std::string op_b = VisitOperand(operation, 1).As(type_b);
|
|
|
|
std::string op_str = fmt::format("({} {} {})", op_a, func, op_b);
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
return ApplyPrecise(operation, std::move(op_str), result_type);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression GenerateBinaryCall(Operation operation, std::string_view func, Type result_type,
|
|
|
|
Type type_a, Type type_b) {
|
|
|
|
const std::string op_a = VisitOperand(operation, 0).As(type_a);
|
|
|
|
const std::string op_b = VisitOperand(operation, 1).As(type_b);
|
|
|
|
std::string op_str = fmt::format("{}({}, {})", func, op_a, op_b);
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
return ApplyPrecise(operation, std::move(op_str), result_type);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression GenerateTernary(Operation operation, std::string_view func, Type result_type,
|
|
|
|
Type type_a, Type type_b, Type type_c) {
|
|
|
|
const std::string op_a = VisitOperand(operation, 0).As(type_a);
|
|
|
|
const std::string op_b = VisitOperand(operation, 1).As(type_b);
|
|
|
|
const std::string op_c = VisitOperand(operation, 2).As(type_c);
|
|
|
|
std::string op_str = fmt::format("{}({}, {}, {})", func, op_a, op_b, op_c);
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
return ApplyPrecise(operation, std::move(op_str), result_type);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression GenerateQuaternary(Operation operation, const std::string& func, Type result_type,
|
|
|
|
Type type_a, Type type_b, Type type_c, Type type_d) {
|
|
|
|
const std::string op_a = VisitOperand(operation, 0).As(type_a);
|
|
|
|
const std::string op_b = VisitOperand(operation, 1).As(type_b);
|
|
|
|
const std::string op_c = VisitOperand(operation, 2).As(type_c);
|
|
|
|
const std::string op_d = VisitOperand(operation, 3).As(type_d);
|
|
|
|
std::string op_str = fmt::format("{}({}, {}, {}, {})", func, op_a, op_b, op_c, op_d);
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
return ApplyPrecise(operation, std::move(op_str), result_type);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-03-29 22:37:37 +01:00
|
|
|
std::string GenerateTexture(Operation operation, const std::string& function_suffix,
|
|
|
|
const std::vector<TextureIR>& extras) {
|
2019-08-24 22:29:19 +02:00
|
|
|
constexpr std::array coord_constructors = {"float", "vec2", "vec3", "vec4"};
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-01-15 21:52:49 +01:00
|
|
|
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
|
|
|
ASSERT(meta);
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-02-12 21:02:59 +01:00
|
|
|
const std::size_t count = operation.GetOperandsCount();
|
2019-01-29 08:31:40 +01:00
|
|
|
const bool has_array = meta->sampler.IsArray();
|
|
|
|
const bool has_shadow = meta->sampler.IsShadow();
|
|
|
|
|
2019-03-29 22:37:37 +01:00
|
|
|
std::string expr = "texture" + function_suffix;
|
|
|
|
if (!meta->aoffi.empty()) {
|
|
|
|
expr += "Offset";
|
|
|
|
}
|
|
|
|
expr += '(' + GetSampler(meta->sampler) + ", ";
|
2019-01-29 08:31:40 +01:00
|
|
|
expr += coord_constructors.at(count + (has_array ? 1 : 0) + (has_shadow ? 1 : 0) - 1);
|
2018-12-21 01:45:49 +01:00
|
|
|
expr += '(';
|
2019-02-12 21:02:59 +01:00
|
|
|
for (std::size_t i = 0; i < count; ++i) {
|
2019-08-24 22:29:19 +02:00
|
|
|
expr += Visit(operation[i]).AsFloat();
|
2019-01-08 00:31:47 +01:00
|
|
|
|
2019-02-12 21:02:59 +01:00
|
|
|
const std::size_t next = i + 1;
|
2019-02-22 07:30:12 +01:00
|
|
|
if (next < count)
|
2019-01-29 08:31:40 +01:00
|
|
|
expr += ", ";
|
|
|
|
}
|
|
|
|
if (has_array) {
|
2019-08-24 22:29:19 +02:00
|
|
|
expr += ", float(" + Visit(meta->array).AsInt() + ')';
|
2019-01-29 08:31:40 +01:00
|
|
|
}
|
|
|
|
if (has_shadow) {
|
2019-08-24 22:29:19 +02:00
|
|
|
expr += ", " + Visit(meta->depth_compare).AsFloat();
|
2019-01-29 08:31:40 +01:00
|
|
|
}
|
|
|
|
expr += ')';
|
|
|
|
|
2019-03-29 22:37:37 +01:00
|
|
|
for (const auto& variant : extras) {
|
|
|
|
if (const auto argument = std::get_if<TextureArgument>(&variant)) {
|
|
|
|
expr += GenerateTextureArgument(*argument);
|
|
|
|
} else if (std::get_if<TextureAoffi>(&variant)) {
|
|
|
|
expr += GenerateTextureAoffi(meta->aoffi);
|
|
|
|
} else {
|
|
|
|
UNREACHABLE();
|
2019-02-22 07:30:12 +01:00
|
|
|
}
|
2019-03-29 22:37:37 +01:00
|
|
|
}
|
2019-02-22 07:30:12 +01:00
|
|
|
|
2019-03-29 22:37:37 +01:00
|
|
|
return expr + ')';
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GenerateTextureArgument(TextureArgument argument) {
|
|
|
|
const auto [type, operand] = argument;
|
|
|
|
if (operand == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string expr = ", ";
|
|
|
|
switch (type) {
|
|
|
|
case Type::Int:
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto immediate = std::get_if<ImmediateNode>(&*operand)) {
|
2019-03-29 22:37:37 +01:00
|
|
|
// Inline the string as an immediate integer in GLSL (some extra arguments are
|
|
|
|
// required to be constant)
|
|
|
|
expr += std::to_string(static_cast<s32>(immediate->GetValue()));
|
|
|
|
} else {
|
2019-08-24 22:29:19 +02:00
|
|
|
expr += Visit(operand).AsInt();
|
2019-03-29 22:37:37 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Type::Float:
|
2019-08-24 22:29:19 +02:00
|
|
|
expr += Visit(operand).AsFloat();
|
2019-03-29 22:37:37 +01:00
|
|
|
break;
|
|
|
|
default: {
|
|
|
|
const auto type_int = static_cast<u32>(type);
|
|
|
|
UNIMPLEMENTED_MSG("Unimplemented extra type={}", type_int);
|
|
|
|
expr += '0';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GenerateTextureAoffi(const std::vector<Node>& aoffi) {
|
|
|
|
if (aoffi.empty()) {
|
|
|
|
return {};
|
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
constexpr std::array coord_constructors = {"int", "ivec2", "ivec3"};
|
2019-03-29 22:37:37 +01:00
|
|
|
std::string expr = ", ";
|
|
|
|
expr += coord_constructors.at(aoffi.size() - 1);
|
|
|
|
expr += '(';
|
|
|
|
|
|
|
|
for (std::size_t index = 0; index < aoffi.size(); ++index) {
|
|
|
|
const auto operand{aoffi.at(index)};
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto immediate = std::get_if<ImmediateNode>(&*operand)) {
|
2019-03-29 22:37:37 +01:00
|
|
|
// Inline the string as an immediate integer in GLSL (AOFFI arguments are required
|
|
|
|
// to be constant by the standard).
|
|
|
|
expr += std::to_string(static_cast<s32>(immediate->GetValue()));
|
2019-04-10 23:03:52 +02:00
|
|
|
} else if (device.HasVariableAoffi()) {
|
|
|
|
// Avoid using variable AOFFI on unsupported devices.
|
2019-08-24 22:29:19 +02:00
|
|
|
expr += Visit(operand).AsInt();
|
2019-04-10 23:03:52 +02:00
|
|
|
} else {
|
|
|
|
// Insert 0 on devices not supporting variable AOFFI.
|
|
|
|
expr += '0';
|
2019-02-22 07:30:12 +01:00
|
|
|
}
|
2019-03-29 22:37:37 +01:00
|
|
|
if (index + 1 < aoffi.size()) {
|
|
|
|
expr += ", ";
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
}
|
2019-03-29 22:37:37 +01:00
|
|
|
expr += ')';
|
2019-01-29 08:31:40 +01:00
|
|
|
|
2019-03-29 22:37:37 +01:00
|
|
|
return expr;
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-07-18 02:03:53 +02:00
|
|
|
std::string BuildIntegerCoordinates(Operation operation) {
|
|
|
|
constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("};
|
|
|
|
const std::size_t coords_count{operation.GetOperandsCount()};
|
|
|
|
std::string expr = constructors.at(coords_count - 1);
|
|
|
|
for (std::size_t i = 0; i < coords_count; ++i) {
|
|
|
|
expr += VisitOperand(operation, i).AsInt();
|
|
|
|
if (i + 1 < coords_count) {
|
|
|
|
expr += ", ";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
expr += ')';
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string BuildImageValues(Operation operation) {
|
|
|
|
const auto meta{std::get<MetaImage>(operation.GetMeta())};
|
|
|
|
const auto [constructors, type] = [&]() -> std::pair<std::array<const char*, 4>, Type> {
|
|
|
|
constexpr std::array float_constructors{"float", "vec2", "vec3", "vec4"};
|
|
|
|
if (!meta.image.IsSizeKnown()) {
|
|
|
|
return {float_constructors, Type::Float};
|
|
|
|
}
|
|
|
|
switch (meta.image.GetSize()) {
|
|
|
|
case Tegra::Shader::ImageAtomicSize::U32:
|
|
|
|
return {{"uint", "uvec2", "uvec3", "uvec4"}, Type::Uint};
|
|
|
|
case Tegra::Shader::ImageAtomicSize::S32:
|
|
|
|
return {{"int", "ivec2", "ivec3", "ivec4"}, Type::Uint};
|
|
|
|
default:
|
|
|
|
UNIMPLEMENTED_MSG("Unimplemented image size={}",
|
|
|
|
static_cast<u32>(meta.image.GetSize()));
|
|
|
|
return {float_constructors, Type::Float};
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
const std::size_t values_count{meta.values.size()};
|
|
|
|
std::string expr = fmt::format("{}(", constructors.at(values_count - 1));
|
|
|
|
for (std::size_t i = 0; i < values_count; ++i) {
|
|
|
|
expr += Visit(meta.values.at(i)).As(type);
|
|
|
|
if (i + 1 < values_count) {
|
|
|
|
expr += ", ";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
expr += ')';
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
Expression AtomicImage(Operation operation, const char* opname) {
|
|
|
|
constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("};
|
|
|
|
const auto meta{std::get<MetaImage>(operation.GetMeta())};
|
|
|
|
ASSERT(meta.values.size() == 1);
|
|
|
|
ASSERT(meta.image.IsSizeKnown());
|
|
|
|
|
|
|
|
const auto type = [&]() {
|
|
|
|
switch (const auto size = meta.image.GetSize()) {
|
|
|
|
case Tegra::Shader::ImageAtomicSize::U32:
|
|
|
|
return Type::Uint;
|
|
|
|
case Tegra::Shader::ImageAtomicSize::S32:
|
|
|
|
return Type::Int;
|
|
|
|
default:
|
|
|
|
UNIMPLEMENTED_MSG("Unimplemented image size={}", static_cast<u32>(size));
|
|
|
|
return Type::Uint;
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
return {fmt::format("{}({}, {}, {})", opname, GetImage(meta.image),
|
|
|
|
BuildIntegerCoordinates(operation), Visit(meta.values[0]).As(type)),
|
|
|
|
type};
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Assign(Operation operation) {
|
2019-06-05 03:44:06 +02:00
|
|
|
const Node& dest = operation[0];
|
|
|
|
const Node& src = operation[1];
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression target;
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto gpr = std::get_if<GprNode>(&*dest)) {
|
2018-12-26 05:49:32 +01:00
|
|
|
if (gpr->GetIndex() == Register::ZeroIndex) {
|
|
|
|
// Writing to Register::ZeroIndex is a no op
|
2018-12-21 01:45:49 +01:00
|
|
|
return {};
|
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
target = {GetRegister(gpr->GetIndex()), Type::Float};
|
2019-06-05 03:44:06 +02:00
|
|
|
} else if (const auto abuf = std::get_if<AbufNode>(&*dest)) {
|
2019-05-01 00:46:49 +02:00
|
|
|
UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer());
|
2019-08-24 22:29:19 +02:00
|
|
|
target = GetOutputAttribute(abuf);
|
2019-06-05 03:44:06 +02:00
|
|
|
} else if (const auto lmem = std::get_if<LmemNode>(&*dest)) {
|
2019-07-15 22:28:27 +02:00
|
|
|
if (stage == ProgramType::Compute) {
|
|
|
|
LOG_WARNING(Render_OpenGL, "Local memory is stubbed on compute shaders");
|
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
target = {
|
|
|
|
fmt::format("{}[{} >> 2]", GetLocalMemory(), Visit(lmem->GetAddress()).AsUint()),
|
|
|
|
Type::Uint};
|
2019-08-29 22:18:04 +02:00
|
|
|
} else if (const auto smem = std::get_if<SmemNode>(&*dest)) {
|
|
|
|
ASSERT(stage == ProgramType::Compute);
|
|
|
|
target = {
|
|
|
|
fmt::format("{}[{} >> 2]", GetSharedMemory(), Visit(smem->GetAddress()).AsUint()),
|
|
|
|
Type::Uint};
|
2019-06-05 03:44:06 +02:00
|
|
|
} else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
|
2019-08-24 22:29:19 +02:00
|
|
|
const std::string real = Visit(gmem->GetRealAddress()).AsUint();
|
|
|
|
const std::string base = Visit(gmem->GetBaseAddress()).AsUint();
|
|
|
|
const std::string final_offset = fmt::format("({} - {}) >> 2", real, base);
|
|
|
|
target = {fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset),
|
|
|
|
Type::Uint};
|
2018-12-21 01:45:49 +01:00
|
|
|
} else {
|
|
|
|
UNREACHABLE_MSG("Assign called without a proper target");
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
code.AddLine("{} = {};", target.GetCode(), Visit(src).As(target.GetType()));
|
2018-12-21 01:45:49 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Add(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, "+", type, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Mul(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, "*", type, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Div(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, "/", type, type, type);
|
|
|
|
}
|
|
|
|
|
2018-12-23 06:26:35 +01:00
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Fma(Operation operation) {
|
2018-12-23 06:26:35 +01:00
|
|
|
return GenerateTernary(operation, "fma", type, type, type, type);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Negate(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "-", type, type);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Absolute(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "abs", type, type);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression FClamp(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateTernary(operation, "clamp", Type::Float, Type::Float, Type::Float,
|
|
|
|
Type::Float);
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression FCastHalf0(Operation operation) {
|
|
|
|
return {fmt::format("({})[0]", VisitOperand(operation, 0).AsHalfFloat()), Type::Float};
|
2019-07-20 23:38:25 +02:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression FCastHalf1(Operation operation) {
|
|
|
|
return {fmt::format("({})[1]", VisitOperand(operation, 0).AsHalfFloat()), Type::Float};
|
2019-07-20 23:38:25 +02:00
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Min(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryCall(operation, "min", type, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Max(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryCall(operation, "max", type, type, type);
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Select(Operation operation) {
|
|
|
|
const std::string condition = Visit(operation[0]).AsBool();
|
|
|
|
const std::string true_case = Visit(operation[1]).AsUint();
|
|
|
|
const std::string false_case = Visit(operation[2]).AsUint();
|
|
|
|
std::string op_str = fmt::format("({} ? {} : {})", condition, true_case, false_case);
|
2019-05-14 18:18:07 +02:00
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
return ApplyPrecise(operation, std::move(op_str), Type::Uint);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression FCos(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "cos", Type::Float, Type::Float);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression FSin(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "sin", Type::Float, Type::Float);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression FExp2(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "exp2", Type::Float, Type::Float);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression FLog2(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "log2", Type::Float, Type::Float);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression FInverseSqrt(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "inversesqrt", Type::Float, Type::Float);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression FSqrt(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "sqrt", Type::Float, Type::Float);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression FRoundEven(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "roundEven", Type::Float, Type::Float);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression FFloor(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "floor", Type::Float, Type::Float);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression FCeil(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "ceil", Type::Float, Type::Float);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression FTrunc(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "trunc", Type::Float, Type::Float);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression FCastInteger(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "float", Type::Float, type);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression ICastFloat(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "int", Type::Int, Type::Float);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression ICastUnsigned(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "int", Type::Int, Type::Uint);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LogicalShiftLeft(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, "<<", type, type, Type::Uint);
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression ILogicalShiftRight(Operation operation) {
|
|
|
|
const std::string op_a = VisitOperand(operation, 0).AsUint();
|
|
|
|
const std::string op_b = VisitOperand(operation, 1).AsUint();
|
|
|
|
std::string op_str = fmt::format("int({} >> {})", op_a, op_b);
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
return ApplyPrecise(operation, std::move(op_str), Type::Int);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression IArithmeticShiftRight(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, ">>", Type::Int, Type::Int, Type::Uint);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression BitwiseAnd(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, "&", type, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression BitwiseOr(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, "|", type, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression BitwiseXor(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, "^", type, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression BitwiseNot(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "~", type, type);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression UCastFloat(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "uint", Type::Uint, Type::Float);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression UCastSigned(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "uint", Type::Uint, Type::Int);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression UShiftRight(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, ">>", Type::Uint, Type::Uint, Type::Uint);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression BitfieldInsert(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateQuaternary(operation, "bitfieldInsert", type, type, type, Type::Int,
|
|
|
|
Type::Int);
|
|
|
|
}
|
|
|
|
|
2018-12-26 06:58:47 +01:00
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression BitfieldExtract(Operation operation) {
|
2018-12-26 06:58:47 +01:00
|
|
|
return GenerateTernary(operation, "bitfieldExtract", type, type, Type::Int, Type::Int);
|
|
|
|
}
|
|
|
|
|
2018-12-23 05:33:47 +01:00
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression BitCount(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "bitCount", type, type);
|
2018-12-23 05:33:47 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression HNegate(Operation operation) {
|
2019-05-14 18:18:07 +02:00
|
|
|
const auto GetNegate = [&](std::size_t index) {
|
2019-08-24 22:29:19 +02:00
|
|
|
return VisitOperand(operation, index).AsBool() + " ? -1 : 1";
|
2018-12-21 01:45:49 +01:00
|
|
|
};
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("({} * vec2({}, {}))", VisitOperand(operation, 0).AsHalfFloat(),
|
|
|
|
GetNegate(1), GetNegate(2)),
|
|
|
|
Type::HalfFloat};
|
|
|
|
}
|
|
|
|
|
|
|
|
Expression HClamp(Operation operation) {
|
|
|
|
const std::string value = VisitOperand(operation, 0).AsHalfFloat();
|
|
|
|
const std::string min = VisitOperand(operation, 1).AsFloat();
|
|
|
|
const std::string max = VisitOperand(operation, 2).AsFloat();
|
|
|
|
std::string clamped = fmt::format("clamp({}, vec2({}), vec2({}))", value, min, max);
|
|
|
|
|
|
|
|
return ApplyPrecise(operation, std::move(clamped), Type::HalfFloat);
|
|
|
|
}
|
|
|
|
|
|
|
|
Expression HCastFloat(Operation operation) {
|
|
|
|
return {fmt::format("vec2({})", VisitOperand(operation, 0).AsFloat()), Type::HalfFloat};
|
|
|
|
}
|
|
|
|
|
|
|
|
Expression HUnpack(Operation operation) {
|
|
|
|
Expression operand = VisitOperand(operation, 0);
|
|
|
|
switch (std::get<Tegra::Shader::HalfType>(operation.GetMeta())) {
|
|
|
|
case Tegra::Shader::HalfType::H0_H1:
|
|
|
|
return operand;
|
|
|
|
case Tegra::Shader::HalfType::F32:
|
|
|
|
return {fmt::format("vec2({})", operand.AsFloat()), Type::HalfFloat};
|
|
|
|
case Tegra::Shader::HalfType::H0_H0:
|
|
|
|
return {fmt::format("vec2({}[0])", operand.AsHalfFloat()), Type::HalfFloat};
|
|
|
|
case Tegra::Shader::HalfType::H1_H1:
|
|
|
|
return {fmt::format("vec2({}[1])", operand.AsHalfFloat()), Type::HalfFloat};
|
|
|
|
}
|
2019-04-16 00:48:11 +02:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression HMergeF32(Operation operation) {
|
|
|
|
return {fmt::format("float({}[0])", VisitOperand(operation, 0).AsHalfFloat()), Type::Float};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression HMergeH0(Operation operation) {
|
|
|
|
std::string dest = VisitOperand(operation, 0).AsUint();
|
|
|
|
std::string src = VisitOperand(operation, 1).AsUint();
|
|
|
|
return {fmt::format("(({} & 0x0000FFFFU) | ({} & 0xFFFF0000U))", src, dest), Type::Uint};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression HMergeH1(Operation operation) {
|
|
|
|
std::string dest = VisitOperand(operation, 0).AsUint();
|
|
|
|
std::string src = VisitOperand(operation, 1).AsUint();
|
|
|
|
return {fmt::format("(({} & 0x0000FFFFU) | ({} & 0xFFFF0000U))", dest, src), Type::Uint};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression HPack2(Operation operation) {
|
|
|
|
return {fmt::format("vec2({}, {})", VisitOperand(operation, 0).AsFloat(),
|
|
|
|
VisitOperand(operation, 1).AsFloat()),
|
|
|
|
Type::HalfFloat};
|
2018-12-27 05:50:22 +01:00
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LogicalLessThan(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, "<", Type::Bool, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LogicalEqual(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, "==", Type::Bool, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LogicalLessEqual(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, "<=", Type::Bool, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LogicalGreaterThan(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, ">", Type::Bool, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LogicalNotEqual(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, "!=", Type::Bool, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LogicalGreaterEqual(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, ">=", Type::Bool, type, type);
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LogicalFIsNan(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "isnan", Type::Bool, Type::Float);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LogicalAssign(Operation operation) {
|
2019-06-05 03:44:06 +02:00
|
|
|
const Node& dest = operation[0];
|
|
|
|
const Node& src = operation[1];
|
2018-12-21 01:45:49 +01:00
|
|
|
|
|
|
|
std::string target;
|
|
|
|
|
2019-06-05 03:44:06 +02:00
|
|
|
if (const auto pred = std::get_if<PredicateNode>(&*dest)) {
|
2018-12-21 01:45:49 +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 = GetPredicate(index);
|
2019-06-05 03:44:06 +02:00
|
|
|
} else if (const auto flag = std::get_if<InternalFlagNode>(&*dest)) {
|
2018-12-21 01:45:49 +01:00
|
|
|
target = GetInternalFlag(flag->GetFlag());
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
code.AddLine("{} = {};", target, Visit(src).AsBool());
|
2018-12-21 01:45:49 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LogicalAnd(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, "&&", Type::Bool, Type::Bool, Type::Bool);
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LogicalOr(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, "||", Type::Bool, Type::Bool, Type::Bool);
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LogicalXor(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GenerateBinaryInfix(operation, "^^", Type::Bool, Type::Bool, Type::Bool);
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LogicalNegate(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "!", Type::Bool, Type::Bool);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LogicalPick2(Operation operation) {
|
|
|
|
return {fmt::format("{}[{}]", VisitOperand(operation, 0).AsBool2(),
|
|
|
|
VisitOperand(operation, 1).AsUint()),
|
|
|
|
Type::Bool};
|
2018-12-24 04:51:52 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LogicalAnd2(Operation operation) {
|
2018-12-24 00:59:49 +01:00
|
|
|
return GenerateUnary(operation, "all", Type::Bool, Type::Bool2);
|
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-04-09 22:33:48 +02:00
|
|
|
template <bool with_nan>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression GenerateHalfComparison(Operation operation, std::string_view compare_op) {
|
|
|
|
Expression comparison = GenerateBinaryCall(operation, compare_op, Type::Bool2,
|
|
|
|
Type::HalfFloat, Type::HalfFloat);
|
2019-04-09 22:33:48 +02:00
|
|
|
if constexpr (!with_nan) {
|
|
|
|
return comparison;
|
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("HalfFloatNanComparison({}, {}, {})", comparison.AsBool2(),
|
|
|
|
VisitOperand(operation, 0).AsHalfFloat(),
|
|
|
|
VisitOperand(operation, 1).AsHalfFloat()),
|
|
|
|
Type::Bool2};
|
2019-04-09 22:33:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template <bool with_nan>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Logical2HLessThan(Operation operation) {
|
2019-04-09 22:33:48 +02:00
|
|
|
return GenerateHalfComparison<with_nan>(operation, "lessThan");
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-04-09 22:33:48 +02:00
|
|
|
template <bool with_nan>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Logical2HEqual(Operation operation) {
|
2019-04-09 22:33:48 +02:00
|
|
|
return GenerateHalfComparison<with_nan>(operation, "equal");
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-04-09 22:33:48 +02:00
|
|
|
template <bool with_nan>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Logical2HLessEqual(Operation operation) {
|
2019-04-09 22:33:48 +02:00
|
|
|
return GenerateHalfComparison<with_nan>(operation, "lessThanEqual");
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-04-09 22:33:48 +02:00
|
|
|
template <bool with_nan>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Logical2HGreaterThan(Operation operation) {
|
2019-04-09 22:33:48 +02:00
|
|
|
return GenerateHalfComparison<with_nan>(operation, "greaterThan");
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-04-09 22:33:48 +02:00
|
|
|
template <bool with_nan>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Logical2HNotEqual(Operation operation) {
|
2019-04-09 22:33:48 +02:00
|
|
|
return GenerateHalfComparison<with_nan>(operation, "notEqual");
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-04-09 22:33:48 +02:00
|
|
|
template <bool with_nan>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Logical2HGreaterEqual(Operation operation) {
|
2019-04-09 22:33:48 +02:00
|
|
|
return GenerateHalfComparison<with_nan>(operation, "greaterThanEqual");
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Texture(Operation operation) {
|
2019-01-15 21:52:49 +01:00
|
|
|
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
|
|
|
ASSERT(meta);
|
|
|
|
|
2019-03-29 22:37:37 +01:00
|
|
|
std::string expr = GenerateTexture(
|
|
|
|
operation, "", {TextureAoffi{}, TextureArgument{Type::Float, meta->bias}});
|
2019-01-15 21:52:49 +01:00
|
|
|
if (meta->sampler.IsShadow()) {
|
2018-12-21 01:45:49 +01:00
|
|
|
expr = "vec4(" + expr + ')';
|
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
return {expr + GetSwizzle(meta->element), Type::Float};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression TextureLod(Operation operation) {
|
2019-01-15 21:52:49 +01:00
|
|
|
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
|
|
|
ASSERT(meta);
|
|
|
|
|
2019-03-29 22:37:37 +01:00
|
|
|
std::string expr = GenerateTexture(
|
|
|
|
operation, "Lod", {TextureArgument{Type::Float, meta->lod}, TextureAoffi{}});
|
2019-01-15 21:52:49 +01:00
|
|
|
if (meta->sampler.IsShadow()) {
|
2018-12-21 01:45:49 +01:00
|
|
|
expr = "vec4(" + expr + ')';
|
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
return {expr + GetSwizzle(meta->element), Type::Float};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression TextureGather(Operation operation) {
|
2019-01-15 21:52:49 +01:00
|
|
|
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
|
|
|
ASSERT(meta);
|
|
|
|
|
2019-02-22 07:30:12 +01:00
|
|
|
const auto type = meta->sampler.IsShadow() ? Type::Float : Type::Int;
|
2019-08-24 22:29:19 +02:00
|
|
|
return {GenerateTexture(operation, "Gather",
|
|
|
|
{TextureArgument{type, meta->component}, TextureAoffi{}}) +
|
|
|
|
GetSwizzle(meta->element),
|
|
|
|
Type::Float};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression TextureQueryDimensions(Operation operation) {
|
2019-01-15 21:52:49 +01:00
|
|
|
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
|
|
|
ASSERT(meta);
|
|
|
|
|
|
|
|
const std::string sampler = GetSampler(meta->sampler);
|
2019-08-24 22:29:19 +02:00
|
|
|
const std::string lod = VisitOperand(operation, 0).AsInt();
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-01-15 21:52:49 +01:00
|
|
|
switch (meta->element) {
|
2018-12-27 05:50:22 +01:00
|
|
|
case 0:
|
|
|
|
case 1:
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("textureSize({}, {}){}", sampler, lod, GetSwizzle(meta->element)),
|
|
|
|
Type::Int};
|
2018-12-27 05:50:22 +01:00
|
|
|
case 3:
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("textureQueryLevels({})", sampler), Type::Int};
|
2018-12-27 05:50:22 +01:00
|
|
|
}
|
|
|
|
UNREACHABLE();
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"0", Type::Int};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression TextureQueryLod(Operation operation) {
|
2019-01-15 21:52:49 +01:00
|
|
|
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
|
|
|
ASSERT(meta);
|
|
|
|
|
|
|
|
if (meta->element < 2) {
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("int(({} * vec2(256)){})",
|
|
|
|
GenerateTexture(operation, "QueryLod", {}),
|
|
|
|
GetSwizzle(meta->element)),
|
|
|
|
Type::Int};
|
2018-12-27 05:50:22 +01:00
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"0", Type::Int};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression TexelFetch(Operation operation) {
|
|
|
|
constexpr std::array constructors = {"int", "ivec2", "ivec3", "ivec4"};
|
2019-01-15 21:52:49 +01:00
|
|
|
const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
|
|
|
|
ASSERT(meta);
|
2019-01-29 08:31:40 +01:00
|
|
|
UNIMPLEMENTED_IF(meta->sampler.IsArray());
|
2019-02-12 21:02:59 +01:00
|
|
|
const std::size_t count = operation.GetOperandsCount();
|
2018-12-23 05:18:33 +01:00
|
|
|
|
|
|
|
std::string expr = "texelFetch(";
|
2019-01-15 21:52:49 +01:00
|
|
|
expr += GetSampler(meta->sampler);
|
2018-12-23 05:18:33 +01:00
|
|
|
expr += ", ";
|
|
|
|
|
2019-02-12 21:02:59 +01:00
|
|
|
expr += constructors.at(operation.GetOperandsCount() - 1);
|
2018-12-23 05:18:33 +01:00
|
|
|
expr += '(';
|
2019-02-12 21:02:59 +01:00
|
|
|
for (std::size_t i = 0; i < count; ++i) {
|
2019-08-24 22:29:19 +02:00
|
|
|
expr += VisitOperand(operation, i).AsInt();
|
2019-02-12 21:02:59 +01:00
|
|
|
const std::size_t next = i + 1;
|
2019-01-29 08:31:40 +01:00
|
|
|
if (next == count)
|
2018-12-23 05:18:33 +01:00
|
|
|
expr += ')';
|
2019-02-12 21:02:59 +01:00
|
|
|
else if (next < count)
|
2018-12-23 05:18:33 +01:00
|
|
|
expr += ", ";
|
|
|
|
}
|
2019-04-28 02:45:59 +02:00
|
|
|
|
|
|
|
// Store a copy of the expression without the lod to be used with texture buffers
|
|
|
|
std::string expr_buffer = expr;
|
|
|
|
|
2019-02-22 07:30:12 +01:00
|
|
|
if (meta->lod) {
|
2019-02-12 21:02:59 +01:00
|
|
|
expr += ", ";
|
2019-08-24 22:29:19 +02:00
|
|
|
expr += Visit(meta->lod).AsInt();
|
2019-02-12 21:02:59 +01:00
|
|
|
}
|
2018-12-23 05:18:33 +01:00
|
|
|
expr += ')';
|
2019-04-28 02:45:59 +02:00
|
|
|
expr += GetSwizzle(meta->element);
|
2019-01-29 08:31:40 +01:00
|
|
|
|
2019-04-28 02:45:59 +02:00
|
|
|
expr_buffer += ')';
|
|
|
|
expr_buffer += GetSwizzle(meta->element);
|
|
|
|
|
|
|
|
const std::string tmp{code.GenerateTemporary()};
|
|
|
|
EmitIfdefIsBuffer(meta->sampler);
|
2019-06-24 06:56:38 +02:00
|
|
|
code.AddLine("float {} = {};", tmp, expr_buffer);
|
2019-04-28 02:45:59 +02:00
|
|
|
code.AddLine("#else");
|
2019-06-24 06:56:38 +02:00
|
|
|
code.AddLine("float {} = {};", tmp, expr);
|
2019-04-28 02:45:59 +02:00
|
|
|
code.AddLine("#endif");
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
return {tmp, Type::Float};
|
2018-12-23 05:18:33 +01:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression ImageStore(Operation operation) {
|
2019-04-27 07:07:18 +02:00
|
|
|
const auto meta{std::get<MetaImage>(operation.GetMeta())};
|
2019-07-18 02:03:53 +02:00
|
|
|
code.AddLine("imageStore({}, {}, {});", GetImage(meta.image),
|
|
|
|
BuildIntegerCoordinates(operation), BuildImageValues(operation));
|
|
|
|
return {};
|
|
|
|
}
|
2019-04-27 07:07:18 +02:00
|
|
|
|
2019-07-18 02:03:53 +02:00
|
|
|
Expression AtomicImageAdd(Operation operation) {
|
|
|
|
return AtomicImage(operation, "imageAtomicAdd");
|
|
|
|
}
|
2019-04-27 07:07:18 +02:00
|
|
|
|
2019-07-18 02:03:53 +02:00
|
|
|
Expression AtomicImageMin(Operation operation) {
|
|
|
|
return AtomicImage(operation, "imageAtomicMin");
|
|
|
|
}
|
2019-04-27 07:07:18 +02:00
|
|
|
|
2019-07-18 02:03:53 +02:00
|
|
|
Expression AtomicImageMax(Operation operation) {
|
|
|
|
return AtomicImage(operation, "imageAtomicMax");
|
|
|
|
}
|
|
|
|
Expression AtomicImageAnd(Operation operation) {
|
|
|
|
return AtomicImage(operation, "imageAtomicAnd");
|
|
|
|
}
|
2019-04-27 07:07:18 +02:00
|
|
|
|
2019-07-18 02:03:53 +02:00
|
|
|
Expression AtomicImageOr(Operation operation) {
|
|
|
|
return AtomicImage(operation, "imageAtomicOr");
|
|
|
|
}
|
|
|
|
|
|
|
|
Expression AtomicImageXor(Operation operation) {
|
|
|
|
return AtomicImage(operation, "imageAtomicXor");
|
|
|
|
}
|
|
|
|
|
|
|
|
Expression AtomicImageExchange(Operation operation) {
|
|
|
|
return AtomicImage(operation, "imageAtomicExchange");
|
2019-04-27 07:07:18 +02:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Branch(Operation operation) {
|
2019-06-05 03:44:06 +02:00
|
|
|
const auto target = std::get_if<ImmediateNode>(&*operation[0]);
|
2019-01-15 21:52:49 +01:00
|
|
|
UNIMPLEMENTED_IF(!target);
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
code.AddLine("jmp_to = 0x{:X}U;", target->GetValue());
|
2018-12-21 01:45:49 +01:00
|
|
|
code.AddLine("break;");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression BranchIndirect(Operation operation) {
|
|
|
|
const std::string op_a = VisitOperand(operation, 0).AsUint();
|
2019-06-25 03:25:38 +02:00
|
|
|
|
|
|
|
code.AddLine("jmp_to = {};", op_a);
|
|
|
|
code.AddLine("break;");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression PushFlowStack(Operation operation) {
|
2019-06-02 23:52:07 +02:00
|
|
|
const auto stack = std::get<MetaStackClass>(operation.GetMeta());
|
2019-06-05 03:44:06 +02:00
|
|
|
const auto target = std::get_if<ImmediateNode>(&*operation[0]);
|
2019-01-15 21:52:49 +01:00
|
|
|
UNIMPLEMENTED_IF(!target);
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
code.AddLine("{}[{}++] = 0x{:X}U;", FlowStackName(stack), FlowStackTopName(stack),
|
2019-06-02 23:52:07 +02:00
|
|
|
target->GetValue());
|
2018-12-21 01:45:49 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression PopFlowStack(Operation operation) {
|
2019-06-02 23:52:07 +02:00
|
|
|
const auto stack = std::get<MetaStackClass>(operation.GetMeta());
|
|
|
|
code.AddLine("jmp_to = {}[--{}];", FlowStackName(stack), FlowStackTopName(stack));
|
2018-12-21 01:45:49 +01:00
|
|
|
code.AddLine("break;");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Exit(Operation operation) {
|
2019-07-15 03:25:13 +02:00
|
|
|
if (stage != ProgramType::Fragment) {
|
2018-12-21 01:45:49 +01:00
|
|
|
code.AddLine("return;");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
const auto& used_registers = ir.GetRegisters();
|
2019-08-24 22:29:19 +02:00
|
|
|
const auto SafeGetRegister = [&](u32 reg) -> Expression {
|
2018-12-21 01:45:49 +01:00
|
|
|
// TODO(Rodrigo): Replace with contains once C++20 releases
|
|
|
|
if (used_registers.find(reg) != used_registers.end()) {
|
2019-08-24 22:29:19 +02:00
|
|
|
return {GetRegister(reg), Type::Float};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"0.0f", Type::Float};
|
2018-12-21 01:45:49 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented");
|
|
|
|
|
|
|
|
// Write the color outputs using the data in the shader registers, disabled
|
|
|
|
// rendertargets/components are skipped in the register assignment.
|
2019-05-22 01:28:09 +02:00
|
|
|
u32 current_reg = 0;
|
2018-12-21 01:45:49 +01:00
|
|
|
for (u32 render_target = 0; render_target < Maxwell::NumRenderTargets; ++render_target) {
|
|
|
|
// TODO(Subv): Figure out how dual-source blending is configured in the Switch.
|
|
|
|
for (u32 component = 0; component < 4; ++component) {
|
|
|
|
if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("FragColor{}[{}] = {};", render_target, component,
|
2019-08-24 22:29:19 +02:00
|
|
|
SafeGetRegister(current_reg).AsFloat());
|
2018-12-21 01:45:49 +01:00
|
|
|
++current_reg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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-08-24 22:29:19 +02:00
|
|
|
code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1).AsFloat());
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
code.AddLine("return;");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Discard(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
// Enclose "discard" in a conditional, so that GLSL compilation does not complain
|
|
|
|
// about unexecuted instructions that may follow this.
|
2019-05-14 23:59:25 +02:00
|
|
|
code.AddLine("if (true) {{");
|
2018-12-21 01:45:49 +01:00
|
|
|
++code.scope;
|
|
|
|
code.AddLine("discard;");
|
|
|
|
--code.scope;
|
2019-05-14 23:59:25 +02:00
|
|
|
code.AddLine("}}");
|
2018-12-21 01:45:49 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression EmitVertex(Operation operation) {
|
2019-07-15 03:25:13 +02:00
|
|
|
ASSERT_MSG(stage == ProgramType::Geometry,
|
2018-12-24 06:24:38 +01:00
|
|
|
"EmitVertex is expected to be used in a geometry shader.");
|
|
|
|
|
|
|
|
// If a geometry shader is attached, it will always flip (it's the last stage before
|
|
|
|
// fragment). For more info about flipping, refer to gl_shader_gen.cpp.
|
2019-06-03 06:01:34 +02:00
|
|
|
code.AddLine("gl_Position.xy *= viewport_flip.xy;");
|
2018-12-24 06:24:38 +01:00
|
|
|
code.AddLine("EmitVertex();");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression EndPrimitive(Operation operation) {
|
2019-07-15 03:25:13 +02:00
|
|
|
ASSERT_MSG(stage == ProgramType::Geometry,
|
2018-12-24 06:24:38 +01:00
|
|
|
"EndPrimitive is expected to be used in a geometry shader.");
|
|
|
|
|
|
|
|
code.AddLine("EndPrimitive();");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression YNegate(Operation operation) {
|
2018-12-21 01:45:49 +01:00
|
|
|
// Config pack's third value is Y_NEGATE's state.
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"config_pack[2]", Type::Uint};
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-05-03 08:00:51 +02:00
|
|
|
template <u32 element>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression LocalInvocationId(Operation) {
|
|
|
|
return {"gl_LocalInvocationID"s + GetSwizzle(element), Type::Uint};
|
2019-05-03 08:00:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template <u32 element>
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression WorkGroupId(Operation) {
|
|
|
|
return {"gl_WorkGroupID"s + GetSwizzle(element), Type::Uint};
|
2019-05-03 08:00:51 +02:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression BallotThread(Operation operation) {
|
|
|
|
const std::string value = VisitOperand(operation, 0).AsBool();
|
2019-08-10 04:50:21 +02:00
|
|
|
if (!device.HasWarpIntrinsics()) {
|
|
|
|
LOG_ERROR(Render_OpenGL,
|
|
|
|
"Nvidia warp intrinsics are not available and its required by a shader");
|
|
|
|
// Stub on non-Nvidia devices by simulating all threads voting the same as the active
|
|
|
|
// one.
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("({} ? 0xFFFFFFFFU : 0U)", value), Type::Uint};
|
2019-08-10 04:50:21 +02:00
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("ballotThreadNV({})", value), Type::Uint};
|
2019-08-10 04:50:21 +02:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression Vote(Operation operation, const char* func) {
|
|
|
|
const std::string value = VisitOperand(operation, 0).AsBool();
|
2019-08-10 04:50:21 +02:00
|
|
|
if (!device.HasWarpIntrinsics()) {
|
|
|
|
LOG_ERROR(Render_OpenGL,
|
|
|
|
"Nvidia vote intrinsics are not available and its required by a shader");
|
|
|
|
// Stub with a warp size of one.
|
2019-08-24 22:29:19 +02:00
|
|
|
return {value, Type::Bool};
|
2019-08-10 04:50:21 +02:00
|
|
|
}
|
2019-08-24 22:29:19 +02:00
|
|
|
return {fmt::format("{}({})", func, value), Type::Bool};
|
2019-08-10 04:50:21 +02:00
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression VoteAll(Operation operation) {
|
2019-08-10 04:50:21 +02:00
|
|
|
return Vote(operation, "allThreadsNV");
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression VoteAny(Operation operation) {
|
2019-08-10 04:50:21 +02:00
|
|
|
return Vote(operation, "anyThreadNV");
|
|
|
|
}
|
|
|
|
|
2019-08-24 22:29:19 +02:00
|
|
|
Expression VoteEqual(Operation operation) {
|
2019-08-10 04:50:21 +02:00
|
|
|
if (!device.HasWarpIntrinsics()) {
|
|
|
|
LOG_ERROR(Render_OpenGL,
|
|
|
|
"Nvidia vote intrinsics are not available and its required by a shader");
|
|
|
|
// We must return true here since a stub for a theoretical warp size of 1 will always
|
|
|
|
// return an equal result for all its votes.
|
2019-08-24 22:29:19 +02:00
|
|
|
return {"true", Type::Bool};
|
2019-08-10 04:50:21 +02:00
|
|
|
}
|
|
|
|
return Vote(operation, "allThreadsEqualNV");
|
|
|
|
}
|
|
|
|
|
2019-07-20 03:20:34 +02:00
|
|
|
static constexpr std::array operation_decompilers = {
|
2018-12-24 07:10:13 +01:00
|
|
|
&GLSLDecompiler::Assign,
|
|
|
|
|
|
|
|
&GLSLDecompiler::Select,
|
|
|
|
|
|
|
|
&GLSLDecompiler::Add<Type::Float>,
|
|
|
|
&GLSLDecompiler::Mul<Type::Float>,
|
|
|
|
&GLSLDecompiler::Div<Type::Float>,
|
|
|
|
&GLSLDecompiler::Fma<Type::Float>,
|
|
|
|
&GLSLDecompiler::Negate<Type::Float>,
|
|
|
|
&GLSLDecompiler::Absolute<Type::Float>,
|
|
|
|
&GLSLDecompiler::FClamp,
|
2019-07-20 23:38:25 +02:00
|
|
|
&GLSLDecompiler::FCastHalf0,
|
|
|
|
&GLSLDecompiler::FCastHalf1,
|
2018-12-24 07:10:13 +01:00
|
|
|
&GLSLDecompiler::Min<Type::Float>,
|
|
|
|
&GLSLDecompiler::Max<Type::Float>,
|
|
|
|
&GLSLDecompiler::FCos,
|
|
|
|
&GLSLDecompiler::FSin,
|
|
|
|
&GLSLDecompiler::FExp2,
|
|
|
|
&GLSLDecompiler::FLog2,
|
|
|
|
&GLSLDecompiler::FInverseSqrt,
|
|
|
|
&GLSLDecompiler::FSqrt,
|
|
|
|
&GLSLDecompiler::FRoundEven,
|
|
|
|
&GLSLDecompiler::FFloor,
|
|
|
|
&GLSLDecompiler::FCeil,
|
|
|
|
&GLSLDecompiler::FTrunc,
|
|
|
|
&GLSLDecompiler::FCastInteger<Type::Int>,
|
|
|
|
&GLSLDecompiler::FCastInteger<Type::Uint>,
|
|
|
|
|
|
|
|
&GLSLDecompiler::Add<Type::Int>,
|
|
|
|
&GLSLDecompiler::Mul<Type::Int>,
|
|
|
|
&GLSLDecompiler::Div<Type::Int>,
|
|
|
|
&GLSLDecompiler::Negate<Type::Int>,
|
|
|
|
&GLSLDecompiler::Absolute<Type::Int>,
|
|
|
|
&GLSLDecompiler::Min<Type::Int>,
|
|
|
|
&GLSLDecompiler::Max<Type::Int>,
|
|
|
|
|
|
|
|
&GLSLDecompiler::ICastFloat,
|
|
|
|
&GLSLDecompiler::ICastUnsigned,
|
|
|
|
&GLSLDecompiler::LogicalShiftLeft<Type::Int>,
|
|
|
|
&GLSLDecompiler::ILogicalShiftRight,
|
|
|
|
&GLSLDecompiler::IArithmeticShiftRight,
|
|
|
|
&GLSLDecompiler::BitwiseAnd<Type::Int>,
|
|
|
|
&GLSLDecompiler::BitwiseOr<Type::Int>,
|
|
|
|
&GLSLDecompiler::BitwiseXor<Type::Int>,
|
|
|
|
&GLSLDecompiler::BitwiseNot<Type::Int>,
|
|
|
|
&GLSLDecompiler::BitfieldInsert<Type::Int>,
|
2018-12-26 06:58:47 +01:00
|
|
|
&GLSLDecompiler::BitfieldExtract<Type::Int>,
|
2018-12-24 07:10:13 +01:00
|
|
|
&GLSLDecompiler::BitCount<Type::Int>,
|
|
|
|
|
|
|
|
&GLSLDecompiler::Add<Type::Uint>,
|
|
|
|
&GLSLDecompiler::Mul<Type::Uint>,
|
|
|
|
&GLSLDecompiler::Div<Type::Uint>,
|
|
|
|
&GLSLDecompiler::Min<Type::Uint>,
|
|
|
|
&GLSLDecompiler::Max<Type::Uint>,
|
|
|
|
&GLSLDecompiler::UCastFloat,
|
|
|
|
&GLSLDecompiler::UCastSigned,
|
|
|
|
&GLSLDecompiler::LogicalShiftLeft<Type::Uint>,
|
|
|
|
&GLSLDecompiler::UShiftRight,
|
|
|
|
&GLSLDecompiler::UShiftRight,
|
|
|
|
&GLSLDecompiler::BitwiseAnd<Type::Uint>,
|
|
|
|
&GLSLDecompiler::BitwiseOr<Type::Uint>,
|
|
|
|
&GLSLDecompiler::BitwiseXor<Type::Uint>,
|
|
|
|
&GLSLDecompiler::BitwiseNot<Type::Uint>,
|
|
|
|
&GLSLDecompiler::BitfieldInsert<Type::Uint>,
|
2018-12-26 06:58:47 +01:00
|
|
|
&GLSLDecompiler::BitfieldExtract<Type::Uint>,
|
2018-12-24 07:10:13 +01:00
|
|
|
&GLSLDecompiler::BitCount<Type::Uint>,
|
|
|
|
|
|
|
|
&GLSLDecompiler::Add<Type::HalfFloat>,
|
|
|
|
&GLSLDecompiler::Mul<Type::HalfFloat>,
|
|
|
|
&GLSLDecompiler::Fma<Type::HalfFloat>,
|
|
|
|
&GLSLDecompiler::Absolute<Type::HalfFloat>,
|
|
|
|
&GLSLDecompiler::HNegate,
|
2019-04-09 23:41:41 +02:00
|
|
|
&GLSLDecompiler::HClamp,
|
2019-07-20 23:38:25 +02:00
|
|
|
&GLSLDecompiler::HCastFloat,
|
2019-04-16 00:48:11 +02:00
|
|
|
&GLSLDecompiler::HUnpack,
|
2018-12-24 07:10:13 +01:00
|
|
|
&GLSLDecompiler::HMergeF32,
|
|
|
|
&GLSLDecompiler::HMergeH0,
|
|
|
|
&GLSLDecompiler::HMergeH1,
|
2018-12-27 05:50:22 +01:00
|
|
|
&GLSLDecompiler::HPack2,
|
2018-12-24 07:10:13 +01:00
|
|
|
|
|
|
|
&GLSLDecompiler::LogicalAssign,
|
|
|
|
&GLSLDecompiler::LogicalAnd,
|
|
|
|
&GLSLDecompiler::LogicalOr,
|
|
|
|
&GLSLDecompiler::LogicalXor,
|
|
|
|
&GLSLDecompiler::LogicalNegate,
|
|
|
|
&GLSLDecompiler::LogicalPick2,
|
2019-07-20 03:20:34 +02:00
|
|
|
&GLSLDecompiler::LogicalAnd2,
|
2018-12-24 07:10:13 +01:00
|
|
|
|
|
|
|
&GLSLDecompiler::LogicalLessThan<Type::Float>,
|
|
|
|
&GLSLDecompiler::LogicalEqual<Type::Float>,
|
|
|
|
&GLSLDecompiler::LogicalLessEqual<Type::Float>,
|
|
|
|
&GLSLDecompiler::LogicalGreaterThan<Type::Float>,
|
|
|
|
&GLSLDecompiler::LogicalNotEqual<Type::Float>,
|
|
|
|
&GLSLDecompiler::LogicalGreaterEqual<Type::Float>,
|
|
|
|
&GLSLDecompiler::LogicalFIsNan,
|
|
|
|
|
|
|
|
&GLSLDecompiler::LogicalLessThan<Type::Int>,
|
|
|
|
&GLSLDecompiler::LogicalEqual<Type::Int>,
|
|
|
|
&GLSLDecompiler::LogicalLessEqual<Type::Int>,
|
|
|
|
&GLSLDecompiler::LogicalGreaterThan<Type::Int>,
|
|
|
|
&GLSLDecompiler::LogicalNotEqual<Type::Int>,
|
|
|
|
&GLSLDecompiler::LogicalGreaterEqual<Type::Int>,
|
|
|
|
|
|
|
|
&GLSLDecompiler::LogicalLessThan<Type::Uint>,
|
|
|
|
&GLSLDecompiler::LogicalEqual<Type::Uint>,
|
|
|
|
&GLSLDecompiler::LogicalLessEqual<Type::Uint>,
|
|
|
|
&GLSLDecompiler::LogicalGreaterThan<Type::Uint>,
|
|
|
|
&GLSLDecompiler::LogicalNotEqual<Type::Uint>,
|
|
|
|
&GLSLDecompiler::LogicalGreaterEqual<Type::Uint>,
|
|
|
|
|
2019-04-09 22:33:48 +02:00
|
|
|
&GLSLDecompiler::Logical2HLessThan<false>,
|
|
|
|
&GLSLDecompiler::Logical2HEqual<false>,
|
|
|
|
&GLSLDecompiler::Logical2HLessEqual<false>,
|
|
|
|
&GLSLDecompiler::Logical2HGreaterThan<false>,
|
|
|
|
&GLSLDecompiler::Logical2HNotEqual<false>,
|
|
|
|
&GLSLDecompiler::Logical2HGreaterEqual<false>,
|
|
|
|
&GLSLDecompiler::Logical2HLessThan<true>,
|
|
|
|
&GLSLDecompiler::Logical2HEqual<true>,
|
|
|
|
&GLSLDecompiler::Logical2HLessEqual<true>,
|
|
|
|
&GLSLDecompiler::Logical2HGreaterThan<true>,
|
|
|
|
&GLSLDecompiler::Logical2HNotEqual<true>,
|
|
|
|
&GLSLDecompiler::Logical2HGreaterEqual<true>,
|
2018-12-24 07:10:13 +01:00
|
|
|
|
2019-01-29 08:39:07 +01:00
|
|
|
&GLSLDecompiler::Texture,
|
|
|
|
&GLSLDecompiler::TextureLod,
|
|
|
|
&GLSLDecompiler::TextureGather,
|
|
|
|
&GLSLDecompiler::TextureQueryDimensions,
|
|
|
|
&GLSLDecompiler::TextureQueryLod,
|
|
|
|
&GLSLDecompiler::TexelFetch,
|
2018-12-24 07:10:13 +01:00
|
|
|
|
2019-04-27 07:07:18 +02:00
|
|
|
&GLSLDecompiler::ImageStore,
|
2019-07-18 02:03:53 +02:00
|
|
|
&GLSLDecompiler::AtomicImageAdd,
|
|
|
|
&GLSLDecompiler::AtomicImageMin,
|
|
|
|
&GLSLDecompiler::AtomicImageMax,
|
|
|
|
&GLSLDecompiler::AtomicImageAnd,
|
|
|
|
&GLSLDecompiler::AtomicImageOr,
|
|
|
|
&GLSLDecompiler::AtomicImageXor,
|
|
|
|
&GLSLDecompiler::AtomicImageExchange,
|
2019-04-27 07:07:18 +02:00
|
|
|
|
2018-12-26 07:18:11 +01:00
|
|
|
&GLSLDecompiler::Branch,
|
2019-06-25 03:25:38 +02:00
|
|
|
&GLSLDecompiler::BranchIndirect,
|
2018-12-26 07:18:11 +01:00
|
|
|
&GLSLDecompiler::PushFlowStack,
|
|
|
|
&GLSLDecompiler::PopFlowStack,
|
2018-12-24 07:10:13 +01:00
|
|
|
&GLSLDecompiler::Exit,
|
2018-12-26 07:18:11 +01:00
|
|
|
&GLSLDecompiler::Discard,
|
2018-12-24 07:10:13 +01:00
|
|
|
|
|
|
|
&GLSLDecompiler::EmitVertex,
|
|
|
|
&GLSLDecompiler::EndPrimitive,
|
|
|
|
|
|
|
|
&GLSLDecompiler::YNegate,
|
2019-05-03 08:00:51 +02:00
|
|
|
&GLSLDecompiler::LocalInvocationId<0>,
|
|
|
|
&GLSLDecompiler::LocalInvocationId<1>,
|
|
|
|
&GLSLDecompiler::LocalInvocationId<2>,
|
|
|
|
&GLSLDecompiler::WorkGroupId<0>,
|
|
|
|
&GLSLDecompiler::WorkGroupId<1>,
|
|
|
|
&GLSLDecompiler::WorkGroupId<2>,
|
2019-08-10 04:50:21 +02:00
|
|
|
|
|
|
|
&GLSLDecompiler::BallotThread,
|
|
|
|
&GLSLDecompiler::VoteAll,
|
|
|
|
&GLSLDecompiler::VoteAny,
|
|
|
|
&GLSLDecompiler::VoteEqual,
|
2018-12-21 01:45:49 +01:00
|
|
|
};
|
2019-07-20 03:20:34 +02:00
|
|
|
static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount));
|
2018-12-21 01:45:49 +01:00
|
|
|
|
|
|
|
std::string GetRegister(u32 index) const {
|
|
|
|
return GetDeclarationWithSuffix(index, "gpr");
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetPredicate(Tegra::Shader::Pred pred) const {
|
|
|
|
return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred");
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetInputAttribute(Attribute::Index attribute) const {
|
2019-04-30 05:18:28 +02:00
|
|
|
return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "input_attr");
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetOutputAttribute(Attribute::Index attribute) const {
|
2019-04-30 05:18:28 +02:00
|
|
|
return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "output_attr");
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetConstBuffer(u32 index) const {
|
|
|
|
return GetDeclarationWithSuffix(index, "cbuf");
|
|
|
|
}
|
|
|
|
|
2018-12-29 06:44:54 +01:00
|
|
|
std::string GetGlobalMemory(const GlobalMemoryBase& descriptor) const {
|
|
|
|
return fmt::format("gmem_{}_{}_{}", descriptor.cbuf_index, descriptor.cbuf_offset, suffix);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetGlobalMemoryBlock(const GlobalMemoryBase& descriptor) const {
|
|
|
|
return fmt::format("gmem_block_{}_{}_{}", descriptor.cbuf_index, descriptor.cbuf_offset,
|
|
|
|
suffix);
|
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
std::string GetConstBufferBlock(u32 index) const {
|
|
|
|
return GetDeclarationWithSuffix(index, "cbuf_block");
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetLocalMemory() const {
|
|
|
|
return "lmem_" + suffix;
|
|
|
|
}
|
|
|
|
|
2019-08-29 22:18:04 +02:00
|
|
|
std::string GetSharedMemory() const {
|
|
|
|
return fmt::format("smem_{}", suffix);
|
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
std::string GetInternalFlag(InternalFlag flag) const {
|
2019-08-24 22:29:19 +02:00
|
|
|
constexpr std::array InternalFlagNames = {"zero_flag", "sign_flag", "carry_flag",
|
|
|
|
"overflow_flag"};
|
2018-12-21 01:45:49 +01:00
|
|
|
const auto index = static_cast<u32>(flag);
|
|
|
|
ASSERT(index < static_cast<u32>(InternalFlag::Amount));
|
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("{}_{}", InternalFlagNames[index], suffix);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetSampler(const Sampler& sampler) const {
|
2018-12-21 07:39:46 +01:00
|
|
|
return GetDeclarationWithSuffix(static_cast<u32>(sampler.GetIndex()), "sampler");
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-04-27 07:07:18 +02:00
|
|
|
std::string GetImage(const Image& image) const {
|
|
|
|
return GetDeclarationWithSuffix(static_cast<u32>(image.GetIndex()), "image");
|
|
|
|
}
|
|
|
|
|
2019-04-28 02:45:59 +02:00
|
|
|
void EmitIfdefIsBuffer(const Sampler& sampler) {
|
2019-06-24 06:56:38 +02:00
|
|
|
code.AddLine("#ifdef SAMPLER_{}_IS_BUFFER", sampler.GetIndex());
|
2019-04-28 02:45:59 +02:00
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
std::string GetDeclarationWithSuffix(u32 index, const std::string& name) const {
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("{}_{}_{}", name, index, suffix);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-05-01 00:36:18 +02:00
|
|
|
u32 GetNumPhysicalInputAttributes() const {
|
2019-07-15 03:25:13 +02:00
|
|
|
return IsVertexShader(stage) ? GetNumPhysicalAttributes() : GetNumPhysicalVaryings();
|
2019-05-01 00:36:18 +02:00
|
|
|
}
|
|
|
|
|
2019-04-30 05:09:51 +02:00
|
|
|
u32 GetNumPhysicalAttributes() const {
|
|
|
|
return std::min<u32>(device.GetMaxVertexAttributes(), Maxwell::NumVertexAttributes);
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 GetNumPhysicalVaryings() const {
|
2019-06-03 06:01:34 +02:00
|
|
|
return std::min<u32>(device.GetMaxVaryings(), Maxwell::NumVaryings);
|
2019-04-30 05:09:51 +02:00
|
|
|
}
|
|
|
|
|
2019-04-10 23:03:52 +02:00
|
|
|
const Device& device;
|
2018-12-21 01:45:49 +01:00
|
|
|
const ShaderIR& ir;
|
2019-07-15 03:25:13 +02:00
|
|
|
const ProgramType stage;
|
2018-12-21 01:45:49 +01:00
|
|
|
const std::string suffix;
|
|
|
|
const Header header;
|
|
|
|
|
|
|
|
ShaderWriter code;
|
|
|
|
};
|
|
|
|
|
2019-03-31 05:24:10 +02:00
|
|
|
} // Anonymous namespace
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
std::string GetCommonDeclarations() {
|
2019-05-15 00:12:29 +02:00
|
|
|
return fmt::format(
|
|
|
|
"#define ftoi floatBitsToInt\n"
|
|
|
|
"#define ftou floatBitsToUint\n"
|
|
|
|
"#define itof intBitsToFloat\n"
|
|
|
|
"#define utof uintBitsToFloat\n\n"
|
2019-08-24 22:29:19 +02:00
|
|
|
"bvec2 HalfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {{\n"
|
2019-05-15 00:12:29 +02:00
|
|
|
" bvec2 is_nan1 = isnan(pair1);\n"
|
|
|
|
" bvec2 is_nan2 = isnan(pair2);\n"
|
|
|
|
" return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || "
|
|
|
|
"is_nan2.y);\n"
|
2019-08-24 22:29:19 +02:00
|
|
|
"}}\n\n");
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-07-15 03:25:13 +02:00
|
|
|
ProgramResult Decompile(const Device& device, const ShaderIR& ir, ProgramType stage,
|
2019-04-10 23:03:52 +02:00
|
|
|
const std::string& suffix) {
|
|
|
|
GLSLDecompiler decompiler(device, ir, stage, suffix);
|
2018-12-21 01:45:49 +01:00
|
|
|
decompiler.Decompile();
|
|
|
|
return {decompiler.GetResult(), decompiler.GetShaderEntries()};
|
|
|
|
}
|
|
|
|
|
2019-02-13 02:14:39 +01:00
|
|
|
} // namespace OpenGL::GLShader
|