diff --git a/.gitmodules b/.gitmodules
index cd98acb..494af93 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -10,3 +10,6 @@
 [submodule "third_party/spdlog"]
 	path = third_party/spdlog
 	url = https://github.com/gabime/spdlog.git
+[submodule "third_party/glslang"]
+	path = third_party/glslang
+	url = https://github.com/KhronosGroup/glslang.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index abbdbd5..089a3b1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -63,6 +63,7 @@ add_subdirectory(third_party/portaudio)
 add_subdirectory(third_party/spdlog)
 add_subdirectory(third_party/slang)
 add_subdirectory(third_party/glad)
+add_subdirectory(third_party/glslang)
 
 # setup portaudio
 set(PA_USE_ASIO ON CACHE BOOL "" FORCE)
@@ -140,5 +141,8 @@ set(SPDLOG_WCHAR_SUPPORT ON CACHE BOOL "" FORCE)
 set(SPDLOG_ENABLE_PCH ON CACHE BOOL "" FORCE)
 set(SPDLOG_USE_STD_FORMAT OFF CACHE BOOL "" FORCE)
 
+# setup glslang
+set(ENABLE_OPT OFF CACHE BOOL "" FORCE)
+
 # install
 install(DIRECTORY ${CMAKE_SOURCE_DIR}/third_party/imgui/imgui/misc/fonts DESTINATION ${CMAKE_BINARY_DIR}/bin)
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index 939cb00..e64a099 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -8,10 +8,10 @@ retrieve_files(ALL_FILES)
 add_library(${PROJECT_NAME} SHARED ${ALL_FILES})
 
 
-target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} spdlog imgui slang glad)
+target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} spdlog imgui slang glad glslang)
 
-target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} spdlog imgui glad)
-target_link_libraries(${PROJECT_NAME} PUBLIC imgui spdlog ${SDL2_LIBRARIES} slang glad)
+target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} spdlog imgui glad glslang)
+target_link_libraries(${PROJECT_NAME} PUBLIC imgui spdlog ${SDL2_LIBRARIES} slang glad glslang)
 
 
 target_precompile_headers(${PROJECT_NAME} PUBLIC extern.h)
diff --git a/core/application/application.cpp b/core/application/application.cpp
index b163372..15f61fa 100644
--- a/core/application/application.cpp
+++ b/core/application/application.cpp
@@ -19,21 +19,16 @@
 
 bool g_is_running = true;
 bool g_exit_requested = false;
-slang::IGlobalSession* g_slang_global_session = nullptr;
+Slang::ComPtr<slang::IGlobalSession> g_slang_global_session = nullptr;
 application* g_app_instance = nullptr;
 
-application::~application()
-{
-    application::shutdown();
-}
-
 void application::init(window_params in_window_params, int argc, char** argv)
 {
-    slang::createGlobalSession(&g_slang_global_session);
+    slang::createGlobalSession(g_slang_global_session.writeRef());
     
     try 
     {
-        auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/log.txt");
+        async_spdlog_ = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/log.txt");
     }
     catch (const spdlog::spdlog_ex &ex)
     {
@@ -208,15 +203,28 @@ int application::run()
         draw_gui();
         renderer_->end_frame(window_);
     }
+    
+    shutdown();
     return 0;
 }
 
 void application::shutdown()
 {
+    init_imgui(nullptr);
     renderer_->shutdown();
     ImGui::DestroyContext();
 
+    SDL_HideWindow(window_);
+    SDL_DestroyWindow(window_);
+    window_ = nullptr;
+
+    renderer_->post_shutdown();
+
     delete renderer_;
+    renderer_ = nullptr;
+
+    
+    SDL_Quit();
 }
 
 std::shared_ptr<texture> application::load_texture(const std::string& path) const
diff --git a/core/application/application.h b/core/application/application.h
index 5378ff4..813cab7 100644
--- a/core/application/application.h
+++ b/core/application/application.h
@@ -2,6 +2,7 @@
 #include <string>
 #include "SDL.h"
 #include "imgui.h"
+#include "slang-com-ptr.h"
 #include "slang.h"
 
 class render_target;
@@ -11,7 +12,7 @@ class application;
 
 extern bool g_is_running;
 extern bool g_exit_requested;
-extern slang::IGlobalSession* g_slang_global_session;
+extern Slang::ComPtr<slang::IGlobalSession>g_slang_global_session;
 extern application* g_app_instance;
 
 struct window_params
@@ -35,7 +36,7 @@ public:
     {
         g_app_instance = this;
     }
-    virtual ~application();
+    virtual ~application() = default;
     application(const application&) = delete;
     application(application&&) = delete;
     static application* get()
@@ -60,4 +61,5 @@ public:
 protected:
     renderer* renderer_ = nullptr;
     SDL_Window* window_ = nullptr;
+    std::shared_ptr<spdlog::logger> async_spdlog_;
 };
diff --git a/core/misc/likely.h b/core/misc/likely.h
new file mode 100644
index 0000000..86ee836
--- /dev/null
+++ b/core/misc/likely.h
@@ -0,0 +1,21 @@
+#pragma once
+
+
+/** Branch prediction hints */
+#ifndef LIKELY						/* Hints compiler that expression is likely to be true, much softer than UE_ASSUME - allows (penalized by worse performance) expression to be false */
+    #if ( defined(__clang__) || defined(__GNUC__) ) && defined(PLATFORM_UNIX)	// effect of these on non-Linux platform has not been analyzed as of 2016-03-21
+        #define LIKELY(x)			__builtin_expect(!!(x), 1)
+    #else
+        // the additional "!!" is added to silence "warning: equality comparison with exteraneous parenthese" messages on android
+        #define LIKELY(x)			(!!(x))
+    #endif
+#endif
+
+#ifndef UNLIKELY					/* Hints compiler that expression is unlikely to be true, allows (penalized by worse performance) expression to be true */
+    #if ( defined(__clang__) || defined(__GNUC__) ) && defined(PLATFORM_UNIX)	// effect of these on non-Linux platform has not been analyzed as of 2016-03-21
+        #define UNLIKELY(x)			__builtin_expect(!!(x), 0)
+    #else
+        // the additional "!!" is added to silence "warning: equality comparison with exteraneous parenthese" messages on android
+        #define UNLIKELY(x)			(!!(x))
+    #endif
+#endif
\ No newline at end of file
diff --git a/core/rhi/opengl/opengl_def.h b/core/rhi/opengl/opengl_def.h
index 3752cb4..63a030e 100644
--- a/core/rhi/opengl/opengl_def.h
+++ b/core/rhi/opengl/opengl_def.h
@@ -6,25 +6,3 @@
     if (Error != 0)\
     spdlog::critical("GL error: 0x{:x}", Error);\
 }
-
-struct im_gui_impl_open_gl3_data
-{
-    GLuint          GlVersion;               // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
-    char            GlslVersionString[32];   // Specified by user or detected based on compile time GL settings.
-    bool            GlProfileIsES2;
-    bool            GlProfileIsES3;
-    bool            GlProfileIsCompat;
-    GLint           GlProfileMask;
-    GLuint          FontTexture;
-    GLuint          ShaderHandle;
-    GLint           AttribLocationTex;       // Uniforms location
-    GLint           AttribLocationProjMtx;
-    GLuint          AttribLocationVtxPos;    // Vertex attributes location
-    GLuint          AttribLocationVtxUV;
-    GLuint          AttribLocationVtxColor;
-    unsigned int    VboHandle, ElementsHandle;
-    GLsizeiptr      VertexBufferSize;
-    GLsizeiptr      IndexBufferSize;
-    bool            HasClipOrigin;
-    bool            UseBufferSubData;
-};
diff --git a/core/rhi/opengl/render_target_opengl.cpp b/core/rhi/opengl/render_target_opengl.cpp
index 10738d7..1c3d491 100644
--- a/core/rhi/opengl/render_target_opengl.cpp
+++ b/core/rhi/opengl/render_target_opengl.cpp
@@ -2,11 +2,84 @@
 
 #include "opengl_def.h"
 
+GLint to_internal_format(texture_format format)
+{
+    switch (format)
+    {
+    case texture_format::RGBA8:
+        return GL_RGBA8;
+    case texture_format::RGBA16_FLOAT:
+        return GL_RGBA16F;
+    case texture_format::RGBA32_FLOAT:
+        return GL_RGBA32F;
+    case texture_format::R32_FLOAT:
+        return GL_R32F;
+    case texture_format::RG32_FLOAT:
+        return GL_RG32F;
+    case texture_format::R11F_G11F_B10F:
+        return GL_R11F_G11F_B10F;
+    case texture_format::R16_FLOAT:
+        return GL_R16F;
+    case texture_format::RGBA16:
+        return GL_RGBA16;
+    case texture_format::RGB10_A2:
+        return GL_RGB10_A2;
+    case texture_format::RG16:
+        return GL_RG16;
+    case texture_format::RG8:
+        return GL_RG8;
+    case texture_format::R16:
+        return GL_R16;
+    case texture_format::R8:
+        return GL_R8;
+    case texture_format::RG16_SNORM:
+        return GL_RG16_SNORM;
+    case texture_format::RG8_SNORM:
+        return GL_RG8_SNORM;
+    case texture_format::R16_SNORM:
+        return GL_R16_SNORM;
+    case texture_format::R8_SNORM:
+        return GL_R8_SNORM;
+    case texture_format::R32I:
+        return GL_R32I;
+    case texture_format::R32UI:
+        return GL_R32UI;
+    case texture_format::R8I:
+        return GL_R8I;
+    case texture_format::R16I:
+        return GL_R16I;
+    case texture_format::R32F:
+        return GL_R32F;
+    case texture_format::R8UI:
+        return GL_R8UI;
+    case texture_format::R16UI:
+        return GL_R16UI;
+    case texture_format::RG32I:
+        return GL_RG32I;
+    case texture_format::RG32UI:
+        return GL_RG32UI;
+    case texture_format::RG8I:
+        return GL_RG8I;
+    case texture_format::RG16I:
+        return GL_RG16I;
+    case texture_format::RGB10_A2UI:
+        return GL_RGB10_A2UI;
+    case texture_format::RG16UI:
+        return GL_RG16UI;
+    default:
+        return GL_RGBA8;
+    }
+    return 0;
+}
+
 void render_target_opengl::init(int width, int height, texture_format format)
 {
+    width_ = width;
+    height_ = height;
+    
     glGenFramebuffers(1, &fbo_);
     CHECK_GL_ERRORS
-    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_);
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
     CHECK_GL_ERRORS
 
 #if defined(__APPLE__)
@@ -21,17 +94,27 @@ LockGLContext([NSOpenGLContext currentContext]);
     
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-    
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    internal_format_ = to_internal_format(format);
+    glTexImage2D(GL_TEXTURE_2D, 0, internal_format_, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
     CHECK_GL_ERRORS
+
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_, 0);
+
+    CHECK_GL_ERRORS
+    
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
 #if defined(__APPLE__)
     UnlockGLContext([NSOpenGLContext currentContext]);
 #endif
 
-    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_, 0);
-    CHECK_GL_ERRORS
+}
 
-    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+render_target_opengl::~render_target_opengl()
+{
+    glDeleteFramebuffers(1, &fbo_);
+    glDeleteTextures(1, &texture_);
 }
 
 void* render_target_opengl::lock(lock_state state)
@@ -79,16 +162,32 @@ void render_target_opengl::on_resize(int width, int height)
     width_ = width;
     height_ = height;
     glDeleteTextures(1, &texture_);
+    glDeleteFramebuffers(1, &fbo_);
+    texture_ = 0;
+    fbo_ = 0;
     
 #if defined(__APPLE__)
     LockGLContext([NSOpenGLContext currentContext]);
 #endif
-
+    glGenTextures(1, &texture_);
+    CHECK_GL_ERRORS
+    
     glBindTexture(GL_TEXTURE_2D, texture_);
+    
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    CHECK_GL_ERRORS
+
+    glGenFramebuffers(1, &fbo_);
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_, 0);
+
+    CHECK_GL_ERRORS
+
+    glBindTexture(GL_TEXTURE_2D, 0);
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
     
 #if defined(__APPLE__)
     UnlockGLContext([NSOpenGLContext currentContext]);
diff --git a/core/rhi/opengl/render_target_opengl.h b/core/rhi/opengl/render_target_opengl.h
index 9a95ad7..63400b9 100644
--- a/core/rhi/opengl/render_target_opengl.h
+++ b/core/rhi/opengl/render_target_opengl.h
@@ -6,10 +6,14 @@
 class render_target_opengl : public render_target
 {
 public:
-    
-
     void init(int width, int height, texture_format format) override;
-    ImTextureID get_texture_id() override { return (void*)static_cast<intptr_t>(fbo_); }
+    ~render_target_opengl() override;
+    
+    ImTextureID get_texture_id() override { return (void*)static_cast<intptr_t>(texture_); }
+    [[nodiscard]] GLuint get_fbo() const { return fbo_; }
+    [[nodiscard]] GLuint get_texture() const { return texture_; }
+    [[nodiscard]] GLint get_internal_format() const { return internal_format_; }
+    
     void* lock(lock_state state) override;
     void unlock() override;
 protected:
@@ -17,5 +21,6 @@ protected:
 private:
     GLuint fbo_ = 0;
     GLuint texture_ = 0;
+    GLint internal_format_ = 0;
     void* locked_texture_ = nullptr;
 };
diff --git a/core/rhi/opengl/renderer_opengl.cpp b/core/rhi/opengl/renderer_opengl.cpp
index e3b7c15..771c0de 100644
--- a/core/rhi/opengl/renderer_opengl.cpp
+++ b/core/rhi/opengl/renderer_opengl.cpp
@@ -62,13 +62,19 @@ bool renderer_opengl::init(SDL_Window* window_handle)
 
 void renderer_opengl::shutdown()
 {
+    renderer::shutdown();
     ImGui_ImplOpenGL3_Shutdown();
     ImGui_ImplSDL3_Shutdown();
-
-    SDL_GL_DeleteContext(g_gl_context);
 }
 
-Slang::ComPtr<slang::ISession> renderer_opengl::create_slang_session(const std::string& shader_path)
+void renderer_opengl::post_shutdown()
+{
+    SDL_GL_DeleteContext(g_gl_context);
+    g_gl_context = nullptr;
+    renderer::post_shutdown();
+}
+
+void renderer_opengl::create_slang_session(const std::string& shader_path, slang::ISession** out_session)
 {
     slang::TargetDesc target_desc;
     target_desc.format = SLANG_GLSL;
@@ -80,10 +86,9 @@ Slang::ComPtr<slang::ISession> renderer_opengl::create_slang_session(const std::
     session_desc.searchPathCount = 1;
     session_desc.targets = &target_desc;
     session_desc.targetCount = 1;
+    session_desc.defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_COLUMN_MAJOR; // in OpenGL, matrices are row-major by default
 
-    Slang::ComPtr<slang::ISession> out;
-    g_slang_global_session->createSession(session_desc, out.writeRef());
-    return out;
+    g_slang_global_session->createSession(session_desc, out_session);
 }
 
 std::shared_ptr<shader> renderer_opengl::load_shader(const std::string& entry_name)
diff --git a/core/rhi/opengl/renderer_opengl.h b/core/rhi/opengl/renderer_opengl.h
index 047772a..f2aeaa1 100644
--- a/core/rhi/opengl/renderer_opengl.h
+++ b/core/rhi/opengl/renderer_opengl.h
@@ -8,8 +8,9 @@ public:
     void pre_init() override;
     bool init(SDL_Window* window_handle) override;
     void shutdown() override;
+    void post_shutdown() override;
 
-    Slang::ComPtr<slang::ISession> create_slang_session(const std::string& shader_path) override;
+    void create_slang_session(const std::string& shader_path, slang::ISession** out_session) override;
     std::shared_ptr<shader> load_shader(const std::string& entry_name) override;
 
     void new_frame(SDL_Window* window_handle) override;
diff --git a/core/rhi/opengl/shader/shader_cs_opengl.cpp b/core/rhi/opengl/shader/shader_cs_opengl.cpp
index 4147239..68034b9 100644
--- a/core/rhi/opengl/shader/shader_cs_opengl.cpp
+++ b/core/rhi/opengl/shader/shader_cs_opengl.cpp
@@ -1 +1,26 @@
 #include "shader_cs_opengl.h"
+
+#include "shader_program_opengl.h"
+
+
+shader_cs_opengl::shader_cs_opengl(const std::shared_ptr<slang_handle>& handle): shader_opengl(handle)
+{
+    
+}
+
+bool shader_cs_opengl::init()
+{
+    if (!shader_opengl::init())
+        return false;
+    program_ = std::make_shared<shader_program_opengl>();
+    std::shared_ptr<shader_opengl> in_compute_shader = std::static_pointer_cast<shader_opengl>(shared_from_this());
+    program_->create_program(in_compute_shader);
+    return true;
+}
+
+void shader_cs_opengl::compute(int x, int y, int z)
+{
+    program_->use_program();
+    glDispatchCompute(x, y, z);
+    program_->unuse_program();
+}
diff --git a/core/rhi/opengl/shader/shader_cs_opengl.h b/core/rhi/opengl/shader/shader_cs_opengl.h
index 93b0ec7..0bb4b21 100644
--- a/core/rhi/opengl/shader/shader_cs_opengl.h
+++ b/core/rhi/opengl/shader/shader_cs_opengl.h
@@ -1,10 +1,14 @@
 #pragma once
 #include "shader_opengl.h"
-#include "rhi/opengl/opengl_def.h"
 
 class shader_cs_opengl : public shader_opengl
 {
 public:
-    shader_cs_opengl(const std::shared_ptr<slang_handle>& handle) : shader_opengl(handle) {}
-    GLenum get_shader_type() const override { return GL_COMPUTE_SHADER; }
+    explicit shader_cs_opengl(const std::shared_ptr<slang_handle>& handle);
+    bool init() override;
+    [[nodiscard]] GLenum get_shader_type() const override { return GL_COMPUTE_SHADER; }
+
+    void compute(int x, int y, int z) override;
+private:
+    std::shared_ptr<shader_program_opengl> program_;
 };
diff --git a/core/rhi/opengl/shader/shader_gs_opengl.h b/core/rhi/opengl/shader/shader_gs_opengl.h
index a3110bc..8278b07 100644
--- a/core/rhi/opengl/shader/shader_gs_opengl.h
+++ b/core/rhi/opengl/shader/shader_gs_opengl.h
@@ -7,5 +7,5 @@ class shader_gs_opengl : public shader_opengl
 public:
     shader_gs_opengl(const std::shared_ptr<slang_handle>& handle) : shader_opengl(handle) {}
     
-    GLenum get_shader_type() const override { return GL_GEOMETRY_SHADER; }
+    [[nodiscard]] GLenum get_shader_type() const override { return GL_GEOMETRY_SHADER; }
 };
diff --git a/core/rhi/opengl/shader/shader_opengl.cpp b/core/rhi/opengl/shader/shader_opengl.cpp
index 0b5dee7..c59b746 100644
--- a/core/rhi/opengl/shader/shader_opengl.cpp
+++ b/core/rhi/opengl/shader/shader_opengl.cpp
@@ -1,8 +1,204 @@
 #include "shader_opengl.h"
 
-#include "imgui.h"
+#include "shader_program_opengl.h"
 #include "rhi/slang_handle.h"
-#include "rhi/opengl/opengl_def.h"
+
+#include "glslang/Include/intermediate.h"
+#include "glslang/MachineIndependent/localintermediate.h"
+#include "glslang/Public/ShaderLang.h"
+
+#include <string>
+
+GLint to_internal_format(glslang::TLayoutFormat format)
+{
+    switch (format)
+    {
+    case glslang::TLayoutFormat::ElfR32f:
+        return GL_R32F;
+    case glslang::TLayoutFormat::ElfRg32f:
+        return GL_RG32F;
+    case glslang::TLayoutFormat::ElfRgba32f:
+        return GL_RGBA32F;
+    case glslang::TLayoutFormat::ElfRgba8:
+        return GL_RGBA8;
+    case glslang::TLayoutFormat::ElfRgba16f:
+        return GL_RGBA16F;
+    case glslang::TLayoutFormat::ElfRgba32i:
+        return GL_RGBA32I;
+    case glslang::TLayoutFormat::ElfRgba32ui:
+        return GL_RGBA32UI;
+    case glslang::TLayoutFormat::ElfRgba8i:
+        return GL_RGBA8I;
+    case glslang::TLayoutFormat::ElfRgba8ui:
+        return GL_RGBA8UI;
+    case glslang::TLayoutFormat::ElfRgba16i:
+        return GL_RGBA16I;
+    case glslang::TLayoutFormat::ElfRgba16ui:
+        return GL_RGBA16UI;
+    case glslang::TLayoutFormat::ElfRgba8Snorm:
+        return GL_RGBA8_SNORM;
+    case glslang::TLayoutFormat::ElfRgba16Snorm:
+        return GL_RGBA16_SNORM;
+    case glslang::ElfEsFloatGuard:
+        return GL_R32F;
+    case glslang::ElfRg16f:
+        return GL_RG16F;
+    case glslang::ElfR11fG11fB10f:
+        return GL_R11F_G11F_B10F;
+    case glslang::ElfR16f:
+        return GL_R16F;
+    case glslang::ElfRgba16:
+        return GL_RGBA16;
+    case glslang::ElfRgb10A2:
+        return GL_RGB10_A2;
+    case glslang::ElfRg16:
+        return GL_RG16;
+    case glslang::ElfRg8:
+        return GL_RG8;
+    case glslang::ElfR16:
+        return GL_R16;
+    case glslang::ElfR8:
+        return GL_R8;
+    case glslang::ElfRg16Snorm:
+        return GL_RG16_SNORM;
+    case glslang::ElfRg8Snorm:
+        return GL_RG8_SNORM;
+    case glslang::ElfR16Snorm:
+        return GL_R16_SNORM;
+    case glslang::ElfR8Snorm:
+        return GL_R8_SNORM;
+    case glslang::ElfFloatGuard:
+        return GL_R32F;
+    case glslang::ElfR32i:
+        return GL_R32I;
+    case glslang::ElfEsIntGuard:
+        return GL_R32I;
+    case glslang::ElfRg32i:
+        return GL_RG32I;
+    case glslang::ElfRg16i:
+        return GL_RG16I;
+    case glslang::ElfRg8i:
+        return GL_RG8I;
+    case glslang::ElfR16i:
+        return GL_R16I;
+    case glslang::ElfR8i:
+        return GL_R8I;
+    case glslang::ElfIntGuard:
+        return GL_R32I;
+    case glslang::ElfR32ui:
+        return GL_R32UI;
+    case glslang::ElfEsUintGuard:
+        return GL_R32UI;
+    case glslang::ElfRg32ui:
+        return GL_RG32UI;
+    case glslang::ElfRg16ui:
+        return GL_RG16UI;
+    case glslang::ElfRgb10a2ui:
+        return GL_RGB10_A2UI;
+    case glslang::ElfRg8ui:
+        return GL_RG8UI;
+    case glslang::ElfR16ui:
+        return GL_R16UI;
+    case glslang::ElfR8ui:
+        return GL_R8UI;
+    }
+    return 0;
+}
+
+// return uniform map
+void opengl_get_refletion_data(const std::string& src, std::map<std::string, opengl_uniform_data>& out_uniform, std::map<std::string, opengl_texture_data>& out_texture)
+{
+    glslang::InitializeProcess();
+    {
+        const char* const sources[] = { src.c_str() };
+        const int source_lengths[] = {static_cast<int>(src.size())};
+        const char* source_names[] = {""};
+        const int sources_num = 1;
+        const auto shader_stage = EShLanguage::EShLangCompute;
+
+        glslang::TShader shader { shader_stage };
+        shader.setStringsWithLengthsAndNames(sources, source_lengths, source_names, sources_num);
+        shader.setEnvClient(glslang::EShClientOpenGL, glslang::EShTargetOpenGL_450);
+        shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0);
+
+        TBuiltInResource default_builtin_resources{};
+        shader.parse(&default_builtin_resources, 450, ECoreProfile, false, true, EShMsgDefault);
+
+        class RefrectionTraverser : public glslang::TIntermTraverser
+        {
+        public:
+            std::map<std::string, opengl_uniform_data> uniform_map;
+            std::map<std::string, opengl_texture_data> texture_map;
+
+            virtual void visitSymbol(glslang::TIntermSymbol* symbol) override
+            {
+                if (symbol->getQualifier().isUniformOrBuffer())
+                {
+                    auto t_name = symbol->getName();
+                    std::string name = t_name.c_str();
+                    auto basic_type = symbol->getBasicType();
+                    const glslang::TType& type = symbol->getType();
+                    if (basic_type == glslang::EbtSampler)
+                    {
+                        auto qualifier = symbol->getQualifier();
+                        opengl_texture_data data;
+                        data.sampler_name = name;
+                        
+                        if (qualifier.hasBinding())
+                        {
+                            data.binding = qualifier.layoutBinding;
+                        }
+                        if (qualifier.hasFormat())
+                        {
+                            data.format = to_internal_format(qualifier.layoutFormat);
+                        }
+                        
+                        const size_t& clean_name_index = name.find('_');
+                        std::string clean_name = name.substr(0, clean_name_index);
+                    
+                        texture_map.insert(std::pair(clean_name, data));
+                    }
+                    else
+                    {
+                        auto qualifier = symbol->getQualifier();
+
+                        opengl_uniform_data data;
+                        data.name = name;
+                        data.type_name = type.getTypeName();
+
+                        if (qualifier.hasLocation())
+                        {
+                            data.location = qualifier.layoutLocation;
+                        }
+                        if (qualifier.hasBinding())
+                        {
+                            data.binding = qualifier.layoutBinding;
+                        }
+                        if (qualifier.hasFormat())
+                        {
+                            data.format = to_internal_format(qualifier.layoutFormat);
+                        }
+                    
+                        const size_t& clean_name_index = name.find('_');
+                        std::string clean_name = name.substr(0, clean_name_index);
+                    
+                        uniform_map.insert(std::pair(clean_name, data));
+                    }
+                }
+            }
+        };
+
+        RefrectionTraverser traverser;
+
+        auto root = shader.getIntermediate()->getTreeRoot();
+        root->traverse(&traverser);
+
+        out_uniform = traverser.uniform_map;
+        out_texture = traverser.texture_map;
+    }
+
+    glslang::FinalizeProcess();
+}
 
 bool shader_opengl::init()
 {
@@ -17,6 +213,8 @@ bool shader_opengl::init()
     const GLint code_size = static_cast<GLint>(code_blob->getBufferSize());
     glShaderSource(shader_id_, 1, &code_array, &code_size);
 
+    opengl_get_refletion_data(code_array, uniform_map_, texture_map_); // slow
+    
     glCompileShader(shader_id_);
     GLint compile_status = GL_FALSE;
     glGetShaderiv(shader_id_, GL_COMPILE_STATUS, &compile_status);
@@ -32,18 +230,39 @@ bool shader_opengl::init()
         shader_id_ = 0;
         return false;
     }
-    
     return true;
 }
 
-void shader_opengl::bind()
+void shader_opengl::set_using_program(std::shared_ptr<shader_program_opengl> in_program)
 {
-    glUseProgram(shader_id_);
+    if (using_program_.lock())
+    {
+        spdlog::error("Shader is already used by another program");
+        return;
+    }
+    using_program_ = in_program;
 }
 
-void shader_opengl::unbind()
+void shader_opengl::set_cbuffer(const char* name, void* buffer, int size)
 {
-    const ImGuiIO& io = ImGui::GetIO();
-    const auto open_gl3_data = static_cast<im_gui_impl_open_gl3_data*>(io.BackendRendererUserData);
-    glUseProgram(open_gl3_data->ShaderHandle);
+    if (const auto program = using_program_.lock())
+        program->set_uniform(name, buffer, size);
+}
+
+void shader_opengl::set_uav_buffer(const char* name, void* buffer, int size, int element_size)
+{
+    if (const auto program = using_program_.lock())
+        program->set_ssbo(name, buffer, size, element_size);
+}
+
+void shader_opengl::set_render_target(const char* name, std::shared_ptr<render_target> in_render_target)
+{
+    if (const auto program = using_program_.lock())
+        program->set_render_target(name, in_render_target);
+}
+
+void shader_opengl::set_texture(const char* name, std::shared_ptr<texture> in_texture)
+{
+    if (const auto program = using_program_.lock())
+        program->set_texture(name, in_texture);
 }
diff --git a/core/rhi/opengl/shader/shader_opengl.h b/core/rhi/opengl/shader/shader_opengl.h
index 3d64e22..72c54f4 100644
--- a/core/rhi/opengl/shader/shader_opengl.h
+++ b/core/rhi/opengl/shader/shader_opengl.h
@@ -1,21 +1,55 @@
 #pragma once
+#include <map>
+
+#include "slang.h"
 #include "rhi/shader.h"
 
+class shader_program_opengl;
+
+struct opengl_uniform_data
+{
+    std::string type_name;
+    std::string name;
+    GLuint location = 0;
+    GLuint binding = 0;
+    GLint format = 0;
+};
+
+struct opengl_texture_data
+{
+    std::string sampler_name;
+    GLuint binding = 0;
+    GLint format = 0;
+};
+
 class shader_opengl : public shader
 {
 public:
-    shader_opengl(const std::shared_ptr<slang_handle>& handle) : shader(handle) {}
-    
+    explicit shader_opengl(const std::shared_ptr<slang_handle>& handle) : shader(handle), shader_id_(0)
+    {
+    }
+
     bool init() override;
     [[nodiscard]] bool is_initialized() const override { return shader_id_ != 0; }
-    void bind() override;
-    void unbind() override;
+    
+    void bind() override {}
+    void unbind() override {}
 
-    void set_constant_buffer(const char* name, void* buffer, int size) override {}
-    void set_render_target(const char* name, std::shared_ptr<render_target> in_render_target) override {}
-    void set_texture(const char* name, std::shared_ptr<texture> in_texture) override {}
+    void set_using_program(std::shared_ptr<shader_program_opengl> in_program);
 
+    void set_cbuffer(const char* name, void* buffer, int size) override;
+    void set_uav_buffer(const char* name, void* buffer, int size, int element_size) override;
+    void set_render_target(const char* name, std::shared_ptr<render_target> in_render_target) override;
+    void set_texture(const char* name, std::shared_ptr<texture> in_texture) override;
+    
     [[nodiscard]] virtual GLenum get_shader_type() const = 0;
+    [[nodiscard]] GLuint get_shader_id() const { return shader_id_; }
+    
+    [[nodiscard]] const std::map<std::string, opengl_uniform_data>& get_uniform_map() const { return uniform_map_; }
+    [[nodiscard]] const std::map<std::string, opengl_texture_data>& get_texture_map() const { return texture_map_; }
 protected:
     GLuint shader_id_;
+    std::weak_ptr<shader_program_opengl> using_program_;
+    std::map<std::string, opengl_uniform_data> uniform_map_;
+    std::map<std::string, opengl_texture_data> texture_map_;
 };
diff --git a/core/rhi/opengl/shader/shader_program_opengl.cpp b/core/rhi/opengl/shader/shader_program_opengl.cpp
new file mode 100644
index 0000000..cfb713d
--- /dev/null
+++ b/core/rhi/opengl/shader/shader_program_opengl.cpp
@@ -0,0 +1,214 @@
+#include "shader_program_opengl.h"
+
+#include <ranges>
+
+#include "shader_opengl.h"
+#include "rhi/slang_handle.h"
+#include "rhi/opengl/render_target_opengl.h"
+#include "rhi/opengl/texture_opengl.h"
+
+bool shader_program_opengl::create_program(std::shared_ptr<shader_opengl> in_pixel_shader, std::shared_ptr<shader_opengl> in_vertex_shader)
+{
+    if (program_id_)
+    {
+        destroy_program();
+    }
+    if (!in_pixel_shader || !in_pixel_shader->is_initialized())
+    {
+        spdlog::error("Pixel shader is not initialized");
+        return false;
+    }
+    if (!in_vertex_shader || !in_vertex_shader->is_initialized())
+    {
+        spdlog::error("Vertex shader is not initialized");
+        return false;
+    }
+    const GLuint pixel_id = in_pixel_shader->get_shader_id();
+    const GLuint vertex_id = in_vertex_shader->get_shader_id();
+    
+    program_id_ = glCreateProgram();
+    if (program_id_ == 0)
+    {
+        spdlog::error("Failed to create program");
+        return false;
+    }
+    
+    glAttachShader(program_id_, vertex_id);
+    glAttachShader(program_id_, pixel_id);
+    
+    glLinkProgram(program_id_);
+    GLint link_status = GL_FALSE;
+    glGetProgramiv(program_id_, GL_LINK_STATUS, &link_status);
+    if (link_status == GL_FALSE)
+    {
+        GLint log_length = 0;
+        glGetProgramiv(program_id_, GL_INFO_LOG_LENGTH, &log_length);
+        std::vector<GLchar> log(log_length);
+        glGetProgramInfoLog(program_id_, log_length, nullptr, log.data());
+        spdlog::error("Failed to link program: {}", log.data());
+
+        glDeleteProgram(program_id_);
+        program_id_ = 0;
+        return false;
+    }
+    
+    glDetachShader(program_id_, vertex_id);
+    glDetachShader(program_id_, pixel_id);
+
+    pixel_shader_ = in_pixel_shader;
+    vertex_shader_ = in_vertex_shader;
+    in_pixel_shader->set_using_program(shared_from_this());
+    in_vertex_shader->set_using_program(shared_from_this());
+
+    uniform_map_.clear();
+    texture_map_.clear();
+    update_param_map(in_pixel_shader);
+    update_param_map(in_vertex_shader);
+    return true;
+}
+
+bool shader_program_opengl::create_program(std::shared_ptr<shader_opengl> in_compute_shader)
+{
+    if (program_id_)
+    {
+        destroy_program();
+    }
+    if (!in_compute_shader || !in_compute_shader->is_initialized())
+    {
+        spdlog::error("Compute shader is not initialized");
+        return false;
+    }
+    const GLuint compute_id = in_compute_shader->get_shader_id();
+    
+    program_id_ = glCreateProgram();
+    if (program_id_ == 0)
+    {
+        spdlog::error("Failed to create program");
+        return false;
+    }
+    
+    glAttachShader(program_id_, compute_id);
+    
+    glLinkProgram(program_id_);
+    GLint link_status = GL_FALSE;
+    glGetProgramiv(program_id_, GL_LINK_STATUS, &link_status);
+    if (link_status == GL_FALSE)
+    {
+        GLint log_length = 0;
+        glGetProgramiv(program_id_, GL_INFO_LOG_LENGTH, &log_length);
+        std::vector<GLchar> log(log_length);
+        glGetProgramInfoLog(program_id_, log_length, nullptr, log.data());
+        spdlog::error("Failed to link program: {}", log.data());
+
+        glDeleteProgram(program_id_);
+        program_id_ = 0;
+        return false;
+    }
+    
+    glDetachShader(program_id_, compute_id);
+    compute_shader_ = in_compute_shader;
+    in_compute_shader->set_using_program(shared_from_this());
+
+    uniform_map_.clear();
+    texture_map_.clear();
+    update_param_map(in_compute_shader);
+    return true;
+}
+
+void shader_program_opengl::destroy_program()
+{
+    for (const auto& val : ubo_map_ | std::views::values)
+        glDeleteBuffers(1, &val);
+    
+    if (const auto shader = pixel_shader_.lock())
+        shader->set_using_program(nullptr);
+    if (const auto shader = vertex_shader_.lock())
+        shader->set_using_program(nullptr);
+    if (const auto shader = compute_shader_.lock())
+        shader->set_using_program(nullptr);
+    if (!program_id_)
+        return;
+    glDeleteProgram(program_id_);
+    program_id_ = 0;
+    ubo_map_.clear();
+    uniform_map_.clear();
+    texture_map_.clear();
+}
+
+void shader_program_opengl::set_uniform(const char* slang_name, void* buffer, int size)
+{
+    const auto& uniform_name = uniform_map_.find(slang_name);
+    if (uniform_name == uniform_map_.end())
+        return;
+    
+    const auto& uniform_data = uniform_name->second;
+    GLuint using_bind_point = uniform_data.binding;
+    const auto find_ubo = ubo_map_.find(using_bind_point);
+    if (find_ubo != ubo_map_.end())
+    {
+        glBindBuffer(GL_UNIFORM_BUFFER, find_ubo->second);
+        glBufferSubData(GL_UNIFORM_BUFFER, 0, size, buffer);
+        return;
+    }
+
+    const GLint block_index = glGetUniformBlockIndex(program_id_, uniform_data.type_name.c_str());
+    glUniformBlockBinding(program_id_, block_index, uniform_data.binding);
+    
+    // Create a buffer and copy the projection matrix to it.
+    GLuint ubo = 0;
+    glGenBuffers(1, &ubo);
+    
+    glBindBuffer(GL_UNIFORM_BUFFER, ubo);
+    glBufferData(GL_UNIFORM_BUFFER, size, buffer, GL_STATIC_DRAW);
+    
+    glBindBufferBase(GL_UNIFORM_BUFFER, using_bind_point, ubo);
+
+    ubo_map_.insert(std::pair(using_bind_point, ubo));
+}
+
+void shader_program_opengl::set_ssbo(const char* slang_name, void* buffer, int size, int element_size)
+{
+    const auto& uniform_name = uniform_map_.find(slang_name);
+    if (uniform_name == uniform_map_.end())
+        return;
+    const auto& uniform_data = uniform_name->second;
+    const GLint location = glGetProgramResourceIndex(program_id_, GL_SHADER_STORAGE_BLOCK, uniform_data.name.c_str());
+    if (location == -1)
+    {
+        spdlog::error("Failed to find ssbo: {}", slang_name);
+        return;
+    }
+}
+
+void shader_program_opengl::set_render_target(const char* name, std::shared_ptr<render_target> in_render_target)
+{
+    const auto& texture_name = texture_map_.find(name);
+    if (texture_name == texture_map_.end())
+        return;
+
+    std::shared_ptr<render_target_opengl> rt = std::static_pointer_cast<render_target_opengl>(in_render_target);
+    const opengl_texture_data& texture_data = texture_name->second;
+    if (texture_data.format != rt->get_internal_format())
+        spdlog::error("Mismatched texture format: {}, need{}, yours {}", name, texture_data.format, rt->get_internal_format());
+    glBindImageTexture(texture_data.binding, rt->get_texture(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
+}
+
+void shader_program_opengl::set_texture(const char* name, std::shared_ptr<texture> in_texture)
+{
+    const auto& texture_name = texture_map_.find(name);
+    if (texture_name == texture_map_.end())
+        return;
+
+    std::shared_ptr<texture_opengl> t = std::static_pointer_cast<texture_opengl>(in_texture);
+    const opengl_texture_data& texture_data = texture_name->second;
+    glBindImageTexture(texture_data.binding, t->get_texture(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
+}
+
+void shader_program_opengl::update_param_map(std::shared_ptr<shader_opengl> in_shader)
+{
+    const auto& uniform_pairs = in_shader->get_uniform_map();
+    const auto& texture_pairs = in_shader->get_texture_map();
+    
+    uniform_map_.insert(uniform_pairs.begin(), uniform_pairs.end());
+    texture_map_.insert(texture_pairs.begin(), texture_pairs.end());
+}
diff --git a/core/rhi/opengl/shader/shader_program_opengl.h b/core/rhi/opengl/shader/shader_program_opengl.h
new file mode 100644
index 0000000..9c27bd9
--- /dev/null
+++ b/core/rhi/opengl/shader/shader_program_opengl.h
@@ -0,0 +1,77 @@
+#pragma once
+#include <map>
+#include <memory>
+#include <SDL_opengl.h>
+
+#include "shader_opengl.h"
+
+class texture;
+class render_target;
+class shader;
+class shader_opengl;
+
+class shader_program_opengl : public std::enable_shared_from_this<shader_program_opengl>
+{
+public:
+    ~shader_program_opengl()
+    {
+        destroy_program();
+    }
+
+    void use_program()
+    {
+        GLint prev_program_id = 0;
+        glGetIntegerv(GL_CURRENT_PROGRAM, &prev_program_id);
+        prev_program_id_ = prev_program_id;
+        glUseProgram(program_id_);
+    }
+    void unuse_program() const { glUseProgram(prev_program_id_); }
+    
+    [[nodiscard]] GLuint get_program_id() const { return program_id_; }
+    bool create_program(std::shared_ptr<shader_opengl> in_pixel_shader, std::shared_ptr<shader_opengl> in_vertex_shader);
+    bool create_program(std::shared_ptr<shader_opengl> in_compute_shader);
+    void destroy_program();
+
+    void set_uniform(const char* slang_name, void* buffer, int size);
+    void set_ssbo(const char* slang_name, void* buffer, int size, int element_size);
+    void set_render_target(const char* name, std::shared_ptr<render_target> in_render_target);
+    void set_texture(const char* name, std::shared_ptr<texture> in_texture);
+
+    template<typename T>
+    void set_uniform(const char* name, const T& buffer)
+    {
+        set_uniform(name, (void*)&buffer, sizeof(T));
+    }
+    template<typename T>
+    void set_uniform(const char* name, const std::vector<T>& buffer)
+    {
+        set_uniform(name, (void*)buffer.data(), sizeof(T) * buffer.size());
+    }
+
+    template<typename T>
+    void set_ssbo(const char* name, const T& buffer)
+    {
+        set_ssbo(name, (void*)&buffer, sizeof(T));
+    }
+    template<typename T>
+    void set_ssbo(const char* name, const std::vector<T>& buffer)
+    {
+        set_ssbo(name, (void*)buffer.data(), sizeof(T) * buffer.size());
+    }
+    
+    [[nodiscard]] bool is_initialized() const { return program_id_ != 0; }
+private:
+    void update_param_map(std::shared_ptr<shader_opengl> in_shader);
+
+    
+    std::map<std::string, opengl_uniform_data> uniform_map_; // slang_name -> uniform_data
+    std::map<std::string, opengl_texture_data> texture_map_; // slang_name -> uniform_data
+    std::map<GLuint, GLuint> ubo_map_; // binding -> ubo
+
+    GLuint program_id_ = 0;
+    GLuint prev_program_id_ = 0;
+    
+    std::weak_ptr<shader_opengl> pixel_shader_;
+    std::weak_ptr<shader_opengl> vertex_shader_;
+    std::weak_ptr<shader_opengl> compute_shader_;
+};
diff --git a/core/rhi/opengl/shader/shader_ps_opengl.cpp b/core/rhi/opengl/shader/shader_ps_opengl.cpp
index 6b33561..55da2b7 100644
--- a/core/rhi/opengl/shader/shader_ps_opengl.cpp
+++ b/core/rhi/opengl/shader/shader_ps_opengl.cpp
@@ -1 +1,133 @@
 #include "shader_ps_opengl.h"
+
+#include "shader_program_opengl.h"
+#include "shader_vs_opengl.h"
+#include "application/application.h"
+#include "misc/likely.h"
+#include "rhi/renderer.h"
+
+void ps_opengl_compute_callback(const ImDrawList* parent_list, const ImDrawCmd* cmd)
+{
+    const auto data = (shader_ps_opengl::ps_data*)cmd->UserCallbackData;
+    std::shared_ptr<render_target_opengl>& rt = data->rt;
+    const auto program = data->program;
+    program->use_program();
+
+    // do on callback
+    float x = 0;
+    float width = rt->get_width();
+    float y = 0;
+    float height = rt->get_height();
+    
+    const float ortho_projection[4][4] =
+        {
+        { 2.0f/(width-x),   0.0f,         0.0f,   0.0f },
+        { 0.0f,         2.0f/(y-height),   0.0f,   0.0f },
+        { 0.0f,         0.0f,        -1.0f,   0.0f },
+        { (width+x)/(x-width),  (y+height)/(height-y),  0.0f,   1.0f },
+    };
+    // glUseProgram(bd->ShaderHandle);
+    // glUniform1i(bd->AttribLocationTex, 0);
+    // glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
+    program->set_uniform("viewMatrix", ortho_projection);
+    
+    ImVec2 clip_min(x, y);
+    ImVec2 clip_max(width, height);
+    glViewport(0, 0, width, height);
+    
+    // Apply scissor/clipping rectangle (Y is inverted in OpenGL)
+    glScissor(0, 0, width, height);
+    // glScissor((int)clip_min.x, (int)(0 - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y));
+    
+    ImDrawVert& vert1 = data->vtx_buffer[0];
+    ImDrawVert& vert2 = data->vtx_buffer[1];
+    ImDrawVert& vert3 = data->vtx_buffer[2];
+    ImDrawVert& vert4 = data->vtx_buffer[3];
+    vert1.pos = ImVec2(0, 0);
+    vert2.pos = ImVec2(width, 0);
+    vert3.pos = ImVec2(width, height);
+    vert4.pos = ImVec2(0, height);
+    vert1.uv = ImVec2(0, 0);
+    vert2.uv = ImVec2(1, 0);
+    vert3.uv = ImVec2(1, 1);
+    vert4.uv = ImVec2(0, 1);
+    vert1.col = 0xFFFFFFFF;
+    vert2.col = 0xFFFFFFFF;
+    vert3.col = 0xFFFFFFFF;
+    vert4.col = 0xFFFFFFFF;
+    
+
+    const size_t vtx_size = data->vtx_buffer.size() * sizeof(ImDrawVert);
+    const size_t idx_size = data->idx_buffer.size() * sizeof(ImDrawIdx);
+    glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_size, data->vtx_buffer.data());
+    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_size, data->idx_buffer.data());
+
+    glBindBuffer(GL_ARRAY_BUFFER, data->vbo);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data->ibo);
+    
+    glBindFramebuffer(GL_FRAMEBUFFER, rt->get_fbo());
+    
+    glDrawElementsBaseVertex(GL_TRIANGLES, 6, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, 0, 0);
+    // glDrawElements(GL_TRIANGLES, 6, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, 0);
+    
+    program->unuse_program();
+
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+void shader_ps_opengl::draw(int width, int height, std::shared_ptr<shader> in_vertex_shader)
+{
+    const bool is_default_vtx_shader = in_vertex_shader == nullptr;
+    if (is_default_vtx_shader)
+    {
+        in_vertex_shader = application::get()->get_renderer()->get_pixel_shader_render_default_vs();
+    }
+    const std::shared_ptr<shader_vs_opengl> vs = std::static_pointer_cast<shader_vs_opengl>(in_vertex_shader);
+    const std::shared_ptr<shader_ps_opengl> ps = std::static_pointer_cast<shader_ps_opengl>(shared_from_this());
+    
+    if (draw_data.fragment_shader_id != get_shader_id() || draw_data.vertex_shader_id != vs->get_shader_id())
+    {
+        if (!draw_data.program)
+            draw_data.program = std::make_shared<shader_program_opengl>();
+        
+        draw_data.program->create_program(ps, vs);
+        draw_data.fragment_shader_id = get_shader_id();
+        draw_data.vertex_shader_id = vs->get_shader_id();
+    }
+
+    if (UNLIKELY(!draw_data.rt))
+    {
+        draw_data.rt = std::static_pointer_cast<render_target_opengl>(application::get()->create_render_target(width, height, texture_format::RGBA8));
+    }
+    
+    draw_data.rt->resize(width, height);
+
+    if (draw_data.vbo == 0)
+    {
+        glGenBuffers(1, &draw_data.vbo);
+        glGenBuffers(1, &draw_data.ibo);
+        glBindBuffer(GL_ARRAY_BUFFER, draw_data.vbo);
+        const size_t vtx_size = draw_data.vtx_buffer.size() * sizeof(ImDrawVert);
+        glBufferData(GL_ARRAY_BUFFER, vtx_size, draw_data.vtx_buffer.data(), GL_STREAM_DRAW);
+        glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, draw_data.ibo);
+        const size_t idx_size = draw_data.idx_buffer.size() * sizeof(ImDrawIdx);
+        glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_size, draw_data.idx_buffer.data(), GL_STREAM_DRAW);
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+    }
+    
+    ImGui::GetWindowDrawList()->AddCallback(ps_opengl_compute_callback, &draw_data);
+    ImGui::GetWindowDrawList()->AddCallback(ImDrawCallback_ResetRenderState, nullptr);
+    draw_data.rt->draw();
+}
+
+shader_ps_opengl::ps_data::~ps_data()
+{
+    if (vbo)
+        glDeleteBuffers(1, &vbo);
+    if (ibo)
+        glDeleteBuffers(1, &ibo);
+}
diff --git a/core/rhi/opengl/shader/shader_ps_opengl.h b/core/rhi/opengl/shader/shader_ps_opengl.h
index 944b861..97d51d3 100644
--- a/core/rhi/opengl/shader/shader_ps_opengl.h
+++ b/core/rhi/opengl/shader/shader_ps_opengl.h
@@ -1,9 +1,33 @@
 #pragma once
 #include "shader_opengl.h"
+#include "rhi/opengl/render_target_opengl.h"
+
+class shader_vs_opengl;
 
 class shader_ps_opengl : public shader_opengl
 {
 public:
-    shader_ps_opengl(const std::shared_ptr<slang_handle>& handle) : shader_opengl(handle) {}
-    GLenum get_shader_type() const override { return GL_FRAGMENT_SHADER; }
+    explicit shader_ps_opengl(const std::shared_ptr<slang_handle>& handle) : shader_opengl(handle), draw_data({})
+    {
+        draw_data.idx_buffer = {0, 1, 2, 0, 2, 3};
+        draw_data.vtx_buffer.resize(4);
+    }
+
+    [[nodiscard]] GLenum get_shader_type() const override { return GL_FRAGMENT_SHADER; }
+    
+    void draw(int width, int height, std::shared_ptr<shader> in_vertex_shader) override;
+
+    struct ps_data
+    {
+        ~ps_data();
+        std::shared_ptr<render_target_opengl> rt;
+        std::shared_ptr<shader_program_opengl> program;
+        std::vector<ImDrawVert> vtx_buffer;
+        std::vector<ImDrawIdx> idx_buffer;
+        GLuint vbo = 0;
+        GLuint ibo = 0;
+        
+        GLuint vertex_shader_id;
+        GLuint fragment_shader_id;
+    } draw_data;
 };
diff --git a/core/rhi/opengl/texture_opengl.cpp b/core/rhi/opengl/texture_opengl.cpp
index 97b5605..f079d29 100644
--- a/core/rhi/opengl/texture_opengl.cpp
+++ b/core/rhi/opengl/texture_opengl.cpp
@@ -2,6 +2,14 @@
 
 #include "opengl_def.h"
 
+texture_opengl::~texture_opengl()
+{
+    if (texture_id_ != 0)
+    {
+        glDeleteTextures(1, &texture_id_);
+    }
+}
+
 bool texture_opengl::init_data(const unsigned char* data, int width, int height)
 {
     width_ = width;
diff --git a/core/rhi/opengl/texture_opengl.h b/core/rhi/opengl/texture_opengl.h
index cb0f2e9..b047b04 100644
--- a/core/rhi/opengl/texture_opengl.h
+++ b/core/rhi/opengl/texture_opengl.h
@@ -6,7 +6,9 @@
 class texture_opengl : public texture
 {
 public:
+    ~texture_opengl() override;
     ImTextureID get_texture_id() override { return (void*)static_cast<intptr_t>(texture_id_); }
+    GLuint get_texture() const { return texture_id_; }
     bool init_data(const unsigned char* data, int width, int height) override;
     [[nodiscard]] bool is_valid() const override { return texture_id_ != 0; }
 private:
diff --git a/core/rhi/renderer.cpp b/core/rhi/renderer.cpp
index f8d9350..44703b1 100644
--- a/core/rhi/renderer.cpp
+++ b/core/rhi/renderer.cpp
@@ -2,9 +2,15 @@
 
 #include "application/application.h"
 
+void renderer::shutdown()
+{
+    default_vs_ = nullptr;
+    session_ = nullptr;
+}
+
 void renderer::init_slang(const std::string& shader_path)
 {
-    session_ = create_slang_session(shader_path);
+    create_slang_session(shader_path, session_.writeRef());
 }
 
 std::shared_ptr<shader> renderer::get_pixel_shader_render_default_vs()
diff --git a/core/rhi/renderer.h b/core/rhi/renderer.h
index d9677af..ac074ce 100644
--- a/core/rhi/renderer.h
+++ b/core/rhi/renderer.h
@@ -16,10 +16,11 @@ public:
     virtual ~renderer() = default;
     virtual void pre_init() {}
     virtual bool init(SDL_Window* window_handle) = 0;
-    virtual void shutdown() = 0;
+    virtual void shutdown();
+    virtual void post_shutdown() {}
 
     void init_slang(const std::string& shader_path);
-    virtual Slang::ComPtr<slang::ISession> create_slang_session(const std::string& shader_path) = 0;
+    virtual void create_slang_session(const std::string& shader_path, slang::ISession** out_session) = 0;
     virtual Slang::ComPtr<slang::ISession> get_slang_session() { return session_; }
     virtual std::shared_ptr<shader> load_shader(const std::string& entry_name) = 0;
     virtual std::shared_ptr<shader> get_pixel_shader_render_default_vs();
diff --git a/core/rhi/rhi_defintion.h b/core/rhi/rhi_defintion.h
index 6fe7fc5..fa79b86 100644
--- a/core/rhi/rhi_defintion.h
+++ b/core/rhi/rhi_defintion.h
@@ -5,4 +5,31 @@ enum class texture_format
     RGBA8,
     RGBA16_FLOAT,
     RGBA32_FLOAT,
+    R32_FLOAT,
+    RG32_FLOAT,
+    R11F_G11F_B10F,
+    R16_FLOAT,
+    RGBA16,
+    RGB10_A2,
+    RG16,
+    RG8,
+    R16,
+    R8,
+    RG16_SNORM,
+    RG8_SNORM,
+    R16_SNORM,
+    R8_SNORM,
+    R32I,
+    R32UI,
+    R8I,
+    R16I,
+    R32F,
+    R8UI,
+    R16UI,
+    RG32I,
+    RG32UI,
+    RG8I,
+    RG16I,
+    RGB10_A2UI,
+    RG16UI,
 };
diff --git a/core/rhi/shader.h b/core/rhi/shader.h
index 76f930e..52787a7 100644
--- a/core/rhi/shader.h
+++ b/core/rhi/shader.h
@@ -4,7 +4,7 @@ class render_target;
 class texture;
 class slang_handle;
 
-class shader
+class shader : public std::enable_shared_from_this<shader>
 {
 public:
     virtual ~shader() = default;
@@ -13,6 +13,7 @@ public:
     virtual bool init() { return false; }
     [[nodiscard]] virtual bool is_initialized() const = 0;
     [[nodiscard]] virtual bool is_valid() const { return handle_ != nullptr && is_initialized(); }
+    [[nodiscard]] std::shared_ptr<slang_handle> get_handle() { return handle_; }
     
     virtual void bind() = 0;
     virtual void unbind() = 0;
@@ -20,20 +21,20 @@ public:
     virtual void draw(int width, int height, std::shared_ptr<shader> in_vertex_shader = nullptr) {}
 
     // param setters
-    virtual void set_constant_buffer(const char* name, void* buffer, int size) = 0;
+    virtual void set_cbuffer(const char* name, void* buffer, int size) = 0;
     virtual void set_uav_buffer(const char* name, void* buffer, int size, int element_size) {}
     virtual void set_texture(const char* name, std::shared_ptr<texture> in_texture) = 0;
     virtual void set_render_target(const char* name, std::shared_ptr<render_target> in_render_target) = 0;
 
     template<typename T>
-    void set_constant_buffer(const char* name, const T& buffer)
+    void set_cbuffer(const char* name, const T& buffer)
     {
-        set_constant_buffer(name, (void*)&buffer, sizeof(T));
+        set_cbuffer(name, (void*)&buffer, sizeof(T));
     }
     template<typename T>
-    void set_constant_buffer(const char* name, const std::vector<T>& buffer)
+    void set_cbuffer(const char* name, const std::vector<T>& buffer)
     {
-        set_constant_buffer(name, (void*)buffer.data(), sizeof(T) * buffer.size());
+        set_cbuffer(name, (void*)buffer.data(), sizeof(T) * buffer.size());
     }
     
     template<typename T>
diff --git a/core/rhi/slang_handle.cpp b/core/rhi/slang_handle.cpp
index 9a92580..726ff49 100644
--- a/core/rhi/slang_handle.cpp
+++ b/core/rhi/slang_handle.cpp
@@ -59,6 +59,7 @@ Slang::ComPtr<slang::IBlob> slang_handle::get_entry_point_code() const
 {
     Slang::ComPtr<slang::IBlob> diagnostics;
     Slang::ComPtr<slang::IBlob> code_blob;
+    
     program->getEntryPointCode(
         entry_point_index_,
         target_index,
@@ -72,3 +73,27 @@ Slang::ComPtr<slang::IBlob> slang_handle::get_entry_point_code() const
     }
     return code_blob;
 }
+
+std::map<std::string, slang::BindingType> slang_handle::get_binding_types() const
+{
+    std::map<std::string, slang::BindingType> out;
+    slang::ProgramLayout* layout = program->getLayout(target_index);
+    layout->getGlobalConstantBufferBinding();
+    return out;
+}
+
+unsigned int slang_handle::get_bind_index(const char* param_name) const
+{
+    slang::ProgramLayout* layout = program->getLayout(target_index);
+    // const auto entry_reflection = layout->getEntryPointByIndex(entry_point_index_);
+    const auto param_reflection = layout->getParameterCount();
+    for (unsigned int i = 0; i < param_reflection; ++i)
+    {
+        const auto param = layout->getParameterByIndex(i);
+        if (strcmp(param->getName(), param_name) == 0)
+        {
+            return param->getBindingIndex();
+        }
+    }
+    return -1;
+}
diff --git a/core/rhi/slang_handle.h b/core/rhi/slang_handle.h
index 64fd88f..93013cb 100644
--- a/core/rhi/slang_handle.h
+++ b/core/rhi/slang_handle.h
@@ -1,4 +1,6 @@
 #pragma once
+#include <map>
+
 #include "slang-com-ptr.h"
 #include <string>
 
@@ -19,6 +21,8 @@ public:
         return entry_reflection->getName();
     }
     [[nodiscard]] SlangStage get_shader_type() const { return shader_type_; }
+    [[nodiscard]] std::map<std::string, slang::BindingType> get_binding_types() const;
+    [[nodiscard]] unsigned int get_bind_index(const char* param_name) const;
 private:
     int entry_point_index_ = -1;
     SlangStage shader_type_ = SLANG_STAGE_NONE;
diff --git a/core/rhi/windows/dx11/dx_format.h b/core/rhi/windows/dx11/dx_format.h
index 462c128..bc74552 100644
--- a/core/rhi/windows/dx11/dx_format.h
+++ b/core/rhi/windows/dx11/dx_format.h
@@ -13,6 +13,60 @@ inline DXGI_FORMAT to_dx_format(texture_format format)
         return DXGI_FORMAT_R16G16B16A16_FLOAT;
     case texture_format::RGBA32_FLOAT:
         return DXGI_FORMAT_R32G32B32A32_FLOAT;
+    case texture_format::R32_FLOAT:
+        return DXGI_FORMAT_R32_FLOAT;
+    case texture_format::RG32_FLOAT:
+        return DXGI_FORMAT_R32G32_FLOAT;
+    case texture_format::R11F_G11F_B10F:
+        return DXGI_FORMAT_R11G11B10_FLOAT;
+    case texture_format::R16_FLOAT:
+        return DXGI_FORMAT_R16_FLOAT;
+    case texture_format::RGBA16:
+        return DXGI_FORMAT_R16G16B16A16_UNORM;
+    case texture_format::RGB10_A2:
+        return DXGI_FORMAT_R10G10B10A2_UNORM;
+    case texture_format::RG16:
+        return DXGI_FORMAT_R16G16_UNORM;
+    case texture_format::RG8:
+        return DXGI_FORMAT_R8G8_UNORM;
+    case texture_format::R16:
+        return DXGI_FORMAT_R16_UNORM;
+    case texture_format::R8:
+        return DXGI_FORMAT_R8_UNORM;
+    case texture_format::RG16_SNORM:
+        return DXGI_FORMAT_R16G16_SNORM;
+    case texture_format::RG8_SNORM:
+        return DXGI_FORMAT_R8G8_SNORM;
+    case texture_format::R16_SNORM:
+        return DXGI_FORMAT_R16_SNORM;
+    case texture_format::R8_SNORM:
+        return DXGI_FORMAT_R8_SNORM;
+    case texture_format::R32I:
+        return DXGI_FORMAT_R32_SINT;
+    case texture_format::R32UI:
+        return DXGI_FORMAT_R32_UINT;
+    case texture_format::R8I:
+        return DXGI_FORMAT_R8_SINT;
+    case texture_format::R16I:
+        return DXGI_FORMAT_R16_SINT;
+    case texture_format::R32F:
+        return DXGI_FORMAT_R32_FLOAT;
+    case texture_format::R8UI:
+        return DXGI_FORMAT_R8_UINT;
+    case texture_format::R16UI:
+        return DXGI_FORMAT_R16_UINT;
+    case texture_format::RG32I:
+        return DXGI_FORMAT_R32G32_SINT;
+    case texture_format::RG32UI:
+        return DXGI_FORMAT_R32G32_UINT;
+    case texture_format::RG8I:
+        return DXGI_FORMAT_R8G8_SINT;
+    case texture_format::RG16I:
+        return DXGI_FORMAT_R16G16_SINT;
+    case texture_format::RGB10_A2UI:
+        return DXGI_FORMAT_R10G10B10A2_UINT;
+    case texture_format::RG16UI:
+        return DXGI_FORMAT_R16G16_UINT;
     }
     return DXGI_FORMAT_UNKNOWN;
 }
diff --git a/core/rhi/windows/dx11/find_best_device_dx11.cpp b/core/rhi/windows/dx11/find_best_device_dx11.cpp
index 4a295d7..66d220f 100644
--- a/core/rhi/windows/dx11/find_best_device_dx11.cpp
+++ b/core/rhi/windows/dx11/find_best_device_dx11.cpp
@@ -138,6 +138,7 @@ void find_best_device_dx11::select_adapter()
 	for (unsigned int adapter_index = 0; enumerate_adapters(adapter_index, testing_adapter.get_init_reference()) != DXGI_ERROR_NOT_FOUND; ++adapter_index)
 	{
 		DXGI_ADAPTER_DESC adapter_desc;
+		
 		const HRESULT desc_result = testing_adapter->GetDesc(&adapter_desc);
 		if (FAILED(desc_result))
 		{
diff --git a/core/rhi/windows/dx11/renderer_dx11.cpp b/core/rhi/windows/dx11/renderer_dx11.cpp
index b252eef..025db35 100644
--- a/core/rhi/windows/dx11/renderer_dx11.cpp
+++ b/core/rhi/windows/dx11/renderer_dx11.cpp
@@ -47,6 +47,7 @@ bool renderer_dx11::init(SDL_Window* window_handle)
 
 void renderer_dx11::shutdown()
 {
+    renderer::shutdown();
     // Cleanup
     ImGui_ImplDX11_Shutdown();
     ImGui_ImplSDL3_Shutdown();
@@ -56,7 +57,7 @@ void renderer_dx11::shutdown()
     g_d3d11_swap_chain.safe_release();
 }
 
-Slang::ComPtr<slang::ISession> renderer_dx11::create_slang_session(const std::string& shader_path)
+void renderer_dx11::create_slang_session(const std::string& shader_path, slang::ISession** out_session)
 {
     slang::TargetDesc target_desc;
     target_desc.format = SLANG_HLSL;
@@ -68,10 +69,9 @@ Slang::ComPtr<slang::ISession> renderer_dx11::create_slang_session(const std::st
     session_desc.searchPathCount = 1;
     session_desc.targets = &target_desc;
     session_desc.targetCount = 1;
+    session_desc.defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_COLUMN_MAJOR;
     
-    Slang::ComPtr<slang::ISession> out;
-    g_slang_global_session->createSession(session_desc, out.writeRef());
-    return out;
+    g_slang_global_session->createSession(session_desc, out_session);
 }
 
 std::shared_ptr<shader> renderer_dx11::load_shader(const std::string& entry_name)
diff --git a/core/rhi/windows/dx11/renderer_dx11.h b/core/rhi/windows/dx11/renderer_dx11.h
index 97dbd79..c9bd2f2 100644
--- a/core/rhi/windows/dx11/renderer_dx11.h
+++ b/core/rhi/windows/dx11/renderer_dx11.h
@@ -19,8 +19,8 @@ public:
     renderer_dx11();
     bool init(SDL_Window* window_handle) override;
     void shutdown() override;
-    
-    Slang::ComPtr<slang::ISession> create_slang_session(const std::string& shader_path) override;
+
+    void create_slang_session(const std::string& shader_path, slang::ISession** out_session) override;
     std::shared_ptr<shader> load_shader(const std::string& entry_name) override;
     
     void new_frame(SDL_Window* window_handle) override;
diff --git a/core/rhi/windows/dx11/shader/constant_buffer_dx11.h b/core/rhi/windows/dx11/shader/constant_buffer_dx11.h
index 2a69ab9..b501791 100644
--- a/core/rhi/windows/dx11/shader/constant_buffer_dx11.h
+++ b/core/rhi/windows/dx11/shader/constant_buffer_dx11.h
@@ -15,7 +15,7 @@ public:
         malloc_size_ = (malloc_size_ + 15) & ~15;
         // and less than D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT
         malloc_size_ = min(malloc_size_, D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT);
-        D3D11_BUFFER_DESC desc;
+        D3D11_BUFFER_DESC desc = {};
         desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
         desc.Usage = D3D11_USAGE_DYNAMIC;
         desc.ByteWidth = malloc_size_;
@@ -39,6 +39,7 @@ public:
     void set_buffer_data(const void* in_buffer_data) const
     {
         memcpy(buffer_data_, in_buffer_data, element_size_);
+        // g_d3d11_device_context->UpdateSubresource(buffer_, 0, nullptr, in_buffer_data, 0, 0);
     }
     void update_buffer() const
     {
diff --git a/core/rhi/windows/dx11/shader/shader_dx11.cpp b/core/rhi/windows/dx11/shader/shader_dx11.cpp
index 5121188..f842d41 100644
--- a/core/rhi/windows/dx11/shader/shader_dx11.cpp
+++ b/core/rhi/windows/dx11/shader/shader_dx11.cpp
@@ -65,7 +65,7 @@ bool shader_dx11::init()
     return true;
 }
 
-void shader_dx11::set_constant_buffer(const char* name, void* buffer, int size)
+void shader_dx11::set_cbuffer(const char* name, void* buffer, int size)
 {
     auto find = constant_buffers_.find(name);
     if (find == constant_buffers_.end())
diff --git a/core/rhi/windows/dx11/shader/shader_dx11.h b/core/rhi/windows/dx11/shader/shader_dx11.h
index 3acd6bf..9c38a3f 100644
--- a/core/rhi/windows/dx11/shader/shader_dx11.h
+++ b/core/rhi/windows/dx11/shader/shader_dx11.h
@@ -23,7 +23,7 @@ public:
     [[nodiscard]] virtual ID3D11DeviceChild* get_shader() = 0;
     [[nodiscard]] virtual const char* get_shader_model() const = 0;
 
-    void set_constant_buffer(const char* name, void* buffer, int size) override;
+    void set_cbuffer(const char* name, void* buffer, int size) override;
     void set_texture(const char* name, std::shared_ptr<texture> in_texture) override;
     void set_render_target(const char* name, std::shared_ptr<render_target> in_render_target) override;
 protected:
diff --git a/core/rhi/windows/dx11/shader/shader_ps_dx11.cpp b/core/rhi/windows/dx11/shader/shader_ps_dx11.cpp
index de16d55..8f134f8 100644
--- a/core/rhi/windows/dx11/shader/shader_ps_dx11.cpp
+++ b/core/rhi/windows/dx11/shader/shader_ps_dx11.cpp
@@ -177,14 +177,22 @@ void shader_ps_dx11::draw(int width, int height, std::shared_ptr<shader> in_vert
         const float R = width;
         const float T = 0;
         const float B = height;
+        // float mvp[4][4] =
+        // {
+        //     { 2.0f/(R-L),   0.0f,           0.0f,       (R+L)/(L-R) },
+        //     { 0.0f,         2.0f/(T-B),     0.0f,       (T+B)/(B-T) },
+        //     { 0.0f,         0.0f,           0.5f,       0.5f },
+        //     { 0.0f,         0.0f,           0.0f,       1.0f },
+        // };
+
         float mvp[4][4] =
         {
-            { 2.0f/(R-L),   0.0f,           0.0f,       (R+L)/(L-R) },
-            { 0.0f,         2.0f/(T-B),     0.0f,       (T+B)/(B-T) },
-            { 0.0f,         0.0f,           0.5f,       0.5f },
-            { 0.0f,         0.0f,           0.0f,       1.0f },
+            { 2.0f/(R-L),   0.0f,         0.0f,   0.0f },
+            { 0.0f,         2.0f/(T-B),   0.0f,   0.0f },
+            { 0.0f,         0.0f,        -1.0f,   0.0f },
+            { (R+L)/(L-R),  (T+B)/(B-T),  0.0f,   1.0f },
         };
-        in_vertex_shader->set_constant_buffer("ProjectionMatrix", mvp, sizeof(mvp));
+        in_vertex_shader->set_cbuffer("viewMatrix", mvp, sizeof(mvp));
     }
     draw_data.vertex_shader = in_vertex_shader;
     
diff --git a/third_party/glslang b/third_party/glslang
new file mode 160000
index 0000000..8c3dbb3
--- /dev/null
+++ b/third_party/glslang
@@ -0,0 +1 @@
+Subproject commit 8c3dbb3596e552d8e89c16350b07373ac36262bb
diff --git a/third_party/slang/inc/slang-tag-version.h b/third_party/slang/inc/slang-tag-version.h
index e0ef88a..1a78860 100644
--- a/third_party/slang/inc/slang-tag-version.h
+++ b/third_party/slang/inc/slang-tag-version.h
@@ -1 +1 @@
-#define SLANG_TAG_VERSION "v2024.0.0" 
+#define SLANG_TAG_VERSION "v2024.0.1" 
diff --git a/third_party/slang/inc/slang.h b/third_party/slang/inc/slang.h
index c4fb555..54466ca 100644
--- a/third_party/slang/inc/slang.h
+++ b/third_party/slang/inc/slang.h
@@ -4179,6 +4179,8 @@ namespace slang
 
         virtual SLANG_NO_THROW void SLANG_MCALL setReportPerfBenchmark(bool value) = 0;
 
+        virtual SLANG_NO_THROW void SLANG_MCALL setSkipSPIRVValidation(bool value) = 0;
+
     };
 
     #define SLANG_UUID_ICompileRequest ICompileRequest::getTypeGuid()
diff --git a/third_party/slang/lib/win64/gfx.dll b/third_party/slang/lib/win64/gfx.dll
index adb4c7a..8d04770 100644
Binary files a/third_party/slang/lib/win64/gfx.dll and b/third_party/slang/lib/win64/gfx.dll differ
diff --git a/third_party/slang/lib/win64/slang-rt.dll b/third_party/slang/lib/win64/slang-rt.dll
index 3f8a76c..80c19b1 100644
Binary files a/third_party/slang/lib/win64/slang-rt.dll and b/third_party/slang/lib/win64/slang-rt.dll differ
diff --git a/third_party/slang/lib/win64/slang.dll b/third_party/slang/lib/win64/slang.dll
index 23e3ae2..93fb079 100644
Binary files a/third_party/slang/lib/win64/slang.dll and b/third_party/slang/lib/win64/slang.dll differ
diff --git a/third_party/slang/lib/win64/slangc.exe b/third_party/slang/lib/win64/slangc.exe
index f6835d3..a9f9948 100644
Binary files a/third_party/slang/lib/win64/slangc.exe and b/third_party/slang/lib/win64/slangc.exe differ
diff --git a/third_party/slang/lib/win64/slangd.exe b/third_party/slang/lib/win64/slangd.exe
index fbe9386..eef6804 100644
Binary files a/third_party/slang/lib/win64/slangd.exe and b/third_party/slang/lib/win64/slangd.exe differ