mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-07-04 23:31:19 +01:00
Merge pull request #1425 from ReinUsesLisp/geometry-shaders
gl_shader_decompiler: Implement geometry shaders
This commit is contained in:
commit
ee1b204749
11 changed files with 554 additions and 131 deletions
|
@ -314,6 +314,15 @@ enum class TextureMiscMode : u64 {
|
|||
PTP,
|
||||
};
|
||||
|
||||
enum class IsberdMode : u64 {
|
||||
None = 0,
|
||||
Patch = 1,
|
||||
Prim = 2,
|
||||
Attr = 3,
|
||||
};
|
||||
|
||||
enum class IsberdShift : u64 { None = 0, U16 = 1, B32 = 2 };
|
||||
|
||||
enum class IpaInterpMode : u64 {
|
||||
Linear = 0,
|
||||
Perspective = 1,
|
||||
|
@ -340,6 +349,87 @@ struct IpaMode {
|
|||
}
|
||||
};
|
||||
|
||||
enum class SystemVariable : u64 {
|
||||
LaneId = 0x00,
|
||||
VirtCfg = 0x02,
|
||||
VirtId = 0x03,
|
||||
Pm0 = 0x04,
|
||||
Pm1 = 0x05,
|
||||
Pm2 = 0x06,
|
||||
Pm3 = 0x07,
|
||||
Pm4 = 0x08,
|
||||
Pm5 = 0x09,
|
||||
Pm6 = 0x0a,
|
||||
Pm7 = 0x0b,
|
||||
OrderingTicket = 0x0f,
|
||||
PrimType = 0x10,
|
||||
InvocationId = 0x11,
|
||||
Ydirection = 0x12,
|
||||
ThreadKill = 0x13,
|
||||
ShaderType = 0x14,
|
||||
DirectBeWriteAddressLow = 0x15,
|
||||
DirectBeWriteAddressHigh = 0x16,
|
||||
DirectBeWriteEnabled = 0x17,
|
||||
MachineId0 = 0x18,
|
||||
MachineId1 = 0x19,
|
||||
MachineId2 = 0x1a,
|
||||
MachineId3 = 0x1b,
|
||||
Affinity = 0x1c,
|
||||
InvocationInfo = 0x1d,
|
||||
WscaleFactorXY = 0x1e,
|
||||
WscaleFactorZ = 0x1f,
|
||||
Tid = 0x20,
|
||||
TidX = 0x21,
|
||||
TidY = 0x22,
|
||||
TidZ = 0x23,
|
||||
CtaParam = 0x24,
|
||||
CtaIdX = 0x25,
|
||||
CtaIdY = 0x26,
|
||||
CtaIdZ = 0x27,
|
||||
NtId = 0x28,
|
||||
CirQueueIncrMinusOne = 0x29,
|
||||
Nlatc = 0x2a,
|
||||
SmSpaVersion = 0x2c,
|
||||
MultiPassShaderInfo = 0x2d,
|
||||
LwinHi = 0x2e,
|
||||
SwinHi = 0x2f,
|
||||
SwinLo = 0x30,
|
||||
SwinSz = 0x31,
|
||||
SmemSz = 0x32,
|
||||
SmemBanks = 0x33,
|
||||
LwinLo = 0x34,
|
||||
LwinSz = 0x35,
|
||||
LmemLosz = 0x36,
|
||||
LmemHioff = 0x37,
|
||||
EqMask = 0x38,
|
||||
LtMask = 0x39,
|
||||
LeMask = 0x3a,
|
||||
GtMask = 0x3b,
|
||||
GeMask = 0x3c,
|
||||
RegAlloc = 0x3d,
|
||||
CtxAddr = 0x3e, // .fmask = F_SM50
|
||||
BarrierAlloc = 0x3e, // .fmask = F_SM60
|
||||
GlobalErrorStatus = 0x40,
|
||||
WarpErrorStatus = 0x42,
|
||||
WarpErrorStatusClear = 0x43,
|
||||
PmHi0 = 0x48,
|
||||
PmHi1 = 0x49,
|
||||
PmHi2 = 0x4a,
|
||||
PmHi3 = 0x4b,
|
||||
PmHi4 = 0x4c,
|
||||
PmHi5 = 0x4d,
|
||||
PmHi6 = 0x4e,
|
||||
PmHi7 = 0x4f,
|
||||
ClockLo = 0x50,
|
||||
ClockHi = 0x51,
|
||||
GlobalTimerLo = 0x52,
|
||||
GlobalTimerHi = 0x53,
|
||||
HwTaskId = 0x60,
|
||||
CircularQueueEntryIndex = 0x61,
|
||||
CircularQueueEntryAddressLow = 0x62,
|
||||
CircularQueueEntryAddressHigh = 0x63,
|
||||
};
|
||||
|
||||
union Instruction {
|
||||
Instruction& operator=(const Instruction& instr) {
|
||||
value = instr.value;
|
||||
|
@ -914,6 +1004,18 @@ union Instruction {
|
|||
}
|
||||
} bra;
|
||||
|
||||
union {
|
||||
BitField<39, 1, u64> emit; // EmitVertex
|
||||
BitField<40, 1, u64> cut; // EndPrimitive
|
||||
} out;
|
||||
|
||||
union {
|
||||
BitField<31, 1, u64> skew;
|
||||
BitField<32, 1, u64> o;
|
||||
BitField<33, 2, IsberdMode> mode;
|
||||
BitField<47, 2, IsberdShift> shift;
|
||||
} isberd;
|
||||
|
||||
union {
|
||||
BitField<20, 16, u64> imm20_16;
|
||||
BitField<36, 1, u64> product_shift_left;
|
||||
|
@ -936,6 +1038,10 @@ union Instruction {
|
|||
BitField<36, 5, u64> index;
|
||||
} cbuf36;
|
||||
|
||||
// Unsure about the size of this one.
|
||||
// It's always used with a gpr0, so any size should be fine.
|
||||
BitField<20, 8, SystemVariable> sys20;
|
||||
|
||||
BitField<47, 1, u64> generates_cc;
|
||||
BitField<61, 1, u64> is_b_imm;
|
||||
BitField<60, 1, u64> is_b_gpr;
|
||||
|
@ -975,6 +1081,8 @@ public:
|
|||
TMML, // Texture Mip Map Level
|
||||
EXIT,
|
||||
IPA,
|
||||
OUT_R, // Emit vertex/primitive
|
||||
ISBERD,
|
||||
FFMA_IMM, // Fused Multiply and Add
|
||||
FFMA_CR,
|
||||
FFMA_RC,
|
||||
|
@ -1034,6 +1142,7 @@ public:
|
|||
MOV_C,
|
||||
MOV_R,
|
||||
MOV_IMM,
|
||||
MOV_SYS,
|
||||
MOV32_IMM,
|
||||
SHL_C,
|
||||
SHL_R,
|
||||
|
@ -1209,6 +1318,8 @@ private:
|
|||
INST("1101111101011---", Id::TMML, Type::Memory, "TMML"),
|
||||
INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
|
||||
INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
|
||||
INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
|
||||
INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
|
||||
INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
|
||||
INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
|
||||
INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
|
||||
|
@ -1255,6 +1366,7 @@ private:
|
|||
INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"),
|
||||
INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"),
|
||||
INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"),
|
||||
INST("1111000011001---", Id::MOV_SYS, Type::Trivial, "MOV_SYS"),
|
||||
INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"),
|
||||
INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"),
|
||||
INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"),
|
||||
|
|
|
@ -255,7 +255,7 @@ DrawParameters RasterizerOpenGL::SetupDraw() {
|
|||
return params;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupShaders() {
|
||||
void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Shader);
|
||||
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
|
||||
|
@ -270,6 +270,11 @@ void RasterizerOpenGL::SetupShaders() {
|
|||
|
||||
// Skip stages that are not enabled
|
||||
if (!gpu.regs.IsShaderConfigEnabled(index)) {
|
||||
switch (program) {
|
||||
case Maxwell::ShaderProgram::Geometry:
|
||||
shader_program_manager->UseTrivialGeometryShader();
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -288,11 +293,18 @@ void RasterizerOpenGL::SetupShaders() {
|
|||
switch (program) {
|
||||
case Maxwell::ShaderProgram::VertexA:
|
||||
case Maxwell::ShaderProgram::VertexB: {
|
||||
shader_program_manager->UseProgrammableVertexShader(shader->GetProgramHandle());
|
||||
shader_program_manager->UseProgrammableVertexShader(
|
||||
shader->GetProgramHandle(primitive_mode));
|
||||
break;
|
||||
}
|
||||
case Maxwell::ShaderProgram::Geometry: {
|
||||
shader_program_manager->UseProgrammableGeometryShader(
|
||||
shader->GetProgramHandle(primitive_mode));
|
||||
break;
|
||||
}
|
||||
case Maxwell::ShaderProgram::Fragment: {
|
||||
shader_program_manager->UseProgrammableFragmentShader(shader->GetProgramHandle());
|
||||
shader_program_manager->UseProgrammableFragmentShader(
|
||||
shader->GetProgramHandle(primitive_mode));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -302,12 +314,13 @@ void RasterizerOpenGL::SetupShaders() {
|
|||
}
|
||||
|
||||
// Configure the const buffers for this shader stage.
|
||||
current_constbuffer_bindpoint = SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage),
|
||||
shader, current_constbuffer_bindpoint);
|
||||
current_constbuffer_bindpoint =
|
||||
SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), shader, primitive_mode,
|
||||
current_constbuffer_bindpoint);
|
||||
|
||||
// Configure the textures for this shader stage.
|
||||
current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader,
|
||||
current_texture_bindpoint);
|
||||
primitive_mode, current_texture_bindpoint);
|
||||
|
||||
// When VertexA is enabled, we have dual vertex shaders
|
||||
if (program == Maxwell::ShaderProgram::VertexA) {
|
||||
|
@ -317,8 +330,6 @@ void RasterizerOpenGL::SetupShaders() {
|
|||
}
|
||||
|
||||
state.Apply();
|
||||
|
||||
shader_program_manager->UseTrivialGeometryShader();
|
||||
}
|
||||
|
||||
std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
|
||||
|
@ -580,7 +591,7 @@ void RasterizerOpenGL::DrawArrays() {
|
|||
|
||||
SetupVertexArrays();
|
||||
DrawParameters params = SetupDraw();
|
||||
SetupShaders();
|
||||
SetupShaders(params.primitive_mode);
|
||||
|
||||
buffer_cache.Unmap();
|
||||
|
||||
|
@ -719,7 +730,7 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
|
|||
}
|
||||
|
||||
u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
|
||||
u32 current_bindpoint) {
|
||||
GLenum primitive_mode, u32 current_bindpoint) {
|
||||
MICROPROFILE_SCOPE(OpenGL_UBO);
|
||||
const auto& gpu = Core::System::GetInstance().GPU();
|
||||
const auto& maxwell3d = gpu.Maxwell3D();
|
||||
|
@ -771,7 +782,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad
|
|||
buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment));
|
||||
|
||||
// Now configure the bindpoint of the buffer inside the shader
|
||||
glUniformBlockBinding(shader->GetProgramHandle(),
|
||||
glUniformBlockBinding(shader->GetProgramHandle(primitive_mode),
|
||||
shader->GetProgramResourceIndex(used_buffer),
|
||||
current_bindpoint + bindpoint);
|
||||
|
||||
|
@ -787,7 +798,8 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad
|
|||
return current_bindpoint + static_cast<u32>(entries.size());
|
||||
}
|
||||
|
||||
u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, u32 current_unit) {
|
||||
u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
|
||||
GLenum primitive_mode, u32 current_unit) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Texture);
|
||||
const auto& gpu = Core::System::GetInstance().GPU();
|
||||
const auto& maxwell3d = gpu.Maxwell3D();
|
||||
|
@ -802,8 +814,8 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
|
|||
|
||||
// Bind the uniform to the sampler.
|
||||
|
||||
glProgramUniform1i(shader->GetProgramHandle(), shader->GetUniformLocation(entry),
|
||||
current_bindpoint);
|
||||
glProgramUniform1i(shader->GetProgramHandle(primitive_mode),
|
||||
shader->GetUniformLocation(entry), current_bindpoint);
|
||||
|
||||
const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ private:
|
|||
* @returns The next available bindpoint for use in the next shader stage.
|
||||
*/
|
||||
u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
|
||||
u32 current_bindpoint);
|
||||
GLenum primitive_mode, u32 current_bindpoint);
|
||||
|
||||
/*
|
||||
* Configures the current textures to use for the draw command.
|
||||
|
@ -130,7 +130,7 @@ private:
|
|||
* @returns The next available bindpoint for use in the next shader stage.
|
||||
*/
|
||||
u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
|
||||
u32 current_unit);
|
||||
GLenum primitive_mode, u32 current_unit);
|
||||
|
||||
/// Syncs the viewport to match the guest state
|
||||
void SyncViewport();
|
||||
|
@ -207,7 +207,7 @@ private:
|
|||
|
||||
DrawParameters SetupDraw();
|
||||
|
||||
void SetupShaders();
|
||||
void SetupShaders(GLenum primitive_mode);
|
||||
|
||||
enum class AccelDraw { Disabled, Arrays, Indexed };
|
||||
AccelDraw accelerate_draw = AccelDraw::Disabled;
|
||||
|
|
|
@ -68,6 +68,10 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
|
|||
program_result = GLShader::GenerateVertexShader(setup);
|
||||
gl_type = GL_VERTEX_SHADER;
|
||||
break;
|
||||
case Maxwell::ShaderProgram::Geometry:
|
||||
program_result = GLShader::GenerateGeometryShader(setup);
|
||||
gl_type = GL_GEOMETRY_SHADER;
|
||||
break;
|
||||
case Maxwell::ShaderProgram::Fragment:
|
||||
program_result = GLShader::GenerateFragmentShader(setup);
|
||||
gl_type = GL_FRAGMENT_SHADER;
|
||||
|
@ -80,11 +84,16 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
|
|||
|
||||
entries = program_result.second;
|
||||
|
||||
OGLShader shader;
|
||||
shader.Create(program_result.first.c_str(), gl_type);
|
||||
program.Create(true, shader.handle);
|
||||
SetShaderUniformBlockBindings(program.handle);
|
||||
VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr);
|
||||
if (program_type != Maxwell::ShaderProgram::Geometry) {
|
||||
OGLShader shader;
|
||||
shader.Create(program_result.first.c_str(), gl_type);
|
||||
program.Create(true, shader.handle);
|
||||
SetShaderUniformBlockBindings(program.handle);
|
||||
VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr);
|
||||
} else {
|
||||
// Store shader's code to lazily build it on draw
|
||||
geometry_programs.code = program_result.first;
|
||||
}
|
||||
}
|
||||
|
||||
GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) {
|
||||
|
@ -110,6 +119,21 @@ GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
|
|||
return search->second;
|
||||
}
|
||||
|
||||
GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program,
|
||||
const std::string& glsl_topology,
|
||||
const std::string& debug_name) {
|
||||
if (target_program.handle != 0) {
|
||||
return target_program.handle;
|
||||
}
|
||||
const std::string source{geometry_programs.code + "layout (" + glsl_topology + ") in;\n"};
|
||||
OGLShader shader;
|
||||
shader.Create(source.c_str(), GL_GEOMETRY_SHADER);
|
||||
target_program.Create(true, shader.handle);
|
||||
SetShaderUniformBlockBindings(target_program.handle);
|
||||
VideoCore::LabelGLObject(GL_PROGRAM, target_program.handle, addr, debug_name);
|
||||
return target_program.handle;
|
||||
};
|
||||
|
||||
Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
|
||||
const VAddr program_addr{GetShaderAddress(program)};
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
|
@ -38,8 +39,31 @@ public:
|
|||
}
|
||||
|
||||
/// Gets the GL program handle for the shader
|
||||
GLuint GetProgramHandle() const {
|
||||
return program.handle;
|
||||
GLuint GetProgramHandle(GLenum primitive_mode) {
|
||||
if (program_type != Maxwell::ShaderProgram::Geometry) {
|
||||
return program.handle;
|
||||
}
|
||||
switch (primitive_mode) {
|
||||
case GL_POINTS:
|
||||
return LazyGeometryProgram(geometry_programs.points, "points", "ShaderPoints");
|
||||
case GL_LINES:
|
||||
case GL_LINE_STRIP:
|
||||
return LazyGeometryProgram(geometry_programs.lines, "lines", "ShaderLines");
|
||||
case GL_LINES_ADJACENCY:
|
||||
case GL_LINE_STRIP_ADJACENCY:
|
||||
return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency",
|
||||
"ShaderLinesAdjacency");
|
||||
case GL_TRIANGLES:
|
||||
case GL_TRIANGLE_STRIP:
|
||||
case GL_TRIANGLE_FAN:
|
||||
return LazyGeometryProgram(geometry_programs.triangles, "triangles", "ShaderTriangles");
|
||||
case GL_TRIANGLES_ADJACENCY:
|
||||
case GL_TRIANGLE_STRIP_ADJACENCY:
|
||||
return LazyGeometryProgram(geometry_programs.triangles_adjacency, "triangles_adjacency",
|
||||
"ShaderLines");
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown primitive mode.");
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the GL program resource location for the specified resource, caching as needed
|
||||
|
@ -49,12 +73,30 @@ public:
|
|||
GLint GetUniformLocation(const GLShader::SamplerEntry& sampler);
|
||||
|
||||
private:
|
||||
/// Generates a geometry shader or returns one that already exists.
|
||||
GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology,
|
||||
const std::string& debug_name);
|
||||
|
||||
VAddr addr;
|
||||
Maxwell::ShaderProgram program_type;
|
||||
GLShader::ShaderSetup setup;
|
||||
GLShader::ShaderEntries entries;
|
||||
|
||||
// Non-geometry program.
|
||||
OGLProgram program;
|
||||
|
||||
// Geometry programs. These are needed because GLSL needs an input topology but it's not
|
||||
// declared by the hardware. Workaround this issue by generating a different shader per input
|
||||
// topology class.
|
||||
struct {
|
||||
std::string code;
|
||||
OGLProgram points;
|
||||
OGLProgram lines;
|
||||
OGLProgram lines_adjacency;
|
||||
OGLProgram triangles;
|
||||
OGLProgram triangles_adjacency;
|
||||
} geometry_programs;
|
||||
|
||||
std::map<u32, GLuint> resource_cache;
|
||||
std::map<u32, GLint> uniform_cache;
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
|
@ -29,11 +30,32 @@ using Tegra::Shader::SubOp;
|
|||
constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
|
||||
constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header);
|
||||
|
||||
enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
|
||||
|
||||
constexpr u32 MAX_GEOMETRY_BUFFERS = 6;
|
||||
constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested
|
||||
|
||||
class DecompileFail : public std::runtime_error {
|
||||
public:
|
||||
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.
|
||||
enum class ExitMethod {
|
||||
Undetermined, ///< Internal value. Only occur when analyzing JMP loop.
|
||||
|
@ -253,8 +275,9 @@ enum class InternalFlag : u64 {
|
|||
class GLSLRegisterManager {
|
||||
public:
|
||||
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();
|
||||
BuildInputList();
|
||||
}
|
||||
|
@ -358,11 +381,13 @@ public:
|
|||
* @param reg The destination register to use.
|
||||
* @param elem The element to use for the operation.
|
||||
* @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,
|
||||
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 src = GetInputAttribute(attribute, input_mode) + GetSwizzle(elem);
|
||||
const std::string src = GetInputAttribute(attribute, input_mode, vertex) + GetSwizzle(elem);
|
||||
shader.AddLine(dest + " = " + src + ';');
|
||||
}
|
||||
|
||||
|
@ -391,16 +416,29 @@ public:
|
|||
* are stored as floats, so this may require conversion.
|
||||
* @param attribute The destination output attribute.
|
||||
* @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 src = GetRegisterAsFloat(reg);
|
||||
const std::string src = GetRegisterAsFloat(val_reg);
|
||||
|
||||
if (!dest.empty()) {
|
||||
// Can happen with unknown/unimplemented output attributes, in which case we ignore the
|
||||
// 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,58 +479,18 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
/// Add declarations for registers
|
||||
/// Add declarations.
|
||||
void GenerateDeclarations(const std::string& suffix) {
|
||||
for (const auto& reg : regs) {
|
||||
declarations.AddLine(GLSLRegister::GetTypeString() + ' ' + reg.GetPrefixString() +
|
||||
std::to_string(reg.GetIndex()) + '_' + suffix + " = 0;");
|
||||
}
|
||||
declarations.AddNewLine();
|
||||
|
||||
for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) {
|
||||
const InternalFlag code = static_cast<InternalFlag>(ii);
|
||||
declarations.AddLine("bool " + GetInternalFlag(code) + " = false;");
|
||||
}
|
||||
declarations.AddNewLine();
|
||||
|
||||
for (const auto element : declr_input_attribute) {
|
||||
// TODO(bunnei): Use proper number of elements for these
|
||||
u32 idx =
|
||||
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) + ';');
|
||||
}
|
||||
declarations.AddNewLine();
|
||||
|
||||
for (const auto& index : declr_output_attribute) {
|
||||
// TODO(bunnei): Use proper number of elements for these
|
||||
declarations.AddLine("layout(location = " +
|
||||
std::to_string(static_cast<u32>(index) -
|
||||
static_cast<u32>(Attribute::Index::Attribute_0)) +
|
||||
") out vec4 " + GetOutputAttribute(index) + ';');
|
||||
}
|
||||
declarations.AddNewLine();
|
||||
|
||||
for (const auto& entry : GetConstBuffersDeclarations()) {
|
||||
declarations.AddLine("layout(std140) uniform " + entry.GetName());
|
||||
declarations.AddLine('{');
|
||||
declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) +
|
||||
"[MAX_CONSTBUFFER_ELEMENTS];");
|
||||
declarations.AddLine("};");
|
||||
declarations.AddNewLine();
|
||||
}
|
||||
declarations.AddNewLine();
|
||||
|
||||
const auto& samplers = GetSamplers();
|
||||
for (const auto& sampler : samplers) {
|
||||
declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() +
|
||||
';');
|
||||
}
|
||||
declarations.AddNewLine();
|
||||
GenerateRegisters(suffix);
|
||||
GenerateInternalFlags();
|
||||
GenerateInputAttrs();
|
||||
GenerateOutputAttrs();
|
||||
GenerateConstBuffers();
|
||||
GenerateSamplers();
|
||||
GenerateGeometry();
|
||||
}
|
||||
|
||||
/// Returns a list of constant buffer declarations
|
||||
/// 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(),
|
||||
|
@ -500,7 +498,7 @@ public:
|
|||
return result;
|
||||
}
|
||||
|
||||
/// Returns a list of samplers used in the shader
|
||||
/// Returns a list of samplers used in the shader.
|
||||
const std::vector<SamplerEntry>& GetSamplers() const {
|
||||
return used_samplers;
|
||||
}
|
||||
|
@ -509,7 +507,7 @@ public:
|
|||
/// 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());
|
||||
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 =
|
||||
|
@ -530,6 +528,129 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
/// Generates declarations for registers.
|
||||
void GenerateRegisters(const std::string& suffix) {
|
||||
for (const auto& reg : regs) {
|
||||
declarations.AddLine(GLSLRegister::GetTypeString() + ' ' + reg.GetPrefixString() +
|
||||
std::to_string(reg.GetIndex()) + '_' + suffix + " = 0;");
|
||||
}
|
||||
declarations.AddNewLine();
|
||||
}
|
||||
|
||||
/// Generates declarations for internal flags.
|
||||
void GenerateInternalFlags() {
|
||||
for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) {
|
||||
const InternalFlag code = static_cast<InternalFlag>(ii);
|
||||
declarations.AddLine("bool " + GetInternalFlag(code) + " = false;");
|
||||
}
|
||||
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) {
|
||||
// TODO(bunnei): Use proper number of elements for these
|
||||
u32 idx =
|
||||
static_cast<u32>(element.first) - static_cast<u32>(Attribute::Index::Attribute_0);
|
||||
if (stage != Maxwell3D::Regs::ShaderStage::Vertex) {
|
||||
// If inputs are varyings, add an offset
|
||||
idx += GENERIC_VARYING_START_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();
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
// TODO(bunnei): Use proper number of elements for these
|
||||
const u32 idx = static_cast<u32>(index) -
|
||||
static_cast<u32>(Attribute::Index::Attribute_0) +
|
||||
GENERIC_VARYING_START_LOCATION;
|
||||
declarations.AddLine("layout (location = " + std::to_string(idx) + ") out vec4 " +
|
||||
GetOutputAttribute(index) + ';');
|
||||
}
|
||||
declarations.AddNewLine();
|
||||
}
|
||||
|
||||
/// Generates declarations for constant buffers.
|
||||
void GenerateConstBuffers() {
|
||||
for (const auto& entry : GetConstBuffersDeclarations()) {
|
||||
declarations.AddLine("layout (std140) uniform " + entry.GetName());
|
||||
declarations.AddLine('{');
|
||||
declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) +
|
||||
"[MAX_CONSTBUFFER_ELEMENTS];");
|
||||
declarations.AddLine("};");
|
||||
declarations.AddNewLine();
|
||||
}
|
||||
declarations.AddNewLine();
|
||||
}
|
||||
|
||||
/// Generates declarations for samplers.
|
||||
void GenerateSamplers() {
|
||||
const auto& samplers = GetSamplers();
|
||||
for (const auto& sampler : samplers) {
|
||||
declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() +
|
||||
';');
|
||||
}
|
||||
declarations.AddNewLine();
|
||||
}
|
||||
|
||||
/// Generates declarations used for geometry shaders.
|
||||
void GenerateGeometry() {
|
||||
if (stage != Maxwell3D::Regs::ShaderStage::Geometry)
|
||||
return;
|
||||
|
||||
declarations.AddLine(
|
||||
"layout (" + GetTopologyName(header.common3.output_topology) +
|
||||
", max_vertices = " + std::to_string(header.common4.max_output_vertices) + ") out;");
|
||||
declarations.AddNewLine();
|
||||
|
||||
declarations.AddLine("vec4 amem[" + std::to_string(MAX_GEOMETRY_BUFFERS) + "][" +
|
||||
std::to_string(MAX_ATTRIBUTES) + "];");
|
||||
declarations.AddNewLine();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/// Generates code representing a temporary (GPR) register.
|
||||
std::string GetRegister(const Register& reg, unsigned elem) {
|
||||
if (reg == Register::ZeroIndex) {
|
||||
|
@ -586,11 +707,19 @@ private:
|
|||
|
||||
/// Generates code representing an input attribute register.
|
||||
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) {
|
||||
case Attribute::Index::Position:
|
||||
if (stage != Maxwell3D::Regs::ShaderStage::Fragment) {
|
||||
return "position";
|
||||
return GeometryPass("position");
|
||||
} else {
|
||||
return "vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)";
|
||||
}
|
||||
|
@ -619,7 +748,7 @@ private:
|
|||
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));
|
||||
|
@ -672,7 +801,7 @@ private:
|
|||
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) {
|
||||
switch (attribute) {
|
||||
case Attribute::Index::Position:
|
||||
|
@ -708,6 +837,7 @@ private:
|
|||
std::vector<SamplerEntry> used_samplers;
|
||||
const Maxwell3D::Regs::ShaderStage& stage;
|
||||
const std::string& suffix;
|
||||
const Tegra::Shader::Header& header;
|
||||
};
|
||||
|
||||
class GLSLGenerator {
|
||||
|
@ -1103,8 +1233,8 @@ private:
|
|||
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;
|
||||
ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute,
|
||||
|
@ -1826,7 +1956,7 @@ private:
|
|||
const auto LoadNextElement = [&](u32 reg_offset) {
|
||||
regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element,
|
||||
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
|
||||
// to load goes beyond the vec4 size, load the first element of the next
|
||||
|
@ -1890,8 +2020,8 @@ private:
|
|||
|
||||
const auto StoreNextElement = [&](u32 reg_offset) {
|
||||
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
|
||||
// to load goes beyond the vec4 size, load the first element of the next
|
||||
|
@ -2734,6 +2864,52 @@ private:
|
|||
|
||||
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: {
|
||||
ASSERT_MSG(instr.bra.constant_buffer == 0,
|
||||
"BRA with constant buffers are not implemented");
|
||||
|
@ -2907,7 +3083,7 @@ private:
|
|||
|
||||
ShaderWriter shader;
|
||||
ShaderWriter declarations;
|
||||
GLSLRegisterManager regs{shader, declarations, stage, suffix};
|
||||
GLSLRegisterManager regs{shader, declarations, stage, suffix, header};
|
||||
|
||||
// Declarations
|
||||
std::set<std::string> declr_predicates;
|
||||
|
|
|
@ -17,7 +17,18 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
|
|||
std::string out = "#version 430 core\n";
|
||||
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||
out += Decompiler::GetCommonDeclarations();
|
||||
out += "bool exec_vertex();\n";
|
||||
|
||||
out += R"(
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
layout(std140) uniform vs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 instance_id;
|
||||
uvec4 flip_stage;
|
||||
};
|
||||
)";
|
||||
|
||||
if (setup.IsDualProgram()) {
|
||||
out += "bool exec_vertex_b();\n";
|
||||
|
@ -28,19 +39,18 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
|
|||
Maxwell3D::Regs::ShaderStage::Vertex, "vertex")
|
||||
.get_value_or({});
|
||||
|
||||
out += program.first;
|
||||
|
||||
if (setup.IsDualProgram()) {
|
||||
ProgramResult program_b =
|
||||
Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET,
|
||||
Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b")
|
||||
.get_value_or({});
|
||||
out += program_b.first;
|
||||
}
|
||||
|
||||
out += R"(
|
||||
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
out vec4 position;
|
||||
|
||||
layout (std140) uniform vs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 instance_id;
|
||||
};
|
||||
|
||||
void main() {
|
||||
position = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
exec_vertex();
|
||||
|
@ -52,27 +62,52 @@ void main() {
|
|||
|
||||
out += R"(
|
||||
|
||||
// Viewport can be flipped, which is unsupported by glViewport
|
||||
position.xy *= viewport_flip.xy;
|
||||
// Check if the flip stage is VertexB
|
||||
if (flip_stage[0] == 1) {
|
||||
// Viewport can be flipped, which is unsupported by glViewport
|
||||
position.xy *= viewport_flip.xy;
|
||||
}
|
||||
gl_Position = position;
|
||||
|
||||
// TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0
|
||||
// For now, this is here to bring order in lieu of proper emulation
|
||||
position.w = 1.0;
|
||||
if (flip_stage[0] == 1) {
|
||||
position.w = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
)";
|
||||
|
||||
return {out, program.second};
|
||||
}
|
||||
|
||||
ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
|
||||
std::string out = "#version 430 core\n";
|
||||
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||
out += Decompiler::GetCommonDeclarations();
|
||||
out += "bool exec_geometry();\n";
|
||||
|
||||
ProgramResult program =
|
||||
Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
|
||||
Maxwell3D::Regs::ShaderStage::Geometry, "geometry")
|
||||
.get_value_or({});
|
||||
out += R"(
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
layout (std140) uniform gs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 instance_id;
|
||||
uvec4 flip_stage;
|
||||
};
|
||||
|
||||
void main() {
|
||||
exec_geometry();
|
||||
}
|
||||
|
||||
)";
|
||||
out += program.first;
|
||||
|
||||
if (setup.IsDualProgram()) {
|
||||
ProgramResult program_b =
|
||||
Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET,
|
||||
Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b")
|
||||
.get_value_or({});
|
||||
out += program_b.first;
|
||||
}
|
||||
|
||||
return {out, program.second};
|
||||
}
|
||||
|
||||
|
@ -87,7 +122,6 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup) {
|
|||
Maxwell3D::Regs::ShaderStage::Fragment, "fragment")
|
||||
.get_value_or({});
|
||||
out += R"(
|
||||
in vec4 position;
|
||||
layout(location = 0) out vec4 FragColor0;
|
||||
layout(location = 1) out vec4 FragColor1;
|
||||
layout(location = 2) out vec4 FragColor2;
|
||||
|
@ -100,6 +134,7 @@ layout(location = 7) out vec4 FragColor7;
|
|||
layout (std140) uniform fs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 instance_id;
|
||||
uvec4 flip_stage;
|
||||
};
|
||||
|
||||
void main() {
|
||||
|
@ -110,5 +145,4 @@ void main() {
|
|||
out += program.first;
|
||||
return {out, program.second};
|
||||
}
|
||||
|
||||
} // namespace OpenGL::GLShader
|
||||
} // namespace OpenGL::GLShader
|
|
@ -195,6 +195,12 @@ private:
|
|||
*/
|
||||
ProgramResult GenerateVertexShader(const ShaderSetup& setup);
|
||||
|
||||
/**
|
||||
* Generates the GLSL geometry shader program source code for the given GS program
|
||||
* @returns String of the shader source code
|
||||
*/
|
||||
ProgramResult GenerateGeometryShader(const ShaderSetup& setup);
|
||||
|
||||
/**
|
||||
* Generates the GLSL fragment shader program source code for the given FS program
|
||||
* @returns String of the shader source code
|
||||
|
|
|
@ -18,6 +18,14 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
|
|||
|
||||
// We only assign the instance to the first component of the vector, the rest is just padding.
|
||||
instance_id[0] = state.current_instance;
|
||||
|
||||
// Assign in which stage the position has to be flipped
|
||||
// (the last stage before the fragment shader).
|
||||
if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) {
|
||||
flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
|
||||
} else {
|
||||
flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OpenGL::GLShader
|
||||
|
|
|
@ -21,8 +21,9 @@ struct MaxwellUniformData {
|
|||
void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage);
|
||||
alignas(16) GLvec4 viewport_flip;
|
||||
alignas(16) GLuvec4 instance_id;
|
||||
alignas(16) GLuvec4 flip_stage;
|
||||
};
|
||||
static_assert(sizeof(MaxwellUniformData) == 32, "MaxwellUniformData structure size is incorrect");
|
||||
static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect");
|
||||
static_assert(sizeof(MaxwellUniformData) < 16384,
|
||||
"MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
|
||||
|
||||
|
@ -36,6 +37,10 @@ public:
|
|||
vs = program;
|
||||
}
|
||||
|
||||
void UseProgrammableGeometryShader(GLuint program) {
|
||||
gs = program;
|
||||
}
|
||||
|
||||
void UseProgrammableFragmentShader(GLuint program) {
|
||||
fs = program;
|
||||
}
|
||||
|
|
|
@ -169,16 +169,20 @@ static void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr,
|
|||
const std::string nice_addr = fmt::format("0x{:016x}", addr);
|
||||
std::string object_label;
|
||||
|
||||
switch (identifier) {
|
||||
case GL_TEXTURE:
|
||||
object_label = extra_info + "@" + nice_addr;
|
||||
break;
|
||||
case GL_PROGRAM:
|
||||
object_label = "ShaderProgram@" + nice_addr;
|
||||
break;
|
||||
default:
|
||||
object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
|
||||
break;
|
||||
if (extra_info.empty()) {
|
||||
switch (identifier) {
|
||||
case GL_TEXTURE:
|
||||
object_label = "Texture@" + nice_addr;
|
||||
break;
|
||||
case GL_PROGRAM:
|
||||
object_label = "Shader@" + nice_addr;
|
||||
break;
|
||||
default:
|
||||
object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
object_label = extra_info + '@' + nice_addr;
|
||||
}
|
||||
glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str()));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue