init
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/.idea
|
||||
/__pycache__
|
||||
27
__init__.py
Normal file
27
__init__.py
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SDL3_GPU Slang Compiler Package
|
||||
自动处理资源绑定点的Slang着色器编译器
|
||||
"""
|
||||
|
||||
from shader_types import ShaderStage, ResourceType, TargetFormat, Resource, ShaderInfo
|
||||
from compiler import SDL3GPUSlangCompiler
|
||||
from slangc_finder import SlangcFinder
|
||||
from shader_parser import ShaderParser
|
||||
from binding_manager import BindingManager
|
||||
from code_generator import CodeGenerator
|
||||
|
||||
__all__ = [
|
||||
'ShaderStage',
|
||||
'ResourceType',
|
||||
'TargetFormat',
|
||||
'Resource',
|
||||
'ShaderInfo',
|
||||
'SDL3GPUSlangCompiler',
|
||||
'SlangcFinder',
|
||||
'ShaderParser',
|
||||
'BindingManager',
|
||||
'CodeGenerator'
|
||||
]
|
||||
|
||||
version = '1.0.0'
|
||||
285
binding_manager.py
Normal file
285
binding_manager.py
Normal file
@@ -0,0 +1,285 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SDL3_GPU Slang Compiler - Binding Manager
|
||||
资源绑定点分配和管理
|
||||
"""
|
||||
|
||||
import re
|
||||
import os
|
||||
from typing import Dict
|
||||
|
||||
from shader_types import ShaderStage, ResourceType, TargetFormat, ShaderInfo
|
||||
|
||||
|
||||
class BindingManager:
|
||||
def assign_bindings_spirv(self, shader_info: ShaderInfo):
|
||||
"""为SPIR-V着色器分配绑定点"""
|
||||
|
||||
if shader_info.stage == ShaderStage.VERTEX:
|
||||
texture_set = 0
|
||||
uniform_set = 1
|
||||
elif shader_info.stage == ShaderStage.FRAGMENT:
|
||||
texture_set = 2
|
||||
uniform_set = 3
|
||||
else:
|
||||
raise ValueError(f"Unsupported shader stage: {shader_info.stage}")
|
||||
|
||||
# 按类型分组资源
|
||||
sampled_textures = []
|
||||
storage_textures = []
|
||||
storage_buffers = []
|
||||
uniform_buffers = []
|
||||
samplers = []
|
||||
|
||||
for resource in shader_info.resources:
|
||||
if resource.type == ResourceType.SAMPLED_TEXTURE:
|
||||
sampled_textures.append(resource)
|
||||
elif resource.type == ResourceType.STORAGE_TEXTURE:
|
||||
storage_textures.append(resource)
|
||||
elif resource.type == ResourceType.STORAGE_BUFFER:
|
||||
storage_buffers.append(resource)
|
||||
elif resource.type == ResourceType.UNIFORM_BUFFER:
|
||||
uniform_buffers.append(resource)
|
||||
elif resource.type == ResourceType.SAMPLER:
|
||||
samplers.append(resource)
|
||||
|
||||
# 分配绑定点 - Set 0/2: 采样纹理、存储纹理、存储缓冲区
|
||||
binding = 0
|
||||
for tex in sampled_textures:
|
||||
tex.set = texture_set
|
||||
tex.binding = binding
|
||||
binding += 1
|
||||
|
||||
for tex in storage_textures:
|
||||
tex.set = texture_set
|
||||
tex.binding = binding
|
||||
binding += 1
|
||||
|
||||
for buf in storage_buffers:
|
||||
buf.set = texture_set
|
||||
buf.binding = binding
|
||||
binding += 1
|
||||
|
||||
# 分配绑定点 - Set 1/3: Uniform缓冲区
|
||||
binding = 0
|
||||
for buf in uniform_buffers:
|
||||
buf.set = uniform_set
|
||||
buf.binding = binding
|
||||
binding += 1
|
||||
|
||||
# Samplers通常和sampled textures配对
|
||||
for i, sampler in enumerate(samplers):
|
||||
if i < len(sampled_textures):
|
||||
sampler.set = sampled_textures[i].set
|
||||
sampler.binding = sampled_textures[i].binding
|
||||
|
||||
|
||||
def assign_bindings_dxil(self, shader_info: ShaderInfo):
|
||||
"""为DXIL/DXBC着色器分配绑定点"""
|
||||
if shader_info.stage == ShaderStage.VERTEX:
|
||||
texture_space = 0
|
||||
uniform_space = 1
|
||||
elif shader_info.stage == ShaderStage.FRAGMENT:
|
||||
texture_space = 2
|
||||
uniform_space = 3
|
||||
else:
|
||||
raise ValueError(f"Unsupported shader stage: {shader_info.stage}")
|
||||
|
||||
# 分组资源
|
||||
textures = [] # t registers
|
||||
samplers = [] # s registers
|
||||
uniforms = [] # b registers
|
||||
|
||||
for resource in shader_info.resources:
|
||||
if resource.type in [ResourceType.SAMPLED_TEXTURE, ResourceType.STORAGE_TEXTURE, ResourceType.STORAGE_BUFFER]:
|
||||
textures.append(resource)
|
||||
elif resource.type == ResourceType.SAMPLER:
|
||||
samplers.append(resource)
|
||||
elif resource.type == ResourceType.UNIFORM_BUFFER:
|
||||
uniforms.append(resource)
|
||||
|
||||
# 分配t寄存器
|
||||
for i, tex in enumerate(textures):
|
||||
tex.register = f"t{i}"
|
||||
tex.space = texture_space
|
||||
|
||||
# 分配s寄存器
|
||||
for i, samp in enumerate(samplers):
|
||||
samp.register = f"s{i}"
|
||||
samp.space = texture_space
|
||||
|
||||
# 分配b寄存器
|
||||
for i, buf in enumerate(uniforms):
|
||||
buf.register = f"b{i}"
|
||||
buf.space = uniform_space
|
||||
|
||||
|
||||
def assign_bindings_msl(self, shader_info: ShaderInfo):
|
||||
"""为MSL着色器分配绑定点"""
|
||||
texture_index = 0
|
||||
sampler_index = 0
|
||||
buffer_index = 0
|
||||
|
||||
# 按SDL3要求的顺序分配
|
||||
# 纹理:采样纹理,然后存储纹理
|
||||
for resource in shader_info.resources:
|
||||
if resource.type == ResourceType.SAMPLED_TEXTURE:
|
||||
resource.metal_index = texture_index
|
||||
texture_index += 1
|
||||
|
||||
for resource in shader_info.resources:
|
||||
if resource.type == ResourceType.STORAGE_TEXTURE:
|
||||
resource.metal_index = texture_index
|
||||
texture_index += 1
|
||||
|
||||
# 采样器
|
||||
for resource in shader_info.resources:
|
||||
if resource.type == ResourceType.SAMPLER:
|
||||
resource.metal_index = sampler_index
|
||||
sampler_index += 1
|
||||
|
||||
# 缓冲区:uniform缓冲区,然后存储缓冲区
|
||||
for resource in shader_info.resources:
|
||||
if resource.type == ResourceType.UNIFORM_BUFFER:
|
||||
resource.metal_index = buffer_index
|
||||
buffer_index += 1
|
||||
|
||||
for resource in shader_info.resources:
|
||||
if resource.type == ResourceType.STORAGE_BUFFER:
|
||||
resource.metal_index = buffer_index
|
||||
buffer_index += 1
|
||||
|
||||
|
||||
def generate_binding_code(self, shader_info: ShaderInfo, target: TargetFormat) -> str:
|
||||
"""生成绑定代码"""
|
||||
if target == TargetFormat.SPIRV:
|
||||
return self._generate_spirv_bindings(shader_info)
|
||||
elif target in [TargetFormat.DXIL, TargetFormat.DXBC]:
|
||||
return self._generate_dx_bindings(shader_info)
|
||||
elif target == TargetFormat.MSL:
|
||||
return self._generate_msl_bindings(shader_info)
|
||||
return ''
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _generate_spirv_bindings(shader_info: ShaderInfo) -> str:
|
||||
"""生成SPIR-V绑定属性"""
|
||||
bindings = []
|
||||
for resource in shader_info.resources:
|
||||
if resource.set >= 0 and resource.binding >= 0:
|
||||
bindings.append(f"[[vk::binding({resource.binding}, {resource.set})]] {resource.name}")
|
||||
return "\n".join(bindings)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _generate_dx_bindings(shader_info: ShaderInfo) -> str:
|
||||
"""生成DirectX绑定属性"""
|
||||
bindings = []
|
||||
for resource in shader_info.resources:
|
||||
if resource.register and resource.space >= 0:
|
||||
bindings.append(f"register({resource.register}, space{resource.space}) {resource.name}")
|
||||
return "\n".join(bindings)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _generate_msl_bindings(shader_info: ShaderInfo) -> str:
|
||||
"""生成Metal绑定属性"""
|
||||
bindings = []
|
||||
for resource in shader_info.resources:
|
||||
if resource.metal_index >= 0:
|
||||
if resource.type in [ResourceType.SAMPLED_TEXTURE, ResourceType.STORAGE_TEXTURE]:
|
||||
bindings.append(f"[[texture({resource.metal_index})]] {resource.name}")
|
||||
elif resource.type == ResourceType.SAMPLER:
|
||||
bindings.append(f"[[sampler({resource.metal_index})]] {resource.name}")
|
||||
elif resource.type in [ResourceType.UNIFORM_BUFFER, ResourceType.STORAGE_BUFFER]:
|
||||
bindings.append(f"[[buffer({resource.metal_index})]] {resource.name}")
|
||||
return "\n".join(bindings)
|
||||
|
||||
|
||||
def inject_bindings(self, shader_info: ShaderInfo, target: TargetFormat) -> str:
|
||||
"""在着色器源码中注入绑定信息"""
|
||||
source_lines = shader_info.source_code.split('\n')
|
||||
modified_lines = []
|
||||
|
||||
# 创建资源名到绑定信息的映射
|
||||
resource_bindings = {}
|
||||
for resource in shader_info.resources:
|
||||
if target == TargetFormat.SPIRV:
|
||||
if resource.set >= 0 and resource.binding >= 0:
|
||||
resource_bindings[resource.name] = f"[[vk::binding({resource.binding}, {resource.set})]]"
|
||||
elif target in [TargetFormat.DXIL, TargetFormat.DXBC]:
|
||||
if resource.register and resource.space >= 0:
|
||||
resource_bindings[resource.name] = f"register({resource.register}, space{resource.space})"
|
||||
elif target == TargetFormat.MSL:
|
||||
if resource.metal_index >= 0:
|
||||
if resource.type in [ResourceType.SAMPLED_TEXTURE, ResourceType.STORAGE_TEXTURE]:
|
||||
resource_bindings[resource.name] = f"[[texture({resource.metal_index})]]"
|
||||
elif resource.type == ResourceType.SAMPLER:
|
||||
resource_bindings[resource.name] = f"[[sampler({resource.metal_index})]]"
|
||||
elif resource.type in [ResourceType.UNIFORM_BUFFER, ResourceType.STORAGE_BUFFER]:
|
||||
resource_bindings[resource.name] = f"[[buffer({resource.metal_index})]]"
|
||||
|
||||
# 简单的文本替换方式注入绑定
|
||||
# 实际应该使用AST解析,这里简化处理
|
||||
for line in source_lines:
|
||||
modified_line = line
|
||||
|
||||
# 查找资源声明
|
||||
for resource_name, binding in resource_bindings.items():
|
||||
# 匹配各种资源声明模式
|
||||
patterns = [
|
||||
rf'(\b\w+<.*?>\s+{resource_name}\b)', # Texture2D<float4> texName
|
||||
rf'(\bSamplerState\s+{resource_name}\b)', # SamplerState sampName
|
||||
rf'(\bConstantBuffer<.*?>\s+{resource_name}\b)', # ConstantBuffer<T> bufName
|
||||
rf'(\bStructuredBuffer<.*?>\s+{resource_name}\b)', # StructuredBuffer<T> bufName
|
||||
rf'(\bRWStructuredBuffer<.*?>\s+{resource_name}\b)', # RWStructuredBuffer<T> bufName
|
||||
rf'(\bParameterBlock<.*?>\s+{resource_name}\b)', # ParameterBlock<T> bufName
|
||||
]
|
||||
|
||||
for pattern in patterns:
|
||||
if re.search(pattern, line):
|
||||
# 如果是SPIRV和Metal,在声明前添加绑定属性
|
||||
if target == TargetFormat.SPIRV or target == TargetFormat.MSL:
|
||||
modified_line = re.sub(pattern, f'{binding} \\1', line)
|
||||
# 如果是DXIL/DXBC,在声明后添加绑定属性
|
||||
if target in [TargetFormat.DXIL, TargetFormat.DXBC]:
|
||||
modified_line = re.sub(pattern, f'\\1 : {binding}', line)
|
||||
break
|
||||
|
||||
modified_lines.append(modified_line)
|
||||
|
||||
return '\n'.join(modified_lines)
|
||||
|
||||
|
||||
def generate_binding_info(self, shader_info: ShaderInfo, target: TargetFormat, output_path: str) -> tuple[str, dict]:
|
||||
"""生成绑定信息字典,供运行时使用"""
|
||||
binding_info = {
|
||||
'stage': shader_info.stage.value,
|
||||
'entry_point': shader_info.entry_point,
|
||||
'resources': []
|
||||
}
|
||||
|
||||
# 读取output_path中的二进制数据
|
||||
if not os.path.exists(output_path):
|
||||
raise FileNotFoundError(f"Output file {output_path} does not exist after compilation.")
|
||||
with open(output_path, 'rb') as f:
|
||||
binding_info['blob'] = f.read()
|
||||
|
||||
for resource in shader_info.resources:
|
||||
res_info = {
|
||||
'name': resource.name,
|
||||
'type': resource.type.value
|
||||
}
|
||||
|
||||
if target == TargetFormat.SPIRV:
|
||||
res_info['set'] = resource.set
|
||||
res_info['binding'] = resource.binding
|
||||
elif target in [TargetFormat.DXIL, TargetFormat.DXBC]:
|
||||
res_info['register'] = resource.register
|
||||
res_info['space'] = resource.space
|
||||
elif target == TargetFormat.MSL:
|
||||
res_info['index'] = resource.metal_index
|
||||
|
||||
binding_info['resources'].append(res_info)
|
||||
|
||||
return binding_info
|
||||
266
code_generator.py
Normal file
266
code_generator.py
Normal file
@@ -0,0 +1,266 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SDL3_GPU Slang Compiler - Code Generator
|
||||
生成C/C++绑定代码
|
||||
"""
|
||||
import os.path
|
||||
from typing import List, Dict, TextIO, Optional
|
||||
from pathlib import Path
|
||||
from contextlib import contextmanager
|
||||
|
||||
class IndentManager:
|
||||
"""RAII风格的缩进管理器"""
|
||||
|
||||
def __init__(self, file: TextIO, indent_char: str = '\t'):
|
||||
self.file = file
|
||||
self.indent_char = indent_char
|
||||
self.indent_level = 0
|
||||
|
||||
def write(self, text: str = '') -> None:
|
||||
"""写入带缩进的文本"""
|
||||
if text:
|
||||
self.file.write(self.indent_char * self.indent_level + text + '\n')
|
||||
else:
|
||||
self.file.write('\n')
|
||||
self.file.flush()
|
||||
|
||||
def write_raw(self, text: str) -> None:
|
||||
"""写入原始文本(无缩进)"""
|
||||
self.file.write(text)
|
||||
|
||||
@contextmanager
|
||||
def indent(self, header: Optional[str] = None, footer: Optional[str] = None):
|
||||
"""缩进上下文管理器"""
|
||||
if header:
|
||||
self.write(header)
|
||||
|
||||
self.indent_level += 1
|
||||
try:
|
||||
yield self
|
||||
finally:
|
||||
self.indent_level -= 1
|
||||
if footer:
|
||||
self.write(footer)
|
||||
|
||||
@contextmanager
|
||||
def block(self, header: str, footer: str = '}'):
|
||||
"""代码块上下文管理器"""
|
||||
self.write(header + ' {')
|
||||
self.indent_level += 1
|
||||
try:
|
||||
yield self
|
||||
finally:
|
||||
self.indent_level -= 1
|
||||
self.write(footer)
|
||||
|
||||
class CodeGenerator:
|
||||
"""C++绑定代码生成器"""
|
||||
|
||||
# 资源类型映射表
|
||||
RESOURCE_TYPE_MAP = {
|
||||
'sampled_texture': 'Resource::SampledTexture',
|
||||
'storage_texture': 'Resource::StorageTexture',
|
||||
'storage_buffer': 'Resource::StorageBuffer',
|
||||
'uniform_buffer': 'Resource::UniformBuffer',
|
||||
'sampler': 'Resource::Sampler'
|
||||
}
|
||||
|
||||
def generate_binding_functions(self, source_file_pathname, binding_infos: List[Dict], output_path: str) -> None:
|
||||
"""生成C++绑定函数的入口方法"""
|
||||
self._generate_cpp_bindings(source_file_pathname, binding_infos, output_path)
|
||||
|
||||
def _generate_cpp_bindings(self, source_file_pathname, binding_infos: List[Dict], output_path: str) -> None:
|
||||
"""生成C++绑定函数"""
|
||||
output_file = Path(output_path)
|
||||
# 尝试创建输出目录
|
||||
output_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(output_file, 'w', encoding='utf-8') as file:
|
||||
writer = IndentManager(file)
|
||||
self._write_complete_file(writer, source_file_pathname, binding_infos)
|
||||
|
||||
def _write_complete_file(self, writer: IndentManager, source_file_pathname, binding_infos: List[Dict]) -> None:
|
||||
"""写入完整的文件内容"""
|
||||
self._write_header(writer)
|
||||
|
||||
with writer.block('namespace SDL3GPU', '} // namespace SDL3GPU'):
|
||||
writer.write()
|
||||
self._write_shader_bindings_class(writer, source_file_pathname, binding_infos)
|
||||
writer.write()
|
||||
|
||||
def _write_header(self, writer: IndentManager) -> None:
|
||||
"""写入文件头部"""
|
||||
headers = [
|
||||
'#pragma once',
|
||||
'#include <SDL3/SDL_gpu.h>',
|
||||
'#include <string>',
|
||||
'#include <vector>',
|
||||
'#include <unordered_map>',
|
||||
]
|
||||
|
||||
for header in headers:
|
||||
writer.write(header)
|
||||
writer.write()
|
||||
|
||||
def _write_shader_bindings_class(self, writer: IndentManager, source_file_pathname: str, binding_infos: List[Dict]) -> None:
|
||||
"""写入ShaderBindings类"""
|
||||
# 获取源文件名(不带路径和拓展名)
|
||||
source_file_name = Path(source_file_pathname).stem
|
||||
|
||||
with writer.block(f'class {source_file_name}ShaderBindings','};'):
|
||||
self._write_public_section(writer)
|
||||
self._write_private_section(writer)
|
||||
self._write_constructor(writer, binding_infos)
|
||||
self._write_public_methods(writer)
|
||||
|
||||
def _write_public_section(self, writer: IndentManager) -> None:
|
||||
"""写入public部分的结构体定义"""
|
||||
writer.write('public:')
|
||||
|
||||
# Resource结构体
|
||||
with writer.indent():
|
||||
with writer.block('struct Resource', '};'):
|
||||
with writer.block('enum Type'):
|
||||
enum_values = [
|
||||
'SampledTexture,',
|
||||
'StorageTexture,',
|
||||
'StorageBuffer,',
|
||||
'UniformBuffer,',
|
||||
'Sampler'
|
||||
]
|
||||
for value in enum_values:
|
||||
writer.write(value)
|
||||
writer.write()
|
||||
writer.write('std::string name;')
|
||||
writer.write('Type type;')
|
||||
writer.write('int binding = -1;')
|
||||
writer.write('int set = -1;')
|
||||
writer.write('int space = -1;')
|
||||
writer.write('int index = -1;')
|
||||
writer.write()
|
||||
|
||||
# ShaderInfo结构体
|
||||
with writer.block('struct ShaderInfo','};'):
|
||||
writer.write('std::string entryPoint;')
|
||||
writer.write('SDL_GPUShaderStage stage;')
|
||||
writer.write('std::vector<Resource> resources;')
|
||||
writer.write()
|
||||
|
||||
def _write_private_section(self, writer: IndentManager) -> None:
|
||||
"""写入private部分"""
|
||||
writer.write('private:')
|
||||
with writer.indent():
|
||||
writer.write('std::unordered_map<std::string, ShaderInfo> m_shaderInfos;')
|
||||
writer.write()
|
||||
|
||||
def _write_constructor(self, writer: IndentManager, binding_infos: List[Dict]) -> None:
|
||||
"""写入构造函数"""
|
||||
writer.write('public:')
|
||||
|
||||
with writer.indent():
|
||||
with writer.block('ShaderBindings()'):
|
||||
for info in binding_infos:
|
||||
self._write_shader_initialization(writer, info)
|
||||
|
||||
def _write_shader_initialization(self, writer: IndentManager, shader_info: Dict) -> None:
|
||||
"""写入单个着色器的初始化代码"""
|
||||
entry_point = shader_info['entry_point']
|
||||
stage = shader_info['stage'].upper()
|
||||
|
||||
writer.write(f'// {entry_point}')
|
||||
|
||||
with writer.indent('{', '}'):
|
||||
writer.write('ShaderInfo info;')
|
||||
writer.write(f'info.entryPoint = "{entry_point}";')
|
||||
writer.write(f'info.stage = SDL_GPU_SHADERSTAGE_{stage};')
|
||||
|
||||
# 写入资源信息
|
||||
for resource in shader_info['resources']:
|
||||
self._write_resource_initialization(writer, resource)
|
||||
|
||||
writer.write(f'm_shaderInfos["{entry_point}"] = std::move(info);')
|
||||
|
||||
writer.write()
|
||||
|
||||
def _write_resource_initialization(self, writer: IndentManager, resource: Dict) -> None:
|
||||
"""写入单个资源的初始化代码"""
|
||||
with writer.indent('{', '}'):
|
||||
writer.write('Resource r;')
|
||||
writer.write(f'r.name = "{resource["name"]}";')
|
||||
|
||||
# 设置资源类型
|
||||
resource_type = self.RESOURCE_TYPE_MAP.get(resource["type"])
|
||||
if resource_type:
|
||||
writer.write(f'r.type = {resource_type};')
|
||||
|
||||
# 设置资源绑定信息
|
||||
if 'binding' in resource:
|
||||
writer.write(f'r.binding = {resource["binding"]};')
|
||||
writer.write(f'r.set = {resource["set"]};')
|
||||
elif 'register' in resource:
|
||||
writer.write(f'r.space = {resource["space"]};')
|
||||
elif 'index' in resource:
|
||||
writer.write(f'r.index = {resource["index"]};')
|
||||
|
||||
writer.write('info.resources.push_back(r);')
|
||||
|
||||
def _write_public_methods(self, writer: IndentManager) -> None:
|
||||
"""写入公共方法"""
|
||||
writer.write()
|
||||
self._write_bind_resources_method(writer)
|
||||
writer.write()
|
||||
self._write_get_shader_info_method(writer)
|
||||
|
||||
def _write_bind_resources_method(self, writer: IndentManager) -> None:
|
||||
"""写入bindResources方法"""
|
||||
method_signature = (
|
||||
'void bindResources(\n'
|
||||
f'{writer.indent_char * (writer.indent_level + 2)}SDL_GPUCommandBuffer* cmd,\n'
|
||||
f'{writer.indent_char * (writer.indent_level + 2)}const std::string& shaderName,\n'
|
||||
f'{writer.indent_char * (writer.indent_level + 2)}const std::unordered_map<std::string, void*>& resources)'
|
||||
)
|
||||
|
||||
with writer.block(method_signature):
|
||||
writer.write('auto it = m_shaderInfos.find(shaderName);')
|
||||
|
||||
with writer.block('if (it == m_shaderInfos.end())'):
|
||||
writer.write('return;')
|
||||
|
||||
writer.write()
|
||||
writer.write('const auto& info = it->second;')
|
||||
writer.write()
|
||||
writer.write('// 根据着色器阶段和资源类型进行绑定')
|
||||
writer.write('// 这里需要根据SDL3 GPU的实际API来实现')
|
||||
|
||||
with writer.block('for (const auto& res : info.resources)'):
|
||||
writer.write('auto resIt = resources.find(res.name);')
|
||||
|
||||
with writer.block('if (resIt == resources.end())'):
|
||||
writer.write('continue;')
|
||||
|
||||
writer.write()
|
||||
writer.write('void* resource = resIt->second;')
|
||||
writer.write()
|
||||
writer.write('// 示例绑定逻辑')
|
||||
|
||||
with writer.block('switch (res.type)'):
|
||||
writer.write('case Resource::SampledTexture:')
|
||||
with writer.indent():
|
||||
writer.write('// SDL_BindGPUVertexSamplers(cmd, ...);')
|
||||
writer.write('break;')
|
||||
|
||||
writer.write('case Resource::StorageBuffer:')
|
||||
with writer.indent():
|
||||
writer.write('// SDL_BindGPUVertexStorageBuffers(cmd, ...);')
|
||||
writer.write('break;')
|
||||
|
||||
writer.write('// ... 其他资源类型')
|
||||
writer.write('default:')
|
||||
with writer.indent():
|
||||
writer.write('break;')
|
||||
|
||||
def _write_get_shader_info_method(self, writer: IndentManager) -> None:
|
||||
"""写入getShaderInfo方法"""
|
||||
with writer.block('const ShaderInfo* getShaderInfo(const std::string& shaderName) const'):
|
||||
writer.write('auto it = m_shaderInfos.find(shaderName);')
|
||||
writer.write('return it != m_shaderInfos.end() ? &it->second : nullptr;')
|
||||
93
compiler.py
Normal file
93
compiler.py
Normal file
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SDL3_GPU Slang Compiler - Main Compiler
|
||||
主编译器类,整合所有功能模块
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
from typing import List, Dict, Tuple
|
||||
from shader_types import ShaderInfo, TargetFormat, ShaderStage
|
||||
from slangc_finder import SlangcFinder
|
||||
from shader_parser import ShaderParser
|
||||
from binding_manager import BindingManager
|
||||
from code_generator import CodeGenerator
|
||||
|
||||
class SDL3GPUSlangCompiler:
|
||||
def __init__(self, include_paths: List[str] = None):
|
||||
self.slangc_path = SlangcFinder.find_slangc()
|
||||
self.include_paths = include_paths or []
|
||||
self.parser = ShaderParser(self.slangc_path, self.include_paths)
|
||||
self.binding_manager = BindingManager()
|
||||
self.code_generator = CodeGenerator()
|
||||
|
||||
def parse_slang_shader(self, source_path: str, include_paths: List[str] = None) -> Dict[str, ShaderInfo]:
|
||||
"""解析Slang着色器源码,提取资源信息"""
|
||||
return self.parser.parse_slang_shader(source_path, include_paths)
|
||||
|
||||
|
||||
def compile_shader(self, shader_info: ShaderInfo, target: TargetFormat) -> tuple[str, dict]:
|
||||
"""编译着色器并返回二进制路径和绑定信息"""
|
||||
output_path = tempfile.mktemp()
|
||||
# 根据目标格式分配绑定点
|
||||
if target == TargetFormat.SPIRV:
|
||||
self.binding_manager.assign_bindings_spirv(shader_info)
|
||||
elif target in [TargetFormat.DXIL, TargetFormat.DXBC]:
|
||||
self.binding_manager.assign_bindings_dxil(shader_info)
|
||||
elif target == TargetFormat.MSL:
|
||||
self.binding_manager.assign_bindings_msl(shader_info)
|
||||
|
||||
# 生成带绑定信息的着色器代码
|
||||
modified_source = self.binding_manager.inject_bindings(shader_info, target)
|
||||
|
||||
# 写入临时文件
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.slang', delete=False, encoding='utf8') as tmp:
|
||||
tmp.write(modified_source)
|
||||
tmp_path = tmp.name
|
||||
|
||||
try:
|
||||
# 编译着色器
|
||||
target_flag = {
|
||||
TargetFormat.SPIRV: 'spirv',
|
||||
TargetFormat.DXIL: 'dxil',
|
||||
TargetFormat.DXBC: 'dxbc',
|
||||
TargetFormat.MSL: 'metal'
|
||||
}[target]
|
||||
|
||||
stage_flag = {
|
||||
ShaderStage.VERTEX: 'vertex',
|
||||
ShaderStage.FRAGMENT: 'fragment',
|
||||
ShaderStage.COMPUTE: 'compute'
|
||||
}[shader_info.stage]
|
||||
|
||||
cmd = [
|
||||
self.slangc_path,
|
||||
tmp_path,
|
||||
'-entry', shader_info.entry_point,
|
||||
'-o', output_path,
|
||||
'-target', target_flag,
|
||||
'-stage', stage_flag,
|
||||
'-profile', 'sm_6_6',
|
||||
'-I', *self.include_paths, # 添加包含路径
|
||||
]
|
||||
|
||||
print(f"Compiling shader with command: {' '.join(cmd)}")
|
||||
|
||||
subprocess.run(cmd, check=True)
|
||||
print(f"Shader compiled successfully")
|
||||
|
||||
# 生成绑定信息
|
||||
binding_info = self.binding_manager.generate_binding_info(shader_info, target, output_path)
|
||||
|
||||
return binding_info
|
||||
|
||||
finally:
|
||||
# 清理临时文件
|
||||
os.unlink(tmp_path)
|
||||
os.unlink(output_path)
|
||||
|
||||
|
||||
def generate_binding_functions(self, source_file_pathname, binding_infos: List[Dict], output_path: str):
|
||||
"""生成C/C++绑定函数"""
|
||||
self.code_generator.generate_binding_functions(source_file_pathname, binding_infos, output_path)
|
||||
58
main.py
Normal file
58
main.py
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SDL3_GPU Slang Compiler - Command Line Interface
|
||||
命令行接口
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import json
|
||||
import argparse
|
||||
from compiler import SDL3GPUSlangCompiler
|
||||
from shader_types import TargetFormat
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='SDL3 GPU Slang Compiler')
|
||||
|
||||
parser.add_argument('input', help='Input Slang shader file')
|
||||
parser.add_argument('-t', '--target', choices=['spirv', 'dxil', 'dxbc', 'msl'],
|
||||
required=True, help='Target shader format')
|
||||
parser.add_argument('--binding-output-dir', required=True, help='Output path for binding code')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# 获取编译文件的绝对路径
|
||||
input_path = os.path.abspath(args.input)
|
||||
# 获取不包含拓展名的源文件名
|
||||
source_file_name = os.path.splitext(os.path.basename(input_path))[0]
|
||||
# 仅保留路径部分
|
||||
input_path = os.path.dirname(input_path)
|
||||
|
||||
# 创建编译器实例
|
||||
compiler = SDL3GPUSlangCompiler([input_path])
|
||||
|
||||
# 解析着色器
|
||||
print(f"**Parsing** {args.input}...")
|
||||
shaders = compiler.parse_slang_shader(args.input)
|
||||
|
||||
# 编译每个入口点
|
||||
target = TargetFormat(args.target)
|
||||
binding_infos = []
|
||||
|
||||
for name, shader_info in shaders.items():
|
||||
print(f"**Compiling** {name}...")
|
||||
|
||||
binding_info = compiler.compile_shader(shader_info, target)
|
||||
binding_infos.append(binding_info)
|
||||
|
||||
binding_output_file_pathname = os.path.abspath(args.binding_output_dir)
|
||||
binding_output_file_pathname = os.path.join(binding_output_file_pathname, f"{source_file_name}.shader.h")
|
||||
|
||||
# 生成绑定代码
|
||||
print(f"\n**Generating** binding code to {binding_output_file_pathname}...")
|
||||
compiler.generate_binding_functions(os.path.abspath(args.input), binding_infos, binding_output_file_pathname)
|
||||
print("**Done!**")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
383
shader_parser.py
Normal file
383
shader_parser.py
Normal file
@@ -0,0 +1,383 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SDL3_GPU Slang Compiler - Shader Parser
|
||||
着色器源码解析功能
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import subprocess
|
||||
import tempfile
|
||||
import shutil
|
||||
import re
|
||||
from typing import List, Dict, Optional
|
||||
from shader_types import ShaderStage, ResourceType, Resource, ShaderInfo
|
||||
|
||||
|
||||
class ShaderParser:
|
||||
def __init__(self, slangc_path: str, include_paths: List[str] = None):
|
||||
self.slangc_path = slangc_path
|
||||
self.include_paths = include_paths or []
|
||||
|
||||
def parse_slang_shader(self, source_path: str, include_paths: List[str] = None) -> Dict[str, ShaderInfo]:
|
||||
"""解析Slang着色器源码,提取资源信息"""
|
||||
|
||||
# 合并包含路径
|
||||
all_include_paths = self.include_paths.copy()
|
||||
if include_paths:
|
||||
all_include_paths.extend(include_paths)
|
||||
|
||||
# 添加源文件所在目录作为包含路径
|
||||
source_dir = os.path.dirname(os.path.abspath(source_path))
|
||||
if source_dir not in all_include_paths:
|
||||
all_include_paths.insert(0, source_dir)
|
||||
|
||||
print(f"Include paths: {all_include_paths}")
|
||||
|
||||
with open(source_path, 'r', encoding='utf-8') as f:
|
||||
source = f.read()
|
||||
|
||||
# 首先分析源码找到所有入口点
|
||||
entry_points = self._find_entry_points(source)
|
||||
print(f"Found potential entry points: {entry_points}")
|
||||
|
||||
shaders = {}
|
||||
|
||||
# 为每个入口点单独进行完整编译和反射
|
||||
for entry_name, stage in entry_points.items():
|
||||
print(f"\nProcessing entry point: {entry_name} (stage: {stage.value})")
|
||||
|
||||
shader_info = self._compile_and_reflect_entry_point(
|
||||
source_path, entry_name, stage, all_include_paths, source
|
||||
)
|
||||
|
||||
if shader_info:
|
||||
shaders[entry_name] = shader_info
|
||||
else:
|
||||
print(f"Failed to process entry point: {entry_name}")
|
||||
|
||||
return shaders
|
||||
|
||||
def _find_entry_points(self, source: str) -> Dict[str, ShaderStage]:
|
||||
"""在源码中查找入口点函数"""
|
||||
entry_points = {}
|
||||
|
||||
# 1. 查找带有Slang属性的函数
|
||||
attribute_patterns = [
|
||||
(r'$$shader\s*$\s*["\']vertex["\']\s*$\s*$$.*?(\w+)\s*\(', ShaderStage.VERTEX),
|
||||
(r'$$shader\s*$\s*["\']fragment["\']\s*$\s*$$.*?(\w+)\s*\(', ShaderStage.FRAGMENT),
|
||||
(r'$$shader\s*$\s*["\']pixel["\']\s*$\s*$$.*?(\w+)\s*\(', ShaderStage.FRAGMENT),
|
||||
(r'$$shader\s*$\s*["\']compute["\']\s*$\s*$$.*?(\w+)\s*\(', ShaderStage.COMPUTE),
|
||||
]
|
||||
|
||||
for pattern, stage in attribute_patterns:
|
||||
matches = re.finditer(pattern, source, re.DOTALL | re.IGNORECASE)
|
||||
for match in matches:
|
||||
entry_points[match.group(1)] = stage
|
||||
print(f"Found attributed entry point: {match.group(1)} -> {stage.value}")
|
||||
|
||||
# 2. 查找常见的命名约定
|
||||
common_entry_points = {
|
||||
# Vertex shaders
|
||||
'vertex_main': ShaderStage.VERTEX,
|
||||
'vertexMain': ShaderStage.VERTEX,
|
||||
'vert_main': ShaderStage.VERTEX,
|
||||
'vs_main': ShaderStage.VERTEX,
|
||||
'vertex_shader': ShaderStage.VERTEX,
|
||||
'VS': ShaderStage.VERTEX,
|
||||
|
||||
# Fragment shaders
|
||||
'fragment_main': ShaderStage.FRAGMENT,
|
||||
'fragmentMain': ShaderStage.FRAGMENT,
|
||||
'frag_main': ShaderStage.FRAGMENT,
|
||||
'pixel_main': ShaderStage.FRAGMENT,
|
||||
'ps_main': ShaderStage.FRAGMENT,
|
||||
'fragment_shader': ShaderStage.FRAGMENT,
|
||||
'PS': ShaderStage.FRAGMENT,
|
||||
|
||||
# Compute shaders
|
||||
'compute_main': ShaderStage.COMPUTE,
|
||||
'computeMain': ShaderStage.COMPUTE,
|
||||
'comp_main': ShaderStage.COMPUTE,
|
||||
'cs_main': ShaderStage.COMPUTE,
|
||||
'compute_shader': ShaderStage.COMPUTE,
|
||||
'CS': ShaderStage.COMPUTE,
|
||||
}
|
||||
|
||||
# 查找这些函数名是否在源码中存在
|
||||
for func_name, stage in common_entry_points.items():
|
||||
if func_name not in entry_points:
|
||||
# 使用词边界确保完整匹配函数名
|
||||
pattern = r'\b' + re.escape(func_name) + r'\s*\('
|
||||
if re.search(pattern, source, re.IGNORECASE):
|
||||
entry_points[func_name] = stage
|
||||
print(f"Found common entry point: {func_name} -> {stage.value}")
|
||||
|
||||
# 3. 查找所有函数声明,根据返回类型和参数推断
|
||||
function_pattern = r'(?:float4|void|float3|float2|float)\s+(\w+)\s*$[^)]*$(?:\s*:\s*\w+)?'
|
||||
matches = re.finditer(function_pattern, source)
|
||||
|
||||
for match in matches:
|
||||
func_name = match.group(1)
|
||||
if func_name not in entry_points:
|
||||
# 根据函数名模式推断
|
||||
func_lower = func_name.lower()
|
||||
|
||||
# 顶点着色器关键词
|
||||
if any(keyword in func_lower for keyword in ['vert', 'vs', 'vertex']):
|
||||
entry_points[func_name] = ShaderStage.VERTEX
|
||||
print(f"Inferred vertex shader: {func_name}")
|
||||
# 片元着色器关键词
|
||||
elif any(keyword in func_lower for keyword in ['frag', 'pixel', 'ps', 'fs', 'fragment']):
|
||||
entry_points[func_name] = ShaderStage.FRAGMENT
|
||||
print(f"Inferred fragment shader: {func_name}")
|
||||
# 计算着色器关键词
|
||||
elif any(keyword in func_lower for keyword in ['comp', 'cs', 'compute']):
|
||||
entry_points[func_name] = ShaderStage.COMPUTE
|
||||
print(f"Inferred compute shader: {func_name}")
|
||||
|
||||
# 4. 查找带有HLSL语义的函数
|
||||
semantic_patterns = [
|
||||
(r'(\w+)\s*$[^)]*$\s*:\s*SV_Position', ShaderStage.VERTEX),
|
||||
(r'(\w+)\s*$[^)]*$\s*:\s*SV_Target', ShaderStage.FRAGMENT),
|
||||
(r'(\w+)\s*$[^)]*$\s*:\s*POSITION', ShaderStage.VERTEX),
|
||||
]
|
||||
|
||||
for pattern, stage in semantic_patterns:
|
||||
matches = re.finditer(pattern, source, re.IGNORECASE)
|
||||
for match in matches:
|
||||
func_name = match.group(1)
|
||||
if func_name not in entry_points:
|
||||
entry_points[func_name] = stage
|
||||
print(f"Found semantic-based entry point: {func_name} -> {stage.value}")
|
||||
|
||||
# 5. 如果没找到任何入口点,查找main函数
|
||||
if not entry_points:
|
||||
if re.search(r'\bmain\s*\(', source, re.IGNORECASE):
|
||||
entry_points['main'] = ShaderStage.VERTEX
|
||||
print("Found main function, assuming vertex shader")
|
||||
|
||||
print(f"Total entry points found: {len(entry_points)}")
|
||||
return entry_points
|
||||
|
||||
def _compile_and_reflect_entry_point(self, source_path: str, entry_name: str,
|
||||
stage: ShaderStage, include_paths: List[str],
|
||||
source: str) -> Optional[ShaderInfo]:
|
||||
"""为单个入口点进行完整编译和反射"""
|
||||
|
||||
# 创建临时输出文件
|
||||
with tempfile.NamedTemporaryFile(suffix='.hlsl', delete=False) as tmp_output:
|
||||
output_path = tmp_output.name
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix='.json', delete=False) as tmp_reflection:
|
||||
reflection_path = tmp_reflection.name
|
||||
|
||||
# 构建完整的slangc编译命令(类似你成功的命令)
|
||||
cmd = [self.slangc_path]
|
||||
|
||||
# 添加包含路径
|
||||
for include_path in include_paths:
|
||||
cmd.extend(['-I', include_path])
|
||||
|
||||
# 添加编译参数
|
||||
stage_name = {
|
||||
ShaderStage.VERTEX: 'vertex',
|
||||
ShaderStage.FRAGMENT: 'fragment',
|
||||
ShaderStage.COMPUTE: 'compute'
|
||||
}[stage]
|
||||
|
||||
cmd.extend([
|
||||
source_path, # 输入文件
|
||||
'-reflection-json', reflection_path, # 反射输出
|
||||
'-o', output_path, # 编译输出
|
||||
'-target', 'hlsl', # 目标格式(用于测试)
|
||||
'-entry', entry_name, # 入口点
|
||||
'-stage', stage_name # 着色器阶段
|
||||
])
|
||||
|
||||
print(f"Command: {' '.join(cmd)}")
|
||||
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60
|
||||
)
|
||||
|
||||
print(f"Return code: {result.returncode}")
|
||||
if result.stdout:
|
||||
print(f"Stdout: {result.stdout}")
|
||||
if result.stderr:
|
||||
print(f"Stderr: {result.stderr}")
|
||||
|
||||
if result.returncode == 0:
|
||||
# 读取反射数据
|
||||
if os.path.exists(reflection_path):
|
||||
with open(reflection_path, 'r', encoding='utf-8') as f:
|
||||
reflection_json = f.read()
|
||||
print(f"Reflection JSON length: {len(reflection_json)}")
|
||||
|
||||
if reflection_json.strip():
|
||||
try:
|
||||
reflection = json.loads(reflection_json)
|
||||
return self._create_shader_info_from_reflection(
|
||||
reflection, entry_name, stage, source
|
||||
)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"JSON parsing error: {e}")
|
||||
print(f"Raw JSON: {reflection_json[:500]}...")
|
||||
else:
|
||||
print(f"Reflection file not created: {reflection_path}")
|
||||
else:
|
||||
print(f"Compilation failed for entry point {entry_name}")
|
||||
|
||||
# 如果反射失败,尝试手动解析
|
||||
print(f"Falling back to manual parsing for {entry_name}")
|
||||
# 清理临时文件
|
||||
for temp_file in [output_path, reflection_path]:
|
||||
if os.path.exists(temp_file):
|
||||
os.unlink(temp_file)
|
||||
|
||||
return self._create_shader_info_manual(entry_name, stage, source)
|
||||
|
||||
def _create_shader_info_from_reflection(self, reflection: dict, entry_name: str,
|
||||
stage: ShaderStage, source: str) -> ShaderInfo:
|
||||
"""从反射数据创建ShaderInfo"""
|
||||
print(f"Processing reflection data for {entry_name}")
|
||||
print(f"Reflection keys: {list(reflection.keys())}")
|
||||
|
||||
shader_info = ShaderInfo(
|
||||
stage=stage,
|
||||
entry_point=entry_name,
|
||||
resources=[],
|
||||
source_code=source
|
||||
)
|
||||
|
||||
# Slang反射数据的可能结构
|
||||
# 尝试不同的数据结构
|
||||
entry_point_data = None
|
||||
|
||||
# 方法1: 直接在根级别查找
|
||||
if 'parameters' in reflection:
|
||||
entry_point_data = reflection
|
||||
|
||||
# 方法2: 在entryPoints数组中查找
|
||||
elif 'entryPoints' in reflection:
|
||||
for ep in reflection['entryPoints']:
|
||||
if ep.get('name') == entry_name:
|
||||
entry_point_data = ep
|
||||
break
|
||||
|
||||
# 方法3: 在modules中查找
|
||||
elif 'modules' in reflection:
|
||||
for module in reflection['modules']:
|
||||
if 'entryPoints' in module:
|
||||
for ep in module['entryPoints']:
|
||||
if ep.get('name') == entry_name:
|
||||
entry_point_data = ep
|
||||
break
|
||||
|
||||
if entry_point_data:
|
||||
print(f"Found entry point data: {list(entry_point_data.keys())}")
|
||||
|
||||
# 解析资源参数
|
||||
parameters = entry_point_data.get('parameters', [])
|
||||
print(f"Found {len(parameters)} parameters")
|
||||
|
||||
for param in parameters:
|
||||
print(f"Processing parameter: {param}")
|
||||
resource = self._parse_resource(param)
|
||||
if resource:
|
||||
shader_info.resources.append(resource)
|
||||
print(f"Added resource: {resource.name} ({resource.type.value})")
|
||||
else:
|
||||
print("No entry point data found in reflection, using manual parsing")
|
||||
# 使用手动解析作为fallback
|
||||
manual_resources = self._extract_resources_from_source(source)
|
||||
shader_info.resources.extend(manual_resources)
|
||||
|
||||
print(f"Shader {entry_name} has {len(shader_info.resources)} resources")
|
||||
return shader_info
|
||||
|
||||
def _create_shader_info_manual(self, entry_name: str, stage: ShaderStage, source: str) -> ShaderInfo:
|
||||
"""手动创建ShaderInfo(fallback方法)"""
|
||||
print(f"Creating shader info manually for {entry_name}")
|
||||
shader_info = ShaderInfo(
|
||||
stage=stage,
|
||||
entry_point=entry_name,
|
||||
resources=[],
|
||||
source_code=source
|
||||
)
|
||||
|
||||
# 手动解析资源
|
||||
resources = self._extract_resources_from_source(source)
|
||||
shader_info.resources.extend(resources)
|
||||
|
||||
print(f"Manual parsing found {len(resources)} resources")
|
||||
return shader_info
|
||||
|
||||
def _extract_resources_from_source(self, source: str) -> List[Resource]:
|
||||
"""从源码中提取资源声明"""
|
||||
resources = []
|
||||
|
||||
# 资源声明的正则表达式模式
|
||||
patterns = {
|
||||
# Texture resources
|
||||
ResourceType.SAMPLED_TEXTURE: [
|
||||
r'Texture2D\s*(?:<[^>]*>)?\s+(\w+)',
|
||||
r'Texture3D\s*(?:<[^>]*>)?\s+(\w+)',
|
||||
r'TextureCube\s*(?:<[^>]*>)?\s+(\w+)',
|
||||
],
|
||||
ResourceType.STORAGE_TEXTURE: [
|
||||
r'RWTexture2D\s*(?:<[^>]*>)?\s+(\w+)',
|
||||
r'RWTexture3D\s*(?:<[^>]*>)?\s+(\w+)',
|
||||
],
|
||||
# Buffer resources
|
||||
ResourceType.STORAGE_BUFFER: [
|
||||
r'RWStructuredBuffer\s*<[^>]*>\s+(\w+)',
|
||||
r'RWByteAddressBuffer\s+(\w+)',
|
||||
r'StructuredBuffer\s*<[^>]*>\s+(\w+)',
|
||||
r'ByteAddressBuffer\s+(\w+)',
|
||||
],
|
||||
ResourceType.UNIFORM_BUFFER: [
|
||||
r'ConstantBuffer\s*<[^>]*>\s+(\w+)',
|
||||
r'cbuffer\s+(\w+)',
|
||||
],
|
||||
ResourceType.SAMPLER: [
|
||||
r'SamplerState\s+(\w+)',
|
||||
r'SamplerComparisonState\s+(\w+)',
|
||||
]
|
||||
}
|
||||
|
||||
for resource_type, type_patterns in patterns.items():
|
||||
for pattern in type_patterns:
|
||||
matches = re.findall(pattern, source, re.IGNORECASE)
|
||||
for match in matches:
|
||||
resources.append(Resource(match, resource_type))
|
||||
print(f"Found resource: {match} ({resource_type.value})")
|
||||
|
||||
return resources
|
||||
|
||||
def _parse_resource(self, param: dict) -> Optional[Resource]:
|
||||
"""解析资源参数"""
|
||||
type_info = param.get('type', {})
|
||||
type_name = type_info.get('name', '')
|
||||
|
||||
# 判断资源类型
|
||||
if 'Texture2D' in type_name or 'Texture3D' in type_name or 'TextureCube' in type_name:
|
||||
if 'RW' in type_name:
|
||||
return Resource(param['name'], ResourceType.STORAGE_TEXTURE)
|
||||
else:
|
||||
return Resource(param['name'], ResourceType.SAMPLED_TEXTURE)
|
||||
elif 'StructuredBuffer' in type_name or 'ByteAddressBuffer' in type_name:
|
||||
if 'RW' in type_name:
|
||||
return Resource(param['name'], ResourceType.STORAGE_BUFFER)
|
||||
else:
|
||||
# SDL3 GPU似乎不支持只读storage buffer,这里可能需要特殊处理
|
||||
return Resource(param['name'], ResourceType.STORAGE_BUFFER)
|
||||
elif 'ConstantBuffer' in type_name or type_info.get('kind') == 'ConstantBuffer' or type_info.get('kind') == 'parameterBlock':
|
||||
return Resource(param['name'], ResourceType.UNIFORM_BUFFER)
|
||||
elif 'SamplerState' in type_name or 'Sampler' in type_name:
|
||||
return Resource(param['name'], ResourceType.SAMPLER)
|
||||
|
||||
return None
|
||||
44
shader_types.py
Normal file
44
shader_types.py
Normal file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
SDL3_GPU Slang Compiler - Type Definitions
|
||||
数据类型和枚举定义
|
||||
"""
|
||||
|
||||
from enum import Enum
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
class ShaderStage(Enum):
|
||||
VERTEX = "vertex"
|
||||
FRAGMENT = "fragment"
|
||||
COMPUTE = "compute"
|
||||
|
||||
class ResourceType(Enum):
|
||||
SAMPLED_TEXTURE = "sampled_texture"
|
||||
STORAGE_TEXTURE = "storage_texture"
|
||||
STORAGE_BUFFER = "storage_buffer"
|
||||
UNIFORM_BUFFER = "uniform_buffer"
|
||||
SAMPLER = "sampler"
|
||||
|
||||
class TargetFormat(Enum):
|
||||
SPIRV = "spirv"
|
||||
DXIL = "dxil"
|
||||
DXBC = "dxbc"
|
||||
MSL = "msl"
|
||||
|
||||
@dataclass
|
||||
class Resource:
|
||||
name: str
|
||||
type: ResourceType
|
||||
binding: int = -1
|
||||
set: int = -1
|
||||
space: int = -1
|
||||
register: str = ""
|
||||
metal_index: int = -1
|
||||
|
||||
@dataclass
|
||||
class ShaderInfo:
|
||||
stage: ShaderStage
|
||||
entry_point: str
|
||||
resources: List[Resource]
|
||||
source_code: str
|
||||
90
slangc_finder.py
Normal file
90
slangc_finder.py
Normal file
@@ -0,0 +1,90 @@
|
||||
# !/usr/bin/env python3
|
||||
"""
|
||||
SDL3_GPU Slang Compiler - Slangc Finder
|
||||
查找和验证Slangc编译器路径
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
class SlangcFinder:
|
||||
@staticmethod
|
||||
def find_slangc() -> str:
|
||||
"""查找slangc编译器路径"""
|
||||
|
||||
# 方法1: 使用shutil.which (推荐)
|
||||
slangc_path = shutil.which('slangc')
|
||||
if slangc_path:
|
||||
try:
|
||||
subprocess.run([slangc_path, '-v'],
|
||||
capture_output=True, check=True, timeout=10)
|
||||
print(f"Found slangc at: {slangc_path}")
|
||||
return slangc_path
|
||||
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
|
||||
pass
|
||||
|
||||
# 方法2: 从环境变量获取
|
||||
slangc_from_env = os.environ.get('SLANGC_PATH')
|
||||
if slangc_from_env and os.path.isfile(slangc_from_env):
|
||||
try:
|
||||
subprocess.run([slangc_from_env, '-v'],
|
||||
capture_output=True, check=True, timeout=10)
|
||||
print(f"Found slangc from SLANGC_PATH: {slangc_from_env}")
|
||||
return slangc_from_env
|
||||
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
|
||||
pass
|
||||
|
||||
# 方法3: 在常见位置搜索
|
||||
common_paths = [
|
||||
r'C:\Program Files\Slang\bin\slangc.exe',
|
||||
r'C:\Program Files (x86)\Slang\bin\slangc.exe',
|
||||
r'C:\tools\slang\bin\slangc.exe',
|
||||
r'C:\slang\bin\slangc.exe'
|
||||
]
|
||||
|
||||
# 也尝试从PATH中的每个目录查找
|
||||
path_dirs = os.environ.get('PATH', '').split(os.pathsep)
|
||||
for path_dir in path_dirs:
|
||||
if path_dir: # 跳过空路径
|
||||
potential_path = os.path.join(path_dir, 'slangc.exe')
|
||||
common_paths.append(potential_path)
|
||||
|
||||
for path in common_paths:
|
||||
if os.path.isfile(path):
|
||||
try:
|
||||
subprocess.run([path, '-v'],
|
||||
capture_output=True, check=True, timeout=10)
|
||||
print(f"Found slangc at: {path}")
|
||||
return path
|
||||
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
|
||||
continue
|
||||
|
||||
# 方法4: 尝试直接调用 (有时候PATH在Python中不完整)
|
||||
try:
|
||||
result = subprocess.run(['slangc', '-v'],
|
||||
capture_output=True, check=True, timeout=10)
|
||||
print("Found slangc in PATH")
|
||||
return 'slangc'
|
||||
except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError):
|
||||
pass
|
||||
|
||||
# 如果都失败了,显示调试信息
|
||||
print("Debug information:")
|
||||
print(f"Current working directory: {os.getcwd()}")
|
||||
print(f"Python executable: {sys.executable}")
|
||||
print(f"PATH environment variable:")
|
||||
for i, path in enumerate(os.environ.get('PATH', '').split(os.pathsep)):
|
||||
print(f" {i}: {path}")
|
||||
|
||||
raise RuntimeError("""
|
||||
Cannot find slangc compiler. Please try one of the following:
|
||||
Set SLANGC_PATH environment variable to the full path of slangc.exe
|
||||
Ensure slangc.exe is in your PATH and restart your terminal
|
||||
Install Slang to a standard location like C:\\Program Files\\Slang\\bin\\
|
||||
Run the script from the same terminal where 'slangc --version' works
|
||||
You can also specify the path directly when creating the compiler:
|
||||
compiler = SDL3GPUSlangCompiler()
|
||||
compiler.slangc_path = r'C:\\path\\to\\slangc.exe'
|
||||
""")
|
||||
Reference in New Issue
Block a user