|
|
@ -7,6 +7,7 @@ |
|
|
#include <string>
|
|
|
#include <string>
|
|
|
#include <string_view>
|
|
|
#include <string_view>
|
|
|
|
|
|
|
|
|
|
|
|
#include <boost/optional.hpp>
|
|
|
#include <fmt/format.h>
|
|
|
#include <fmt/format.h>
|
|
|
|
|
|
|
|
|
#include "common/assert.h"
|
|
|
#include "common/assert.h"
|
|
|
@ -29,11 +30,32 @@ using Tegra::Shader::SubOp; |
|
|
constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; |
|
|
constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; |
|
|
constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); |
|
|
constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); |
|
|
|
|
|
|
|
|
|
|
|
constexpr u32 POSITION_VARYING_LOCATION = 15; |
|
|
|
|
|
|
|
|
|
|
|
constexpr u32 MAX_GEOMETRY_BUFFERS = 6; |
|
|
|
|
|
constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested
|
|
|
|
|
|
|
|
|
class DecompileFail : public std::runtime_error { |
|
|
class DecompileFail : public std::runtime_error { |
|
|
public: |
|
|
public: |
|
|
using std::runtime_error::runtime_error; |
|
|
using std::runtime_error::runtime_error; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/// Translate topology
|
|
|
|
|
|
static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) { |
|
|
|
|
|
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: |
|
|
|
|
|
LOG_CRITICAL(Render_OpenGL, "Unknown output topology {}", static_cast<u32>(topology)); |
|
|
|
|
|
UNREACHABLE(); |
|
|
|
|
|
return "points"; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/// Describes the behaviour of code path of a given entry point and a return point.
|
|
|
/// Describes the behaviour of code path of a given entry point and a return point.
|
|
|
enum class ExitMethod { |
|
|
enum class ExitMethod { |
|
|
Undetermined, ///< Internal value. Only occur when analyzing JMP loop.
|
|
|
Undetermined, ///< Internal value. Only occur when analyzing JMP loop.
|
|
|
@ -253,8 +275,9 @@ enum class InternalFlag : u64 { |
|
|
class GLSLRegisterManager { |
|
|
class GLSLRegisterManager { |
|
|
public: |
|
|
public: |
|
|
GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, |
|
|
GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, |
|
|
const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix) |
|
|
|
|
|
: shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix} { |
|
|
|
|
|
|
|
|
const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix, |
|
|
|
|
|
const Tegra::Shader::Header& header) |
|
|
|
|
|
: shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header} { |
|
|
BuildRegisterList(); |
|
|
BuildRegisterList(); |
|
|
BuildInputList(); |
|
|
BuildInputList(); |
|
|
} |
|
|
} |
|
|
@ -358,11 +381,13 @@ public: |
|
|
* @param reg The destination register to use. |
|
|
* @param reg The destination register to use. |
|
|
* @param elem The element to use for the operation. |
|
|
* @param elem The element to use for the operation. |
|
|
* @param attribute The input attribute to use as the source value. |
|
|
* @param attribute The input attribute to use as the source value. |
|
|
|
|
|
* @param vertex The register that decides which vertex to read from (used in GS). |
|
|
*/ |
|
|
*/ |
|
|
void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute, |
|
|
void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute, |
|
|
const Tegra::Shader::IpaMode& input_mode) { |
|
|
|
|
|
|
|
|
const Tegra::Shader::IpaMode& input_mode, |
|
|
|
|
|
boost::optional<Register> vertex = {}) { |
|
|
const std::string dest = GetRegisterAsFloat(reg); |
|
|
const std::string dest = GetRegisterAsFloat(reg); |
|
|
const std::string src = GetInputAttribute(attribute, input_mode) + GetSwizzle(elem); |
|
|
|
|
|
|
|
|
const std::string src = GetInputAttribute(attribute, input_mode, vertex) + GetSwizzle(elem); |
|
|
shader.AddLine(dest + " = " + src + ';'); |
|
|
shader.AddLine(dest + " = " + src + ';'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -391,16 +416,29 @@ public: |
|
|
* are stored as floats, so this may require conversion. |
|
|
* are stored as floats, so this may require conversion. |
|
|
* @param attribute The destination output attribute. |
|
|
* @param attribute The destination output attribute. |
|
|
* @param elem The element to use for the operation. |
|
|
* @param elem The element to use for the operation. |
|
|
* @param reg The register to use as the source value. |
|
|
|
|
|
|
|
|
* @param val_reg The register to use as the source value. |
|
|
|
|
|
* @param buf_reg The register that tells which buffer to write to (used in geometry shaders). |
|
|
*/ |
|
|
*/ |
|
|
void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) { |
|
|
|
|
|
|
|
|
void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& val_reg, |
|
|
|
|
|
const Register& buf_reg) { |
|
|
const std::string dest = GetOutputAttribute(attribute); |
|
|
const std::string dest = GetOutputAttribute(attribute); |
|
|
const std::string src = GetRegisterAsFloat(reg); |
|
|
|
|
|
|
|
|
const std::string src = GetRegisterAsFloat(val_reg); |
|
|
|
|
|
|
|
|
if (!dest.empty()) { |
|
|
if (!dest.empty()) { |
|
|
// Can happen with unknown/unimplemented output attributes, in which case we ignore the
|
|
|
// Can happen with unknown/unimplemented output attributes, in which case we ignore the
|
|
|
// instruction for now.
|
|
|
// instruction for now.
|
|
|
shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); |
|
|
|
|
|
|
|
|
if (stage == Maxwell3D::Regs::ShaderStage::Geometry) { |
|
|
|
|
|
// TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry
|
|
|
|
|
|
// shader. These instructions use a dirty register as buffer index. To avoid some
|
|
|
|
|
|
// drivers from complaining for the out of boundary writes, guard them.
|
|
|
|
|
|
const std::string buf_index{"min(" + GetRegisterAsInteger(buf_reg) + ", " + |
|
|
|
|
|
std::to_string(MAX_GEOMETRY_BUFFERS - 1) + ')'}; |
|
|
|
|
|
shader.AddLine("amem[" + buf_index + "][" + |
|
|
|
|
|
std::to_string(static_cast<u32>(attribute)) + ']' + |
|
|
|
|
|
GetSwizzle(elem) + " = " + src + ';'); |
|
|
|
|
|
} else { |
|
|
|
|
|
shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -441,41 +479,119 @@ public: |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// Add declarations for registers
|
|
|
|
|
|
|
|
|
/// Add declarations.
|
|
|
void GenerateDeclarations(const std::string& suffix) { |
|
|
void GenerateDeclarations(const std::string& suffix) { |
|
|
|
|
|
GenerateRegisters(suffix); |
|
|
|
|
|
GenerateInternalFlags(); |
|
|
|
|
|
GenerateInputAttrs(); |
|
|
|
|
|
GenerateOutputAttrs(); |
|
|
|
|
|
GenerateConstBuffers(); |
|
|
|
|
|
GenerateSamplers(); |
|
|
|
|
|
GenerateGeometry(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns a list of constant buffer declarations.
|
|
|
|
|
|
std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const { |
|
|
|
|
|
std::vector<ConstBufferEntry> result; |
|
|
|
|
|
std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(), |
|
|
|
|
|
std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); }); |
|
|
|
|
|
return result; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns a list of samplers used in the shader.
|
|
|
|
|
|
const std::vector<SamplerEntry>& GetSamplers() const { |
|
|
|
|
|
return used_samplers; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
|
|
|
|
|
|
/// necessary.
|
|
|
|
|
|
std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type, |
|
|
|
|
|
bool is_array, bool is_shadow) { |
|
|
|
|
|
const auto offset = static_cast<std::size_t>(sampler.index.Value()); |
|
|
|
|
|
|
|
|
|
|
|
// If this sampler has already been used, return the existing mapping.
|
|
|
|
|
|
const auto itr = |
|
|
|
|
|
std::find_if(used_samplers.begin(), used_samplers.end(), |
|
|
|
|
|
[&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); |
|
|
|
|
|
|
|
|
|
|
|
if (itr != used_samplers.end()) { |
|
|
|
|
|
ASSERT(itr->GetType() == type && itr->IsArray() == is_array && |
|
|
|
|
|
itr->IsShadow() == is_shadow); |
|
|
|
|
|
return itr->GetName(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Otherwise create a new mapping for this sampler
|
|
|
|
|
|
const std::size_t next_index = used_samplers.size(); |
|
|
|
|
|
const SamplerEntry entry{stage, offset, next_index, type, is_array, is_shadow}; |
|
|
|
|
|
used_samplers.emplace_back(entry); |
|
|
|
|
|
return entry.GetName(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
|
/// Generates declarations for registers.
|
|
|
|
|
|
void GenerateRegisters(const std::string& suffix) { |
|
|
for (const auto& reg : regs) { |
|
|
for (const auto& reg : regs) { |
|
|
declarations.AddLine(GLSLRegister::GetTypeString() + ' ' + reg.GetPrefixString() + |
|
|
declarations.AddLine(GLSLRegister::GetTypeString() + ' ' + reg.GetPrefixString() + |
|
|
std::to_string(reg.GetIndex()) + '_' + suffix + " = 0;"); |
|
|
std::to_string(reg.GetIndex()) + '_' + suffix + " = 0;"); |
|
|
} |
|
|
} |
|
|
declarations.AddNewLine(); |
|
|
declarations.AddNewLine(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Generates declarations for internal flags.
|
|
|
|
|
|
void GenerateInternalFlags() { |
|
|
for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { |
|
|
for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { |
|
|
const InternalFlag code = static_cast<InternalFlag>(ii); |
|
|
const InternalFlag code = static_cast<InternalFlag>(ii); |
|
|
declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); |
|
|
declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); |
|
|
} |
|
|
} |
|
|
declarations.AddNewLine(); |
|
|
declarations.AddNewLine(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Generates declarations for input attributes.
|
|
|
|
|
|
void GenerateInputAttrs() { |
|
|
|
|
|
if (stage != Maxwell3D::Regs::ShaderStage::Vertex) { |
|
|
|
|
|
const std::string attr = |
|
|
|
|
|
stage == Maxwell3D::Regs::ShaderStage::Geometry ? "gs_position[]" : "position"; |
|
|
|
|
|
declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) + |
|
|
|
|
|
") in vec4 " + attr + ';'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
for (const auto element : declr_input_attribute) { |
|
|
for (const auto element : declr_input_attribute) { |
|
|
// TODO(bunnei): Use proper number of elements for these
|
|
|
// TODO(bunnei): Use proper number of elements for these
|
|
|
u32 idx = |
|
|
u32 idx = |
|
|
static_cast<u32>(element.first) - static_cast<u32>(Attribute::Index::Attribute_0); |
|
|
static_cast<u32>(element.first) - static_cast<u32>(Attribute::Index::Attribute_0); |
|
|
declarations.AddLine("layout(location = " + std::to_string(idx) + ")" + |
|
|
|
|
|
GetInputFlags(element.first) + "in vec4 " + |
|
|
|
|
|
GetInputAttribute(element.first, element.second) + ';'); |
|
|
|
|
|
|
|
|
ASSERT(idx != POSITION_VARYING_LOCATION); |
|
|
|
|
|
|
|
|
|
|
|
std::string attr{GetInputAttribute(element.first, element.second)}; |
|
|
|
|
|
if (stage == Maxwell3D::Regs::ShaderStage::Geometry) { |
|
|
|
|
|
attr = "gs_" + attr + "[]"; |
|
|
|
|
|
} |
|
|
|
|
|
declarations.AddLine("layout (location = " + std::to_string(idx) + ") " + |
|
|
|
|
|
GetInputFlags(element.first) + "in vec4 " + attr + ';'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
declarations.AddNewLine(); |
|
|
declarations.AddNewLine(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Generates declarations for output attributes.
|
|
|
|
|
|
void GenerateOutputAttrs() { |
|
|
|
|
|
if (stage != Maxwell3D::Regs::ShaderStage::Fragment) { |
|
|
|
|
|
declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) + |
|
|
|
|
|
") out vec4 position;"); |
|
|
|
|
|
} |
|
|
for (const auto& index : declr_output_attribute) { |
|
|
for (const auto& index : declr_output_attribute) { |
|
|
// TODO(bunnei): Use proper number of elements for these
|
|
|
// TODO(bunnei): Use proper number of elements for these
|
|
|
declarations.AddLine("layout(location = " + |
|
|
|
|
|
|
|
|
declarations.AddLine("layout (location = " + |
|
|
std::to_string(static_cast<u32>(index) - |
|
|
std::to_string(static_cast<u32>(index) - |
|
|
static_cast<u32>(Attribute::Index::Attribute_0)) + |
|
|
static_cast<u32>(Attribute::Index::Attribute_0)) + |
|
|
") out vec4 " + GetOutputAttribute(index) + ';'); |
|
|
") out vec4 " + GetOutputAttribute(index) + ';'); |
|
|
} |
|
|
} |
|
|
declarations.AddNewLine(); |
|
|
declarations.AddNewLine(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Generates declarations for constant buffers.
|
|
|
|
|
|
void GenerateConstBuffers() { |
|
|
for (const auto& entry : GetConstBuffersDeclarations()) { |
|
|
for (const auto& entry : GetConstBuffersDeclarations()) { |
|
|
declarations.AddLine("layout(std140) uniform " + entry.GetName()); |
|
|
|
|
|
|
|
|
declarations.AddLine("layout (std140) uniform " + entry.GetName()); |
|
|
declarations.AddLine('{'); |
|
|
declarations.AddLine('{'); |
|
|
declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) + |
|
|
declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) + |
|
|
"[MAX_CONSTBUFFER_ELEMENTS];"); |
|
|
"[MAX_CONSTBUFFER_ELEMENTS];"); |
|
|
@ -483,7 +599,10 @@ public: |
|
|
declarations.AddNewLine(); |
|
|
declarations.AddNewLine(); |
|
|
} |
|
|
} |
|
|
declarations.AddNewLine(); |
|
|
declarations.AddNewLine(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Generates declarations for samplers.
|
|
|
|
|
|
void GenerateSamplers() { |
|
|
const auto& samplers = GetSamplers(); |
|
|
const auto& samplers = GetSamplers(); |
|
|
for (const auto& sampler : samplers) { |
|
|
for (const auto& sampler : samplers) { |
|
|
declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() + |
|
|
declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() + |
|
|
@ -492,44 +611,42 @@ public: |
|
|
declarations.AddNewLine(); |
|
|
declarations.AddNewLine(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// Returns a list of constant buffer declarations
|
|
|
|
|
|
std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const { |
|
|
|
|
|
std::vector<ConstBufferEntry> result; |
|
|
|
|
|
std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(), |
|
|
|
|
|
std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); }); |
|
|
|
|
|
return result; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns a list of samplers used in the shader
|
|
|
|
|
|
const std::vector<SamplerEntry>& GetSamplers() const { |
|
|
|
|
|
return used_samplers; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
|
|
|
|
|
|
/// necessary.
|
|
|
|
|
|
std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type, |
|
|
|
|
|
bool is_array, bool is_shadow) { |
|
|
|
|
|
const std::size_t offset = static_cast<std::size_t>(sampler.index.Value()); |
|
|
|
|
|
|
|
|
/// Generates declarations used for geometry shaders.
|
|
|
|
|
|
void GenerateGeometry() { |
|
|
|
|
|
if (stage != Maxwell3D::Regs::ShaderStage::Geometry) |
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
// If this sampler has already been used, return the existing mapping.
|
|
|
|
|
|
const auto itr = |
|
|
|
|
|
std::find_if(used_samplers.begin(), used_samplers.end(), |
|
|
|
|
|
[&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); |
|
|
|
|
|
|
|
|
declarations.AddLine( |
|
|
|
|
|
"layout (" + GetTopologyName(header.common3.output_topology) + |
|
|
|
|
|
", max_vertices = " + std::to_string(header.common4.max_output_vertices) + ") out;"); |
|
|
|
|
|
declarations.AddNewLine(); |
|
|
|
|
|
|
|
|
if (itr != used_samplers.end()) { |
|
|
|
|
|
ASSERT(itr->GetType() == type && itr->IsArray() == is_array && |
|
|
|
|
|
itr->IsShadow() == is_shadow); |
|
|
|
|
|
return itr->GetName(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
declarations.AddLine("vec4 amem[" + std::to_string(MAX_GEOMETRY_BUFFERS) + "][" + |
|
|
|
|
|
std::to_string(MAX_ATTRIBUTES) + "];"); |
|
|
|
|
|
declarations.AddNewLine(); |
|
|
|
|
|
|
|
|
// Otherwise create a new mapping for this sampler
|
|
|
|
|
|
const std::size_t next_index = used_samplers.size(); |
|
|
|
|
|
const SamplerEntry entry{stage, offset, next_index, type, is_array, is_shadow}; |
|
|
|
|
|
used_samplers.emplace_back(entry); |
|
|
|
|
|
return entry.GetName(); |
|
|
|
|
|
|
|
|
constexpr char buffer[] = "amem[output_buffer]"; |
|
|
|
|
|
declarations.AddLine("void emit_vertex(uint output_buffer) {"); |
|
|
|
|
|
++declarations.scope; |
|
|
|
|
|
for (const auto element : declr_output_attribute) { |
|
|
|
|
|
declarations.AddLine(GetOutputAttribute(element) + " = " + buffer + '[' + |
|
|
|
|
|
std::to_string(static_cast<u32>(element)) + "];"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
declarations.AddLine("position = " + std::string(buffer) + '[' + |
|
|
|
|
|
std::to_string(static_cast<u32>(Attribute::Index::Position)) + "];"); |
|
|
|
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
declarations.AddLine("position.xy *= viewport_flip.xy;"); |
|
|
|
|
|
declarations.AddLine("gl_Position = position;"); |
|
|
|
|
|
declarations.AddLine("position.w = 1.0;"); |
|
|
|
|
|
declarations.AddLine("EmitVertex();"); |
|
|
|
|
|
--declarations.scope; |
|
|
|
|
|
declarations.AddLine('}'); |
|
|
|
|
|
declarations.AddNewLine(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private: |
|
|
|
|
|
/// Generates code representing a temporary (GPR) register.
|
|
|
/// Generates code representing a temporary (GPR) register.
|
|
|
std::string GetRegister(const Register& reg, unsigned elem) { |
|
|
std::string GetRegister(const Register& reg, unsigned elem) { |
|
|
if (reg == Register::ZeroIndex) { |
|
|
if (reg == Register::ZeroIndex) { |
|
|
@ -586,11 +703,19 @@ private: |
|
|
|
|
|
|
|
|
/// Generates code representing an input attribute register.
|
|
|
/// Generates code representing an input attribute register.
|
|
|
std::string GetInputAttribute(Attribute::Index attribute, |
|
|
std::string GetInputAttribute(Attribute::Index attribute, |
|
|
const Tegra::Shader::IpaMode& input_mode) { |
|
|
|
|
|
|
|
|
const Tegra::Shader::IpaMode& input_mode, |
|
|
|
|
|
boost::optional<Register> vertex = {}) { |
|
|
|
|
|
auto GeometryPass = [&](const std::string& name) { |
|
|
|
|
|
if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) { |
|
|
|
|
|
return "gs_" + name + '[' + GetRegisterAsInteger(vertex.value(), 0, false) + ']'; |
|
|
|
|
|
} |
|
|
|
|
|
return name; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
switch (attribute) { |
|
|
switch (attribute) { |
|
|
case Attribute::Index::Position: |
|
|
case Attribute::Index::Position: |
|
|
if (stage != Maxwell3D::Regs::ShaderStage::Fragment) { |
|
|
if (stage != Maxwell3D::Regs::ShaderStage::Fragment) { |
|
|
return "position"; |
|
|
|
|
|
|
|
|
return GeometryPass("position"); |
|
|
} else { |
|
|
} else { |
|
|
return "vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)"; |
|
|
return "vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)"; |
|
|
} |
|
|
} |
|
|
@ -619,7 +744,7 @@ private: |
|
|
UNREACHABLE(); |
|
|
UNREACHABLE(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
return "input_attribute_" + std::to_string(index); |
|
|
|
|
|
|
|
|
return GeometryPass("input_attribute_" + std::to_string(index)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute)); |
|
|
LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute)); |
|
|
@ -672,7 +797,7 @@ private: |
|
|
return out; |
|
|
return out; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// Generates code representing an output attribute register.
|
|
|
|
|
|
|
|
|
/// Generates code representing the declaration name of an output attribute register.
|
|
|
std::string GetOutputAttribute(Attribute::Index attribute) { |
|
|
std::string GetOutputAttribute(Attribute::Index attribute) { |
|
|
switch (attribute) { |
|
|
switch (attribute) { |
|
|
case Attribute::Index::Position: |
|
|
case Attribute::Index::Position: |
|
|
@ -708,6 +833,7 @@ private: |
|
|
std::vector<SamplerEntry> used_samplers; |
|
|
std::vector<SamplerEntry> used_samplers; |
|
|
const Maxwell3D::Regs::ShaderStage& stage; |
|
|
const Maxwell3D::Regs::ShaderStage& stage; |
|
|
const std::string& suffix; |
|
|
const std::string& suffix; |
|
|
|
|
|
const Tegra::Shader::Header& header; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
class GLSLGenerator { |
|
|
class GLSLGenerator { |
|
|
@ -1103,8 +1229,8 @@ private: |
|
|
return offset + 1; |
|
|
return offset + 1; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName() + " (" + |
|
|
|
|
|
std::to_string(instr.value) + ')'); |
|
|
|
|
|
|
|
|
shader.AddLine( |
|
|
|
|
|
fmt::format("// {}: {} (0x{:016x})", offset, opcode->GetName(), instr.value)); |
|
|
|
|
|
|
|
|
using Tegra::Shader::Pred; |
|
|
using Tegra::Shader::Pred; |
|
|
ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, |
|
|
ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, |
|
|
@ -1826,7 +1952,7 @@ private: |
|
|
const auto LoadNextElement = [&](u32 reg_offset) { |
|
|
const auto LoadNextElement = [&](u32 reg_offset) { |
|
|
regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element, |
|
|
regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element, |
|
|
static_cast<Attribute::Index>(next_index), |
|
|
static_cast<Attribute::Index>(next_index), |
|
|
input_mode); |
|
|
|
|
|
|
|
|
input_mode, instr.gpr39.Value()); |
|
|
|
|
|
|
|
|
// Load the next attribute element into the following register. If the element
|
|
|
// Load the next attribute element into the following register. If the element
|
|
|
// to load goes beyond the vec4 size, load the first element of the next
|
|
|
// to load goes beyond the vec4 size, load the first element of the next
|
|
|
@ -1890,8 +2016,8 @@ private: |
|
|
|
|
|
|
|
|
const auto StoreNextElement = [&](u32 reg_offset) { |
|
|
const auto StoreNextElement = [&](u32 reg_offset) { |
|
|
regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index), |
|
|
regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index), |
|
|
next_element, |
|
|
|
|
|
instr.gpr0.Value() + reg_offset); |
|
|
|
|
|
|
|
|
next_element, instr.gpr0.Value() + reg_offset, |
|
|
|
|
|
instr.gpr39.Value()); |
|
|
|
|
|
|
|
|
// Load the next attribute element into the following register. If the element
|
|
|
// Load the next attribute element into the following register. If the element
|
|
|
// to load goes beyond the vec4 size, load the first element of the next
|
|
|
// to load goes beyond the vec4 size, load the first element of the next
|
|
|
@ -2738,6 +2864,52 @@ private: |
|
|
|
|
|
|
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
case OpCode::Id::OUT_R: { |
|
|
|
|
|
ASSERT(instr.gpr20.Value() == Register::ZeroIndex); |
|
|
|
|
|
ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, |
|
|
|
|
|
"OUT is expected to be used in a geometry shader."); |
|
|
|
|
|
|
|
|
|
|
|
if (instr.out.emit) { |
|
|
|
|
|
// gpr0 is used to store the next address. Hardware returns a pointer but
|
|
|
|
|
|
// we just return the next index with a cyclic cap.
|
|
|
|
|
|
const std::string current{regs.GetRegisterAsInteger(instr.gpr8, 0, false)}; |
|
|
|
|
|
const std::string next = "((" + current + " + 1" + ") % " + |
|
|
|
|
|
std::to_string(MAX_GEOMETRY_BUFFERS) + ')'; |
|
|
|
|
|
shader.AddLine("emit_vertex(" + current + ");"); |
|
|
|
|
|
regs.SetRegisterToInteger(instr.gpr0, false, 0, next, 1, 1); |
|
|
|
|
|
} |
|
|
|
|
|
if (instr.out.cut) { |
|
|
|
|
|
shader.AddLine("EndPrimitive();"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
case OpCode::Id::MOV_SYS: { |
|
|
|
|
|
switch (instr.sys20) { |
|
|
|
|
|
case Tegra::Shader::SystemVariable::InvocationInfo: { |
|
|
|
|
|
LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete"); |
|
|
|
|
|
regs.SetRegisterToInteger(instr.gpr0, false, 0, "0u", 1, 1); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
default: { |
|
|
|
|
|
LOG_CRITICAL(HW_GPU, "Unhandled system move: {}", |
|
|
|
|
|
static_cast<u32>(instr.sys20.Value())); |
|
|
|
|
|
UNREACHABLE(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
case OpCode::Id::ISBERD: { |
|
|
|
|
|
ASSERT(instr.isberd.o == 0); |
|
|
|
|
|
ASSERT(instr.isberd.skew == 0); |
|
|
|
|
|
ASSERT(instr.isberd.shift == Tegra::Shader::IsberdShift::None); |
|
|
|
|
|
ASSERT(instr.isberd.mode == Tegra::Shader::IsberdMode::None); |
|
|
|
|
|
ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry, |
|
|
|
|
|
"ISBERD is expected to be used in a geometry shader."); |
|
|
|
|
|
LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete"); |
|
|
|
|
|
regs.SetRegisterToFloat(instr.gpr0, 0, regs.GetRegisterAsFloat(instr.gpr8), 1, 1); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
case OpCode::Id::BRA: { |
|
|
case OpCode::Id::BRA: { |
|
|
ASSERT_MSG(instr.bra.constant_buffer == 0, |
|
|
ASSERT_MSG(instr.bra.constant_buffer == 0, |
|
|
"BRA with constant buffers are not implemented"); |
|
|
"BRA with constant buffers are not implemented"); |
|
|
@ -2911,7 +3083,7 @@ private: |
|
|
|
|
|
|
|
|
ShaderWriter shader; |
|
|
ShaderWriter shader; |
|
|
ShaderWriter declarations; |
|
|
ShaderWriter declarations; |
|
|
GLSLRegisterManager regs{shader, declarations, stage, suffix}; |
|
|
|
|
|
|
|
|
GLSLRegisterManager regs{shader, declarations, stage, suffix, header}; |
|
|
|
|
|
|
|
|
// Declarations
|
|
|
// Declarations
|
|
|
std::set<std::string> declr_predicates; |
|
|
std::set<std::string> declr_predicates; |
|
|
|