Skip to content

Instantly share code, notes, and snippets.

@bczhc
Created April 24, 2026 03:37
Show Gist options
  • Select an option

  • Save bczhc/e8d11db2280ffd20fee43fb1fed7410e to your computer and use it in GitHub Desktop.

Select an option

Save bczhc/e8d11db2280ffd20fee43fb1fed7410e to your computer and use it in GitHub Desktop.
vkd3d issue
use std::env;
use std::sync::Arc;
use wgpu::util::RenderEncoder;
use winit::{
application::ApplicationHandler,
event::WindowEvent,
event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
window::{Window, WindowId},
};
#[derive(Default)]
struct App {
state: Option<render::State>,
window: Option<Arc<Window>>,
}
macro_rules! default {
() => {
Default::default()
};
}
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
// Create window object
let window = Arc::new(
event_loop
.create_window(Window::default_attributes())
.unwrap(),
);
let state = pollster::block_on(render::State::new(window.clone()));
self.state = Some(state);
window.request_redraw();
self.window = Some(Arc::clone(&window));
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
let state = self.state.as_mut().unwrap();
match event {
WindowEvent::CloseRequested => {
println!("The close button was pressed; stopping");
event_loop.exit();
}
WindowEvent::RedrawRequested => {
if let Some(w) = self.window.as_ref() {
state.render(|| w.pre_present_notify());
w.request_redraw();
}
}
WindowEvent::Resized(size) => {
// Reconfigures the size of the surface. We do not re-render
// here as this event is always followed up by redraw request.
state.resize(size);
}
_ => {}
}
}
}
fn main() {
unsafe {
env::set_var("RUST_LOG", "info");
}
env_logger::init();
let event_loop = EventLoop::new().unwrap();
event_loop.set_control_flow(ControlFlow::Poll);
let mut app = App::default();
event_loop.run_app(&mut app).unwrap();
}
mod render {
use clap::Parser;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::sync::Arc;
use wgpu::{
BackendOptions, Backends, Color, ColorTargetState, CurrentSurfaceTexture, Features,
FragmentState, Instance, InstanceDescriptor, InstanceFlags, MemoryBudgetThresholds
, PipelineLayoutDescriptor, RenderPipeline, RenderPipelineDescriptor
, ShaderModuleDescriptorPassthrough, VertexState,
};
use winit::window::Window;
pub struct State {
device: wgpu::Device,
queue: wgpu::Queue,
size: winit::dpi::PhysicalSize<u32>,
surface: wgpu::Surface<'static>,
surface_format: wgpu::TextureFormat,
pipeline: RenderPipeline,
}
#[derive(Parser)]
struct Args {
hlsl_file: PathBuf,
vs_entry: String,
}
fn parse_cli_args() -> Args {
Args::parse()
}
impl State {
pub async fn new(window: Arc<Window>) -> State {
let instance = Instance::new(InstanceDescriptor {
backends: Backends::from_env().unwrap_or_default(),
backend_options: BackendOptions::default(),
display: None,
flags: InstanceFlags::default(),
memory_budget_thresholds: MemoryBudgetThresholds::default(),
});
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions::default())
.await
.unwrap();
let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor {
required_features: Features::PASSTHROUGH_SHADERS,
..default!()
})
.await
.unwrap();
let size = window.inner_size();
let surface = instance.create_surface(window.clone()).unwrap();
let cap = surface.get_capabilities(&adapter);
let surface_format = cap.formats[0];
let args = parse_cli_args();
let mut hlsl_code = String::new();
File::open(args.hlsl_file)
.unwrap()
.read_to_string(&mut hlsl_code)
.unwrap();
let shader_module = unsafe {
device.create_shader_module_passthrough(ShaderModuleDescriptorPassthrough {
label: None,
num_workgroups: (1, 1, 1),
spirv: None,
dxil: None,
hlsl: Some(hlsl_code.into()),
metallib: None,
msl: None,
glsl: None,
wgsl: None,
})
};
let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[],
immediate_size: 0,
});
let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
vertex: VertexState {
module: &shader_module,
entry_point: Some(&args.vs_entry),
compilation_options: Default::default(),
buffers: default!(),
},
fragment: Some(FragmentState {
module: &shader_module,
entry_point: Some("fs_main"),
compilation_options: Default::default(),
targets: &[Some(ColorTargetState {
format: surface_format.add_srgb_suffix(),
blend: None,
write_mask: Default::default(),
})],
}),
label: None,
layout: Some(&pipeline_layout),
primitive: default!(),
depth_stencil: None,
multisample: Default::default(),
multiview_mask: None,
cache: None,
});
let state = State {
device,
queue,
size,
surface,
surface_format,
pipeline,
};
// Configure surface for the first time
state.configure_surface();
state
}
fn configure_surface(&self) {
let surface_config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: self.surface_format,
// Request compatibility with the sRGB-format texture view we‘re going to create later.
view_formats: vec![self.surface_format.add_srgb_suffix()],
alpha_mode: wgpu::CompositeAlphaMode::Auto,
width: self.size.width,
height: self.size.height,
desired_maximum_frame_latency: 2,
present_mode: wgpu::PresentMode::AutoVsync,
};
self.surface.configure(&self.device, &surface_config);
}
pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size;
// reconfigure the surface
self.configure_surface();
}
pub fn render(&mut self, before_submit: impl FnOnce()) {
// Create texture view
let surface_texture = self.surface.get_current_texture();
let surface_texture = match surface_texture {
CurrentSurfaceTexture::Success(x) => x,
_ => {
return;
}
};
let texture_view = surface_texture
.texture
.create_view(&wgpu::TextureViewDescriptor {
// Without add_srgb_suffix() the image we will be working with
// might not be "gamma correct".
format: Some(self.surface_format.add_srgb_suffix()),
..Default::default()
});
// Renders a gray screen
let mut encoder = self.device.create_command_encoder(&Default::default());
// Create the renderpass which will clear the screen.
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &texture_view,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(Color {
r: 0.3,
g: 0.3,
b: 0.3,
a: 1.0,
}),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
// pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
pass.set_pipeline(&self.pipeline);
pass.draw(0..3, 0..1);
// End the renderpass.
drop(pass);
before_submit();
self.queue.submit([encoder.finish()]);
surface_texture.present();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment