Skip to content

Commit

Permalink
rt(gl): use identity matrix for intermediate GL passes
Browse files Browse the repository at this point in the history
  • Loading branch information
chyyran committed Feb 17, 2024
1 parent 913ede3 commit affe7d7
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 80 deletions.
13 changes: 1 addition & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 4 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ The Metal runtime is **not thread safe**. However you can still defer submission
`filter_chain_create_deferred` function.

### Quad vertices and rotations
All runtimes except OpenGL render with an identity matrix MVP and a VBO for with range `[-1, 1]`. The final pass uses a
All runtimes render intermediate passes with an identity matrix MVP and a VBO for with range `[-1, 1]`. The final pass uses a
Quad VBO with range `[0, 1]` and the following projection matrix by default.

```rust
Expand All @@ -81,17 +81,6 @@ static DEFAULT_MVP: &[f32; 16] = &[
As with RetroArch, a rotation on this MVP will be applied only on the final pass for these runtimes. This is the only way to
pass orientation information to shaders.

The OpenGL runtime uses a VBO for range `[0, 1]` for all passes and the following MVP for all passes.

```rust
static GL_DEFAULT_MVP: &[f32; 16] = &[
2.0, 0.0, 0.0, 0.0,
0.0, 2.0, 0.0, 0.0,
0.0, 0.0, 2.0, 0.0,
-1.0, -1.0, 0.0, 1.0,
];
```

### Building

For Rust projects, simply add the crate to your `Cargo.toml`.
Expand Down Expand Up @@ -159,10 +148,12 @@ Please report an issue if you run into a shader that works in RetroArch, but not
* Sampler objects are used rather than `glTexParameter`.
* Sampler inputs and outputs are not renamed. This is useful for debugging shaders in RenderDoc.
* UBO and Push Constant Buffer sizes are padded to 16-byte boundaries.
* The OpenGL runtime uses the same VBOs as the other runtimes as well as the identity matrix MVP for intermediate passes. RetroArch's OpenGL driver uses only the final VBO.
* OpenGL 4.6+
* All caveats from the OpenGL 3.3+ section should be considered.
* Should work on OpenGL 4.5 but this is not guaranteed. The OpenGL 4.6 runtime may eventually switch to using `ARB_spirv_extensions` for loading shaders, and this will not be marked as a breaking change.
* The OpenGL 4.6 runtime uses Direct State Access to minimize changes to the OpenGL state. For GPUs released within the last 5 years, this may improve performance.
* The OpenGL runtime uses the same VBOs as the other runtimes as well as the identity matrix MVP for intermediate passes. RetroArch's OpenGL driver uses only the final VBO.
* Vulkan
* The Vulkan runtime can use [`VK_KHR_dynamic_rendering`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_dynamic_rendering.html).
This extension must be enabled at device creation.
Expand All @@ -177,7 +168,7 @@ Please report an issue if you run into a shader that works in RetroArch, but not
This brings shader compatibility beyond what the RetroArch Direct3D 12 driver provides. The HLSL pipeline fallback may be removed in the future as `spirv-to-dxil` improves.
* The Direct3D 12 runtime requires `dxil.dll` and `dxcompiler.dll` from the [DirectX Shader Compiler](https://github.com/microsoft/DirectXShaderCompiler).
* Metal
* The Metal runtime uses the same VBOs as the other non-OpenGL runtimes as well as the identity matrix MVP for intermediate passes. RetroArch's Metal driver uses only the final VBO.
* The Metal runtime uses the same VBOs as the other runtimes as well as the identity matrix MVP for intermediate passes. RetroArch's Metal driver uses only the final VBO.

Most, if not all shader presets should work fine on librashader. The runtime specific differences should not affect the output,
and are more a heads-up for integrating librashader into your project.
Expand Down
2 changes: 1 addition & 1 deletion librashader-runtime-gl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ gl = "0.14.0"
bytemuck = { version = "1.12.3", features = ["derive"] }
thiserror = "1.0.37"
rayon = "1.6.1"
concat-arrays = "0.1.2"
array-concat = "0.5.2"

sptr = "0.3"

Expand Down
15 changes: 5 additions & 10 deletions librashader-runtime-gl/src/filter_chain/filter_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,11 @@ use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtif
use librashader_reflect::reflect::ReflectShader;
use librashader_runtime::binding::BindingUtil;
use librashader_runtime::framebuffer::FramebufferInit;
use librashader_runtime::quad::QuadType;
use librashader_runtime::render_target::RenderTarget;
use librashader_runtime::scaling::ScaleFramebuffer;
use std::collections::VecDeque;

#[rustfmt::skip]
pub static GL_MVP_DEFAULT: &[f32; 16] = &[
2f32, 0.0, 0.0, 0.0,
0.0, 2.0, 0.0, 0.0,
0.0, 0.0, 2.0, 0.0,
-1.0, -1.0, 0.0, 1.0,
];

pub(crate) struct FilterChainImpl<T: GLInterface> {
pub(crate) common: FilterCommon,
passes: Box<[FilterPass<T>]>,
Expand Down Expand Up @@ -293,7 +286,7 @@ impl<T: GLInterface> FilterChainImpl<T> {

// do not need to rebind FBO 0 here since first `draw` will
// bind automatically.
self.draw_quad.bind_vertices();
self.draw_quad.bind_vertices(QuadType::Offscreen);

let filter = passes[0].config.filter;
let wrap_mode = passes[0].config.wrap_mode;
Expand Down Expand Up @@ -346,6 +339,7 @@ impl<T: GLInterface> FilterChainImpl<T> {
let passes_len = passes.len();
let (pass, last) = passes.split_at_mut(passes_len - 1);

self.draw_quad.bind_vertices(QuadType::Offscreen);
for (index, pass) in pass.iter_mut().enumerate() {
let target = &self.output_framebuffers[index];
source.filter = pass.config.filter;
Expand All @@ -360,14 +354,15 @@ impl<T: GLInterface> FilterChainImpl<T> {
viewport,
&original,
&source,
RenderTarget::offscreen(target, viewport.mvp.unwrap_or(GL_MVP_DEFAULT)),
RenderTarget::identity(target),
);

let target = target.as_texture(pass.config.filter, pass.config.wrap_mode);
self.common.output_textures[index] = target;
source = target;
}

self.draw_quad.bind_vertices(QuadType::Final);
// try to hint the optimizer
assert_eq!(last.len(), 1);
if let Some(pass) = last.iter_mut().next() {
Expand Down
44 changes: 31 additions & 13 deletions librashader-runtime-gl/src/gl/gl3/draw_quad.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,62 @@
use crate::gl::FINAL_VBO_DATA;
use crate::gl::{DrawQuad, OpenGLVertex};
use crate::gl::{FINAL_VBO_DATA, OFFSCREEN_VBO_DATA};
use bytemuck::offset_of;
use gl::types::{GLsizei, GLsizeiptr, GLuint};

use librashader_runtime::quad::QuadType;
pub struct Gl3DrawQuad {
vbo: GLuint,
vbo: [GLuint; 2],
vao: GLuint,
}

impl DrawQuad for Gl3DrawQuad {
fn new() -> Gl3DrawQuad {
let mut vbo = 0;
let mut vbo = [0, 0];
let mut vao = 0;

unsafe {
gl::GenBuffers(1, &mut vbo);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::GenBuffers(2, vbo.as_mut_ptr());
gl::BindBuffer(gl::ARRAY_BUFFER, vbo[0]);
gl::BufferData(
gl::ARRAY_BUFFER,
std::mem::size_of_val(OFFSCREEN_VBO_DATA) as GLsizeiptr,
OFFSCREEN_VBO_DATA.as_ptr().cast(),
gl::STATIC_DRAW,
);

gl::BindBuffer(gl::ARRAY_BUFFER, vbo[1]);
gl::BufferData(
gl::ARRAY_BUFFER,
4 * std::mem::size_of::<OpenGLVertex>() as GLsizeiptr,
std::mem::size_of_val(FINAL_VBO_DATA) as GLsizeiptr,
FINAL_VBO_DATA.as_ptr().cast(),
gl::STATIC_DRAW,
);

gl::BindBuffer(gl::ARRAY_BUFFER, 0);
gl::GenVertexArrays(1, &mut vao);
}

Self { vbo, vao }
}

fn bind_vertices(&self) {
fn bind_vertices(&self, quad_type: QuadType) {
let buffer_index = match quad_type {
QuadType::Offscreen => 0,
QuadType::Final => 1,
};

unsafe {
gl::BindVertexArray(self.vao);
gl::EnableVertexAttribArray(0);
gl::EnableVertexAttribArray(1);

gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo[buffer_index]);

// the provided pointers are of OpenGL provenance with respect to the buffer bound to quad_vbo,
// and not a known provenance to the Rust abstract machine, therefore we give it invalid pointers.
// that are inexpressible in Rust
gl::VertexAttribPointer(
0,
2,
4,
gl::FLOAT,
gl::FALSE,
std::mem::size_of::<OpenGLVertex>() as GLsizei,
Expand All @@ -54,7 +68,7 @@ impl DrawQuad for Gl3DrawQuad {
gl::FLOAT,
gl::FALSE,
std::mem::size_of::<OpenGLVertex>() as GLsizei,
sptr::invalid(offset_of!(OpenGLVertex, position)),
sptr::invalid(offset_of!(OpenGLVertex, texcoord)),
);
}
}
Expand All @@ -72,8 +86,12 @@ impl DrawQuad for Gl3DrawQuad {
impl Drop for Gl3DrawQuad {
fn drop(&mut self) {
unsafe {
if self.vbo != 0 {
gl::DeleteBuffers(1, &self.vbo);
if self.vbo[0] != 0 {
gl::DeleteBuffers(1, &self.vbo[0]);
}

if self.vbo[1] != 0 {
gl::DeleteBuffers(1, &self.vbo[1]);
}

if self.vao != 0 {
Expand Down
54 changes: 36 additions & 18 deletions librashader-runtime-gl/src/gl/gl46/draw_quad.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,44 @@
use crate::gl::FINAL_VBO_DATA;
use crate::gl::{DrawQuad, OpenGLVertex};
use crate::gl::{FINAL_VBO_DATA, OFFSCREEN_VBO_DATA};
use bytemuck::offset_of;
use gl::types::{GLint, GLsizeiptr, GLuint};
use librashader_runtime::quad::QuadType;

pub struct Gl46DrawQuad {
vbo: GLuint,
vbo: [GLuint; 2],
vao: GLuint,
}

impl DrawQuad for Gl46DrawQuad {
fn new() -> Self {
let mut vbo = 0;
let mut vbo = [0, 0];
let mut vao = 0;

unsafe {
gl::CreateBuffers(1, &mut vbo);
gl::CreateBuffers(2, vbo.as_mut_ptr());
gl::NamedBufferData(
vbo,
4 * std::mem::size_of::<OpenGLVertex>() as GLsizeiptr,
vbo[0],
std::mem::size_of_val(OFFSCREEN_VBO_DATA) as GLsizeiptr,
OFFSCREEN_VBO_DATA.as_ptr().cast(),
gl::STATIC_DRAW,
);

gl::NamedBufferData(
vbo[1],
std::mem::size_of_val(FINAL_VBO_DATA) as GLsizeiptr,
FINAL_VBO_DATA.as_ptr().cast(),
gl::STATIC_DRAW,
);

gl::CreateVertexArrays(1, &mut vao);

gl::EnableVertexArrayAttrib(vao, 0);
gl::EnableVertexArrayAttrib(vao, 1);

gl::VertexArrayVertexBuffer(
vao,
0,
vbo,
0,
std::mem::size_of::<OpenGLVertex>() as GLint,
);

gl::VertexArrayAttribFormat(
vao,
0,
2,
4,
gl::FLOAT,
gl::FALSE,
offset_of!(OpenGLVertex, position) as GLuint,
Expand All @@ -58,8 +59,21 @@ impl DrawQuad for Gl46DrawQuad {
Self { vbo, vao }
}

fn bind_vertices(&self) {
fn bind_vertices(&self, quad_type: QuadType) {
let buffer_index = match quad_type {
QuadType::Offscreen => 0,
QuadType::Final => 1,
};

unsafe {
gl::VertexArrayVertexBuffer(
self.vao,
0,
self.vbo[buffer_index],
0,
std::mem::size_of::<OpenGLVertex>() as GLint,
);

gl::BindVertexArray(self.vao);
}
}
Expand All @@ -74,8 +88,12 @@ impl DrawQuad for Gl46DrawQuad {
impl Drop for Gl46DrawQuad {
fn drop(&mut self) {
unsafe {
if self.vbo != 0 {
gl::DeleteBuffers(1, &self.vbo);
if self.vbo[0] != 0 {
gl::DeleteBuffers(1, &self.vbo[0]);
}

if self.vbo[1] != 0 {
gl::DeleteBuffers(1, &self.vbo[1]);
}

if self.vao != 0 {
Expand Down
Loading

0 comments on commit affe7d7

Please sign in to comment.