gl_shader_decompiler: Implement geometry shaders
This commit is contained in:
		| @@ -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); | ||||
|  | ||||
| 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 { | ||||
| 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,125 @@ 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); | ||||
|             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(); | ||||
|     } | ||||
|  | ||||
|     /// 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 | ||||
|             declarations.AddLine("layout (location = " + | ||||
|                                  std::to_string(static_cast<u32>(index) - | ||||
|                                                 static_cast<u32>(Attribute::Index::Attribute_0)) + | ||||
|                                  ") 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 +703,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 +744,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 +797,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 +833,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 +1229,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 +1952,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 +2016,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 | ||||
| @@ -2738,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"); | ||||
| @@ -2911,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; | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user