feat(widget): add rounded rectangle mask widget and shader widget base

- Implemented `rounded_rect_mask_widget` for creating rounded rectangle masks with customizable corner radii, feathering, and border width.
- Introduced `shader_widget` and `shader_widget_base` classes for managing shader resources, including pipeline and descriptor set creation.
- Added concepts for shader bindings to ensure type safety and proper usage of shader resources.
- Defined enums for shader widget modes and dirty flags to manage widget state and updates effectively.
- Created utility functions for bitwise operations on dirty flags to simplify state management.
This commit is contained in:
2026-01-02 21:33:03 +08:00
parent d5d74034be
commit 5d9cde18a3
34 changed files with 6554 additions and 1733 deletions

View File

@@ -121,6 +121,7 @@ add_subdirectory(src/window)
add_subdirectory(src/resource)
add_subdirectory(src/shader)
add_subdirectory(src/render)
add_subdirectory(src/widget)
add_subdirectory(src/text)
add_subdirectory(src/input)
add_subdirectory(src/reactive)

View File

@@ -1,14 +1,16 @@
# ============================================================================
# MIRAI 着色器编译 CMake 模块
# ============================================================================
# 提供自动化着色器编译和代码生成功能
# 自动检测 [shader("xxx")] 属性并编译所有入口点
# 提供自动化 GLSL 着色器编译和代码生成功能
# 使用 glslangValidator 或 glslc (shaderc) 编译 GLSL 到 SPIR-V
#
# 主要函数:
# add_shader_library() - 创建着色器库目标
# add_glsl_shader_library() - 创建 GLSL 着色器库目标
# compile_glsl_shader() - 编译单个 GLSL 着色器
#
# 辅助函数:
# find_python_with_jinja2() - 查找带 Jinja2 的 Python
# find_glsl_compiler() - 查找 GLSL 编译器
# ============================================================================
include_guard(GLOBAL)
@@ -44,6 +46,46 @@ function(find_python_with_jinja2 OUT_PYTHON_EXECUTABLE)
set(${OUT_PYTHON_EXECUTABLE} "${Python3_EXECUTABLE}" PARENT_SCOPE)
endfunction()
# ============================================================================
# find_glsl_compiler - 查找 GLSL 编译器 (glslangValidator 或 glslc)
# ============================================================================
function(find_glsl_compiler OUT_COMPILER OUT_COMPILER_TYPE)
# 首先尝试查找 glslc (shaderc)
find_program(GLSLC_EXECUTABLE glslc
HINTS
$ENV{VULKAN_SDK}/Bin
$ENV{VULKAN_SDK}/bin
${Vulkan_GLSLC_EXECUTABLE}
)
if(GLSLC_EXECUTABLE)
message(STATUS "Found GLSL compiler: ${GLSLC_EXECUTABLE} (glslc)")
set(${OUT_COMPILER} "${GLSLC_EXECUTABLE}" PARENT_SCOPE)
set(${OUT_COMPILER_TYPE} "glslc" PARENT_SCOPE)
return()
endif()
# 尝试查找 glslangValidator
find_program(GLSLANG_VALIDATOR_EXECUTABLE glslangValidator
HINTS
$ENV{VULKAN_SDK}/Bin
$ENV{VULKAN_SDK}/bin
${Vulkan_GLSLANG_VALIDATOR_EXECUTABLE}
)
if(GLSLANG_VALIDATOR_EXECUTABLE)
message(STATUS "Found GLSL compiler: ${GLSLANG_VALIDATOR_EXECUTABLE} (glslangValidator)")
set(${OUT_COMPILER} "${GLSLANG_VALIDATOR_EXECUTABLE}" PARENT_SCOPE)
set(${OUT_COMPILER_TYPE} "glslangValidator" PARENT_SCOPE)
return()
endif()
message(FATAL_ERROR
"No GLSL compiler found. Please install Vulkan SDK or shaderc.\n"
"Expected: glslc or glslangValidator in PATH or VULKAN_SDK"
)
endfunction()
# ============================================================================
# 内部变量设置
# ============================================================================
@@ -65,73 +107,188 @@ function(_mirai_ensure_shader_compiler)
if(NOT TARGET ${MIRAI_SHADER_COMPILER_TARGET})
message(FATAL_ERROR
"Shader compiler target '${MIRAI_SHADER_COMPILER_TARGET}' not found.\n"
"Make sure add_subdirectory(tools/shader_compile) is called before using add_shader_library()."
"Make sure add_subdirectory(tools/shader_compile) is called before using add_glsl_shader_library()."
)
endif()
endfunction()
# ============================================================================
# add_shader_library - 添加着色器库到现有目标
# _get_shader_stage_from_extension - 从文件扩展名获取着色器阶段
# ============================================================================
function(_get_shader_stage_from_extension FILE_PATH OUT_STAGE)
get_filename_component(EXT "${FILE_PATH}" EXT)
string(TOLOWER "${EXT}" EXT_LOWER)
if(EXT_LOWER STREQUAL ".vert")
set(${OUT_STAGE} "vert" PARENT_SCOPE)
elseif(EXT_LOWER STREQUAL ".frag")
set(${OUT_STAGE} "frag" PARENT_SCOPE)
elseif(EXT_LOWER STREQUAL ".comp")
set(${OUT_STAGE} "comp" PARENT_SCOPE)
elseif(EXT_LOWER STREQUAL ".geom")
set(${OUT_STAGE} "geom" PARENT_SCOPE)
elseif(EXT_LOWER STREQUAL ".tesc")
set(${OUT_STAGE} "tesc" PARENT_SCOPE)
elseif(EXT_LOWER STREQUAL ".tese")
set(${OUT_STAGE} "tese" PARENT_SCOPE)
else()
set(${OUT_STAGE} "" PARENT_SCOPE)
endif()
endfunction()
# ============================================================================
# compile_glsl_shader - 编译单个 GLSL 着色器
# ============================================================================
#
# 将着色器编译和代码生成附加到现有的编译目标上
# 自动检测所有 [shader("xxx")] 入口点并编译
# 用法:
# compile_glsl_shader(
# INPUT shader.vert
# OUTPUT shader.vert.spv
# STAGE vert
# DEFINES DEBUG FEATURE_X
# INCLUDE_DIRS include/
# )
#
# 参数:
# INPUT - 输入 GLSL 文件路径(必需)
# OUTPUT - 输出 SPIR-V 文件路径(必需)
# STAGE - 着色器阶段 (vert/frag/comp/geom/tesc/tese)(可选,自动检测)
# DEFINES - 预处理器定义(可选)
# INCLUDE_DIRS - include 目录(可选)
#
function(compile_glsl_shader)
cmake_parse_arguments(SHADER "" "INPUT;OUTPUT;STAGE" "DEFINES;INCLUDE_DIRS" ${ARGN})
# 验证必需参数
if(NOT SHADER_INPUT)
message(FATAL_ERROR "compile_glsl_shader: INPUT is required")
endif()
if(NOT SHADER_OUTPUT)
message(FATAL_ERROR "compile_glsl_shader: OUTPUT is required")
endif()
# 自动检测着色器阶段
if(NOT SHADER_STAGE)
_get_shader_stage_from_extension("${SHADER_INPUT}" SHADER_STAGE)
if(NOT SHADER_STAGE)
message(FATAL_ERROR "compile_glsl_shader: Cannot determine shader stage from extension. Please specify STAGE.")
endif()
endif()
# 查找 GLSL 编译器
find_glsl_compiler(GLSL_COMPILER GLSL_COMPILER_TYPE)
# 构建编译命令参数
set(COMPILE_ARGS "")
if(GLSL_COMPILER_TYPE STREQUAL "glslc")
# glslc 参数
list(APPEND COMPILE_ARGS "-fshader-stage=${SHADER_STAGE}")
list(APPEND COMPILE_ARGS "--target-env=vulkan1.3")
list(APPEND COMPILE_ARGS "-o" "${SHADER_OUTPUT}")
# 添加 include 目录
foreach(INC_DIR ${SHADER_INCLUDE_DIRS})
list(APPEND COMPILE_ARGS "-I${INC_DIR}")
endforeach()
# 添加宏定义
foreach(DEF ${SHADER_DEFINES})
list(APPEND COMPILE_ARGS "-D${DEF}")
endforeach()
list(APPEND COMPILE_ARGS "${SHADER_INPUT}")
else()
# glslangValidator 参数
list(APPEND COMPILE_ARGS "-V") # 生成 SPIR-V
list(APPEND COMPILE_ARGS "--target-env" "vulkan1.3")
list(APPEND COMPILE_ARGS "-S" "${SHADER_STAGE}")
list(APPEND COMPILE_ARGS "-o" "${SHADER_OUTPUT}")
# 添加 include 目录
foreach(INC_DIR ${SHADER_INCLUDE_DIRS})
list(APPEND COMPILE_ARGS "-I${INC_DIR}")
endforeach()
# 添加宏定义
foreach(DEF ${SHADER_DEFINES})
list(APPEND COMPILE_ARGS "-D${DEF}")
endforeach()
list(APPEND COMPILE_ARGS "${SHADER_INPUT}")
endif()
# 创建编译命令
add_custom_command(
OUTPUT "${SHADER_OUTPUT}"
COMMAND ${GLSL_COMPILER} ${COMPILE_ARGS}
DEPENDS "${SHADER_INPUT}"
COMMENT "Compiling GLSL shader: ${SHADER_INPUT}"
VERBATIM
)
endfunction()
# ============================================================================
# add_glsl_shader_library - 添加 GLSL 着色器库到现有目标
# ============================================================================
#
# 将 GLSL 着色器编译和代码生成附加到现有的编译目标上
#
# 用法:
# # 方案1: 自动搜索着色器,指定引用路径
# add_shader_library(TARGET mirai_shader
# SHADER_PATH shaders
# REF_PATHS shaders/common shaders/math
# # 方案1: 自动搜索着色器
# add_glsl_shader_library(TARGET mirai_shader
# SHADER_PATH shaders/glsl
# INCLUDE_DIRS shaders/glsl/common
# )
#
# # 方案2: 指定具体着色器文件
# add_shader_library(TARGET mirai_shader
# SHADERS shaders/ui/rect.slang shaders/ui/text.slang
# REF_PATHS shaders/common
# )
#
# # 方案3: 混合模式,添加额外着色器
# add_shader_library(TARGET mirai_shader
# SHADER_PATH shaders
# SHADERS shaders/ui/extra.slang
# REF_PATHS shaders/common
# add_glsl_shader_library(TARGET mirai_shader
# SHADERS shaders/ui/rect.vert shaders/ui/rect.frag
# INCLUDE_DIRS shaders/common
# )
#
# 参数:
# TARGET - 现有的编译目标(必需)
# SHADERS - 具体的着色器文件列表(可选)
# SHADER_PATH - 着色器搜索目录,相对于 CMAKE_CURRENT_SOURCE_DIR可选
# REF_PATHS - 着色器引用路径,用于 include 通用模块(可选)
# INCLUDE_DIRS - 着色器 include 路径(可选)
# DEFINES - 预处理器定义(可选)
# RECURSIVE - 是否递归搜索子目录(可选,默认 OFF
#
function(add_shader_library)
function(add_glsl_shader_library)
# 解析参数
set(options RECURSIVE)
set(oneValueArgs TARGET SHADER_PATH)
set(multiValueArgs SHADERS REF_PATHS DEFINES)
set(multiValueArgs SHADERS INCLUDE_DIRS DEFINES)
cmake_parse_arguments(SHADER "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# 验证必需参数
if(NOT SHADER_TARGET)
message(FATAL_ERROR "add_shader_library: TARGET is required")
message(FATAL_ERROR "add_glsl_shader_library: TARGET is required")
endif()
# 验证目标存在
if(NOT TARGET ${SHADER_TARGET})
message(FATAL_ERROR "add_shader_library: Target '${SHADER_TARGET}' does not exist")
message(FATAL_ERROR "add_glsl_shader_library: Target '${SHADER_TARGET}' does not exist")
endif()
# 收集着色器文件
set(ALL_SHADERS "${SHADER_SHADERS}")
if(SHADER_SHADER_PATH)
set(SHADER_EXTENSIONS "vert;frag;comp;geom;tesc;tese")
if(SHADER_RECURSIVE)
retrieve_files_custom(${CMAKE_CURRENT_SOURCE_DIR} "slang" FOUND_SHADERS)
foreach(EXT ${SHADER_EXTENSIONS})
file(GLOB_RECURSE FOUND_SHADERS "${CMAKE_CURRENT_SOURCE_DIR}/${SHADER_SHADER_PATH}/*.${EXT}")
list(APPEND ALL_SHADERS ${FOUND_SHADERS})
endforeach()
else()
file(GLOB FOUND_SHADERS "${CMAKE_CURRENT_SOURCE_DIR}/${SHADER_SHADER_PATH}/*.slang")
foreach(EXT ${SHADER_EXTENSIONS})
file(GLOB FOUND_SHADERS "${CMAKE_CURRENT_SOURCE_DIR}/${SHADER_SHADER_PATH}/*.${EXT}")
list(APPEND ALL_SHADERS ${FOUND_SHADERS})
endforeach()
endif()
list(APPEND ALL_SHADERS ${FOUND_SHADERS})
endif()
# 去重
@@ -152,71 +309,145 @@ function(add_shader_library)
# 查找 Python with Jinja2
find_python_with_jinja2(PYTHON_EXECUTABLE)
# 查找 GLSL 编译器
find_glsl_compiler(GLSL_COMPILER GLSL_COMPILER_TYPE)
# 构建 include 路径参数
set(INCLUDE_ARGS "")
foreach(INC_DIR ${SHADER_REF_PATHS})
foreach(INC_DIR ${SHADER_INCLUDE_DIRS})
if(IS_ABSOLUTE "${INC_DIR}")
list(APPEND INCLUDE_ARGS "-I${INC_DIR}")
list(APPEND INCLUDE_ARGS "${INC_DIR}")
else()
list(APPEND INCLUDE_ARGS "-I${CMAKE_CURRENT_SOURCE_DIR}/${INC_DIR}")
list(APPEND INCLUDE_ARGS "${CMAKE_CURRENT_SOURCE_DIR}/${INC_DIR}")
endif()
endforeach()
# 构建宏定义参数
set(DEFINE_ARGS "")
foreach(DEF ${SHADER_DEFINES})
list(APPEND DEFINE_ARGS "-D${DEF}")
endforeach()
# 收集生成的文件
set(ALL_GENERATED_HEADERS "")
set(ALL_SPV_FILES "")
# 处理每个着色器文件
# 按着色器名称分组(去掉扩展名后相同的文件归为一组)
set(SHADER_GROUPS "")
foreach(SHADER_SOURCE ${ALL_SHADERS})
get_filename_component(SHADER_NAME_WE "${SHADER_SOURCE}" NAME_WE)
get_filename_component(SHADER_DIR "${SHADER_SOURCE}" DIRECTORY)
if(IS_ABSOLUTE "${SHADER_SOURCE}")
set(SHADER_ABS "${SHADER_SOURCE}")
else()
set(SHADER_ABS "${CMAKE_CURRENT_SOURCE_DIR}/${SHADER_SOURCE}")
if(NOT EXISTS "${SHADER_ABS}")
message(FATAL_ERROR "Shader not found: ${SHADER_SOURCE}")
# 创建唯一的组标识符
string(REPLACE "/" "_" DIR_SAFE "${SHADER_DIR}")
set(GROUP_ID "${DIR_SAFE}_${SHADER_NAME_WE}")
# 添加到组列表
list(APPEND SHADER_GROUPS "${GROUP_ID}")
list(APPEND ${GROUP_ID}_SOURCES "${SHADER_SOURCE}")
set(${GROUP_ID}_NAME "${SHADER_NAME_WE}")
set(${GROUP_ID}_DIR "${SHADER_DIR}")
endforeach()
# 去重组列表
if(SHADER_GROUPS)
list(REMOVE_DUPLICATES SHADER_GROUPS)
endif()
# 处理每个着色器组
foreach(GROUP_ID ${SHADER_GROUPS})
set(GROUP_SOURCES "${${GROUP_ID}_SOURCES}")
set(GROUP_NAME "${${GROUP_ID}_NAME}")
set(GROUP_DIR "${${GROUP_ID}_DIR}")
# 每个着色器组的输出目录
set(SPIRV_OUTPUT_DIR "${SHADER_INTERMEDIATE_DIR}/${GROUP_NAME}")
set(GENERATED_HPP "${SHADER_OUTPUT_DIR}/${GROUP_NAME}_bindings.hpp")
file(MAKE_DIRECTORY "${SPIRV_OUTPUT_DIR}")
set(GROUP_SPV_FILES "")
# 编译组内的每个着色器
foreach(SHADER_SOURCE ${GROUP_SOURCES})
get_filename_component(SHADER_FILENAME "${SHADER_SOURCE}" NAME)
_get_shader_stage_from_extension("${SHADER_SOURCE}" SHADER_STAGE)
if(NOT SHADER_STAGE)
message(WARNING "Skipping unknown shader type: ${SHADER_SOURCE}")
continue()
endif()
set(SPV_OUTPUT "${SPIRV_OUTPUT_DIR}/${SHADER_FILENAME}.spv")
# 构建编译命令参数
set(COMPILE_ARGS "")
if(GLSL_COMPILER_TYPE STREQUAL "glslc")
list(APPEND COMPILE_ARGS "-fshader-stage=${SHADER_STAGE}")
list(APPEND COMPILE_ARGS "--target-env=vulkan1.3")
list(APPEND COMPILE_ARGS "-o" "${SPV_OUTPUT}")
foreach(INC_DIR ${INCLUDE_ARGS})
list(APPEND COMPILE_ARGS "-I${INC_DIR}")
endforeach()
foreach(DEF ${SHADER_DEFINES})
list(APPEND COMPILE_ARGS "-D${DEF}")
endforeach()
list(APPEND COMPILE_ARGS "${SHADER_SOURCE}")
else()
list(APPEND COMPILE_ARGS "-V")
list(APPEND COMPILE_ARGS "--target-env" "vulkan1.3")
list(APPEND COMPILE_ARGS "-S" "${SHADER_STAGE}")
list(APPEND COMPILE_ARGS "-o" "${SPV_OUTPUT}")
foreach(INC_DIR ${INCLUDE_ARGS})
list(APPEND COMPILE_ARGS "-I${INC_DIR}")
endforeach()
foreach(DEF ${SHADER_DEFINES})
list(APPEND COMPILE_ARGS "-D${DEF}")
endforeach()
list(APPEND COMPILE_ARGS "${SHADER_SOURCE}")
endif()
# 创建编译命令
add_custom_command(
OUTPUT "${SPV_OUTPUT}"
COMMAND ${GLSL_COMPILER} ${COMPILE_ARGS}
DEPENDS "${SHADER_SOURCE}"
COMMENT "Compiling GLSL: ${SHADER_FILENAME}"
VERBATIM
)
list(APPEND GROUP_SPV_FILES "${SPV_OUTPUT}")
list(APPEND ALL_SPV_FILES "${SPV_OUTPUT}")
endforeach()
# 使用 mirai_shader_compile 工具生成反射和绑定
if(GROUP_SPV_FILES)
# 创建标记文件表示编译完成
set(COMPILED_MARKER "${SPIRV_OUTPUT_DIR}/.compiled")
add_custom_command(
OUTPUT "${COMPILED_MARKER}"
COMMAND ${CMAKE_COMMAND} -E touch "${COMPILED_MARKER}"
DEPENDS ${GROUP_SPV_FILES}
COMMENT "GLSL shaders compiled: ${GROUP_NAME}"
VERBATIM
)
# 生成绑定头文件
add_custom_command(
OUTPUT "${GENERATED_HPP}"
COMMAND $<TARGET_FILE:${MIRAI_SHADER_COMPILER_TARGET}>
--dir "${SPIRV_OUTPUT_DIR}"
--output "${GENERATED_HPP}"
--name "${GROUP_NAME}"
DEPENDS "${COMPILED_MARKER}" ${MIRAI_SHADER_COMPILER_TARGET}
COMMENT "Generating bindings: ${GROUP_NAME}"
VERBATIM
)
list(APPEND ALL_GENERATED_HEADERS "${GENERATED_HPP}")
endif()
# 每个着色器文件的输出目录
set(SPIRV_OUTPUT_DIR "${SHADER_INTERMEDIATE_DIR}/${SHADER_NAME_WE}")
set(GENERATED_HPP "${SHADER_OUTPUT_DIR}/${SHADER_NAME_WE}_bindings.hpp")
# 步骤1: 编译着色器(自动检测所有入口点)
add_custom_command(
OUTPUT "${SPIRV_OUTPUT_DIR}/.compiled"
COMMAND ${CMAKE_COMMAND} -E make_directory "${SPIRV_OUTPUT_DIR}"
COMMAND $<TARGET_FILE:${MIRAI_SHADER_COMPILER_TARGET}>
"${SHADER_ABS}"
-o "${SPIRV_OUTPUT_DIR}"
${INCLUDE_ARGS}
${DEFINE_ARGS}
COMMAND ${CMAKE_COMMAND} -E touch "${SPIRV_OUTPUT_DIR}/.compiled"
DEPENDS "${SHADER_ABS}" ${MIRAI_SHADER_COMPILER_TARGET}
COMMENT "Compiling shader: ${SHADER_NAME_WE}"
VERBATIM COMMAND_EXPAND_LISTS
)
# 步骤2: 生成绑定头文件Python 脚本自己查找 spv 文件)
add_custom_command(
OUTPUT "${GENERATED_HPP}"
COMMAND ${PYTHON_EXECUTABLE} "${MIRAI_SHADER_GENERATOR_SCRIPT}"
--dir "${SPIRV_OUTPUT_DIR}"
--output "${GENERATED_HPP}"
--name "${SHADER_NAME_WE}"
--template-dir "${MIRAI_SHADER_TEMPLATE_DIR}"
DEPENDS "${SPIRV_OUTPUT_DIR}/.compiled" "${MIRAI_SHADER_GENERATOR_SCRIPT}"
COMMENT "Generating bindings: ${SHADER_NAME_WE}"
VERBATIM
)
list(APPEND ALL_GENERATED_HEADERS "${GENERATED_HPP}")
endforeach()
# 附加到目标:添加生成目录到 include 路径
@@ -233,10 +464,52 @@ function(add_shader_library)
# 导出变量
set(${SHADER_TARGET}_SHADER_HEADERS ${ALL_GENERATED_HEADERS} PARENT_SCOPE)
set(${SHADER_TARGET}_SHADER_OUTPUT_DIR ${SHADER_OUTPUT_DIR} PARENT_SCOPE)
set(${SHADER_TARGET}_SPV_FILES ${ALL_SPV_FILES} PARENT_SCOPE)
message(STATUS "Added shaders to target: ${SHADER_TARGET}")
message(STATUS "Added GLSL shaders to target: ${SHADER_TARGET}")
message(STATUS " Output: ${SHADER_OUTPUT_DIR}")
if(ALL_SHADERS)
message(STATUS " Shaders: ${ALL_SHADERS}")
list(LENGTH ALL_SHADERS SHADER_COUNT)
message(STATUS " Shaders: ${SHADER_COUNT} files")
endif()
endfunction()
# ============================================================================
# add_shader_library - 兼容性别名(调用 add_glsl_shader_library
# ============================================================================
#
# 为了向后兼容,保留 add_shader_library 函数名
# 现在默认使用 GLSL 编译
#
function(add_shader_library)
# 解析参数
set(options RECURSIVE)
set(oneValueArgs TARGET SHADER_PATH)
set(multiValueArgs SHADERS REF_PATHS DEFINES)
cmake_parse_arguments(SHADER "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# 转换参数并调用新函数
set(FORWARD_ARGS "TARGET" "${SHADER_TARGET}")
if(SHADER_SHADER_PATH)
list(APPEND FORWARD_ARGS "SHADER_PATH" "${SHADER_SHADER_PATH}")
endif()
if(SHADER_SHADERS)
list(APPEND FORWARD_ARGS "SHADERS" ${SHADER_SHADERS})
endif()
if(SHADER_REF_PATHS)
list(APPEND FORWARD_ARGS "INCLUDE_DIRS" ${SHADER_REF_PATHS})
endif()
if(SHADER_DEFINES)
list(APPEND FORWARD_ARGS "DEFINES" ${SHADER_DEFINES})
endif()
if(SHADER_RECURSIVE)
list(APPEND FORWARD_ARGS "RECURSIVE")
endif()
add_glsl_shader_library(${FORWARD_ARGS})
endfunction()

View File

@@ -1,15 +1,22 @@
---
version: 0.1.0
status: accepted
status: superseded
created: 2024-01-15
superseded_by: adr-003-glsl-shader-system.md
author: MIRAI Team
---
# ADR-002: 选择 Slang 作为着色器语言
> **⚠️ 已废弃**:本 ADR 已被 [ADR-003: 迁移到 GLSL 着色器系统](adr-003-glsl-shader-system.md) 取代。
>
> 项目已从 Slang 迁移到标准 GLSL以简化工具链并提供更好的用户自定义支持。
## 状态
**已接受** - 2024-01-15
**已废弃** - 2024-01-20被 [ADR-003](adr-003-glsl-shader-system.md) 取代)
原状态:已接受 - 2024-01-15
## 背景

View File

@@ -0,0 +1,192 @@
---
version: 0.2.0
status: accepted
created: 2024-01-20
author: MIRAI Team
supersedes: adr-002-slang-shader-system.md
---
# ADR-003: 迁移到 GLSL 着色器系统
## 状态
**已接受** - 2024-01-20
## 背景
原系统使用 Slang 着色器语言(参见 [ADR-002](adr-002-slang-shader-system.md)),虽然 Slang 提供了强大的反射和泛型功能,但在实际使用中遇到了以下问题:
1. **工具链复杂性**Slang 编译器需要额外的依赖和配置
2. **用户学习成本**:开发者需要学习新的着色器语言
3. **调试困难**Slang 生成的 SPIR-V 难以调试
4. **生态系统兼容性**:大多数着色器工具和示例都基于 GLSL
## 决策
迁移到标准 **GLSL** 着色器语言,采用以下架构设计:
### 1. 资源分配约定
| 资源类型 | 分配 | 说明 |
|---------|------|------|
| **Set 0** | 框架专用 | 用户不要使用 |
| **Set 1** | 用户 UBO | 用户自定义 Uniform Buffer |
| **Set 2** | 用户纹理 | 用户自定义纹理 |
| **Push Constant** | 用户专用 | 最大 128 bytes |
### 2. 框架 UBO 结构 (Set 0, Binding 0)
```glsl
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds; // (x, y, width, height)
float u_opacity; // 0.0 - 1.0
float _pad0;
float _pad1;
float _pad2;
} widget;
```
### 3. 四种渲染模式
| 模式 | 顶点着色器 | 片段着色器 | 说明 |
|------|-----------|-----------|------|
| **Backdrop** | 框架提供 | 用户提供 | 后处理效果,框架提供背景纹理 |
| **Procedural** | 框架提供 | 用户提供 | 程序化生成 |
| **Mask** | 框架提供 | 用户提供 | 遮罩效果 |
| **Custom Mesh** | 用户提供 | 用户提供 | 自定义网格渲染 |
### 4. Push Constant 完全留给用户
框架不使用 Push Constant用户可以完全控制这 128 bytes 的快速更新数据。
## 理由
### 1. 简化工具链
```
原流程: .slang → slangc → SPIR-V + 反射 JSON → C++ 绑定
新流程: .glsl → glslangValidator → SPIR-V
```
### 2. 用户友好
- GLSL 是业界标准,大多数图形开发者已经熟悉
- 丰富的学习资源和示例代码
- 更好的 IDE 支持和语法高亮
### 3. 调试便利
- SPIR-V 工具链成熟
- RenderDoc 等工具原生支持
- 错误信息更易理解
### 4. 灵活的用户控制
- Push Constant 完全留给用户,支持高频参数更新
- 用户可以自由定义 Set 1/2 的资源布局
- 模板文件提供清晰的起点
## 替代方案
| 方案 | 优点 | 缺点 |
|------|------|------|
| **GLSL** (选择) | 标准、简单、广泛支持 | 无反射、手动绑定 |
| Slang | 反射、泛型、多后端 | 工具链复杂、学习成本 |
| HLSL | DX12 原生 | 仅限 Windows |
| WGSL | WebGPU 原生 | 生态不成熟 |
## 后果
### 积极
- 简化了编译工具链
- 降低了用户学习成本
- 用户可以完全控制 Push Constant
- 更好的调试体验
- 更广泛的社区支持
### 消极
- 失去了 Slang 的自动反射功能
- 需要用户手动复制着色器模板
- 需要手动维护 C++ 和 GLSL 的数据结构同步
## 实现
### 着色器编译流程
```
用户 GLSL 片段着色器
glslangValidator -V
SPIR-V
spirv-cross (可选反射)
```
### 目录结构
```
src/shader/shaders/glsl/
├── framework/ # 框架内置着色器
│ ├── quad.vert # 四边形顶点着色器
│ └── fullscreen.vert # 全屏顶点着色器
├── templates/ # 用户模板
│ ├── procedural.frag.template
│ ├── backdrop.frag.template
│ ├── mask.frag.template
│ └── mesh.vert.template
└── examples/ # 示例着色器
├── gradient.frag
├── blur.frag
└── rounded_rect.frag
```
### CMake 集成
```cmake
function(compile_glsl_shader INPUT OUTPUT)
add_custom_command(
OUTPUT ${OUTPUT}
COMMAND glslangValidator -V ${INPUT} -o ${OUTPUT}
DEPENDS ${INPUT}
COMMENT "Compiling ${INPUT}"
)
endfunction()
```
### C++ 端 UBO 结构
```cpp
namespace mirai {
struct alignas(16) widget_bounds_ubo {
float bounds[4]; // x, y, width, height
float opacity;
float _pad[3];
};
static_assert(sizeof(widget_bounds_ubo) == 32);
} // namespace mirai
```
## 迁移指南
### 从 Slang 迁移到 GLSL
1. **复制模板文件**:根据渲染模式选择对应的模板
2. **转换语法**
- `float4``vec4`
- `float2``vec2`
- `ConstantBuffer<T>``uniform T { ... }`
- `[[vk::binding(n, m)]]``layout(set = m, binding = n)`
3. **更新绑定**:确保使用正确的 Set 和 Binding
4. **测试验证**:使用 glslangValidator 验证语法
## 参考
- [GLSL 规范](https://www.khronos.org/opengl/wiki/Core_Language_(GLSL))
- [Vulkan GLSL](https://www.khronos.org/opengl/wiki/Vulkan_GLSL)
- [glslangValidator](https://github.com/KhronosGroup/glslang)
- [MIRAI GLSL 着色器系统设计](../../plans/glsl_shader_system_design.md)

View File

@@ -0,0 +1,646 @@
# MIRAI Shader Widget 统一接口架构设计
## 1. 概述
本文档描述 MIRAI 框架的着色器控件Shader Widget统一接口架构设计。该架构基于标准 GLSL 着色器语言,支持四种渲染模式,每种模式提供特定的核心功能,而具体效果参数由派生类和着色器定义。
> **注意**:本文档反映了从 Slang 迁移到 GLSL 后的新架构。详见 [ADR-003: 迁移到 GLSL 着色器系统](adr/adr-003-glsl-shader-system.md)。
### 1.1 设计目标
1. **使用标准 GLSL**:采用业界标准着色器语言,降低学习成本
2. **Push Constant 完全留给用户**:框架不使用 Push Constant用户可完全控制
3. **框架数据最小化**:只提供 bounds 和 opacity
4. **简单约定**:提前约定框架和用户各自使用的资源
5. **模板驱动**:提供模板让用户复制和修改
### 1.2 四种渲染模式
| 模式 | 顶点着色器 | 片段着色器 | 说明 |
|------|-----------|-----------|------|
| **Backdrop** | 框架提供 | 用户提供 | 后处理效果,框架提供背景纹理 |
| **Procedural** | 框架提供 | 用户提供 | 程序化生成 |
| **Mask** | 框架提供 | 用户提供 | 遮罩效果 |
| **Custom Mesh** | 用户提供 | 用户提供 | 自定义网格渲染 |
---
## 2. 资源分配约定
### 2.1 Descriptor Set 布局
```
┌─────────────────────────────────────────────────────────────┐
│ 资源分配约定表 │
├─────────────────┬───────────────────────────────────────────┤
│ 资源类型 │ 分配 │
├─────────────────┼───────────────────────────────────────────┤
│ Set 0 │ 框架专用(用户不要使用) │
│ Set 1 │ 用户 UBO │
│ Set 2 │ 用户纹理 │
│ Push Constant │ 用户专用(最大 128 bytes
├─────────────────┼───────────────────────────────────────────┤
│ 顶点着色器 │ 框架提供Custom Mesh 除外) │
│ 片段着色器 │ 用户提供 │
└─────────────────┴───────────────────────────────────────────┘
```
### 2.2 Set 0框架专用
#### 通用模式Procedural, Mask
```glsl
// Set 0: 框架专用 - 用户不要在 Set 0 定义任何资源
// Binding 0: Widget 边界
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds; // (x, y, width, height)
float u_opacity; // 0.0 - 1.0
float _pad0;
float _pad1;
float _pad2;
} widget;
// 大小: 32 bytes
```
#### Backdrop 模式
```glsl
// Binding 0: Widget 边界(同上)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0;
float _pad1;
float _pad2;
} widget;
// Binding 1: 背景纹理(框架自动绑定)
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
```
### 2.3 Set 1用户 UBO
用户可以在 Set 1 定义自己的 Uniform Buffer
```glsl
// 示例:模糊参数
layout(set = 1, binding = 0, std140) uniform BlurParams {
float sigma; // 高斯分布标准差
int samples; // 采样半径
vec2 texel_size; // 纹理像素大小
} params;
```
### 2.4 Set 2用户纹理
用户可以在 Set 2 定义自己的纹理:
```glsl
// 示例:自定义纹理
layout(set = 2, binding = 0) uniform sampler2D u_my_texture;
layout(set = 2, binding = 1) uniform sampler2D u_noise_texture;
```
### 2.5 Push Constant用户专用
Push Constant 完全留给用户,最大 128 bytes
```glsl
layout(push_constant) uniform PushConstants {
vec4 color_start; // 起始颜色
vec4 color_end; // 结束颜色
float angle; // 渐变角度
float _pad[3];
} pc;
```
---
## 3. 框架内置顶点着色器
框架为 Backdrop/Procedural/Mask 模式提供内置顶点着色器,用户无需编写。
### 3.1 顶点着色器输出
框架的顶点着色器会输出以下数据给片段着色器:
```glsl
// 框架顶点着色器输出(用户片段着色器的输入)
layout(location = 0) in vec2 v_uv; // UV 坐标 (0-1)
layout(location = 1) in vec2 v_local_pos; // 局部坐标 (0-1),相对于 widget bounds
```
### 3.2 框架顶点着色器实现
```glsl
// 框架内部实现 - quad.vert
#version 450
layout(location = 0) in vec2 a_position;
layout(location = 1) in vec2 a_uv;
layout(location = 0) out vec2 v_uv;
layout(location = 1) out vec2 v_local_pos;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0;
float _pad1;
float _pad2;
} widget;
void main() {
// 将顶点位置转换到 NDC
vec2 pos = a_position * widget.u_bounds.zw + widget.u_bounds.xy;
vec2 ndc = pos * 2.0 - 1.0;
gl_Position = vec4(ndc, 0.0, 1.0);
v_uv = a_uv;
v_local_pos = a_position;
}
```
---
## 4. 用户片段着色器模板
### 4.1 Procedural 模式模板
用于程序化生成效果(渐变、噪声、图案等):
```glsl
#version 450
// ============================================================
// 框架输入(不要修改)
// ============================================================
layout(location = 0) in vec2 v_uv; // UV 坐标 (0-1)
layout(location = 1) in vec2 v_local_pos; // 局部坐标 (0-1)
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用(不要修改)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds; // (x, y, width, height)
float u_opacity; // 0.0 - 1.0
float _pad0;
float _pad1;
float _pad2;
} widget;
// ============================================================
// 用户区域(自由定义)
// ============================================================
// Set 1: 用户 UBO可选
// layout(set = 1, binding = 0, std140) uniform MyParams {
// float time;
// // ... 自定义参数
// } params;
// Set 2: 用户纹理(可选)
// layout(set = 2, binding = 0) uniform sampler2D u_my_texture;
// Push Constant可选最大 128 bytes
layout(push_constant) uniform PushConstants {
vec4 color;
// ... 自定义参数
} pc;
// ============================================================
// 主函数
// ============================================================
void main() {
// TODO: 用户实现程序化效果
frag_color = pc.color;
frag_color.a *= widget.u_opacity;
}
```
### 4.2 Backdrop 模式模板
用于后处理效果(模糊、颜色调整等):
```glsl
#version 450
// ============================================================
// 框架输入(不要修改)
// ============================================================
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用(不要修改)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0;
float _pad1;
float _pad2;
} widget;
// 背景纹理(框架提供)
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
// ============================================================
// 用户区域(自由定义)
// ============================================================
// Set 1: 用户 UBO可选
// layout(set = 1, binding = 0, std140) uniform MyParams {
// float radius;
// // ... 自定义参数
// } params;
// Push Constant可选
// layout(push_constant) uniform PushConstants {
// // ... 自定义参数
// } pc;
// ============================================================
// 主函数
// ============================================================
void main() {
// 采样背景纹理
vec4 color = texture(u_backdrop, v_uv);
// TODO: 用户实现后处理效果
frag_color = color;
frag_color.a *= widget.u_opacity;
}
```
### 4.3 Mask 模式模板
用于遮罩效果(圆角矩形、圆形、自定义形状等):
```glsl
#version 450
// ============================================================
// 框架输入(不要修改)
// ============================================================
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用(不要修改)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0;
float _pad1;
float _pad2;
} widget;
// ============================================================
// 用户区域(自由定义)
// ============================================================
// Push Constant可选
layout(push_constant) uniform PushConstants {
vec4 color; // 遮罩颜色
float corner_radius; // 圆角半径
float _pad[3];
} pc;
// ============================================================
// 主函数
// ============================================================
void main() {
// 示例:圆角矩形遮罩
vec2 size = widget.u_bounds.zw;
vec2 half_size = size * 0.5;
vec2 p = (v_local_pos - 0.5) * size;
vec2 q = abs(p) - half_size + pc.corner_radius;
float d = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - pc.corner_radius;
float mask = 1.0 - smoothstep(-1.0, 1.0, d);
frag_color = pc.color;
frag_color.a *= mask * widget.u_opacity;
}
```
### 4.4 Custom Mesh 模式模板
用于自定义网格渲染(粒子系统、自定义形状等):
**顶点着色器模板:**
```glsl
#version 450
// ============================================================
// 用户定义顶点输入
// ============================================================
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec2 a_uv;
layout(location = 2) in vec4 a_color;
layout(location = 0) out vec2 v_uv;
layout(location = 1) out vec4 v_color;
// Set 0: 框架专用(不要修改)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0;
float _pad1;
float _pad2;
} widget;
// ============================================================
// 用户区域(自由定义)
// ============================================================
// Push Constant
layout(push_constant) uniform PushConstants {
mat4 mvp;
} pc;
// ============================================================
// 主函数
// ============================================================
void main() {
gl_Position = pc.mvp * vec4(a_position, 1.0);
v_uv = a_uv;
v_color = a_color;
}
```
**片段着色器模板:**
```glsl
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec4 v_color;
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用(不要修改)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0;
float _pad1;
float _pad2;
} widget;
// ============================================================
// 用户区域(自由定义)
// ============================================================
// Set 2: 用户纹理(可选)
layout(set = 2, binding = 0) uniform sampler2D u_texture;
void main() {
frag_color = texture(u_texture, v_uv) * v_color;
frag_color.a *= widget.u_opacity;
}
```
---
## 5. 编译系统
### 5.1 编译流程
```
用户 GLSL 着色器
glslangValidator -V
SPIR-V
spirv-cross (可选反射)
```
### 5.2 CMake 集成
```cmake
# cmake/shader_compile.cmake
function(compile_glsl_shader INPUT OUTPUT)
get_filename_component(INPUT_NAME ${INPUT} NAME)
add_custom_command(
OUTPUT ${OUTPUT}
COMMAND glslangValidator -V ${INPUT} -o ${OUTPUT}
DEPENDS ${INPUT}
COMMENT "Compiling GLSL shader: ${INPUT_NAME}"
VERBATIM
)
endfunction()
# 批量编译着色器
function(compile_glsl_shaders TARGET_NAME SHADER_DIR OUTPUT_DIR)
file(GLOB_RECURSE SHADER_FILES
"${SHADER_DIR}/*.vert"
"${SHADER_DIR}/*.frag"
"${SHADER_DIR}/*.comp"
)
set(SPIRV_FILES)
foreach(SHADER ${SHADER_FILES})
get_filename_component(SHADER_NAME ${SHADER} NAME)
set(SPIRV_FILE "${OUTPUT_DIR}/${SHADER_NAME}.spv")
compile_glsl_shader(${SHADER} ${SPIRV_FILE})
list(APPEND SPIRV_FILES ${SPIRV_FILE})
endforeach()
add_custom_target(${TARGET_NAME} DEPENDS ${SPIRV_FILES})
endfunction()
```
### 5.3 目录结构
```
src/shader/shaders/glsl/
├── framework/ # 框架内置着色器
│ ├── quad.vert # 四边形顶点着色器
│ └── fullscreen.vert # 全屏顶点着色器
├── templates/ # 用户模板(复制使用)
│ ├── procedural.frag.template
│ ├── backdrop.frag.template
│ ├── mask.frag.template
│ └── mesh.vert.template
└── examples/ # 示例着色器
├── gradient.frag # 渐变效果
├── blur.frag # 模糊效果
└── rounded_rect.frag # 圆角矩形
```
---
## 6. C++ 端实现
### 6.1 框架 UBO 结构
```cpp
namespace mirai {
struct alignas(16) widget_bounds_ubo {
float bounds[4]; // x, y, width, height
float opacity;
float _pad[3];
};
static_assert(sizeof(widget_bounds_ubo) == 32);
} // namespace mirai
```
### 6.2 描述符集布局创建
```cpp
namespace mirai {
// 通用模式 Set 0 布局
inline vk::UniqueDescriptorSetLayout create_set0_layout_common(vk::Device device) {
vk::DescriptorSetLayoutBinding binding{
0, vk::DescriptorType::eUniformBuffer, 1,
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment
};
return device.createDescriptorSetLayoutUnique({{}, 1, &binding});
}
// Backdrop 模式 Set 0 布局
inline vk::UniqueDescriptorSetLayout create_set0_layout_backdrop(vk::Device device) {
std::array<vk::DescriptorSetLayoutBinding, 2> bindings = {{
{0, vk::DescriptorType::eUniformBuffer, 1,
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{1, vk::DescriptorType::eCombinedImageSampler, 1,
vk::ShaderStageFlagBits::eFragment}
}};
return device.createDescriptorSetLayoutUnique({{}, bindings});
}
} // namespace mirai
```
### 6.3 Push Constant 范围
```cpp
// 用户 Push Constant 范围128 bytes
vk::PushConstantRange user_push_constant_range{
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
0, // offset
128 // size (最大)
};
```
---
## 7. 整体架构图
### 7.1 系统架构总览
```mermaid
flowchart TB
subgraph BuildTime[构建时]
A[GLSL 源文件] --> B[glslangValidator]
B --> C[SPIR-V 字节码]
end
subgraph Runtime[运行时]
C --> D[shader_library]
D --> E[shader_module]
subgraph ShaderWidgets[Shader Widget 层]
F[shader_widget 基类]
G[backdrop_widget]
H[procedural_widget]
I[mask_widget]
J[custom_mesh_widget]
F --> G
F --> H
F --> I
F --> J
end
E --> F
subgraph Resources[资源管理]
K[gpu_allocator]
L[descriptor_pool]
end
F --> K
F --> L
subgraph Rendering[渲染管线]
M[pipeline]
N[command_buffer]
end
F --> M
M --> N
end
```
### 7.2 数据流
```mermaid
sequenceDiagram
participant User as 用户代码
participant Widget as shader_widget
participant UBO as Uniform Buffer
participant PC as Push Constant
participant GPU as GPU
User->>Widget: 设置参数
Widget->>UBO: 更新 Set 1 UBO
Widget->>PC: 更新 Push Constant
loop 每帧渲染
Widget->>GPU: vkCmdBindPipeline
Widget->>GPU: vkCmdBindDescriptorSets (Set 0, 1, 2)
Widget->>GPU: vkCmdPushConstants
Widget->>GPU: vkCmdDraw
end
```
---
## 8. 资源分配总结
### 8.1 资源归属表
| 资源 | 归属 | 说明 |
|------|------|------|
| Set 0 | 框架 | WidgetBounds + Backdrop纹理 |
| Set 1 | 用户 | 用户 UBO |
| Set 2 | 用户 | 用户纹理 |
| Push Constant | 用户 | 最大 128 bytes |
| 顶点着色器 | 框架 | Custom Mesh 除外 |
| 片段着色器 | 用户 | 所有模式 |
### 8.2 框架提供的数据
| 数据 | 位置 | 类型 | 说明 |
|------|------|------|------|
| u_bounds | Set 0, Binding 0 | vec4 | Widget 边界 (x, y, w, h) |
| u_opacity | Set 0, Binding 0 | float | 透明度 (0-1) |
| u_backdrop | Set 0, Binding 1 | sampler2D | 背景纹理(仅 Backdrop 模式) |
### 8.3 框架顶点着色器输出
| 输出 | location | 类型 | 说明 |
|------|----------|------|------|
| v_uv | 0 | vec2 | UV 坐标 (0-1) |
| v_local_pos | 1 | vec2 | 局部坐标 (0-1) |
---
## 9. 相关文档
- [ADR-003: 迁移到 GLSL 着色器系统](adr/adr-003-glsl-shader-system.md) - 架构决策记录
- [GLSL 着色器系统设计](../plans/glsl_shader_system_design.md) - 详细设计文档
- [Shader Widget 使用示例](shader_widget_examples.md) - 使用示例

View File

@@ -0,0 +1,842 @@
# MIRAI Shader Widget 使用示例
本文档提供 MIRAI 框架着色器控件系统的详细使用示例,涵盖四种渲染模式和自定义控件开发。
> **注意**:本文档使用标准 GLSL 着色器语言。详见 [ADR-003: 迁移到 GLSL 着色器系统](adr/adr-003-glsl-shader-system.md)。
## 目录
1. [Procedural 模式示例](#1-procedural-模式示例)
2. [Backdrop 模式示例](#2-backdrop-模式示例)
3. [Mask 模式示例](#3-mask-模式示例)
4. [Custom Mesh 模式示例](#4-custom-mesh-模式示例)
5. [Push Constant 使用示例](#5-push-constant-使用示例)
6. [用户 UBO 使用示例](#6-用户-ubo-使用示例)
---
## 1. Procedural 模式示例
Procedural 模式用于程序化渲染,基于坐标生成图形。框架提供顶点着色器,用户只需编写片段着色器。
### 1.1 线性渐变效果
使用 Push Constant 传递颜色和角度参数:
```glsl
#version 450
// ============================================================
// 线性渐变效果 (Procedural 模式)
// ============================================================
// 框架输入
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
// Push Constant: 渐变参数
layout(push_constant) uniform PushConstants {
vec4 color_start; // 起始颜色
vec4 color_end; // 结束颜色
float angle; // 渐变角度(弧度)
float _pad[3];
} pc;
void main() {
// 计算渐变方向向量
vec2 dir = vec2(cos(pc.angle), sin(pc.angle));
// 计算当前位置在渐变方向上的投影
float t = dot(v_local_pos - 0.5, dir) + 0.5;
// 线性插值颜色
frag_color = mix(pc.color_start, pc.color_end, clamp(t, 0.0, 1.0));
// 应用框架透明度
frag_color.a *= widget.u_opacity;
}
```
### 1.2 径向渐变效果
```glsl
#version 450
// 框架输入
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
// Push Constant: 径向渐变参数
layout(push_constant) uniform PushConstants {
vec4 color_center; // 中心颜色
vec4 color_edge; // 边缘颜色
vec2 center; // 渐变中心 (0-1)
float radius; // 渐变半径
float _pad;
} pc;
void main() {
// 计算到中心的距离
float dist = length(v_local_pos - pc.center) / pc.radius;
// 径向插值
frag_color = mix(pc.color_center, pc.color_edge, clamp(dist, 0.0, 1.0));
frag_color.a *= widget.u_opacity;
}
```
### 1.3 棋盘格图案
```glsl
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
layout(push_constant) uniform PushConstants {
vec4 color_a; // 颜色 A
vec4 color_b; // 颜色 B
float grid_size; // 格子大小
float _pad[3];
} pc;
void main() {
// 计算格子坐标
vec2 grid = floor(v_local_pos * widget.u_bounds.zw / pc.grid_size);
// 棋盘格模式
float checker = mod(grid.x + grid.y, 2.0);
frag_color = mix(pc.color_a, pc.color_b, checker);
frag_color.a *= widget.u_opacity;
}
```
---
## 2. Backdrop 模式示例
Backdrop 模式用于后处理效果,框架提供背景纹理 `u_backdrop`
### 2.1 高斯模糊效果
使用用户 UBO (Set 1) 传递模糊参数:
```glsl
#version 450
// ============================================================
// 高斯模糊效果 (Backdrop 模式)
// ============================================================
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
// 背景纹理(框架提供)
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
// Set 1: 用户 UBO - 模糊参数
layout(set = 1, binding = 0, std140) uniform BlurParams {
float sigma; // 高斯分布标准差
int samples; // 采样半径
vec2 texel_size; // 纹理像素大小 (1.0/width, 1.0/height)
} params;
void main() {
vec4 color = vec4(0.0);
float total_weight = 0.0;
// 高斯模糊核
float sigma2 = params.sigma * params.sigma;
float inv_2sigma2 = 1.0 / (2.0 * sigma2);
// 双重循环采样
for (int i = -params.samples; i <= params.samples; i++) {
for (int j = -params.samples; j <= params.samples; j++) {
vec2 offset = vec2(float(i), float(j)) * params.texel_size;
// 计算高斯权重
float dist2 = float(i * i + j * j);
float weight = exp(-dist2 * inv_2sigma2);
color += texture(u_backdrop, v_uv + offset) * weight;
total_weight += weight;
}
}
frag_color = color / total_weight;
frag_color.a *= widget.u_opacity;
}
```
### 2.2 色彩调整效果
```glsl
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
// Push Constant: 色彩调整参数
layout(push_constant) uniform PushConstants {
float brightness; // 亮度 (-1 to 1)
float contrast; // 对比度 (0 to 2)
float saturation; // 饱和度 (0 to 2)
float _pad;
} pc;
void main() {
vec4 color = texture(u_backdrop, v_uv);
// 亮度调整
color.rgb += pc.brightness;
// 对比度调整
color.rgb = (color.rgb - 0.5) * pc.contrast + 0.5;
// 饱和度调整
float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
color.rgb = mix(vec3(gray), color.rgb, pc.saturation);
frag_color = color;
frag_color.a *= widget.u_opacity;
}
```
### 2.3 毛玻璃效果
```glsl
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
// Set 1: 模糊参数
layout(set = 1, binding = 0, std140) uniform BlurParams {
float sigma;
int samples;
vec2 texel_size;
} blur;
// Push Constant: 毛玻璃参数
layout(push_constant) uniform PushConstants {
vec4 tint_color; // 着色颜色
float tint_amount; // 着色强度 (0-1)
float _pad[3];
} pc;
void main() {
// 模糊采样
vec4 color = vec4(0.0);
float total = 0.0;
float inv_2sigma2 = 1.0 / (2.0 * blur.sigma * blur.sigma);
for (int i = -blur.samples; i <= blur.samples; i++) {
for (int j = -blur.samples; j <= blur.samples; j++) {
vec2 offset = vec2(float(i), float(j)) * blur.texel_size;
float w = exp(-float(i*i + j*j) * inv_2sigma2);
color += texture(u_backdrop, v_uv + offset) * w;
total += w;
}
}
color /= total;
// 应用着色
color.rgb = mix(color.rgb, pc.tint_color.rgb, pc.tint_amount);
frag_color = color;
frag_color.a *= widget.u_opacity;
}
```
---
## 3. Mask 模式示例
Mask 模式用于创建遮罩效果,通常输出带有 alpha 通道的颜色。
### 3.1 圆角矩形遮罩
使用 SDF有符号距离场生成高质量圆角矩形
```glsl
#version 450
// ============================================================
// 圆角矩形遮罩 (Mask 模式)
// ============================================================
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
// Push Constant: 遮罩参数
layout(push_constant) uniform PushConstants {
vec4 color; // 遮罩颜色
float radius; // 圆角半径(像素)
float _pad[3];
} pc;
// 圆角矩形 SDF
float rounded_rect_sdf(vec2 p, vec2 half_size, float radius) {
vec2 q = abs(p) - half_size + radius;
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius;
}
void main() {
vec2 size = widget.u_bounds.zw;
vec2 half_size = size * 0.5;
// 转换到以中心为原点的坐标系
vec2 p = (v_local_pos - 0.5) * size;
// 计算 SDF
float d = rounded_rect_sdf(p, half_size, pc.radius);
// 抗锯齿
float alpha = 1.0 - smoothstep(-1.0, 1.0, d);
frag_color = pc.color;
frag_color.a *= alpha * widget.u_opacity;
}
```
### 3.2 圆形遮罩
```glsl
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
layout(push_constant) uniform PushConstants {
vec4 color;
vec2 center; // 圆心 (0-1)
float radius; // 半径 (相对于短边)
float softness; // 边缘柔和度
} pc;
void main() {
// 计算宽高比
vec2 size = widget.u_bounds.zw;
float aspect = size.x / size.y;
// 调整坐标以保持圆形
vec2 uv = v_local_pos;
uv.x *= aspect;
vec2 c = pc.center;
c.x *= aspect;
// 计算到圆心的距离
float dist = length(uv - c);
// 计算遮罩
float min_size = min(size.x, size.y);
float r = pc.radius * min_size / size.x; // 调整半径
float alpha = 1.0 - smoothstep(r - pc.softness, r + pc.softness, dist);
frag_color = pc.color;
frag_color.a *= alpha * widget.u_opacity;
}
```
### 3.3 四角独立圆角
```glsl
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
layout(push_constant) uniform PushConstants {
vec4 color;
vec4 radii; // (左上, 右上, 右下, 左下)
} pc;
// 四角独立圆角 SDF
float rounded_rect_sdf_4(vec2 p, vec2 half_size, vec4 radii) {
// 根据象限选择圆角半径
float r = (p.x > 0.0)
? ((p.y > 0.0) ? radii.y : radii.z) // 右上 / 右下
: ((p.y > 0.0) ? radii.x : radii.w); // 左上 / 左下
vec2 q = abs(p) - half_size + r;
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
}
void main() {
vec2 size = widget.u_bounds.zw;
vec2 half_size = size * 0.5;
vec2 p = (v_local_pos - 0.5) * size;
float d = rounded_rect_sdf_4(p, half_size, pc.radii);
float alpha = 1.0 - smoothstep(-1.0, 1.0, d);
frag_color = pc.color;
frag_color.a *= alpha * widget.u_opacity;
}
```
---
## 4. Custom Mesh 模式示例
Custom Mesh 模式需要用户同时提供顶点和片段着色器。
### 4.1 粒子系统
**顶点着色器:**
```glsl
#version 450
// 粒子实例数据
layout(location = 0) in vec3 a_position; // 粒子位置
layout(location = 1) in float a_size; // 粒子大小
layout(location = 2) in vec4 a_color; // 粒子颜色
layout(location = 3) in float a_rotation; // 粒子旋转
layout(location = 0) out vec2 v_uv;
layout(location = 1) out vec4 v_color;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
// Push Constant: 变换矩阵
layout(push_constant) uniform PushConstants {
mat4 view_proj;
} pc;
// 四边形顶点Billboard
const vec2 QUAD_VERTICES[6] = vec2[](
vec2(-0.5, -0.5), vec2(0.5, -0.5), vec2(0.5, 0.5),
vec2(-0.5, -0.5), vec2(0.5, 0.5), vec2(-0.5, 0.5)
);
const vec2 QUAD_UVS[6] = vec2[](
vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(1.0, 1.0),
vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 1.0)
);
void main() {
// 获取四边形顶点
vec2 quad_pos = QUAD_VERTICES[gl_VertexIndex % 6];
v_uv = QUAD_UVS[gl_VertexIndex % 6];
// 应用旋转
float c = cos(a_rotation);
float s = sin(a_rotation);
mat2 rot = mat2(c, -s, s, c);
quad_pos = rot * quad_pos;
// 应用大小
quad_pos *= a_size;
// 计算世界位置
vec3 world_pos = a_position + vec3(quad_pos, 0.0);
gl_Position = pc.view_proj * vec4(world_pos, 1.0);
v_color = a_color;
}
```
**片段着色器:**
```glsl
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec4 v_color;
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
// Set 2: 粒子纹理(可选)
layout(set = 2, binding = 0) uniform sampler2D u_particle_texture;
void main() {
// 采样粒子纹理
vec4 tex_color = texture(u_particle_texture, v_uv);
frag_color = tex_color * v_color;
frag_color.a *= widget.u_opacity;
}
```
### 4.2 简单圆形粒子(无纹理)
**片段着色器:**
```glsl
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec4 v_color;
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
void main() {
// 计算到中心的距离
vec2 center = v_uv - 0.5;
float dist = length(center) * 2.0;
// 圆形遮罩,带软边缘
float alpha = 1.0 - smoothstep(0.8, 1.0, dist);
frag_color = v_color;
frag_color.a *= alpha * widget.u_opacity;
}
```
---
## 5. Push Constant 使用示例
Push Constant 完全留给用户,最大 128 bytes适合高频更新的参数。
### 5.1 Push Constant 结构设计
```glsl
// 示例 1简单颜色参数 (32 bytes)
layout(push_constant) uniform PushConstants {
vec4 color; // 16 bytes
vec4 secondary; // 16 bytes
} pc;
// 示例 2渐变参数 (48 bytes)
layout(push_constant) uniform PushConstants {
vec4 color_start; // 16 bytes
vec4 color_end; // 16 bytes
float angle; // 4 bytes
float _pad[3]; // 12 bytes (对齐)
} pc;
// 示例 3变换矩阵 (64 bytes)
layout(push_constant) uniform PushConstants {
mat4 transform; // 64 bytes
} pc;
// 示例 4复杂参数 (128 bytes - 最大)
layout(push_constant) uniform PushConstants {
mat4 transform; // 64 bytes
vec4 color; // 16 bytes
vec4 params; // 16 bytes
vec4 extra[2]; // 32 bytes
} pc;
```
### 5.2 C++ 端 Push Constant 更新
```cpp
// 定义与着色器匹配的结构体
struct GradientPushConstants {
float color_start[4];
float color_end[4];
float angle;
float _pad[3];
};
static_assert(sizeof(GradientPushConstants) == 48);
// 更新 Push Constant
void update_gradient(vk::CommandBuffer cmd, vk::PipelineLayout layout) {
GradientPushConstants pc{};
pc.color_start[0] = 1.0f; pc.color_start[1] = 0.0f;
pc.color_start[2] = 0.0f; pc.color_start[3] = 1.0f;
pc.color_end[0] = 0.0f; pc.color_end[1] = 0.0f;
pc.color_end[2] = 1.0f; pc.color_end[3] = 1.0f;
pc.angle = 0.785f; // 45 度
cmd.pushConstants(
layout,
vk::ShaderStageFlagBits::eFragment,
0,
sizeof(pc),
&pc
);
}
```
---
## 6. 用户 UBO 使用示例
用户 UBO 使用 Set 1适合不频繁更新的参数。
### 6.1 UBO 结构设计
```glsl
// Set 1: 用户 UBO
layout(set = 1, binding = 0, std140) uniform MyParams {
// 注意 std140 布局规则:
// - vec4 对齐到 16 bytes
// - vec3 对齐到 16 bytes
// - vec2 对齐到 8 bytes
// - float/int 对齐到 4 bytes
// - 数组元素对齐到 16 bytes
vec4 color; // offset: 0, size: 16
vec2 scale; // offset: 16, size: 8
float intensity; // offset: 24, size: 4
float _pad0; // offset: 28, size: 4 (对齐)
vec4 extra_params; // offset: 32, size: 16
} params;
// 总大小: 48 bytes
```
### 6.2 C++ 端 UBO 结构
```cpp
// 必须与着色器中的布局完全匹配
struct alignas(16) MyParamsUBO {
float color[4]; // vec4
float scale[2]; // vec2
float intensity; // float
float _pad0; // padding
float extra_params[4]; // vec4
};
static_assert(sizeof(MyParamsUBO) == 48);
// 更新 UBO
void update_params(buffer& ubo) {
MyParamsUBO data{};
data.color[0] = 1.0f;
data.color[1] = 0.5f;
data.color[2] = 0.0f;
data.color[3] = 1.0f;
data.scale[0] = 2.0f;
data.scale[1] = 2.0f;
data.intensity = 0.8f;
ubo.write(&data, sizeof(data));
}
```
### 6.3 多个 UBO 绑定
```glsl
// 可以在 Set 1 定义多个 UBO
layout(set = 1, binding = 0, std140) uniform MaterialParams {
vec4 albedo;
float roughness;
float metallic;
vec2 _pad;
} material;
layout(set = 1, binding = 1, std140) uniform LightParams {
vec4 light_pos;
vec4 light_color;
float intensity;
float _pad[3];
} light;
```
---
## 7. 完整示例:自定义效果控件
### 7.1 创建波纹效果
**着色器 (ripple.frag)**
```glsl
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
// Set 1: 波纹参数
layout(set = 1, binding = 0, std140) uniform RippleParams {
float time; // 当前时间
float frequency; // 波纹频率
float amplitude; // 波纹振幅
float decay; // 衰减系数
} params;
// Push Constant: 波纹中心和颜色
layout(push_constant) uniform PushConstants {
vec4 color;
vec2 center;
float _pad[2];
} pc;
void main() {
// 计算到中心的距离
float dist = length(v_local_pos - pc.center);
// 计算波纹
float wave = sin(dist * params.frequency - params.time * 3.0);
wave *= params.amplitude * exp(-dist * params.decay);
// 应用波纹到颜色亮度
vec4 color = pc.color;
color.rgb *= 1.0 + wave;
frag_color = color;
frag_color.a *= widget.u_opacity;
}
```
**C++ 使用:**
```cpp
// UBO 结构
struct alignas(16) RippleParamsUBO {
float time;
float frequency;
float amplitude;
float decay;
};
// Push Constant 结构
struct RipplePushConstants {
float color[4];
float center[2];
float _pad[2];
};
class ripple_widget {
public:
void set_center(float x, float y) {
push_constants_.center[0] = x;
push_constants_.center[1] = y;
}
void set_color(float r, float g, float b, float a) {
push_constants_.color[0] = r;
push_constants_.color[1] = g;
push_constants_.color[2] = b;
push_constants_.color[3] = a;
}
void update(float delta_time) {
params_.time += delta_time;
ubo_->write(&params_, sizeof(params_));
}
void render(vk::CommandBuffer cmd) {
cmd.pushConstants(
pipeline_layout_,
vk::ShaderStageFlagBits::eFragment,
0,
sizeof(push_constants_),
&push_constants_
);
// ... 绑定管线和描述符集,绘制
}
private:
RippleParamsUBO params_{0.0f, 10.0f, 0.1f, 2.0f};
RipplePushConstants push_constants_{};
std::unique_ptr<buffer> ubo_;
vk::PipelineLayout pipeline_layout_;
};
```
---
## 8. 相关文档
- [Shader Widget 架构设计](shader_widget_architecture.md) - 详细的架构设计文档
- [ADR-003: 迁移到 GLSL 着色器系统](adr/adr-003-glsl-shader-system.md) - 架构决策记录
- [GLSL 着色器系统设计](../plans/glsl_shader_system_design.md) - 详细设计文档

View File

@@ -0,0 +1,598 @@
# MIRAI GLSL 着色器系统架构设计
## 1. 系统架构概述
### 1.1 设计目标
1. **从 Slang 迁移到 GLSL**:使用标准 GLSL
2. **Push Constant 完全留给用户**:框架不使用
3. **框架数据最小化**:只提供 bounds 和 opacity
4. **简单约定**:提前约定框架和用户各自使用的资源
5. **无 include 机制**:提供模板让用户复制
### 1.2 四种渲染模式
| 模式 | 顶点着色器 | 片段着色器 | 说明 |
|------|-----------|-----------|------|
| Backdrop | 框架提供 | 用户提供 | 后处理效果,框架提供背景纹理 |
| Procedural | 框架提供 | 用户提供 | 程序化生成 |
| Mask | 框架提供 | 用户提供 | 遮罩效果 |
| Custom Mesh | 用户提供 | 用户提供 | 自定义网格渲染 |
---
## 2. 资源分配约定
```
┌─────────────────────────────────────────────────────────────┐
│ 资源分配约定表 │
├─────────────────┬───────────────────────────────────────────┤
│ 资源类型 │ 分配 │
├─────────────────┼───────────────────────────────────────────┤
│ Set 0 │ 框架专用(用户不要使用) │
│ Set 1 │ 用户 UBO │
│ Set 2 │ 用户纹理 │
│ Push Constant │ 用户专用(最大 128 bytes
├─────────────────┼───────────────────────────────────────────┤
│ 顶点着色器 │ 框架提供Custom Mesh 除外) │
│ 片段着色器 │ 用户提供 │
└─────────────────┴───────────────────────────────────────────┘
```
---
## 3. Set 0框架专用用户不要使用
### 3.1 通用模式Procedural, Mask
```glsl
// ============================================================
// Set 0: 框架专用 - 用户不要在 Set 0 定义任何资源
// ============================================================
// Binding 0: Widget 边界
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds; // (x, y, width, height)
float u_opacity; // 0.0 - 1.0
float _pad0;
float _pad1;
float _pad2;
} widget;
// 大小: 32 bytes
```
### 3.2 Backdrop 模式
```glsl
// Binding 0: Widget 边界(同上)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0;
float _pad1;
float _pad2;
} widget;
// Binding 1: 背景纹理(框架自动绑定)
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
```
---
## 4. 框架内置顶点着色器
框架为 Backdrop/Procedural/Mask 模式提供内置顶点着色器,用户无需编写。
### 4.1 内置顶点着色器输出
框架的顶点着色器会输出以下数据给片段着色器:
```glsl
// 框架顶点着色器输出(用户片段着色器的输入)
layout(location = 0) in vec2 v_uv; // UV 坐标 (0-1)
layout(location = 1) in vec2 v_local_pos; // 局部坐标 (0-1),相对于 widget bounds
```
### 4.2 框架内置顶点着色器实现(参考)
```glsl
// 框架内部实现,用户无需关心
#version 450
layout(location = 0) in vec2 a_position;
layout(location = 1) in vec2 a_uv;
layout(location = 0) out vec2 v_uv;
layout(location = 1) out vec2 v_local_pos;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0;
float _pad1;
float _pad2;
} widget;
void main() {
// 将顶点位置转换到 NDC
vec2 pos = a_position * widget.u_bounds.zw + widget.u_bounds.xy;
vec2 ndc = pos * 2.0 - 1.0;
gl_Position = vec4(ndc, 0.0, 1.0);
v_uv = a_uv;
v_local_pos = a_position;
}
```
---
## 5. 用户片段着色器模板
### 5.1 Procedural 模式模板
用户只需编写片段着色器,复制以下模板:
```glsl
#version 450
// ============================================================
// 框架输入(不要修改)
// ============================================================
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用(不要修改)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds; // (x, y, width, height)
float u_opacity;
float _pad0;
float _pad1;
float _pad2;
} widget;
// ============================================================
// 用户区域(自由定义)
// ============================================================
// Set 1: 用户 UBO可选
layout(set = 1, binding = 0, std140) uniform MyParams {
float time;
// ... 自定义参数
} params;
// Set 2: 用户纹理(可选)
// layout(set = 2, binding = 0) uniform sampler2D u_my_texture;
// Push Constant可选最大 128 bytes
layout(push_constant) uniform PushConstants {
vec4 color;
// ... 自定义参数
} pc;
// ============================================================
// 主函数
// ============================================================
void main() {
// 用户实现...
frag_color = pc.color;
frag_color.a *= widget.u_opacity;
}
```
### 5.2 Backdrop 模式模板
```glsl
#version 450
// ============================================================
// 框架输入(不要修改)
// ============================================================
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用(不要修改)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0;
float _pad1;
float _pad2;
} widget;
// 背景纹理(框架提供)
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
// ============================================================
// 用户区域(自由定义)
// ============================================================
// Set 1: 用户 UBO可选
layout(set = 1, binding = 0, std140) uniform BlurParams {
float radius;
int samples;
vec2 texel_size;
} params;
// Push Constant可选
layout(push_constant) uniform PushConstants {
// ... 自定义参数
} pc;
// ============================================================
// 主函数
// ============================================================
void main() {
// 采样背景纹理
vec4 color = texture(u_backdrop, v_uv);
// 用户处理...
frag_color = color;
frag_color.a *= widget.u_opacity;
}
```
### 5.3 Mask 模式模板
```glsl
#version 450
// ============================================================
// 框架输入(不要修改)
// ============================================================
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用(不要修改)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0;
float _pad1;
float _pad2;
} widget;
// ============================================================
// 用户区域(自由定义)
// ============================================================
// Push Constant可选
layout(push_constant) uniform PushConstants {
vec4 color;
float corner_radius;
float _pad[3];
} pc;
// ============================================================
// 主函数
// ============================================================
void main() {
// 计算遮罩(示例:圆角矩形)
vec2 size = widget.u_bounds.zw;
vec2 p = (v_local_pos - 0.5) * size;
vec2 q = abs(p) - size * 0.5 + pc.corner_radius;
float d = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - pc.corner_radius;
float mask = 1.0 - smoothstep(-1.0, 1.0, d);
frag_color = pc.color;
frag_color.a *= mask * widget.u_opacity;
}
```
### 5.4 Custom Mesh 模式模板
Custom Mesh 模式需要用户同时提供顶点和片段着色器:
**顶点着色器:**
```glsl
#version 450
// ============================================================
// 用户定义顶点输入
// ============================================================
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec2 a_uv;
layout(location = 2) in vec4 a_color;
layout(location = 0) out vec2 v_uv;
layout(location = 1) out vec4 v_color;
// Set 0: 框架专用(不要修改)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0;
float _pad1;
float _pad2;
} widget;
// ============================================================
// 用户区域(自由定义)
// ============================================================
// Push Constant
layout(push_constant) uniform PushConstants {
mat4 mvp;
} pc;
// ============================================================
// 主函数
// ============================================================
void main() {
gl_Position = pc.mvp * vec4(a_position, 1.0);
v_uv = a_uv;
v_color = a_color;
}
```
**片段着色器:**
```glsl
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec4 v_color;
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用(不要修改)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0;
float _pad1;
float _pad2;
} widget;
// ============================================================
// 用户区域(自由定义)
// ============================================================
// Set 2: 用户纹理(可选)
layout(set = 2, binding = 0) uniform sampler2D u_texture;
void main() {
frag_color = texture(u_texture, v_uv) * v_color;
frag_color.a *= widget.u_opacity;
}
```
---
## 6. 示例着色器
### 6.1 纯色填充Procedural
```glsl
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
layout(push_constant) uniform PC {
vec4 color;
} pc;
void main() {
frag_color = pc.color;
frag_color.a *= widget.u_opacity;
}
```
### 6.2 线性渐变Procedural
```glsl
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
layout(push_constant) uniform PC {
vec4 color_start;
vec4 color_end;
float angle;
float _pad[3];
} pc;
void main() {
vec2 dir = vec2(cos(pc.angle), sin(pc.angle));
float t = dot(v_local_pos - 0.5, dir) + 0.5;
frag_color = mix(pc.color_start, pc.color_end, clamp(t, 0.0, 1.0));
frag_color.a *= widget.u_opacity;
}
```
### 6.3 高斯模糊Backdrop
```glsl
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
layout(set = 1, binding = 0, std140) uniform BlurParams {
float sigma;
int samples;
vec2 texel_size;
} params;
void main() {
vec4 color = vec4(0.0);
float total = 0.0;
for (int i = -params.samples; i <= params.samples; i++) {
for (int j = -params.samples; j <= params.samples; j++) {
vec2 offset = vec2(i, j) * params.texel_size;
float w = exp(-(float(i*i + j*j)) / (2.0 * params.sigma * params.sigma));
color += texture(u_backdrop, v_uv + offset) * w;
total += w;
}
}
frag_color = color / total;
frag_color.a *= widget.u_opacity;
}
```
### 6.4 圆角矩形Mask
```glsl
#version 450
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
layout(push_constant) uniform PC {
vec4 color;
float radius;
float _pad[3];
} pc;
void main() {
vec2 size = widget.u_bounds.zw;
vec2 half_size = size * 0.5;
vec2 p = (v_local_pos - 0.5) * size;
vec2 q = abs(p) - half_size + pc.radius;
float d = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - pc.radius;
float alpha = 1.0 - smoothstep(-1.0, 1.0, d);
frag_color = pc.color;
frag_color.a *= alpha * widget.u_opacity;
}
```
---
## 7. C++ 端实现Vulkan HPP
### 7.1 框架 UBO 结构
```cpp
namespace mirai {
struct alignas(16) widget_bounds_ubo {
float bounds[4]; // x, y, width, height
float opacity;
float _pad[3];
};
static_assert(sizeof(widget_bounds_ubo) == 32);
} // namespace mirai
```
### 7.2 描述符集布局创建
```cpp
namespace mirai {
// 通用模式 Set 0 布局
inline vk::UniqueDescriptorSetLayout create_set0_layout_common(vk::Device device) {
vk::DescriptorSetLayoutBinding binding{
0, vk::DescriptorType::eUniformBuffer, 1,
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment
};
return device.createDescriptorSetLayoutUnique({{}, 1, &binding});
}
// Backdrop 模式 Set 0 布局
inline vk::UniqueDescriptorSetLayout create_set0_layout_backdrop(vk::Device device) {
std::array<vk::DescriptorSetLayoutBinding, 2> bindings = {{
{0, vk::DescriptorType::eUniformBuffer, 1,
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{1, vk::DescriptorType::eCombinedImageSampler, 1,
vk::ShaderStageFlagBits::eFragment}
}};
return device.createDescriptorSetLayoutUnique({{}, bindings});
}
} // namespace mirai
```
---
## 8. 编译系统
### 8.1 编译流程
```
用户 GLSL 片段着色器 → glslangValidator → SPIR-V → spirv-cross(反射)
```
### 8.2 CMake 集成
```cmake
function(compile_glsl_shader INPUT OUTPUT)
add_custom_command(
OUTPUT ${OUTPUT}
COMMAND glslangValidator -V ${INPUT} -o ${OUTPUT}
DEPENDS ${INPUT}
COMMENT "Compiling ${INPUT}"
)
endfunction()
```
---
## 9. 总结
### 资源分配
| 资源 | 归属 | 说明 |
|------|------|------|
| Set 0 | 框架 | WidgetBounds + Backdrop纹理 |
| Set 1 | 用户 | 用户 UBO |
| Set 2 | 用户 | 用户纹理 |
| Push Constant | 用户 | 最大 128 bytes |
| 顶点着色器 | 框架 | Custom Mesh 除外 |
| 片段着色器 | 用户 | 所有模式 |
### 框架提供的数据
| 数据 | 位置 | 类型 |
|------|------|------|
| u_bounds | Set 0, Binding 0 | vec4 |
| u_opacity | Set 0, Binding 0 | float |
| u_backdrop | Set 0, Binding 1 | sampler2D (仅 Backdrop) |
### 框架顶点着色器输出
| 输出 | location | 类型 | 说明 |
|------|----------|------|------|
| v_uv | 0 | vec2 | UV 坐标 (0-1) |
| v_local_pos | 1 | vec2 | 局部坐标 (0-1) |

View File

@@ -7,12 +7,12 @@ project(mirai_shader)
simple_library(STATIC)
target_link_libraries(${PROJECT_NAME} PUBLIC mirai_core mirai_render mirai_resource Vulkan::Vulkan)
# 添加着色器编译到现有目标
# 自动搜索 shaders 目录下的所有 .slang 文件
# shaders/common 和 shaders/math 用于着色器模块引用
add_shader_library(TARGET ${PROJECT_NAME}
SHADER_PATH shaders
REF_PATHS shaders/common shaders/math
# 添加 GLSL 着色器编译到现有目标
# 自动搜索 shaders/glsl 目录下的所有 .vert/.frag 文件
add_glsl_shader_library(TARGET ${PROJECT_NAME}
SHADER_PATH shaders/glsl
INCLUDE_DIRS shaders/glsl
RECURSIVE
)
set_target_properties(${PROJECT_NAME} PROPERTIES
@@ -27,4 +27,4 @@ install(TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
message(STATUS "Configured mirai_shader library")
message(STATUS "Configured mirai_shader library with GLSL shaders")

View File

@@ -1,159 +0,0 @@
/**
* @file basic.slang
* @brief 基础 UI 着色器示例
* @description 展示 GUI 渲染中常用的 Uniform Buffer、纹理采样、顶点属性、Push Constants
* 以及多入口点支持(通过 [shader("xxx")] 属性自动检测)
*/
// ============================================================================
// Uniform Buffer 定义
// ============================================================================
/**
* @brief 全局变换 Uniform Buffer
* @set 0
* @binding 0
*/
struct GlobalUniforms {
float4x4 projection; // 正交投影矩阵
float2 screenSize; // 屏幕尺寸
float2 _padding; // 对齐填充
};
[[vk::binding(0, 0)]]
ConstantBuffer<GlobalUniforms> globals;
// ============================================================================
// 纹理和采样器
// ============================================================================
/**
* @brief UI 纹理(图标、图片等)
*/
[[vk::binding(0, 1)]]
Texture2D<float4> uiTexture;
/**
* @brief 线性采样器
*/
[[vk::binding(1, 1)]]
SamplerState linearSampler;
/**
* @brief 计算着色器用的输入纹理
*/
[[vk::binding(0, 2)]]
RWTexture2D<float4> computeInput;
/**
* @brief 计算着色器用的输出纹理
*/
[[vk::binding(1, 2)]]
RWTexture2D<float4> computeOutput;
// ============================================================================
// Push Constants
// ============================================================================
/**
* @brief 每次绘制调用的参数
*/
struct PushConstants {
float4 color; // 顶点颜色乘数
float2 offset; // 位置偏移
float opacity; // 不透明度
uint useTexture; // 是否使用纹理 (0 或 1)
};
[[vk::push_constant]]
PushConstants pc;
/**
* @brief 计算着色器的 Push Constants
*/
struct ComputePushConstants {
uint2 workgroupSize; // 工作组大小
uint2 workgroupCount; // 工作组数量
float intensity; // 强度参数
};
[[vk::push_constant]]
ComputePushConstants computePc;
// ============================================================================
// 顶点输入输出结构
// ============================================================================
/**
* @brief 顶点着色器输入
*/
struct VertexInput {
[[vk::location(0)]] float2 position : POSITION; // 2D 位置
[[vk::location(1)]] float2 texCoord : TEXCOORD0; // 纹理坐标
[[vk::location(2)]] float4 color : COLOR0; // 顶点颜色
};
/**
* @brief 顶点着色器输出
*/
struct VertexOutput {
float4 position : SV_Position;
float2 texCoord : TEXCOORD0;
float4 color : COLOR0;
};
// ============================================================================
// 顶点着色器
// ============================================================================
[shader("vertex")]
VertexOutput vertexMain(VertexInput input) {
VertexOutput output;
// 应用偏移并变换到裁剪空间
float2 pos = input.position + pc.offset;
output.position = mul(globals.projection, float4(pos, 0.0f, 1.0f));
// 传递纹理坐标和颜色
output.texCoord = input.texCoord;
output.color = input.color * pc.color;
return output;
}
// ============================================================================
// 片段着色器
// ============================================================================
[shader("fragment")]
float4 fragmentMain(VertexOutput input) : SV_Target {
float4 color = input.color;
// 如果使用纹理,采样并相乘
if (pc.useTexture != 0) {
float4 texColor = uiTexture.Sample(linearSampler, input.texCoord);
color *= texColor;
}
// 应用不透明度
color.a *= pc.opacity;
return color;
}
// ============================================================================
// 计算着色器
// ============================================================================
/**
* @brief 简单的图像处理计算着色器
* @description 演示多入口点支持 - 自动检测 [shader("compute")]
*/
[shader("compute")]
void computeMain(uint3 globalID : SV_DispatchThreadID) {
// 简单的图像处理:应用强度
float4 color = computeInput[globalID.xy];
color.rgb *= computePc.intensity;
color.a = 1.0f;
computeOutput[globalID.xy] = color;
}

View File

@@ -0,0 +1,60 @@
#version 450
// ============================================================
// 高斯模糊效果示例 (Backdrop 模式)
// 使用用户 UBO (Set 1) 传递模糊参数
// ============================================================
// 框架输入
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
// 背景纹理(框架提供)
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
// Set 1: 用户 UBO - 模糊参数
layout(set = 1, binding = 0, std140) uniform BlurParams {
float sigma; // 高斯分布标准差
int samples; // 采样半径(实际采样数为 (2*samples+1)^2
vec2 texel_size; // 纹理像素大小 (1.0/width, 1.0/height)
} params;
void main() {
vec4 color = vec4(0.0);
float total_weight = 0.0;
// 高斯模糊核
float sigma2 = params.sigma * params.sigma;
float inv_2sigma2 = 1.0 / (2.0 * sigma2);
// 双重循环采样
for (int i = -params.samples; i <= params.samples; i++) {
for (int j = -params.samples; j <= params.samples; j++) {
// 计算偏移
vec2 offset = vec2(float(i), float(j)) * params.texel_size;
// 计算高斯权重
float dist2 = float(i * i + j * j);
float weight = exp(-dist2 * inv_2sigma2);
// 累加采样
color += texture(u_backdrop, v_uv + offset) * weight;
total_weight += weight;
}
}
// 归一化
frag_color = color / total_weight;
// 应用框架透明度
frag_color.a *= widget.u_opacity;
}

View File

@@ -0,0 +1,42 @@
#version 450
// ============================================================
// 线性渐变效果示例 (Procedural 模式)
// 使用 Push Constant 传递颜色和角度
// ============================================================
// 框架输入
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
// Push Constant: 渐变参数
layout(push_constant) uniform PushConstants {
vec4 color_start; // 起始颜色
vec4 color_end; // 结束颜色
float angle; // 渐变角度(弧度)
float _pad[3];
} pc;
void main() {
// 计算渐变方向向量
vec2 dir = vec2(cos(pc.angle), sin(pc.angle));
// 计算当前位置在渐变方向上的投影
// v_local_pos 范围是 0-1将其转换到 -0.5 到 0.5 范围
float t = dot(v_local_pos - 0.5, dir) + 0.5;
// 线性插值颜色
frag_color = mix(pc.color_start, pc.color_end, clamp(t, 0.0, 1.0));
// 应用框架透明度
frag_color.a *= widget.u_opacity;
}

View File

@@ -0,0 +1,53 @@
#version 450
// ============================================================
// 圆角矩形遮罩示例 (Mask 模式)
// 使用 Push Constant 传递圆角半径
// ============================================================
// 框架输入
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds;
float u_opacity;
float _pad0, _pad1, _pad2;
} widget;
// Push Constant: 遮罩参数
layout(push_constant) uniform PushConstants {
vec4 color; // 遮罩颜色
float radius; // 圆角半径(像素)
float _pad[3];
} pc;
// 计算圆角矩形的有符号距离场 (SDF)
float rounded_rect_sdf(vec2 p, vec2 half_size, float radius) {
vec2 q = abs(p) - half_size + radius;
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius;
}
void main() {
// 获取 widget 尺寸
vec2 size = widget.u_bounds.zw;
vec2 half_size = size * 0.5;
// 将局部坐标转换到以中心为原点的坐标系
// v_local_pos 范围是 0-1转换到 -half_size 到 half_size
vec2 p = (v_local_pos - 0.5) * size;
// 计算 SDF
float d = rounded_rect_sdf(p, half_size, pc.radius);
// 使用 smoothstep 进行抗锯齿
// d < 0 表示在形状内部d > 0 表示在形状外部
float alpha = 1.0 - smoothstep(-1.0, 1.0, d);
// 输出遮罩颜色
frag_color = pc.color;
frag_color.a *= alpha * widget.u_opacity;
}

View File

@@ -0,0 +1,41 @@
#version 450
// ============================================================
// 框架内置全屏顶点着色器
// 用于全屏后处理效果
// ============================================================
// 无顶点输入 - 使用 gl_VertexIndex 生成全屏三角形
// 输出到片段着色器
layout(location = 0) out vec2 v_uv;
layout(location = 1) out vec2 v_local_pos;
// 框架 UBO - Set 0 专用
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds; // (x, y, width, height) - 归一化屏幕坐标
float u_opacity; // 0.0 - 1.0
float _pad0;
float _pad1;
float _pad2;
} widget;
void main() {
// 生成覆盖整个屏幕的三角形
// 使用一个大三角形覆盖整个视口,比两个三角形更高效
vec2 positions[3] = vec2[3](
vec2(-1.0, -1.0),
vec2( 3.0, -1.0),
vec2(-1.0, 3.0)
);
vec2 uvs[3] = vec2[3](
vec2(0.0, 0.0),
vec2(2.0, 0.0),
vec2(0.0, 2.0)
);
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
v_uv = uvs[gl_VertexIndex];
v_local_pos = uvs[gl_VertexIndex];
}

View File

@@ -0,0 +1,38 @@
#version 450
// ============================================================
// 框架内置四边形顶点着色器
// 用于 Backdrop/Procedural/Mask 模式
// ============================================================
// 顶点输入
layout(location = 0) in vec2 a_position; // 顶点位置 (0-1 范围)
layout(location = 1) in vec2 a_uv; // UV 坐标
// 输出到片段着色器
layout(location = 0) out vec2 v_uv;
layout(location = 1) out vec2 v_local_pos;
// 框架 UBO - Set 0 专用
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds; // (x, y, width, height) - 归一化屏幕坐标
float u_opacity; // 0.0 - 1.0
float _pad0;
float _pad1;
float _pad2;
} widget;
void main() {
// 将顶点位置转换到 NDC
// a_position 是 0-1 范围widget.u_bounds.xy 是左上角位置zw 是宽高
vec2 pos = a_position * widget.u_bounds.zw + widget.u_bounds.xy;
// 转换到 NDC (-1 到 1)
vec2 ndc = pos * 2.0 - 1.0;
// Vulkan Y轴翻转
gl_Position = vec4(ndc.x, -ndc.y, 0.0, 1.0);
v_uv = a_uv;
v_local_pos = a_position;
}

View File

@@ -0,0 +1,62 @@
#version 450
// ============================================================
// Backdrop 模式片段着色器模板
// 用于后处理效果,框架提供背景纹理
// ============================================================
// ============================================================
// 框架输入(不要修改)
// ============================================================
layout(location = 0) in vec2 v_uv; // UV 坐标 (0-1)
layout(location = 1) in vec2 v_local_pos; // 局部坐标 (0-1),相对于 widget bounds
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用(不要修改)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds; // (x, y, width, height)
float u_opacity; // 0.0 - 1.0
float _pad0;
float _pad1;
float _pad2;
} widget;
// 背景纹理(框架提供)
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;
// ============================================================
// 用户区域(自由定义)
// ============================================================
// Set 1: 用户 UBO可选
// layout(set = 1, binding = 0, std140) uniform MyParams {
// float radius;
// int samples;
// vec2 texel_size;
// // ... 自定义参数
// } params;
// Set 2: 用户纹理(可选)
// layout(set = 2, binding = 0) uniform sampler2D u_my_texture;
// Push Constant可选最大 128 bytes
// layout(push_constant) uniform PushConstants {
// // ... 自定义参数
// } pc;
// ============================================================
// 主函数
// ============================================================
void main() {
// 采样背景纹理
vec4 color = texture(u_backdrop, v_uv);
// TODO: 用户实现后处理效果
// 例如:模糊、色彩调整、扭曲等
frag_color = color;
// 应用框架提供的透明度
frag_color.a *= widget.u_opacity;
}

View File

@@ -0,0 +1,62 @@
#version 450
// ============================================================
// Mask 模式片段着色器模板
// 用于遮罩效果,输出单通道 alpha 值
// ============================================================
// ============================================================
// 框架输入(不要修改)
// ============================================================
layout(location = 0) in vec2 v_uv; // UV 坐标 (0-1)
layout(location = 1) in vec2 v_local_pos; // 局部坐标 (0-1),相对于 widget bounds
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用(不要修改)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds; // (x, y, width, height)
float u_opacity; // 0.0 - 1.0
float _pad0;
float _pad1;
float _pad2;
} widget;
// ============================================================
// 用户区域(自由定义)
// ============================================================
// Set 1: 用户 UBO可选
// layout(set = 1, binding = 0, std140) uniform MyParams {
// // ... 自定义参数
// } params;
// Set 2: 用户纹理(可选)
// layout(set = 2, binding = 0) uniform sampler2D u_mask_texture;
// Push Constant可选最大 128 bytes
layout(push_constant) uniform PushConstants {
vec4 color; // 遮罩颜色
float corner_radius; // 圆角半径
float _pad[3];
} pc;
// ============================================================
// 主函数
// ============================================================
void main() {
// TODO: 用户实现遮罩计算
// 示例:圆角矩形遮罩
vec2 size = widget.u_bounds.zw;
vec2 half_size = size * 0.5;
vec2 p = (v_local_pos - 0.5) * size;
vec2 q = abs(p) - half_size + pc.corner_radius;
float d = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - pc.corner_radius;
float mask = 1.0 - smoothstep(-1.0, 1.0, d);
// 输出遮罩颜色alpha 通道包含遮罩值
frag_color = pc.color;
frag_color.a *= mask * widget.u_opacity;
}

View File

@@ -0,0 +1,54 @@
#version 450
// ============================================================
// Custom Mesh 模式顶点着色器模板
// 用于自定义网格渲染,用户需要同时提供顶点和片段着色器
// ============================================================
// ============================================================
// 用户定义顶点输入
// ============================================================
layout(location = 0) in vec3 a_position; // 顶点位置
layout(location = 1) in vec2 a_uv; // UV 坐标
layout(location = 2) in vec4 a_color; // 顶点颜色
// 输出到片段着色器
layout(location = 0) out vec2 v_uv;
layout(location = 1) out vec4 v_color;
// Set 0: 框架专用(不要修改)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds; // (x, y, width, height)
float u_opacity; // 0.0 - 1.0
float _pad0;
float _pad1;
float _pad2;
} widget;
// ============================================================
// 用户区域(自由定义)
// ============================================================
// Set 1: 用户 UBO可选
// layout(set = 1, binding = 0, std140) uniform MyParams {
// mat4 view;
// mat4 projection;
// // ... 自定义参数
// } params;
// Push Constant可选最大 128 bytes
layout(push_constant) uniform PushConstants {
mat4 mvp; // Model-View-Projection 矩阵
} pc;
// ============================================================
// 主函数
// ============================================================
void main() {
// 使用 MVP 矩阵变换顶点位置
gl_Position = pc.mvp * vec4(a_position, 1.0);
// 传递数据到片段着色器
v_uv = a_uv;
v_color = a_color;
}

View File

@@ -0,0 +1,55 @@
#version 450
// ============================================================
// Procedural 模式片段着色器模板
// 用于程序化生成效果
// ============================================================
// ============================================================
// 框架输入(不要修改)
// ============================================================
layout(location = 0) in vec2 v_uv; // UV 坐标 (0-1)
layout(location = 1) in vec2 v_local_pos; // 局部坐标 (0-1),相对于 widget bounds
layout(location = 0) out vec4 frag_color;
// Set 0: 框架专用(不要修改)
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
vec4 u_bounds; // (x, y, width, height)
float u_opacity; // 0.0 - 1.0
float _pad0;
float _pad1;
float _pad2;
} widget;
// ============================================================
// 用户区域(自由定义)
// ============================================================
// Set 1: 用户 UBO可选
// layout(set = 1, binding = 0, std140) uniform MyParams {
// float time;
// // ... 自定义参数
// } params;
// Set 2: 用户纹理(可选)
// layout(set = 2, binding = 0) uniform sampler2D u_my_texture;
// Push Constant可选最大 128 bytes
layout(push_constant) uniform PushConstants {
vec4 color;
// ... 自定义参数
} pc;
// ============================================================
// 主函数
// ============================================================
void main() {
// TODO: 用户实现程序化效果
// 示例:使用 Push Constant 中的颜色
frag_color = pc.color;
// 应用框架提供的透明度
frag_color.a *= widget.u_opacity;
}

View File

@@ -1,200 +0,0 @@
/**
* @file math.slang
* @brief 公共数学函数模块
* @description 提供常用的数学计算函数,供其他着色器模块导入使用
*/
// ============================================================================
// 常量定义
// ============================================================================
static const float PI = 3.14159265358979323846f;
static const float TWO_PI = 6.28318530717958647692f;
static const float HALF_PI = 1.57079632679489661923f;
static const float INV_PI = 0.31830988618379067154f;
static const float EPSILON = 1e-6f;
// ============================================================================
// 基础数学函数
// ============================================================================
/**
* @brief 将值限制在 [0, 1] 范围内
*/
float saturate(float x) {
return clamp(x, 0.0f, 1.0f);
}
float2 saturate(float2 v) {
return clamp(v, float2(0.0f), float2(1.0f));
}
float3 saturate(float3 v) {
return clamp(v, float3(0.0f), float3(1.0f));
}
float4 saturate(float4 v) {
return clamp(v, float4(0.0f), float4(1.0f));
}
/**
* @brief 平滑插值Hermite 插值)
*/
float smoothstep01(float x) {
float t = saturate(x);
return t * t * (3.0f - 2.0f * t);
}
/**
* @brief 更平滑的插值5次多项式
*/
float smootherstep(float x) {
float t = saturate(x);
return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
}
// ============================================================================
// 向量操作
// ============================================================================
/**
* @brief 计算向量的平方长度(避免 sqrt
*/
float lengthSq(float2 v) {
return dot(v, v);
}
float lengthSq(float3 v) {
return dot(v, v);
}
float lengthSq(float4 v) {
return dot(v, v);
}
/**
* @brief 安全的向量归一化(避免除零)
*/
float3 safeNormalize(float3 v) {
float len = length(v);
return len > EPSILON ? v / len : float3(0.0f, 0.0f, 1.0f);
}
/**
* @brief 计算反射向量
*/
float3 reflectVector(float3 incident, float3 normal) {
return incident - 2.0f * dot(incident, normal) * normal;
}
// ============================================================================
// 颜色空间转换
// ============================================================================
/**
* @brief 线性空间到 sRGB
*/
float linearToSrgb(float linear) {
if (linear <= 0.0031308f) {
return linear * 12.92f;
}
return 1.055f * pow(linear, 1.0f / 2.4f) - 0.055f;
}
float3 linearToSrgb(float3 linear) {
return float3(
linearToSrgb(linear.x),
linearToSrgb(linear.y),
linearToSrgb(linear.z)
);
}
/**
* @brief sRGB 到线性空间
*/
float srgbToLinear(float srgb) {
if (srgb <= 0.04045f) {
return srgb / 12.92f;
}
return pow((srgb + 0.055f) / 1.055f, 2.4f);
}
float3 srgbToLinear(float3 srgb) {
return float3(
srgbToLinear(srgb.x),
srgbToLinear(srgb.y),
srgbToLinear(srgb.z)
);
}
/**
* @brief 计算亮度Luminance
*/
float luminance(float3 color) {
return dot(color, float3(0.2126f, 0.7152f, 0.0722f));
}
// ============================================================================
// 矩阵工具函数
// ============================================================================
/**
* @brief 创建绕 Z 轴旋转的 2D 旋转矩阵
*/
float2x2 rotate2D(float angle) {
float c = cos(angle);
float s = sin(angle);
return float2x2(c, -s, s, c);
}
/**
* @brief 创建 2D 缩放矩阵
*/
float2x2 scale2D(float2 scale) {
return float2x2(scale.x, 0.0f, 0.0f, scale.y);
}
// ============================================================================
// 噪声函数
// ============================================================================
/**
* @brief 简单的伪随机哈希函数
*/
float hash11(float p) {
p = frac(p * 0.1031f);
p *= p + 33.33f;
p *= p + p;
return frac(p);
}
float hash21(float2 p) {
float3 p3 = frac(float3(p.x, p.y, p.x) * 0.1031f);
p3 += dot(p3, p3.yzx + 33.33f);
return frac((p3.x + p3.y) * p3.z);
}
float2 hash22(float2 p) {
float3 p3 = frac(float3(p.x, p.y, p.x) * float3(0.1031f, 0.1030f, 0.0973f));
p3 += dot(p3, p3.yzx + 33.33f);
return frac((p3.xx + p3.yz) * p3.zy);
}
/**
* @brief 值噪声
*/
float valueNoise(float2 p) {
float2 i = floor(p);
float2 f = frac(p);
// 四个角的随机值
float a = hash21(i);
float b = hash21(i + float2(1.0f, 0.0f));
float c = hash21(i + float2(0.0f, 1.0f));
float d = hash21(i + float2(1.0f, 1.0f));
// 平滑插值
float2 u = f * f * (3.0f - 2.0f * f);
return lerp(lerp(a, b, u.x), lerp(c, d, u.x), u.y);
}

25
src/widget/CMakeLists.txt Normal file
View File

@@ -0,0 +1,25 @@
# ================================================================================================
# MIRAI Framework - Widget 模块
# 描述: shader_widget 基础类和控件系统
# ================================================================================================
project(mirai_widget)
simple_library(STATIC)
target_link_libraries(${PROJECT_NAME} PUBLIC
mirai_ui
mirai_shader
)
set_target_properties(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
EXPORT_NAME widget
OUTPUT_NAME mirai_widget
)
install(TARGETS ${PROJECT_NAME}
EXPORT mirai-targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
message(STATUS "Configured mirai_widget library")

230
src/widget/effects.hpp Normal file
View File

@@ -0,0 +1,230 @@
/**
* @file effects.hpp
* @brief MIRAI 框架效果控件汇总头文件
* @author MIRAI Team
* @version 0.1.0
*
* 本文件提供所有效果控件的统一包含入口,方便用户一次性包含所有效果。
*
* 包含的效果控件:
* - blur_widget: 高斯模糊效果Backdrop 模式)
* - gradient_widget: 渐变效果Procedural 模式)
* - rounded_rect_mask_widget: 圆角矩形遮罩Mask 模式)
* - particle_widget: 粒子系统Mesh 模式)
*
* @example
* @code
* #include "widget/effects.hpp"
*
* using namespace mirai;
*
* // 创建各种效果控件
* auto blur = std::make_shared<blur_widget>();
* auto gradient = std::make_shared<gradient_widget>();
* auto mask = std::make_shared<rounded_rect_mask_widget>();
* auto particles = std::make_shared<particle_widget>(1000);
* @endcode
*
* @see shader_widget_base
* @see shader_widget
*/
#pragma once
// ============================================================================
// 基础类型和基类
// ============================================================================
#include "widget_types.hpp"
#include "shader_widget_base.hpp"
#include "shader_widget.hpp"
// ============================================================================
// Backdrop 模式效果
// ============================================================================
/**
* @defgroup backdrop_effects Backdrop 模式效果
* @brief 后效处理效果,可采样背景纹理进行处理
* @{
*/
#include "effects/blur_widget.hpp"
/** @} */
// ============================================================================
// Procedural 模式效果
// ============================================================================
/**
* @defgroup procedural_effects Procedural 模式效果
* @brief 程序化渲染效果,基于时间和坐标生成图形
* @{
*/
#include "effects/gradient_widget.hpp"
/** @} */
// ============================================================================
// Mask 模式效果
// ============================================================================
/**
* @defgroup mask_effects Mask 模式效果
* @brief 遮罩效果,用于裁剪和形状遮罩
* @{
*/
#include "effects/rounded_rect_mask_widget.hpp"
/** @} */
// ============================================================================
// Mesh 模式效果
// ============================================================================
/**
* @defgroup mesh_effects Mesh 模式效果
* @brief 自定义网格渲染效果,支持实例化渲染
* @{
*/
#include "effects/particle_widget.hpp"
/** @} */
// ============================================================================
// 便捷类型别名
// ============================================================================
namespace mirai {
/**
* @brief 效果控件类型别名
* @{
*/
/// 模糊效果控件指针类型
using blur_widget_ptr = std::shared_ptr<blur_widget>;
/// 渐变效果控件指针类型
using gradient_widget_ptr = std::shared_ptr<gradient_widget>;
/// 圆角遮罩控件指针类型
using rounded_rect_mask_widget_ptr = std::shared_ptr<rounded_rect_mask_widget>;
/// 粒子系统控件指针类型
using particle_widget_ptr = std::shared_ptr<particle_widget>;
/** @} */
// ============================================================================
// 工厂函数
// ============================================================================
/**
* @brief 效果控件工厂函数
* @{
*/
/**
* @brief 创建模糊效果控件
* @param radius 模糊半径(像素),默认 8.0f
* @param quality 模糊质量,默认 medium
* @return 模糊效果控件指针
*
* @example
* @code
* auto blur = make_blur_widget(10.0f, blur_quality::high);
* @endcode
*/
[[nodiscard]] inline blur_widget_ptr make_blur_widget(
f32 radius = 8.0f,
blur_quality quality = blur_quality::medium
) {
return std::make_shared<blur_widget>(radius, quality);
}
/**
* @brief 创建渐变效果控件
* @param start 起始颜色
* @param end 结束颜色
* @param type 渐变类型,默认 linear
* @return 渐变效果控件指针
*
* @example
* @code
* auto gradient = make_gradient_widget(
* color{1.0f, 0.0f, 0.0f, 1.0f},
* color{0.0f, 0.0f, 1.0f, 1.0f},
* gradient_type::radial
* );
* @endcode
*/
[[nodiscard]] inline gradient_widget_ptr make_gradient_widget(
const color& start = color{1.0f, 1.0f, 1.0f, 1.0f},
const color& end = color{0.0f, 0.0f, 0.0f, 1.0f},
gradient_type type = gradient_type::linear
) {
return std::make_shared<gradient_widget>(start, end, type);
}
/**
* @brief 创建圆角遮罩控件
* @param radius 统一圆角半径,默认 0.0f
* @return 圆角遮罩控件指针
*
* @example
* @code
* auto mask = make_rounded_rect_mask_widget(0.1f);
* @endcode
*/
[[nodiscard]] inline rounded_rect_mask_widget_ptr make_rounded_rect_mask_widget(
f32 radius = 0.0f
) {
return std::make_shared<rounded_rect_mask_widget>(radius);
}
/**
* @brief 创建圆角遮罩控件(四角独立)
* @param top_left 左上角圆角半径
* @param top_right 右上角圆角半径
* @param bottom_right 右下角圆角半径
* @param bottom_left 左下角圆角半径
* @return 圆角遮罩控件指针
*
* @example
* @code
* auto mask = make_rounded_rect_mask_widget(0.1f, 0.1f, 0.2f, 0.2f);
* @endcode
*/
[[nodiscard]] inline rounded_rect_mask_widget_ptr make_rounded_rect_mask_widget(
f32 top_left,
f32 top_right,
f32 bottom_right,
f32 bottom_left
) {
return std::make_shared<rounded_rect_mask_widget>(
top_left, top_right, bottom_right, bottom_left
);
}
/**
* @brief 创建粒子系统控件
* @param count 粒子数量,默认 100
* @return 粒子系统控件指针
*
* @example
* @code
* auto particles = make_particle_widget(1000);
* @endcode
*/
[[nodiscard]] inline particle_widget_ptr make_particle_widget(u32 count = 100) {
return std::make_shared<particle_widget>(count);
}
/** @} */
} // namespace mirai

View File

@@ -0,0 +1,164 @@
/**
* @file blur_widget.hpp
* @brief 高斯模糊效果控件
* @author MIRAI Team
* @version 0.1.0
*
* 本文件定义了 blur_widget 类,实现 Backdrop 模式的高斯模糊效果。
*
* @example
* @code
* auto blur = std::make_shared<blur_widget>();
* blur->set_blur_radius(10.0f);
* blur->set_blur_quality(blur_quality::medium);
* @endcode
*/
#pragma once
#include "shader_widget.hpp"
#include ""
#include <span>
#include <cstdint>
namespace mirai {
// ================================================================================================
// 模糊质量枚举
// ================================================================================================
/**
* @brief 模糊质量等级
*/
enum class blur_quality : u8 {
low = 1, ///< 低质量4 次采样)
medium = 2, ///< 中等质量8 次采样)
high = 3 ///< 高质量12 次采样)
};
// ================================================================================================
// blur_widget 类
// ================================================================================================
/**
* @brief 高斯模糊效果控件
*
* 继承自 backdrop_widget实现对背景的高斯模糊效果。
*
* 特点:
* - 可调节的模糊半径
* - 三档质量选择(低/中/高)
* - 使用分离式高斯模糊算法
*
* @see backdrop_widget
* @see blur_bindings
*/
class blur_widget : public backdrop_widget<blur_bindings> {
public:
MIRAI_OBJECT_TYPE_INFO(blur_widget, backdrop_widget<blur_bindings>)
// ============================================================================================
// 构造与析构
// ============================================================================================
/**
* @brief 默认构造函数
*
* 初始化默认参数:
* - blur_radius = 8.0f
* - quality = medium
*/
blur_widget()
: blur_radius_(8.0f)
, quality_(blur_quality::medium) {
}
/**
* @brief 带参数的构造函数
* @param radius 模糊半径(像素)
* @param quality 模糊质量
*/
explicit blur_widget(f32 radius, blur_quality quality = blur_quality::medium)
: blur_radius_(radius)
, quality_(quality) {
}
/**
* @brief 虚析构函数
*/
~blur_widget() override = default;
// ============================================================================================
// 参数设置
// ============================================================================================
/**
* @brief 设置模糊半径
* @param radius 模糊半径(像素),范围 [0, 100]
*/
void set_blur_radius(f32 radius) {
blur_radius_ = std::clamp(radius, 0.0f, 100.0f);
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取模糊半径
* @return 当前模糊半径
*/
[[nodiscard]] f32 blur_radius() const noexcept { return blur_radius_; }
/**
* @brief 设置模糊质量
* @param quality 模糊质量等级
*/
void set_blur_quality(blur_quality quality) {
quality_ = quality;
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取模糊质量
* @return 当前模糊质量
*/
[[nodiscard]] blur_quality quality() const noexcept { return quality_; }
protected:
// ============================================================================================
// 重写基类方法
// ============================================================================================
/**
* @brief 更新 Uniform Buffer
*
* 将模糊参数写入 GPU 缓冲区。
*/
void update_uniform_buffer() override {
// BlurParams 结构体布局(与 blur.slang 中定义一致)
struct BlurParams {
f32 blur_radius;
f32 blur_quality;
f32 _padding[2];
};
BlurParams params{};
params.blur_radius = blur_radius_;
params.blur_quality = static_cast<f32>(quality_);
// TODO: 将 params 写入 uniform buffer
// uniform_buffer_->write(&params, sizeof(params));
}
private:
// ============================================================================================
// 成员变量
// ============================================================================================
/// 模糊半径(像素)
f32 blur_radius_;
/// 模糊质量
blur_quality quality_;
};
} // namespace mirai

View File

@@ -0,0 +1,372 @@
/**
* @file gradient_widget.hpp
* @brief 渐变效果控件
* @author MIRAI Team
* @version 0.1.0
*
* 本文件定义了 gradient_widget 类,实现 Procedural 模式的渐变效果。
*
* @example
* @code
* auto gradient = std::make_shared<gradient_widget>();
* gradient->set_start_color(color::red());
* gradient->set_end_color(color::blue());
* gradient->set_gradient_type(gradient_type::radial);
* @endcode
*/
#pragma once
#include "shader_widget.hpp"
#include "types/color.h"
#include <span>
#include <cstdint>
#include <cmath>
namespace mirai {
// ================================================================================================
// 渐变类型枚举
// ================================================================================================
/**
* @brief 渐变类型
*/
enum class gradient_type : u32 {
linear = 0, ///< 线性渐变
radial = 1, ///< 径向渐变
angular = 2, ///< 角度渐变(圆锥渐变)
diamond = 3 ///< 菱形渐变
};
// ================================================================================================
// Gradient 绑定类型(模拟代码生成器输出)
// ================================================================================================
/**
* @brief gradient_widget 的着色器绑定类型
*
* 此类型模拟代码生成器的输出,提供:
* - SPIR-V 字节码访问
* - 绑定点常量
* - 静态反射信息
*
* @note 在实际项目中,此类型由 shader_codegen 工具自动生成
*/
struct gradient_bindings {
// ============================================================================================
// 着色器存在性标记
// ============================================================================================
static constexpr bool HAS_VERTEX_SHADER = true;
static constexpr bool HAS_FRAGMENT_SHADER = true;
// ============================================================================================
// Procedural 模式特有标记
// ============================================================================================
static constexpr bool HAS_FRAME_DATA = true;
static constexpr u32 FRAME_DATA_BINDING = 0;
static constexpr u32 FRAME_DATA_SET = 0;
// ============================================================================================
// SPIR-V 数据访问(占位实现)
// ============================================================================================
/**
* @brief 获取顶点着色器 SPIR-V 数据
* @return SPIR-V 字节码的 span
* @note 实际实现中,此数据由编译后的着色器提供
*/
[[nodiscard]] static std::span<const u32> get_vertex_spirv() {
// 占位:实际数据由着色器编译生成
static const u32 placeholder[] = { 0x07230203 }; // SPIR-V magic number
return std::span<const u32>(placeholder);
}
/**
* @brief 获取片段着色器 SPIR-V 数据
* @return SPIR-V 字节码的 span
* @note 实际实现中,此数据由编译后的着色器提供
*/
[[nodiscard]] static std::span<const u32> get_fragment_spirv() {
// 占位:实际数据由着色器编译生成
static const u32 placeholder[] = { 0x07230203 }; // SPIR-V magic number
return std::span<const u32>(placeholder);
}
};
// ================================================================================================
// gradient_widget 类
// ================================================================================================
/**
* @brief 渐变效果控件
*
* 继承自 procedural_widget实现程序化渐变效果。
*
* 特点:
* - 支持四种渐变类型(线性、径向、角度、菱形)
* - 可自定义起始和结束颜色
* - 支持渐变角度和中心点设置
* - 支持重复模式
*
* @see procedural_widget
* @see gradient_bindings
*/
class gradient_widget : public procedural_widget<gradient_bindings> {
public:
MIRAI_OBJECT_TYPE_INFO(gradient_widget, procedural_widget<gradient_bindings>)
// ============================================================================================
// 构造与析构
// ============================================================================================
/**
* @brief 默认构造函数
*
* 初始化默认参数:
* - start_color = 白色
* - end_color = 黑色
* - type = linear
* - angle = 0 (水平)
*/
gradient_widget()
: start_color_{1.0f, 1.0f, 1.0f, 1.0f}
, end_color_{0.0f, 0.0f, 0.0f, 1.0f}
, type_(gradient_type::linear)
, angle_(0.0f)
, center_{0.5f, 0.5f}
, radius_(0.5f)
, repeat_count_(0.0f) {
}
/**
* @brief 带颜色参数的构造函数
* @param start 起始颜色
* @param end 结束颜色
* @param type 渐变类型
*/
gradient_widget(const color& start, const color& end, gradient_type type = gradient_type::linear)
: start_color_(start)
, end_color_(end)
, type_(type)
, angle_(0.0f)
, center_{0.5f, 0.5f}
, radius_(0.5f)
, repeat_count_(0.0f) {
}
/**
* @brief 虚析构函数
*/
~gradient_widget() override = default;
// ============================================================================================
// 颜色设置
// ============================================================================================
/**
* @brief 设置起始颜色
* @param c 起始颜色
*/
void set_start_color(const color& c) {
start_color_ = c;
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取起始颜色
* @return 当前起始颜色
*/
[[nodiscard]] const color& start_color() const noexcept { return start_color_; }
/**
* @brief 设置结束颜色
* @param c 结束颜色
*/
void set_end_color(const color& c) {
end_color_ = c;
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取结束颜色
* @return 当前结束颜色
*/
[[nodiscard]] const color& end_color() const noexcept { return end_color_; }
/**
* @brief 同时设置起始和结束颜色
* @param start 起始颜色
* @param end 结束颜色
*/
void set_colors(const color& start, const color& end) {
start_color_ = start;
end_color_ = end;
mark_dirty(widget_dirty_flags::uniform_buffer);
}
// ============================================================================================
// 渐变类型设置
// ============================================================================================
/**
* @brief 设置渐变类型
* @param type 渐变类型
*/
void set_gradient_type(gradient_type type) {
type_ = type;
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取渐变类型
* @return 当前渐变类型
*/
[[nodiscard]] gradient_type type() const noexcept { return type_; }
// ============================================================================================
// 渐变参数设置
// ============================================================================================
/**
* @brief 设置渐变角度(用于线性渐变)
* @param angle_degrees 角度度数0 = 水平向右
*/
void set_angle(f32 angle_degrees) {
angle_ = angle_degrees * (3.14159265358979323846f / 180.0f);
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取渐变角度
* @return 当前角度(弧度)
*/
[[nodiscard]] f32 angle() const noexcept { return angle_; }
/**
* @brief 设置渐变中心(用于径向/角度渐变)
* @param x 中心 X 坐标 (0-1)
* @param y 中心 Y 坐标 (0-1)
*/
void set_center(f32 x, f32 y) {
center_[0] = x;
center_[1] = y;
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取渐变中心 X 坐标
* @return 中心 X 坐标
*/
[[nodiscard]] f32 center_x() const noexcept { return center_[0]; }
/**
* @brief 获取渐变中心 Y 坐标
* @return 中心 Y 坐标
*/
[[nodiscard]] f32 center_y() const noexcept { return center_[1]; }
/**
* @brief 设置渐变半径(用于径向渐变)
* @param r 半径 (0-1)
*/
void set_radius(f32 r) {
radius_ = std::max(0.001f, r);
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取渐变半径
* @return 当前半径
*/
[[nodiscard]] f32 radius() const noexcept { return radius_; }
/**
* @brief 设置重复次数
* @param count 重复次数0 = 不重复)
*/
void set_repeat_count(f32 count) {
repeat_count_ = std::max(0.0f, count);
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取重复次数
* @return 当前重复次数
*/
[[nodiscard]] f32 repeat_count() const noexcept { return repeat_count_; }
protected:
// ============================================================================================
// 重写基类方法
// ============================================================================================
/**
* @brief 更新 Uniform Buffer
*
* 将渐变参数写入 GPU 缓冲区。
*/
void update_uniform_buffer() override {
// GradientParams 结构体布局(与 gradient.slang 中定义一致)
struct GradientParams {
f32 start_color[4];
f32 end_color[4];
f32 angle;
u32 gradient_type;
f32 center[2];
f32 radius;
f32 repeat_count;
f32 _padding[2];
};
GradientParams params{};
params.start_color[0] = start_color_.r;
params.start_color[1] = start_color_.g;
params.start_color[2] = start_color_.b;
params.start_color[3] = start_color_.a;
params.end_color[0] = end_color_.r;
params.end_color[1] = end_color_.g;
params.end_color[2] = end_color_.b;
params.end_color[3] = end_color_.a;
params.angle = angle_;
params.gradient_type = static_cast<u32>(type_);
params.center[0] = center_[0];
params.center[1] = center_[1];
params.radius = radius_;
params.repeat_count = repeat_count_;
// TODO: 将 params 写入 uniform buffer
// uniform_buffer_->write(&params, sizeof(params));
}
private:
// ============================================================================================
// 成员变量
// ============================================================================================
/// 起始颜色
color start_color_;
/// 结束颜色
color end_color_;
/// 渐变类型
gradient_type type_;
/// 渐变角度(弧度)
f32 angle_;
/// 渐变中心
f32 center_[2];
/// 渐变半径
f32 radius_;
/// 重复次数
f32 repeat_count_;
};
} // namespace mirai

View File

@@ -0,0 +1,485 @@
/**
* @file particle_widget.hpp
* @brief 粒子系统控件
* @author MIRAI Team
* @version 0.1.0
*
* 本文件定义了 particle_widget 类,实现 Mesh 模式的粒子渲染效果。
*
* @example
* @code
* auto particles = std::make_shared<particle_widget>();
* particles->set_particle_count(1000);
* particles->set_particle_size(5.0f, 1.0f); // 起始大小 -> 结束大小
* particles->set_colors(color::yellow(), color::red());
* particles->set_particle_mode(particle_mode::billboard);
* @endcode
*/
#pragma once
#include "shader_widget.hpp"
#include "types/color.h"
#include <span>
#include <cstdint>
#include <vector>
#include <algorithm>
namespace mirai {
// ================================================================================================
// 粒子渲染模式枚举
// ================================================================================================
/**
* @brief 粒子渲染模式
*/
enum class particle_mode : u32 {
billboard = 0, ///< Billboard 模式(始终面向相机)
velocity_aligned = 1, ///< 速度对齐模式
stretched = 2 ///< 拉伸模式(沿速度方向拉伸)
};
/**
* @brief 粒子形状
*/
enum class particle_shape : u8 {
textured, ///< 使用纹理
circle, ///< 圆形(无纹理)
soft, ///< 软边缘圆形
glow ///< 发光效果
};
// ================================================================================================
// 粒子实例数据
// ================================================================================================
/**
* @brief 单个粒子的实例数据
*
* 与 mesh.slang 中的 ParticleInstance 结构体对应
*/
struct particle_instance {
f32 position[3]; ///< 粒子位置
f32 size; ///< 粒子大小
f32 color[4]; ///< 粒子颜色
f32 rotation; ///< 粒子旋转
f32 life; ///< 生命周期 (0-1)
f32 velocity[2]; ///< 速度
};
// ================================================================================================
// Particle 绑定类型(模拟代码生成器输出)
// ================================================================================================
/**
* @brief particle_widget 的着色器绑定类型
*
* 此类型模拟代码生成器的输出,提供:
* - SPIR-V 字节码访问
* - 绑定点常量
* - 顶点类型定义
*
* @note 在实际项目中,此类型由 shader_codegen 工具自动生成
*/
struct particle_bindings {
// ============================================================================================
// 着色器存在性标记
// ============================================================================================
static constexpr bool HAS_VERTEX_SHADER = true;
static constexpr bool HAS_FRAGMENT_SHADER = true;
// ============================================================================================
// Mesh 模式特有标记
// ============================================================================================
static constexpr bool HAS_CUSTOM_VERTEX_INPUT = true;
/// 顶点类型定义
using VertexType = particle_instance;
// ============================================================================================
// SPIR-V 数据访问(占位实现)
// ============================================================================================
/**
* @brief 获取顶点着色器 SPIR-V 数据
* @return SPIR-V 字节码的 span
* @note 实际实现中,此数据由编译后的着色器提供
*/
[[nodiscard]] static std::span<const u32> get_vertex_spirv() {
// 占位:实际数据由着色器编译生成
static const u32 placeholder[] = { 0x07230203 }; // SPIR-V magic number
return std::span<const u32>(placeholder);
}
/**
* @brief 获取片段着色器 SPIR-V 数据
* @return SPIR-V 字节码的 span
* @note 实际实现中,此数据由编译后的着色器提供
*/
[[nodiscard]] static std::span<const u32> get_fragment_spirv() {
// 占位:实际数据由着色器编译生成
static const u32 placeholder[] = { 0x07230203 }; // SPIR-V magic number
return std::span<const u32>(placeholder);
}
};
// ================================================================================================
// particle_widget 类
// ================================================================================================
/**
* @brief 粒子系统控件
*
* 继承自 mesh_widget使用实例化渲染绘制大量粒子。
*
* 特点:
* - 支持大量粒子的高效渲染
* - 多种渲染模式Billboard、速度对齐、拉伸
* - 生命周期颜色和大小动画
* - 淡入淡出效果
*
* @see mesh_widget
* @see particle_bindings
*/
class particle_widget : public mesh_widget<particle_bindings> {
public:
MIRAI_OBJECT_TYPE_INFO(particle_widget, mesh_widget<particle_bindings>)
// ============================================================================================
// 构造与析构
// ============================================================================================
/**
* @brief 默认构造函数
*
* 初始化默认参数:
* - particle_count = 100
* - size_start = 10.0f
* - size_end = 2.0f
* - color_start = 白色
* - color_end = 透明白色
*/
particle_widget()
: particle_count_(100)
, size_start_(10.0f)
, size_end_(2.0f)
, color_start_{1.0f, 1.0f, 1.0f, 1.0f}
, color_end_{1.0f, 1.0f, 1.0f, 0.0f}
, fade_in_(0.1f)
, fade_out_(0.3f)
, mode_(particle_mode::billboard)
, shape_(particle_shape::circle)
, stretch_factor_(0.5f) {
}
/**
* @brief 带粒子数量的构造函数
* @param count 粒子数量
*/
explicit particle_widget(u32 count)
: particle_count_(count)
, size_start_(10.0f)
, size_end_(2.0f)
, color_start_{1.0f, 1.0f, 1.0f, 1.0f}
, color_end_{1.0f, 1.0f, 1.0f, 0.0f}
, fade_in_(0.1f)
, fade_out_(0.3f)
, mode_(particle_mode::billboard)
, shape_(particle_shape::circle)
, stretch_factor_(0.5f) {
}
/**
* @brief 虚析构函数
*/
~particle_widget() override = default;
// ============================================================================================
// 粒子数量设置
// ============================================================================================
/**
* @brief 设置粒子数量
* @param count 粒子数量
*/
void set_particle_count(u32 count) {
particle_count_ = count;
particles_.resize(count);
mark_dirty(widget_dirty_flags::vertex_buffer);
}
/**
* @brief 获取粒子数量
* @return 当前粒子数量
*/
[[nodiscard]] u32 particle_count() const noexcept { return particle_count_; }
// ============================================================================================
// 大小设置
// ============================================================================================
/**
* @brief 设置粒子大小范围
* @param start 起始大小(刚出生时)
* @param end 结束大小(死亡时)
*/
void set_particle_size(f32 start, f32 end) {
size_start_ = std::max(0.0f, start);
size_end_ = std::max(0.0f, end);
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 设置统一粒子大小
* @param size 粒子大小
*/
void set_particle_size(f32 size) {
set_particle_size(size, size);
}
/**
* @brief 获取起始大小
* @return 起始大小
*/
[[nodiscard]] f32 size_start() const noexcept { return size_start_; }
/**
* @brief 获取结束大小
* @return 结束大小
*/
[[nodiscard]] f32 size_end() const noexcept { return size_end_; }
// ============================================================================================
// 颜色设置
// ============================================================================================
/**
* @brief 设置粒子颜色范围
* @param start 起始颜色(刚出生时)
* @param end 结束颜色(死亡时)
*/
void set_colors(const color& start, const color& end) {
color_start_ = start;
color_end_ = end;
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 设置起始颜色
* @param c 起始颜色
*/
void set_color_start(const color& c) {
color_start_ = c;
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 设置结束颜色
* @param c 结束颜色
*/
void set_color_end(const color& c) {
color_end_ = c;
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取起始颜色
* @return 起始颜色
*/
[[nodiscard]] const color& color_start() const noexcept { return color_start_; }
/**
* @brief 获取结束颜色
* @return 结束颜色
*/
[[nodiscard]] const color& color_end() const noexcept { return color_end_; }
// ============================================================================================
// 淡入淡出设置
// ============================================================================================
/**
* @brief 设置淡入淡出时间
* @param fade_in 淡入时间比例 (0-1)
* @param fade_out 淡出时间比例 (0-1)
*/
void set_fade(f32 fade_in, f32 fade_out) {
fade_in_ = std::clamp(fade_in, 0.0f, 1.0f);
fade_out_ = std::clamp(fade_out, 0.0f, 1.0f);
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取淡入时间
* @return 淡入时间比例
*/
[[nodiscard]] f32 fade_in() const noexcept { return fade_in_; }
/**
* @brief 获取淡出时间
* @return 淡出时间比例
*/
[[nodiscard]] f32 fade_out() const noexcept { return fade_out_; }
// ============================================================================================
// 渲染模式设置
// ============================================================================================
/**
* @brief 设置粒子渲染模式
* @param mode 渲染模式
*/
void set_particle_mode(particle_mode mode) {
mode_ = mode;
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取粒子渲染模式
* @return 当前渲染模式
*/
[[nodiscard]] particle_mode mode() const noexcept { return mode_; }
/**
* @brief 设置粒子形状
* @param shape 粒子形状
*/
void set_particle_shape(particle_shape shape) {
shape_ = shape;
mark_dirty(widget_dirty_flags::pipeline);
}
/**
* @brief 获取粒子形状
* @return 当前粒子形状
*/
[[nodiscard]] particle_shape shape() const noexcept { return shape_; }
/**
* @brief 设置拉伸因子
* @param factor 拉伸因子(用于速度拉伸模式)
*/
void set_stretch_factor(f32 factor) {
stretch_factor_ = std::max(0.0f, factor);
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取拉伸因子
* @return 当前拉伸因子
*/
[[nodiscard]] f32 stretch_factor() const noexcept { return stretch_factor_; }
// ============================================================================================
// 粒子数据访问
// ============================================================================================
/**
* @brief 获取粒子数据(可修改)
* @return 粒子数据向量的引用
*/
[[nodiscard]] std::vector<particle_instance>& particles() { return particles_; }
/**
* @brief 获取粒子数据(只读)
* @return 粒子数据向量的常量引用
*/
[[nodiscard]] const std::vector<particle_instance>& particles() const { return particles_; }
/**
* @brief 标记粒子数据已更新
*
* 在修改粒子数据后调用此方法以触发 GPU 缓冲区更新
*/
void mark_particles_dirty() {
mark_dirty(widget_dirty_flags::vertex_buffer);
}
protected:
// ============================================================================================
// 重写基类方法
// ============================================================================================
/**
* @brief 更新 Uniform Buffer
*
* 将粒子系统参数写入 GPU 缓冲区。
*/
void update_uniform_buffer() override {
// ParticleSystemParams 结构体布局(与 particle.slang 中定义一致)
struct ParticleSystemParams {
f32 color_start[4];
f32 color_end[4];
f32 size_start;
f32 size_end;
f32 fade_in;
f32 fade_out;
u32 particle_mode;
f32 stretch_factor;
f32 _padding[2];
};
ParticleSystemParams params{};
params.color_start[0] = color_start_.r;
params.color_start[1] = color_start_.g;
params.color_start[2] = color_start_.b;
params.color_start[3] = color_start_.a;
params.color_end[0] = color_end_.r;
params.color_end[1] = color_end_.g;
params.color_end[2] = color_end_.b;
params.color_end[3] = color_end_.a;
params.size_start = size_start_;
params.size_end = size_end_;
params.fade_in = fade_in_;
params.fade_out = fade_out_;
params.particle_mode = static_cast<u32>(mode_);
params.stretch_factor = stretch_factor_;
// TODO: 将 params 写入 uniform buffer
// uniform_buffer_->write(&params, sizeof(params));
}
private:
// ============================================================================================
// 成员变量
// ============================================================================================
/// 粒子数量
u32 particle_count_;
/// 起始大小
f32 size_start_;
/// 结束大小
f32 size_end_;
/// 起始颜色
color color_start_;
/// 结束颜色
color color_end_;
/// 淡入时间比例
f32 fade_in_;
/// 淡出时间比例
f32 fade_out_;
/// 渲染模式
particle_mode mode_;
/// 粒子形状
particle_shape shape_;
/// 拉伸因子
f32 stretch_factor_;
/// 粒子实例数据
std::vector<particle_instance> particles_;
};
} // namespace mirai

View File

@@ -0,0 +1,313 @@
/**
* @file rounded_rect_mask_widget.hpp
* @brief 圆角矩形遮罩控件
* @author MIRAI Team
* @version 0.1.0
*
* 本文件定义了 rounded_rect_mask_widget 类,实现 Mask 模式的圆角矩形遮罩效果。
*
* @example
* @code
* auto mask = std::make_shared<rounded_rect_mask_widget>();
* mask->set_corner_radius(10.0f); // 统一圆角
* // 或者设置四角独立圆角
* mask->set_corner_radius(10.0f, 20.0f, 10.0f, 20.0f);
* mask->set_feather(2.0f); // 边缘羽化
* @endcode
*/
#pragma once
#include "shader_widget.hpp"
#include "types/corner_radius.h"
#include <span>
#include <cstdint>
#include <algorithm>
namespace mirai {
// ================================================================================================
// RoundedRectMask 绑定类型(模拟代码生成器输出)
// ================================================================================================
/**
* @brief rounded_rect_mask_widget 的着色器绑定类型
*
* 此类型模拟代码生成器的输出,提供:
* - SPIR-V 字节码访问
* - 绑定点常量
* - 静态反射信息
*
* @note 在实际项目中,此类型由 shader_codegen 工具自动生成
*/
struct rounded_rect_mask_bindings {
// ============================================================================================
// 着色器存在性标记
// ============================================================================================
static constexpr bool HAS_VERTEX_SHADER = true;
static constexpr bool HAS_FRAGMENT_SHADER = true;
// ============================================================================================
// Mask 模式特有标记
// ============================================================================================
static constexpr bool HAS_MASK_SAMPLER = true;
static constexpr u32 MASK_BINDING = 0;
static constexpr u32 MASK_SET = 0;
// ============================================================================================
// SPIR-V 数据访问(占位实现)
// ============================================================================================
/**
* @brief 获取顶点着色器 SPIR-V 数据
* @return SPIR-V 字节码的 span
* @note 实际实现中,此数据由编译后的着色器提供
*/
[[nodiscard]] static std::span<const u32> get_vertex_spirv() {
// 占位:实际数据由着色器编译生成
static const u32 placeholder[] = { 0x07230203 }; // SPIR-V magic number
return std::span<const u32>(placeholder);
}
/**
* @brief 获取片段着色器 SPIR-V 数据
* @return SPIR-V 字节码的 span
* @note 实际实现中,此数据由编译后的着色器提供
*/
[[nodiscard]] static std::span<const u32> get_fragment_spirv() {
// 占位:实际数据由着色器编译生成
static const u32 placeholder[] = { 0x07230203 }; // SPIR-V magic number
return std::span<const u32>(placeholder);
}
};
// ================================================================================================
// rounded_rect_mask_widget 类
// ================================================================================================
/**
* @brief 圆角矩形遮罩控件
*
* 继承自 mask_widget使用 SDF 生成圆角矩形遮罩。
*
* 特点:
* - 支持四角独立圆角半径
* - 可调节的边缘羽化
* - 支持边框模式
* - 高质量抗锯齿边缘
*
* @see mask_widget
* @see rounded_rect_mask_bindings
*/
class rounded_rect_mask_widget : public mask_widget<rounded_rect_mask_bindings> {
public:
MIRAI_OBJECT_TYPE_INFO(rounded_rect_mask_widget, mask_widget<rounded_rect_mask_bindings>)
// ============================================================================================
// 构造与析构
// ============================================================================================
/**
* @brief 默认构造函数
*
* 初始化默认参数:
* - corner_radius = 0 (无圆角)
* - feather = 0 (无羽化)
* - border_width = 0 (填充模式)
*/
rounded_rect_mask_widget()
: corner_radius_{0.0f, 0.0f, 0.0f, 0.0f}
, feather_(0.0f)
, border_width_(0.0f) {
}
/**
* @brief 带统一圆角的构造函数
* @param radius 统一圆角半径
*/
explicit rounded_rect_mask_widget(f32 radius)
: corner_radius_{radius, radius, radius, radius}
, feather_(0.0f)
, border_width_(0.0f) {
}
/**
* @brief 带四角独立圆角的构造函数
* @param top_left 左上角圆角半径
* @param top_right 右上角圆角半径
* @param bottom_right 右下角圆角半径
* @param bottom_left 左下角圆角半径
*/
rounded_rect_mask_widget(f32 top_left, f32 top_right, f32 bottom_right, f32 bottom_left)
: corner_radius_{top_left, top_right, bottom_right, bottom_left}
, feather_(0.0f)
, border_width_(0.0f) {
}
/**
* @brief 虚析构函数
*/
~rounded_rect_mask_widget() override = default;
// ============================================================================================
// 圆角设置
// ============================================================================================
/**
* @brief 设置统一圆角半径
* @param radius 圆角半径(相对于控件尺寸,范围 0-0.5
*/
void set_corner_radius(f32 radius) {
radius = std::clamp(radius, 0.0f, 0.5f);
corner_radius_[0] = radius;
corner_radius_[1] = radius;
corner_radius_[2] = radius;
corner_radius_[3] = radius;
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 设置四角独立圆角半径
* @param top_left 左上角圆角半径
* @param top_right 右上角圆角半径
* @param bottom_right 右下角圆角半径
* @param bottom_left 左下角圆角半径
*/
void set_corner_radius(f32 top_left, f32 top_right, f32 bottom_right, f32 bottom_left) {
corner_radius_[0] = std::clamp(top_left, 0.0f, 0.5f);
corner_radius_[1] = std::clamp(top_right, 0.0f, 0.5f);
corner_radius_[2] = std::clamp(bottom_right, 0.0f, 0.5f);
corner_radius_[3] = std::clamp(bottom_left, 0.0f, 0.5f);
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 使用 corner_radius 类型设置圆角
* @param radius corner_radius 对象
*/
void set_corner_radius(const corner_radius& radius) {
corner_radius_[0] = std::clamp(radius.top_left, 0.0f, 0.5f);
corner_radius_[1] = std::clamp(radius.top_right, 0.0f, 0.5f);
corner_radius_[2] = std::clamp(radius.bottom_right, 0.0f, 0.5f);
corner_radius_[3] = std::clamp(radius.bottom_left, 0.0f, 0.5f);
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取左上角圆角半径
* @return 左上角圆角半径
*/
[[nodiscard]] f32 top_left_radius() const noexcept { return corner_radius_[0]; }
/**
* @brief 获取右上角圆角半径
* @return 右上角圆角半径
*/
[[nodiscard]] f32 top_right_radius() const noexcept { return corner_radius_[1]; }
/**
* @brief 获取右下角圆角半径
* @return 右下角圆角半径
*/
[[nodiscard]] f32 bottom_right_radius() const noexcept { return corner_radius_[2]; }
/**
* @brief 获取左下角圆角半径
* @return 左下角圆角半径
*/
[[nodiscard]] f32 bottom_left_radius() const noexcept { return corner_radius_[3]; }
// ============================================================================================
// 羽化设置
// ============================================================================================
/**
* @brief 设置边缘羽化量
* @param feather 羽化量(相对于控件尺寸,范围 0-0.5
*/
void set_feather(f32 feather) {
feather_ = std::clamp(feather, 0.0f, 0.5f);
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取边缘羽化量
* @return 当前羽化量
*/
[[nodiscard]] f32 feather() const noexcept { return feather_; }
// ============================================================================================
// 边框设置
// ============================================================================================
/**
* @brief 设置边框宽度
* @param width 边框宽度0 = 填充模式)
*/
void set_border_width(f32 width) {
border_width_ = std::max(0.0f, width);
mark_dirty(widget_dirty_flags::uniform_buffer);
}
/**
* @brief 获取边框宽度
* @return 当前边框宽度
*/
[[nodiscard]] f32 border_width() const noexcept { return border_width_; }
/**
* @brief 检查是否为边框模式
* @return 如果边框宽度大于 0 返回 true
*/
[[nodiscard]] bool is_border_mode() const noexcept { return border_width_ > 0.0f; }
protected:
// ============================================================================================
// 重写基类方法
// ============================================================================================
/**
* @brief 更新 Uniform Buffer
*
* 将遮罩参数写入 GPU 缓冲区。
*/
void update_uniform_buffer() override {
// RoundedRectMaskParams 结构体布局(与 rounded_rect_mask.slang 中定义一致)
struct RoundedRectMaskParams {
f32 corner_radius[4];
f32 feather;
f32 border_width;
f32 _padding[2];
};
RoundedRectMaskParams params{};
params.corner_radius[0] = corner_radius_[0];
params.corner_radius[1] = corner_radius_[1];
params.corner_radius[2] = corner_radius_[2];
params.corner_radius[3] = corner_radius_[3];
params.feather = feather_;
params.border_width = border_width_;
// TODO: 将 params 写入 uniform buffer
// uniform_buffer_->write(&params, sizeof(params));
}
private:
// ============================================================================================
// 成员变量
// ============================================================================================
/// 四角圆角半径 (top_left, top_right, bottom_right, bottom_left)
f32 corner_radius_[4];
/// 边缘羽化量
f32 feather_;
/// 边框宽度0 = 填充模式)
f32 border_width_;
};
} // namespace mirai

View File

@@ -0,0 +1,421 @@
/**
* @file shader_widget.hpp
* @brief MIRAI 框架 shader_widget 模板化基类
* @author MIRAI Team
* @version 0.1.0
*
* 本文件定义了 shader_widget 模板化基类,包括:
* - 类型安全的着色器资源管理
* - 编译时绑定类型检查
* - 静态反射信息访问
*/
#pragma once
#include "shader_widget_base.hpp"
#include "widget_types.hpp"
#include "shader_types.hpp"
#include "pipeline.hpp"
#include "buffer.hpp"
#include "descriptor_pool.hpp"
#include <memory>
#include <vector>
namespace mirai {
// ================================================================================================
// shader_widget 模板类
// ================================================================================================
/**
* @brief 模板化的 shader_widget 基类
*
* @tparam Bindings 着色器绑定类型,必须满足 shader_bindings concept
*
* 这是所有着色器控件的基类模板。子类通过继承此类并指定绑定类型,
* 获得类型安全的着色器资源管理能力。
*
* Bindings 类型必须提供:
* - `HAS_VERTEX_SHADER` 静态常量
* - `HAS_FRAGMENT_SHADER` 静态常量
* - `get_vertex_spirv()` 静态方法
* - `get_fragment_spirv()` 静态方法
*
* @example
* @code
* // 假设 blur_shader_bindings 是由代码生成器生成的绑定类
* class blur_widget : public shader_widget<blur_shader_bindings> {
* public:
* void set_radius(float radius) {
* params_.radius = radius;
* mark_dirty(widget_dirty_flags::uniform_buffer);
* }
* protected:
* void update_uniform_buffer() override {
* // 更新 uniform buffer 数据
* }
* };
* @endcode
*/
template<typename Bindings>
requires shader_bindings<Bindings>
class shader_widget : public shader_widget_base {
public:
MIRAI_OBJECT_TYPE_INFO(shader_widget, shader_widget_base)
/// 绑定类型别名
using bindings_type = Bindings;
// 编译时验证
static_assert(Bindings::HAS_VERTEX_SHADER, "Bindings must have vertex shader");
static_assert(Bindings::HAS_FRAGMENT_SHADER, "Bindings must have fragment shader");
/**
* @brief 默认构造函数
*/
shader_widget() = default;
/**
* @brief 虚析构函数
*/
~shader_widget() override = default;
// ============================================================================================
// 静态访问器
// ============================================================================================
/**
* @brief 获取顶点着色器 SPIR-V 数据
* @return SPIR-V 字节码的 span
*/
[[nodiscard]] static std::span<const u32> vertex_spirv() {
return Bindings::get_vertex_spirv();
}
/**
* @brief 获取片段着色器 SPIR-V 数据
* @return SPIR-V 字节码的 span
*/
[[nodiscard]] static std::span<const u32> fragment_spirv() {
return Bindings::get_fragment_spirv();
}
/**
* @brief 获取静态反射信息
*
* 如果 Bindings 类型提供了 get_reflection() 方法,则返回反射信息;
* 否则返回空的反射信息。
*
* @return 静态着色器反射信息
*/
[[nodiscard]] static const static_shader_reflection& reflection() {
if constexpr (requires { Bindings::get_reflection(); }) {
return Bindings::get_reflection();
} else {
static const static_shader_reflection empty_reflection{};
return empty_reflection;
}
}
protected:
// ============================================================================================
// 资源访问
// ============================================================================================
/**
* @brief 获取管线句柄
* @return Vulkan 管线句柄
*/
[[nodiscard]] VkPipeline pipeline() const noexcept { return pipeline_; }
/**
* @brief 获取管线布局句柄
* @return Vulkan 管线布局句柄
*/
[[nodiscard]] VkPipelineLayout pipeline_layout() const noexcept { return pipeline_layout_; }
/**
* @brief 获取描述符集
* @param index 描述符集索引
* @return Vulkan 描述符集句柄,如果索引超出范围返回 VK_NULL_HANDLE
*/
[[nodiscard]] VkDescriptorSet descriptor_set(u32 index = 0) const noexcept {
return index < descriptor_sets_.size() ? descriptor_sets_[index] : VK_NULL_HANDLE;
}
/**
* @brief 获取描述符集数量
* @return 描述符集数量
*/
[[nodiscard]] size_type descriptor_set_count() const noexcept {
return descriptor_sets_.size();
}
// ============================================================================================
// 默认实现
// ============================================================================================
/**
* @brief 创建管线(默认实现)
*
* 子类可以重写此方法以自定义管线创建。
* 默认实现使用 vertex_spirv() 和 fragment_spirv() 创建标准的图形管线。
*/
void create_pipeline() override {
// 获取 SPIR-V 数据
[[maybe_unused]] auto vert_spirv = vertex_spirv();
[[maybe_unused]] auto frag_spirv = fragment_spirv();
// TODO: 使用 pipeline 类创建管线
// 这里需要根据实际的 pipeline 类接口实现
//
// 示例伪代码:
// pipeline_create_info info;
// info.vertex_spirv = vert_spirv;
// info.fragment_spirv = frag_spirv;
// info.descriptor_set_layouts = ...;
// pipeline_ = create_graphics_pipeline(device(), info);
// pipeline_layout_ = ...;
}
/**
* @brief 创建描述符集(默认实现)
*
* 子类可以重写此方法以自定义描述符集创建。
* 默认实现根据反射信息创建描述符集。
*/
void create_descriptor_sets() override {
// TODO: 使用 descriptor_pool 创建描述符集
// 这里需要根据实际的 descriptor_pool 类接口实现
//
// 示例伪代码:
// auto& refl = reflection();
// for (const auto& block : refl.uniform_blocks) {
// // 创建描述符集布局
// // 分配描述符集
// }
}
/**
* @brief 更新 Uniform Buffer默认实现
*
* 子类必须实现此方法来更新自己的 uniform 数据。
* 默认为空实现。
*/
void update_uniform_buffer() override {
// 子类必须实现此方法
// 默认为空实现
}
/**
* @brief 记录绘制命令(默认实现)
*
* 子类可以重写此方法以自定义绘制逻辑。
* 默认实现绑定管线和描述符集,绘制全屏四边形。
*
* @param cmd 命令缓冲区
*/
void draw(command_buffer& cmd) override {
// TODO: 实现默认绘制逻辑
//
// 示例伪代码:
// cmd.bind_pipeline(vk::PipelineBindPoint::eGraphics, pipeline_);
// cmd.bind_descriptor_sets(pipeline_layout_, 0, descriptor_sets_);
// cmd.draw(6, 1, 0, 0); // 全屏四边形(两个三角形)
}
/**
* @brief 销毁资源(重写)
*
* 清理模板类特有的资源。
*/
void destroy() override {
// 清理 Vulkan 资源
// 注意:实际的资源销毁需要 VkDevice
// 这里只是重置句柄
if (pipeline_ != VK_NULL_HANDLE) {
// TODO: vkDestroyPipeline(device().get_handle(), pipeline_, nullptr);
pipeline_ = VK_NULL_HANDLE;
}
if (pipeline_layout_ != VK_NULL_HANDLE) {
// TODO: vkDestroyPipelineLayout(device().get_handle(), pipeline_layout_, nullptr);
pipeline_layout_ = VK_NULL_HANDLE;
}
// 描述符集由描述符池管理,通常不需要手动释放
descriptor_sets_.clear();
// 调用基类销毁
shader_widget_base::destroy();
}
protected:
// ============================================================================================
// 成员变量
// ============================================================================================
/// Vulkan 管线句柄
VkPipeline pipeline_ = VK_NULL_HANDLE;
/// Vulkan 管线布局句柄
VkPipelineLayout pipeline_layout_ = VK_NULL_HANDLE;
/// 描述符集列表
std::vector<VkDescriptorSet> descriptor_sets_;
};
// ================================================================================================
// 特化模式模板类(可选扩展)
// ================================================================================================
/**
* @brief Backdrop 模式控件基类模板
*
* 用于后效处理模式的控件。提供背景纹理采样能力。
*
* @tparam Bindings 满足 backdrop_bindings concept 的绑定类型
*/
template<typename Bindings>
requires backdrop_bindings<Bindings>
class backdrop_widget : public shader_widget<Bindings> {
public:
MIRAI_OBJECT_TYPE_INFO(backdrop_widget, shader_widget<Bindings>)
/**
* @brief 获取渲染模式
* @return 始终返回 backdrop
*/
[[nodiscard]] static constexpr shader_widget_mode mode() noexcept {
return shader_widget_mode::backdrop;
}
/**
* @brief 获取背景采样器绑定点
* @return 绑定点索引
*/
[[nodiscard]] static constexpr u32 backdrop_binding() noexcept {
return Bindings::BACKDROP_BINDING;
}
/**
* @brief 获取背景采样器所在描述符集
* @return 描述符集索引
*/
[[nodiscard]] static constexpr u32 backdrop_set() noexcept {
return Bindings::BACKDROP_SET;
}
};
/**
* @brief Procedural 模式控件基类模板
*
* 用于程序化渲染模式的控件。提供时间和坐标输入。
*
* @tparam Bindings 满足 procedural_bindings concept 的绑定类型
*/
template<typename Bindings>
requires procedural_bindings<Bindings>
class procedural_widget : public shader_widget<Bindings> {
public:
MIRAI_OBJECT_TYPE_INFO(procedural_widget, shader_widget<Bindings>)
/**
* @brief 获取渲染模式
* @return 始终返回 procedural
*/
[[nodiscard]] static constexpr shader_widget_mode mode() noexcept {
return shader_widget_mode::procedural;
}
/**
* @brief 获取帧数据绑定点
* @return 绑定点索引
*/
[[nodiscard]] static constexpr u32 frame_data_binding() noexcept {
return Bindings::FRAME_DATA_BINDING;
}
/**
* @brief 获取帧数据所在描述符集
* @return 描述符集索引
*/
[[nodiscard]] static constexpr u32 frame_data_set() noexcept {
return Bindings::FRAME_DATA_SET;
}
};
/**
* @brief Mask 模式控件基类模板
*
* 用于遮罩效果模式的控件。提供遮罩纹理采样能力。
*
* @tparam Bindings 满足 mask_bindings concept 的绑定类型
*/
template<typename Bindings>
requires mask_bindings<Bindings>
class mask_widget : public shader_widget<Bindings> {
public:
MIRAI_OBJECT_TYPE_INFO(mask_widget, shader_widget<Bindings>)
/**
* @brief 获取渲染模式
* @return 始终返回 mask
*/
[[nodiscard]] static constexpr shader_widget_mode mode() noexcept {
return shader_widget_mode::mask;
}
/**
* @brief 获取遮罩纹理绑定点
* @return 绑定点索引
*/
[[nodiscard]] static constexpr u32 mask_binding() noexcept {
return Bindings::MASK_BINDING;
}
/**
* @brief 获取遮罩纹理所在描述符集
* @return 描述符集索引
*/
[[nodiscard]] static constexpr u32 mask_set() noexcept {
return Bindings::MASK_SET;
}
};
/**
* @brief Mesh 模式控件基类模板
*
* 用于自定义网格渲染模式的控件。提供自定义顶点数据支持。
*
* @tparam Bindings 满足 mesh_bindings concept 的绑定类型
*/
template<typename Bindings>
requires mesh_bindings<Bindings>
class mesh_widget : public shader_widget<Bindings> {
public:
MIRAI_OBJECT_TYPE_INFO(mesh_widget, shader_widget<Bindings>)
/// 顶点类型别名
using vertex_type = typename Bindings::VertexType;
/**
* @brief 获取渲染模式
* @return 始终返回 mesh
*/
[[nodiscard]] static constexpr shader_widget_mode mode() noexcept {
return shader_widget_mode::mesh;
}
/**
* @brief 检查是否有自定义顶点输入
* @return 始终返回 true由 concept 保证)
*/
[[nodiscard]] static constexpr bool has_custom_vertex_input() noexcept {
return Bindings::HAS_CUSTOM_VERTEX_INPUT;
}
};
} // namespace mirai

View File

@@ -0,0 +1,139 @@
/**
* @file shader_widget_base.cpp
* @brief MIRAI 框架 shader_widget_base 类实现
* @author MIRAI Team
* @version 0.1.0
*/
#include "shader_widget_base.hpp"
#include "vulkan_device.hpp"
#include "allocator.hpp"
#include "buffer.hpp"
#include "descriptor_pool.hpp"
#include "assert.hpp"
namespace mirai {
// ================================================================================================
// 构造函数和析构函数
// ================================================================================================
shader_widget_base::shader_widget_base() = default;
shader_widget_base::~shader_widget_base() {
if (initialized_) {
destroy();
}
}
// ================================================================================================
// 生命周期方法
// ================================================================================================
void shader_widget_base::initialize(vulkan_device& device, gpu_allocator& allocator, descriptor_pool& pool) {
MIRAI_ASSERT(!initialized_, "shader_widget_base already initialized");
device_ = &device;
allocator_ = &allocator;
pool_ = &pool;
// 子类负责创建管线和描述符集
create_pipeline();
create_descriptor_sets();
initialized_ = true;
dirty_flags_ = widget_dirty_flags::all;
}
void shader_widget_base::destroy() {
if (!initialized_) {
return;
}
// 子类应该重写此方法以清理自己的资源
// 基类只负责清理引用
device_ = nullptr;
allocator_ = nullptr;
pool_ = nullptr;
initialized_ = false;
}
// ================================================================================================
// 渲染方法
// ================================================================================================
void shader_widget_base::update(command_buffer& cmd) {
if (!initialized_) {
return;
}
// 如果 uniform_buffer 标记为脏,则更新
if (has_flag(dirty_flags_, widget_dirty_flags::uniform_buffer)) {
update_uniform_buffer();
clear_dirty(widget_dirty_flags::uniform_buffer);
}
}
// ================================================================================================
// 状态管理
// ================================================================================================
void shader_widget_base::mark_dirty(widget_dirty_flags flags) {
dirty_flags_ |= flags;
}
void shader_widget_base::clear_dirty(widget_dirty_flags flags) {
dirty_flags_ = dirty_flags_ & (~flags);
}
bool shader_widget_base::is_dirty(widget_dirty_flags flag) const noexcept {
return has_flag(dirty_flags_, flag);
}
// ================================================================================================
// 区域设置
// ================================================================================================
void shader_widget_base::set_bounds(f32 x, f32 y, f32 width, f32 height) {
// 检查是否有变化
if (x_ != x || y_ != y || width_ != width || height_ != height) {
x_ = x;
y_ = y;
width_ = width;
height_ = height;
// 标记 uniform_buffer 为脏,因为边界变化通常需要更新 uniform 数据
mark_dirty(widget_dirty_flags::uniform_buffer);
}
}
// ================================================================================================
// 辅助方法
// ================================================================================================
std::unique_ptr<buffer> shader_widget_base::create_uniform_buffer(size_type size) {
MIRAI_ASSERT(allocator_ != nullptr, "allocator not set");
// 创建 buffer_create_info
buffer_create_info info;
info.size = static_cast<u64>(size);
info.usage = buffer_usage::uniform;
info.mem_usage = memory_usage::cpu_to_gpu;
info.persistent_mapped = true;
info.debug_name = "shader_widget_uniform";
// 注意buffer 类需要 shared_ptr<gpu_allocator>
// 但我们这里只有原始指针。在实际使用中,
// 可能需要通过其他方式获取 shared_ptr
// 或者修改 buffer 类接口。
//
// 这里返回 nullptr 作为占位实现,
// 实际的 buffer 创建需要在子类中处理,
// 因为子类可能有访问 shared_ptr<gpu_allocator> 的方式。
// TODO: 实现实际的 buffer 创建
// 当前返回空指针,子类需要重写此方法或使用其他方式创建 buffer
return nullptr;
}
} // namespace mirai

View File

@@ -0,0 +1,309 @@
/**
* @file shader_widget_base.hpp
* @brief MIRAI 框架 shader_widget 非模板基类
* @author MIRAI Team
* @version 0.1.0
*
* 本文件定义了 shader_widget 的非模板基类,包括:
* - 基本的渲染资源管理能力
* - 生命周期管理
* - 脏标记系统
* - 区域设置
*/
#pragma once
#include "object.hpp"
#include "widget_types.hpp"
#include "vulkan_types.hpp"
#include <memory>
namespace mirai {
// ================================================================================================
// 前向声明
// ================================================================================================
class vulkan_device;
class gpu_allocator;
class descriptor_pool;
class command_buffer;
class buffer;
class texture_view;
class sampler;
// ================================================================================================
// shader_widget_base 类
// ================================================================================================
/**
* @brief shader_widget 非模板基类
*
* 提供所有 shader_widget 的公共实现,避免模板代码膨胀。
* 子类通过继承此类获取基本的渲染资源管理能力。
*
* @note 此类不能直接实例化,必须通过派生类使用
*
* @example
* @code
* // shader_widget_base 作为所有着色器控件的基类
* // 通常不直接使用,而是通过模板化的 shader_widget<Bindings> 使用
* class my_widget : public shader_widget<my_bindings> {
* // 自定义实现
* };
* @endcode
*/
class shader_widget_base : public object {
public:
MIRAI_OBJECT_TYPE_INFO(shader_widget_base, object)
/**
* @brief 默认构造函数
*/
shader_widget_base();
/**
* @brief 虚析构函数
*/
~shader_widget_base() override;
// 禁止拷贝
shader_widget_base(const shader_widget_base&) = delete;
shader_widget_base& operator=(const shader_widget_base&) = delete;
// 禁止移动(因为只能通过智能指针管理)
shader_widget_base(shader_widget_base&&) = delete;
shader_widget_base& operator=(shader_widget_base&&) = delete;
// ============================================================================================
// 生命周期方法
// ============================================================================================
/**
* @brief 初始化 GPU 资源
*
* 初始化控件所需的 GPU 资源,包括管线、描述符集等。
* 必须在渲染前调用此方法。
*
* @param device Vulkan 设备
* @param allocator GPU 内存分配器
* @param pool 描述符池
*/
virtual void initialize(vulkan_device& device, gpu_allocator& allocator, descriptor_pool& pool);
/**
* @brief 销毁 GPU 资源
*
* 销毁控件持有的所有 GPU 资源。
* 在销毁控件或需要重新初始化时调用。
*/
virtual void destroy();
/**
* @brief 检查是否已初始化
* @return 如果已初始化返回 true
*/
[[nodiscard]] bool is_initialized() const noexcept { return initialized_; }
// ============================================================================================
// 渲染方法
// ============================================================================================
/**
* @brief 更新脏资源
*
* 检查并更新所有标记为脏的资源。
* 应在 draw() 之前调用。
*
* @param cmd 命令缓冲区(用于可能的数据传输)
*/
virtual void update(command_buffer& cmd);
/**
* @brief 记录绘制命令
*
* 将绘制命令记录到命令缓冲区。
* 子类必须实现此方法。
*
* @param cmd 命令缓冲区
*/
virtual void draw(command_buffer& cmd) = 0;
// ============================================================================================
// 状态管理
// ============================================================================================
/**
* @brief 标记为脏
*
* 标记指定的资源需要更新。
*
* @param flags 脏标记,默认为所有标记
*/
void mark_dirty(widget_dirty_flags flags = widget_dirty_flags::all);
/**
* @brief 清除脏标记
*
* 清除指定的脏标记。
*
* @param flags 要清除的标记
*/
void clear_dirty(widget_dirty_flags flags);
/**
* @brief 检查是否脏
*
* 检查是否有指定的脏标记。
*
* @param flag 要检查的标记
* @return 如果有指定标记返回 true
*/
[[nodiscard]] bool is_dirty(widget_dirty_flags flag) const noexcept;
/**
* @brief 获取当前脏标记
* @return 当前的脏标记集合
*/
[[nodiscard]] widget_dirty_flags dirty_flags() const noexcept { return dirty_flags_; }
// ============================================================================================
// 区域设置
// ============================================================================================
/**
* @brief 设置控件区域(相对于父控件)
*
* @param x X 坐标
* @param y Y 坐标
* @param width 宽度
* @param height 高度
*/
void set_bounds(f32 x, f32 y, f32 width, f32 height);
/**
* @brief 获取 X 坐标
* @return X 坐标
*/
[[nodiscard]] f32 x() const noexcept { return x_; }
/**
* @brief 获取 Y 坐标
* @return Y 坐标
*/
[[nodiscard]] f32 y() const noexcept { return y_; }
/**
* @brief 获取宽度
* @return 宽度
*/
[[nodiscard]] f32 width() const noexcept { return width_; }
/**
* @brief 获取高度
* @return 高度
*/
[[nodiscard]] f32 height() const noexcept { return height_; }
protected:
// ============================================================================================
// 子类实现的钩子方法
// ============================================================================================
/**
* @brief 创建管线
*
* 子类必须实现此方法来创建渲染管线。
* 在 initialize() 中调用。
*/
virtual void create_pipeline() = 0;
/**
* @brief 创建描述符集
*
* 子类必须实现此方法来创建描述符集。
* 在 initialize() 中调用。
*/
virtual void create_descriptor_sets() = 0;
/**
* @brief 更新 Uniform Buffer
*
* 子类必须实现此方法来更新 Uniform Buffer 数据。
* 在 update() 中当 uniform_buffer 标记为脏时调用。
*/
virtual void update_uniform_buffer() = 0;
// ============================================================================================
// 辅助方法
// ============================================================================================
/**
* @brief 创建 Uniform Buffer
*
* 使用 GPU 分配器创建指定大小的 Uniform Buffer。
*
* @param size 缓冲区大小(字节)
* @return 创建的缓冲区
*/
[[nodiscard]] std::unique_ptr<buffer> create_uniform_buffer(size_type size);
/**
* @brief 获取设备引用
* @return Vulkan 设备引用
*/
[[nodiscard]] vulkan_device& device() { return *device_; }
/**
* @brief 获取设备引用const 版本)
* @return Vulkan 设备常量引用
*/
[[nodiscard]] const vulkan_device& device() const { return *device_; }
/**
* @brief 获取分配器引用
* @return GPU 内存分配器引用
*/
[[nodiscard]] gpu_allocator& allocator() { return *allocator_; }
/**
* @brief 获取描述符池引用
* @return 描述符池引用
*/
[[nodiscard]] descriptor_pool& pool() { return *pool_; }
protected:
// ============================================================================================
// 成员变量
// ============================================================================================
/// 设备引用(不持有所有权)
vulkan_device* device_ = nullptr;
/// 分配器引用(不持有所有权)
gpu_allocator* allocator_ = nullptr;
/// 描述符池引用(不持有所有权)
descriptor_pool* pool_ = nullptr;
/// 是否已初始化
bool initialized_ = false;
/// 脏标记
widget_dirty_flags dirty_flags_ = widget_dirty_flags::all;
/// X 坐标
f32 x_ = 0.0f;
/// Y 坐标
f32 y_ = 0.0f;
/// 宽度
f32 width_ = 0.0f;
/// 高度
f32 height_ = 0.0f;
};
} // namespace mirai

220
src/widget/widget_types.hpp Normal file
View File

@@ -0,0 +1,220 @@
/**
* @file widget_types.hpp
* @brief MIRAI 框架 Widget 模块基础类型定义
* @author MIRAI Team
* @version 0.1.0
*
* 本文件定义了 Widget 模块使用的基础类型,包括:
* - 着色器绑定 Concepts
* - 渲染模式枚举
* - 脏标记枚举
*/
#pragma once
#include "types/types.hpp"
#include <concepts>
#include <span>
#include <cstdint>
namespace mirai {
// ================================================================================================
// 前向声明
// ================================================================================================
class vulkan_device;
class gpu_allocator;
class descriptor_pool;
class command_buffer;
class texture_view;
class sampler;
// ================================================================================================
// 着色器绑定 Concepts
// ================================================================================================
/**
* @brief 基础绑定检查
*
* 所有绑定类型必须满足此 concept提供基本的着色器信息
*/
template<typename T>
concept shader_bindings = requires {
/// 是否有顶点着色器
{ T::HAS_VERTEX_SHADER } -> std::convertible_to<bool>;
/// 是否有片段着色器
{ T::HAS_FRAGMENT_SHADER } -> std::convertible_to<bool>;
/// 获取顶点着色器 SPIR-V 数据
{ T::get_vertex_spirv() } -> std::same_as<std::span<const u32>>;
/// 获取片段着色器 SPIR-V 数据
{ T::get_fragment_spirv() } -> std::same_as<std::span<const u32>>;
};
/**
* @brief Backdrop 模式绑定检查
*
* 用于后效处理模式的绑定类型必须满足此 concept
*/
template<typename T>
concept backdrop_bindings = shader_bindings<T> && requires {
/// 是否有背景采样器
{ T::HAS_BACKDROP_SAMPLER } -> std::convertible_to<bool>;
/// 背景纹理绑定点
{ T::BACKDROP_BINDING } -> std::convertible_to<u32>;
/// 背景纹理所在描述符集
{ T::BACKDROP_SET } -> std::convertible_to<u32>;
};
/**
* @brief Procedural 模式绑定检查
*
* 用于程序化渲染模式的绑定类型必须满足此 concept
*/
template<typename T>
concept procedural_bindings = shader_bindings<T> && requires {
/// 是否有帧数据
{ T::HAS_FRAME_DATA } -> std::convertible_to<bool>;
/// 帧数据绑定点
{ T::FRAME_DATA_BINDING } -> std::convertible_to<u32>;
/// 帧数据所在描述符集
{ T::FRAME_DATA_SET } -> std::convertible_to<u32>;
};
/**
* @brief Mask 模式绑定检查
*
* 用于遮罩效果模式的绑定类型必须满足此 concept
*/
template<typename T>
concept mask_bindings = shader_bindings<T> && requires {
/// 是否有遮罩采样器
{ T::HAS_MASK_SAMPLER } -> std::convertible_to<bool>;
/// 遮罩纹理绑定点
{ T::MASK_BINDING } -> std::convertible_to<u32>;
/// 遮罩纹理所在描述符集
{ T::MASK_SET } -> std::convertible_to<u32>;
};
/**
* @brief Mesh 模式绑定检查
*
* 用于自定义网格渲染模式的绑定类型必须满足此 concept
*/
template<typename T>
concept mesh_bindings = shader_bindings<T> && requires {
/// 是否有自定义顶点输入
{ T::HAS_CUSTOM_VERTEX_INPUT } -> std::convertible_to<bool>;
/// 顶点类型
typename T::VertexType;
};
// ================================================================================================
// 渲染模式枚举
// ================================================================================================
/**
* @brief 着色器控件渲染模式
*
* 定义了着色器控件支持的四种渲染模式
*/
enum class shader_widget_mode : u8 {
/// 后效处理模式:采样背景纹理进行处理
backdrop,
/// 程序化渲染模式:基于时间和坐标的程序化生成
procedural,
/// 遮罩模式:使用遮罩纹理进行裁剪或混合
mask,
/// 自定义网格模式:使用自定义顶点数据渲染
mesh
};
// ================================================================================================
// 脏标记枚举
// ================================================================================================
/**
* @brief 控件脏标记
*
* 用于标记哪些资源需要更新
*/
enum class widget_dirty_flags : u32 {
/// 无脏标记
none = 0,
/// Uniform Buffer 需要更新
uniform_buffer = 1 << 0,
/// 描述符集需要更新
descriptor_set = 1 << 1,
/// 管线需要重建
pipeline = 1 << 2,
/// 顶点缓冲区需要更新
vertex_buffer = 1 << 3,
/// 所有标记
all = 0xFFFFFFFF
};
// ================================================================================================
// 位运算支持
// ================================================================================================
/**
* @brief 脏标记按位或运算
* @param a 第一个操作数
* @param b 第二个操作数
* @return 运算结果
*/
constexpr widget_dirty_flags operator|(widget_dirty_flags a, widget_dirty_flags b) {
return static_cast<widget_dirty_flags>(static_cast<u32>(a) | static_cast<u32>(b));
}
/**
* @brief 脏标记按位与运算
* @param a 第一个操作数
* @param b 第二个操作数
* @return 运算结果
*/
constexpr widget_dirty_flags operator&(widget_dirty_flags a, widget_dirty_flags b) {
return static_cast<widget_dirty_flags>(static_cast<u32>(a) & static_cast<u32>(b));
}
/**
* @brief 脏标记按位或赋值运算
* @param a 被赋值的操作数
* @param b 第二个操作数
* @return 赋值后的引用
*/
constexpr widget_dirty_flags& operator|=(widget_dirty_flags& a, widget_dirty_flags b) {
return a = a | b;
}
/**
* @brief 脏标记按位与赋值运算
* @param a 被赋值的操作数
* @param b 第二个操作数
* @return 赋值后的引用
*/
constexpr widget_dirty_flags& operator&=(widget_dirty_flags& a, widget_dirty_flags b) {
return a = a & b;
}
/**
* @brief 脏标记按位取反运算
* @param a 操作数
* @return 取反结果
*/
constexpr widget_dirty_flags operator~(widget_dirty_flags a) {
return static_cast<widget_dirty_flags>(~static_cast<u32>(a));
}
/**
* @brief 检查是否包含指定标记
* @param flags 标记集合
* @param flag 要检查的标记
* @return 如果包含返回 true
*/
[[nodiscard]] constexpr bool has_flag(widget_dirty_flags flags, widget_dirty_flags flag) {
return (static_cast<u32>(flags) & static_cast<u32>(flag)) != 0;
}
} // namespace mirai

View File

@@ -1,10 +1,13 @@
# tools/shader_compile/CMakeLists.txt
# MIRAI 着色器编译器工具
# 用于从 SPIR-V 提取反射信息并生成绑定代码
cmake_minimum_required(VERSION 3.20)
# 查找 Slang SDK (通过 vcpkg)
find_package(slang CONFIG REQUIRED)
# 查找 spirv-cross 用于反射
find_package(spirv_cross_core CONFIG REQUIRED)
find_package(spirv_cross_glsl CONFIG REQUIRED)
find_package(spirv_cross_reflect CONFIG REQUIRED)
# 查找 nlohmann_json 用于反射 JSON 输出
find_package(nlohmann_json CONFIG REQUIRED)
@@ -17,7 +20,9 @@ add_executable(mirai_shader_compile
target_compile_features(mirai_shader_compile PRIVATE cxx_std_20)
target_link_libraries(mirai_shader_compile PRIVATE
slang::slang
spirv-cross-core
spirv-cross-glsl
spirv-cross-reflect
nlohmann_json::nlohmann_json
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,12 @@
// tools/shader_compile/compiler.hpp
// MIRAI 着色器编译器封装
// MIRAI 着色器反射工具
// 使用 spirv-cross 从 SPIR-V 提取反射信息
#pragma once
#include <cstdint>
#include <filesystem>
#include <memory>
#include <optional>
#include <slang.h>
#include <string>
#include <vector>
@@ -23,6 +22,7 @@ enum class shader_stage {
geometry,
tessellation_control,
tessellation_evaluation,
unknown
};
/**
@@ -36,157 +36,138 @@ enum class shader_stage {
[[nodiscard]] const char* shader_stage_to_string(shader_stage stage);
/**
* @brief 将 Slang 阶段转换为 shader_stage
* @brief 从文件扩展名推断着色器阶段
*/
[[nodiscard]] std::optional<shader_stage> slang_stage_to_shader_stage(SlangStage stage);
[[nodiscard]] shader_stage stage_from_extension(const std::string& ext);
/**
* @brief 编译结果
* @brief Uniform Buffer 成员信息
*/
struct compile_result {
struct uniform_member {
std::string name;
std::string type;
uint32_t offset{0};
uint32_t size{0};
uint32_t array_size{0}; // 0 表示非数组
};
/**
* @brief Uniform Buffer 信息
*/
struct uniform_buffer_info {
std::string name;
uint32_t set{0};
uint32_t binding{0};
uint32_t size{0};
std::vector<uniform_member> members;
};
/**
* @brief Push Constant 信息
*/
struct push_constant_info {
std::string name;
uint32_t size{0};
std::vector<uniform_member> members;
};
/**
* @brief 采样器/纹理信息
*/
struct sampler_info {
std::string name;
uint32_t set{0};
uint32_t binding{0};
std::string dimension; // "1D", "2D", "3D", "Cube"
bool is_array{false};
};
/**
* @brief 顶点输入属性信息
*/
struct vertex_attribute_info {
std::string name;
uint32_t location{0};
std::string type;
};
/**
* @brief 着色器反射数据
*/
struct shader_reflection {
std::string entry_point{"main"};
shader_stage stage{shader_stage::unknown};
std::vector<uniform_buffer_info> uniform_buffers;
std::vector<push_constant_info> push_constants;
std::vector<sampler_info> samplers;
std::vector<vertex_attribute_info> vertex_inputs;
std::vector<vertex_attribute_info> fragment_outputs;
};
/**
* @brief SPIR-V 反射结果
*/
struct reflection_result {
bool success{false};
std::string entry_point; // 入口点名称
shader_stage stage; // 着色器阶段
std::vector<uint32_t> spirv;
shader_reflection reflection;
std::string reflection_json;
std::string error_message;
std::vector<std::filesystem::path> dependencies;
};
/**
* @brief 编译选项
*/
struct compile_options {
std::string entry_point{"main"};
std::optional<shader_stage> stage;
std::vector<std::filesystem::path> include_paths;
std::vector<std::pair<std::string, std::string>> defines;
bool generate_debug_info{false};
bool optimize{true};
bool emit_spirv{true};
bool emit_reflection{false};
};
/**
* @brief 入口点信息(包含名称和阶段)
*/
struct entry_point_info {
std::string name;
shader_stage stage;
};
/**
* @brief 着色器编译器
* @brief SPIR-V 反射器
*
* 封装 Slang SDK用于将 .slang 文件编译为 SPIR-V
* 使用 spirv-cross 从 SPIR-V 字节码提取反射信息
*/
class shader_compiler {
class spirv_reflector {
public:
shader_compiler();
~shader_compiler();
spirv_reflector() = default;
~spirv_reflector() = default;
// 禁止拷贝
shader_compiler(const shader_compiler&) = delete;
shader_compiler& operator=(const shader_compiler&) = delete;
spirv_reflector(const spirv_reflector&) = delete;
spirv_reflector& operator=(const spirv_reflector&) = delete;
// 允许移动
shader_compiler(shader_compiler&&) noexcept;
shader_compiler& operator=(shader_compiler&&) noexcept;
spirv_reflector(spirv_reflector&&) noexcept = default;
spirv_reflector& operator=(spirv_reflector&&) noexcept = default;
/**
* @brief 编译着色器文件
* @param path 输入文件路径
* @param options 编译选项
* @return 编译结果
* @brief 从 SPIR-V 文件提取反射信息
* @param path SPIR-V 文件路径
* @return 反射结果
*/
[[nodiscard]] compile_result compile_file(
const std::filesystem::path& path,
const compile_options& options
[[nodiscard]] reflection_result reflect_file(const std::filesystem::path& path);
/**
* @brief 从 SPIR-V 字节码提取反射信息
* @param spirv SPIR-V 字节码
* @param filename 文件名(用于错误报告和阶段推断)
* @return 反射结果
*/
[[nodiscard]] reflection_result reflect_spirv(
const std::vector<uint32_t>& spirv,
const std::string& filename = ""
);
/**
* @brief 编译着色器源码
* @param source 着色器源码
* @param filename 虚拟文件名(用于错误报告)
* @param options 编译选项
* @return 编译结果
* @brief 从目录中的所有 SPIR-V 文件提取反射信息
* @param dir 目录路径
* @return 反射结果列表
*/
[[nodiscard]] compile_result compile_source(
const std::string& source,
const std::string& filename,
const compile_options& options
[[nodiscard]] std::vector<reflection_result> reflect_directory(
const std::filesystem::path& dir
);
/**
* @brief 仅生成反射数据
* @param path 输入文件路径
* @param options 编译选项
* @return 反射 JSON 字符串,失败返回 nullopt
*/
[[nodiscard]] std::optional<std::string> generate_reflection(
const std::filesystem::path& path,
const compile_options& options
);
/**
* @brief 检查着色器文件是否存在入口函数
* @param path 输入文件路径
* @param options 编译选项
* @return std::nullopt 表示无法检查,
* true 表示存在入口函数,
* false 表示不存在入口函数
*/
[[nodiscard]] std::optional<bool> has_entry_point(
const std::filesystem::path& path,
const compile_options& options
);
/**
* @brief 获取模块中的所有入口函数
* @param path 输入文件路径
* @param options 编译选项
* @return 入口函数名称列表
*/
[[nodiscard]] std::vector<std::string> get_entry_points(
const std::filesystem::path& path,
const compile_options& options
);
/**
* @brief 获取模块中的所有入口函数及其阶段
* @param path 输入文件路径
* @param options 编译选项
* @return 入口点信息列表
*/
[[nodiscard]] std::vector<entry_point_info> get_entry_points_with_stages(
const std::filesystem::path& path,
const compile_options& options
);
/**
* @brief 编译着色器文件(自动检测所有入口点)
* @param path 输入文件路径
* @param options 编译选项
* @return 编译结果列表(每个入口点一个结果)
*/
[[nodiscard]] std::vector<compile_result> compile_file_all_entries(
const std::filesystem::path& path,
const compile_options& options
);
/**
* @brief 检查编译器是否可用
*/
[[nodiscard]] bool is_available() const noexcept;
/**
* @brief 获取 Slang 版本信息
*/
[[nodiscard]] std::string get_version() const;
private:
struct impl;
std::unique_ptr<impl> impl_;
};
/**
* @brief 将反射数据转换为 JSON 字符串
*/
[[nodiscard]] std::string reflection_to_json(const shader_reflection& reflection);
/**
* @brief 将多个反射数据合并为一个 JSON 字符串
*/
[[nodiscard]] std::string reflections_to_json(const std::vector<shader_reflection>& reflections);
} // namespace mirai::tools

View File

@@ -1,6 +1,6 @@
// tools/shader_compile/main.cpp
// MIRAI 着色器编译器命令行入口
// 自动检测所有 [shader("xxx")] 入口点并编译
// MIRAI 着色器反射工具命令行入口
// 从 SPIR-V 文件提取反射信息并生成绑定代码
#include "compiler.hpp"
@@ -13,47 +13,46 @@ namespace {
void print_usage(const char* program_name) {
std::cout << R"(
MIRAI Shader Compiler - Auto Mode
MIRAI Shader Reflection Tool
Usage: )" << program_name << R"( [options] <input.slang>
Usage: )" << program_name << R"( [options]
Options:
-o, --output <dir> Output directory for compiled shaders
-I, --include <path> Add include search path
-D, --define <MACRO> Define preprocessor macro (e.g., -DDEBUG or -DVALUE=1)
--prefix <name> Prefix for output file names (default: shader name)
-g Generate debug info
-O0 Disable optimization
--list Only list entry points, don't compile
--dir <path> Directory containing SPIR-V files
--file <path> Single SPIR-V file to process
--output <path> Output file path (header or JSON)
--name <name> Shader group name (for header generation)
--json Output JSON reflection data
--header Output C++ header with bindings (default)
-h, --help Show this help
-v, --version Show version info
Examples:
)" << program_name << R"( shader.slang -o ./compiled_shaders
)" << program_name << R"( shader.slang -o ./shaders --prefix myshader
)" << program_name << R"( shader.slang --list
)" << program_name << R"( --dir ./shaders --output bindings.hpp --name my_shader
)" << program_name << R"( --file shader.vert.spv --output shader.json --json
)" << program_name << R"( --dir ./compiled --output reflection.json --json
Output Format:
Automatically compiles all entry points found via [shader("xxx")] attributes.
Output files: <prefix>.<entry_point>.<stage>.spv
<prefix>.<entry_point>.<stage>.reflect.json
Output Formats:
Header (.hpp): C++ header with embedded SPIR-V and reflection data
JSON (.json): Structured reflection data for external tools
)";
}
void print_version() {
std::cout << "MIRAI Shader Compiler v2.0.0 (Auto Mode)\n";
std::cout << "Based on Slang Shader Language\n";
std::cout << "MIRAI Shader Reflection Tool v1.0.0\n";
std::cout << "Based on SPIRV-Cross\n";
}
struct command_line_args {
std::filesystem::path input_path;
std::filesystem::path output_dir;
std::string prefix;
mirai::tools::compile_options options;
std::filesystem::path input_dir;
std::filesystem::path input_file;
std::filesystem::path output_path;
std::string name;
bool output_json = false;
bool output_header = true;
bool show_help = false;
bool show_version = false;
bool list_only = false;
};
bool parse_args(int argc, char* argv[], command_line_args& args) {
@@ -68,67 +67,41 @@ bool parse_args(int argc, char* argv[], command_line_args& args) {
args.show_version = true;
return true;
}
if (arg == "-o" || arg == "--output") {
if (arg == "--dir") {
if (++i >= argc) {
std::cerr << "Error: -o requires an argument\n";
std::cerr << "Error: --dir requires an argument\n";
return false;
}
args.output_dir = argv[i];
args.input_dir = argv[i];
}
else if (arg == "-I" || arg == "--include") {
else if (arg == "--file") {
if (++i >= argc) {
std::cerr << "Error: -I requires an argument\n";
std::cerr << "Error: --file requires an argument\n";
return false;
}
args.options.include_paths.emplace_back(argv[i]);
args.input_file = argv[i];
}
else if (arg.starts_with("-I")) {
args.options.include_paths.emplace_back(arg.substr(2));
}
else if (arg == "-D" || arg == "--define") {
else if (arg == "--output" || arg == "-o") {
if (++i >= argc) {
std::cerr << "Error: -D requires an argument\n";
std::cerr << "Error: --output requires an argument\n";
return false;
}
std::string def = argv[i];
auto eq_pos = def.find('=');
if (eq_pos != std::string::npos) {
args.options.defines.emplace_back(def.substr(0, eq_pos), def.substr(eq_pos + 1));
} else {
args.options.defines.emplace_back(def, "1");
}
args.output_path = argv[i];
}
else if (arg.starts_with("-D")) {
std::string def = arg.substr(2);
auto eq_pos = def.find('=');
if (eq_pos != std::string::npos) {
args.options.defines.emplace_back(def.substr(0, eq_pos), def.substr(eq_pos + 1));
} else {
args.options.defines.emplace_back(def, "1");
}
}
else if (arg == "--prefix") {
else if (arg == "--name") {
if (++i >= argc) {
std::cerr << "Error: --prefix requires an argument\n";
std::cerr << "Error: --name requires an argument\n";
return false;
}
args.prefix = argv[i];
args.name = argv[i];
}
else if (arg == "--list") {
args.list_only = true;
else if (arg == "--json") {
args.output_json = true;
args.output_header = false;
}
else if (arg == "-g") {
args.options.generate_debug_info = true;
}
else if (arg == "-O0") {
args.options.optimize = false;
}
else if (!arg.starts_with("-")) {
if (!args.input_path.empty()) {
std::cerr << "Error: Multiple input files specified\n";
return false;
}
args.input_path = arg;
else if (arg == "--header") {
args.output_header = true;
args.output_json = false;
}
else {
std::cerr << "Error: Unknown option: " << arg << "\n";
@@ -139,29 +112,6 @@ bool parse_args(int argc, char* argv[], command_line_args& args) {
return true;
}
std::string stage_to_suffix(mirai::tools::shader_stage stage) {
switch (stage) {
case mirai::tools::shader_stage::vertex: return "vert";
case mirai::tools::shader_stage::fragment: return "frag";
case mirai::tools::shader_stage::compute: return "comp";
case mirai::tools::shader_stage::geometry: return "geom";
case mirai::tools::shader_stage::tessellation_control: return "tesc";
case mirai::tools::shader_stage::tessellation_evaluation: return "tese";
}
return "unknown";
}
bool write_spirv(const std::filesystem::path& path, const std::vector<uint32_t>& spirv) {
std::ofstream file(path, std::ios::binary);
if (!file) {
std::cerr << "Error: Failed to open output file: " << path << "\n";
return false;
}
file.write(reinterpret_cast<const char*>(spirv.data()), spirv.size() * sizeof(uint32_t));
return file.good();
}
bool write_text(const std::filesystem::path& path, const std::string& text) {
std::ofstream file(path);
if (!file) {
@@ -173,6 +123,112 @@ bool write_text(const std::filesystem::path& path, const std::string& text) {
return file.good();
}
std::vector<uint8_t> read_binary_file(const std::filesystem::path& path) {
std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file) {
return {};
}
auto size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<uint8_t> data(size);
file.read(reinterpret_cast<char*>(data.data()), size);
return data;
}
std::string generate_header(
const std::string& name,
const std::vector<mirai::tools::reflection_result>& results
) {
std::ostringstream ss;
// Header guard
std::string guard_name = name;
for (auto& c : guard_name) {
c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
}
ss << "// Auto-generated shader bindings for: " << name << "\n";
ss << "// DO NOT EDIT - Generated by mirai_shader_compile\n\n";
ss << "#pragma once\n\n";
ss << "#include <array>\n";
ss << "#include <cstdint>\n";
ss << "#include <string_view>\n\n";
ss << "namespace mirai::shaders {\n\n";
ss << "namespace " << name << " {\n\n";
// Generate SPIR-V data for each shader
for (const auto& result : results) {
if (!result.success) {
continue;
}
const auto& reflection = result.reflection;
std::string stage_name = mirai::tools::shader_stage_to_string(reflection.stage);
ss << "// " << stage_name << " shader\n";
ss << "namespace " << stage_name << " {\n\n";
ss << "constexpr std::string_view entry_point = \"" << reflection.entry_point << "\";\n\n";
// Uniform buffer info
if (!reflection.uniform_buffers.empty()) {
ss << "// Uniform Buffers\n";
for (const auto& ub : reflection.uniform_buffers) {
ss << "struct " << ub.name << " {\n";
ss << " static constexpr uint32_t set = " << ub.set << ";\n";
ss << " static constexpr uint32_t binding = " << ub.binding << ";\n";
ss << " static constexpr uint32_t size = " << ub.size << ";\n";
ss << "};\n\n";
}
}
// Push constant info
if (!reflection.push_constants.empty()) {
ss << "// Push Constants\n";
for (const auto& pc : reflection.push_constants) {
ss << "struct " << pc.name << " {\n";
ss << " static constexpr uint32_t size = " << pc.size << ";\n";
ss << "};\n\n";
}
}
// Sampler info
if (!reflection.samplers.empty()) {
ss << "// Samplers\n";
for (const auto& sampler : reflection.samplers) {
ss << "struct " << sampler.name << "_info {\n";
ss << " static constexpr uint32_t set = " << sampler.set << ";\n";
ss << " static constexpr uint32_t binding = " << sampler.binding << ";\n";
ss << " static constexpr std::string_view dimension = \"" << sampler.dimension << "\";\n";
ss << "};\n\n";
}
}
ss << "} // namespace " << stage_name << "\n\n";
}
// Reflection JSON
ss << "// Combined reflection data\n";
ss << "constexpr std::string_view reflection_json = R\"JSON(\n";
std::vector<mirai::tools::shader_reflection> reflections;
for (const auto& result : results) {
if (result.success) {
reflections.push_back(result.reflection);
}
}
ss << mirai::tools::reflections_to_json(reflections);
ss << "\n)JSON\";\n\n";
ss << "} // namespace " << name << "\n\n";
ss << "} // namespace mirai::shaders\n";
return ss.str();
}
} // anonymous namespace
int main(int argc, char* argv[]) {
@@ -192,87 +248,80 @@ int main(int argc, char* argv[]) {
return 0;
}
if (args.input_path.empty()) {
std::cerr << "Error: No input file specified\n";
if (args.input_dir.empty() && args.input_file.empty()) {
std::cerr << "Error: No input specified. Use --dir or --file.\n";
print_usage(argv[0]);
return 1;
}
// 创建编译器
mirai::tools::shader_compiler compiler;
if (!compiler.is_available()) {
std::cerr << "Error: Shader compiler is not available\n";
if (args.output_path.empty()) {
std::cerr << "Error: No output path specified. Use --output.\n";
return 1;
}
// 获取所有入口点及其阶段
auto entry_points = compiler.get_entry_points_with_stages(args.input_path, args.options);
// Create reflector
mirai::tools::spirv_reflector reflector;
std::vector<mirai::tools::reflection_result> results;
if (entry_points.empty()) {
std::cout << "Warning: No entry points found in " << args.input_path << "\n";
std::cout << " (This may be a module file with no [shader(...)] attributes)\n";
// Process input
if (!args.input_dir.empty()) {
results = reflector.reflect_directory(args.input_dir);
} else if (!args.input_file.empty()) {
auto result = reflector.reflect_file(args.input_file);
results.push_back(result);
}
if (results.empty()) {
std::cout << "Warning: No SPIR-V files found to process.\n";
return 0;
}
// 列出入口点
std::cout << "Found " << entry_points.size() << " entry point(s) in " << args.input_path.filename() << ":\n";
for (const auto& ep : entry_points) {
std::cout << " - " << ep.name << " [" << mirai::tools::shader_stage_to_string(ep.stage) << "]\n";
}
// 如果只是列出,不编译
if (args.list_only) {
return 0;
}
// 确保输出目录存在
if (args.output_dir.empty()) {
args.output_dir = ".";
}
if (!std::filesystem::exists(args.output_dir)) {
std::filesystem::create_directories(args.output_dir);
}
// 确定前缀
if (args.prefix.empty()) {
args.prefix = args.input_path.stem().string();
}
// 编译所有入口点
args.options.emit_spirv = true;
args.options.emit_reflection = true;
auto results = compiler.compile_file_all_entries(args.input_path, args.options);
int success_count = 0;
int fail_count = 0;
// Check for errors
int error_count = 0;
for (const auto& result : results) {
if (!result.success) {
std::cerr << "Error compiling " << result.entry_point << ": " << result.error_message << "\n";
fail_count++;
continue;
std::cerr << "Error: " << result.error_message << "\n";
error_count++;
}
std::string suffix = stage_to_suffix(result.stage);
std::string base_name = args.prefix + "." + result.entry_point + "." + suffix;
// 写入 SPIR-V
std::filesystem::path spv_path = args.output_dir / (base_name + ".spv");
if (write_spirv(spv_path, result.spirv)) {
std::cout << "Generated: " << spv_path << " (" << result.spirv.size() * 4 << " bytes)\n";
}
// 写入反射 JSON
std::filesystem::path reflect_path = args.output_dir / (base_name + ".reflect.json");
if (write_text(reflect_path, result.reflection_json)) {
std::cout << "Generated: " << reflect_path << "\n";
}
success_count++;
}
std::cout << "\nSummary: " << success_count << " succeeded, " << fail_count << " failed\n";
// Generate output
std::string output;
if (args.output_json) {
std::vector<mirai::tools::shader_reflection> reflections;
for (const auto& result : results) {
if (result.success) {
reflections.push_back(result.reflection);
}
}
output = mirai::tools::reflections_to_json(reflections);
} else {
// Header output
if (args.name.empty()) {
// Try to derive name from output path
args.name = args.output_path.stem().string();
// Remove _bindings suffix if present
if (args.name.ends_with("_bindings")) {
args.name = args.name.substr(0, args.name.length() - 9);
}
}
output = generate_header(args.name, results);
}
return fail_count > 0 ? 1 : 0;
// Write output
if (!write_text(args.output_path, output)) {
return 1;
}
std::cout << "Generated: " << args.output_path << "\n";
int success_count = static_cast<int>(results.size()) - error_count;
std::cout << "Processed " << success_count << " shader(s)";
if (error_count > 0) {
std::cout << " (" << error_count << " error(s))";
}
std::cout << "\n";
return error_count > 0 ? 1 : 0;
}

View File

@@ -13,8 +13,8 @@
"version>=": "3.2.28"
},
{
"name": "shader-slang",
"version>=": "2025.22.1"
"name": "spirv-cross",
"version>=": "1.3.296.0"
},
{
"name": "nlohmann-json",