d3d12: use hlsl as a fallback pipeline

This commit is contained in:
chyyran 2023-02-05 01:54:56 -05:00
parent d6ac2c665d
commit 72c9548c7a
4 changed files with 167 additions and 53 deletions

View file

@ -1,21 +1,22 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use crate::{error, util}; use crate::{error, graphics_pipeline, util};
use crate::descriptor_heap::{D3D12DescriptorHeap, CpuStagingHeap, ResourceWorkHeap, SamplerWorkHeap, RenderTargetHeap}; use crate::descriptor_heap::{D3D12DescriptorHeap, CpuStagingHeap, ResourceWorkHeap, SamplerWorkHeap, RenderTargetHeap};
use crate::samplers::SamplerSet; use crate::samplers::SamplerSet;
use crate::luts::LutTexture; use crate::luts::LutTexture;
use librashader_presets::{ShaderPreset, TextureConfig}; use librashader_presets::{ShaderPreset, TextureConfig};
use librashader_reflect::back::targets::DXIL; use librashader_reflect::back::targets::{DXIL, HLSL};
use librashader_reflect::front::GlslangCompilation; use librashader_reflect::front::GlslangCompilation;
use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact}; use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact};
use librashader_runtime::image::{Image, UVDirection}; use librashader_runtime::image::{Image, UVDirection};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::error::Error; use std::error::Error;
use std::path::Path; use std::path::Path;
use spirv_cross::hlsl::ShaderModel;
use windows::core::Interface; use windows::core::Interface;
use windows::w; use windows::w;
use windows::Win32::Foundation::CloseHandle; use windows::Win32::Foundation::CloseHandle;
use windows::Win32::Graphics::Direct3D12::{ID3D12CommandAllocator, ID3D12CommandQueue, ID3D12Device, ID3D12Fence, ID3D12GraphicsCommandList, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_DESC, D3D12_COMMAND_QUEUE_FLAG_NONE, D3D12_FENCE_FLAG_NONE, ID3D12DescriptorHeap, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET}; use windows::Win32::Graphics::Direct3D12::{ID3D12CommandAllocator, ID3D12CommandQueue, ID3D12Device, ID3D12Fence, ID3D12GraphicsCommandList, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_DESC, D3D12_COMMAND_QUEUE_FLAG_NONE, D3D12_FENCE_FLAG_NONE, ID3D12DescriptorHeap, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET};
use windows::Win32::Graphics::Direct3D::Dxc::{CLSID_DxcLibrary, CLSID_DxcValidator, DxcCreateInstance, IDxcLibrary, IDxcValidator}; use windows::Win32::Graphics::Direct3D::Dxc::{CLSID_DxcCompiler, CLSID_DxcLibrary, CLSID_DxcValidator, DxcCreateInstance, IDxcCompiler, IDxcLibrary, IDxcUtils, IDxcValidator};
use windows::Win32::System::Threading::{CreateEventA, ResetEvent, WaitForSingleObject}; use windows::Win32::System::Threading::{CreateEventA, ResetEvent, WaitForSingleObject};
use windows::Win32::System::WindowsProgramming::INFINITE; use windows::Win32::System::WindowsProgramming::INFINITE;
use librashader_common::{ImageFormat, Size, Viewport}; use librashader_common::{ImageFormat, Size, Viewport};
@ -34,7 +35,8 @@ use crate::quad_render::DrawQuad;
use crate::render_target::RenderTarget; use crate::render_target::RenderTarget;
use crate::texture::{InputTexture, OutputDescriptor, OutputTexture}; use crate::texture::{InputTexture, OutputDescriptor, OutputTexture};
type ShaderPassMeta = ShaderPassArtifact<impl CompileReflectShader<DXIL, GlslangCompilation>>; type DxilShaderPassMeta = ShaderPassArtifact<impl CompileReflectShader<DXIL, GlslangCompilation>>;
type HlslShaderPassMeta = ShaderPassArtifact<impl CompileReflectShader<HLSL, GlslangCompilation>>;
pub struct FilterMutable { pub struct FilterMutable {
pub(crate) passes_enabled: usize, pub(crate) passes_enabled: usize,
@ -90,11 +92,19 @@ impl FilterChainD3D12 {
) -> error::Result<FilterChainD3D12> { ) -> error::Result<FilterChainD3D12> {
let shader_count = preset.shaders.len(); let shader_count = preset.shaders.len();
let lut_count = preset.textures.len(); let lut_count = preset.textures.len();
let shader_copy = preset.shaders.clone();
let (passes, semantics) = DXIL::compile_preset_passes::<GlslangCompilation, Box<dyn Error>>( let (passes, semantics) = DXIL::compile_preset_passes::<GlslangCompilation, Box<dyn Error>>(
preset.shaders, preset.shaders,
&preset.textures, &preset.textures,
).unwrap(); ).unwrap();
let (hlsl_passes, _) = HLSL::compile_preset_passes::<GlslangCompilation, Box<dyn Error>>(
shader_copy,
&preset.textures,
).unwrap();
let samplers = SamplerSet::new(device)?; let samplers = SamplerSet::new(device)?;
let mipmap_gen = D3D12MipmapGen::new(device).unwrap(); let mipmap_gen = D3D12MipmapGen::new(device).unwrap();
@ -115,7 +125,7 @@ impl FilterChainD3D12 {
let root_signature = D3D12RootSignature::new(device)?; let root_signature = D3D12RootSignature::new(device)?;
let (texture_heap, sampler_heap, filters) let (texture_heap, sampler_heap, filters)
= FilterChainD3D12::init_passes(device, &root_signature, passes, &semantics).unwrap(); = FilterChainD3D12::init_passes(device, &root_signature, passes, hlsl_passes, &semantics).unwrap();
@ -342,17 +352,22 @@ impl FilterChainD3D12 {
fn init_passes(device: &ID3D12Device, fn init_passes(device: &ID3D12Device,
root_signature: &D3D12RootSignature, root_signature: &D3D12RootSignature,
passes: Vec<ShaderPassMeta>, passes: Vec<DxilShaderPassMeta>,
hlsl_passes: Vec<HlslShaderPassMeta>,
semantics: &ShaderSemantics,) semantics: &ShaderSemantics,)
-> error::Result<(ID3D12DescriptorHeap, ID3D12DescriptorHeap, Vec<FilterPass>)> { -> error::Result<(ID3D12DescriptorHeap, ID3D12DescriptorHeap, Vec<FilterPass>)> {
let validator: IDxcValidator = unsafe { let validator: IDxcValidator = unsafe {
DxcCreateInstance(&CLSID_DxcValidator)? DxcCreateInstance(&CLSID_DxcValidator)?
}; };
let dxc: IDxcLibrary = unsafe { let library: IDxcUtils = unsafe {
DxcCreateInstance(&CLSID_DxcLibrary)? DxcCreateInstance(&CLSID_DxcLibrary)?
}; };
let compiler: IDxcCompiler = unsafe {
DxcCreateInstance(&CLSID_DxcCompiler)?
};
let mut filters = Vec::new(); let mut filters = Vec::new();
let shader_count = passes.len(); let shader_count = passes.len();
let work_heap = let work_heap =
@ -372,32 +387,51 @@ impl FilterChainD3D12 {
sampler_work_heap.suballocate(MAX_BINDINGS_COUNT as usize) sampler_work_heap.suballocate(MAX_BINDINGS_COUNT as usize)
}; };
for (index, (((config, source, mut reflect), for (index, ((((config, source, mut dxil), (_, _, mut hlsl)),
mut texture_heap), mut sampler_heap)) mut texture_heap), mut sampler_heap))
in passes.into_iter() in passes.into_iter()
.zip(hlsl_passes)
.zip(work_heaps) .zip(work_heaps)
.zip(sampler_work_heaps) .zip(sampler_work_heaps)
.enumerate() { .enumerate() {
let reflection = reflect.reflect(index, semantics)?; let dxil_reflection = dxil.reflect(index, semantics)?;
let dxil = reflect.compile(None)?; let dxil = dxil.compile(Some(librashader_reflect::back::dxil::ShaderModel::ShaderModel6_0))?;
let hlsl_reflection = hlsl.reflect(index, semantics)?;
let hlsl = hlsl.compile(Some(ShaderModel::V6_0))?;
let render_format = if let Some(format) = config.get_format_override() {
format
} else if source.format != ImageFormat::Unknown {
source.format
} else {
ImageFormat::R8G8B8A8Unorm
}.into();
eprintln!("building pipeline for pass {:?}", index); eprintln!("building pipeline for pass {:?}", index);
let graphics_pipeline = D3D12GraphicsPipeline::new(device, /// incredibly cursed.
&dxc, let (reflection, graphics_pipeline) = if let Ok(graphics_pipeline) =
&validator, D3D12GraphicsPipeline::new_from_dxil(device,
&dxil, &library,
root_signature, &validator,
if let Some(format) = config.get_format_override() { &dxil,
format root_signature,
} else if source.format != ImageFormat::Unknown { render_format
source.format ) {
} else { (dxil_reflection, graphics_pipeline)
ImageFormat::R8G8B8A8Unorm } else {
}.into(), let graphics_pipeline = D3D12GraphicsPipeline::new_from_hlsl(device,
&source &library,
)?; &compiler,
&hlsl,
root_signature,
render_format
)?;
(hlsl_reflection, graphics_pipeline)
};
let uniform_storage = UniformStorage::new( let uniform_storage = UniformStorage::new(
reflection reflection

View file

@ -1,9 +1,12 @@
use windows::Win32::Foundation::BOOL; use windows::Win32::Foundation::BOOL;
use windows::Win32::Graphics::Direct3D12::{D3D12_BLEND_DESC, D3D12_BLEND_INV_SRC_ALPHA, D3D12_BLEND_OP_ADD, D3D12_BLEND_SRC_ALPHA, D3D12_COLOR_WRITE_ENABLE_ALL, D3D12_CULL_MODE_NONE, D3D12_DESCRIPTOR_RANGE, D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, D3D12_FILL_MODE_SOLID, D3D12_GRAPHICS_PIPELINE_STATE_DESC, D3D12_INPUT_LAYOUT_DESC, D3D12_LOGIC_OP_NOOP, D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, D3D12_RASTERIZER_DESC, D3D12_RENDER_TARGET_BLEND_DESC, D3D12_ROOT_DESCRIPTOR, D3D12_ROOT_DESCRIPTOR_TABLE, D3D12_ROOT_PARAMETER, D3D12_ROOT_PARAMETER_0, D3D12_ROOT_PARAMETER_TYPE_CBV, D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE, D3D12_ROOT_SIGNATURE_DESC, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT, D3D12_SHADER_BYTECODE, D3D12_SHADER_VISIBILITY_ALL, D3D12_SHADER_VISIBILITY_PIXEL, D3D12SerializeRootSignature, D3D_ROOT_SIGNATURE_VERSION_1, ID3D12Device, ID3D12PipelineState, ID3D12RootSignature}; use windows::Win32::Graphics::Direct3D12::{D3D12_BLEND_DESC, D3D12_BLEND_INV_SRC_ALPHA, D3D12_BLEND_OP_ADD, D3D12_BLEND_SRC_ALPHA, D3D12_COLOR_WRITE_ENABLE_ALL, D3D12_CULL_MODE_NONE, D3D12_DESCRIPTOR_RANGE, D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, D3D12_FILL_MODE_SOLID, D3D12_GRAPHICS_PIPELINE_STATE_DESC, D3D12_INPUT_LAYOUT_DESC, D3D12_LOGIC_OP_NOOP, D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, D3D12_RASTERIZER_DESC, D3D12_RENDER_TARGET_BLEND_DESC, D3D12_ROOT_DESCRIPTOR, D3D12_ROOT_DESCRIPTOR_TABLE, D3D12_ROOT_PARAMETER, D3D12_ROOT_PARAMETER_0, D3D12_ROOT_PARAMETER_TYPE_CBV, D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE, D3D12_ROOT_SIGNATURE_DESC, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT, D3D12_SHADER_BYTECODE, D3D12_SHADER_VISIBILITY_ALL, D3D12_SHADER_VISIBILITY_PIXEL, D3D12SerializeRootSignature, D3D_ROOT_SIGNATURE_VERSION_1, ID3D12Device, ID3D12PipelineState, ID3D12RootSignature};
use windows::Win32::Graphics::Direct3D::Dxc::{IDxcBlob, IDxcCompiler, IDxcLibrary, IDxcUtils, IDxcValidator};
use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT, DXGI_FORMAT_UNKNOWN, DXGI_SAMPLE_DESC}; use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT, DXGI_FORMAT_UNKNOWN, DXGI_SAMPLE_DESC};
use librashader_reflect::back::cross::CrossHlslContext; use librashader_reflect::back::cross::CrossHlslContext;
use librashader_reflect::back::dxil::DxilObject;
use librashader_reflect::back::ShaderCompilerOutput; use librashader_reflect::back::ShaderCompilerOutput;
use librashader_reflect::reflect::semantics::BindingStage;
use crate::{error, util}; use crate::{error, util};
use crate::quad_render::DrawQuad; use crate::quad_render::DrawQuad;
@ -112,25 +115,13 @@ impl D3D12RootSignature {
} }
} }
impl D3D12GraphicsPipeline { impl D3D12GraphicsPipeline {
pub fn new(device: &ID3D12Device, fn new_from_blobs(
library: &IDxcLibrary, device: &ID3D12Device,
validator: &IDxcValidator, vertex_dxil: IDxcBlob,
shader_assembly: &ShaderCompilerOutput<DxilObject, (Vec<u32>, Vec<u32>)>, fragment_dxil: IDxcBlob,
root_signature: &D3D12RootSignature, root_signature: &D3D12RootSignature,
render_format: DXGI_FORMAT, render_format: DXGI_FORMAT,
source: &ShaderSource,
) -> error::Result<D3D12GraphicsPipeline> { ) -> error::Result<D3D12GraphicsPipeline> {
if shader_assembly.vertex.requires_runtime_data() {
panic!("vertex needs rt data??")
}
if shader_assembly.fragment.requires_runtime_data() {
panic!("fragment needs rt data??")
}
let vertex_dxil =
util::dxc_validate_shader(library, validator, &shader_assembly.vertex)?;
let fragment_dxil =
util::dxc_validate_shader(library, validator, &shader_assembly.fragment)?;
let input_element = DrawQuad::get_spirv_cross_vbo_desc(); let input_element = DrawQuad::get_spirv_cross_vbo_desc();
let pipeline_state: ID3D12PipelineState = unsafe { let pipeline_state: ID3D12PipelineState = unsafe {
@ -207,4 +198,43 @@ impl D3D12GraphicsPipeline {
handle: pipeline_state, handle: pipeline_state,
}) })
} }
pub fn new_from_dxil(device: &ID3D12Device,
library: &IDxcUtils,
validator: &IDxcValidator,
shader_assembly: &ShaderCompilerOutput<DxilObject, ()>,
root_signature: &D3D12RootSignature,
render_format: DXGI_FORMAT,
) -> error::Result<D3D12GraphicsPipeline> {
if shader_assembly.vertex.requires_runtime_data() {
panic!("vertex needs rt data??")
}
if shader_assembly.fragment.requires_runtime_data() {
panic!("fragment needs rt data??")
}
let vertex_dxil =
util::dxc_validate_shader(library, validator, &shader_assembly.vertex)?;
let fragment_dxil =
util::dxc_validate_shader(library, validator, &shader_assembly.fragment)?;
Self::new_from_blobs(device, vertex_dxil, fragment_dxil, root_signature, render_format)
}
pub fn new_from_hlsl(device: &ID3D12Device,
library: &IDxcUtils,
dxc: &IDxcCompiler,
shader_assembly: &ShaderCompilerOutput<String, CrossHlslContext>,
root_signature: &D3D12RootSignature,
render_format: DXGI_FORMAT,
) -> error::Result<D3D12GraphicsPipeline> {
unsafe {
let vertex_dxil = util::dxc_compile_shader(library, dxc, &shader_assembly.vertex, BindingStage::VERTEX)?;
let fragment_dxil =
util::dxc_compile_shader(library, dxc,&shader_assembly.fragment, BindingStage::FRAGMENT)?;
Self::new_from_blobs(device, vertex_dxil, fragment_dxil, root_signature, render_format)
}
}
} }

View file

@ -25,8 +25,8 @@ mod tests {
#[test] #[test]
fn triangle_d3d12() { fn triangle_d3d12() {
let sample = hello_triangle::d3d12_hello_triangle::Sample::new( let sample = hello_triangle::d3d12_hello_triangle::Sample::new(
"../test/slang-shaders/crt/crt-royale.slangp", // "../test/slang-shaders/crt/crt-royale.slangp",
// "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp", "../test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
&SampleCommandLine { &SampleCommandLine {
use_warp_device: false, use_warp_device: false,
}, },

View file

@ -2,12 +2,13 @@ use std::ffi::CStr;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::u64; use std::u64;
use crate::error; use crate::error;
use windows::core::{Interface, PCSTR}; use windows::core::{Interface, PCSTR, PCWSTR};
use windows::Win32::Graphics::Direct3D::Fxc::{D3DCompile, D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION}; use windows::Win32::Graphics::Direct3D::Fxc::{D3DCompile, D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION};
use windows::Win32::Graphics::Direct3D::ID3DBlob; use windows::Win32::Graphics::Direct3D::ID3DBlob;
use windows::Win32::Graphics::Direct3D12::{ID3D12Device, D3D12_FEATURE_DATA_FORMAT_SUPPORT, D3D12_FEATURE_FORMAT_SUPPORT, ID3D12GraphicsCommandList, ID3D12Resource, D3D12_RESOURCE_BARRIER, D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE, D3D12_RESOURCE_BARRIER_0, D3D12_RESOURCE_TRANSITION_BARRIER, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATES, D3D12_PLACED_SUBRESOURCE_FOOTPRINT, D3D12_MEMCPY_DEST, D3D12_SUBRESOURCE_DATA, D3D12_RESOURCE_DIMENSION_BUFFER, D3D12_TEXTURE_COPY_LOCATION, D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, D3D12_TEXTURE_COPY_LOCATION_0, D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT}; use windows::Win32::Graphics::Direct3D12::{ID3D12Device, D3D12_FEATURE_DATA_FORMAT_SUPPORT, D3D12_FEATURE_FORMAT_SUPPORT, ID3D12GraphicsCommandList, ID3D12Resource, D3D12_RESOURCE_BARRIER, D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE, D3D12_RESOURCE_BARRIER_0, D3D12_RESOURCE_TRANSITION_BARRIER, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATES, D3D12_PLACED_SUBRESOURCE_FOOTPRINT, D3D12_MEMCPY_DEST, D3D12_SUBRESOURCE_DATA, D3D12_RESOURCE_DIMENSION_BUFFER, D3D12_TEXTURE_COPY_LOCATION, D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, D3D12_TEXTURE_COPY_LOCATION_0, D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT};
use windows::Win32::Graphics::Direct3D::Dxc::{DXC_CP, DxcValidatorFlags_Default, DxcValidatorFlags_InPlaceEdit, DxcValidatorFlags_ModuleOnly, DxcValidatorFlags_ValidMask, IDxcBlob, IDxcBlobUtf8, IDxcLibrary, IDxcValidator}; use windows::Win32::Graphics::Direct3D::Dxc::{DXC_CP, DXC_CP_UTF8, DxcValidatorFlags_Default, DxcValidatorFlags_InPlaceEdit, DxcValidatorFlags_ModuleOnly, DxcValidatorFlags_ValidMask, IDxcBlob, IDxcBlobUtf8, IDxcCompiler, IDxcLibrary, IDxcUtils, IDxcValidator};
use windows::Win32::Graphics::Dxgi::Common::*; use windows::Win32::Graphics::Dxgi::Common::*;
use librashader_reflect::reflect::semantics::BindingStage;
use crate::error::assume_d3d12_init; use crate::error::assume_d3d12_init;
/// wtf retroarch? /// wtf retroarch?
@ -145,16 +146,65 @@ pub fn fxc_compile_shader(source: &[u8], entry: &[u8], version: &[u8]) -> error:
} }
} }
pub fn dxc_validate_shader(library: &IDxcLibrary, validator: &IDxcValidator, source: &[u8]) -> error::Result<IDxcBlob> {
pub fn dxc_compile_shader(library: &IDxcUtils, compiler: &IDxcCompiler, source: &String, profile: BindingStage) -> error::Result<IDxcBlob> {
// todo: compile with dxc
// let mut source = source.to_vec();
let include = unsafe {
library.CreateDefaultIncludeHandler()?
};
let blob = unsafe {
library.CreateBlobFromPinned(
source.as_ptr().cast(),
source.as_bytes().len() as u32,
DXC_CP_UTF8
)?
};
let profile = if profile == BindingStage::FRAGMENT {
windows::w!("ps_6_0")
} else {
windows::w!("vs_6_0")
};
unsafe {
let result = compiler.Compile(&blob,
PCWSTR::null(),
windows::w!("main"),
profile,
None,
&[],
&include
)?;
if let Ok(buf) = result.GetErrorBuffer() {
unsafe {
let buf: IDxcBlobUtf8 = buf.cast().unwrap();
let buf = std::slice::from_raw_parts(buf.GetBufferPointer()
.cast(), buf.GetBufferSize());
let str = std::str::from_utf8_unchecked(buf);
if str.len() != 0 {
eprintln!("{}", str);
}
}
}
let result = result.GetResult()?;
Ok(result)
}
}
pub fn dxc_validate_shader(library: &IDxcUtils, validator: &IDxcValidator, source: &[u8]) -> error::Result<IDxcBlob> {
// todo: compile with dxc // todo: compile with dxc
// let mut source = source.to_vec(); // let mut source = source.to_vec();
let blob = unsafe { let blob = unsafe {
library.CreateBlobWithEncodingOnHeapCopy( library.CreateBlob(source.as_ptr().cast(),
source.as_ptr().cast(), source.len() as u32,
source.len() as u32, DXC_CP(0))?
DXC_CP(0)
)?
}; };
unsafe { unsafe {