-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uniforms named with brackets fail to get location, causing invisible players/models on OSX (modelMatrix[] / colorMul[]) #25
Comments
…#26) Part of fixing graphical rendering bugs: https://github.com/iceiix/steven/issues/25 Corrupted graphics on AMD video cards: Thinkofname#74 The previous workaround set NUM_SAMPLES to 2, but this added an extra texel fetch. Setting to 1 reproduces the 16px blue striped unexpected behavior. Setting to 0 samples avoids the extra fetch and the striping. This is allowed per http://docs.gl/gl3/glTexImage2DMultisample: > samples specifies the number of samples in the image and must be in the range zero to GL_MAX_SAMPLES - 1.
Fixed some other rendering issues but this one (invisible players, or actually "models" including sun and moon, but not clouds) remains. An easy way to demonstrate the problem is to stare at the sun, or where the sun should be, or another player. Tracked down to this code in src/render/mod.rs: // Model rendering
self.model.draw(&self.frustum, &self.perspective_matrix, &self.camera_matrix, self.light_level, self.sky_offset);
if world.copy_cloud_heightmap(&mut self.clouds.heightmap_data) {
self.clouds.dirty = true;
}
self.clouds.draw(&self.camera.pos, &self.perspective_matrix, &self.camera_matrix, self.light_level, self.sky_offset, delta); self.clouds.draw() renders fine, but not self.model.draw(), which is defined in src/render/model.rs. Ultimately it calls glDrawElements in a loop: gl::draw_elements(gl::TRIANGLES, model.count, self.index_type, 0); triangles, count is 6 for the sun, index type is UNSIGNED_SHORT. Verified this function is called, on both systems, but just doesn't show up. check_gl_error() confirms no glGetError() code either. Does not appear to be a texturing problem, adding to src/render/shaders/sun_frag.glsl Has anyone had similar problems? gamedev.net:
src/render/mod.rs does enable depth tests and back face culling, but if I comment it out: //gl::enable(gl::DEPTH_TEST);
//gl::enable(gl::CULL_FACE_FLAG);
//gl::cull_face(gl::BACK);
//gl::front_face(gl::CLOCK_WISE);
//gl::front_face(gl::COUNTER_CLOCK_WISE); no difference. Besides, my problem seems to be GPU-specific or OS-specific, these two solutions appear to be universal, programming errors. Not to rule out this bug isn't either, but... Another lead: StackOverflow: Call to glDrawElements breaks display on some GPUs?, posted 2018/06/20 but no answers, although some possibly relevant hints:
Steven isn't using glfw so the glfwSwapBuffers equivalent for SDL may be either sdl_video.gl_set_swap_interval and/or window.gl_swap_window();. glClear(GL_COLOR_BUFFER_BIT), Why do we have to clear depth buffer in OpenGL during rendering?, Steven clears the color and depth buffers in src/render/mod.rs tick(): gl::clear(gl::ClearFlags::Color | gl::ClearFlags::Depth); |
While reviewing https://github.com/iceiix/steven/issues/34 other rendering libraries, found this useful tool reference: https://bkaradzic.github.io/bgfx/overview.html - there isn't much available to debug GL on OSX, but there are at least these two tools: https://apitrace.github.io and There are a bunch of keyboard shortcuts available: including disabling various features to help debug. Toggled each of these, but nothing obviously was the problem. Next up is FrameAnalyzer.app, it can also launch the app (click Add), when running click Capture then it captures a frame, then in the browser click Open, this lets you see various bits of useful information. I can see the glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0) call corresponding to src/render/model.rs draw: for model in collection.models.values() {
model.array.bind();
collection.shader.lighting.map(|v| v.set_float2(0.0, 0.0));
collection.shader.model_matrix.map(|v| v.set_matrix4_multi(&model.matrix));
//println!("about to draw model {:?} {:?}", model.count, self.index_type);
gl::draw_elements(gl::TRIANGLES, model.count, self.index_type, 0); // here The input geometry can be viewed, and to make it easier to distinguish from the trans buffer, made the sun slightly trapezoidal, and sure enough the geometry comes through, it is sent to the GPU successfully: this matches what is seen on Ubuntu, where the sun renders successfully: so the problem lies later. The output pane RT: 2, T: 3 shows the solid blue sky, no sun, so it isn't making it to the output. The RT: 2, T: 7 output is the depth buffer (DEPTH_COMPONENT32F), which is a solid 1.000 (the sky is solid R: 122, G: 165, B: 247, A: 255). The API log only shows three calls: glClear, glDrawElements, glClearBufferfv, then glDrawArrays, not too much; the output from glDrawElements doesn't match what is expected, maybe dig deeper there. |
Simplified the shader, this doesn't look good, the modelMatrix is all zeroes! even though if I print it before its sent to OpenGL: for model in collection.models.values() {
model.array.bind();
println!("model.matrix = {:?}", &model.matrix);
collection.shader.model_matrix.map(|v| v.set_matrix4_multi(&model.matrix)); it is the translated identity matrix, we expect: about to draw model 6 5123 How is it sent to GL? v.set_matrix4_multi in the GL wrapper src/gl/mod.rs, this call to glMatrixUniform4fv looks suspicious: pub fn set_matrix4_multi(&self, m: &[::cgmath::Matrix4<f32>]) {
unsafe {
gl::UniformMatrix4fv(self.0, m.len() as i32, false as u8, m.as_ptr() as *const _); // TODO: Most likely isn't safe
}
} "Most likely isn't safe" indeed... |
Sure enough, that was it! set_matrix4_multi is busted, changing to set_matrix4 and the other code to only set and expect a singular matrix iceiix/invisible@2bdfbdd, properly renders the sun (or moon): ...at last. This fixes the test case, but as Steven requires multiple model matrices, a real fix would be different, but at least now the problem is identified. |
set_matrix4 takes a cgmath::Matrix4, which it calls .as_ptr() on to pass to glUniformMatrix4fv. This works because cgmath::Matrix4 is marked as pub struct Matrix4<S> {
pub x: Vector4<S>,
pub y: Vector4<S>,
pub z: Vector4<S>,
pub w: Vector4<S>,
} Rustonomicon: Alternative representations documents repr(C) and others:
set_matrix4_multi takes a reference to an array slice:
m.to_vec().to_ptr(), no difference (and still haven't added repr(C)). But how is a vector better than a slice? Rust by example: Arrays and Slices says arrays are contiguous, which is what we want:
There is discussion in rust-lang about how array slices are not FFI-safe because they are fat pointers: rust-lang/rust#30382 Document how Rust array/slice types are translated for extern "C" functions. but as_ptr() is used to convert the array slice to a raw pointer,
what is Compiling invisible v0.1.0 (/Users/admin/games/rust/invisible)
error[E0308]: mismatched types
--> src/gl/mod.rs:621:71
|
621 | gl::UniformMatrix4fv(self.0, m.len() as i32, false as u8, m.as_ptr()); // TODO: Most likely isn't safe
| ^^^^^^^^^^ expected f32, found struct `cgmath::matrix::Matrix4`
|
= note: expected type `*const f32`
found type `*const cgmath::matrix::Matrix4<f32>` set_matrix4 also uses as_ptr(), but on a Matrix4 itself, which is why it imports this trait: pub fn set_matrix4(&self, m: &::cgmath::Matrix4<f32>) {
use cgmath::Matrix;
unsafe {
gl::UniformMatrix4fv(self.0, 1, false as u8, m.as_ptr());
}
}
How about doing the same on the first element of the slice? pub fn set_matrix4_multi(&self, m: &[::cgmath::Matrix4<f32>]) {
use cgmath::Matrix;
unsafe {
gl::UniformMatrix4fv(self.0, m.len() as i32, false as u8, m[0].as_ptr()); // TODO: Most likely isn't safe
}
} but modelMatrix is still matrix zero. The shader hardcodes let a = [model.matrix[0]; 10];
collection.shader.model_matrix.map(|v| v.set_matrix4_multi(&a)); or? pub fn set_matrix4_multi(&self, m: &[::cgmath::Matrix4<f32>]) {
use cgmath::Matrix;
let a = [m[0]; 10];
unsafe {
gl::UniformMatrix4fv(self.0, 10, false as u8, a[0].as_ptr()); // TODO: Most likely isn't safe
}
} no difference. Interestingly, making only this change, the uniform from an array to a scalar, causes the sun to render, without any other code change: --- a/src/render/model.rs
+++ b/src/render/model.rs
@@ -203,7 +203,7 @@ init_shader! {
uniform = {
optional perspective_matrix => "perspectiveMatrix",
optional camera_matrix => "cameraMatrix",
- optional model_matrix => "modelMatrix[]",
+ optional model_matrix => "modelMatrix",
},
}
} confirmed in IntelGPA Frame Analyzer: even though it is modelMatrix[10] in the shader declaration. Digging deeper, src/gl/mod.rs uniform_location and attribute_location return None if glGetUniformLocation or glGetAttributeLocation fail. Added logging:
change
and the sun renders ok. Is it correct to have brackets on arrays in attribute/uniform names? |
If I log all glGetUniform/AttribLocation failures:
but some are optional, so leaving this logging off for now. Just removing the brackets fixes the rendering, now finally see the sun (and the player): |
Note: The original issue https://github.com/iceiix/steven/issues/5 was somehow deleted, recreating what I remember from memory... UPDATE: partly restored in #5
Players and other models (villagers, sun, moon) do not render on certain systems. Summary of serious graphical bugs:
NUM_SAMPLES=1 graphics corruption?GUI scale/offset bugs?The text was updated successfully, but these errors were encountered: