Sfoglia il codice sorgente

Merge pull request #1104 from yuriks/opengl-samplers

OpenGL: Use Sampler Objects to decouple sampler config from textures
bunnei 11 anni fa
parent
commit
51e91dfd86

+ 3 - 0
src/citra_qt/bootmanager.cpp

@@ -72,6 +72,9 @@ void EmuThread::run() {
         }
     }
 
+    // Shutdown the core emulation
+    System::Shutdown();
+
     MicroProfileOnThreadExit();
 
     render_window->moveContext();

+ 0 - 3
src/citra_qt/main.cpp

@@ -283,9 +283,6 @@ void GMainWindow::ShutdownGame() {
     emu_thread->wait();
     emu_thread = nullptr;
 
-    // Shutdown the core emulation
-    System::Shutdown();
-
     // Update the GUI
     ui.action_Start->setEnabled(false);
     ui.action_Start->setText(tr("Start"));

+ 4 - 0
src/video_core/pica.h

@@ -135,6 +135,7 @@ struct Regs {
         };
 
         union {
+            u32 raw;
             BitField< 0, 8, u32> r;
             BitField< 8, 8, u32> g;
             BitField<16, 8, u32> b;
@@ -339,6 +340,7 @@ struct Regs {
         };
 
         union {
+            u32 const_color;
             BitField< 0, 8, u32> const_r;
             BitField< 8, 8, u32> const_g;
             BitField<16, 8, u32> const_b;
@@ -389,6 +391,7 @@ struct Regs {
     TevStageConfig tev_stage5;
 
     union {
+        u32 raw;
         BitField< 0, 8, u32> r;
         BitField< 8, 8, u32> g;
         BitField<16, 8, u32> b;
@@ -473,6 +476,7 @@ struct Regs {
         };
 
         union {
+            u32 raw;
             BitField< 0, 8, u32> r;
             BitField< 8, 8, u32> g;
             BitField<16, 8, u32> b;

+ 49 - 3
src/video_core/renderer_opengl/gl_rasterizer.cpp

@@ -68,6 +68,12 @@ void RasterizerOpenGL::InitObjects() {
         uniform_tev_cfg.updates_combiner_buffer_color_alpha = glGetUniformLocation(shader.handle, (tev_ref_str + ".updates_combiner_buffer_color_alpha").c_str());
     }
 
+    // Create sampler objects
+    for (int i = 0; i < texture_samplers.size(); ++i) {
+        texture_samplers[i].Create();
+        state.texture_units[i].sampler = texture_samplers[i].sampler.handle;
+    }
+
     // Generate VBO and VAO
     vertex_buffer.Create();
     vertex_array.Create();
@@ -445,6 +451,45 @@ void RasterizerOpenGL::NotifyFlush(PAddr addr, u32 size) {
     res_cache.NotifyFlush(addr, size);
 }
 
+void RasterizerOpenGL::SamplerInfo::Create() {
+    sampler.Create();
+    mag_filter = min_filter = TextureConfig::Linear;
+    wrap_s = wrap_t = TextureConfig::Repeat;
+    border_color = 0;
+
+    glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // default is GL_LINEAR_MIPMAP_LINEAR
+    // Other attributes have correct defaults
+}
+
+void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Pica::Regs::TextureConfig& config) {
+    GLuint s = sampler.handle;
+
+    if (mag_filter != config.mag_filter) {
+        mag_filter = config.mag_filter;
+        glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureFilterMode(mag_filter));
+    }
+    if (min_filter != config.min_filter) {
+        min_filter = config.min_filter;
+        glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, PicaToGL::TextureFilterMode(min_filter));
+    }
+
+    if (wrap_s != config.wrap_s) {
+        wrap_s = config.wrap_s;
+        glSamplerParameteri(s, GL_TEXTURE_WRAP_S, PicaToGL::WrapMode(wrap_s));
+    }
+    if (wrap_t != config.wrap_t) {
+        wrap_t = config.wrap_t;
+        glSamplerParameteri(s, GL_TEXTURE_WRAP_T, PicaToGL::WrapMode(wrap_t));
+    }
+
+    if (wrap_s == TextureConfig::ClampToBorder || wrap_t == TextureConfig::ClampToBorder) {
+        if (border_color != config.border_color.raw) {
+            auto gl_color = PicaToGL::ColorRGBA8(border_color);
+            glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, gl_color.data());
+        }
+    }
+}
+
 void RasterizerOpenGL::ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height) {
     GLint internal_format;
 
@@ -658,7 +703,7 @@ void RasterizerOpenGL::SyncBlendFuncs() {
 }
 
 void RasterizerOpenGL::SyncBlendColor() {
-    auto blend_color = PicaToGL::ColorRGBA8((u8*)&Pica::g_state.regs.output_merger.blend_const.r);
+    auto blend_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.output_merger.blend_const.raw);
     state.blend.color.red = blend_color[0];
     state.blend.color.green = blend_color[1];
     state.blend.color.blue = blend_color[2];
@@ -728,7 +773,7 @@ void RasterizerOpenGL::SyncTevOps(unsigned stage_index, const Pica::Regs::TevSta
 }
 
 void RasterizerOpenGL::SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
-    auto const_color = PicaToGL::ColorRGBA8((u8*)&config.const_r);
+    auto const_color = PicaToGL::ColorRGBA8(config.const_color);
     glUniform4fv(uniform_tev_cfgs[stage_index].const_color, 1, const_color.data());
 }
 
@@ -737,7 +782,7 @@ void RasterizerOpenGL::SyncTevMultipliers(unsigned stage_index, const Pica::Regs
 }
 
 void RasterizerOpenGL::SyncCombinerColor() {
-    auto combiner_color = PicaToGL::ColorRGBA8((u8*)&Pica::g_state.regs.tev_combiner_buffer_color.r);
+    auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw);
     glUniform4fv(uniform_tev_combiner_buffer_color, 1, combiner_color.data());
 }
 
@@ -772,6 +817,7 @@ void RasterizerOpenGL::SyncDrawState() {
         const auto& texture = pica_textures[texture_index];
 
         if (texture.enabled) {
+            texture_samplers[texture_index].SyncWithConfig(texture.config);
             res_cache.LoadAndBindTexture(state, texture_index, texture);
         } else {
             state.texture_units[texture_index].texture_2d = 0;

+ 19 - 0
src/video_core/renderer_opengl/gl_rasterizer.h

@@ -80,6 +80,24 @@ private:
         GLenum gl_type;
     };
 
+    struct SamplerInfo {
+        using TextureConfig = Pica::Regs::TextureConfig;
+
+        OGLSampler sampler;
+
+        /// Creates the sampler object, initializing its state so that it's in sync with the SamplerInfo struct.
+        void Create();
+        /// Syncs the sampler object with the config, updating any necessary state.
+        void SyncWithConfig(const TextureConfig& config);
+
+    private:
+        TextureConfig::TextureFilter mag_filter;
+        TextureConfig::TextureFilter min_filter;
+        TextureConfig::WrapMode wrap_s;
+        TextureConfig::WrapMode wrap_t;
+        u32 border_color;
+    };
+
     /// Structure that the hardware rendered vertices are composed of
     struct HardwareVertex {
         HardwareVertex(const Pica::Shader::OutputVertex& v) {
@@ -193,6 +211,7 @@ private:
     PAddr last_fb_depth_addr;
 
     // Hardware rasterizer
+    std::array<SamplerInfo, 3> texture_samplers;
     TextureInfo fb_color_texture;
     DepthTextureInfo fb_depth_texture;
     OGLShader shader;

+ 5 - 20
src/video_core/renderer_opengl/gl_rasterizer_cache.cpp

@@ -20,9 +20,8 @@ RasterizerCacheOpenGL::~RasterizerCacheOpenGL() {
 
 MICROPROFILE_DEFINE(OpenGL_TextureUpload, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
 
-void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::Regs::FullTextureConfig& config) {
-    PAddr texture_addr = config.config.GetPhysicalAddress();
-    const auto cached_texture = texture_cache.find(texture_addr);
+void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::DebugUtils::TextureInfo& info) {
+    const auto cached_texture = texture_cache.find(info.physical_address);
 
     if (cached_texture != texture_cache.end()) {
         state.texture_units[texture_unit].texture_2d = cached_texture->second->texture.handle;
@@ -37,26 +36,12 @@ void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned text
         state.Apply();
         glActiveTexture(GL_TEXTURE0 + texture_unit);
 
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureFilterMode(config.config.mag_filter));
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, PicaToGL::TextureFilterMode(config.config.min_filter));
-
-        GLenum wrap_s = PicaToGL::WrapMode(config.config.wrap_s);
-        GLenum wrap_t = PicaToGL::WrapMode(config.config.wrap_t);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t);
-
-        if (wrap_s == GL_CLAMP_TO_BORDER || wrap_t == GL_CLAMP_TO_BORDER) {
-            auto border_color = PicaToGL::ColorRGBA8((u8*)&config.config.border_color.r);
-            glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color.data());
-        }
-
-        const auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format);
-        u8* texture_src_data = Memory::GetPhysicalPointer(texture_addr);
+        u8* texture_src_data = Memory::GetPhysicalPointer(info.physical_address);
 
         new_texture->width = info.width;
         new_texture->height = info.height;
         new_texture->size = info.stride * info.height;
-        new_texture->addr = texture_addr;
+        new_texture->addr = info.physical_address;
         new_texture->hash = Common::ComputeHash64(texture_src_data, new_texture->size);
 
         std::unique_ptr<Math::Vec4<u8>[]> temp_texture_buffer_rgba(new Math::Vec4<u8>[info.width * info.height]);
@@ -69,7 +54,7 @@ void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned text
 
         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, info.width, info.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp_texture_buffer_rgba.get());
 
-        texture_cache.emplace(texture_addr, std::move(new_texture));
+        texture_cache.emplace(info.physical_address, std::move(new_texture));
     }
 }
 

+ 6 - 1
src/video_core/renderer_opengl/gl_rasterizer_cache.h

@@ -6,6 +6,7 @@
 
 #include "gl_state.h"
 #include "gl_resource_manager.h"
+#include "video_core/debug_utils/debug_utils.h"
 #include "video_core/pica.h"
 
 #include <memory>
@@ -16,7 +17,11 @@ public:
     ~RasterizerCacheOpenGL();
 
     /// Loads a texture from 3DS memory to OpenGL and caches it (if not already cached)
-    void LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::Regs::FullTextureConfig& config);
+    void LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::DebugUtils::TextureInfo& info);
+
+    void LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::Regs::FullTextureConfig& config) {
+        LoadAndBindTexture(state, texture_unit, Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format));
+    }
 
     /// Flush any cached resource that touches the flushed region
     void NotifyFlush(PAddr addr, u32 size, bool ignore_hash = false);

+ 24 - 0
src/video_core/renderer_opengl/gl_resource_manager.h

@@ -37,6 +37,30 @@ public:
     GLuint handle = 0;
 };
 
+class OGLSampler : private NonCopyable {
+public:
+    OGLSampler() = default;
+    OGLSampler(OGLSampler&& o) { std::swap(handle, o.handle); }
+    ~OGLSampler() { Release(); }
+    OGLSampler& operator=(OGLSampler&& o) { std::swap(handle, o.handle); return *this; }
+
+    /// Creates a new internal OpenGL resource and stores the handle
+    void Create() {
+        if (handle != 0) return;
+        glGenSamplers(1, &handle);
+    }
+
+    /// Deletes the internal OpenGL resource
+    void Release() {
+        if (handle == 0) return;
+        glDeleteSamplers(1, &handle);
+        OpenGLState::ResetSampler(handle);
+        handle = 0;
+    }
+
+    GLuint handle = 0;
+};
+
 class OGLShader : private NonCopyable {
 public:
     OGLShader() = default;

+ 16 - 4
src/video_core/renderer_opengl/gl_state.cpp

@@ -44,6 +44,7 @@ OpenGLState::OpenGLState() {
 
     for (auto& texture_unit : texture_units) {
         texture_unit.texture_2d = 0;
+        texture_unit.sampler = 0;
     }
 
     draw.framebuffer = 0;
@@ -154,10 +155,13 @@ void OpenGLState::Apply() {
     }
 
     // Textures
-    for (unsigned texture_index = 0; texture_index < ARRAY_SIZE(texture_units); ++texture_index) {
-        if (texture_units[texture_index].texture_2d != cur_state.texture_units[texture_index].texture_2d) {
-            glActiveTexture(GL_TEXTURE0 + texture_index);
-            glBindTexture(GL_TEXTURE_2D, texture_units[texture_index].texture_2d);
+    for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) {
+        if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) {
+            glActiveTexture(GL_TEXTURE0 + i);
+            glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d);
+        }
+        if (texture_units[i].sampler != cur_state.texture_units[i].sampler) {
+            glBindSampler(i, texture_units[i].sampler);
         }
     }
 
@@ -192,6 +196,14 @@ void OpenGLState::ResetTexture(GLuint id) {
     }
 }
 
+void OpenGLState::ResetSampler(GLuint id) {
+    for (auto& unit : cur_state.texture_units) {
+        if (unit.sampler == id) {
+            unit.sampler = 0;
+        }
+    }
+}
+
 void OpenGLState::ResetProgram(GLuint id) {
     if (cur_state.draw.shader_program == id) {
         cur_state.draw.shader_program = 0;

+ 2 - 0
src/video_core/renderer_opengl/gl_state.h

@@ -57,6 +57,7 @@ public:
     // 3 texture units - one for each that is used in PICA fragment shader emulation
     struct {
         GLuint texture_2d; // GL_TEXTURE_BINDING_2D
+        GLuint sampler; // GL_SAMPLER_BINDING
     } texture_units[3];
 
     struct {
@@ -77,6 +78,7 @@ public:
     void Apply();
 
     static void ResetTexture(GLuint id);
+    static void ResetSampler(GLuint id);
     static void ResetProgram(GLuint id);
     static void ResetBuffer(GLuint id);
     static void ResetVertexArray(GLuint id);

+ 5 - 5
src/video_core/renderer_opengl/pica_to_gl.h

@@ -175,11 +175,11 @@ inline GLenum StencilOp(Pica::Regs::StencilAction action) {
     return stencil_op_table[(unsigned)action];
 }
 
-inline std::array<GLfloat, 4> ColorRGBA8(const u8* bytes) {
-    return { { bytes[0] / 255.0f,
-               bytes[1] / 255.0f,
-               bytes[2] / 255.0f,
-               bytes[3] / 255.0f
+inline std::array<GLfloat, 4> ColorRGBA8(const u32 color) {
+    return { { (color >>  0 & 0xFF) / 255.0f,
+               (color >>  8 & 0xFF) / 255.0f,
+               (color >> 16 & 0xFF) / 255.0f,
+               (color >> 24 & 0xFF) / 255.0f
            } };
 }