forked from suyu/suyu
a8a2526128
NV_shader_buffer_{load,store} is a 2010 extension that allows GL applications to use what in Vulkan is known as physical pointers, this is basically C pointers. On GLASM these is exposed through the LOAD/STORE/ATOM instructions. Up until now, assembly shaders were using NV_shader_storage_buffer_object. These work fine, but have a (probably unintended) limitation that forces us to have the limit of a single stage for all shader stages. In contrast, with NV_shader_buffer_{load,store} we can pass GPU addresses to the shader through local parameters (GLASM equivalent uniform constants, or push constants on Vulkan). Local parameters have the advantage of being per stage, allowing us to generate code without worrying about binding overlaps.
134 lines
3.6 KiB
C++
134 lines
3.6 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 {
|
|
|
|
namespace {
|
|
|
|
void BindProgram(GLenum stage, GLuint current, GLuint old, bool& enabled) {
|
|
if (current == old) {
|
|
return;
|
|
}
|
|
if (current == 0) {
|
|
if (enabled) {
|
|
enabled = false;
|
|
glDisable(stage);
|
|
}
|
|
return;
|
|
}
|
|
if (!enabled) {
|
|
enabled = true;
|
|
glEnable(stage);
|
|
}
|
|
glBindProgramARB(stage, current);
|
|
}
|
|
|
|
} // Anonymous namespace
|
|
|
|
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) {
|
|
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);
|
|
}
|
|
} else {
|
|
if (!is_graphics_bound) {
|
|
glUseProgram(0);
|
|
}
|
|
}
|
|
glBindProgramPipeline(pipeline);
|
|
}
|
|
|
|
void ProgramManager::RestoreGuestPipeline() {
|
|
if (use_assembly_programs) {
|
|
glBindProgramPipeline(0);
|
|
} else {
|
|
glBindProgramPipeline(graphics_pipeline.handle);
|
|
}
|
|
}
|
|
|
|
void ProgramManager::UseVertexShader(GLuint program) {
|
|
if (use_assembly_programs) {
|
|
BindProgram(GL_VERTEX_PROGRAM_NV, program, current_state.vertex, vertex_enabled);
|
|
}
|
|
current_state.vertex = program;
|
|
}
|
|
|
|
void ProgramManager::UseGeometryShader(GLuint program) {
|
|
if (use_assembly_programs) {
|
|
BindProgram(GL_GEOMETRY_PROGRAM_NV, program, current_state.vertex, geometry_enabled);
|
|
}
|
|
current_state.geometry = program;
|
|
}
|
|
|
|
void ProgramManager::UseFragmentShader(GLuint program) {
|
|
if (use_assembly_programs) {
|
|
BindProgram(GL_FRAGMENT_PROGRAM_NV, program, current_state.vertex, fragment_enabled);
|
|
}
|
|
current_state.fragment = program;
|
|
}
|
|
|
|
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
|