From 021cb0bced1d8045f04b85024b97a07a4d0df12f Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 15 Nov 2015 17:43:01 -0500 Subject: [PATCH] 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. --- .../renderer_opengl/gl_rasterizer.cpp | 51 ++++++++++++++----- .../renderer_opengl/gl_rasterizer.h | 10 ++-- .../renderer_opengl/gl_shader_gen.cpp | 18 +++---- src/video_core/renderer_opengl/gl_state.cpp | 8 +++ src/video_core/renderer_opengl/gl_state.h | 4 ++ 5 files changed, 64 insertions(+), 27 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index c6fb37c53c..6e7d6a40d9 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -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_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, "OpenGL rasterizer framebuffer setup failed, status %X", glCheckFramebufferStatus(GL_FRAMEBUFFER)); } @@ -162,7 +175,7 @@ void RasterizerOpenGL::DrawTriangles() { 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]) { SyncLightingLUT(index); 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): { 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; } @@ -663,6 +676,20 @@ void RasterizerOpenGL::SetShader() { uniform_tex = glGetUniformLocation(shader->shader.handle, "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(); 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) SyncTevConstColor(index, tev_stages[index]); - for (unsigned index = 0; index < Pica::g_state.lighting.luts.size(); ++index) - SyncLightingLUT(index); - SyncGlobalAmbient(); for (int light_index = 0; light_index < 8; light_index++) { SyncLightDiffuse(light_index); @@ -874,16 +898,19 @@ void RasterizerOpenGL::SyncGlobalAmbient() { } void RasterizerOpenGL::SyncLightingLUT(unsigned lut_index) { - auto& lut = uniform_block_data.data.lighting_lut[lut_index / 4]; - std::array, 256> new_lut; + std::array, 256> new_data; - for (int offset = 0; offset < new_lut.size(); ++offset) { - new_lut[offset][lut_index & 3] = Pica::g_state.lighting.luts[lut_index][offset].ToFloat(); + for (unsigned offset = 0; offset < new_data.size(); ++offset) { + 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) { - lut = new_lut; - uniform_block_data.dirty = true; + if (new_data != lighting_lut_data[lut_index]) { + lighting_lut_data[lut_index] = new_data; + glActiveTexture(GL_TEXTURE3 + lut_index); + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, lighting_lut_data[lut_index].data()); } } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 9e93b8b2fb..b50542701a 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -264,11 +264,10 @@ private: std::array lighting_global_ambient; INSERT_PADDING_WORDS(1); LightSrc light_src[8]; - std::array, 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) < 32768, "UniformData structure must be less than 32kb"); + static_assert(sizeof(UniformData) == 0x310, "The size of the UniformData structure has changed, update the structure in the shader"); + 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 void ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height); @@ -378,7 +377,7 @@ private: struct { UniformData data; - bool lut_dirty[24]; + bool lut_dirty[6]; bool dirty; } uniform_block_data; @@ -386,4 +385,7 @@ private: OGLBuffer vertex_buffer; OGLBuffer uniform_buffer; OGLFramebuffer framebuffer; + + std::array lighting_lut; + std::array, 256>, 6> lighting_lut_data; }; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index abcc89f1d3..cb570c4d26 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -324,6 +324,7 @@ std::string GenerateFragmentShader(const PicaShaderConfig& config) { #define NUM_TEV_STAGES 6 #define NUM_LIGHTS 8 #define LIGHTING_LUT_SIZE 256 +#define FLOAT_255 0.99609375 in vec4 primary_color; in vec2 texcoord[3]; @@ -347,15 +348,10 @@ layout (std140) uniform shader_data { float depth_offset; vec3 lighting_global_ambient; 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 sampler1D lut[6]; void main() { vec4 primary_fragment_color = vec4(0.0); @@ -404,11 +400,11 @@ vec4 secondary_fragment_color = vec4(0.0); if (abs) { // In the range of [ 0.f, 1.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 { // In the range of [-1.f, 1.f] 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(); @@ -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 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 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); - 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 @@ -447,7 +443,7 @@ vec4 secondary_fragment_color = vec4(0.0); // Compute secondary fragment color (specular lighting) function 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; - 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"; } diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index a823729956..ab4b6c7b12 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -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 if (draw.framebuffer != cur_state.draw.framebuffer) { glBindFramebuffer(GL_FRAMEBUFFER, draw.framebuffer); diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index b8ab45bb8d..e848058d77 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -61,6 +61,10 @@ public: GLuint sampler; // GL_SAMPLER_BINDING } texture_units[3]; + struct { + GLuint texture_1d; // GL_TEXTURE_BINDING_1D + } lighting_lut[6]; + struct { GLuint framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING