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"
|
|
|
|
#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 ShaderStage = Tegra::Engines::Maxwell3D::Regs::ShaderStage;
|
|
|
|
using Operation = const OperationNode&;
|
|
|
|
|
2019-03-29 22:37:37 +01:00
|
|
|
enum class Type { Bool, Bool2, Float, Int, Uint, HalfFloat };
|
|
|
|
|
|
|
|
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 =
|
|
|
|
static_cast<u32>(RasterizerOpenGL::MaxConstbufferSize) / (4 * sizeof(float));
|
2018-12-21 01:45:49 +01:00
|
|
|
|
|
|
|
class ShaderWriter {
|
|
|
|
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
|
|
|
};
|
|
|
|
|
|
|
|
/// Generates code to use for a swizzle operation.
|
2019-05-03 07:59:25 +02:00
|
|
|
constexpr const char* GetSwizzle(u32 element) {
|
|
|
|
constexpr std::array<const char*, 4> swizzle = {".x", ".y", ".z", ".w"};
|
|
|
|
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-03-31 05:24:10 +02:00
|
|
|
bool IsPrecise(Node node) {
|
2019-01-15 21:52:49 +01:00
|
|
|
if (const auto operation = std::get_if<OperationNode>(node)) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr Attribute::Index ToGenericAttribute(u32 value) {
|
|
|
|
return static_cast<Attribute::Index>(value + static_cast<u32>(Attribute::Index::Attribute_0));
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 GetGenericAttributeIndex(Attribute::Index index) {
|
|
|
|
ASSERT(IsGenericAttribute(index));
|
|
|
|
return static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0);
|
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
class GLSLDecompiler final {
|
|
|
|
public:
|
2019-04-10 23:03:52 +02:00
|
|
|
explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderStage stage,
|
|
|
|
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();
|
|
|
|
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();
|
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-05-14 18:18:07 +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.
|
|
|
|
constexpr u32 FLOW_STACK_SIZE = 20;
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("uint flow_stack[{}];", FLOW_STACK_SIZE);
|
2018-12-21 01:45:49 +01:00
|
|
|
code.AddLine("uint flow_stack_top = 0u;");
|
|
|
|
|
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-05-14 18:18:07 +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-02-07 04:05:41 +01:00
|
|
|
for (const auto& gmem_pair : ir.GetGlobalMemory()) {
|
|
|
|
const auto& [base, usage] = gmem_pair;
|
|
|
|
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();
|
|
|
|
entries.shader_length = ir.GetLength();
|
|
|
|
return entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
using OperationDecompilerFn = std::string (GLSLDecompiler::*)(Operation);
|
|
|
|
using OperationDecompilersArray =
|
|
|
|
std::array<OperationDecompilerFn, static_cast<std::size_t>(OperationCode::Amount)>;
|
|
|
|
|
|
|
|
void DeclareVertex() {
|
|
|
|
if (stage != ShaderStage::Vertex)
|
|
|
|
return;
|
|
|
|
|
2018-12-25 01:28:44 +01:00
|
|
|
DeclareVertexRedeclarations();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeclareGeometry() {
|
2019-05-14 18:18:07 +02:00
|
|
|
if (stage != ShaderStage::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() {
|
2018-12-21 01:45:49 +01:00
|
|
|
bool clip_distances_declared = false;
|
|
|
|
|
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;");
|
|
|
|
|
|
|
|
for (const auto o : ir.GetOutputAttributes()) {
|
|
|
|
if (o == Attribute::Index::PointSize)
|
|
|
|
code.AddLine("float gl_PointSize;");
|
|
|
|
if (!clip_distances_declared && (o == Attribute::Index::ClipDistances0123 ||
|
|
|
|
o == Attribute::Index::ClipDistances4567)) {
|
|
|
|
code.AddLine("float gl_ClipDistance[];");
|
|
|
|
clip_distances_declared = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
--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-05-14 18:18:07 +02:00
|
|
|
code.AddLine("float {} = 0;", 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() {
|
|
|
|
if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) {
|
|
|
|
const auto element_count = Common::AlignUp(local_memory_size, 4) / 4;
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("float {}[{}];", GetLocalMemory(), element_count);
|
2018-12-21 01:45:49 +01:00
|
|
|
code.AddNewLine();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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:
|
|
|
|
UNREACHABLE_MSG("Unused attribute being fetched");
|
|
|
|
return {};
|
|
|
|
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)};
|
|
|
|
if (stage == ShaderStage::Geometry) {
|
|
|
|
name = "gs_" + name + "[]";
|
|
|
|
}
|
2019-04-30 05:18:28 +02:00
|
|
|
|
2019-04-30 05:09:51 +02:00
|
|
|
std::string suffix;
|
|
|
|
if (stage == ShaderStage::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-05-01 00:36:18 +02:00
|
|
|
if (ir.HasPhysicalAttributes() && stage != ShaderStage::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));
|
|
|
|
code.AddLine(" vec4 {}[MAX_CONSTBUFFER_ELEMENTS];", GetConstBuffer(index));
|
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));
|
|
|
|
code.AddLine(" float {}[];", 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-05-14 18:18:07 +02:00
|
|
|
std::string sampler_type = [&sampler] {
|
2018-12-21 01:45:49 +01:00
|
|
|
switch (sampler.GetType()) {
|
|
|
|
case Tegra::Shader::TextureType::Texture1D:
|
|
|
|
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-05-14 18:18:07 +02:00
|
|
|
code.AddLine("layout (binding = SAMPLER_BINDING_{}) uniform {} {};", sampler.GetIndex(),
|
|
|
|
sampler_type, GetSampler(sampler));
|
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-05-14 18:18:07 +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-05-02 21:06:56 +02:00
|
|
|
constexpr u32 generic_base{0x80};
|
|
|
|
constexpr u32 generic_stride{16};
|
|
|
|
constexpr u32 element_stride{4};
|
|
|
|
const u32 address{generic_base + index * generic_stride + element * element_stride};
|
|
|
|
|
|
|
|
const bool declared{stage != ShaderStage::Fragment ||
|
|
|
|
header.ps.GetAttributeUse(index) != AttributeUse::Unused};
|
|
|
|
const std::string value{declared ? ReadAttribute(attribute, element) : "0"};
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("case 0x{:x}: 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-01-30 06:09:40 +01:00
|
|
|
void VisitBlock(const NodeBlock& bb) {
|
2018-12-21 01:45:49 +01:00
|
|
|
for (const Node node : bb) {
|
|
|
|
if (const std::string expr = Visit(node); !expr.empty()) {
|
|
|
|
code.AddLine(expr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Visit(Node node) {
|
|
|
|
if (const auto operation = std::get_if<OperationNode>(node)) {
|
|
|
|
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-05-14 18:18:07 +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) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return "0";
|
|
|
|
}
|
|
|
|
return GetRegister(index);
|
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 (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
|
|
|
|
return fmt::format("utof({}u)", immediate->GetValue());
|
|
|
|
}
|
|
|
|
return fmt::format("utof(0x{:x}u)", immediate->GetValue());
|
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 (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-05-14 18:18:07 +02:00
|
|
|
return fmt::format("!({})", value);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
return value;
|
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 (const auto abuf = std::get_if<AbufNode>(node)) {
|
2019-05-01 00:36:18 +02:00
|
|
|
UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ShaderStage::Geometry,
|
|
|
|
"Physical attributes in geometry shaders are not implemented");
|
|
|
|
if (abuf->IsPhysicalBuffer()) {
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("readPhysicalAttribute(ftou({}))",
|
|
|
|
Visit(abuf->GetPhysicalAddress()));
|
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-05-14 18:18:07 +02:00
|
|
|
if (const auto cbuf = std::get_if<CbufNode>(node)) {
|
2018-12-21 01:45:49 +01:00
|
|
|
const Node offset = cbuf->GetOffset();
|
|
|
|
if (const auto immediate = std::get_if<ImmediateNode>(offset)) {
|
|
|
|
// 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");
|
|
|
|
return fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()),
|
|
|
|
offset_imm / (4 * 4), (offset_imm / 4) % 4);
|
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-05-24 01:27:54 +02:00
|
|
|
code.AddLine("uint {} = ftou({}) >> 2;", final_offset, Visit(offset));
|
|
|
|
|
|
|
|
if (!device.HasComponentIndexingBug()) {
|
|
|
|
return fmt::format("{}[{} >> 2][{} & 3]", GetConstBuffer(cbuf->GetIndex()),
|
|
|
|
final_offset, final_offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
code.AddLine("vec4 {} = {}[{} >> 2];", pack, GetConstBuffer(cbuf->GetIndex()),
|
|
|
|
final_offset);
|
|
|
|
|
|
|
|
const std::string result = code.GenerateTemporary();
|
|
|
|
code.AddLine("float {};", result);
|
|
|
|
for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
|
|
|
|
code.AddLine("if (({} & 3) == {}) {} = {}{};", final_offset, swizzle, result,
|
|
|
|
pack, GetSwizzle(swizzle));
|
|
|
|
}
|
|
|
|
return result;
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
UNREACHABLE_MSG("Unmanaged offset node type");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (const auto gmem = std::get_if<GmemNode>(node)) {
|
2018-12-29 06:44:54 +01:00
|
|
|
const std::string real = Visit(gmem->GetRealAddress());
|
|
|
|
const std::string base = Visit(gmem->GetBaseAddress());
|
2019-05-14 18:18:07 +02:00
|
|
|
const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base);
|
2018-12-29 06:44:54 +01:00
|
|
|
return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
|
2019-05-14 18:18:07 +02:00
|
|
|
}
|
2018-12-29 06:44:54 +01:00
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
if (const auto lmem = std::get_if<LmemNode>(node)) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
|
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 (const auto internal_flag = std::get_if<InternalFlagNode>(node)) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GetInternalFlag(internal_flag->GetFlag());
|
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 (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-05-14 18:18:07 +02:00
|
|
|
code.AddLine("if ({}) {{", Visit(conditional->GetCondition()));
|
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-05-14 18:18:07 +02:00
|
|
|
if (const auto comment = std::get_if<CommentNode>(node)) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return "// " + comment->GetText();
|
|
|
|
}
|
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-05-01 00:36:18 +02:00
|
|
|
std::string ReadAttribute(Attribute::Index attribute, u32 element, Node buffer = {}) {
|
2019-05-14 18:18:07 +02:00
|
|
|
const auto GeometryPass = [&](std::string_view name) {
|
2019-05-01 00:36:18 +02:00
|
|
|
if (stage == ShaderStage::Geometry && buffer) {
|
|
|
|
// 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-05-14 18:18:07 +02:00
|
|
|
return fmt::format("gs_{}[ftou({}) % MAX_VERTEX_INPUT]", name, Visit(buffer));
|
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) {
|
|
|
|
case ShaderStage::Geometry:
|
|
|
|
return fmt::format("gl_in[ftou({})].gl_Position{}", Visit(buffer),
|
|
|
|
GetSwizzle(element));
|
|
|
|
case ShaderStage::Fragment:
|
2019-05-03 07:59:25 +02:00
|
|
|
return element == 3 ? "1.0f" : ("gl_FragCoord"s + GetSwizzle(element));
|
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:
|
|
|
|
return "gl_PointCoord.x";
|
|
|
|
case 1:
|
|
|
|
return "gl_PointCoord.y";
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
return "0";
|
|
|
|
}
|
|
|
|
UNREACHABLE();
|
|
|
|
return "0";
|
|
|
|
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.
|
|
|
|
ASSERT(stage == ShaderStage::Vertex);
|
|
|
|
switch (element) {
|
|
|
|
case 2:
|
|
|
|
// Config pack's first value is instance_id.
|
|
|
|
return "uintBitsToFloat(config_pack[0])";
|
|
|
|
case 3:
|
|
|
|
return "uintBitsToFloat(gl_VertexID)";
|
|
|
|
}
|
|
|
|
UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element);
|
|
|
|
return "0";
|
|
|
|
case Attribute::Index::FrontFacing:
|
|
|
|
// TODO(Subv): Find out what the values are for the other elements.
|
|
|
|
ASSERT(stage == ShaderStage::Fragment);
|
|
|
|
switch (element) {
|
|
|
|
case 3:
|
|
|
|
return "itof(gl_FrontFacing ? -1 : 0)";
|
|
|
|
}
|
|
|
|
UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element);
|
|
|
|
return "0";
|
|
|
|
default:
|
|
|
|
if (IsGenericAttribute(attribute)) {
|
|
|
|
return GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
|
|
|
|
return "0";
|
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
std::string ApplyPrecise(Operation operation, const std::string& value) {
|
|
|
|
if (!IsPrecise(operation)) {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
// There's a bug in NVidia's proprietary drivers that makes precise fail on fragment shaders
|
|
|
|
const std::string precise = stage != ShaderStage::Fragment ? "precise " : "";
|
|
|
|
|
2019-04-05 01:35:01 +02:00
|
|
|
const std::string temporary = code.GenerateTemporary();
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("{}float {} = {};", precise, temporary, value);
|
2019-04-05 01:35:01 +02:00
|
|
|
return temporary;
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string VisitOperand(Operation operation, std::size_t operand_index) {
|
|
|
|
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-04-05 01:35:01 +02:00
|
|
|
const std::string temporary = code.GenerateTemporary();
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("float {} = {};", temporary, Visit(operand));
|
2019-04-05 01:35:01 +02:00
|
|
|
return temporary;
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) {
|
2019-04-16 00:48:11 +02:00
|
|
|
return CastOperand(VisitOperand(operation, operand_index), type);
|
2019-02-12 21:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string CastOperand(const std::string& value, Type type) const {
|
|
|
|
switch (type) {
|
|
|
|
case Type::Bool:
|
|
|
|
case Type::Bool2:
|
|
|
|
case Type::Float:
|
|
|
|
return value;
|
|
|
|
case Type::Int:
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("ftoi({})", value);
|
2019-02-12 21:02:59 +01:00
|
|
|
case Type::Uint:
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("ftou({})", value);
|
2019-02-12 21:02:59 +01:00
|
|
|
case Type::HalfFloat:
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("toHalf2({})", value);
|
2019-02-12 21:02:59 +01:00
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
UNREACHABLE();
|
2018-12-21 22:47:22 +01:00
|
|
|
return value;
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
std::string BitwiseCastResult(const std::string& value, Type type,
|
|
|
|
bool needs_parenthesis = false) {
|
2018-12-21 01:45:49 +01:00
|
|
|
switch (type) {
|
|
|
|
case Type::Bool:
|
2019-02-12 21:02:59 +01:00
|
|
|
case Type::Bool2:
|
2018-12-21 01:45:49 +01:00
|
|
|
case Type::Float:
|
|
|
|
if (needs_parenthesis) {
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("({})", value);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
return value;
|
|
|
|
case Type::Int:
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("itof({})", value);
|
2018-12-21 01:45:49 +01:00
|
|
|
case Type::Uint:
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("utof({})", value);
|
2018-12-21 01:45:49 +01:00
|
|
|
case Type::HalfFloat:
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("fromHalf2({})", value);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
UNREACHABLE();
|
2018-12-21 22:47:22 +01:00
|
|
|
return value;
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string GenerateUnary(Operation operation, const std::string& func, Type result_type,
|
|
|
|
Type type_a, bool needs_parenthesis = true) {
|
2019-05-14 18:18:07 +02:00
|
|
|
const std::string op_str = fmt::format("{}({})", func, VisitOperand(operation, 0, type_a));
|
|
|
|
|
|
|
|
return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type, needs_parenthesis));
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string GenerateBinaryInfix(Operation operation, const std::string& func, Type result_type,
|
|
|
|
Type type_a, Type type_b) {
|
|
|
|
const std::string op_a = VisitOperand(operation, 0, type_a);
|
|
|
|
const std::string op_b = VisitOperand(operation, 1, type_b);
|
2019-05-14 18:18:07 +02:00
|
|
|
const std::string op_str = fmt::format("({} {} {})", op_a, func, op_b);
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type));
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string GenerateBinaryCall(Operation operation, const std::string& func, Type result_type,
|
|
|
|
Type type_a, Type type_b) {
|
|
|
|
const std::string op_a = VisitOperand(operation, 0, type_a);
|
|
|
|
const std::string op_b = VisitOperand(operation, 1, type_b);
|
2019-05-14 18:18:07 +02:00
|
|
|
const std::string op_str = fmt::format("{}({}, {})", func, op_a, op_b);
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type));
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string GenerateTernary(Operation operation, const std::string& func, Type result_type,
|
|
|
|
Type type_a, Type type_b, Type type_c) {
|
|
|
|
const std::string op_a = VisitOperand(operation, 0, type_a);
|
|
|
|
const std::string op_b = VisitOperand(operation, 1, type_b);
|
|
|
|
const std::string op_c = VisitOperand(operation, 2, type_c);
|
2019-05-14 18:18:07 +02:00
|
|
|
const std::string op_str = fmt::format("{}({}, {}, {})", func, op_a, op_b, op_c);
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type));
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string 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, type_a);
|
|
|
|
const std::string op_b = VisitOperand(operation, 1, type_b);
|
|
|
|
const std::string op_c = VisitOperand(operation, 2, type_c);
|
|
|
|
const std::string op_d = VisitOperand(operation, 3, type_d);
|
2019-05-14 18:18:07 +02:00
|
|
|
const std::string op_str = fmt::format("{}({}, {}, {}, {})", func, op_a, op_b, op_c, op_d);
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
return ApplyPrecise(operation, BitwiseCastResult(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) {
|
2018-12-21 01:45:49 +01:00
|
|
|
constexpr std::array<const char*, 4> coord_constructors = {"float", "vec2", "vec3", "vec4"};
|
|
|
|
|
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-01-29 08:31:40 +01:00
|
|
|
expr += Visit(operation[i]);
|
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-02-22 07:30:12 +01:00
|
|
|
expr += ", float(ftoi(" + Visit(meta->array) + "))";
|
2019-01-29 08:31:40 +01:00
|
|
|
}
|
|
|
|
if (has_shadow) {
|
2019-02-22 07:30:12 +01:00
|
|
|
expr += ", " + Visit(meta->depth_compare);
|
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:
|
|
|
|
if (const auto immediate = std::get_if<ImmediateNode>(operand)) {
|
|
|
|
// 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-05-14 18:18:07 +02:00
|
|
|
expr += fmt::format("ftoi({})", Visit(operand));
|
2019-03-29 22:37:37 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Type::Float:
|
|
|
|
expr += Visit(operand);
|
|
|
|
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 {};
|
|
|
|
}
|
|
|
|
constexpr std::array<const char*, 3> coord_constructors = {"int", "ivec2", "ivec3"};
|
|
|
|
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)};
|
|
|
|
if (const auto immediate = std::get_if<ImmediateNode>(operand)) {
|
|
|
|
// 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-05-14 18:18:07 +02:00
|
|
|
expr += fmt::format("ftoi({})", Visit(operand));
|
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
|
|
|
}
|
|
|
|
|
|
|
|
std::string Assign(Operation operation) {
|
|
|
|
const Node dest = operation[0];
|
|
|
|
const Node src = operation[1];
|
|
|
|
|
|
|
|
std::string target;
|
|
|
|
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 {};
|
|
|
|
}
|
|
|
|
target = GetRegister(gpr->GetIndex());
|
|
|
|
} else if (const auto abuf = std::get_if<AbufNode>(dest)) {
|
2019-05-01 00:46:49 +02:00
|
|
|
UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer());
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
target = [&]() -> std::string {
|
|
|
|
switch (const auto attribute = abuf->GetIndex(); abuf->GetIndex()) {
|
|
|
|
case Attribute::Index::Position:
|
2019-06-03 06:01:34 +02:00
|
|
|
return "gl_Position"s + GetSwizzle(abuf->GetElement());
|
2018-12-21 01:45:49 +01:00
|
|
|
case Attribute::Index::PointSize:
|
|
|
|
return "gl_PointSize";
|
|
|
|
case Attribute::Index::ClipDistances0123:
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("gl_ClipDistance[{}]", abuf->GetElement());
|
2018-12-21 01:45:49 +01:00
|
|
|
case Attribute::Index::ClipDistances4567:
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4);
|
2018-12-21 01:45:49 +01:00
|
|
|
default:
|
2019-04-30 05:18:28 +02:00
|
|
|
if (IsGenericAttribute(attribute)) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement());
|
|
|
|
}
|
|
|
|
UNIMPLEMENTED_MSG("Unhandled output attribute: {}",
|
|
|
|
static_cast<u32>(attribute));
|
2018-12-21 22:47:22 +01:00
|
|
|
return "0";
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
}();
|
|
|
|
} else if (const auto lmem = std::get_if<LmemNode>(dest)) {
|
2019-05-14 18:18:07 +02:00
|
|
|
target = fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
|
2019-02-07 04:05:41 +01:00
|
|
|
} else if (const auto gmem = std::get_if<GmemNode>(dest)) {
|
|
|
|
const std::string real = Visit(gmem->GetRealAddress());
|
|
|
|
const std::string base = Visit(gmem->GetBaseAddress());
|
2019-05-14 18:18:07 +02:00
|
|
|
const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base);
|
2019-02-07 04:05:41 +01:00
|
|
|
target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
|
2018-12-21 01:45:49 +01:00
|
|
|
} else {
|
|
|
|
UNREACHABLE_MSG("Assign called without a proper target");
|
|
|
|
}
|
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("{} = {};", target, Visit(src));
|
2018-12-21 01:45:49 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string Add(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, "+", type, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string Mul(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, "*", type, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string Div(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, "/", type, type, type);
|
|
|
|
}
|
|
|
|
|
2018-12-23 06:26:35 +01:00
|
|
|
template <Type type>
|
|
|
|
std::string Fma(Operation operation) {
|
|
|
|
return GenerateTernary(operation, "fma", type, type, type, type);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string Negate(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "-", type, type, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string Absolute(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "abs", type, type, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string FClamp(Operation operation) {
|
|
|
|
return GenerateTernary(operation, "clamp", Type::Float, Type::Float, Type::Float,
|
|
|
|
Type::Float);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string Min(Operation operation) {
|
|
|
|
return GenerateBinaryCall(operation, "min", type, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string Max(Operation operation) {
|
|
|
|
return GenerateBinaryCall(operation, "max", type, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Select(Operation operation) {
|
|
|
|
const std::string condition = Visit(operation[0]);
|
|
|
|
const std::string true_case = Visit(operation[1]);
|
|
|
|
const std::string false_case = Visit(operation[2]);
|
2019-05-14 18:18:07 +02:00
|
|
|
const std::string op_str = fmt::format("({} ? {} : {})", condition, true_case, false_case);
|
|
|
|
|
|
|
|
return ApplyPrecise(operation, op_str);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string FCos(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "cos", Type::Float, Type::Float, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string FSin(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "sin", Type::Float, Type::Float, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string FExp2(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "exp2", Type::Float, Type::Float, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string FLog2(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "log2", Type::Float, Type::Float, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string FInverseSqrt(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "inversesqrt", Type::Float, Type::Float, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string FSqrt(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "sqrt", Type::Float, Type::Float, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string FRoundEven(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "roundEven", Type::Float, Type::Float, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string FFloor(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "floor", Type::Float, Type::Float, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string FCeil(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "ceil", Type::Float, Type::Float, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string FTrunc(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "trunc", Type::Float, Type::Float, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string FCastInteger(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "float", Type::Float, type, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string ICastFloat(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "int", Type::Int, Type::Float, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string ICastUnsigned(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "int", Type::Int, Type::Uint, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string LogicalShiftLeft(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, "<<", type, type, Type::Uint);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string ILogicalShiftRight(Operation operation) {
|
|
|
|
const std::string op_a = VisitOperand(operation, 0, Type::Uint);
|
|
|
|
const std::string op_b = VisitOperand(operation, 1, Type::Uint);
|
2019-05-14 18:18:07 +02:00
|
|
|
const std::string op_str = fmt::format("int({} >> {})", op_a, op_b);
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
return ApplyPrecise(operation, BitwiseCastResult(op_str, Type::Int));
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string IArithmeticShiftRight(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, ">>", Type::Int, Type::Int, Type::Uint);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string BitwiseAnd(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, "&", type, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string BitwiseOr(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, "|", type, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string BitwiseXor(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, "^", type, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string BitwiseNot(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "~", type, type, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string UCastFloat(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "uint", Type::Uint, Type::Float, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string UCastSigned(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "uint", Type::Uint, Type::Int, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string UShiftRight(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, ">>", Type::Uint, Type::Uint, Type::Uint);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string BitfieldInsert(Operation operation) {
|
|
|
|
return GenerateQuaternary(operation, "bitfieldInsert", type, type, type, Type::Int,
|
|
|
|
Type::Int);
|
|
|
|
}
|
|
|
|
|
2018-12-26 06:58:47 +01:00
|
|
|
template <Type type>
|
|
|
|
std::string BitfieldExtract(Operation operation) {
|
|
|
|
return GenerateTernary(operation, "bitfieldExtract", type, type, Type::Int, Type::Int);
|
|
|
|
}
|
|
|
|
|
2018-12-23 05:33:47 +01:00
|
|
|
template <Type type>
|
|
|
|
std::string BitCount(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "bitCount", type, type, false);
|
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
std::string HNegate(Operation operation) {
|
2019-05-14 18:18:07 +02:00
|
|
|
const auto GetNegate = [&](std::size_t index) {
|
2018-12-21 01:45:49 +01:00
|
|
|
return VisitOperand(operation, index, Type::Bool) + " ? -1 : 1";
|
|
|
|
};
|
2019-05-14 18:18:07 +02:00
|
|
|
const std::string value =
|
|
|
|
fmt::format("({} * vec2({}, {}))", VisitOperand(operation, 0, Type::HalfFloat),
|
|
|
|
GetNegate(1), GetNegate(2));
|
2018-12-21 01:45:49 +01:00
|
|
|
return BitwiseCastResult(value, Type::HalfFloat);
|
|
|
|
}
|
|
|
|
|
2019-04-09 23:41:41 +02:00
|
|
|
std::string HClamp(Operation operation) {
|
|
|
|
const std::string value = VisitOperand(operation, 0, Type::HalfFloat);
|
|
|
|
const std::string min = VisitOperand(operation, 1, Type::Float);
|
|
|
|
const std::string max = VisitOperand(operation, 2, Type::Float);
|
2019-05-14 18:18:07 +02:00
|
|
|
const std::string clamped = fmt::format("clamp({}, vec2({}), vec2({}))", value, min, max);
|
|
|
|
|
2019-04-09 23:41:41 +02:00
|
|
|
return ApplyPrecise(operation, BitwiseCastResult(clamped, Type::HalfFloat));
|
|
|
|
}
|
|
|
|
|
2019-04-16 00:48:11 +02:00
|
|
|
std::string HUnpack(Operation operation) {
|
|
|
|
const std::string operand{VisitOperand(operation, 0, Type::HalfFloat)};
|
|
|
|
const auto value = [&]() -> std::string {
|
|
|
|
switch (std::get<Tegra::Shader::HalfType>(operation.GetMeta())) {
|
|
|
|
case Tegra::Shader::HalfType::H0_H1:
|
|
|
|
return operand;
|
|
|
|
case Tegra::Shader::HalfType::F32:
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("vec2(fromHalf2({}))", operand);
|
2019-04-16 00:48:11 +02:00
|
|
|
case Tegra::Shader::HalfType::H0_H0:
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("vec2({}[0])", operand);
|
2019-04-16 00:48:11 +02:00
|
|
|
case Tegra::Shader::HalfType::H1_H1:
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("vec2({}[1])", operand);
|
2019-04-16 00:48:11 +02:00
|
|
|
}
|
|
|
|
UNREACHABLE();
|
|
|
|
return "0";
|
|
|
|
}();
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("fromHalf2({})", value);
|
2019-04-16 00:48:11 +02:00
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
std::string HMergeF32(Operation operation) {
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("float(toHalf2({})[0])", Visit(operation[0]));
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string HMergeH0(Operation operation) {
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[1]),
|
|
|
|
Visit(operation[0]));
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string HMergeH1(Operation operation) {
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[0]),
|
|
|
|
Visit(operation[1]));
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2018-12-27 05:50:22 +01:00
|
|
|
std::string HPack2(Operation operation) {
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("utof(packHalf2x16(vec2({}, {})))", Visit(operation[0]),
|
|
|
|
Visit(operation[1]));
|
2018-12-27 05:50:22 +01:00
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
template <Type type>
|
|
|
|
std::string LogicalLessThan(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, "<", Type::Bool, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string LogicalEqual(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, "==", Type::Bool, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string LogicalLessEqual(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, "<=", Type::Bool, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string LogicalGreaterThan(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, ">", Type::Bool, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string LogicalNotEqual(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, "!=", Type::Bool, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <Type type>
|
|
|
|
std::string LogicalGreaterEqual(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, ">=", Type::Bool, type, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string LogicalFIsNan(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "isnan", Type::Bool, Type::Float, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string LogicalAssign(Operation operation) {
|
|
|
|
const Node dest = operation[0];
|
|
|
|
const Node src = operation[1];
|
|
|
|
|
|
|
|
std::string target;
|
|
|
|
|
|
|
|
if (const auto pred = std::get_if<PredicateNode>(dest)) {
|
|
|
|
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);
|
|
|
|
} else if (const auto flag = std::get_if<InternalFlagNode>(dest)) {
|
|
|
|
target = GetInternalFlag(flag->GetFlag());
|
|
|
|
}
|
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("{} = {};", target, Visit(src));
|
2018-12-21 01:45:49 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string LogicalAnd(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, "&&", Type::Bool, Type::Bool, Type::Bool);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string LogicalOr(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, "||", Type::Bool, Type::Bool, Type::Bool);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string LogicalXor(Operation operation) {
|
|
|
|
return GenerateBinaryInfix(operation, "^^", Type::Bool, Type::Bool, Type::Bool);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string LogicalNegate(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "!", Type::Bool, Type::Bool, false);
|
|
|
|
}
|
|
|
|
|
2018-12-24 04:51:52 +01:00
|
|
|
std::string LogicalPick2(Operation operation) {
|
|
|
|
const std::string pair = VisitOperand(operation, 0, Type::Bool2);
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("{}[{}]", pair, VisitOperand(operation, 1, Type::Uint));
|
2018-12-24 04:51:52 +01:00
|
|
|
}
|
|
|
|
|
2018-12-24 00:59:49 +01:00
|
|
|
std::string LogicalAll2(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "all", Type::Bool, Type::Bool2);
|
|
|
|
}
|
2018-12-21 01:45:49 +01:00
|
|
|
|
2018-12-24 00:59:49 +01:00
|
|
|
std::string LogicalAny2(Operation operation) {
|
|
|
|
return GenerateUnary(operation, "any", 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-05-14 18:18:07 +02:00
|
|
|
std::string GenerateHalfComparison(Operation operation, const std::string& compare_op) {
|
|
|
|
const std::string 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-05-14 18:18:07 +02:00
|
|
|
return fmt::format("halfFloatNanComparison({}, {}, {})", comparison,
|
|
|
|
VisitOperand(operation, 0, Type::HalfFloat),
|
|
|
|
VisitOperand(operation, 1, Type::HalfFloat));
|
2019-04-09 22:33:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template <bool with_nan>
|
2018-12-24 00:59:49 +01:00
|
|
|
std::string 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>
|
2018-12-24 00:59:49 +01:00
|
|
|
std::string 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>
|
2018-12-24 00:59:49 +01:00
|
|
|
std::string 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>
|
2018-12-24 00:59:49 +01:00
|
|
|
std::string 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>
|
2018-12-24 00:59:49 +01:00
|
|
|
std::string 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>
|
2018-12-24 00:59:49 +01:00
|
|
|
std::string 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-01-29 08:39:07 +01:00
|
|
|
std::string 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-01-15 21:52:49 +01:00
|
|
|
return expr + GetSwizzle(meta->element);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-01-29 08:39:07 +01:00
|
|
|
std::string 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-01-15 21:52:49 +01:00
|
|
|
return expr + GetSwizzle(meta->element);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-01-29 08:39:07 +01:00
|
|
|
std::string 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-03-29 22:37:37 +01:00
|
|
|
return GenerateTexture(operation, "Gather",
|
|
|
|
{TextureArgument{type, meta->component}, TextureAoffi{}}) +
|
2019-01-15 21:52:49 +01:00
|
|
|
GetSwizzle(meta->element);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-01-29 08:39:07 +01:00
|
|
|
std::string 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);
|
2018-12-21 01:45:49 +01:00
|
|
|
const std::string lod = VisitOperand(operation, 0, Type::Int);
|
|
|
|
|
2019-01-15 21:52:49 +01:00
|
|
|
switch (meta->element) {
|
2018-12-27 05:50:22 +01:00
|
|
|
case 0:
|
|
|
|
case 1:
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("itof(int(textureSize({}, {}){}))", sampler, lod,
|
|
|
|
GetSwizzle(meta->element));
|
2018-12-27 05:50:22 +01:00
|
|
|
case 2:
|
|
|
|
return "0";
|
|
|
|
case 3:
|
2019-05-14 18:18:07 +02:00
|
|
|
return fmt::format("itof(textureQueryLevels({}))", sampler);
|
2018-12-27 05:50:22 +01:00
|
|
|
}
|
|
|
|
UNREACHABLE();
|
|
|
|
return "0";
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-01-29 08:39:07 +01:00
|
|
|
std::string 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-05-14 18:18:07 +02:00
|
|
|
return fmt::format("itof(int(({} * vec2(256)){}))",
|
|
|
|
GenerateTexture(operation, "QueryLod", {}),
|
|
|
|
GetSwizzle(meta->element));
|
2018-12-27 05:50:22 +01:00
|
|
|
}
|
|
|
|
return "0";
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-01-29 08:39:07 +01:00
|
|
|
std::string TexelFetch(Operation operation) {
|
2018-12-23 05:18:33 +01:00
|
|
|
constexpr std::array<const char*, 4> 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) {
|
2018-12-23 05:18:33 +01:00
|
|
|
expr += VisitOperand(operation, i, Type::Int);
|
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-02-22 07:30:12 +01:00
|
|
|
if (meta->lod) {
|
2019-02-12 21:02:59 +01:00
|
|
|
expr += ", ";
|
2019-02-22 07:30:12 +01:00
|
|
|
expr += CastOperand(Visit(meta->lod), Type::Int);
|
2019-02-12 21:02:59 +01:00
|
|
|
}
|
2018-12-23 05:18:33 +01:00
|
|
|
expr += ')';
|
2019-01-29 08:31:40 +01:00
|
|
|
|
2019-01-15 21:52:49 +01:00
|
|
|
return expr + GetSwizzle(meta->element);
|
2018-12-23 05:18:33 +01:00
|
|
|
}
|
|
|
|
|
2018-12-26 07:18:11 +01:00
|
|
|
std::string Branch(Operation operation) {
|
2019-01-15 21:52:49 +01:00
|
|
|
const auto target = std::get_if<ImmediateNode>(operation[0]);
|
|
|
|
UNIMPLEMENTED_IF(!target);
|
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("jmp_to = 0x{:x}u;", target->GetValue());
|
2018-12-21 01:45:49 +01:00
|
|
|
code.AddLine("break;");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string PushFlowStack(Operation operation) {
|
2019-01-15 21:52:49 +01:00
|
|
|
const auto target = std::get_if<ImmediateNode>(operation[0]);
|
|
|
|
UNIMPLEMENTED_IF(!target);
|
|
|
|
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue());
|
2018-12-21 01:45:49 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string PopFlowStack(Operation operation) {
|
2019-01-15 21:52:49 +01:00
|
|
|
code.AddLine("jmp_to = flow_stack[--flow_stack_top];");
|
2018-12-21 01:45:49 +01:00
|
|
|
code.AddLine("break;");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Exit(Operation operation) {
|
|
|
|
if (stage != ShaderStage::Fragment) {
|
|
|
|
code.AddLine("return;");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
const auto& used_registers = ir.GetRegisters();
|
|
|
|
const auto SafeGetRegister = [&](u32 reg) -> std::string {
|
|
|
|
// TODO(Rodrigo): Replace with contains once C++20 releases
|
|
|
|
if (used_registers.find(reg) != used_registers.end()) {
|
|
|
|
return GetRegister(reg);
|
|
|
|
}
|
|
|
|
return "0.0f";
|
|
|
|
};
|
|
|
|
|
|
|
|
UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented");
|
|
|
|
|
2019-05-14 23:59:25 +02:00
|
|
|
code.AddLine("if (alpha_test[0] != 0) {{");
|
2018-12-21 01:45:49 +01:00
|
|
|
++code.scope;
|
|
|
|
// We start on the register containing the alpha value in the first RT.
|
|
|
|
u32 current_reg = 3;
|
|
|
|
for (u32 render_target = 0; render_target < Maxwell::NumRenderTargets; ++render_target) {
|
|
|
|
// TODO(Blinkhawk): verify the behavior of alpha testing on hardware when
|
|
|
|
// multiple render targets are used.
|
|
|
|
if (header.ps.IsColorComponentOutputEnabled(render_target, 0) ||
|
|
|
|
header.ps.IsColorComponentOutputEnabled(render_target, 1) ||
|
|
|
|
header.ps.IsColorComponentOutputEnabled(render_target, 2) ||
|
|
|
|
header.ps.IsColorComponentOutputEnabled(render_target, 3)) {
|
2019-05-14 18:18:07 +02:00
|
|
|
code.AddLine("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg));
|
2018-12-21 01:45:49 +01:00
|
|
|
current_reg += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
--code.scope;
|
2019-05-14 23:59:25 +02:00
|
|
|
code.AddLine("}}");
|
2018-12-21 01:45:49 +01:00
|
|
|
|
|
|
|
// Write the color outputs using the data in the shader registers, disabled
|
|
|
|
// rendertargets/components are skipped in the register assignment.
|
|
|
|
current_reg = 0;
|
|
|
|
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,
|
|
|
|
SafeGetRegister(current_reg));
|
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-05-14 23:59:25 +02:00
|
|
|
code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1));
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
code.AddLine("return;");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2018-12-26 07:18:11 +01:00
|
|
|
std::string 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 {};
|
|
|
|
}
|
|
|
|
|
2018-12-24 06:24:38 +01:00
|
|
|
std::string EmitVertex(Operation operation) {
|
|
|
|
ASSERT_MSG(stage == ShaderStage::Geometry,
|
|
|
|
"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 {};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string EndPrimitive(Operation operation) {
|
|
|
|
ASSERT_MSG(stage == ShaderStage::Geometry,
|
|
|
|
"EndPrimitive is expected to be used in a geometry shader.");
|
|
|
|
|
|
|
|
code.AddLine("EndPrimitive();");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
std::string YNegate(Operation operation) {
|
|
|
|
// Config pack's third value is Y_NEGATE's state.
|
|
|
|
return "uintBitsToFloat(config_pack[2])";
|
|
|
|
}
|
|
|
|
|
2019-05-03 08:00:51 +02:00
|
|
|
template <u32 element>
|
|
|
|
std::string LocalInvocationId(Operation) {
|
|
|
|
return "utof(gl_LocalInvocationID"s + GetSwizzle(element) + ')';
|
|
|
|
}
|
|
|
|
|
|
|
|
template <u32 element>
|
|
|
|
std::string WorkGroupId(Operation) {
|
|
|
|
return "utof(gl_WorkGroupID"s + GetSwizzle(element) + ')';
|
|
|
|
}
|
|
|
|
|
2018-12-21 01:45:49 +01:00
|
|
|
static constexpr OperationDecompilersArray 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,
|
|
|
|
&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-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,
|
|
|
|
&GLSLDecompiler::LogicalAll2,
|
|
|
|
&GLSLDecompiler::LogicalAny2,
|
|
|
|
|
|
|
|
&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
|
|
|
|
2018-12-26 07:18:11 +01:00
|
|
|
&GLSLDecompiler::Branch,
|
|
|
|
&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>,
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetInternalFlag(InternalFlag flag) const {
|
|
|
|
constexpr std::array<const char*, 4> InternalFlagNames = {"zero_flag", "sign_flag",
|
|
|
|
"carry_flag", "overflow_flag"};
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
|
|
|
return stage == ShaderStage::Vertex ? GetNumPhysicalAttributes() : GetNumPhysicalVaryings();
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
const ShaderStage stage;
|
|
|
|
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 MAX_CONSTBUFFER_ELEMENTS {}\n"
|
|
|
|
"#define ftoi floatBitsToInt\n"
|
|
|
|
"#define ftou floatBitsToUint\n"
|
|
|
|
"#define itof intBitsToFloat\n"
|
|
|
|
"#define utof uintBitsToFloat\n\n"
|
|
|
|
"float fromHalf2(vec2 pair) {{\n"
|
|
|
|
" return utof(packHalf2x16(pair));\n"
|
|
|
|
"}}\n\n"
|
|
|
|
"vec2 toHalf2(float value) {{\n"
|
|
|
|
" return unpackHalf2x16(ftou(value));\n"
|
|
|
|
"}}\n\n"
|
|
|
|
"bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {{\n"
|
|
|
|
" 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"
|
|
|
|
"}}\n",
|
|
|
|
MAX_CONSTBUFFER_ELEMENTS);
|
2018-12-21 01:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-04-10 23:03:52 +02:00
|
|
|
ProgramResult Decompile(const Device& device, const ShaderIR& ir, Maxwell::ShaderStage stage,
|
|
|
|
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
|