diff options
| author | 2018-04-17 22:10:53 -0400 | |
|---|---|---|
| committer | 2018-04-17 22:10:53 -0400 | |
| commit | 71b4a3b9f6894536d1a64265d5eb52a15be2094d (patch) | |
| tree | 53297c2c2659146a7cef8d91ab6b4bf169ec8654 /src/video_core/renderer_opengl | |
| parent | Merge pull request #345 from bunnei/blending (diff) | |
| parent | shader_bytecode: Make ctor's constexpr and explicit. (diff) | |
| download | yuzu-71b4a3b9f6894536d1a64265d5eb52a15be2094d.tar.gz yuzu-71b4a3b9f6894536d1a64265d5eb52a15be2094d.tar.xz yuzu-71b4a3b9f6894536d1a64265d5eb52a15be2094d.zip | |
Merge pull request #344 from bunnei/shader-decompiler-p2
Shader decompiler changes part 2
Diffstat (limited to 'src/video_core/renderer_opengl')
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 197 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 6 |
2 files changed, 146 insertions, 57 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index e11711533..6233ee358 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -17,6 +17,7 @@ using Tegra::Shader::Attribute; | |||
| 17 | using Tegra::Shader::Instruction; | 17 | using Tegra::Shader::Instruction; |
| 18 | using Tegra::Shader::OpCode; | 18 | using Tegra::Shader::OpCode; |
| 19 | using Tegra::Shader::Register; | 19 | using Tegra::Shader::Register; |
| 20 | using Tegra::Shader::Sampler; | ||
| 20 | using Tegra::Shader::SubOp; | 21 | using Tegra::Shader::SubOp; |
| 21 | using Tegra::Shader::Uniform; | 22 | using Tegra::Shader::Uniform; |
| 22 | 23 | ||
| @@ -155,23 +156,27 @@ private: | |||
| 155 | 156 | ||
| 156 | /// Generates code representing an input attribute register. | 157 | /// Generates code representing an input attribute register. |
| 157 | std::string GetInputAttribute(Attribute::Index attribute) { | 158 | std::string GetInputAttribute(Attribute::Index attribute) { |
| 158 | declr_input_attribute.insert(attribute); | 159 | switch (attribute) { |
| 160 | case Attribute::Index::Position: | ||
| 161 | return "position"; | ||
| 162 | default: | ||
| 163 | const u32 index{static_cast<u32>(attribute) - | ||
| 164 | static_cast<u32>(Attribute::Index::Attribute_0)}; | ||
| 165 | if (attribute >= Attribute::Index::Attribute_0) { | ||
| 166 | declr_input_attribute.insert(attribute); | ||
| 167 | return "input_attribute_" + std::to_string(index); | ||
| 168 | } | ||
| 159 | 169 | ||
| 160 | const u32 index{static_cast<u32>(attribute) - | 170 | NGLOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", index); |
| 161 | static_cast<u32>(Attribute::Index::Attribute_0)}; | 171 | UNREACHABLE(); |
| 162 | if (attribute >= Attribute::Index::Attribute_0) { | ||
| 163 | return "input_attribute_" + std::to_string(index); | ||
| 164 | } | 172 | } |
| 165 | |||
| 166 | LOG_CRITICAL(HW_GPU, "Unhandled input attribute: 0x%02x", index); | ||
| 167 | UNREACHABLE(); | ||
| 168 | } | 173 | } |
| 169 | 174 | ||
| 170 | /// Generates code representing an output attribute register. | 175 | /// Generates code representing an output attribute register. |
| 171 | std::string GetOutputAttribute(Attribute::Index attribute) { | 176 | std::string GetOutputAttribute(Attribute::Index attribute) { |
| 172 | switch (attribute) { | 177 | switch (attribute) { |
| 173 | case Attribute::Index::Position: | 178 | case Attribute::Index::Position: |
| 174 | return "gl_Position"; | 179 | return "position"; |
| 175 | default: | 180 | default: |
| 176 | const u32 index{static_cast<u32>(attribute) - | 181 | const u32 index{static_cast<u32>(attribute) - |
| 177 | static_cast<u32>(Attribute::Index::Attribute_0)}; | 182 | static_cast<u32>(Attribute::Index::Attribute_0)}; |
| @@ -180,22 +185,42 @@ private: | |||
| 180 | return "output_attribute_" + std::to_string(index); | 185 | return "output_attribute_" + std::to_string(index); |
| 181 | } | 186 | } |
| 182 | 187 | ||
| 183 | LOG_CRITICAL(HW_GPU, "Unhandled output attribute: 0x%02x", index); | 188 | NGLOG_CRITICAL(HW_GPU, "Unhandled output attribute: {}", index); |
| 184 | UNREACHABLE(); | 189 | UNREACHABLE(); |
| 185 | } | 190 | } |
| 186 | } | 191 | } |
| 187 | 192 | ||
| 193 | /// Generates code representing an immediate value | ||
| 194 | static std::string GetImmediate(const Instruction& instr) { | ||
| 195 | return std::to_string(instr.alu.GetImm20()); | ||
| 196 | } | ||
| 197 | |||
| 188 | /// Generates code representing a temporary (GPR) register. | 198 | /// Generates code representing a temporary (GPR) register. |
| 189 | std::string GetRegister(const Register& reg) { | 199 | std::string GetRegister(const Register& reg, unsigned elem = 0) { |
| 190 | return *declr_register.insert("register_" + std::to_string(reg)).first; | 200 | if (stage == Maxwell3D::Regs::ShaderStage::Fragment && reg < 4) { |
| 201 | // GPRs 0-3 are output color for the fragment shader | ||
| 202 | return std::string{"color."} + "rgba"[(reg + elem) & 3]; | ||
| 203 | } | ||
| 204 | |||
| 205 | return *declr_register.insert("register_" + std::to_string(reg + elem)).first; | ||
| 191 | } | 206 | } |
| 192 | 207 | ||
| 193 | /// Generates code representing a uniform (C buffer) register. | 208 | /// Generates code representing a uniform (C buffer) register. |
| 194 | std::string GetUniform(const Uniform& reg) { | 209 | std::string GetUniform(const Uniform& reg) { |
| 195 | declr_const_buffers[reg.index].MarkAsUsed(reg.index, reg.offset, stage); | 210 | declr_const_buffers[reg.index].MarkAsUsed(static_cast<unsigned>(reg.index), |
| 211 | static_cast<unsigned>(reg.offset), stage); | ||
| 196 | return 'c' + std::to_string(reg.index) + '[' + std::to_string(reg.offset) + ']'; | 212 | return 'c' + std::to_string(reg.index) + '[' + std::to_string(reg.offset) + ']'; |
| 197 | } | 213 | } |
| 198 | 214 | ||
| 215 | /// Generates code representing a texture sampler. | ||
| 216 | std::string GetSampler(const Sampler& sampler) const { | ||
| 217 | // TODO(Subv): Support more than just texture sampler 0 | ||
| 218 | ASSERT_MSG(sampler.index == Sampler::Index::Sampler_0, "unsupported"); | ||
| 219 | const unsigned index{static_cast<unsigned>(sampler.index.Value()) - | ||
| 220 | static_cast<unsigned>(Sampler::Index::Sampler_0)}; | ||
| 221 | return "tex[" + std::to_string(index) + "]"; | ||
| 222 | } | ||
| 223 | |||
| 199 | /** | 224 | /** |
| 200 | * Adds code that calls a subroutine. | 225 | * Adds code that calls a subroutine. |
| 201 | * @param subroutine the subroutine to call. | 226 | * @param subroutine the subroutine to call. |
| @@ -217,12 +242,13 @@ private: | |||
| 217 | * @param value the code representing the value to assign. | 242 | * @param value the code representing the value to assign. |
| 218 | */ | 243 | */ |
| 219 | void SetDest(u64 elem, const std::string& reg, const std::string& value, | 244 | void SetDest(u64 elem, const std::string& reg, const std::string& value, |
| 220 | u64 dest_num_components, u64 value_num_components) { | 245 | u64 dest_num_components, u64 value_num_components, bool is_abs = false) { |
| 221 | std::string swizzle = "."; | 246 | std::string swizzle = "."; |
| 222 | swizzle += "xyzw"[elem]; | 247 | swizzle += "xyzw"[elem]; |
| 223 | 248 | ||
| 224 | std::string dest = reg + (dest_num_components != 1 ? swizzle : ""); | 249 | std::string dest = reg + (dest_num_components != 1 ? swizzle : ""); |
| 225 | std::string src = "(" + value + ")" + (value_num_components != 1 ? swizzle : ""); | 250 | std::string src = "(" + value + ")" + (value_num_components != 1 ? swizzle : ""); |
| 251 | src = is_abs ? "abs(" + src + ")" : src; | ||
| 226 | 252 | ||
| 227 | shader.AddLine(dest + " = " + src + ";"); | 253 | shader.AddLine(dest + " = " + src + ";"); |
| 228 | } | 254 | } |
| @@ -240,8 +266,6 @@ private: | |||
| 240 | 266 | ||
| 241 | switch (OpCode::GetInfo(instr.opcode).type) { | 267 | switch (OpCode::GetInfo(instr.opcode).type) { |
| 242 | case OpCode::Type::Arithmetic: { | 268 | case OpCode::Type::Arithmetic: { |
| 243 | ASSERT(!instr.alu.abs_d); | ||
| 244 | |||
| 245 | std::string dest = GetRegister(instr.gpr0); | 269 | std::string dest = GetRegister(instr.gpr0); |
| 246 | std::string op_a = instr.alu.negate_a ? "-" : ""; | 270 | std::string op_a = instr.alu.negate_a ? "-" : ""; |
| 247 | op_a += GetRegister(instr.gpr8); | 271 | op_a += GetRegister(instr.gpr8); |
| @@ -250,63 +274,109 @@ private: | |||
| 250 | } | 274 | } |
| 251 | 275 | ||
| 252 | std::string op_b = instr.alu.negate_b ? "-" : ""; | 276 | std::string op_b = instr.alu.negate_b ? "-" : ""; |
| 253 | if (instr.is_b_gpr) { | 277 | |
| 254 | op_b += GetRegister(instr.gpr20); | 278 | if (instr.is_b_imm) { |
| 279 | op_b += GetImmediate(instr); | ||
| 255 | } else { | 280 | } else { |
| 256 | op_b += GetUniform(instr.uniform); | 281 | if (instr.is_b_gpr) { |
| 282 | op_b += GetRegister(instr.gpr20); | ||
| 283 | } else { | ||
| 284 | op_b += GetUniform(instr.uniform); | ||
| 285 | } | ||
| 257 | } | 286 | } |
| 287 | |||
| 258 | if (instr.alu.abs_b) { | 288 | if (instr.alu.abs_b) { |
| 259 | op_b = "abs(" + op_b + ")"; | 289 | op_b = "abs(" + op_b + ")"; |
| 260 | } | 290 | } |
| 261 | 291 | ||
| 262 | switch (instr.opcode.EffectiveOpCode()) { | 292 | switch (instr.opcode.EffectiveOpCode()) { |
| 263 | case OpCode::Id::FMUL_C: | 293 | case OpCode::Id::FMUL_C: |
| 264 | case OpCode::Id::FMUL_R: { | 294 | case OpCode::Id::FMUL_R: |
| 265 | SetDest(0, dest, op_a + " * " + op_b, 1, 1); | 295 | case OpCode::Id::FMUL_IMM: { |
| 296 | SetDest(0, dest, op_a + " * " + op_b, 1, 1, instr.alu.abs_d); | ||
| 266 | break; | 297 | break; |
| 267 | } | 298 | } |
| 268 | case OpCode::Id::FADD_C: | 299 | case OpCode::Id::FADD_C: |
| 269 | case OpCode::Id::FADD_R: { | 300 | case OpCode::Id::FADD_R: |
| 270 | SetDest(0, dest, op_a + " + " + op_b, 1, 1); | 301 | case OpCode::Id::FADD_IMM: { |
| 302 | SetDest(0, dest, op_a + " + " + op_b, 1, 1, instr.alu.abs_d); | ||
| 271 | break; | 303 | break; |
| 272 | } | 304 | } |
| 273 | default: { | 305 | case OpCode::Id::MUFU: { |
| 274 | LOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: 0x%02x (%s): 0x%08x", | 306 | switch (instr.sub_op) { |
| 275 | static_cast<unsigned>(instr.opcode.EffectiveOpCode()), | 307 | case SubOp::Cos: |
| 276 | OpCode::GetInfo(instr.opcode).name.c_str(), instr.hex); | 308 | SetDest(0, dest, "cos(" + op_a + ")", 1, 1, instr.alu.abs_d); |
| 277 | throw DecompileFail("Unhandled instruction"); | 309 | break; |
| 310 | case SubOp::Sin: | ||
| 311 | SetDest(0, dest, "sin(" + op_a + ")", 1, 1, instr.alu.abs_d); | ||
| 312 | break; | ||
| 313 | case SubOp::Ex2: | ||
| 314 | SetDest(0, dest, "exp2(" + op_a + ")", 1, 1, instr.alu.abs_d); | ||
| 315 | break; | ||
| 316 | case SubOp::Lg2: | ||
| 317 | SetDest(0, dest, "log2(" + op_a + ")", 1, 1, instr.alu.abs_d); | ||
| 318 | break; | ||
| 319 | case SubOp::Rcp: | ||
| 320 | SetDest(0, dest, "1.0 / " + op_a, 1, 1, instr.alu.abs_d); | ||
| 321 | break; | ||
| 322 | case SubOp::Rsq: | ||
| 323 | SetDest(0, dest, "inversesqrt(" + op_a + ")", 1, 1, instr.alu.abs_d); | ||
| 324 | break; | ||
| 325 | case SubOp::Min: | ||
| 326 | SetDest(0, dest, "min(" + op_a + "," + op_b + ")", 1, 1, instr.alu.abs_d); | ||
| 327 | break; | ||
| 328 | default: | ||
| 329 | NGLOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {}", | ||
| 330 | static_cast<unsigned>(instr.sub_op.Value())); | ||
| 331 | UNREACHABLE(); | ||
| 332 | } | ||
| 278 | break; | 333 | break; |
| 279 | } | 334 | } |
| 335 | default: { | ||
| 336 | NGLOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {} ({}): {}", | ||
| 337 | static_cast<unsigned>(instr.opcode.EffectiveOpCode()), | ||
| 338 | OpCode::GetInfo(instr.opcode).name, instr.hex); | ||
| 339 | UNREACHABLE(); | ||
| 340 | } | ||
| 280 | } | 341 | } |
| 281 | break; | 342 | break; |
| 282 | } | 343 | } |
| 283 | case OpCode::Type::Ffma: { | 344 | case OpCode::Type::Ffma: { |
| 284 | ASSERT_MSG(!instr.ffma.negate_b, "untested"); | ||
| 285 | ASSERT_MSG(!instr.ffma.negate_c, "untested"); | ||
| 286 | |||
| 287 | std::string dest = GetRegister(instr.gpr0); | 345 | std::string dest = GetRegister(instr.gpr0); |
| 288 | std::string op_a = GetRegister(instr.gpr8); | 346 | std::string op_a = GetRegister(instr.gpr8); |
| 289 | |||
| 290 | std::string op_b = instr.ffma.negate_b ? "-" : ""; | 347 | std::string op_b = instr.ffma.negate_b ? "-" : ""; |
| 291 | op_b += GetUniform(instr.uniform); | ||
| 292 | |||
| 293 | std::string op_c = instr.ffma.negate_c ? "-" : ""; | 348 | std::string op_c = instr.ffma.negate_c ? "-" : ""; |
| 294 | op_c += GetRegister(instr.gpr39); | ||
| 295 | 349 | ||
| 296 | switch (instr.opcode.EffectiveOpCode()) { | 350 | switch (instr.opcode.EffectiveOpCode()) { |
| 297 | case OpCode::Id::FFMA_CR: { | 351 | case OpCode::Id::FFMA_CR: { |
| 298 | SetDest(0, dest, op_a + " * " + op_b + " + " + op_c, 1, 1); | 352 | op_b += GetUniform(instr.uniform); |
| 353 | op_c += GetRegister(instr.gpr39); | ||
| 299 | break; | 354 | break; |
| 300 | } | 355 | } |
| 301 | 356 | case OpCode::Id::FFMA_RR: { | |
| 302 | default: { | 357 | op_b += GetRegister(instr.gpr20); |
| 303 | LOG_CRITICAL(HW_GPU, "Unhandled arithmetic FFMA instruction: 0x%02x (%s): 0x%08x", | 358 | op_c += GetRegister(instr.gpr39); |
| 304 | static_cast<unsigned>(instr.opcode.EffectiveOpCode()), | 359 | break; |
| 305 | OpCode::GetInfo(instr.opcode).name.c_str(), instr.hex); | 360 | } |
| 306 | throw DecompileFail("Unhandled instruction"); | 361 | case OpCode::Id::FFMA_RC: { |
| 362 | op_b += GetRegister(instr.gpr39); | ||
| 363 | op_c += GetUniform(instr.uniform); | ||
| 307 | break; | 364 | break; |
| 308 | } | 365 | } |
| 366 | case OpCode::Id::FFMA_IMM: { | ||
| 367 | op_b += GetImmediate(instr); | ||
| 368 | op_c += GetRegister(instr.gpr39); | ||
| 369 | break; | ||
| 309 | } | 370 | } |
| 371 | default: { | ||
| 372 | NGLOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {} ({}): {}", | ||
| 373 | static_cast<unsigned>(instr.opcode.EffectiveOpCode()), | ||
| 374 | OpCode::GetInfo(instr.opcode).name, instr.hex); | ||
| 375 | UNREACHABLE(); | ||
| 376 | } | ||
| 377 | } | ||
| 378 | |||
| 379 | SetDest(0, dest, op_a + " * " + op_b + " + " + op_c, 1, 1); | ||
| 310 | break; | 380 | break; |
| 311 | } | 381 | } |
| 312 | case OpCode::Type::Memory: { | 382 | case OpCode::Type::Memory: { |
| @@ -315,22 +385,33 @@ private: | |||
| 315 | 385 | ||
| 316 | switch (instr.opcode.EffectiveOpCode()) { | 386 | switch (instr.opcode.EffectiveOpCode()) { |
| 317 | case OpCode::Id::LD_A: { | 387 | case OpCode::Id::LD_A: { |
| 318 | ASSERT(instr.attribute.fmt20.size == 0); | 388 | ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); |
| 319 | SetDest(instr.attribute.fmt20.element, gpr0, GetInputAttribute(attribute), 1, 4); | 389 | SetDest(instr.attribute.fmt20.element, gpr0, GetInputAttribute(attribute), 1, 4); |
| 320 | break; | 390 | break; |
| 321 | } | 391 | } |
| 322 | case OpCode::Id::ST_A: { | 392 | case OpCode::Id::ST_A: { |
| 323 | ASSERT(instr.attribute.fmt20.size == 0); | 393 | ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); |
| 324 | SetDest(instr.attribute.fmt20.element, GetOutputAttribute(attribute), gpr0, 4, 1); | 394 | SetDest(instr.attribute.fmt20.element, GetOutputAttribute(attribute), gpr0, 4, 1); |
| 325 | break; | 395 | break; |
| 326 | } | 396 | } |
| 327 | default: { | 397 | case OpCode::Id::TEXS: { |
| 328 | LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: 0x%02x (%s): 0x%08x", | 398 | ASSERT_MSG(instr.attribute.fmt20.size == 4, "untested"); |
| 329 | static_cast<unsigned>(instr.opcode.EffectiveOpCode()), | 399 | const std::string op_a = GetRegister(instr.gpr8); |
| 330 | OpCode::GetInfo(instr.opcode).name.c_str(), instr.hex); | 400 | const std::string op_b = GetRegister(instr.gpr20); |
| 331 | throw DecompileFail("Unhandled instruction"); | 401 | const std::string sampler = GetSampler(instr.sampler); |
| 402 | const std::string coord = "vec2(" + op_a + ", " + op_b + ")"; | ||
| 403 | const std::string texture = "texture(" + sampler + ", " + coord + ")"; | ||
| 404 | for (unsigned elem = 0; elem < instr.attribute.fmt20.size; ++elem) { | ||
| 405 | SetDest(elem, GetRegister(instr.gpr0, elem), texture, 1, 4); | ||
| 406 | } | ||
| 332 | break; | 407 | break; |
| 333 | } | 408 | } |
| 409 | default: { | ||
| 410 | NGLOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {} ({}): {}", | ||
| 411 | static_cast<unsigned>(instr.opcode.EffectiveOpCode()), | ||
| 412 | OpCode::GetInfo(instr.opcode).name, instr.hex); | ||
| 413 | UNREACHABLE(); | ||
| 414 | } | ||
| 334 | } | 415 | } |
| 335 | break; | 416 | break; |
| 336 | } | 417 | } |
| @@ -342,14 +423,18 @@ private: | |||
| 342 | offset = PROGRAM_END - 1; | 423 | offset = PROGRAM_END - 1; |
| 343 | break; | 424 | break; |
| 344 | } | 425 | } |
| 345 | 426 | case OpCode::Id::IPA: { | |
| 346 | default: { | 427 | const auto& attribute = instr.attribute.fmt28; |
| 347 | LOG_CRITICAL(HW_GPU, "Unhandled instruction: 0x%02x (%s): 0x%08x", | 428 | std::string dest = GetRegister(instr.gpr0); |
| 348 | static_cast<unsigned>(instr.opcode.EffectiveOpCode()), | 429 | SetDest(attribute.element, dest, GetInputAttribute(attribute.index), 1, 4); |
| 349 | OpCode::GetInfo(instr.opcode).name.c_str(), instr.hex); | ||
| 350 | throw DecompileFail("Unhandled instruction"); | ||
| 351 | break; | 430 | break; |
| 352 | } | 431 | } |
| 432 | default: { | ||
| 433 | NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {} ({}): {}", | ||
| 434 | static_cast<unsigned>(instr.opcode.EffectiveOpCode()), | ||
| 435 | OpCode::GetInfo(instr.opcode).name, instr.hex); | ||
| 436 | UNREACHABLE(); | ||
| 437 | } | ||
| 353 | } | 438 | } |
| 354 | 439 | ||
| 355 | break; | 440 | break; |
| @@ -514,7 +599,7 @@ boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, | |||
| 514 | GLSLGenerator generator(subroutines, program_code, main_offset, stage); | 599 | GLSLGenerator generator(subroutines, program_code, main_offset, stage); |
| 515 | return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; | 600 | return ProgramResult{generator.GetShaderCode(), generator.GetEntries()}; |
| 516 | } catch (const DecompileFail& exception) { | 601 | } catch (const DecompileFail& exception) { |
| 517 | LOG_ERROR(HW_GPU, "Shader decompilation failed: %s", exception.what()); | 602 | NGLOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); |
| 518 | } | 603 | } |
| 519 | return boost::none; | 604 | return boost::none; |
| 520 | } | 605 | } |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index aeea1c805..8b7f17601 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp | |||
| @@ -27,10 +27,13 @@ out gl_PerVertex { | |||
| 27 | vec4 gl_Position; | 27 | vec4 gl_Position; |
| 28 | }; | 28 | }; |
| 29 | 29 | ||
| 30 | out vec4 position; | ||
| 31 | |||
| 30 | void main() { | 32 | void main() { |
| 31 | exec_shader(); | 33 | exec_shader(); |
| 32 | } | ||
| 33 | 34 | ||
| 35 | gl_Position = position; | ||
| 36 | } | ||
| 34 | )"; | 37 | )"; |
| 35 | out += program.first; | 38 | out += program.first; |
| 36 | return {out, program.second}; | 39 | return {out, program.second}; |
| @@ -46,6 +49,7 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup, const MaxwellFSCo | |||
| 46 | .get_value_or({}); | 49 | .get_value_or({}); |
| 47 | out += R"( | 50 | out += R"( |
| 48 | 51 | ||
| 52 | in vec4 position; | ||
| 49 | out vec4 color; | 53 | out vec4 color; |
| 50 | 54 | ||
| 51 | uniform sampler2D tex[32]; | 55 | uniform sampler2D tex[32]; |