8000 gl: start compiling a shader without waiting for it by benface · Pull Request #692 · not-fl3/miniquad · GitHub
[go: up one dir, main page]

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions js/gl.js
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,10 @@ var importObject = {
name = name.slice(0, leftBrace);
}

var uniformInfo = GL.programInfos[program] && GL.programInfos[program].uniforms[name]; // returns pair [ dimension_of_uniform_array, uniform_location ]
if (!GL.programInfos[program]) {
GL.populateUniformTable(program);
}
var uniformInfo = GL.programInfos[program].uniforms[name]; // returns pair [ dimension_of_uniform_array, uniform_location ]
if (uniformInfo && arrayIndex >= 0 && arrayIndex < uniformInfo[0]) { // Check if user asked for an out-of-bounds element, i.e. for 'vec4 colors[3];' user could ask for 'colors[10]' which should return -1.
return uniformInfo[1] + arrayIndex;
} else {
Expand Down Expand Up @@ -891,7 +894,9 @@ var importObject = {
glLinkProgram: function (program) {
GL.validateGLObjectID(GL.programs, program, 'glLinkProgram', 'program');
gl.linkProgram(GL.programs[program]);
GL.populateUniformTable(program);
// Reading the uniforms waits for the link to finish, so the
// table fills once a uniform is first asked for.
delete GL.programInfos[program];
},
glPixelStorei: function (pname, param) {
gl.pixelStorei(pname, param);
Expand All @@ -907,8 +912,7 @@ var importObject = {
console.error("GL_INVALID_VALUE in glGetProgramiv");
return;
}
var ptable = GL.programInfos[program];
if (!ptable) {
if (!GL.programs[program]) {
console.error('GL_INVALID_OPERATION in glGetProgramiv(program=' + program + ', pname=' + pname + ', p=0x' + p.toString(16) + '): The specified GL object name does not refer to a program object!');
return;
}
Expand Down Expand Up @@ -1010,6 +1014,9 @@ var importObject = {
stringToUTF8(parameter, array, 0, len);
return msg;
},
webgl_enable_extension: function (name, length) {
return gl.getExtension(UTF8ToString(name, length)) !== null;
},
glCompileShader: function (shader, count, string, length) {
GL.validateGLObjectID(GL.shaders, shader, 'glCompileShader', 'shader');
gl.compileShader(GL.shaders[shader]);
Expand Down
29 changes: 29 additions & 0 deletions src/graphics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,15 @@ impl Display for ShaderType {
}
}

/// A shader [`RenderingBackend::start_shader`] started, for
/// [`RenderingBackend::finish_shader`] to finish.
pub struct PendingShader(PendingShaderInner);

enum PendingShaderInner {
Finished(Result<ShaderId, ShaderError>),
Gl(gl::LinkingProgram),
}

#[derive(Clone, Debug)]
pub enum ShaderError {
CompilationError {
Expand Down Expand Up @@ -1128,6 +1137,26 @@ pub trait RenderingBackend {
shader: ShaderSource,
meta: ShaderMeta,
) -> Result<ShaderId, ShaderError>;
/// Starts making a shader as [`new_shader`](Self::new_shader) does, without
/// waiting for it to compile, so the shaders started before any is finished
/// compile at once where the driver can. [`finish_shader`](Self::finish_shader)
/// then returns what `new_shader` would have.
fn start_shader(&mut self, shader: ShaderSource, meta: ShaderMeta) -> PendingShader {
PendingShader(PendingShaderInner::Finished(self.new_shader(shader, meta)))
}
/// Whether `shader` is still compiling, so [`finish_shader`](Self::finish_shader)
/// would wait for it. Only OpenGL with `KHR_parallel_shader_compile` can tell;
/// otherwise this is `false`.
fn is_shader_compiling(&self, _shader: &PendingShader) -> bool {
false
}
/// Waits for `shader` to compile, if it still is, and returns it.
fn finish_shader(&mut self, shader: PendingShader) -> Result<ShaderId, ShaderError> {
match shader.0 {
PendingShaderInner::Finished(result) => result,
PendingShaderInner::Gl(_) => panic!("OpenGL shader on another context"),
}
}
fn new_texture(
&mut self,
access: TextureAccess,
Expand Down
109 changes: 100 additions & 9 deletions src/graphics/gl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,9 @@ pub struct GlContext {
default_framebuffer: GLuint,
pub(crate) cache: GlCache,
pub(crate) info: ContextInfo,
/// Whether the driver says when a program is done compiling and
/// linking, with `KHR_parallel_shader_compile`.
parallel_shader_compile: bool,
}

impl Default for GlContext {
Expand Down Expand Up @@ -548,6 +551,8 @@ impl GlContext {
buffers: ResourceManager::default(),
textures: Textures(vec![]),
inf 10BC8 o,
parallel_shader_compile: has_extension("GL_KHR_parallel_shader_compile")
|| has_extension("GL_ARB_parallel_shader_compile"),
cache: GlCache {
stored_index_buffer: 0,
stored_index_type: None,
Expand Down Expand Up @@ -579,20 +584,51 @@ impl GlContext {
}
}

fn load_shader_internal(
/// A program linking, and the shaders it links, which [`finish_program`] checks.
pub(super) struct LinkingProgram {
program: GLuint,
vertex_shader: GLuint,
fragment_shader: GLuint,
meta: ShaderMeta,
}

/// Compiles and links a program without asking how either went, which
/// would wait for them.
fn start_program(
vertex_shader: &str,
fragment_shader: &str,
meta: ShaderMeta,
) -> Result<ShaderInternal, ShaderError> {
) -> Result<LinkingProgram, ShaderError> {
unsafe {
let vertex_shader = load_shader(GL_VERTEX_SHADER, vertex_shader)?;
let fragment_shader = load_shader(GL_FRAGMENT_SHADER, fragment_shader)?;
let vertex_shader = compile_shader(GL_VERTEX_SHADER, vertex_shader)?;
let fragment_shader = compile_shader(GL_FRAGMENT_SHADER, fragment_shader)?;

let program = glCreateProgram();
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glLinkProgram(program);

Ok(LinkingProgram {
program,
vertex_shader,
fragment_shader,
meta,
})
}
}

/// Waits for `linking` to compile and link, and finds its uniforms.
fn finish_program(linking: LinkingProgram) -> Result<ShaderInternal, ShaderError> {
let LinkingProgram {
program,
vertex_shader,
fragment_shader,
meta,
} = linking;
unsafe {
check_shader(GL_VERTEX_SHADER, vertex_shader)?;
check_shader(GL_FRAGMENT_SHADER, fragment_shader)?;

// delete no longer used shaders
glDetachShader(program, vertex_shader);
glDeleteShader(vertex_shader);
Expand Down Expand Up @@ -643,7 +679,7 @@ fn load_shader_internal(
}
}

pub fn load_shader(shader_type: GLenum, source: &str) -> Result<GLuint, ShaderError> {
fn compile_shader(shader_type: GLenum, source: &str) -> Result<GLuint, ShaderError> {
unsafe {
let shader = glCreateShader(shader_type);
assert!(shader != 0);
Expand All @@ -653,6 +689,12 @@ pub fn load_shader(shader_type: GLenum, source: &str) -> Result<GLuint, ShaderEr
glShaderSource(shader, 1, csource.as_ptr() as *const _, std::ptr::null());
glCompileShader(shader);

Ok(shader)
}
}

fn check_shader(shader_type: GLenum, shader: GLuint) -> Result<(), ShaderError> {
unsafe {
let mut is_compiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &mut is_compiled as *mut _);
if is_compiled == 0 {
Expand Down Expand Up @@ -686,8 +728,7 @@ pub fn load_shader(shader_type: GLenum, source: &str) -> Result<GLuint, ShaderEr
error_message,
});
}

Ok(shader)
Ok(())
}
}

Expand Down Expand Up @@ -881,6 +922,26 @@ fn gl_info() -> ContextInfo {
}
}

/// Whether the context lists `name` among its extensions.
#[cfg(not(target_arch = "wasm32"))]
fn has_extension(name: &str) -> bool {
let mut count = 0;
unsafe { glGetIntegerv(GL_NUM_EXTENSIONS, &mut count) };
(0..count as GLuint).any(|index| {
let extension = unsafe { glGetStringi(GL_EXTENSIONS, index) };
!extension.is_null()
&& unsafe { std::ffi::CStr::from_ptr(extension as _) }.to_bytes() == name.as_bytes()
})
}

/// Whether the context has extension `name`, which WebGL names without
/// its `GL_` prefix, and enables only once it's asked for.
#[cfg(target_arch = "wasm32")]
fn has_extension(name: &str) -> bool {
let name = name.strip_prefix("GL_").unwrap_or(name);
unsafe { webgl_enable_extension(name.as_ptr(), name.len()) }
}

impl RenderingBackend for GlContext {
fn info(&self) -> ContextInfo {
self.info.clone()
Expand All @@ -891,12 +952,42 @@ impl RenderingBackend for GlContext {
shader: ShaderSource,
meta: ShaderMeta,
) -> Result<ShaderId, ShaderError> {
let shader = self.start_shader(shader, meta);
self.finish_shader(shader)
}

fn start_shader(&mut self, shader: ShaderSource, meta: ShaderMeta) -> PendingShader {
let (fragment, vertex) = match shader {
ShaderSource::Glsl { fragment, vertex } => (fragment, vertex),
_ => panic!("Metal source on OpenGl context"),
};
let shader = load_shader_internal(vertex, fragment, meta)?;
Ok(ShaderId(self.shaders.add(shader)))
PendingShader(match start_program(vertex, fragment, meta) {
Ok(linking) => PendingShaderInner::Gl(linking),
Err(error) => PendingShaderInner::Finished(Err(error)),
})
}

fn is_shader_compiling(&self, shader: &PendingShader) -> bool {
match &shader.0 {
PendingShaderInner::Gl(linking) if self.parallel_shader_compile => {
let mut completed = 0;
unsafe {
glGetProgramiv(linking.program, GL_COMPLETION_STATUS_KHR, &mut completed)
};
completed == 0
}
_ => false,
}
}

fn finish_shader(&mut self, shader: PendingShader) -> Result<ShaderId, ShaderError> {
match shader.0 {
PendingShaderInner::Finished(result) => result,
PendingShaderInner::Gl(linking) => {
let shader = finish_program(linking)?;
Ok(ShaderId(self.shaders.add(shader)))
}
}
}

fn new_texture(
Expand Down
1 change: 1 addition & 0 deletions src/native/gl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub const GL_SHORT: u32 = 0x1402;
pub const GL_DEPTH_TEST: u32 = 0x0B71;
pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: u32 = 0x8518;
pub const GL_LINK_STATUS: u32 = 0x8B82;
pub const GL_COMPLETION_STATUS_KHR: u32 = 0x91B1;
pub const GL_TEXTURE_CUBE_MAP_POSITIVE_Y: u32 = 0x8517;
pub const GL_SAMPLE_ALPHA_TO_COVERAGE: u32 = 0x809E;
pub const GL_RGBA16F: u32 = 0x881A;
Expand Down
3 changes: 3 additions & 0 deletions src/native/wasm/webgl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ pub const GL_SHORT: u32 = 0x1402;
pub const GL_DEPTH_TEST: u32 = 0x0B71;
pub const GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: u32 = 0x8518;
pub const GL_LINK_STATUS: u32 = 0x8B82;
pub const GL_COMPLETION_STATUS_KHR: u32 = 0x91B1;
pub const GL_TEXTURE_CUBE_MAP_POSITIVE_Y: u32 = 0x8517;
pub const GL_SAMPLE_ALPHA_TO_COVERAGE: u32 = 0x809E;
pub const GL_RGBA16F: u32 = 0x881A;
Expand Down Expand Up @@ -853,6 +854,8 @@ extern "C" {
pub fn glClearBufferfv(buffer: GLenum, drawbuffer: GLint, value: *const GLfloat);
pub fn glClearBufferfi(buffer: GLenum, drawbuffer: GLint, depth: GLfloat, stencil: GLint);
pub fn glGetStringi(name: GLenum, index: GLuint) -> *const GLubyte;
/// Enables the WebGL extension `name`, and says whether the context has it.
pub fn webgl_enable_extension(name: *const u8, length: usize) -> bool;
pub fn glCopyBufferSubData(
readTarget: GLenum,
writeTarget: GLenum,
Expand Down
Loading
0