Using headless opengl context to lint files, removed last bits of glslangValidator

This commit is contained in:
Noah Santschi-Cooney 2021-01-10 23:42:14 +00:00
parent 26c855f016
commit dc1495686a
No known key found for this signature in database
GPG key ID: 3B22282472C8AE48
3 changed files with 107 additions and 241 deletions

View file

@ -1,102 +0,0 @@
import * as unzip from 'adm-zip'
import { execSync } from 'child_process'
import * as fs from 'fs'
import { writeFileSync } from 'fs'
import fetch from 'node-fetch'
import { platform } from 'os'
import * as vscode from 'vscode'
import { Extension } from './extension'
import { log } from './log'
const url = {
'win32': 'https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-windows-x64-Release.zip',
'linux': 'https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-linux-Release.zip',
'darwin': 'https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-osx-Release.zip'
}
const config = vscode.workspace.getConfiguration()
export async function bootstrapGLSLangValidator(e: Extension): Promise<boolean> {
const glslangValidatorPath = config.get('mcglsl.glslangValidatorPath') as string
if (!testExecutable(glslangValidatorPath)) {
if(!await promptDownload(e, glslangValidatorPath)) return false
}
return true
}
async function promptDownload(e: Extension, glslangValidatorPath: string): Promise<boolean> {
const chosen = await vscode.window.showErrorMessage(
`[mc-glsl] glslangValidator not found at: '${glslangValidatorPath}'.`,
{title: 'Download'},
{title: 'Cancel'}
)
if (!chosen || chosen.title !== 'Download') return false
return await tryInstallExecutable(e)
}
export async function tryInstallExecutable(e: Extension): Promise<boolean> {
try {
await installExecutable(e)
} catch (e) {
log.error(`failed downloading glslangValidator ${e}`)
vscode.window.showErrorMessage(`Failed to install glslangValidator: ${e}`)
e.clearStatus()
return false
}
return true
}
async function installExecutable(e: Extension) {
fs.mkdirSync(e.context.globalStoragePath, { recursive: true })
e.updateStatus('$(cloud-download)', 'Downloading glslangValidator')
const glslangBin = '/glslangValidator' + (platform() === 'win32' ? '.exe' : '')
const glslangPath = e.context.globalStoragePath + glslangBin
const response = await fetch(url[platform()])
log.info('glslangValidator download response status: ' + response.status)
const zip = new unzip(await response.buffer())
const bin = zip.readFile('bin' + glslangBin)
log.info('buffer length ' + bin.length)
writeFileSync(glslangPath, bin, {encoding: null, mode: 0o755})
// Make sure download was successful
if (!testExecutable(glslangPath)) {
throw new Error(`Unexpected error occurred checking for binary at ${glslangPath}. Please try again`)
}
// All done!
log.info(`successfully downloaded glslangValidator to ${glslangPath}`)
vscode.window.showInformationMessage(
`glslangValidator has been downloaded to ${glslangPath}. Your config should be updated automatically.`
)
await config.update('mcglsl.glslangValidatorPath', glslangPath, vscode.ConfigurationTarget.Global)
e.clearStatus()
}
function testExecutable(glslangPath: string): boolean {
let stdout = ''
try {
stdout = execSync(glslangPath, {
stdio: 'pipe',
}).toString()
} catch (e) {
stdout = (e.stdout.toString() as string)
}
log.info('glslangValidator first line stdout: "' + stdout.slice(0, stdout.indexOf('\n')) + '"')
const success = stdout.startsWith('Usage')
if (success) {
log.info(`glslangValidator found at ${glslangPath}`)
} else {
log.warn(`glslangValidator not found at ${glslangPath}`)
}
return success
}

View file

@ -107,6 +107,18 @@ impl Display for IncludePosition {
} }
} }
pub enum TreeType {
Fragment, Vertex
}
impl Into<&'static str> for TreeType {
fn into(self) -> &'static str {
match self {
TreeType::Vertex => "vert",
_ => "frag"
}
}
}
impl MinecraftShaderLanguageServer { impl MinecraftShaderLanguageServer {
pub fn error_not_available<DATA>(data: DATA) -> MethodError<DATA> { pub fn error_not_available<DATA>(data: DATA) -> MethodError<DATA> {
@ -252,14 +264,24 @@ impl MinecraftShaderLanguageServer {
Err(e) => return Err(e), Err(e) => return Err(e),
}; };
eprintln!("ancestors for {}:\n{:?}", uri, file_ancestors.iter().map(|e| self.graph.borrow().graph.node_weight(*e).unwrap().clone()).collect::<Vec<String>>()); eprintln!("ancestors for {}:\n\t{:?}", uri, file_ancestors.iter().map(|e| self.graph.borrow().graph.node_weight(*e).unwrap().clone()).collect::<Vec<String>>());
// the set of all filepath->content. TODO: change to Url?
let mut all_sources: HashMap<String, String> = HashMap::new(); let mut all_sources: HashMap<String, String> = HashMap::new();
let mut diagnostics: HashMap<Url, Vec<Diagnostic>> = HashMap::new();//Vec::new(); // the set of filepath->list of diagnostics to report
let mut diagnostics: HashMap<Url, Vec<Diagnostic>> = HashMap::new();
// we want to backfill the diagnostics map with all linked sources
let back_fill = |all_sources: &HashMap<String, String>, diagnostics: &mut HashMap<Url, Vec<Diagnostic>>| {
for (path, _) in all_sources {
diagnostics.entry(Url::from_file_path(path).unwrap()).or_default();
}
};
// if we are a top-level file (this has to be one of the set defined by Optifine, right?)
if file_ancestors.is_empty() { if file_ancestors.is_empty() {
// gather the list of all descendants
let root = self.graph.borrow_mut().find_node(uri).unwrap(); let root = self.graph.borrow_mut().find_node(uri).unwrap();
let tree = match self.get_dfs_for_node(root) { let tree = match self.get_dfs_for_node(root) {
Ok(tree) => tree, Ok(tree) => tree,
Err(e) => { Err(e) => {
@ -273,24 +295,42 @@ impl MinecraftShaderLanguageServer {
let graph = self.graph.borrow(); let graph = self.graph.borrow();
let views = merge_views::generate_merge_list(&tree, &all_sources, &graph); let views = merge_views::generate_merge_list(&tree, &all_sources, &graph);
let views = merger.generate_merge_list(&tree); let tree_type = if uri.ends_with(".fsh") {
TreeType::Fragment
} else {
TreeType::Vertex
};
let stdout = self.invoke_validator(views)?; let stdout = match opengl::validate(tree_type, views) {
Some(s) => s,
None => {
back_fill(&all_sources, &mut diagnostics);
return Ok(diagnostics)
},
};
eprintln!("glslangValidator output: {}\n", stdout); eprintln!("glslangValidator output: {}\n", stdout);
diagnostics.extend(self.parse_validator_stdout(&stdout, "")); diagnostics.extend(self.parse_validator_stdout(stdout, ""));
} else { } else {
let mut all_trees = Vec::new(); let mut all_trees: Vec<(TreeType, Vec<(NodeIndex, Option<_>)>)> = Vec::new();
for root in &file_ancestors { for root in &file_ancestors {
let nodes = match self.get_dfs_for_node(*root) { let nodes = match self.get_dfs_for_node(*root) {
Ok(nodes) => nodes, Ok(nodes) => nodes,
Err(e) => { Err(e) => {
diagnostics.insert(Url::from_file_path(uri).unwrap(), vec![e.into()]); diagnostics.insert(Url::from_file_path(uri).unwrap(), vec![e.into()]);
back_fill(&all_sources, &mut diagnostics); // TODO: confirm
return Ok(diagnostics); return Ok(diagnostics);
} }
}; };
let tree_type = if uri.ends_with(".fsh") {
TreeType::Fragment
} else {
TreeType::Vertex
};
let sources = self.load_sources(&nodes)?; let sources = self.load_sources(&nodes)?;
all_trees.push(nodes); all_trees.push((tree_type, nodes));
all_sources.extend(sources); all_sources.extend(sources);
} }
@ -298,20 +338,20 @@ impl MinecraftShaderLanguageServer {
let graph = self.graph.borrow(); let graph = self.graph.borrow();
let views = merge_views::generate_merge_list(&tree.1, &all_sources, &graph); let views = merge_views::generate_merge_list(&tree.1, &all_sources, &graph);
let stdout = self.invoke_validator(views)?; let stdout = match opengl::validate(tree.0, views) {
Some(s) => s,
None => continue,
};
eprintln!("glslangValidator output: {}\n", stdout); eprintln!("glslangValidator output: {}\n", stdout);
diagnostics.extend(self.parse_validator_stdout(&stdout, "")); diagnostics.extend(self.parse_validator_stdout(stdout, ""));
} }
}; };
for (path, _) in all_sources { back_fill(&all_sources, &mut diagnostics);
diagnostics.entry(Url::from_file_path(path).unwrap()).or_default();
}
Ok(diagnostics) Ok(diagnostics)
} }
fn parse_validator_stdout(&self, stdout: &str, source: &str) -> HashMap<Url, Vec<Diagnostic>> { fn parse_validator_stdout(&self, stdout: String, source: &str) -> HashMap<Url, Vec<Diagnostic>> {
let stdout_lines = stdout.split('\n'); let stdout_lines = stdout.split('\n');
let mut diagnostics: HashMap<Url, Vec<Diagnostic>> = HashMap::with_capacity(stdout_lines.count()); let mut diagnostics: HashMap<Url, Vec<Diagnostic>> = HashMap::with_capacity(stdout_lines.count());
let stdout_lines = stdout.split('\n'); let stdout_lines = stdout.split('\n');
@ -422,34 +462,6 @@ impl MinecraftShaderLanguageServer {
Ok(Some(roots)) Ok(Some(roots))
} }
fn invoke_validator(&self, tree_type: TreeType, source: LinkedList<&str>) -> Result<String> {
/* let mut eventloop = glutin::event_loop::EventLoop::new();
let context = glutin::ContextBuilder::new().build_headless(&eventloop, glutin::dpi::PhysicalSize::new(1920, 1080)); */
Ok("".to_string())
}
/* fn invoke_validator(&self, tree_type: TreeType, source: LinkedList<&str>) -> Result<String> {
let cmd = process::Command::new(&self.config.glslang_validator_path)
.args(&["--stdin", "-S", tree_type.into()])
.stdin(process::Stdio::piped())
.stdout(process::Stdio::piped())
.spawn();
let mut child = cmd?;//.expect("glslangValidator failed to spawn");
let stdin = child.stdin.as_mut().expect("no stdin handle found");
let mut io_slices: Vec<std::io::IoSlice> = source.iter().map(|s| std::io::IoSlice::new(s.as_bytes())).collect();
stdin.write_all_vectored(&mut io_slices[..])?;//.expect("failed to write to stdin");
stdin.flush()?;
let output = child.wait_with_output()?;//.expect("expected output");
let stdout = String::from_utf8(output.stdout).unwrap();
Ok(stdout.trim().to_string())
} */
pub fn publish_diagnostic(&self, diagnostics: HashMap<Url, Vec<Diagnostic>>, document_version: Option<i32>) { pub fn publish_diagnostic(&self, diagnostics: HashMap<Url, Vec<Diagnostic>>, document_version: Option<i32>) {
eprintln!("DIAGNOSTICS:\n{:?}", diagnostics); eprintln!("DIAGNOSTICS:\n{:?}", diagnostics);
for (uri, diagnostics) in diagnostics { for (uri, diagnostics) in diagnostics {

View file

@ -1,43 +1,11 @@
use std::mem;
use std::ptr; use std::ptr;
use std::str; use std::ffi::{CString, CStr};
use std::os::raw::c_void;
use std::ffi::CString;
const SCREEN_WIDTH: u32 = 800; pub fn validate(tree_type: super::TreeType, source: String) -> Option<String> {
const SCREEN_HEIGHT: u32 = 600; let events_loop = glutin::event_loop::EventLoop::new();
const VERTEX_SHADER_SOURCE: &str = r#"
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor; // Specify a vertex attribute for color
out vec3 color;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
color = aColor; // pass the color along to the fragment shader
}
"#;
const FRAGMENT_SHADER_SOURCE: &str = r#"
#version 330 core
out vec4 FragColor;
in vec3 color;
void main()
{
// Set the fragment color to the color passed from the vertex shader
FragColor = vec4(color, 1.0);
}
"#;
fn doit() {
let mut events_loop = glutin::event_loop::EventLoop::new();
let window = glutin::window::WindowBuilder::new()
.with_title("Glutin Triangle")
.with_inner_size(glutin::dpi::Size::Physical(glutin::dpi::PhysicalSize::new(1, 1)));
let gl_window = glutin::ContextBuilder::new().build_headless(&*events_loop, glutin::dpi::PhysicalSize::new(1, 1)).unwrap(); let gl_window = glutin::ContextBuilder::new().build_headless(&*events_loop, glutin::dpi::PhysicalSize::new(1, 1)).unwrap();
#[allow(unused_variables)]
let gl_window = unsafe { let gl_window = unsafe {
let gl_window = gl_window.make_current().unwrap(); let gl_window = gl_window.make_current().unwrap();
gl::load_with(|symbol| gl_window.get_proc_address(symbol) as *const _); gl::load_with(|symbol| gl_window.get_proc_address(symbol) as *const _);
@ -45,69 +13,57 @@ fn doit() {
}; };
unsafe { unsafe {
eprintln!("{}", String::from_utf8(CStr::from_ptr(gl::GetString(gl::VERSION) as *const _).to_bytes().to_vec()).unwrap());
eprintln!("{}", String::from_utf8(CStr::from_ptr(gl::GetString(gl::VENDOR) as *const _).to_bytes().to_vec()).unwrap());
eprintln!("{}", String::from_utf8(CStr::from_ptr(gl::GetString(gl::RENDERER) as *const _).to_bytes().to_vec()).unwrap());
// Setup shader compilation checks // Setup shader compilation checks
let mut success = i32::from(gl::FALSE); let mut success = i32::from(gl::FALSE);
// Vertex shader match tree_type {
let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER); crate::TreeType::Fragment => {
let c_str_vert = CString::new(VERTEX_SHADER_SOURCE.as_bytes()).unwrap(); // Fragment shader
gl::ShaderSource(vertex_shader, 1, &c_str_vert.as_ptr(), ptr::null()); let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
gl::CompileShader(vertex_shader); let c_str_frag = CString::new(source).unwrap();
gl::ShaderSource(fragment_shader, 1, &c_str_frag.as_ptr(), ptr::null());
gl::CompileShader(fragment_shader);
// Check for shader compilation errors // Check for shader compilation errors
gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success); gl::GetShaderiv(fragment_shader, gl::COMPILE_STATUS, &mut success);
if success != i32::from(gl::TRUE) { let result = if success != i32::from(gl::TRUE) {
let mut info_len: gl::types::GLint = 0; let mut info_len: gl::types::GLint = 0;
gl::GetShaderiv(vertex_shader, gl::INFO_LOG_LENGTH, &mut info_len); gl::GetShaderiv(fragment_shader, gl::INFO_LOG_LENGTH, &mut info_len);
let mut info = vec![0u8; info_len as usize]; let mut info = vec![0u8; info_len as usize];
gl::GetShaderInfoLog(vertex_shader, info_len as gl::types::GLsizei, ptr::null_mut(), info.as_mut_ptr() as *mut gl::types::GLchar); gl::GetShaderInfoLog(fragment_shader, info_len as gl::types::GLsizei, ptr::null_mut(), info.as_mut_ptr() as *mut gl::types::GLchar);
info.set_len((info_len - 1) as usize); // ignore null for str::from_utf8 info.set_len((info_len - 1) as usize); // ignore null for str::from_utf8
println!( Some(String::from_utf8(info).unwrap())
"ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n{}", } else {
str::from_utf8_unchecked(&info) None
); };
gl::DeleteShader(fragment_shader);
result
}
crate::TreeType::Vertex => {
// Vertex shader
let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER);
let c_str_vert = CString::new(source).unwrap();
gl::ShaderSource(vertex_shader, 1, &c_str_vert.as_ptr(), ptr::null());
gl::CompileShader(vertex_shader);
// Check for shader compilation errors
gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success);
let result = if success != i32::from(gl::TRUE) {
let mut info_len: gl::types::GLint = 0;
gl::GetShaderiv(vertex_shader, gl::INFO_LOG_LENGTH, &mut info_len);
let mut info = vec![0u8; info_len as usize];
gl::GetShaderInfoLog(vertex_shader, info_len as gl::types::GLsizei, ptr::null_mut(), info.as_mut_ptr() as *mut gl::types::GLchar);
info.set_len((info_len - 1) as usize); // ignore null for str::from_utf8
Some(String::from_utf8(info).unwrap())
} else {
None
};
gl::DeleteShader(vertex_shader);
result
}
} }
}
// Fragment shader
let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
let c_str_frag = CString::new(FRAGMENT_SHADER_SOURCE.as_bytes()).unwrap();
gl::ShaderSource(fragment_shader, 1, &c_str_frag.as_ptr(), ptr::null());
gl::CompileShader(fragment_shader);
// Check for shader compilation errors
gl::GetShaderiv(fragment_shader, gl::COMPILE_STATUS, &mut success);
if success != i32::from(gl::TRUE) {
let mut info_len: gl::types::GLint = 0;
gl::GetShaderiv(fragment_shader, gl::INFO_LOG_LENGTH, &mut info_len);
let mut info = vec![0u8; info_len as usize];
gl::GetShaderInfoLog(fragment_shader, info_len as gl::types::GLsizei, ptr::null_mut(), info.as_mut_ptr() as *mut gl::types::GLchar);
info.set_len((info_len - 1) as usize); // ignore null for str::from_utf8
println!(
"ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n{}",
str::from_utf8_unchecked(&info)
);
}
// Link Shaders
let shader_program = gl::CreateProgram();
gl::AttachShader(shader_program, vertex_shader);
gl::AttachShader(shader_program, fragment_shader);
gl::LinkProgram(shader_program);
// Check for linking errors
gl::GetProgramiv(shader_program, gl::LINK_STATUS, &mut success);
if success != i32::from(gl::TRUE) {
let mut info_len: gl::types::GLint = 0;
gl::GetProgramiv(shader_program, gl::INFO_LOG_LENGTH, &mut info_len);
let mut info = vec![0u8; info_len as usize];
gl::GetProgramInfoLog(shader_program, info_len as gl::types::GLsizei, ptr::null_mut(), info.as_mut_ptr() as *mut gl::types::GLchar);
info.set_len((info_len - 1) as usize); // ignore null for str::from_utf8
println!(
"ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n{}",
str::from_utf8_unchecked(&info)
);
}
/* gl::DeleteShader(vertex_shader);
gl::DeleteShader(fragment_shader);*/
};
} }