mirror of
https://github.com/Lime3DS/Lime3DS
synced 2024-12-27 01:22:37 -06:00
Merge pull request #3686 from wwylele/glvtx-shader-gen
gl_shader_gen: generate programmable vs/gs and fixed gs
This commit is contained in:
commit
be5777f3de
6 changed files with 636 additions and 25 deletions
|
@ -1266,7 +1266,7 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerOpenGL::SetShader() {
|
void RasterizerOpenGL::SetShader() {
|
||||||
auto config = GLShader::PicaShaderConfig::BuildFromRegs(Pica::g_state.regs);
|
auto config = GLShader::PicaFSConfig::BuildFromRegs(Pica::g_state.regs);
|
||||||
shader_program_manager->UseFragmentShader(config);
|
shader_program_manager->UseFragmentShader(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/bit_field.h"
|
#include "common/bit_field.h"
|
||||||
|
#include "common/bit_set.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "video_core/regs_framebuffer.h"
|
#include "video_core/regs_framebuffer.h"
|
||||||
|
@ -14,6 +15,7 @@
|
||||||
#include "video_core/regs_rasterizer.h"
|
#include "video_core/regs_rasterizer.h"
|
||||||
#include "video_core/regs_texturing.h"
|
#include "video_core/regs_texturing.h"
|
||||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_util.h"
|
#include "video_core/renderer_opengl/gl_shader_util.h"
|
||||||
|
|
||||||
|
@ -22,6 +24,7 @@ using Pica::LightingRegs;
|
||||||
using Pica::RasterizerRegs;
|
using Pica::RasterizerRegs;
|
||||||
using Pica::TexturingRegs;
|
using Pica::TexturingRegs;
|
||||||
using TevStageConfig = TexturingRegs::TevStageConfig;
|
using TevStageConfig = TexturingRegs::TevStageConfig;
|
||||||
|
using VSOutputAttributes = RasterizerRegs::VSOutputAttributes;
|
||||||
|
|
||||||
namespace GLShader {
|
namespace GLShader {
|
||||||
|
|
||||||
|
@ -92,8 +95,8 @@ out gl_PerVertex {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) {
|
PicaFSConfig PicaFSConfig::BuildFromRegs(const Pica::Regs& regs) {
|
||||||
PicaShaderConfig res;
|
PicaFSConfig res;
|
||||||
|
|
||||||
auto& state = res.state;
|
auto& state = res.state;
|
||||||
|
|
||||||
|
@ -219,6 +222,59 @@ PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PicaShaderConfigCommon::Init(const Pica::ShaderRegs& regs, Pica::Shader::ShaderSetup& setup) {
|
||||||
|
program_hash = setup.GetProgramCodeHash();
|
||||||
|
swizzle_hash = setup.GetSwizzleDataHash();
|
||||||
|
main_offset = regs.main_offset;
|
||||||
|
sanitize_mul = false; // TODO (wwylele): stubbed now. Should sync with user settings
|
||||||
|
|
||||||
|
num_outputs = 0;
|
||||||
|
output_map.fill(16);
|
||||||
|
|
||||||
|
for (int reg : Common::BitSet<u32>(regs.output_mask)) {
|
||||||
|
output_map[reg] = num_outputs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PicaGSConfigCommonRaw::Init(const Pica::Regs& regs) {
|
||||||
|
vs_output_attributes = Common::BitSet<u32>(regs.vs.output_mask).Count();
|
||||||
|
gs_output_attributes = vs_output_attributes;
|
||||||
|
|
||||||
|
semantic_maps.fill({16, 0});
|
||||||
|
for (u32 attrib = 0; attrib < regs.rasterizer.vs_output_total; ++attrib) {
|
||||||
|
std::array<VSOutputAttributes::Semantic, 4> semantics = {
|
||||||
|
regs.rasterizer.vs_output_attributes[attrib].map_x,
|
||||||
|
regs.rasterizer.vs_output_attributes[attrib].map_y,
|
||||||
|
regs.rasterizer.vs_output_attributes[attrib].map_z,
|
||||||
|
regs.rasterizer.vs_output_attributes[attrib].map_w};
|
||||||
|
for (u32 comp = 0; comp < 4; ++comp) {
|
||||||
|
const auto semantic = semantics[comp];
|
||||||
|
if (static_cast<size_t>(semantic) < 24) {
|
||||||
|
semantic_maps[static_cast<size_t>(semantic)] = {attrib, comp};
|
||||||
|
} else if (semantic != VSOutputAttributes::INVALID) {
|
||||||
|
NGLOG_ERROR(Render_OpenGL, "Invalid/unknown semantic id: {}",
|
||||||
|
static_cast<u32>(semantic));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PicaGSConfigRaw::Init(const Pica::Regs& regs, Pica::Shader::ShaderSetup& setup) {
|
||||||
|
PicaShaderConfigCommon::Init(regs.gs, setup);
|
||||||
|
PicaGSConfigCommonRaw::Init(regs);
|
||||||
|
|
||||||
|
num_inputs = regs.gs.max_input_attribute_index + 1;
|
||||||
|
input_map.fill(16);
|
||||||
|
|
||||||
|
for (u32 attr = 0; attr < num_inputs; ++attr) {
|
||||||
|
input_map[regs.gs.GetRegisterForAttribute(attr)] = attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes_per_vertex = regs.pipeline.vs_outmap_total_minus_1_a + 1;
|
||||||
|
|
||||||
|
gs_output_attributes = num_outputs;
|
||||||
|
}
|
||||||
|
|
||||||
/// Detects if a TEV stage is configured to be skipped (to avoid generating unnecessary code)
|
/// Detects if a TEV stage is configured to be skipped (to avoid generating unnecessary code)
|
||||||
static bool IsPassThroughTevStage(const TevStageConfig& stage) {
|
static bool IsPassThroughTevStage(const TevStageConfig& stage) {
|
||||||
return (stage.color_op == TevStageConfig::Operation::Replace &&
|
return (stage.color_op == TevStageConfig::Operation::Replace &&
|
||||||
|
@ -230,7 +286,7 @@ static bool IsPassThroughTevStage(const TevStageConfig& stage) {
|
||||||
stage.GetColorMultiplier() == 1 && stage.GetAlphaMultiplier() == 1);
|
stage.GetColorMultiplier() == 1 && stage.GetAlphaMultiplier() == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string SampleTexture(const PicaShaderConfig& config, unsigned texture_unit) {
|
static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_unit) {
|
||||||
const auto& state = config.state;
|
const auto& state = config.state;
|
||||||
switch (texture_unit) {
|
switch (texture_unit) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -274,7 +330,7 @@ static std::string SampleTexture(const PicaShaderConfig& config, unsigned textur
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the specified TEV stage source component(s)
|
/// Writes the specified TEV stage source component(s)
|
||||||
static void AppendSource(std::string& out, const PicaShaderConfig& config,
|
static void AppendSource(std::string& out, const PicaFSConfig& config,
|
||||||
TevStageConfig::Source source, const std::string& index_name) {
|
TevStageConfig::Source source, const std::string& index_name) {
|
||||||
const auto& state = config.state;
|
const auto& state = config.state;
|
||||||
using Source = TevStageConfig::Source;
|
using Source = TevStageConfig::Source;
|
||||||
|
@ -317,7 +373,7 @@ static void AppendSource(std::string& out, const PicaShaderConfig& config,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the color components to use for the specified TEV stage color modifier
|
/// Writes the color components to use for the specified TEV stage color modifier
|
||||||
static void AppendColorModifier(std::string& out, const PicaShaderConfig& config,
|
static void AppendColorModifier(std::string& out, const PicaFSConfig& config,
|
||||||
TevStageConfig::ColorModifier modifier,
|
TevStageConfig::ColorModifier modifier,
|
||||||
TevStageConfig::Source source, const std::string& index_name) {
|
TevStageConfig::Source source, const std::string& index_name) {
|
||||||
using ColorModifier = TevStageConfig::ColorModifier;
|
using ColorModifier = TevStageConfig::ColorModifier;
|
||||||
|
@ -375,7 +431,7 @@ static void AppendColorModifier(std::string& out, const PicaShaderConfig& config
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the alpha component to use for the specified TEV stage alpha modifier
|
/// Writes the alpha component to use for the specified TEV stage alpha modifier
|
||||||
static void AppendAlphaModifier(std::string& out, const PicaShaderConfig& config,
|
static void AppendAlphaModifier(std::string& out, const PicaFSConfig& config,
|
||||||
TevStageConfig::AlphaModifier modifier,
|
TevStageConfig::AlphaModifier modifier,
|
||||||
TevStageConfig::Source source, const std::string& index_name) {
|
TevStageConfig::Source source, const std::string& index_name) {
|
||||||
using AlphaModifier = TevStageConfig::AlphaModifier;
|
using AlphaModifier = TevStageConfig::AlphaModifier;
|
||||||
|
@ -540,7 +596,7 @@ static void AppendAlphaTestCondition(std::string& out, FramebufferRegs::CompareF
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the code to emulate the specified TEV stage
|
/// Writes the code to emulate the specified TEV stage
|
||||||
static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsigned index) {
|
static void WriteTevStage(std::string& out, const PicaFSConfig& config, unsigned index) {
|
||||||
const auto stage =
|
const auto stage =
|
||||||
static_cast<const TexturingRegs::TevStageConfig>(config.state.tev_stages[index]);
|
static_cast<const TexturingRegs::TevStageConfig>(config.state.tev_stages[index]);
|
||||||
if (!IsPassThroughTevStage(stage)) {
|
if (!IsPassThroughTevStage(stage)) {
|
||||||
|
@ -598,7 +654,7 @@ static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsi
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the code to emulate fragment lighting
|
/// Writes the code to emulate fragment lighting
|
||||||
static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
|
static void WriteLighting(std::string& out, const PicaFSConfig& config) {
|
||||||
const auto& lighting = config.state.lighting;
|
const auto& lighting = config.state.lighting;
|
||||||
|
|
||||||
// Define lighting globals
|
// Define lighting globals
|
||||||
|
@ -994,7 +1050,7 @@ void AppendProcTexCombineAndMap(std::string& out, ProcTexCombiner combiner,
|
||||||
out += "ProcTexLookupLUT(" + map_lut + ", " + combined + ")";
|
out += "ProcTexLookupLUT(" + map_lut + ", " + combined + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppendProcTexSampler(std::string& out, const PicaShaderConfig& config) {
|
void AppendProcTexSampler(std::string& out, const PicaFSConfig& config) {
|
||||||
// LUT sampling uitlity
|
// LUT sampling uitlity
|
||||||
// For NoiseLUT/ColorMap/AlphaMap, coord=0.0 is lut[0], coord=127.0/128.0 is lut[127] and
|
// For NoiseLUT/ColorMap/AlphaMap, coord=0.0 is lut[0], coord=127.0/128.0 is lut[127] and
|
||||||
// coord=1.0 is lut[127]+lut_diff[127]. For other indices, the result is interpolated using
|
// coord=1.0 is lut[127]+lut_diff[127]. For other indices, the result is interpolated using
|
||||||
|
@ -1121,7 +1177,7 @@ float ProcTexNoiseCoef(vec2 x) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GenerateFragmentShader(const PicaShaderConfig& config, bool separable_shader) {
|
std::string GenerateFragmentShader(const PicaFSConfig& config, bool separable_shader) {
|
||||||
const auto& state = config.state;
|
const auto& state = config.state;
|
||||||
|
|
||||||
std::string out = "#version 330 core\n";
|
std::string out = "#version 330 core\n";
|
||||||
|
@ -1327,4 +1383,296 @@ void main() {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup& setup,
|
||||||
|
const PicaVSConfig& config,
|
||||||
|
bool separable_shader) {
|
||||||
|
std::string out = "#version 330 core\n";
|
||||||
|
if (separable_shader) {
|
||||||
|
out += "#extension GL_ARB_separate_shader_objects : enable\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
out += Pica::Shader::Decompiler::GetCommonDeclarations();
|
||||||
|
|
||||||
|
std::array<bool, 16> used_regs{};
|
||||||
|
auto get_input_reg = [&](u32 reg) -> std::string {
|
||||||
|
ASSERT(reg < 16);
|
||||||
|
used_regs[reg] = true;
|
||||||
|
return "vs_in_reg" + std::to_string(reg);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto get_output_reg = [&](u32 reg) -> std::string {
|
||||||
|
ASSERT(reg < 16);
|
||||||
|
if (config.state.output_map[reg] < config.state.num_outputs) {
|
||||||
|
return "vs_out_attr" + std::to_string(config.state.output_map[reg]);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
auto program_source_opt = Pica::Shader::Decompiler::DecompileProgram(
|
||||||
|
setup.program_code, setup.swizzle_data, config.state.main_offset, get_input_reg,
|
||||||
|
get_output_reg, config.state.sanitize_mul, false);
|
||||||
|
|
||||||
|
if (!program_source_opt)
|
||||||
|
return boost::none;
|
||||||
|
|
||||||
|
std::string& program_source = program_source_opt.get();
|
||||||
|
|
||||||
|
out += R"(
|
||||||
|
#define uniforms vs_uniforms
|
||||||
|
layout (std140) uniform vs_config {
|
||||||
|
pica_uniforms uniforms;
|
||||||
|
};
|
||||||
|
|
||||||
|
)";
|
||||||
|
// input attributes declaration
|
||||||
|
for (std::size_t i = 0; i < used_regs.size(); ++i) {
|
||||||
|
if (used_regs[i]) {
|
||||||
|
out += "layout(location = " + std::to_string(i) + ") in vec4 vs_in_reg" +
|
||||||
|
std::to_string(i) + ";\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out += "\n";
|
||||||
|
|
||||||
|
// output attributes declaration
|
||||||
|
for (u32 i = 0; i < config.state.num_outputs; ++i) {
|
||||||
|
out += (separable_shader ? "layout(location = " + std::to_string(i) + ")" : std::string{}) +
|
||||||
|
" out vec4 vs_out_attr" + std::to_string(i) + ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
out += "\nvoid main() {\n";
|
||||||
|
for (u32 i = 0; i < config.state.num_outputs; ++i) {
|
||||||
|
out += " vs_out_attr" + std::to_string(i) + " = vec4(0.0, 0.0, 0.0, 1.0);\n";
|
||||||
|
}
|
||||||
|
out += "\n exec_shader();\n}\n\n";
|
||||||
|
|
||||||
|
out += program_source;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string GetGSCommonSource(const PicaGSConfigCommonRaw& config, bool separable_shader) {
|
||||||
|
std::string out = GetVertexInterfaceDeclaration(true, separable_shader);
|
||||||
|
out += UniformBlockDef;
|
||||||
|
out += Pica::Shader::Decompiler::GetCommonDeclarations();
|
||||||
|
|
||||||
|
out += '\n';
|
||||||
|
for (u32 i = 0; i < config.vs_output_attributes; ++i) {
|
||||||
|
out += (separable_shader ? "layout(location = " + std::to_string(i) + ")" : std::string{}) +
|
||||||
|
" in vec4 vs_out_attr" + std::to_string(i) + "[];\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
out += R"(
|
||||||
|
#define uniforms gs_uniforms
|
||||||
|
layout (std140) uniform gs_config {
|
||||||
|
pica_uniforms uniforms;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Vertex {
|
||||||
|
)";
|
||||||
|
out += " vec4 attributes[" + std::to_string(config.gs_output_attributes) + "];\n";
|
||||||
|
out += "};\n\n";
|
||||||
|
|
||||||
|
auto semantic = [&config](VSOutputAttributes::Semantic slot_semantic) -> std::string {
|
||||||
|
u32 slot = static_cast<u32>(slot_semantic);
|
||||||
|
u32 attrib = config.semantic_maps[slot].attribute_index;
|
||||||
|
u32 comp = config.semantic_maps[slot].component_index;
|
||||||
|
if (attrib < config.gs_output_attributes) {
|
||||||
|
return "vtx.attributes[" + std::to_string(attrib) + "]." + "xyzw"[comp];
|
||||||
|
}
|
||||||
|
return "0.0";
|
||||||
|
};
|
||||||
|
|
||||||
|
out += "vec4 GetVertexQuaternion(Vertex vtx) {\n";
|
||||||
|
out += " return vec4(" + semantic(VSOutputAttributes::QUATERNION_X) + ", " +
|
||||||
|
semantic(VSOutputAttributes::QUATERNION_Y) + ", " +
|
||||||
|
semantic(VSOutputAttributes::QUATERNION_Z) + ", " +
|
||||||
|
semantic(VSOutputAttributes::QUATERNION_W) + ");\n";
|
||||||
|
out += "}\n\n";
|
||||||
|
|
||||||
|
out += "void EmitVtx(Vertex vtx, bool quats_opposite) {\n";
|
||||||
|
out += " vec4 vtx_pos = vec4(" + semantic(VSOutputAttributes::POSITION_X) + ", " +
|
||||||
|
semantic(VSOutputAttributes::POSITION_Y) + ", " +
|
||||||
|
semantic(VSOutputAttributes::POSITION_Z) + ", " +
|
||||||
|
semantic(VSOutputAttributes::POSITION_W) + ");\n";
|
||||||
|
out += " gl_Position = vtx_pos;\n";
|
||||||
|
out += " gl_ClipDistance[0] = -vtx_pos.z;\n"; // fixed PICA clipping plane z <= 0
|
||||||
|
out += " gl_ClipDistance[1] = dot(clip_coef, vtx_pos);\n\n";
|
||||||
|
|
||||||
|
out += " vec4 vtx_quat = GetVertexQuaternion(vtx);\n";
|
||||||
|
out += " normquat = mix(vtx_quat, -vtx_quat, bvec4(quats_opposite));\n\n";
|
||||||
|
|
||||||
|
out += " vec4 vtx_color = vec4(" + semantic(VSOutputAttributes::COLOR_R) + ", " +
|
||||||
|
semantic(VSOutputAttributes::COLOR_G) + ", " + semantic(VSOutputAttributes::COLOR_B) +
|
||||||
|
", " + semantic(VSOutputAttributes::COLOR_A) + ");\n";
|
||||||
|
out += " primary_color = min(abs(vtx_color), vec4(1.0));\n\n";
|
||||||
|
|
||||||
|
out += " texcoord0 = vec2(" + semantic(VSOutputAttributes::TEXCOORD0_U) + ", " +
|
||||||
|
semantic(VSOutputAttributes::TEXCOORD0_V) + ");\n";
|
||||||
|
out += " texcoord1 = vec2(" + semantic(VSOutputAttributes::TEXCOORD1_U) + ", " +
|
||||||
|
semantic(VSOutputAttributes::TEXCOORD1_V) + ");\n\n";
|
||||||
|
|
||||||
|
out += " texcoord0_w = " + semantic(VSOutputAttributes::TEXCOORD0_W) + ";\n";
|
||||||
|
out += " view = vec3(" + semantic(VSOutputAttributes::VIEW_X) + ", " +
|
||||||
|
semantic(VSOutputAttributes::VIEW_Y) + ", " + semantic(VSOutputAttributes::VIEW_Z) +
|
||||||
|
");\n\n";
|
||||||
|
|
||||||
|
out += " texcoord2 = vec2(" + semantic(VSOutputAttributes::TEXCOORD2_U) + ", " +
|
||||||
|
semantic(VSOutputAttributes::TEXCOORD2_V) + ");\n\n";
|
||||||
|
|
||||||
|
out += " EmitVertex();\n";
|
||||||
|
out += "}\n";
|
||||||
|
|
||||||
|
out += R"(
|
||||||
|
bool AreQuaternionsOpposite(vec4 qa, vec4 qb) {
|
||||||
|
return (dot(qa, qb) < 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitPrim(Vertex vtx0, Vertex vtx1, Vertex vtx2) {
|
||||||
|
EmitVtx(vtx0, false);
|
||||||
|
EmitVtx(vtx1, AreQuaternionsOpposite(GetVertexQuaternion(vtx0), GetVertexQuaternion(vtx1)));
|
||||||
|
EmitVtx(vtx2, AreQuaternionsOpposite(GetVertexQuaternion(vtx0), GetVertexQuaternion(vtx2)));
|
||||||
|
EndPrimitive();
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string GenerateFixedGeometryShader(const PicaFixedGSConfig& config, bool separable_shader) {
|
||||||
|
std::string out = "#version 330 core\n";
|
||||||
|
if (separable_shader) {
|
||||||
|
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
out += R"(
|
||||||
|
layout(triangles) in;
|
||||||
|
layout(triangle_strip, max_vertices = 3) out;
|
||||||
|
|
||||||
|
)";
|
||||||
|
|
||||||
|
out += GetGSCommonSource(config.state, separable_shader);
|
||||||
|
|
||||||
|
out += R"(
|
||||||
|
void main() {
|
||||||
|
Vertex prim_buffer[3];
|
||||||
|
)";
|
||||||
|
for (u32 vtx = 0; vtx < 3; ++vtx) {
|
||||||
|
out += " prim_buffer[" + std::to_string(vtx) + "].attributes = vec4[" +
|
||||||
|
std::to_string(config.state.gs_output_attributes) + "](";
|
||||||
|
for (u32 i = 0; i < config.state.vs_output_attributes; ++i) {
|
||||||
|
out += std::string(i == 0 ? "" : ", ") + "vs_out_attr" + std::to_string(i) + "[" +
|
||||||
|
std::to_string(vtx) + "]";
|
||||||
|
}
|
||||||
|
out += ");\n";
|
||||||
|
}
|
||||||
|
out += " EmitPrim(prim_buffer[0], prim_buffer[1], prim_buffer[2]);\n";
|
||||||
|
out += "}\n";
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::optional<std::string> GenerateGeometryShader(const Pica::Shader::ShaderSetup& setup,
|
||||||
|
const PicaGSConfig& config,
|
||||||
|
bool separable_shader) {
|
||||||
|
std::string out = "#version 330 core\n";
|
||||||
|
if (separable_shader) {
|
||||||
|
out += "#extension GL_ARB_separate_shader_objects : enable\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.state.num_inputs % config.state.attributes_per_vertex != 0)
|
||||||
|
return boost::none;
|
||||||
|
|
||||||
|
switch (config.state.num_inputs / config.state.attributes_per_vertex) {
|
||||||
|
case 1:
|
||||||
|
out += "layout(points) in;\n";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
out += "layout(lines) in;\n";
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
out += "layout(lines_adjacency) in;\n";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
out += "layout(triangles) in;\n";
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
out += "layout(triangles_adjacency) in;\n";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
out += "layout(triangle_strip, max_vertices = 30) out;\n\n";
|
||||||
|
|
||||||
|
out += GetGSCommonSource(config.state, separable_shader);
|
||||||
|
|
||||||
|
auto get_input_reg = [&](u32 reg) -> std::string {
|
||||||
|
ASSERT(reg < 16);
|
||||||
|
u32 attr = config.state.input_map[reg];
|
||||||
|
if (attr < config.state.num_inputs) {
|
||||||
|
return "vs_out_attr" + std::to_string(attr % config.state.attributes_per_vertex) + "[" +
|
||||||
|
std::to_string(attr / config.state.attributes_per_vertex) + "]";
|
||||||
|
}
|
||||||
|
return "vec4(0.0, 0.0, 0.0, 1.0)";
|
||||||
|
};
|
||||||
|
|
||||||
|
auto get_output_reg = [&](u32 reg) -> std::string {
|
||||||
|
ASSERT(reg < 16);
|
||||||
|
if (config.state.output_map[reg] < config.state.num_outputs) {
|
||||||
|
return "output_buffer.attributes[" + std::to_string(config.state.output_map[reg]) + "]";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
auto program_source_opt = Pica::Shader::Decompiler::DecompileProgram(
|
||||||
|
setup.program_code, setup.swizzle_data, config.state.main_offset, get_input_reg,
|
||||||
|
get_output_reg, config.state.sanitize_mul, true);
|
||||||
|
|
||||||
|
if (!program_source_opt)
|
||||||
|
return boost::none;
|
||||||
|
|
||||||
|
std::string& program_source = program_source_opt.get();
|
||||||
|
|
||||||
|
out += R"(
|
||||||
|
Vertex output_buffer;
|
||||||
|
Vertex prim_buffer[3];
|
||||||
|
uint vertex_id = 0u;
|
||||||
|
bool prim_emit = false;
|
||||||
|
bool winding = false;
|
||||||
|
|
||||||
|
void setemit(uint vertex_id_, bool prim_emit_, bool winding_) {
|
||||||
|
vertex_id = vertex_id_;
|
||||||
|
prim_emit = prim_emit_;
|
||||||
|
winding = winding_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emit() {
|
||||||
|
prim_buffer[vertex_id] = output_buffer;
|
||||||
|
|
||||||
|
if (prim_emit) {
|
||||||
|
if (winding) {
|
||||||
|
EmitPrim(prim_buffer[1], prim_buffer[0], prim_buffer[2]);
|
||||||
|
winding = false;
|
||||||
|
} else {
|
||||||
|
EmitPrim(prim_buffer[0], prim_buffer[1], prim_buffer[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
)";
|
||||||
|
for (u32 i = 0; i < config.state.num_outputs; ++i) {
|
||||||
|
out +=
|
||||||
|
" output_buffer.attributes[" + std::to_string(i) + "] = vec4(0.0, 0.0, 0.0, 1.0);\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute shader
|
||||||
|
out += "\n exec_shader();\n\n";
|
||||||
|
|
||||||
|
out += "}\n\n";
|
||||||
|
|
||||||
|
out += program_source;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace GLShader
|
} // namespace GLShader
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
#include "common/hash.h"
|
#include "common/hash.h"
|
||||||
#include "video_core/regs.h"
|
#include "video_core/regs.h"
|
||||||
#include "video_core/shader/shader.h"
|
#include "video_core/shader/shader.h"
|
||||||
|
@ -47,7 +48,7 @@ struct TevStageConfigRaw {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PicaShaderConfigState {
|
struct PicaFSConfigState {
|
||||||
Pica::FramebufferRegs::CompareFunc alpha_test_func;
|
Pica::FramebufferRegs::CompareFunc alpha_test_func;
|
||||||
Pica::RasterizerRegs::ScissorMode scissor_test_mode;
|
Pica::RasterizerRegs::ScissorMode scissor_test_mode;
|
||||||
Pica::TexturingRegs::TextureConfig::TextureType texture0_type;
|
Pica::TexturingRegs::TextureConfig::TextureType texture0_type;
|
||||||
|
@ -112,17 +113,17 @@ struct PicaShaderConfigState {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This struct contains all state used to generate the GLSL shader program that emulates the current
|
* This struct contains all state used to generate the GLSL fragment shader that emulates the
|
||||||
* Pica register configuration. This struct is used as a cache key for generated GLSL shader
|
* current Pica register configuration. This struct is used as a cache key for generated GLSL shader
|
||||||
* programs. The functions in gl_shader_gen.cpp should retrieve state from this struct only, not by
|
* programs. The functions in gl_shader_gen.cpp should retrieve state from this struct only, not by
|
||||||
* directly accessing Pica registers. This should reduce the risk of bugs in shader generation where
|
* directly accessing Pica registers. This should reduce the risk of bugs in shader generation where
|
||||||
* Pica state is not being captured in the shader cache key, thereby resulting in (what should be)
|
* Pica state is not being captured in the shader cache key, thereby resulting in (what should be)
|
||||||
* two separate shaders sharing the same key.
|
* two separate shaders sharing the same key.
|
||||||
*/
|
*/
|
||||||
struct PicaShaderConfig : Common::HashableStruct<PicaShaderConfigState> {
|
struct PicaFSConfig : Common::HashableStruct<PicaFSConfigState> {
|
||||||
|
|
||||||
/// Construct a PicaShaderConfig with the given Pica register configuration.
|
/// Construct a PicaFSConfig with the given Pica register configuration.
|
||||||
static PicaShaderConfig BuildFromRegs(const Pica::Regs& regs);
|
static PicaFSConfig BuildFromRegs(const Pica::Regs& regs);
|
||||||
|
|
||||||
bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
|
bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
|
||||||
return (stage_index < 4) && (state.combiner_buffer_input & (1 << stage_index));
|
return (stage_index < 4) && (state.combiner_buffer_input & (1 << stage_index));
|
||||||
|
@ -133,6 +134,79 @@ struct PicaShaderConfig : Common::HashableStruct<PicaShaderConfigState> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This struct contains common information to identify a GL vertex/geometry shader generated from
|
||||||
|
* PICA vertex/geometry shader.
|
||||||
|
*/
|
||||||
|
struct PicaShaderConfigCommon {
|
||||||
|
void Init(const Pica::ShaderRegs& regs, Pica::Shader::ShaderSetup& setup);
|
||||||
|
|
||||||
|
u64 program_hash;
|
||||||
|
u64 swizzle_hash;
|
||||||
|
u32 main_offset;
|
||||||
|
bool sanitize_mul;
|
||||||
|
|
||||||
|
u32 num_outputs;
|
||||||
|
|
||||||
|
// output_map[output register index] -> output attribute index
|
||||||
|
std::array<u32, 16> output_map;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This struct contains information to identify a GL vertex shader generated from PICA vertex
|
||||||
|
* shader.
|
||||||
|
*/
|
||||||
|
struct PicaVSConfig : Common::HashableStruct<PicaShaderConfigCommon> {
|
||||||
|
explicit PicaVSConfig(const Pica::Regs& regs, Pica::Shader::ShaderSetup& setup) {
|
||||||
|
state.Init(regs.vs, setup);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PicaGSConfigCommonRaw {
|
||||||
|
void Init(const Pica::Regs& regs);
|
||||||
|
|
||||||
|
u32 vs_output_attributes;
|
||||||
|
u32 gs_output_attributes;
|
||||||
|
|
||||||
|
struct SemanticMap {
|
||||||
|
u32 attribute_index;
|
||||||
|
u32 component_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
// semantic_maps[semantic name] -> GS output attribute index + component index
|
||||||
|
std::array<SemanticMap, 24> semantic_maps;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This struct contains information to identify a GL geometry shader generated from PICA no-geometry
|
||||||
|
* shader pipeline
|
||||||
|
*/
|
||||||
|
struct PicaFixedGSConfig : Common::HashableStruct<PicaGSConfigCommonRaw> {
|
||||||
|
explicit PicaFixedGSConfig(const Pica::Regs& regs) {
|
||||||
|
state.Init(regs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PicaGSConfigRaw : PicaShaderConfigCommon, PicaGSConfigCommonRaw {
|
||||||
|
void Init(const Pica::Regs& regs, Pica::Shader::ShaderSetup& setup);
|
||||||
|
|
||||||
|
u32 num_inputs;
|
||||||
|
u32 attributes_per_vertex;
|
||||||
|
|
||||||
|
// input_map[input register index] -> input attribute index
|
||||||
|
std::array<u32, 16> input_map;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This struct contains information to identify a GL geometry shader generated from PICA geometry
|
||||||
|
* shader.
|
||||||
|
*/
|
||||||
|
struct PicaGSConfig : Common::HashableStruct<PicaGSConfigRaw> {
|
||||||
|
explicit PicaGSConfig(const Pica::Regs& regs, Pica::Shader::ShaderSetup& setups) {
|
||||||
|
state.Init(regs, setups);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the GLSL vertex shader program source code that accepts vertices from software shader
|
* Generates the GLSL vertex shader program source code that accepts vertices from software shader
|
||||||
* and directly passes them to the fragment shader.
|
* and directly passes them to the fragment shader.
|
||||||
|
@ -141,6 +215,29 @@ struct PicaShaderConfig : Common::HashableStruct<PicaShaderConfigState> {
|
||||||
*/
|
*/
|
||||||
std::string GenerateTrivialVertexShader(bool separable_shader);
|
std::string GenerateTrivialVertexShader(bool separable_shader);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the GLSL vertex shader program source code for the given VS program
|
||||||
|
* @returns String of the shader source code; boost::none on failure
|
||||||
|
*/
|
||||||
|
boost::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup& setup,
|
||||||
|
const PicaVSConfig& config,
|
||||||
|
bool separable_shader);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generates the GLSL fixed geometry shader program source code for non-GS PICA pipeline
|
||||||
|
* @returns String of the shader source code
|
||||||
|
*/
|
||||||
|
std::string GenerateFixedGeometryShader(const PicaFixedGSConfig& config, bool separable_shader);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the GLSL geometry shader program source code for the given GS program and its
|
||||||
|
* configuration
|
||||||
|
* @returns String of the shader source code; boost::none on failure
|
||||||
|
*/
|
||||||
|
boost::optional<std::string> GenerateGeometryShader(const Pica::Shader::ShaderSetup& setup,
|
||||||
|
const PicaGSConfig& config,
|
||||||
|
bool separable_shader);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the GLSL fragment shader program source code for the current Pica state
|
* Generates the GLSL fragment shader program source code for the current Pica state
|
||||||
* @param config ShaderCacheKey object generated for the current Pica state, used for the shader
|
* @param config ShaderCacheKey object generated for the current Pica state, used for the shader
|
||||||
|
@ -148,14 +245,35 @@ std::string GenerateTrivialVertexShader(bool separable_shader);
|
||||||
* @param separable_shader generates shader that can be used for separate shader object
|
* @param separable_shader generates shader that can be used for separate shader object
|
||||||
* @returns String of the shader source code
|
* @returns String of the shader source code
|
||||||
*/
|
*/
|
||||||
std::string GenerateFragmentShader(const PicaShaderConfig& config, bool separable_shader);
|
std::string GenerateFragmentShader(const PicaFSConfig& config, bool separable_shader);
|
||||||
|
|
||||||
} // namespace GLShader
|
} // namespace GLShader
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
template <>
|
template <>
|
||||||
struct hash<GLShader::PicaShaderConfig> {
|
struct hash<GLShader::PicaFSConfig> {
|
||||||
size_t operator()(const GLShader::PicaShaderConfig& k) const {
|
size_t operator()(const GLShader::PicaFSConfig& k) const {
|
||||||
|
return k.Hash();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<GLShader::PicaVSConfig> {
|
||||||
|
size_t operator()(const GLShader::PicaVSConfig& k) const {
|
||||||
|
return k.Hash();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<GLShader::PicaFixedGSConfig> {
|
||||||
|
size_t operator()(const GLShader::PicaFixedGSConfig& k) const {
|
||||||
|
return k.Hash();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<GLShader::PicaGSConfig> {
|
||||||
|
size_t operator()(const GLShader::PicaGSConfig& k) const {
|
||||||
return k.Hash();
|
return k.Hash();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <boost/functional/hash.hpp>
|
#include <boost/functional/hash.hpp>
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
|
@ -23,6 +24,8 @@ static void SetShaderUniformBlockBinding(GLuint shader, const char* name, Unifor
|
||||||
static void SetShaderUniformBlockBindings(GLuint shader) {
|
static void SetShaderUniformBlockBindings(GLuint shader) {
|
||||||
SetShaderUniformBlockBinding(shader, "shader_data", UniformBindings::Common,
|
SetShaderUniformBlockBinding(shader, "shader_data", UniformBindings::Common,
|
||||||
sizeof(UniformData));
|
sizeof(UniformData));
|
||||||
|
SetShaderUniformBlockBinding(shader, "vs_config", UniformBindings::VS, sizeof(VSUniformData));
|
||||||
|
SetShaderUniformBlockBinding(shader, "gs_config", UniformBindings::GS, sizeof(GSUniformData));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SetShaderSamplerBinding(GLuint shader, const char* name,
|
static void SetShaderSamplerBinding(GLuint shader, const char* name,
|
||||||
|
@ -57,6 +60,21 @@ static void SetShaderSamplerBindings(GLuint shader) {
|
||||||
cur_state.Apply();
|
cur_state.Apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PicaUniformsData::SetFromRegs(const Pica::ShaderRegs& regs,
|
||||||
|
const Pica::Shader::ShaderSetup& setup) {
|
||||||
|
std::transform(std::begin(setup.uniforms.b), std::end(setup.uniforms.b), std::begin(bools),
|
||||||
|
[](bool value) -> BoolAligned { return {value ? GL_TRUE : GL_FALSE}; });
|
||||||
|
std::transform(std::begin(regs.int_uniforms), std::end(regs.int_uniforms), std::begin(i),
|
||||||
|
[](const auto& value) -> GLuvec4 {
|
||||||
|
return {value.x.Value(), value.y.Value(), value.z.Value(), value.w.Value()};
|
||||||
|
});
|
||||||
|
std::transform(std::begin(setup.uniforms.f), std::end(setup.uniforms.f), std::begin(f),
|
||||||
|
[](const auto& value) -> GLvec4 {
|
||||||
|
return {value.x.ToFloat32(), value.y.ToFloat32(), value.z.ToFloat32(),
|
||||||
|
value.w.ToFloat32()};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object representing a shader program staging. It can be either a shader object or a program
|
* An object representing a shader program staging. It can be either a shader object or a program
|
||||||
* object, depending on whether separable program is used.
|
* object, depending on whether separable program is used.
|
||||||
|
@ -128,13 +146,70 @@ private:
|
||||||
std::unordered_map<KeyConfigType, OGLShaderStage> shaders;
|
std::unordered_map<KeyConfigType, OGLShaderStage> shaders;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is a cache designed for shaders translated from PICA shaders. The first cache matches the
|
||||||
|
// config structure like a normal cache does. On cache miss, the second cache matches the generated
|
||||||
|
// GLSL code. The configuration is like this because there might be leftover code in the PICA shader
|
||||||
|
// program buffer from the previous shader, which is hashed into the config, resulting several
|
||||||
|
// different config values from the same shader program.
|
||||||
|
template <typename KeyConfigType,
|
||||||
|
boost::optional<std::string> (*CodeGenerator)(const Pica::Shader::ShaderSetup&,
|
||||||
|
const KeyConfigType&, bool),
|
||||||
|
GLenum ShaderType>
|
||||||
|
class ShaderDoubleCache {
|
||||||
|
public:
|
||||||
|
explicit ShaderDoubleCache(bool separable) : separable(separable) {}
|
||||||
|
GLuint Get(const KeyConfigType& key, const Pica::Shader::ShaderSetup& setup) {
|
||||||
|
auto map_it = shader_map.find(key);
|
||||||
|
if (map_it == shader_map.end()) {
|
||||||
|
auto program_opt = CodeGenerator(setup, key, separable);
|
||||||
|
if (!program_opt) {
|
||||||
|
shader_map[key] = nullptr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string& program = program_opt.get();
|
||||||
|
auto [iter, new_shader] = shader_cache.emplace(program, OGLShaderStage{separable});
|
||||||
|
OGLShaderStage& cached_shader = iter->second;
|
||||||
|
if (new_shader) {
|
||||||
|
cached_shader.Create(program.c_str(), ShaderType);
|
||||||
|
}
|
||||||
|
shader_map[key] = &cached_shader;
|
||||||
|
return cached_shader.GetHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map_it->second == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return map_it->second->GetHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool separable;
|
||||||
|
std::unordered_map<KeyConfigType, OGLShaderStage*> shader_map;
|
||||||
|
std::unordered_map<std::string, OGLShaderStage> shader_cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ProgrammableVertexShaders =
|
||||||
|
ShaderDoubleCache<GLShader::PicaVSConfig, &GLShader::GenerateVertexShader, GL_VERTEX_SHADER>;
|
||||||
|
|
||||||
|
using ProgrammableGeometryShaders =
|
||||||
|
ShaderDoubleCache<GLShader::PicaGSConfig, &GLShader::GenerateGeometryShader,
|
||||||
|
GL_GEOMETRY_SHADER>;
|
||||||
|
|
||||||
|
using FixedGeometryShaders =
|
||||||
|
ShaderCache<GLShader::PicaFixedGSConfig, &GLShader::GenerateFixedGeometryShader,
|
||||||
|
GL_GEOMETRY_SHADER>;
|
||||||
|
|
||||||
using FragmentShaders =
|
using FragmentShaders =
|
||||||
ShaderCache<GLShader::PicaShaderConfig, &GLShader::GenerateFragmentShader, GL_FRAGMENT_SHADER>;
|
ShaderCache<GLShader::PicaFSConfig, &GLShader::GenerateFragmentShader, GL_FRAGMENT_SHADER>;
|
||||||
|
|
||||||
class ShaderProgramManager::Impl {
|
class ShaderProgramManager::Impl {
|
||||||
public:
|
public:
|
||||||
explicit Impl(bool separable)
|
explicit Impl(bool separable)
|
||||||
: separable(separable), trivial_vertex_shader(separable), fragment_shaders(separable) {
|
: separable(separable), programmable_vertex_shaders(separable),
|
||||||
|
trivial_vertex_shader(separable), programmable_geometry_shaders(separable),
|
||||||
|
fixed_geometry_shaders(separable), fragment_shaders(separable) {
|
||||||
if (separable)
|
if (separable)
|
||||||
pipeline.Create();
|
pipeline.Create();
|
||||||
}
|
}
|
||||||
|
@ -165,8 +240,12 @@ public:
|
||||||
|
|
||||||
ShaderTuple current;
|
ShaderTuple current;
|
||||||
|
|
||||||
|
ProgrammableVertexShaders programmable_vertex_shaders;
|
||||||
TrivialVertexShader trivial_vertex_shader;
|
TrivialVertexShader trivial_vertex_shader;
|
||||||
|
|
||||||
|
ProgrammableGeometryShaders programmable_geometry_shaders;
|
||||||
|
FixedGeometryShaders fixed_geometry_shaders;
|
||||||
|
|
||||||
FragmentShaders fragment_shaders;
|
FragmentShaders fragment_shaders;
|
||||||
|
|
||||||
bool separable;
|
bool separable;
|
||||||
|
@ -179,15 +258,37 @@ ShaderProgramManager::ShaderProgramManager(bool separable)
|
||||||
|
|
||||||
ShaderProgramManager::~ShaderProgramManager() = default;
|
ShaderProgramManager::~ShaderProgramManager() = default;
|
||||||
|
|
||||||
|
bool ShaderProgramManager::UseProgrammableVertexShader(const GLShader::PicaVSConfig& config,
|
||||||
|
const Pica::Shader::ShaderSetup setup) {
|
||||||
|
GLuint handle = impl->programmable_vertex_shaders.Get(config, setup);
|
||||||
|
if (handle == 0)
|
||||||
|
return false;
|
||||||
|
impl->current.vs = handle;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void ShaderProgramManager::UseTrivialVertexShader() {
|
void ShaderProgramManager::UseTrivialVertexShader() {
|
||||||
impl->current.vs = impl->trivial_vertex_shader.Get();
|
impl->current.vs = impl->trivial_vertex_shader.Get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShaderProgramManager::UseProgrammableGeometryShader(const GLShader::PicaGSConfig& config,
|
||||||
|
const Pica::Shader::ShaderSetup setup) {
|
||||||
|
GLuint handle = impl->programmable_geometry_shaders.Get(config, setup);
|
||||||
|
if (handle == 0)
|
||||||
|
return false;
|
||||||
|
impl->current.gs = handle;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderProgramManager::UseFixedGeometryShader(const GLShader::PicaFixedGSConfig& config) {
|
||||||
|
impl->current.gs = impl->fixed_geometry_shaders.Get(config);
|
||||||
|
}
|
||||||
|
|
||||||
void ShaderProgramManager::UseTrivialGeometryShader() {
|
void ShaderProgramManager::UseTrivialGeometryShader() {
|
||||||
impl->current.gs = 0;
|
impl->current.gs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderProgramManager::UseFragmentShader(const GLShader::PicaShaderConfig& config) {
|
void ShaderProgramManager::UseFragmentShader(const GLShader::PicaFSConfig& config) {
|
||||||
impl->current.fs = impl->fragment_shaders.Get(config);
|
impl->current.fs = impl->fragment_shaders.Get(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
||||||
#include "video_core/renderer_opengl/pica_to_gl.h"
|
#include "video_core/renderer_opengl/pica_to_gl.h"
|
||||||
|
|
||||||
enum class UniformBindings : GLuint { Common };
|
enum class UniformBindings : GLuint { Common, VS, GS };
|
||||||
|
|
||||||
struct LightSrc {
|
struct LightSrc {
|
||||||
alignas(16) GLvec3 specular_0;
|
alignas(16) GLvec3 specular_0;
|
||||||
|
@ -53,17 +53,57 @@ static_assert(
|
||||||
static_assert(sizeof(UniformData) < 16384,
|
static_assert(sizeof(UniformData) < 16384,
|
||||||
"UniformData structure must be less than 16kb as per the OpenGL spec");
|
"UniformData structure must be less than 16kb as per the OpenGL spec");
|
||||||
|
|
||||||
|
/// Uniform struct for the Uniform Buffer Object that contains PICA vertex/geometry shader uniforms.
|
||||||
|
// NOTE: the same rule from UniformData also applies here.
|
||||||
|
struct PicaUniformsData {
|
||||||
|
void SetFromRegs(const Pica::ShaderRegs& regs, const Pica::Shader::ShaderSetup& setup);
|
||||||
|
|
||||||
|
struct BoolAligned {
|
||||||
|
alignas(16) GLint b;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::array<BoolAligned, 16> bools;
|
||||||
|
alignas(16) std::array<GLuvec4, 4> i;
|
||||||
|
alignas(16) std::array<GLvec4, 96> f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VSUniformData {
|
||||||
|
PicaUniformsData uniforms;
|
||||||
|
};
|
||||||
|
static_assert(
|
||||||
|
sizeof(VSUniformData) == 1856,
|
||||||
|
"The size of the VSUniformData structure has changed, update the structure in the shader");
|
||||||
|
static_assert(sizeof(VSUniformData) < 16384,
|
||||||
|
"VSUniformData structure must be less than 16kb as per the OpenGL spec");
|
||||||
|
|
||||||
|
struct GSUniformData {
|
||||||
|
PicaUniformsData uniforms;
|
||||||
|
};
|
||||||
|
static_assert(
|
||||||
|
sizeof(GSUniformData) == 1856,
|
||||||
|
"The size of the GSUniformData structure has changed, update the structure in the shader");
|
||||||
|
static_assert(sizeof(GSUniformData) < 16384,
|
||||||
|
"GSUniformData structure must be less than 16kb as per the OpenGL spec");
|
||||||
|
|
||||||
/// A class that manage different shader stages and configures them with given config data.
|
/// A class that manage different shader stages and configures them with given config data.
|
||||||
class ShaderProgramManager {
|
class ShaderProgramManager {
|
||||||
public:
|
public:
|
||||||
explicit ShaderProgramManager(bool separable);
|
explicit ShaderProgramManager(bool separable);
|
||||||
~ShaderProgramManager();
|
~ShaderProgramManager();
|
||||||
|
|
||||||
|
bool UseProgrammableVertexShader(const GLShader::PicaVSConfig& config,
|
||||||
|
const Pica::Shader::ShaderSetup setup);
|
||||||
|
|
||||||
void UseTrivialVertexShader();
|
void UseTrivialVertexShader();
|
||||||
|
|
||||||
|
bool UseProgrammableGeometryShader(const GLShader::PicaGSConfig& config,
|
||||||
|
const Pica::Shader::ShaderSetup setup);
|
||||||
|
|
||||||
|
void UseFixedGeometryShader(const GLShader::PicaFixedGSConfig& config);
|
||||||
|
|
||||||
void UseTrivialGeometryShader();
|
void UseTrivialGeometryShader();
|
||||||
|
|
||||||
void UseFragmentShader(const GLShader::PicaShaderConfig& config);
|
void UseFragmentShader(const GLShader::PicaFSConfig& config);
|
||||||
|
|
||||||
void ApplyTo(OpenGLState& state);
|
void ApplyTo(OpenGLState& state);
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,10 @@ using GLvec2 = std::array<GLfloat, 2>;
|
||||||
using GLvec3 = std::array<GLfloat, 3>;
|
using GLvec3 = std::array<GLfloat, 3>;
|
||||||
using GLvec4 = std::array<GLfloat, 4>;
|
using GLvec4 = std::array<GLfloat, 4>;
|
||||||
|
|
||||||
|
using GLuvec2 = std::array<GLuint, 2>;
|
||||||
|
using GLuvec3 = std::array<GLuint, 3>;
|
||||||
|
using GLuvec4 = std::array<GLuint, 4>;
|
||||||
|
|
||||||
namespace PicaToGL {
|
namespace PicaToGL {
|
||||||
|
|
||||||
inline GLenum TextureFilterMode(Pica::TexturingRegs::TextureConfig::TextureFilter mode) {
|
inline GLenum TextureFilterMode(Pica::TexturingRegs::TextureConfig::TextureFilter mode) {
|
||||||
|
|
Loading…
Reference in a new issue