From 9dfc4f2fed39e86b724f9b43de4d084d78da21e6 Mon Sep 17 00:00:00 2001
From: Nanako <469449812@qq.com>
Date: Wed, 6 Nov 2024 21:56:47 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=9B=BE=E7=89=87=E7=BB=98?=
 =?UTF-8?q?=E5=88=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/renderer/CMakeLists.txt                   |   2 +
 src/renderer/backend/dx/dx_renderer.cpp       | 165 +-----------------
 src/renderer/backend/dx/dx_renderer.h         | 142 ++++++++++++++-
 src/renderer/backend/dx/dx_texture.cpp        |  61 +++++--
 src/renderer/backend/dx/dx_texture.h          |  12 +-
 src/renderer/backend/dx/dx_window.cpp         |  58 ++----
 src/renderer/backend/dx/dx_window.h           |   3 +
 .../dx/pipeline/dx_pipeline_loader.cpp        |  41 +++++
 .../backend/dx/pipeline/dx_pipeline_loader.h  |  12 ++
 .../backend/dx/pipeline/dx_rect_pipeline.cpp  |  33 +---
 .../backend/dx/pipeline/dx_rect_pipeline.h    |   5 +-
 .../dx/pipeline/dx_rounded_rect_pipeline.cpp  |  34 +---
 .../dx/pipeline/dx_rounded_rect_pipeline.h    |   5 +-
 .../dx/pipeline/dx_texture_pipeline.cpp       |  53 ++++++
 .../backend/dx/pipeline/dx_texture_pipeline.h |  19 ++
 .../core/pipeline/texture_pipeline.cpp        |  16 ++
 src/renderer/core/pipeline/texture_pipeline.h |  18 ++
 src/renderer/core/renderer/renderer.cpp       |  58 ++++++
 src/renderer/core/renderer/renderer.h         | 114 +++++++++++-
 .../core/renderer/renderer_context.cpp        | 110 +++++++-----
 src/renderer/core/renderer/renderer_context.h |  38 ++--
 src/renderer/core/renderer/renderer_texture.h |  32 +++-
 src/renderer/misc/color.cpp                   |   3 +
 src/renderer/misc/color.h                     |   8 +
 src/renderer/shader/aorii_texture.slang       |  35 ++++
 25 files changed, 708 insertions(+), 369 deletions(-)
 create mode 100644 src/renderer/backend/dx/pipeline/dx_pipeline_loader.cpp
 create mode 100644 src/renderer/backend/dx/pipeline/dx_pipeline_loader.h
 create mode 100644 src/renderer/backend/dx/pipeline/dx_texture_pipeline.cpp
 create mode 100644 src/renderer/backend/dx/pipeline/dx_texture_pipeline.h
 create mode 100644 src/renderer/core/pipeline/texture_pipeline.cpp
 create mode 100644 src/renderer/core/pipeline/texture_pipeline.h
 create mode 100644 src/renderer/misc/color.cpp
 create mode 100644 src/renderer/shader/aorii_texture.slang

diff --git a/src/renderer/CMakeLists.txt b/src/renderer/CMakeLists.txt
index ab51178..d20b11b 100644
--- a/src/renderer/CMakeLists.txt
+++ b/src/renderer/CMakeLists.txt
@@ -63,6 +63,8 @@ compile_slang_shaders("${CMAKE_CURRENT_SOURCE_DIR}/shader/aorii_rect.slang" "ver
 compile_slang_shaders("${CMAKE_CURRENT_SOURCE_DIR}/shader/aorii_rect.slang" "pixel" "pixel_main")
 compile_slang_shaders("${CMAKE_CURRENT_SOURCE_DIR}/shader/aorii_rounded_rect.slang" "vertex" "vertex_main")
 compile_slang_shaders("${CMAKE_CURRENT_SOURCE_DIR}/shader/aorii_rounded_rect.slang" "pixel" "pixel_main")
+compile_slang_shaders("${CMAKE_CURRENT_SOURCE_DIR}/shader/aorii_texture.slang" "vertex" "vertex_main")
+compile_slang_shaders("${CMAKE_CURRENT_SOURCE_DIR}/shader/aorii_texture.slang" "pixel" "pixel_main")
 add_compile_shaders_target("compile_aorii_shaders" ${PROJECT_NAME})
 
 # 如果需要编译example, 添加自定义命令用于拷贝shader文件
diff --git a/src/renderer/backend/dx/dx_renderer.cpp b/src/renderer/backend/dx/dx_renderer.cpp
index 4371ff1..5359ea9 100644
--- a/src/renderer/backend/dx/dx_renderer.cpp
+++ b/src/renderer/backend/dx/dx_renderer.cpp
@@ -9,145 +9,6 @@
 #include "core/window/window_manager.h"
 #include "spdlog/spdlog.h"
 
-DXGI_FORMAT get_dxgi_format(texture_format format) {
-       switch (format) {
-        // 8-bit formats
-        case texture_format::R8_UNORM:
-            return DXGI_FORMAT_R8_UNORM;
-        case texture_format::R8_SNORM:
-            return DXGI_FORMAT_R8_SNORM;
-        case texture_format::R8_UINT:
-            return DXGI_FORMAT_R8_UINT;
-        case texture_format::R8_SINT:
-            return DXGI_FORMAT_R8_SINT;
-
-        // 16-bit formats
-        case texture_format::RG8_UNORM:
-            return DXGI_FORMAT_R8G8_UNORM;
-        case texture_format::RG8_SNORM:
-            return DXGI_FORMAT_R8G8_SNORM;
-        case texture_format::RG8_UINT:
-            return DXGI_FORMAT_R8G8_UINT;
-        case texture_format::RG8_SINT:
-            return DXGI_FORMAT_R8G8_SINT;
-
-        // 24-bit formats
-        case texture_format::RGB8_UNORM:
-            return DXGI_FORMAT_B8G8R8X8_UNORM; // Note: DXGI doesn't have a direct RGB8 format
-        case texture_format::RGB8_SNORM:
-            return DXGI_FORMAT_UNKNOWN; // No direct equivalent
-        case texture_format::RGB8_UINT:
-            return DXGI_FORMAT_UNKNOWN; // No direct equivalent
-        case texture_format::RGB8_SINT:
-            return DXGI_FORMAT_UNKNOWN; // No direct equivalent
-
-        // 32-bit formats
-        case texture_format::RGBA8_UNORM:
-            return DXGI_FORMAT_R8G8B8A8_UNORM;
-        case texture_format::RGBA8_SNORM:
-            return DXGI_FORMAT_R8G8B8A8_SNORM;
-        case texture_format::RGBA8_UINT:
-            return DXGI_FORMAT_R8G8B8A8_UINT;
-        case texture_format::RGBA8_SINT:
-            return DXGI_FORMAT_R8G8B8A8_SINT;
-
-        // SRGB formats
-        case texture_format::SRGB8:
-            return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; // Note: Approximation
-        case texture_format::SRGB8_ALPHA8:
-            return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
-
-        // 16-bit float formats
-        case texture_format::R16_FLOAT:
-            return DXGI_FORMAT_R16_FLOAT;
-        case texture_format::RG16_FLOAT:
-            return DXGI_FORMAT_R16G16_FLOAT;
-        case texture_format::RGB16_FLOAT:
-            return DXGI_FORMAT_R16G16B16A16_FLOAT; // Note: No direct RGB16 format
-        case texture_format::RGBA16_FLOAT:
-            return DXGI_FORMAT_R16G16B16A16_FLOAT;
-
-        // 32-bit float formats
-        case texture_format::R32_FLOAT:
-            return DXGI_FORMAT_R32_FLOAT;
-        case texture_format::RG32_FLOAT:
-            return DXGI_FORMAT_R32G32_FLOAT;
-        case texture_format::RGB32_FLOAT:
-            return DXGI_FORMAT_R32G32B32_FLOAT;
-        case texture_format::RGBA32_FLOAT:
-            return DXGI_FORMAT_R32G32B32A32_FLOAT;
-
-        // Integer formats
-        case texture_format::R16_UINT:
-            return DXGI_FORMAT_R16_UINT;
-        case texture_format::R16_SINT:
-            return DXGI_FORMAT_R16_SINT;
-        case texture_format::RG16_UINT:
-            return DXGI_FORMAT_R16G16_UINT;
-        case texture_format::RG16_SINT:
-            return DXGI_FORMAT_R16G16_SINT;
-        case texture_format::RGBA16_UINT:
-            return DXGI_FORMAT_R16G16B16A16_UINT;
-        case texture_format::RGBA16_SINT:
-            return DXGI_FORMAT_R16G16B16A16_SINT;
-        case texture_format::R32_UINT:
-            return DXGI_FORMAT_R32_UINT;
-        case texture_format::R32_SINT:
-            return DXGI_FORMAT_R32_SINT;
-        case texture_format::RG32_UINT:
-            return DXGI_FORMAT_R32G32_UINT;
-        case texture_format::RG32_SINT:
-            return DXGI_FORMAT_R32G32_SINT;
-        case texture_format::RGBA32_UINT:
-            return DXGI_FORMAT_R32G32B32A32_UINT;
-        case texture_format::RGBA32_SINT:
-            return DXGI_FORMAT_R32G32B32A32_SINT;
-
-        // Depth/stencil formats
-        case texture_format::D16_UNORM:
-            return DXGI_FORMAT_D16_UNORM;
-        case texture_format::D24_UNORM_S8_UINT:
-            return DXGI_FORMAT_D24_UNORM_S8_UINT;
-        case texture_format::D32_FLOAT:
-            return DXGI_FORMAT_D32_FLOAT;
-        case texture_format::D32_FLOAT_S8X24_UINT:
-            return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
-
-        // Compressed formats
-        case texture_format::BC1_UNORM:
-            return DXGI_FORMAT_BC1_UNORM;
-        case texture_format::BC1_UNORM_SRGB:
-            return DXGI_FORMAT_BC1_UNORM_SRGB;
-        case texture_format::BC2_UNORM:
-            return DXGI_FORMAT_BC2_UNORM;
-        case texture_format::BC2_UNORM_SRGB:
-            return DXGI_FORMAT_BC2_UNORM_SRGB;
-        case texture_format::BC3_UNORM:
-            return DXGI_FORMAT_BC3_UNORM;
-        case texture_format::BC3_UNORM_SRGB:
-            return DXGI_FORMAT_BC3_UNORM_SRGB;
-        case texture_format::BC4_UNORM:
-            return DXGI_FORMAT_BC4_UNORM;
-        case texture_format::BC4_SNORM:
-            return DXGI_FORMAT_BC4_SNORM;
-        case texture_format::BC5_UNORM:
-            return DXGI_FORMAT_BC5_UNORM;
-        case texture_format::BC5_SNORM:
-            return DXGI_FORMAT_BC5_SNORM;
-        case texture_format::BC6H_UF16:
-            return DXGI_FORMAT_BC6H_UF16;
-        case texture_format::BC6H_SF16:
-            return DXGI_FORMAT_BC6H_SF16;
-        case texture_format::BC7_UNORM:
-            return DXGI_FORMAT_BC7_UNORM;
-        case texture_format::BC7_UNORM_SRGB:
-            return DXGI_FORMAT_BC7_UNORM_SRGB;
-
-        default:
-            return DXGI_FORMAT_UNKNOWN;
-    }
-}
-
 bool dx_renderer::init() {
     if (!glfwInit()) return false;
     glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
@@ -172,6 +33,7 @@ bool dx_renderer::init() {
 
     rect_p.init();
     rounded_rect_p.init();
+    texture_p.init();
 
     return true;
 }
@@ -180,6 +42,7 @@ void dx_renderer::destroy() {
     if (blend_state) blend_state->Release();
     rect_p.destroy();
     rounded_rect_p.destroy();
+    texture_p.destroy();
     glfwTerminate();
 }
 
@@ -192,30 +55,8 @@ bool dx_renderer::render() {
     return true;
 }
 
-void dx_renderer::destroy_texture(renderer_texture* texture) {
-#ifdef DEBUG
-    spdlog::info("Destroying texture");
-#endif
-}
-
 renderer_texture* dx_renderer::create_texture(const Eigen::Vector2i& size, texture_format in_format) {
-    D3D11_TEXTURE2D_DESC desc = {};
-    desc.Width = size.x();
-    desc.Height = size.y();
-    desc.MipLevels = 1;
-    desc.ArraySize = 1;
-    desc.Format = get_dxgi_format(in_format);
-    desc.SampleDesc.Count = 1;
-    desc.SampleDesc.Quality = 0;
-    desc.Usage = D3D11_USAGE_DYNAMIC;
-    desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
-    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
-    desc.MiscFlags = 0;
-
-    ID3D11Texture2D* texture = nullptr;
-    if (FAILED(device->CreateTexture2D(&desc, nullptr, &texture))) { return nullptr; }
-
-    return new dx_texture(texture);
+    return new dx_texture(size, in_format);
 }
 
 std::vector<char> dx_renderer::load_shader(const std::string& shader_name) {
diff --git a/src/renderer/backend/dx/dx_renderer.h b/src/renderer/backend/dx/dx_renderer.h
index 394bda9..10f090f 100644
--- a/src/renderer/backend/dx/dx_renderer.h
+++ b/src/renderer/backend/dx/dx_renderer.h
@@ -4,6 +4,7 @@
 #include "core/renderer/renderer.h"
 #include "pipeline/dx_rect_pipeline.h"
 #include "pipeline/dx_rounded_rect_pipeline.h"
+#include "pipeline/dx_texture_pipeline.h"
 
 class dx_window;
 
@@ -14,13 +15,13 @@ public:
     bool render() override;
 
     renderer_texture* create_texture(const Eigen::Vector2i& size, texture_format in_format) override;
-    void destroy_texture(renderer_texture* texture) override;
 
     [[nodiscard]] ID3D11Device* get_d3d_device() const { return device; }
     [[nodiscard]] ID3D11DeviceContext* get_d3d_context() const { return context; }
 
     rect_pipeline* get_rect_pipeline() override { return &rect_p; }
     rounded_rect_pipeline* get_rounded_rect_pipeline() override { return &rounded_rect_p; }
+    texture_pipeline* get_texture_pipeline() override { return &texture_p; }
     [[nodiscard]] ID3D11BlendState* get_blend_state() const { return blend_state; }
 
     std::vector<char> load_shader(const std::string& shader_name) override;
@@ -37,5 +38,144 @@ private:
 
     dx_rect_pipeline rect_p;
     dx_rounded_rect_pipeline rounded_rect_p;
+    dx_texture_pipeline texture_p;
 };
 
+inline DXGI_FORMAT get_dxgi_format(texture_format format) {
+       switch (format) {
+        // 8-bit formats
+        case texture_format::R8_UNORM:
+            return DXGI_FORMAT_R8_UNORM;
+        case texture_format::R8_SNORM:
+            return DXGI_FORMAT_R8_SNORM;
+        case texture_format::R8_UINT:
+            return DXGI_FORMAT_R8_UINT;
+        case texture_format::R8_SINT:
+            return DXGI_FORMAT_R8_SINT;
+
+        // 16-bit formats
+        case texture_format::RG8_UNORM:
+            return DXGI_FORMAT_R8G8_UNORM;
+        case texture_format::RG8_SNORM:
+            return DXGI_FORMAT_R8G8_SNORM;
+        case texture_format::RG8_UINT:
+            return DXGI_FORMAT_R8G8_UINT;
+        case texture_format::RG8_SINT:
+            return DXGI_FORMAT_R8G8_SINT;
+
+        // 24-bit formats
+        case texture_format::RGB8_UNORM:
+            return DXGI_FORMAT_B8G8R8X8_UNORM; // Note: DXGI doesn't have a direct RGB8 format
+        case texture_format::RGB8_SNORM:
+            return DXGI_FORMAT_UNKNOWN; // No direct equivalent
+        case texture_format::RGB8_UINT:
+            return DXGI_FORMAT_UNKNOWN; // No direct equivalent
+        case texture_format::RGB8_SINT:
+            return DXGI_FORMAT_UNKNOWN; // No direct equivalent
+
+        // 32-bit formats
+        case texture_format::RGBA8_UNORM:
+            return DXGI_FORMAT_R8G8B8A8_UNORM;
+        case texture_format::RGBA8_SNORM:
+            return DXGI_FORMAT_R8G8B8A8_SNORM;
+        case texture_format::RGBA8_UINT:
+            return DXGI_FORMAT_R8G8B8A8_UINT;
+        case texture_format::RGBA8_SINT:
+            return DXGI_FORMAT_R8G8B8A8_SINT;
+
+        // SRGB formats
+        case texture_format::SRGB8:
+            return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; // Note: Approximation
+        case texture_format::SRGB8_ALPHA8:
+            return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
+
+        // 16-bit float formats
+        case texture_format::R16_FLOAT:
+            return DXGI_FORMAT_R16_FLOAT;
+        case texture_format::RG16_FLOAT:
+            return DXGI_FORMAT_R16G16_FLOAT;
+        case texture_format::RGB16_FLOAT:
+            return DXGI_FORMAT_R16G16B16A16_FLOAT; // Note: No direct RGB16 format
+        case texture_format::RGBA16_FLOAT:
+            return DXGI_FORMAT_R16G16B16A16_FLOAT;
+
+        // 32-bit float formats
+        case texture_format::R32_FLOAT:
+            return DXGI_FORMAT_R32_FLOAT;
+        case texture_format::RG32_FLOAT:
+            return DXGI_FORMAT_R32G32_FLOAT;
+        case texture_format::RGB32_FLOAT:
+            return DXGI_FORMAT_R32G32B32_FLOAT;
+        case texture_format::RGBA32_FLOAT:
+            return DXGI_FORMAT_R32G32B32A32_FLOAT;
+
+        // Integer formats
+        case texture_format::R16_UINT:
+            return DXGI_FORMAT_R16_UINT;
+        case texture_format::R16_SINT:
+            return DXGI_FORMAT_R16_SINT;
+        case texture_format::RG16_UINT:
+            return DXGI_FORMAT_R16G16_UINT;
+        case texture_format::RG16_SINT:
+            return DXGI_FORMAT_R16G16_SINT;
+        case texture_format::RGBA16_UINT:
+            return DXGI_FORMAT_R16G16B16A16_UINT;
+        case texture_format::RGBA16_SINT:
+            return DXGI_FORMAT_R16G16B16A16_SINT;
+        case texture_format::R32_UINT:
+            return DXGI_FORMAT_R32_UINT;
+        case texture_format::R32_SINT:
+            return DXGI_FORMAT_R32_SINT;
+        case texture_format::RG32_UINT:
+            return DXGI_FORMAT_R32G32_UINT;
+        case texture_format::RG32_SINT:
+            return DXGI_FORMAT_R32G32_SINT;
+        case texture_format::RGBA32_UINT:
+            return DXGI_FORMAT_R32G32B32A32_UINT;
+        case texture_format::RGBA32_SINT:
+            return DXGI_FORMAT_R32G32B32A32_SINT;
+
+        // Depth/stencil formats
+        case texture_format::D16_UNORM:
+            return DXGI_FORMAT_D16_UNORM;
+        case texture_format::D24_UNORM_S8_UINT:
+            return DXGI_FORMAT_D24_UNORM_S8_UINT;
+        case texture_format::D32_FLOAT:
+            return DXGI_FORMAT_D32_FLOAT;
+        case texture_format::D32_FLOAT_S8X24_UINT:
+            return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
+
+        // Compressed formats
+        case texture_format::BC1_UNORM:
+            return DXGI_FORMAT_BC1_UNORM;
+        case texture_format::BC1_UNORM_SRGB:
+            return DXGI_FORMAT_BC1_UNORM_SRGB;
+        case texture_format::BC2_UNORM:
+            return DXGI_FORMAT_BC2_UNORM;
+        case texture_format::BC2_UNORM_SRGB:
+            return DXGI_FORMAT_BC2_UNORM_SRGB;
+        case texture_format::BC3_UNORM:
+            return DXGI_FORMAT_BC3_UNORM;
+        case texture_format::BC3_UNORM_SRGB:
+            return DXGI_FORMAT_BC3_UNORM_SRGB;
+        case texture_format::BC4_UNORM:
+            return DXGI_FORMAT_BC4_UNORM;
+        case texture_format::BC4_SNORM:
+            return DXGI_FORMAT_BC4_SNORM;
+        case texture_format::BC5_UNORM:
+            return DXGI_FORMAT_BC5_UNORM;
+        case texture_format::BC5_SNORM:
+            return DXGI_FORMAT_BC5_SNORM;
+        case texture_format::BC6H_UF16:
+            return DXGI_FORMAT_BC6H_UF16;
+        case texture_format::BC6H_SF16:
+            return DXGI_FORMAT_BC6H_SF16;
+        case texture_format::BC7_UNORM:
+            return DXGI_FORMAT_BC7_UNORM;
+        case texture_format::BC7_UNORM_SRGB:
+            return DXGI_FORMAT_BC7_UNORM_SRGB;
+
+        default:
+            return DXGI_FORMAT_UNKNOWN;
+    }
+}
diff --git a/src/renderer/backend/dx/dx_texture.cpp b/src/renderer/backend/dx/dx_texture.cpp
index ce87bdd..d91552e 100644
--- a/src/renderer/backend/dx/dx_texture.cpp
+++ b/src/renderer/backend/dx/dx_texture.cpp
@@ -2,33 +2,56 @@
 
 #include "dx_renderer.h"
 
-dx_texture::dx_texture(ID3D11Texture2D* texture) { m_texture = texture; }
+dx_texture::dx_texture(const Eigen::Vector2i& size, texture_format format) : renderer_texture(format) {
+    auto d3d_device = aorii::get_renderer<dx_renderer>()->get_d3d_device();
+    D3D11_TEXTURE2D_DESC desc = {};
+    desc.Width = size.x();
+    desc.Height = size.y();
+    desc.MipLevels = 1;
+    desc.ArraySize = 1;
+    desc.Format = get_dxgi_format(format);
+    desc.SampleDesc.Count = 1;
+    desc.SampleDesc.Quality = 0;
+    desc.Usage = D3D11_USAGE_DYNAMIC;
+    desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+    desc.MiscFlags = 0;
+    auto hr = d3d_device->CreateTexture2D(&desc, nullptr, &m_texture);
+    if (FAILED(hr)) {
+        spdlog::critical("无法创建纹理, 错误: {0}", hr);
+        return;
+    }
 
-dx_texture::~dx_texture() {
-    // 如果是Debug模式, 检查m_data是否被释放
-#if defined(DEBUG)
-    assert(!m_data);
-#endif
-    free(m_data);
+    D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = {};
+    srv_desc.Format = desc.Format;
+    srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+    srv_desc.Texture2D.MostDetailedMip = 0;
+    srv_desc.Texture2D.MipLevels = 1;
+    hr = d3d_device->CreateShaderResourceView(m_texture, &srv_desc, &srv);
+    if (FAILED(hr)) {
+        spdlog::critical("无法创建着色器资源视图, 错误: {0}", hr);
+        return;
+    }
 }
 
-void* dx_texture::lock() {
+dx_texture::~dx_texture() {
+    if (srv) srv->Release();
+    if (m_texture) m_texture->Release();
+}
+
+void* dx_texture::lock(unsigned int* out_row_pitch) {
     D3D11_TEXTURE2D_DESC desc;
     m_texture->GetDesc(&desc);
-    m_data = new char[desc.Width * desc.Height * 4];
-    return m_data;
+    D3D11_MAPPED_SUBRESOURCE mappedResource;
+    auto context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
+    context->Map(m_texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
+    if (out_row_pitch) *out_row_pitch = mappedResource.RowPitch;
+    return mappedResource.pData;
 }
 
 void dx_texture::unlock() {
-    // D3D11_TEXTURE2D_DESC desc;
-    // m_texture->GetDesc(&desc);
-    // D3D11_MAPPED_SUBRESOURCE mappedResource;
-    // auto context = dx_backend::get_instance();
-    // context->Map(m_texture.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
-    // memcpy(mappedResource.pData, m_data, desc.Width * desc.Height * 4);
-    // context->Unmap(m_texture.get(), 0);
-    // delete[] m_data;
-    // m_data = nullptr;
+    auto context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
+    context->Unmap(m_texture, 0);
 }
 
 bool dx_texture::resize(const Eigen::Vector2i& size) { return true; }
diff --git a/src/renderer/backend/dx/dx_texture.h b/src/renderer/backend/dx/dx_texture.h
index 7d4ad9e..d7bc306 100644
--- a/src/renderer/backend/dx/dx_texture.h
+++ b/src/renderer/backend/dx/dx_texture.h
@@ -7,14 +7,18 @@
 // DX11纹理
 class dx_texture : public renderer_texture {
 public:
-    explicit dx_texture(ID3D11Texture2D* texture);
+    dx_texture(const Eigen::Vector2i& size, texture_format format);
     ~dx_texture() override;
-    void* lock() override;
+    void* lock(unsigned int* out_row_pitch) override;
     void unlock() override;
 
     bool resize(const Eigen::Vector2i& size) override;
     Eigen::Vector2i size() override;
+
+    [[nodiscard]] ID3D11ShaderResourceView* get_srv() const { return srv; }
+    void* get_native_handle() override { return m_texture; }
+    void* get_shader_resource_view() override { return srv; }
 private:
-    void* m_data = nullptr;
-    ID3D11Texture2D* m_texture;
+    ID3D11Texture2D* m_texture{};
+    ID3D11ShaderResourceView* srv{};
 };
diff --git a/src/renderer/backend/dx/dx_window.cpp b/src/renderer/backend/dx/dx_window.cpp
index b751904..2570231 100644
--- a/src/renderer/backend/dx/dx_window.cpp
+++ b/src/renderer/backend/dx/dx_window.cpp
@@ -3,6 +3,7 @@
 #include <spdlog/spdlog.h>
 
 #include "dx_renderer.h"
+#include "core/renderer/renderer_texture.h"
 #include "misc/scope_exit.h"
 
 using namespace aorii;
@@ -67,10 +68,10 @@ bool dx_window::create_surface(GLFWwindow* in_window) {
         return false;
     }
 
+    test_texture = dx->load_image(R"(D:\69054578_p0.jpg)", texture_format::RGBA8_UNORM);
     return true;
 }
 
-
 void dx_window::begin_frame() {
     glfwMakeContextCurrent(get_glfw_window());
     const auto render = aorii::get_renderer<dx_renderer>();
@@ -79,54 +80,17 @@ void dx_window::begin_frame() {
     d3d_context->OMSetRenderTargets(1, &render_target_view, nullptr);
     d3d_context->OMSetBlendState(render->get_blend_state(), nullptr, 0xffffffff);
     d3d_context->ClearRenderTargetView(render_target_view, clear_color);
-
-    if constexpr (false) {
-        static std::chrono::duration<double> timer = {};
-        static linear_color random_color = { 0, 0, 0, 1 };
-        static bool is_white = false;
-        static float pos_x = 0;
-        static float pos_y = 0;
-        static bool draw_test = true;
-
-        if (draw_test) {
-            // 生成渐变色
-            if (is_white) {
-                if (random_color.r > 0) {
-                    random_color.r -= 0.01f;
-                } else if (random_color.b > 0) {
-                    random_color.b -= 0.01f;
-                } else if (random_color.g > 0) {
-                    random_color.g -= 0.01f;
-                } else {
-                    is_white = false;
-                    random_color = {0.0f, 0.0f, 0.0f, 1.0f};
-                }
-            }
-            else {
-                if (random_color.r < 1) {
-                    random_color.r += 0.01f;
-                } else if (random_color.b < 1) {
-                    random_color.b += 0.01f;
-                } else if (random_color.g < 1) {
-                    random_color.g += 0.01f;
-                } else {
-                    is_white = true;
-                    random_color = {1.0f, 1.0f, 1.0f, 1.0f};
-                }
-            }
-
-            context.draw_rectangle({ pos_x, pos_y }, { 1, 100 }, random_color);
-            if (pos_x >= 1000) {
-                pos_x = 0;
-                draw_test = false;
-            }
-            pos_x += 1;
-        }
-    }
+    context.set_projection_matrix(projection_matrix);
 
     context.draw_rectangle({ 400, 0 }, { 100, 100 }, { 0, 1, 0, 1 });
     auto radius = abs(sin(get_total_time().count())) * 50;
     context.draw_rounded_rectangle({ 100, 100 }, { 200, 200 }, { 1, 0, 0, 1 }, radius);
+
+    auto height = sin(get_total_time().count()) * 100;
+    context.draw_line({ 600, 600 - height }, { 700, 600 + height }, { 0, 0, 1, 1 }, 0.1);
+
+    context.draw_texture({0, 0}, test_texture->size(), test_texture);
+
     context.flush();
 
     swap_chain->Present(1, 0);
@@ -183,9 +147,7 @@ HRESULT dx_window::build_render_target_view() {
     viewport.TopLeftY = 0.0f;
     d3d_context->RSSetViewports(1, &viewport);
 
-    const auto& projection_matrix = get_projection_matrix().transpose();
-
-    context.set_projection_matrix(projection_matrix);
+    projection_matrix = get_projection_matrix().transpose();
 
     return hr;
 }
diff --git a/src/renderer/backend/dx/dx_window.h b/src/renderer/backend/dx/dx_window.h
index e0ac60c..54fd904 100644
--- a/src/renderer/backend/dx/dx_window.h
+++ b/src/renderer/backend/dx/dx_window.h
@@ -18,4 +18,7 @@ private:
     renderer_context context;
     IDXGISwapChain1* swap_chain = nullptr;
     ID3D11RenderTargetView* render_target_view = nullptr;
+    Eigen::Matrix4f projection_matrix;
+
+    renderer_texture* test_texture = nullptr;
 };
diff --git a/src/renderer/backend/dx/pipeline/dx_pipeline_loader.cpp b/src/renderer/backend/dx/pipeline/dx_pipeline_loader.cpp
new file mode 100644
index 0000000..3174481
--- /dev/null
+++ b/src/renderer/backend/dx/pipeline/dx_pipeline_loader.cpp
@@ -0,0 +1,41 @@
+#include "dx_pipeline_loader.h"
+
+#include "backend/dx/dx_renderer.h"
+
+bool dx_pipeline_loader::load(const char* vertex_shader_name, const char* pixel_shader_name) {
+    auto renderer = aorii::get_renderer<dx_renderer>();
+    const auto d3d_device = renderer->get_d3d_device();
+
+    const auto& vertex_shader_code = renderer->load_shader(vertex_shader_name);
+    const auto& pixel_shader_code = renderer->load_shader(pixel_shader_name);
+
+    auto hr = d3d_device->CreateVertexShader(vertex_shader_code.data(), vertex_shader_code.size(), nullptr, &vertex_shader);
+    if (FAILED(hr)) {
+        spdlog::critical("rect_pipeline 无法创建顶点着色器: {0}", hr);
+        return false;
+    }
+    hr = d3d_device->CreatePixelShader(pixel_shader_code.data(), pixel_shader_code.size(), nullptr, &pixel_shader);
+    if (FAILED(hr)) {
+        spdlog::critical("rect_pipeline 无法创建像素着色器: {0}", hr);
+        return false;
+    }
+    D3D11_INPUT_ELEMENT_DESC layout_desc[] = {
+        { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+        {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0},
+        { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+    };
+    constexpr int32_t element_count = sizeof(layout_desc) / sizeof(D3D11_INPUT_ELEMENT_DESC);
+    hr = d3d_device->CreateInputLayout(layout_desc, element_count, vertex_shader_code.data(), vertex_shader_code.size(), &input_layout);
+    if (FAILED(hr)) {
+        spdlog::critical("rect_pipeline 无法创建输入布局: {0}", hr);
+        return false;
+    }
+    return true;
+}
+
+void dx_pipeline_loader::use() const {
+    const auto d3d_context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
+    d3d_context->VSSetShader(vertex_shader, nullptr, 0);
+    d3d_context->PSSetShader(pixel_shader, nullptr, 0);
+    d3d_context->IASetInputLayout(input_layout);
+}
diff --git a/src/renderer/backend/dx/pipeline/dx_pipeline_loader.h b/src/renderer/backend/dx/pipeline/dx_pipeline_loader.h
new file mode 100644
index 0000000..d3de9f1
--- /dev/null
+++ b/src/renderer/backend/dx/pipeline/dx_pipeline_loader.h
@@ -0,0 +1,12 @@
+#pragma once
+#include <d3d11.h>
+
+class dx_pipeline_loader {
+public:
+    bool load(const char* vertex_shader_name, const char* pixel_shader_name);
+    void use() const;
+
+    ID3D11VertexShader* vertex_shader = nullptr;
+    ID3D11PixelShader* pixel_shader = nullptr;
+    ID3D11InputLayout* input_layout = nullptr;
+};
diff --git a/src/renderer/backend/dx/pipeline/dx_rect_pipeline.cpp b/src/renderer/backend/dx/pipeline/dx_rect_pipeline.cpp
index 6dc5c4f..840460a 100644
--- a/src/renderer/backend/dx/pipeline/dx_rect_pipeline.cpp
+++ b/src/renderer/backend/dx/pipeline/dx_rect_pipeline.cpp
@@ -4,41 +4,12 @@
 
 bool dx_rect_pipeline::init() {
     rect_pipeline::init();
-    auto renderer = aorii::get_renderer<dx_renderer>();
-    const auto d3d_device = renderer->get_d3d_device();
-
-    const auto& vertex_shader_code = renderer->load_shader("aorii_rect_vertex_main");
-    const auto& pixel_shader_code = renderer->load_shader("aorii_rect_pixel_main");
-
-    auto hr = d3d_device->CreateVertexShader(vertex_shader_code.data(), vertex_shader_code.size(), nullptr, &vertex_shader);
-    if (FAILED(hr)) {
-        spdlog::critical("rect_pipeline 无法创建顶点着色器: {0}", hr);
-        return false;
-    }
-    hr = d3d_device->CreatePixelShader(pixel_shader_code.data(), pixel_shader_code.size(), nullptr, &pixel_shader);
-    if (FAILED(hr)) {
-        spdlog::critical("rect_pipeline 无法创建像素着色器: {0}", hr);
-        return false;
-    }
-    D3D11_INPUT_ELEMENT_DESC layout_desc[] = {
-        { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
-        {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0},
-        { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0 },
-    };
-    constexpr int32_t element_count = sizeof(layout_desc) / sizeof(D3D11_INPUT_ELEMENT_DESC);
-    hr = d3d_device->CreateInputLayout(layout_desc, element_count, vertex_shader_code.data(), vertex_shader_code.size(), &input_layout);
-    if (FAILED(hr)) {
-        spdlog::critical("rect_pipeline 无法创建输入布局: {0}", hr);
-        return false;
-    }
+    loader.load("aorii_rect_vertex_main", "aorii_rect_pixel_main");
     return true;
 }
 
 void dx_rect_pipeline::use() {
-    const auto d3d_context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
-    d3d_context->VSSetShader(vertex_shader, nullptr, 0);
-    d3d_context->PSSetShader(pixel_shader, nullptr, 0);
-    d3d_context->IASetInputLayout(input_layout);
+    loader.use();
 }
 
 void dx_rect_pipeline::draw() {
diff --git a/src/renderer/backend/dx/pipeline/dx_rect_pipeline.h b/src/renderer/backend/dx/pipeline/dx_rect_pipeline.h
index 3e7d12c..75ce40f 100644
--- a/src/renderer/backend/dx/pipeline/dx_rect_pipeline.h
+++ b/src/renderer/backend/dx/pipeline/dx_rect_pipeline.h
@@ -1,6 +1,7 @@
 #pragma once
 #include <d3d11.h>
 
+#include "dx_pipeline_loader.h"
 #include "core/pipeline/rect_pipeline.h"
 
 class dx_rect_pipeline : public rect_pipeline {
@@ -9,7 +10,5 @@ public:
     void use() override;
     void draw() override;
 private:
-    ID3D11VertexShader* vertex_shader = nullptr;
-    ID3D11PixelShader* pixel_shader = nullptr;
-    ID3D11InputLayout* input_layout = nullptr;
+    dx_pipeline_loader loader;
 };
diff --git a/src/renderer/backend/dx/pipeline/dx_rounded_rect_pipeline.cpp b/src/renderer/backend/dx/pipeline/dx_rounded_rect_pipeline.cpp
index 02018be..b54d147 100644
--- a/src/renderer/backend/dx/pipeline/dx_rounded_rect_pipeline.cpp
+++ b/src/renderer/backend/dx/pipeline/dx_rounded_rect_pipeline.cpp
@@ -4,41 +4,11 @@
 
 bool dx_rounded_rect_pipeline::init() {
     rounded_rect_pipeline::init();
-    auto renderer = aorii::get_renderer<dx_renderer>();
-    const auto d3d_device = renderer->get_d3d_device();
-
-    const auto& vertex_shader_code = renderer->load_shader("aorii_rounded_rect_vertex_main");
-    const auto& pixel_shader_code = renderer->load_shader("aorii_rounded_rect_pixel_main");
-
-    auto hr = d3d_device->CreateVertexShader(vertex_shader_code.data(), vertex_shader_code.size(), nullptr, &vertex_shader);
-    if (FAILED(hr)) {
-        spdlog::critical("rounded_rect_pipeline 无法创建顶点着色器: {0}", hr);
-        return false;
-    }
-    hr = d3d_device->CreatePixelShader(pixel_shader_code.data(), pixel_shader_code.size(), nullptr, &pixel_shader);
-    if (FAILED(hr)) {
-        spdlog::critical("rounded_rect_pipeline 无法创建像素着色器: {0}", hr);
-        return false;
-    }
-    D3D11_INPUT_ELEMENT_DESC layout_desc[] = {
-        { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
-        {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0},
-        { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0 },
-    };
-    constexpr int32_t element_count = sizeof(layout_desc) / sizeof(D3D11_INPUT_ELEMENT_DESC);
-    hr = d3d_device->CreateInputLayout(layout_desc, element_count, vertex_shader_code.data(), vertex_shader_code.size(), &input_layout);
-    if (FAILED(hr)) {
-        spdlog::critical("rect_pipeline 无法创建输入布局: {0}", hr);
-        return false;
-    }
-    return true;
+    return loader.load("aorii_rounded_rect_vertex_main", "aorii_rounded_rect_pixel_main");
 }
 
 void dx_rounded_rect_pipeline::use() {
-    const auto d3d_context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
-    d3d_context->VSSetShader(vertex_shader, nullptr, 0);
-    d3d_context->PSSetShader(pixel_shader, nullptr, 0);
-    d3d_context->IASetInputLayout(input_layout);
+    loader.use();
 }
 
 void dx_rounded_rect_pipeline::draw() {
diff --git a/src/renderer/backend/dx/pipeline/dx_rounded_rect_pipeline.h b/src/renderer/backend/dx/pipeline/dx_rounded_rect_pipeline.h
index 1d2d79a..593771a 100644
--- a/src/renderer/backend/dx/pipeline/dx_rounded_rect_pipeline.h
+++ b/src/renderer/backend/dx/pipeline/dx_rounded_rect_pipeline.h
@@ -1,6 +1,7 @@
 #pragma once
 #include <d3d11.h>
 
+#include "dx_pipeline_loader.h"
 #include "core/pipeline/rounded_rect_pipeline.h"
 
 class dx_rounded_rect_pipeline : public rounded_rect_pipeline {
@@ -9,7 +10,5 @@ public:
     void use() override;
     void draw() override;
 private:
-    ID3D11VertexShader* vertex_shader = nullptr;
-    ID3D11PixelShader* pixel_shader = nullptr;
-    ID3D11InputLayout* input_layout = nullptr;
+    dx_pipeline_loader loader;
 };
diff --git a/src/renderer/backend/dx/pipeline/dx_texture_pipeline.cpp b/src/renderer/backend/dx/pipeline/dx_texture_pipeline.cpp
new file mode 100644
index 0000000..7d0831a
--- /dev/null
+++ b/src/renderer/backend/dx/pipeline/dx_texture_pipeline.cpp
@@ -0,0 +1,53 @@
+#include "dx_texture_pipeline.h"
+
+#include "backend/dx/dx_renderer.h"
+#include "backend/dx/dx_texture.h"
+
+bool dx_texture_pipeline::init() {
+    texture_pipeline::init();
+    const auto d3d_device = aorii::get_renderer<dx_renderer>()->get_d3d_device();
+
+    D3D11_SAMPLER_DESC sampler_desc = {};
+    sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+    sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
+    sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
+    sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
+    sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
+    sampler_desc.MinLOD = 0;
+    sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;
+    d3d_device->CreateSamplerState(&sampler_desc, &sampler_state);
+
+    return loader.load("aorii_texture_vertex_main", "aorii_texture_pixel_main");
+}
+
+void dx_texture_pipeline::use() {
+    loader.use();
+}
+
+void dx_texture_pipeline::draw() {
+    if (!texture) return;
+    const auto d3d_context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
+
+    // 设置顶点缓冲区
+    constexpr UINT stride = sizeof(aorii_vertex);
+    constexpr UINT offset = 0;
+    auto* v_buffer = static_cast<ID3D11Buffer*>(vertex_buffer->get_native_handle());
+    auto* i_buffer = static_cast<ID3D11Buffer*>(index_buffer->get_native_handle());
+    auto* c_buffer = static_cast<ID3D11Buffer*>(param_buffer->get_native_handle());
+    auto* srv = (ID3D11ShaderResourceView*)texture->get_shader_resource_view();
+    d3d_context->IASetVertexBuffers(0, 1, &v_buffer, &stride, &offset);
+    d3d_context->IASetIndexBuffer(i_buffer, DXGI_FORMAT_R32_UINT, 0);
+    d3d_context->PSSetSamplers(0, 1, &sampler_state);
+    d3d_context->VSSetConstantBuffers(0, 1, &c_buffer);
+    d3d_context->PSSetShaderResources(0, 1, &srv);
+
+    // 设置图元拓扑
+    d3d_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+
+    // 绘制矩形
+    d3d_context->Draw(vertex_buffer->get_size(), 0);
+}
+
+void dx_texture_pipeline::set_texture(renderer_texture* in_texture) {
+    texture = static_cast<dx_texture*>(in_texture);
+}
diff --git a/src/renderer/backend/dx/pipeline/dx_texture_pipeline.h b/src/renderer/backend/dx/pipeline/dx_texture_pipeline.h
new file mode 100644
index 0000000..ed4c3ad
--- /dev/null
+++ b/src/renderer/backend/dx/pipeline/dx_texture_pipeline.h
@@ -0,0 +1,19 @@
+#pragma once
+#include <d3d11.h>
+
+#include "dx_pipeline_loader.h"
+#include "core/pipeline/texture_pipeline.h"
+
+class dx_texture_pipeline : public texture_pipeline {
+public:
+    bool init() override;
+    void use() override;
+    void draw() override;
+
+    void set_texture(renderer_texture* in_texture) override;
+private:
+    dx_pipeline_loader loader;
+
+    ID3D11SamplerState* sampler_state = nullptr;
+    renderer_texture* texture = nullptr;
+};
diff --git a/src/renderer/core/pipeline/texture_pipeline.cpp b/src/renderer/core/pipeline/texture_pipeline.cpp
new file mode 100644
index 0000000..dda5ba6
--- /dev/null
+++ b/src/renderer/core/pipeline/texture_pipeline.cpp
@@ -0,0 +1,16 @@
+#include "texture_pipeline.h"
+
+#include "core/renderer/renderer.h"
+
+bool texture_pipeline::init() {
+    param_buffer = aorii::get_renderer_raw()->create_buffer(buffer_type::constant, sizeof(param), 1);
+    return pipeline::init();
+}
+void texture_pipeline::destroy() {
+    pipeline::destroy();
+    aorii::get_renderer_raw()->destroy_buffer(param_buffer);
+}
+
+void texture_pipeline::set_param(const param& in_param) {
+    param_buffer->set_data(in_param);
+}
diff --git a/src/renderer/core/pipeline/texture_pipeline.h b/src/renderer/core/pipeline/texture_pipeline.h
new file mode 100644
index 0000000..afe57b2
--- /dev/null
+++ b/src/renderer/core/pipeline/texture_pipeline.h
@@ -0,0 +1,18 @@
+#pragma once
+#include "pipeline.h"
+
+class renderer_texture;
+
+class texture_pipeline : public pipeline {
+public:
+    struct param {
+        Eigen::Matrix4f projection_matrix;
+    };
+    bool init() override;
+    void destroy() override;
+
+    void set_param(const param& in_param);
+    virtual void set_texture(renderer_texture* in_texture) = 0;
+protected:
+    renderer_buffer* param_buffer = nullptr;
+};
diff --git a/src/renderer/core/renderer/renderer.cpp b/src/renderer/core/renderer/renderer.cpp
index 93ce95d..bf65593 100644
--- a/src/renderer/core/renderer/renderer.cpp
+++ b/src/renderer/core/renderer/renderer.cpp
@@ -2,7 +2,10 @@
 
 #include <spdlog/spdlog.h>
 
+#include "renderer_texture.h"
 #include "core/window/renderer_window.h"
+#define STB_IMAGE_IMPLEMENTATION
+#include "stb_image.h"
 
 #if DX_BACKEND
 #include "backend/dx/dx_renderer.h"
@@ -17,6 +20,61 @@ renderer* s_renderer = nullptr;
 void renderer::tick() {
 }
 
+void renderer::destroy_texture(renderer_texture* texture) { delete texture; }
+
+renderer_texture* renderer::load_image(const std::string& file_path, texture_format in_format) {
+    int width, height, channels;
+    auto result = stbi_load(file_path.c_str(), &width, &height, &channels, 0);
+    if (!result) {
+        spdlog::critical("Failed to load image: {0}", file_path);
+        return nullptr;
+    }
+
+    int target_channel_count = get_format_channel_count(in_format);
+    unsigned int row_pitch = 0;
+
+    auto texture = create_texture({ width, height }, in_format);
+    auto data = texture->lock(&row_pitch);
+    for (int y = 0; y < height; ++y) {
+        for (int x = 0; x < width; ++x) {
+            unsigned char* src = result + (y * width + x) * channels;
+            unsigned char* dst = static_cast<unsigned char*>(data) + y * row_pitch + x * target_channel_count;
+            switch (channels) {
+            case 1:
+                dst[0] = src[0];
+                dst[1] = 0;
+                dst[2] = 0;
+                dst[3] = 255;
+                break;
+            case 2:
+                dst[0] = src[0];
+                dst[1] = src[1];
+                dst[2] = 0;
+                dst[3] = 255;
+                break;
+            case 3:
+                dst[0] = src[0];
+                dst[1] = src[1];
+                dst[2] = src[2];
+                dst[3] = 255;
+                break;
+            case 4:
+                dst[0] = src[0];
+                dst[1] = src[1];
+                dst[2] = src[2];
+                dst[3] = src[3];
+                break;
+            default:
+                break;
+            }
+        }
+    }
+    texture->unlock();
+    stbi_image_free(result);
+
+    return texture;
+}
+
 void renderer::destroy_window(renderer_window* window) {
     delete window;
 }
diff --git a/src/renderer/core/renderer/renderer.h b/src/renderer/core/renderer/renderer.h
index 58517f7..d67e6da 100644
--- a/src/renderer/core/renderer/renderer.h
+++ b/src/renderer/core/renderer/renderer.h
@@ -6,6 +6,7 @@
 #include "renderer_buffer.h"
 #include "core/pipeline/pipeline.h"
 
+class texture_pipeline;
 class rounded_rect_pipeline;
 class rect_pipeline;
 class renderer_window;
@@ -98,6 +99,114 @@ enum class texture_format {
     BC7_UNORM_SRGB,
 };
 
+inline int get_format_channel_count(texture_format format) {
+    switch (format) {
+    case texture_format::R8_UNORM:
+    case texture_format::R8_SNORM:
+    case texture_format::R8_UINT:
+    case texture_format::R8_SINT:
+    case texture_format::R16_FLOAT:
+    case texture_format::R16_UINT:
+    case texture_format::R16_SINT:
+    case texture_format::R32_FLOAT:
+    case texture_format::R32_UINT:
+    case texture_format::R32_SINT:
+        return 1;
+    case texture_format::RG8_UNORM:
+    case texture_format::RG8_SNORM:
+    case texture_format::RG8_UINT:
+    case texture_format::RG8_SINT:
+    case texture_format::RG16_FLOAT:
+    case texture_format::RG16_UINT:
+    case texture_format::RG16_SINT:
+    case texture_format::RG32_FLOAT:
+    case texture_format::RG32_UINT:
+    case texture_format::RG32_SINT:
+        return 2;
+    case texture_format::RGB8_UNORM:
+    case texture_format::RGB8_SNORM:
+    case texture_format::RGB8_UINT:
+    case texture_format::RGB8_SINT:
+    case texture_format::RGB16_FLOAT:
+    case texture_format::RGB32_FLOAT:
+        return 3;
+    case texture_format::RGBA8_UNORM:
+    case texture_format::RGBA8_SNORM:
+    case texture_format::RGBA8_UINT:
+    case texture_format::RGBA8_SINT:
+    case texture_format::RGBA16_FLOAT:
+    case texture_format::RGBA32_FLOAT:
+    case texture_format::RGBA32_UINT:
+    case texture_format::RGBA32_SINT:
+        return 4;
+    default:
+        return 0;
+    }
+}
+
+inline int get_format_bits_pre_channel(texture_format format) {
+    switch (format) {
+        case texture_format::R8_UNORM:
+        case texture_format::R8_SNORM:
+        case texture_format::R8_UINT:
+        case texture_format::R8_SINT:
+        case texture_format::RG8_UNORM:
+        case texture_format::RG8_SNORM:
+        case texture_format::RG8_UINT:
+        case texture_format::RG8_SINT:
+        case texture_format::RGB8_UNORM:
+        case texture_format::RGB8_SNORM:
+        case texture_format::RGB8_UINT:
+        case texture_format::RGB8_SINT:
+        case texture_format::RGBA8_UNORM:
+        case texture_format::RGBA8_SNORM:
+        case texture_format::RGBA8_UINT:
+        case texture_format::RGBA8_SINT:
+        case texture_format::SRGB8:
+        case texture_format::SRGB8_ALPHA8:
+        case texture_format::BC1_UNORM:
+        case texture_format::BC1_UNORM_SRGB:
+        case texture_format::BC2_UNORM:
+        case texture_format::BC2_UNORM_SRGB:
+        case texture_format::BC3_UNORM:
+        case texture_format::BC3_UNORM_SRGB:
+        case texture_format::BC4_UNORM:
+        case texture_format::BC4_SNORM:
+        case texture_format::BC5_UNORM:
+        case texture_format::BC5_SNORM:
+        case texture_format::BC6H_UF16:
+        case texture_format::BC6H_SF16:
+        case texture_format::BC7_UNORM:
+        case texture_format::BC7_UNORM_SRGB:
+            return 8;
+        case texture_format::R16_FLOAT:
+        case texture_format::R16_UINT:
+        case texture_format::R16_SINT:
+        case texture_format::RG16_FLOAT:
+        case texture_format::RG16_UINT:
+        case texture_format::RG16_SINT:
+        case texture_format::RGB16_FLOAT:
+        case texture_format::RGBA16_FLOAT:
+        case texture_format::D16_UNORM:
+            return 16;
+        case texture_format::R32_FLOAT:
+        case texture_format::R32_UINT:
+        case texture_format::R32_SINT:
+        case texture_format::RG32_FLOAT:
+        case texture_format::RG32_UINT:
+        case texture_format::RG32_SINT:
+        case texture_format::RGB32_FLOAT:
+        case texture_format::RGBA32_FLOAT:
+            return 32;
+        case texture_format::D24_UNORM_S8_UINT:
+        case texture_format::D32_FLOAT:
+        case texture_format::D32_FLOAT_S8X24_UINT:
+            return 24;
+        default:
+            return 0;
+    }
+}
+
 class renderer {
     friend class window_manager;
 public:
@@ -109,7 +218,9 @@ public:
     virtual bool render() = 0;
 
     virtual renderer_texture* create_texture(const Eigen::Vector2i& size, texture_format in_format) = 0;
-    virtual void destroy_texture(renderer_texture* texture) = 0;
+    virtual void destroy_texture(renderer_texture* texture);
+
+    renderer_texture* load_image(const std::string& file_path, texture_format in_format);
 
     /**
      * 读取着色器代码
@@ -129,6 +240,7 @@ public:
 
     virtual rect_pipeline* get_rect_pipeline() = 0;
     virtual rounded_rect_pipeline* get_rounded_rect_pipeline() = 0;
+    virtual texture_pipeline* get_texture_pipeline() = 0;
 private:
     virtual renderer_window* create_window() = 0;
     virtual void destroy_window(renderer_window* window);
diff --git a/src/renderer/core/renderer/renderer_context.cpp b/src/renderer/core/renderer/renderer_context.cpp
index bbcb4e5..0b9b002 100644
--- a/src/renderer/core/renderer/renderer_context.cpp
+++ b/src/renderer/core/renderer/renderer_context.cpp
@@ -1,62 +1,25 @@
 #include "renderer_context.h"
 
 #include "renderer.h"
+#include "backend/dx/dx_renderer.h"
 
 void renderer_context::init() {
-    rect_p = aorii::get_renderer_raw()->get_rect_pipeline();
-    rounded_rect_p = aorii::get_renderer_raw()->get_rounded_rect_pipeline();
 }
 
 void renderer_context::draw_rectangle(const Eigen::Vector2i& in_pos, const Eigen::Vector2i& in_size, const linear_color& in_color) {
     to_rect_pipeline();
-    const aorii_vertex v1{ { in_pos.x(), in_pos.y() }, { 0, 0 }, in_color }; // 左上角
-    const aorii_vertex v2{ { in_pos.x() + in_size.x(), in_pos.y() }, { 1, 0 }, in_color }; // 右上角
-    const aorii_vertex v3{ { in_pos.x(), in_pos.y() + in_size.y() }, { 0, 1 }, in_color }; // 左下角
-    const aorii_vertex v4{ { in_pos.x() + in_size.x(), in_pos.y() + in_size.y() }, { 1, 1 }, in_color }; // 右下角
-
-    const uint32_t index1 = vertices.size();
-    vertices.push_back(v1);
-    const uint32_t index2 = vertices.size();
-    vertices.push_back(v2);
-    const uint32_t index3 = vertices.size();
-    vertices.push_back(v3);
-    const uint32_t index4 = vertices.size();
-    vertices.push_back(v4);
-
-    const aorii_triangle t1 = { index1, index2, index3 };
-    const aorii_triangle t2 = { index2, index3, index4 };
-    triangles.push_back(t1);
-    triangles.push_back(t2);
+    make_rect(in_pos, in_size, in_color);
 }
 
 void renderer_context::draw_rounded_rectangle(const Eigen::Vector2i& in_pos, const Eigen::Vector2i& in_size, const linear_color& in_color, float in_radius) {
     to_rounded_rect_pipeline(in_pos, in_size, in_radius);
-
-    const aorii_vertex v1{ { in_pos.x(), in_pos.y() }, { 0, 0 }, in_color }; // 左上角
-    const aorii_vertex v2{ { in_pos.x() + in_size.x(), in_pos.y() }, { 1, 0 }, in_color }; // 右上角
-    const aorii_vertex v3{ { in_pos.x(), in_pos.y() + in_size.y() }, { 0, 1 }, in_color }; // 左下角
-    const aorii_vertex v4{ { in_pos.x() + in_size.x(), in_pos.y() + in_size.y() }, { 1, 1 }, in_color }; // 右下角
-
-    const uint32_t index1 = vertices.size();
-    vertices.push_back(v1);
-    const uint32_t index2 = vertices.size();
-    vertices.push_back(v2);
-    const uint32_t index3 = vertices.size();
-    vertices.push_back(v3);
-    const uint32_t index4 = vertices.size();
-    vertices.push_back(v4);
-
-    const aorii_triangle t1 = { index1, index2, index3 };
-    const aorii_triangle t2 = { index2, index3, index4 };
-    triangles.push_back(t1);
-    triangles.push_back(t2);
-
+    make_rect(in_pos, in_size, in_color);
     // 立刻绘制并重置管线, 因为下一个绘制命令的参数可能不同, 所以无法合批
     flush();
 }
 
 void renderer_context::draw_line(const Eigen::Vector2i& in_pos_p1, const Eigen::Vector2i& in_pos_p2, const linear_color& in_color, float in_thickness) {
-    switch_pipeline(rect_p);
+    to_rect_pipeline();
     const Eigen::Vector2f direction = (in_pos_p2 - in_pos_p1).cast<float>().normalized();
     const Eigen::Vector2f normal = { -direction.y(), direction.x() };
     const Eigen::Vector2f offset = normal * in_thickness;
@@ -80,3 +43,68 @@ void renderer_context::draw_line(const Eigen::Vector2i& in_pos_p1, const Eigen::
     triangles.push_back(t1);
     triangles.push_back(t2);
 }
+
+void renderer_context::draw_texture(const Eigen::Vector2i& in_pos, const Eigen::Vector2i& in_size,
+    renderer_texture* in_texture, const linear_color& in_color) {
+    to_texture_pipeline(in_texture);
+    make_rect(in_pos, in_size, in_color);
+    // 立刻绘制并重置管线, 因为下一个绘制命令的参数可能不同, 所以无法合批
+    flush();
+}
+
+void renderer_context::set_projection_matrix(const Eigen::Matrix4f& in_matrix) {
+    projection_matrix = in_matrix;
+
+    // 更新默认管线的投影矩阵
+    const auto rect_p = aorii::get_renderer<dx_renderer>()->get_rect_pipeline();
+    rect_p->set_param({ in_matrix });
+    // 更新纹理管线的投影矩阵
+    const auto texture_p = aorii::get_renderer<dx_renderer>()->get_texture_pipeline();
+    texture_p->set_param({ in_matrix });
+
+    // 圆角矩形管线的投影矩阵不需要更新, 因为它在每次绘制时都会更新
+}
+
+void renderer_context::to_rect_pipeline() {
+    const auto rect_p = aorii::get_renderer<dx_renderer>()->get_rect_pipeline();
+    switch_pipeline(rect_p);
+}
+
+void renderer_context::to_rounded_rect_pipeline(const Eigen::Vector2i& in_pos, const Eigen::Vector2i& in_size, float in_radius) {
+    const auto rounded_rect_p = aorii::get_renderer<dx_renderer>()->get_rounded_rect_pipeline();
+    switch_pipeline(rounded_rect_p);
+    rounded_rect_pipeline::param param;
+    param.projection_matrix = projection_matrix;
+    param.pos = in_pos.cast<float>();
+    param.size = in_size.cast<float>();
+    param.radius = in_radius;
+    rounded_rect_p->set_param(param);
+}
+
+void renderer_context::to_texture_pipeline(renderer_texture* in_texture) {
+    const auto texture_p = aorii::get_renderer<dx_renderer>()->get_texture_pipeline();
+    switch_pipeline(texture_p);
+    texture_p->set_texture(in_texture);
+}
+
+void renderer_context::make_rect(const Eigen::Vector2i& in_pos, const Eigen::Vector2i& in_size,
+    const linear_color& in_color) {
+    const aorii_vertex v1{ { in_pos.x(), in_pos.y() }, { 0, 0 }, in_color }; // 左上角
+    const aorii_vertex v2{ { in_pos.x() + in_size.x(), in_pos.y() }, { 1, 0 }, in_color }; // 右上角
+    const aorii_vertex v3{ { in_pos.x(), in_pos.y() + in_size.y() }, { 0, 1 }, in_color }; // 左下角
+    const aorii_vertex v4{ { in_pos.x() + in_size.x(), in_pos.y() + in_size.y() }, { 1, 1 }, in_color }; // 右下角
+
+    const uint32_t index1 = vertices.size();
+    vertices.push_back(v1);
+    const uint32_t index2 = vertices.size();
+    vertices.push_back(v2);
+    const uint32_t index3 = vertices.size();
+    vertices.push_back(v3);
+    const uint32_t index4 = vertices.size();
+    vertices.push_back(v4);
+
+    const aorii_triangle t1 = { index1, index2, index3 };
+    const aorii_triangle t2 = { index2, index3, index4 };
+    triangles.push_back(t1);
+    triangles.push_back(t2);
+}
diff --git a/src/renderer/core/renderer/renderer_context.h b/src/renderer/core/renderer/renderer_context.h
index 7bb3763..af88cb3 100644
--- a/src/renderer/core/renderer/renderer_context.h
+++ b/src/renderer/core/renderer/renderer_context.h
@@ -5,6 +5,8 @@
 #include "core/pipeline/rect_pipeline.h"
 #include "core/pipeline/rounded_rect_pipeline.h"
 
+class renderer_texture;
+
 class renderer_context {
 public:
     void init();
@@ -34,6 +36,14 @@ public:
      */
     void draw_line(const Eigen::Vector2i& in_pos_p1, const Eigen::Vector2i& in_pos_p2, const linear_color& in_color, float in_thickness);
 
+    /**
+     * 绘制一个纹理
+     * @param in_pos 当前控件坐标系的位置
+     * @param in_size 绘制的大小(受控件缩放影响)
+     * @param in_texture 纹理
+     */
+    void draw_texture(const Eigen::Vector2i& in_pos, const Eigen::Vector2i& in_size, renderer_texture* in_texture, const linear_color& in_color = linear_color::white);
+
     void clear() {
         vertices.clear();
         triangles.clear();
@@ -53,14 +63,8 @@ public:
         }
         clear();
     }
-    void set_projection_matrix(const Eigen::Matrix4f& in_matrix) {
-        projection_matrix = in_matrix;
+    void set_projection_matrix(const Eigen::Matrix4f& in_matrix);
 
-        // 更新默认管线的投影矩阵
-        rect_p->set_param({ in_matrix });
-
-        // 圆角矩形管线的投影矩阵不需要更新, 因为它在每次绘制时都会更新
-    }
 protected:
     void switch_pipeline(pipeline* in_pipeline) {
 #if DEBUG
@@ -74,25 +78,15 @@ protected:
         }
     }
 
-    void to_rect_pipeline() {
-        switch_pipeline(rect_p);
-    }
-    void to_rounded_rect_pipeline(const Eigen::Vector2i& in_pos, const Eigen::Vector2i& in_size, float in_radius) {
-        switch_pipeline(rounded_rect_p);
-        rounded_rect_pipeline::param param;
-        param.projection_matrix = projection_matrix;
-        param.pos = in_pos.cast<float>();
-        param.size = in_size.cast<float>();
-        param.radius = in_radius;
-        rounded_rect_p->set_param(param);
-    }
+    void to_rect_pipeline();
+    void to_rounded_rect_pipeline(const Eigen::Vector2i& in_pos, const Eigen::Vector2i& in_size, float in_radius);
+    void to_texture_pipeline(renderer_texture* in_texture);
 private:
     std::vector<aorii_vertex> vertices;
     std::vector<aorii_triangle> triangles;
 
-    rect_pipeline* rect_p = nullptr;
-    rounded_rect_pipeline* rounded_rect_p = nullptr;
-
     pipeline* current_pipeline = nullptr;
     Eigen::Matrix4f projection_matrix;
+private:
+    void make_rect(const Eigen::Vector2i& in_pos, const Eigen::Vector2i& in_size, const linear_color& in_color);
 };
diff --git a/src/renderer/core/renderer/renderer_texture.h b/src/renderer/core/renderer/renderer_texture.h
index eb37a69..7676ad4 100644
--- a/src/renderer/core/renderer/renderer_texture.h
+++ b/src/renderer/core/renderer/renderer_texture.h
@@ -1,14 +1,42 @@
 #pragma once
 #include <Eigen/Eigen>
 
+#include "renderer.h"
+
 class renderer_texture {
 public:
+    renderer_texture(texture_format in_format) : format(in_format) {}
     virtual ~renderer_texture() = default;
 
-
-    virtual void* lock() = 0;
+    virtual void* lock(unsigned int* out_row_pitch) = 0;
     virtual void unlock() = 0;
 
     virtual bool resize(const Eigen::Vector2i& size) = 0;
     virtual Eigen::Vector2i size() = 0;
+
+    virtual void* get_native_handle() = 0;
+    virtual void* get_shader_resource_view() = 0;
+    [[nodiscard]] texture_format get_format() const { return format; }
+
+
+    static void convert_rgb_to_rgba(const unsigned char* rgb, unsigned char* rgba, int width, int height) {
+        for (int i = 0; i < width * height; ++i) {
+            rgba[i * 4 + 0] = rgb[i * 3 + 0]; // R
+            rgba[i * 4 + 1] = rgb[i * 3 + 1]; // G
+            rgba[i * 4 + 2] = rgb[i * 3 + 2]; // B
+            rgba[i * 4 + 3] = 255; // A (完全不透明)
+        }
+    }
+private:
+    const texture_format format;
+};
+
+class texture_format_converter {
+public:
+    texture_format_converter(Eigen::Vector2i size, texture_format in_format, texture_format out_format) : size(size), in_format(in_format), out_format(out_format) {}
+
+private:
+    Eigen::Vector2i size;
+    texture_format in_format;
+    texture_format out_format;
 };
diff --git a/src/renderer/misc/color.cpp b/src/renderer/misc/color.cpp
new file mode 100644
index 0000000..ad05900
--- /dev/null
+++ b/src/renderer/misc/color.cpp
@@ -0,0 +1,3 @@
+#include "color.h"
+
+const linear_color linear_color::white = linear_color(1, 1, 1, 1);
diff --git a/src/renderer/misc/color.h b/src/renderer/misc/color.h
index 66d90c6..e9a3a3a 100644
--- a/src/renderer/misc/color.h
+++ b/src/renderer/misc/color.h
@@ -38,4 +38,12 @@ public:
         return { r - in_color.r, g - in_color.g, b - in_color.b, a - in_color.a };
     }
     float r, g, b, a;
+
+
+
+
+
+
+
+    static const linear_color white;
 };
diff --git a/src/renderer/shader/aorii_texture.slang b/src/renderer/shader/aorii_texture.slang
new file mode 100644
index 0000000..75f68ce
--- /dev/null
+++ b/src/renderer/shader/aorii_texture.slang
@@ -0,0 +1,35 @@
+struct ParamBuffer
+{
+    matrix transform;
+};
+ParameterBlock<ParamBuffer> param_buffer : register(b0);
+
+struct VSInput {
+    float2 position : POSITION; // 窗口坐标
+    float2 uv : TEXCOORD0; // 纹理坐标
+    float4 color : COLOR; // 颜色
+};
+
+struct PSInput {
+    float4 position : SV_POSITION; // 裁剪空间坐标
+    float2 uv : TEXCOORD0; // 纹理坐标
+    float4 color : COLOR;  // 颜色
+};
+
+Texture2D<float4> texture : register(t0);
+SamplerState sampler : register(s0);
+
+PSInput vertex_main(VSInput input)
+{
+    PSInput output;
+    output.position = mul(float4(input.position, 0.0, 1.0), param_buffer.transform);
+    output.uv = input.uv;
+    output.color = input.color;
+    return output;
+}
+
+float4 pixel_main(PSInput input) : SV_TARGET
+{
+    return input.color * texture.Sample(sampler, input.uv);
+}
+