420cc13248
Add code required to use OpenGL assembly programs based on NV_gpu_program5. Decompilation for ARB programs is intended to be added in a follow up commit. This does **not** include ARB decompilation and it's not in an usable state. The intention behind assembly programs is to reduce shader stutter significantly on drivers supporting NV_gpu_program5 (and other required extensions). Currently only Nvidia's proprietary driver supports these extensions. Add a UI option hidden for now to avoid people enabling this option accidentally. This code path has some limitations that OpenGL compatibility doesn't have: - NV_shader_storage_buffer_object is limited to 16 entries for a single OpenGL context state (I don't know if this is an intended limitation, an specification issue or I am missing something). Currently causes issues on The Legend of Zelda: Link's Awakening. - NV_parameter_buffer_object can't bind buffers using an offset different to zero. The used workaround is to copy to a temporary buffer (this doesn't happen often so it's not an issue). On the other hand, it has the following advantages: - Shaders build a lot faster. - We have control over how floating point rounding is done over individual instructions (SPIR-V on Vulkan can't do this). - Operations on shared memory can be unsigned and signed. - Transform feedbacks are dynamic state (not yet implemented). - Parameter buffers (uniform buffers) are per stage, matching NVN and hardware's behavior. - The API to bind and create assembly programs makes sense, unlike ARB_separate_shader_objects.
117 lines
3.4 KiB
C++
117 lines
3.4 KiB
C++
// Copyright 2018 yuzu Emulator Project
|
|
// Licensed under GPLv2 or any later version
|
|
// Refer to the license.txt file included.
|
|
|
|
#include <glad/glad.h>
|
|
|
|
#include "common/common_types.h"
|
|
#include "video_core/engines/maxwell_3d.h"
|
|
#include "video_core/renderer_opengl/gl_device.h"
|
|
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
|
|
|
namespace OpenGL {
|
|
|
|
ProgramManager::ProgramManager(const Device& device) {
|
|
use_assembly_programs = device.UseAssemblyShaders();
|
|
if (use_assembly_programs) {
|
|
glEnable(GL_COMPUTE_PROGRAM_NV);
|
|
} else {
|
|
graphics_pipeline.Create();
|
|
glBindProgramPipeline(graphics_pipeline.handle);
|
|
}
|
|
}
|
|
|
|
ProgramManager::~ProgramManager() = default;
|
|
|
|
void ProgramManager::BindCompute(GLuint program) {
|
|
if (use_assembly_programs) {
|
|
glBindProgramARB(GL_COMPUTE_PROGRAM_NV, program);
|
|
} else {
|
|
is_graphics_bound = false;
|
|
glUseProgram(program);
|
|
}
|
|
}
|
|
|
|
void ProgramManager::BindGraphicsPipeline() {
|
|
if (use_assembly_programs) {
|
|
UpdateAssemblyPrograms();
|
|
} else {
|
|
UpdateSourcePrograms();
|
|
}
|
|
}
|
|
|
|
void ProgramManager::BindHostPipeline(GLuint pipeline) {
|
|
if (use_assembly_programs) {
|
|
if (geometry_enabled) {
|
|
geometry_enabled = false;
|
|
old_state.geometry = 0;
|
|
glDisable(GL_GEOMETRY_PROGRAM_NV);
|
|
}
|
|
}
|
|
glBindProgramPipeline(pipeline);
|
|
}
|
|
|
|
void ProgramManager::RestoreGuestPipeline() {
|
|
if (use_assembly_programs) {
|
|
glBindProgramPipeline(0);
|
|
} else {
|
|
glBindProgramPipeline(graphics_pipeline.handle);
|
|
}
|
|
}
|
|
|
|
void ProgramManager::UpdateAssemblyPrograms() {
|
|
const auto update_state = [](GLenum stage, bool& enabled, GLuint current, GLuint old) {
|
|
if (current == old) {
|
|
return;
|
|
}
|
|
if (current == 0) {
|
|
if (enabled) {
|
|
enabled = false;
|
|
glDisable(stage);
|
|
}
|
|
return;
|
|
}
|
|
if (!enabled) {
|
|
enabled = true;
|
|
glEnable(stage);
|
|
}
|
|
glBindProgramARB(stage, current);
|
|
};
|
|
|
|
update_state(GL_VERTEX_PROGRAM_NV, vertex_enabled, current_state.vertex, old_state.vertex);
|
|
update_state(GL_GEOMETRY_PROGRAM_NV, geometry_enabled, current_state.geometry,
|
|
old_state.geometry);
|
|
update_state(GL_FRAGMENT_PROGRAM_NV, fragment_enabled, current_state.fragment,
|
|
old_state.fragment);
|
|
|
|
old_state = current_state;
|
|
}
|
|
|
|
void ProgramManager::UpdateSourcePrograms() {
|
|
if (!is_graphics_bound) {
|
|
is_graphics_bound = true;
|
|
glUseProgram(0);
|
|
}
|
|
|
|
const GLuint handle = graphics_pipeline.handle;
|
|
const auto update_state = [handle](GLenum stage, GLuint current, GLuint old) {
|
|
if (current == old) {
|
|
return;
|
|
}
|
|
glUseProgramStages(handle, stage, current);
|
|
};
|
|
update_state(GL_VERTEX_SHADER_BIT, current_state.vertex, old_state.vertex);
|
|
update_state(GL_GEOMETRY_SHADER_BIT, current_state.geometry, old_state.geometry);
|
|
update_state(GL_FRAGMENT_SHADER_BIT, current_state.fragment, old_state.fragment);
|
|
|
|
old_state = current_state;
|
|
}
|
|
|
|
void MaxwellUniformData::SetFromRegs(const Tegra::Engines::Maxwell3D& maxwell) {
|
|
const auto& regs = maxwell.regs;
|
|
|
|
// Y_NEGATE controls what value S2R returns for the Y_DIRECTION system value.
|
|
y_direction = regs.screen_y_control.y_negate == 0 ? 1.0f : -1.0f;
|
|
}
|
|
|
|
} // namespace OpenGL
|