opengl: remove hw geometry shader related stuff

This commit is contained in:
Weiyi Wang 2019-08-18 20:07:50 -04:00
parent 1cf75e55c2
commit dd3ba7bd21
8 changed files with 32 additions and 283 deletions

View file

@ -104,8 +104,6 @@ RasterizerOpenGL::RasterizerOpenGL(Frontend::EmuWindow& window)
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment);
uniform_size_aligned_vs =
Common::AlignUp<std::size_t>(sizeof(VSUniformData), uniform_buffer_alignment);
uniform_size_aligned_gs =
Common::AlignUp<std::size_t>(sizeof(GSUniformData), uniform_buffer_alignment);
uniform_size_aligned_fs =
Common::AlignUp<std::size_t>(sizeof(UniformData), uniform_buffer_alignment);
@ -392,8 +390,7 @@ bool RasterizerOpenGL::SetupGeometryShader() {
shader_program_manager->UseFixedGeometryShader(gs_config);
return true;
} else {
PicaGSConfig gs_config(regs, Pica::g_state.gs);
return shader_program_manager->UseProgrammableGeometryShader(gs_config, Pica::g_state.gs);
LOG_ERROR(Render_OpenGL, "Accelerate draw doesn't support geometry shader");
}
}
@ -417,42 +414,24 @@ bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) {
return Draw(true, is_indexed);
}
static GLenum GetCurrentPrimitiveMode(bool use_gs) {
static GLenum GetCurrentPrimitiveMode() {
const auto& regs = Pica::g_state.regs;
if (use_gs) {
switch ((regs.gs.max_input_attribute_index + 1) /
(regs.pipeline.vs_outmap_total_minus_1_a + 1)) {
case 1:
return GL_POINTS;
case 2:
return GL_LINES;
case 4:
return GL_LINES_ADJACENCY;
case 3:
return GL_TRIANGLES;
case 6:
return GL_TRIANGLES_ADJACENCY;
default:
UNREACHABLE();
}
} else {
switch (regs.pipeline.triangle_topology) {
case Pica::PipelineRegs::TriangleTopology::Shader:
case Pica::PipelineRegs::TriangleTopology::List:
return GL_TRIANGLES;
case Pica::PipelineRegs::TriangleTopology::Fan:
return GL_TRIANGLE_FAN;
case Pica::PipelineRegs::TriangleTopology::Strip:
return GL_TRIANGLE_STRIP;
default:
UNREACHABLE();
}
switch (regs.pipeline.triangle_topology) {
case Pica::PipelineRegs::TriangleTopology::Shader:
case Pica::PipelineRegs::TriangleTopology::List:
return GL_TRIANGLES;
case Pica::PipelineRegs::TriangleTopology::Fan:
return GL_TRIANGLE_FAN;
case Pica::PipelineRegs::TriangleTopology::Strip:
return GL_TRIANGLE_STRIP;
default:
UNREACHABLE();
}
}
bool RasterizerOpenGL::AccelerateDrawBatchInternal(bool is_indexed, bool use_gs) {
bool RasterizerOpenGL::AccelerateDrawBatchInternal(bool is_indexed) {
const auto& regs = Pica::g_state.regs;
GLenum primitive_mode = GetCurrentPrimitiveMode(use_gs);
GLenum primitive_mode = GetCurrentPrimitiveMode();
auto [vs_input_index_min, vs_input_index_max, vs_input_size] = AnalyzeVertexArray(is_indexed);
@ -787,8 +766,7 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
SyncAndUploadLUTs();
// Sync the uniform data
const bool use_gs = regs.pipeline.use_gs == Pica::PipelineRegs::UseGS::Yes;
UploadUniforms(accelerate, use_gs);
UploadUniforms(accelerate);
// Viewport can have negative offsets or larger
// dimensions than our framebuffer sub-rect.
@ -804,7 +782,7 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
// Draw the vertex batch
bool succeeded = true;
if (accelerate) {
succeeded = AccelerateDrawBatchInternal(is_indexed, use_gs);
succeeded = AccelerateDrawBatchInternal(is_indexed);
} else {
state.draw.vertex_array = sw_vao.handle;
state.draw.vertex_buffer = vertex_buffer.GetHandle();
@ -2119,21 +2097,19 @@ void RasterizerOpenGL::SyncAndUploadLUTs() {
texture_buffer.Unmap(bytes_used);
}
void RasterizerOpenGL::UploadUniforms(bool accelerate_draw, bool use_gs) {
void RasterizerOpenGL::UploadUniforms(bool accelerate_draw) {
// glBindBufferRange below also changes the generic buffer binding point, so we sync the state
// first
state.draw.uniform_buffer = uniform_buffer.GetHandle();
state.Apply();
bool sync_vs = accelerate_draw;
bool sync_gs = accelerate_draw && use_gs;
bool sync_fs = uniform_block_data.dirty;
if (!sync_vs && !sync_gs && !sync_fs)
if (!sync_vs && !sync_fs)
return;
std::size_t uniform_size =
uniform_size_aligned_vs + uniform_size_aligned_gs + uniform_size_aligned_fs;
std::size_t uniform_size = uniform_size_aligned_vs + uniform_size_aligned_fs;
std::size_t used_bytes = 0;
u8* uniforms;
GLintptr offset;
@ -2150,15 +2126,6 @@ void RasterizerOpenGL::UploadUniforms(bool accelerate_draw, bool use_gs) {
used_bytes += uniform_size_aligned_vs;
}
if (sync_gs) {
GSUniformData gs_uniforms;
gs_uniforms.uniforms.SetFromRegs(Pica::g_state.regs.gs, Pica::g_state.gs);
std::memcpy(uniforms + used_bytes, &gs_uniforms, sizeof(gs_uniforms));
glBindBufferRange(GL_UNIFORM_BUFFER, static_cast<GLuint>(UniformBindings::GS),
uniform_buffer.GetHandle(), offset + used_bytes, sizeof(GSUniformData));
used_bytes += uniform_size_aligned_gs;
}
if (sync_fs || invalidate) {
std::memcpy(uniforms + used_bytes, &uniform_block_data.data, sizeof(UniformData));
glBindBufferRange(GL_UNIFORM_BUFFER, static_cast<GLuint>(UniformBindings::Common),

View file

@ -231,13 +231,13 @@ private:
void SyncAndUploadLUTs();
/// Upload the uniform blocks to the uniform buffer object
void UploadUniforms(bool accelerate_draw, bool use_gs);
void UploadUniforms(bool accelerate_draw);
/// Generic draw function for DrawTriangles and AccelerateDrawBatch
bool Draw(bool accelerate, bool is_indexed);
/// Internal implementation for AccelerateDrawBatch
bool AccelerateDrawBatchInternal(bool is_indexed, bool use_gs);
bool AccelerateDrawBatchInternal(bool is_indexed);
struct VertexArrayInfo {
u32 vs_input_index_min;
@ -304,7 +304,6 @@ private:
OGLFramebuffer framebuffer;
GLint uniform_buffer_alignment;
std::size_t uniform_size_aligned_vs;
std::size_t uniform_size_aligned_gs;
std::size_t uniform_size_aligned_fs;
SamplerInfo texture_cube_sampler;

View file

@ -249,10 +249,10 @@ public:
GLSLGenerator(const std::set<Subroutine>& subroutines, const ProgramCode& program_code,
const SwizzleData& swizzle_data, u32 main_offset,
const RegGetter& inputreg_getter, const RegGetter& outputreg_getter,
bool sanitize_mul, bool is_gs)
bool sanitize_mul)
: subroutines(subroutines), program_code(program_code), swizzle_data(swizzle_data),
main_offset(main_offset), inputreg_getter(inputreg_getter),
outputreg_getter(outputreg_getter), sanitize_mul(sanitize_mul), is_gs(is_gs) {
outputreg_getter(outputreg_getter), sanitize_mul(sanitize_mul) {
Generate();
}
@ -342,13 +342,6 @@ private:
/// Generates code representing a bool uniform
std::string GetUniformBool(u32 index) const {
if (is_gs && index == 15) {
// In PICA geometry shader, b15 is set to true after every geometry shader invocation.
// Accessing b15 usually indicates that the program relies on register value
// preservation across invocation (and therefore it uses b15 to determine whether to
// initialize the registers), which cannot be implemented in GL shaders.
throw DecompileFail("b15 access in geometry shader");
}
return "uniforms.b[" + std::to_string(index) + "]";
}
@ -751,22 +744,10 @@ private:
break;
}
case OpCode::Id::EMIT: {
if (is_gs) {
shader.AddLine("emit();");
}
case OpCode::Id::EMIT:
case OpCode::Id::SETEMIT:
LOG_ERROR(HW_GPU, "Geometry shader operation detected in vertex shader");
break;
}
case OpCode::Id::SETEMIT: {
if (is_gs) {
ASSERT(instr.setemit.vertex_id < 3);
shader.AddLine("setemit(" + std::to_string(instr.setemit.vertex_id) + "u, " +
((instr.setemit.prim_emit != 0) ? "true" : "false") + ", " +
((instr.setemit.winding != 0) ? "true" : "false") + ");");
}
break;
}
default: {
LOG_ERROR(HW_GPU, "Unhandled instruction: 0x{:02x} ({}): 0x{:08x}",
@ -890,7 +871,6 @@ private:
const RegGetter& inputreg_getter;
const RegGetter& outputreg_getter;
const bool sanitize_mul;
const bool is_gs;
ShaderWriter shader;
};
@ -911,13 +891,12 @@ bool exec_shader();
std::optional<std::string> DecompileProgram(const ProgramCode& program_code,
const SwizzleData& swizzle_data, u32 main_offset,
const RegGetter& inputreg_getter,
const RegGetter& outputreg_getter, bool sanitize_mul,
bool is_gs) {
const RegGetter& outputreg_getter, bool sanitize_mul) {
try {
auto subroutines = ControlFlowAnalyzer(program_code, main_offset).MoveSubroutines();
GLSLGenerator generator(subroutines, program_code, swizzle_data, main_offset,
inputreg_getter, outputreg_getter, sanitize_mul, is_gs);
inputreg_getter, outputreg_getter, sanitize_mul);
return generator.MoveShaderCode();
} catch (const DecompileFail& exception) {
LOG_INFO(HW_GPU, "Shader decompilation failed: {}", exception.what());

View file

@ -20,7 +20,6 @@ std::string GetCommonDeclarations();
std::optional<std::string> DecompileProgram(const ProgramCode& program_code,
const SwizzleData& swizzle_data, u32 main_offset,
const RegGetter& inputreg_getter,
const RegGetter& outputreg_getter, bool sanitize_mul,
bool is_gs);
const RegGetter& outputreg_getter, bool sanitize_mul);
} // namespace OpenGL::ShaderDecompiler

View file

@ -283,22 +283,6 @@ void PicaGSConfigCommonRaw::Init(const Pica::Regs& regs) {
}
}
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)
static bool IsPassThroughTevStage(const TevStageConfig& stage) {
return (stage.color_op == TevStageConfig::Operation::Replace &&
@ -1675,7 +1659,7 @@ std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup&
auto program_source_opt = ShaderDecompiler::DecompileProgram(
setup.program_code, setup.swizzle_data, config.state.main_offset, get_input_reg,
get_output_reg, config.state.sanitize_mul, false);
get_output_reg, config.state.sanitize_mul);
if (!program_source_opt)
return {};
@ -1727,11 +1711,6 @@ static std::string GetGSCommonSource(const PicaGSConfigCommonRaw& config, bool s
}
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";
@ -1838,116 +1817,4 @@ void main() {
return out;
}
std::optional<std::string> GenerateGeometryShader(const Pica::Shader::ShaderSetup& setup,
const PicaGSConfig& config,
bool separable_shader) {
std::string out = "";
if (separable_shader) {
out += "#extension GL_ARB_separate_shader_objects : enable\n";
}
if (config.state.num_inputs % config.state.attributes_per_vertex != 0)
return {};
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 {};
}
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 = ShaderDecompiler::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 {};
std::string& program_source = *program_source_opt;
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_);
void emit();
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";
// Put the definition of setemit and emit after main to avoid spurious warning about
// uninitialized output_buffer in some drivers
out += R"(
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]);
}
}
}
)";
out += program_source;
return out;
}
} // namespace OpenGL

View file

@ -195,26 +195,6 @@ struct PicaFixedGSConfig : Common::HashableStruct<PicaGSConfigCommonRaw> {
}
};
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
* and directly passes them to the fragment shader.
@ -236,15 +216,6 @@ std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup&
*/
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
*/
std::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
* @param config ShaderCacheKey object generated for the current Pica state, used for the shader
@ -277,11 +248,4 @@ struct hash<OpenGL::PicaFixedGSConfig> {
return k.Hash();
}
};
template <>
struct hash<OpenGL::PicaGSConfig> {
std::size_t operator()(const OpenGL::PicaGSConfig& k) const {
return k.Hash();
}
};
} // namespace std

View file

@ -27,7 +27,6 @@ static void SetShaderUniformBlockBindings(GLuint shader) {
SetShaderUniformBlockBinding(shader, "shader_data", UniformBindings::Common,
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,
@ -205,9 +204,6 @@ private:
using ProgrammableVertexShaders =
ShaderDoubleCache<PicaVSConfig, &GenerateVertexShader, GL_VERTEX_SHADER>;
using ProgrammableGeometryShaders =
ShaderDoubleCache<PicaGSConfig, &GenerateGeometryShader, GL_GEOMETRY_SHADER>;
using FixedGeometryShaders =
ShaderCache<PicaFixedGSConfig, &GenerateFixedGeometryShader, GL_GEOMETRY_SHADER>;
@ -217,8 +213,8 @@ class ShaderProgramManager::Impl {
public:
explicit Impl(bool separable, bool is_amd)
: is_amd(is_amd), separable(separable), programmable_vertex_shaders(separable),
trivial_vertex_shader(separable), programmable_geometry_shaders(separable),
fixed_geometry_shaders(separable), fragment_shaders(separable) {
trivial_vertex_shader(separable), fixed_geometry_shaders(separable),
fragment_shaders(separable) {
if (separable)
pipeline.Create();
}
@ -254,7 +250,6 @@ public:
ProgrammableVertexShaders programmable_vertex_shaders;
TrivialVertexShader trivial_vertex_shader;
ProgrammableGeometryShaders programmable_geometry_shaders;
FixedGeometryShaders fixed_geometry_shaders;
FragmentShaders fragment_shaders;
@ -282,15 +277,6 @@ void ShaderProgramManager::UseTrivialVertexShader() {
impl->current.vs = impl->trivial_vertex_shader.Get();
}
bool ShaderProgramManager::UseProgrammableGeometryShader(const 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 PicaFixedGSConfig& config) {
impl->current.gs = impl->fixed_geometry_shaders.Get(config);
}

View file

@ -91,15 +91,6 @@ static_assert(
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.
class ShaderProgramManager {
public:
@ -111,9 +102,6 @@ public:
void UseTrivialVertexShader();
bool UseProgrammableGeometryShader(const PicaGSConfig& config,
const Pica::Shader::ShaderSetup setup);
void UseFixedGeometryShader(const PicaFixedGSConfig& config);
void UseTrivialGeometryShader();