diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp index 01c1d4303536..e0f0eac72d9c 100644 --- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp +++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp @@ -528,6 +528,7 @@ void ProgramShaderCache::CreateHeader() "%s\n" // storage buffer "%s\n" // shader5 "%s\n" // AEP + "%s\n" // texture buffer // Precision defines for GLSL ES "%s\n" @@ -563,6 +564,7 @@ void ProgramShaderCache::CreateHeader() , g_ActiveConfig.backend_info.bSupportsBBox ? "#extension GL_ARB_shader_storage_buffer_object : enable" : "" , g_ActiveConfig.backend_info.bSupportsGSInstancing ? "#extension GL_ARB_gpu_shader5 : enable" : "" , g_ogl_config.bSupportsAEP ? "#extension GL_ANDROID_extension_pack_es31a : enable" : "" + , v=GLSLES_300 ? "precision highp float;" : "" , v>=GLSLES_300 ? "precision highp int;" : "" diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index 019f5d2556a9..3e50f70481f9 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -465,6 +465,7 @@ Renderer::Renderer() g_Config.backend_info.bSupportsBBox = GLExtensions::Supports("GL_ARB_shader_storage_buffer_object"); g_Config.backend_info.bSupportsGSInstancing = GLExtensions::Supports("GL_ARB_gpu_shader5"); g_Config.backend_info.bSupportsGeometryShaders = GLExtensions::Version() >= 320; + g_Config.backend_info.bSupportsPaletteConversion = GLExtensions::Supports("GL_ARB_texture_buffer_object"); // Desktop OpenGL supports the binding layout if it supports 420pack // OpenGL ES 3.1 supports it implicitly without an extension @@ -499,6 +500,7 @@ Renderer::Renderer() g_Config.backend_info.bSupportsBindingLayout = true; g_Config.backend_info.bSupportsEarlyZ = true; g_Config.backend_info.bSupportsGeometryShaders = g_ogl_config.bSupportsAEP; + //g_Config.backend_info.bSupportsPaletteConversion = GLExtensions::Supports("GL_EXT_texture_buffer"); } } else diff --git a/Source/Core/VideoBackends/OGL/TextureCache.cpp b/Source/Core/VideoBackends/OGL/TextureCache.cpp index bfed930245ba..77a503523efc 100644 --- a/Source/Core/VideoBackends/OGL/TextureCache.cpp +++ b/Source/Core/VideoBackends/OGL/TextureCache.cpp @@ -18,6 +18,7 @@ #include "VideoBackends/OGL/GLInterfaceBase.h" #include "VideoBackends/OGL/ProgramShaderCache.h" #include "VideoBackends/OGL/Render.h" +#include "VideoBackends/OGL/StreamBuffer.h" #include "VideoBackends/OGL/TextureCache.h" #include "VideoBackends/OGL/TextureConverter.h" @@ -43,6 +44,13 @@ static u32 s_DepthCbufid; static u32 s_Textures[8]; static u32 s_ActiveTexture; +static SHADER s_palette_pixel_shader[3]; +static StreamBuffer* s_palette_stream_buffer = nullptr; +static GLuint s_palette_resolv_texture; +static GLuint s_palette_buffer_offset_uniform[3]; +static GLuint s_palette_multiplier_uniform[3]; +static GLuint s_palette_copy_position_uniform[3]; + bool SaveTexture(const std::string& filename, u32 textarget, u32 tex, int virtual_width, int virtual_height, unsigned int level) { if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL) @@ -237,12 +245,27 @@ TextureCache::TextureCache() s_ActiveTexture = -1; for (auto& gtex : s_Textures) gtex = -1; + + if (g_ActiveConfig.backend_info.bSupportsPaletteConversion) + { + s_palette_stream_buffer = StreamBuffer::Create(GL_TEXTURE_BUFFER, 1024*1024); + glGenTextures(1, &s_palette_resolv_texture); + glBindTexture(GL_TEXTURE_BUFFER, s_palette_resolv_texture); + glTexBuffer(GL_TEXTURE_BUFFER, GL_R16UI, s_palette_stream_buffer->m_buffer); + } } TextureCache::~TextureCache() { DeleteShaders(); + + if (g_ActiveConfig.backend_info.bSupportsPaletteConversion) + { + delete s_palette_stream_buffer; + s_palette_stream_buffer = nullptr; + glDeleteTextures(1, &s_palette_resolv_texture); + } } void TextureCache::DisableStage(unsigned int stage) @@ -347,18 +370,161 @@ void TextureCache::CompileShaders() s_ColorCopyPositionUniform = glGetUniformLocation(s_ColorMatrixProgram.glprogid, "copy_position"); s_DepthCopyPositionUniform = glGetUniformLocation(s_DepthMatrixProgram.glprogid, "copy_position"); + + std::string palette_shader = + R"GLSL( + uniform int texture_buffer_offset; + uniform float multiplier; + SAMPLER_BINDING(9) uniform sampler2DArray samp9; + SAMPLER_BINDING(10) uniform usamplerBuffer samp10; + + in vec3 f_uv0; + out vec4 ocol0; + + int Convert3To8(int v) + { + // Swizzle bits: 00000123 -> 12312312 + return (v << 5) | (v << 2) | (v >> 1); + } + + int Convert4To8(int v) + { + // Swizzle bits: 00001234 -> 12341234 + return (v << 4) | v; + } + + int Convert5To8(int v) + { + // Swizzle bits: 00012345 -> 12345123 + return (v << 3) | (v >> 2); + } + + int Convert6To8(int v) + { + // Swizzle bits: 00123456 -> 12345612 + return (v << 2) | (v >> 4); + } + + float4 DecodePixel_RGB5A3(int val) + { + int r,g,b,a; + if ((val&0x8000) > 0) + { + r=Convert5To8((val>>10) & 0x1f); + g=Convert5To8((val>>5 ) & 0x1f); + b=Convert5To8((val ) & 0x1f); + a=0xFF; + } + else + { + a=Convert3To8((val>>12) & 0x7); + r=Convert4To8((val>>8 ) & 0xf); + g=Convert4To8((val>>4 ) & 0xf); + b=Convert4To8((val ) & 0xf); + } + return float4(r, g, b, a) / 255.0; + } + + float4 DecodePixel_RGB565(int val) + { + int r, g, b, a; + r = Convert5To8((val >> 11) & 0x1f); + g = Convert6To8((val >> 5) & 0x3f); + b = Convert5To8((val) & 0x1f); + a = 0xFF; + return float4(r, g, b, a) / 255.0; + } + + float4 DecodePixel_IA8(int val) + { + int i = val & 0xFF; + int a = val >> 8; + return float4(i, i, i, a) / 255.0; + } + + void main() + { + int src = int(round(texture(samp9, f_uv0).r * multiplier)); + src = int(texelFetch(samp10, src + texture_buffer_offset).r); + src = ((src << 8) & 0xFF00) | (src >> 8); + ocol0 = DECODE(src); + } + )GLSL"; + + if (g_ActiveConfig.backend_info.bSupportsPaletteConversion) + { + ProgramShaderCache::CompileShader( + s_palette_pixel_shader[GX_TL_IA8], + StringFromFormat(VProgram, prefix, prefix).c_str(), + ("#define DECODE DecodePixel_IA8" + palette_shader).c_str(), + GProgram); + s_palette_buffer_offset_uniform[GX_TL_IA8] = glGetUniformLocation(s_palette_pixel_shader[GX_TL_IA8].glprogid, "texture_buffer_offset"); + s_palette_multiplier_uniform[GX_TL_IA8] = glGetUniformLocation(s_palette_pixel_shader[GX_TL_IA8].glprogid, "multiplier"); + s_palette_copy_position_uniform[GX_TL_IA8] = glGetUniformLocation(s_palette_pixel_shader[GX_TL_IA8].glprogid, "copy_position"); + + ProgramShaderCache::CompileShader( + s_palette_pixel_shader[GX_TL_RGB565], + StringFromFormat(VProgram, prefix, prefix).c_str(), + ("#define DECODE DecodePixel_RGB565" + palette_shader).c_str(), + GProgram); + s_palette_buffer_offset_uniform[GX_TL_RGB565] = glGetUniformLocation(s_palette_pixel_shader[GX_TL_RGB565].glprogid, "texture_buffer_offset"); + s_palette_multiplier_uniform[GX_TL_RGB565] = glGetUniformLocation(s_palette_pixel_shader[GX_TL_RGB565].glprogid, "multiplier"); + s_palette_copy_position_uniform[GX_TL_RGB565] = glGetUniformLocation(s_palette_pixel_shader[GX_TL_RGB565].glprogid, "copy_position"); + + ProgramShaderCache::CompileShader( + s_palette_pixel_shader[GX_TL_RGB5A3], + StringFromFormat(VProgram, prefix, prefix).c_str(), + ("#define DECODE DecodePixel_RGB5A3" + palette_shader).c_str(), + GProgram); + s_palette_buffer_offset_uniform[GX_TL_RGB5A3] = glGetUniformLocation(s_palette_pixel_shader[GX_TL_RGB5A3].glprogid, "texture_buffer_offset"); + s_palette_multiplier_uniform[GX_TL_RGB5A3] = glGetUniformLocation(s_palette_pixel_shader[GX_TL_RGB5A3].glprogid, "multiplier"); + s_palette_copy_position_uniform[GX_TL_RGB5A3] = glGetUniformLocation(s_palette_pixel_shader[GX_TL_RGB5A3].glprogid, "copy_position"); + } } void TextureCache::DeleteShaders() { s_ColorMatrixProgram.Destroy(); s_DepthMatrixProgram.Destroy(); + + if (g_ActiveConfig.backend_info.bSupportsPaletteConversion) + for (auto& shader : s_palette_pixel_shader) + shader.Destroy(); } -void TextureCache::ConvertTexture(TextureCache::TCacheEntryBase* entry, TCacheEntryBase* unconverted, void* palette, TlutFormat format) +void TextureCache::ConvertTexture(TCacheEntryBase* _entry, TCacheEntryBase* _unconverted, void* palette, TlutFormat format) { - // TODO: Implement. - return; + if (!g_ActiveConfig.backend_info.bSupportsPaletteConversion) + return; + + g_renderer->ResetAPIState(); + + TCacheEntry* entry = (TCacheEntry*) _entry; + TCacheEntry* unconverted = (TCacheEntry*) _unconverted; + + glActiveTexture(GL_TEXTURE0 + 9); + glBindTexture(GL_TEXTURE_2D_ARRAY, unconverted->texture); + + FramebufferManager::SetFramebuffer(entry->framebuffer); + glViewport(0, 0, entry->config.width, entry->config.height); + s_palette_pixel_shader[format].Bind(); + + int size = unconverted->format == 0 ? 32 : 512; + auto buffer = s_palette_stream_buffer->Map(size); + memcpy(buffer.first, palette, size); + s_palette_stream_buffer->Unmap(size); + glUniform1i(s_palette_buffer_offset_uniform[format], buffer.second / 2); + glUniform1f(s_palette_multiplier_uniform[format], unconverted->format == 0 ? 15.0f : 255.0f); + glUniform4f(s_palette_copy_position_uniform[format], 0.0f, 0.0f, (float)unconverted->config.width, (float)unconverted->config.height); + + glActiveTexture(GL_TEXTURE0 + 10); + glBindTexture(GL_TEXTURE_BUFFER, s_palette_resolv_texture); + + OpenGL_BindAttributelessVAO(); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + FramebufferManager::SetFramebuffer(0); + g_renderer->RestoreAPIState(); } } diff --git a/Source/Core/VideoBackends/OGL/main.cpp b/Source/Core/VideoBackends/OGL/main.cpp index 83ab7593e746..19a1db004b56 100644 --- a/Source/Core/VideoBackends/OGL/main.cpp +++ b/Source/Core/VideoBackends/OGL/main.cpp @@ -134,13 +134,10 @@ static void InitBackendInfo() { g_Config.backend_info.APIType = API_OPENGL; g_Config.backend_info.bSupportsExclusiveFullscreen = false; - //g_Config.backend_info.bSupportsDualSourceBlend = true; // is GPU dependent and must be set in renderer - //g_Config.backend_info.bSupportsEarlyZ = true; // is GPU dependent and must be set in renderer g_Config.backend_info.bSupportsOversizedViewports = true; g_Config.backend_info.bSupportsGeometryShaders = true; g_Config.backend_info.bSupports3DVision = false; g_Config.backend_info.bSupportsPostProcessing = true; - g_Config.backend_info.bSupportsPaletteConversion = false; g_Config.backend_info.Adapters.clear();