mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-30 02:32:46 +01:00
renderer_opengl: Use textures for fragment shader LUTs instead of UBOs.
- Gets us LUT interpolation for free. - Some older Intel GPU drivers did not support the big UBOs needed to store the LUTs.
This commit is contained in:
parent
bf89870437
commit
021cb0bced
5 changed files with 64 additions and 27 deletions
|
@ -126,6 +126,19 @@ void RasterizerOpenGL::InitObjects() {
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_color_texture.texture.handle, 0);
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_color_texture.texture.handle, 0);
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fb_depth_texture.texture.handle, 0);
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fb_depth_texture.texture.handle, 0);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < lighting_lut.size(); ++i) {
|
||||||
|
lighting_lut[i].Create();
|
||||||
|
state.lighting_lut[i].texture_1d = lighting_lut[i].handle;
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE3 + i);
|
||||||
|
glBindTexture(GL_TEXTURE_1D, state.lighting_lut[i].texture_1d);
|
||||||
|
|
||||||
|
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr);
|
||||||
|
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
}
|
||||||
|
state.Apply();
|
||||||
|
|
||||||
ASSERT_MSG(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE,
|
ASSERT_MSG(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE,
|
||||||
"OpenGL rasterizer framebuffer setup failed, status %X", glCheckFramebufferStatus(GL_FRAMEBUFFER));
|
"OpenGL rasterizer framebuffer setup failed, status %X", glCheckFramebufferStatus(GL_FRAMEBUFFER));
|
||||||
}
|
}
|
||||||
|
@ -162,7 +175,7 @@ void RasterizerOpenGL::DrawTriangles() {
|
||||||
state.draw.shader_dirty = false;
|
state.draw.shader_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned index = 0; index < Pica::g_state.lighting.luts.size(); index++) {
|
for (unsigned index = 0; index < lighting_lut.size(); index++) {
|
||||||
if (uniform_block_data.lut_dirty[index]) {
|
if (uniform_block_data.lut_dirty[index]) {
|
||||||
SyncLightingLUT(index);
|
SyncLightingLUT(index);
|
||||||
uniform_block_data.lut_dirty[index] = false;
|
uniform_block_data.lut_dirty[index] = false;
|
||||||
|
@ -451,7 +464,7 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
|
||||||
case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[7], 0x1cf):
|
case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[7], 0x1cf):
|
||||||
{
|
{
|
||||||
auto& lut_config = regs.lighting.lut_config;
|
auto& lut_config = regs.lighting.lut_config;
|
||||||
uniform_block_data.lut_dirty[lut_config.type] = true;
|
uniform_block_data.lut_dirty[lut_config.type / 4] = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -663,6 +676,20 @@ void RasterizerOpenGL::SetShader() {
|
||||||
uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[2]");
|
uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[2]");
|
||||||
if (uniform_tex != -1) { glUniform1i(uniform_tex, 2); }
|
if (uniform_tex != -1) { glUniform1i(uniform_tex, 2); }
|
||||||
|
|
||||||
|
// Set the texture samplers to correspond to different lookup table texture units
|
||||||
|
GLuint uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[0]");
|
||||||
|
if (uniform_lut != -1) { glUniform1i(uniform_lut, 3); }
|
||||||
|
uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[1]");
|
||||||
|
if (uniform_lut != -1) { glUniform1i(uniform_lut, 4); }
|
||||||
|
uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[2]");
|
||||||
|
if (uniform_lut != -1) { glUniform1i(uniform_lut, 5); }
|
||||||
|
uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[3]");
|
||||||
|
if (uniform_lut != -1) { glUniform1i(uniform_lut, 6); }
|
||||||
|
uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[4]");
|
||||||
|
if (uniform_lut != -1) { glUniform1i(uniform_lut, 7); }
|
||||||
|
uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[5]");
|
||||||
|
if (uniform_lut != -1) { glUniform1i(uniform_lut, 8); }
|
||||||
|
|
||||||
current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get();
|
current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get();
|
||||||
|
|
||||||
unsigned int block_index = glGetUniformBlockIndex(current_shader->shader.handle, "shader_data");
|
unsigned int block_index = glGetUniformBlockIndex(current_shader->shader.handle, "shader_data");
|
||||||
|
@ -675,9 +702,6 @@ void RasterizerOpenGL::SetShader() {
|
||||||
for (int index = 0; index < tev_stages.size(); ++index)
|
for (int index = 0; index < tev_stages.size(); ++index)
|
||||||
SyncTevConstColor(index, tev_stages[index]);
|
SyncTevConstColor(index, tev_stages[index]);
|
||||||
|
|
||||||
for (unsigned index = 0; index < Pica::g_state.lighting.luts.size(); ++index)
|
|
||||||
SyncLightingLUT(index);
|
|
||||||
|
|
||||||
SyncGlobalAmbient();
|
SyncGlobalAmbient();
|
||||||
for (int light_index = 0; light_index < 8; light_index++) {
|
for (int light_index = 0; light_index < 8; light_index++) {
|
||||||
SyncLightDiffuse(light_index);
|
SyncLightDiffuse(light_index);
|
||||||
|
@ -874,16 +898,19 @@ void RasterizerOpenGL::SyncGlobalAmbient() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerOpenGL::SyncLightingLUT(unsigned lut_index) {
|
void RasterizerOpenGL::SyncLightingLUT(unsigned lut_index) {
|
||||||
auto& lut = uniform_block_data.data.lighting_lut[lut_index / 4];
|
std::array<std::array<GLfloat, 4>, 256> new_data;
|
||||||
std::array<std::array<GLfloat, 4>, 256> new_lut;
|
|
||||||
|
|
||||||
for (int offset = 0; offset < new_lut.size(); ++offset) {
|
for (unsigned offset = 0; offset < new_data.size(); ++offset) {
|
||||||
new_lut[offset][lut_index & 3] = Pica::g_state.lighting.luts[lut_index][offset].ToFloat();
|
new_data[offset][0] = Pica::g_state.lighting.luts[(lut_index * 4) + 0][offset].ToFloat();
|
||||||
|
new_data[offset][1] = Pica::g_state.lighting.luts[(lut_index * 4) + 1][offset].ToFloat();
|
||||||
|
new_data[offset][2] = Pica::g_state.lighting.luts[(lut_index * 4) + 2][offset].ToFloat();
|
||||||
|
new_data[offset][3] = Pica::g_state.lighting.luts[(lut_index * 4) + 3][offset].ToFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_lut != lut) {
|
if (new_data != lighting_lut_data[lut_index]) {
|
||||||
lut = new_lut;
|
lighting_lut_data[lut_index] = new_data;
|
||||||
uniform_block_data.dirty = true;
|
glActiveTexture(GL_TEXTURE3 + lut_index);
|
||||||
|
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, lighting_lut_data[lut_index].data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -264,11 +264,10 @@ private:
|
||||||
std::array<GLfloat, 3> lighting_global_ambient;
|
std::array<GLfloat, 3> lighting_global_ambient;
|
||||||
INSERT_PADDING_WORDS(1);
|
INSERT_PADDING_WORDS(1);
|
||||||
LightSrc light_src[8];
|
LightSrc light_src[8];
|
||||||
std::array<std::array<std::array<GLfloat, 4>, 256>, 6> lighting_lut;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(UniformData) == 0x6210, "The size of the UniformData structure has changed, update the structure in the shader");
|
static_assert(sizeof(UniformData) == 0x310, "The size of the UniformData structure has changed, update the structure in the shader");
|
||||||
static_assert(sizeof(UniformData) < 32768, "UniformData structure must be less than 32kb");
|
static_assert(sizeof(UniformData) < 16384, "UniformData structure must be less than 16kb as per the OpenGL spec");
|
||||||
|
|
||||||
/// Reconfigure the OpenGL color texture to use the given format and dimensions
|
/// Reconfigure the OpenGL color texture to use the given format and dimensions
|
||||||
void ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height);
|
void ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height);
|
||||||
|
@ -378,7 +377,7 @@ private:
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
UniformData data;
|
UniformData data;
|
||||||
bool lut_dirty[24];
|
bool lut_dirty[6];
|
||||||
bool dirty;
|
bool dirty;
|
||||||
} uniform_block_data;
|
} uniform_block_data;
|
||||||
|
|
||||||
|
@ -386,4 +385,7 @@ private:
|
||||||
OGLBuffer vertex_buffer;
|
OGLBuffer vertex_buffer;
|
||||||
OGLBuffer uniform_buffer;
|
OGLBuffer uniform_buffer;
|
||||||
OGLFramebuffer framebuffer;
|
OGLFramebuffer framebuffer;
|
||||||
|
|
||||||
|
std::array<OGLTexture, 6> lighting_lut;
|
||||||
|
std::array<std::array<std::array<GLfloat, 4>, 256>, 6> lighting_lut_data;
|
||||||
};
|
};
|
||||||
|
|
|
@ -324,6 +324,7 @@ std::string GenerateFragmentShader(const PicaShaderConfig& config) {
|
||||||
#define NUM_TEV_STAGES 6
|
#define NUM_TEV_STAGES 6
|
||||||
#define NUM_LIGHTS 8
|
#define NUM_LIGHTS 8
|
||||||
#define LIGHTING_LUT_SIZE 256
|
#define LIGHTING_LUT_SIZE 256
|
||||||
|
#define FLOAT_255 0.99609375
|
||||||
|
|
||||||
in vec4 primary_color;
|
in vec4 primary_color;
|
||||||
in vec2 texcoord[3];
|
in vec2 texcoord[3];
|
||||||
|
@ -347,15 +348,10 @@ layout (std140) uniform shader_data {
|
||||||
float depth_offset;
|
float depth_offset;
|
||||||
vec3 lighting_global_ambient;
|
vec3 lighting_global_ambient;
|
||||||
LightSrc light_src[NUM_LIGHTS];
|
LightSrc light_src[NUM_LIGHTS];
|
||||||
vec4 lighting_lut_0[LIGHTING_LUT_SIZE];
|
|
||||||
vec4 lighting_lut_1[LIGHTING_LUT_SIZE];
|
|
||||||
vec4 lighting_lut_2[LIGHTING_LUT_SIZE];
|
|
||||||
vec4 lighting_lut_3[LIGHTING_LUT_SIZE];
|
|
||||||
vec4 lighting_lut_4[LIGHTING_LUT_SIZE];
|
|
||||||
vec4 lighting_lut_5[LIGHTING_LUT_SIZE];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
uniform sampler2D tex[3];
|
uniform sampler2D tex[3];
|
||||||
|
uniform sampler1D lut[6];
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 primary_fragment_color = vec4(0.0);
|
vec4 primary_fragment_color = vec4(0.0);
|
||||||
|
@ -404,11 +400,11 @@ vec4 secondary_fragment_color = vec4(0.0);
|
||||||
if (abs) {
|
if (abs) {
|
||||||
// In the range of [ 0.f, 1.f]
|
// In the range of [ 0.f, 1.f]
|
||||||
index = config.light_src[light_num].two_sided_diffuse ? "abs(" + index + ")" : "max(" + index + ", 0.f)";
|
index = config.light_src[light_num].two_sided_diffuse ? "abs(" + index + ")" : "max(" + index + ", 0.f)";
|
||||||
return "clamp(int(" + index + " * 256.0), 0, 255)";
|
return "clamp(" + index + ", 0.0, FLOAT_255)";
|
||||||
} else {
|
} else {
|
||||||
// In the range of [-1.f, 1.f]
|
// In the range of [-1.f, 1.f]
|
||||||
index = "clamp(" + index + ", -1.0, 1.0)";
|
index = "clamp(" + index + ", -1.0, 1.0)";
|
||||||
return std::string("uint(int(" + index + " * 127.f) & 0xff)");
|
return "clamp(((" + index + " < 0) ? " + index + " + 2.0 : " + index + ") / 2.0, 0.0, FLOAT_255)";
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::string();
|
return std::string();
|
||||||
|
@ -435,10 +431,10 @@ vec4 secondary_fragment_color = vec4(0.0);
|
||||||
std::string scale = std::to_string(config.light_src[light_index].dist_atten_scale);
|
std::string scale = std::to_string(config.light_src[light_index].dist_atten_scale);
|
||||||
std::string bias = std::to_string(config.light_src[light_index].dist_atten_bias);
|
std::string bias = std::to_string(config.light_src[light_index].dist_atten_bias);
|
||||||
std::string lut_index = "(" + scale + " * length(fragment_position - " + light_src + ".position) + " + bias + ")";
|
std::string lut_index = "(" + scale + " * length(fragment_position - " + light_src + ".position) + " + bias + ")";
|
||||||
std::string clamped_lut_index = "((clamp(int(" + lut_index + " * 256.0), 0, 255)))";
|
std::string clamped_lut_index = "((clamp(" + lut_index + ", 0.0, FLOAT_255)))";
|
||||||
|
|
||||||
const unsigned lut_num = ((unsigned)Regs::LightingSampler::DistanceAttenuation + num);
|
const unsigned lut_num = ((unsigned)Regs::LightingSampler::DistanceAttenuation + num);
|
||||||
out += "dist_atten = lighting_lut_" + std::to_string(lut_num / 4) + "[" + clamped_lut_index + "][" + std::to_string(lut_num & 3) + "];\n";
|
out += "dist_atten = texture(lut[" + std::to_string(lut_num / 4) + "], " + clamped_lut_index + ")[" + std::to_string(lut_num & 3) + "];\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute primary fragment color (diffuse lighting) function
|
// Compute primary fragment color (diffuse lighting) function
|
||||||
|
@ -447,7 +443,7 @@ vec4 secondary_fragment_color = vec4(0.0);
|
||||||
// Compute secondary fragment color (specular lighting) function
|
// Compute secondary fragment color (specular lighting) function
|
||||||
std::string clamped_lut_index = GetLutIndex(num, config.lighting_lut.d0_type, config.lighting_lut.d0_abs);
|
std::string clamped_lut_index = GetLutIndex(num, config.lighting_lut.d0_type, config.lighting_lut.d0_abs);
|
||||||
const unsigned lut_num = (unsigned)Regs::LightingSampler::Distribution0;
|
const unsigned lut_num = (unsigned)Regs::LightingSampler::Distribution0;
|
||||||
std::string lut_lookup = "lighting_lut_" + std::to_string(lut_num / 4) + "[" + clamped_lut_index + "][" + std::to_string(lut_num & 3) + "]";
|
std::string lut_lookup = "texture(lut[" + std::to_string(lut_num / 4) + "], " + clamped_lut_index + ")[" + std::to_string(lut_num & 3) + "]";
|
||||||
|
|
||||||
out += "specular_sum += (" + lut_lookup + " * light_src[" + std::to_string(num) + "].specular_0 * dist_atten);\n";
|
out += "specular_sum += (" + lut_lookup + " * light_src[" + std::to_string(num) + "].specular_0 * dist_atten);\n";
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,6 +170,14 @@ void OpenGLState::Apply() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lighting LUTs
|
||||||
|
for (unsigned i = 0; i < ARRAY_SIZE(lighting_lut); ++i) {
|
||||||
|
if (lighting_lut[i].texture_1d != cur_state.lighting_lut[i].texture_1d) {
|
||||||
|
glActiveTexture(GL_TEXTURE3 + i);
|
||||||
|
glBindTexture(GL_TEXTURE_1D, lighting_lut[i].texture_1d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Framebuffer
|
// Framebuffer
|
||||||
if (draw.framebuffer != cur_state.draw.framebuffer) {
|
if (draw.framebuffer != cur_state.draw.framebuffer) {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, draw.framebuffer);
|
glBindFramebuffer(GL_FRAMEBUFFER, draw.framebuffer);
|
||||||
|
|
|
@ -61,6 +61,10 @@ public:
|
||||||
GLuint sampler; // GL_SAMPLER_BINDING
|
GLuint sampler; // GL_SAMPLER_BINDING
|
||||||
} texture_units[3];
|
} texture_units[3];
|
||||||
|
|
||||||
|
struct {
|
||||||
|
GLuint texture_1d; // GL_TEXTURE_BINDING_1D
|
||||||
|
} lighting_lut[6];
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
GLuint framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING
|
GLuint framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING
|
||||||
GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING
|
GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING
|
||||||
|
|
Loading…
Reference in a new issue