├── examples ├── pbr │ ├── assets │ │ ├── .gitkeep │ │ └── Shaders │ │ │ ├── skybox.fs │ │ │ ├── cubemap_proj.fs │ │ │ ├── skybox.vs │ │ │ ├── cubemap.vs │ │ │ ├── brdf_integration.vs │ │ │ ├── pbr.vs │ │ │ ├── cubemap_irradiance.fs │ │ │ ├── brdf_integration.fs │ │ │ ├── cubemap_specular_filtered.fs │ │ │ └── pbr.fs │ └── camera.rs ├── device.rs ├── triangle.rs ├── texture.rs └── multi_context.rs ├── .gitignore ├── info ├── grr_logo.png ├── examples │ └── pbr.png └── doc │ └── graphics_pipeline_base_vs_ps.png ├── src ├── __gl.rs ├── error.rs ├── sync.rs ├── query.rs ├── lib.rs ├── debug.rs ├── sampler.rs ├── format.rs ├── device.rs ├── framebuffer.rs ├── buffer.rs ├── image.rs ├── vertex.rs ├── transfer.rs └── command.rs ├── LICENSE ├── LICENSE-MIT ├── Cargo.toml ├── .github └── workflows │ └── main.yml ├── CHANGELOG.md ├── README.md └── LICENSE-APACHE /examples/pbr/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | /.vscode/ -------------------------------------------------------------------------------- /info/grr_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msiglreith/grr/HEAD/info/grr_logo.png -------------------------------------------------------------------------------- /info/examples/pbr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msiglreith/grr/HEAD/info/examples/pbr.png -------------------------------------------------------------------------------- /src/__gl.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::all)] 2 | 3 | include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs")); 4 | -------------------------------------------------------------------------------- /info/doc/graphics_pipeline_base_vs_ps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msiglreith/grr/HEAD/info/doc/graphics_pipeline_base_vs_ps.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The library is licensed under MIT and/or Apache2 2 | See `LICENSE-MIT` and `LICENSE-APACHE` for a full copy. 3 | 4 | ## License PBR sample 5 | 6 | The pbr sample (/examples/pbr) is based on the work of Joey de Vries and licensed under CC BY-NC 4.0. 7 | See https://github.com/JoeyDeVries/LearnOpenGL/tree/master/src/6.pbr/2.2.2.ibl_specular_textured for the original code. 8 | -------------------------------------------------------------------------------- /examples/pbr/assets/Shaders/skybox.fs: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | layout (location = 0) in vec3 a_view_dir; 4 | 5 | layout (binding = 0) uniform samplerCube u_skybox; 6 | 7 | out vec4 f_color; 8 | 9 | vec3 tonemap(vec3 color) { 10 | return color / (color + 1.0); 11 | } 12 | 13 | void main() { 14 | vec3 env_color = textureLod(u_skybox, a_view_dir, 0.0).rgb; 15 | f_color = vec4(tonemap(env_color), 1.0); 16 | } 17 | -------------------------------------------------------------------------------- /examples/pbr/assets/Shaders/cubemap_proj.fs: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | const float PI = 3.141592653; 4 | 5 | layout (location = 0) in vec3 a_view_dir; 6 | 7 | layout (binding = 0) uniform sampler2D u_envmap; 8 | 9 | out vec4 f_color; 10 | 11 | vec2 sample_spherical_map(vec3 dir) { 12 | vec2 uv = vec2(atan(dir.z, -dir.x) / (2 * PI), asin(dir.y) / PI); 13 | uv += 0.5; 14 | return uv; 15 | } 16 | 17 | void main() { 18 | vec2 uv = sample_spherical_map(normalize(a_view_dir)); 19 | f_color = vec4(texture(u_envmap, uv).rgb, 1.0); 20 | } 21 | -------------------------------------------------------------------------------- /examples/pbr/assets/Shaders/skybox.vs: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | layout (location = 0) out vec3 a_view_dir; 4 | 5 | layout(location = 0) uniform mat4 u_inv_proj; 6 | layout(location = 1) uniform mat3 u_inv_view; 7 | 8 | vec2 screen_space_triangle() { 9 | return vec2( 10 | float((gl_VertexID & 1) << 2) - 1.0, 11 | float((gl_VertexID & 2) << 1) - 1.0 12 | ); 13 | } 14 | 15 | void main() { 16 | vec4 pos = vec4(screen_space_triangle(), 0.0, 1.0); 17 | a_view_dir = u_inv_view * (u_inv_proj * pos).xyz; 18 | gl_Position = pos; 19 | } -------------------------------------------------------------------------------- /examples/pbr/assets/Shaders/cubemap.vs: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | layout (location = 0) out vec3 a_view_dir; 4 | 5 | layout(location = 0) uniform mat4 u_inv_proj; 6 | layout(location = 1) uniform mat3 u_inv_view; 7 | 8 | vec2 screen_space_triangle() { 9 | return vec2( 10 | float((gl_VertexID & 1) << 2) - 1.0, 11 | float((gl_VertexID & 2) << 1) - 1.0 12 | ); 13 | } 14 | 15 | void main() { 16 | vec4 pos = vec4(screen_space_triangle(), 0.0, 1.0); 17 | a_view_dir = u_inv_view * (u_inv_proj * pos).xyz; 18 | gl_Position = pos; 19 | } 20 | -------------------------------------------------------------------------------- /examples/pbr/assets/Shaders/brdf_integration.vs: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | layout (location = 0) out vec2 a_uv; 4 | 5 | vec2 screen_space_triangle_pos() { 6 | return vec2( 7 | float((gl_VertexID & 1) << 2) - 1.0, 8 | float((gl_VertexID & 2) << 1) - 1.0 9 | ); 10 | } 11 | 12 | vec2 screen_space_triangle_uv() { 13 | return vec2( 14 | float((gl_VertexID & 1) << 2) * 0.5, 15 | float((gl_VertexID & 2) << 1) * 0.5 16 | ); 17 | } 18 | 19 | void main() { 20 | a_uv = screen_space_triangle_uv(); 21 | gl_Position = vec4(screen_space_triangle_pos(), 0.0, 1.0); 22 | } -------------------------------------------------------------------------------- /examples/device.rs: -------------------------------------------------------------------------------- 1 | use glutin::{event_loop::EventLoop, ContextBuilder}; 2 | 3 | fn main() -> anyhow::Result<()> { 4 | let event_loop = EventLoop::new(); 5 | let context = unsafe { 6 | ContextBuilder::new() 7 | .with_gl_debug_flag(true) 8 | .build_headless(&event_loop, (1.0, 1.0).into())? 9 | .make_current() 10 | .unwrap() 11 | }; 12 | 13 | unsafe { 14 | let grr = grr::Device::new( 15 | |symbol| context.get_proc_address(symbol) as *const _, 16 | grr::Debug::Disable, 17 | ); 18 | 19 | println!("{:#?}", grr.limits()); 20 | } 21 | 22 | Ok(()) 23 | } 24 | -------------------------------------------------------------------------------- /examples/pbr/assets/Shaders/pbr.vs: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | layout (location = 0) in vec3 v_pos; 3 | layout (location = 1) in vec2 v_uv; 4 | layout (location = 2) in vec3 v_normal; 5 | 6 | layout (location = 0) out vec3 a_pos; 7 | layout (location = 1) out vec2 a_uv; 8 | layout (location = 2) out vec3 a_normal; 9 | 10 | layout (location = 0) uniform mat4 u_perspective; 11 | layout (location = 1) uniform mat4 u_view; 12 | layout (location = 2) uniform mat4 u_model; 13 | 14 | void main() { 15 | a_pos = v_pos; 16 | a_uv = v_uv; 17 | a_normal = (transpose(inverse(u_model)) * vec4(v_normal, 0.0)).xyz; 18 | gl_Position = u_perspective * u_view * u_model * vec4(v_pos, 1.0); 19 | } -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 msiglreith 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "grr" 3 | version = "0.8.0" 4 | authors = ["msiglreith "] 5 | description = "Bare metal OpenGL 4.5+ wrapper" 6 | homepage = "https://github.com/msiglreith/grr" 7 | repository = "https://github.com/msiglreith/grr" 8 | keywords = ["graphics"] 9 | edition = "2018" 10 | documentation = "https://docs.rs/grr" 11 | license = "MIT OR Apache-2.0" 12 | exclude = [ 13 | "examples/*", 14 | "info/*", 15 | ] 16 | build = "build.rs" 17 | 18 | [dependencies] 19 | bitflags = "1" 20 | 21 | [dev-dependencies] 22 | assimp = "0.3" 23 | assimp-sys = "0.3" 24 | winit = "0.24" 25 | glutin = "0.26" 26 | image = "0.23" 27 | nalgebra-glm = "0.4" 28 | rand = "0.7" 29 | ndarray = "0.13" 30 | raw-gl-context = "0.1" 31 | anyhow = "1" 32 | 33 | [build-dependencies] 34 | gl_generator = "0.14" 35 | 36 | [[example]] 37 | name = "device" 38 | path = "examples/device.rs" 39 | 40 | [[example]] 41 | name = "triangle" 42 | path = "examples/triangle.rs" 43 | 44 | [[example]] 45 | name = "texture" 46 | path = "examples/texture.rs" 47 | 48 | [[example]] 49 | name = "pbr" 50 | path = "examples/pbr/pbr.rs" 51 | 52 | [[example]] 53 | name = "multi_context" 54 | path = "examples/multi_context.rs" 55 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: Build - ${{ matrix.os }} 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: [ubuntu-latest, windows-latest] 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Install stable 18 | uses: actions-rs/toolchain@v1 19 | with: 20 | toolchain: stable 21 | override: true 22 | 23 | - name: Install components 24 | run: rustup component add --toolchain stable rustfmt clippy 25 | 26 | - if: matrix.os == 'ubuntu-latest' 27 | name: Install unix dependencies 28 | run: sudo apt install libgl1-mesa-dev 29 | 30 | - name: Fetch 31 | uses: actions-rs/cargo@v1 32 | with: 33 | command: fetch 34 | 35 | - name: Check 36 | uses: actions-rs/cargo@v1 37 | with: 38 | command: check 39 | args: --examples 40 | 41 | - name: Format 42 | uses: actions-rs/cargo@v1 43 | with: 44 | command: fmt 45 | args: --all -- --check 46 | 47 | - name: Clippy 48 | uses: actions-rs/cargo@v1 49 | with: 50 | command: clippy 51 | args: -- -D warnings 52 | -------------------------------------------------------------------------------- /examples/pbr/assets/Shaders/cubemap_irradiance.fs: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | 4 | layout (location = 0) in vec3 a_pos; 5 | 6 | out vec4 f_color; 7 | 8 | layout (binding = 0) uniform samplerCube u_env_map; 9 | 10 | const float PI = 3.14159265359; 11 | 12 | void main() 13 | { 14 | vec3 N = normalize(a_pos); 15 | 16 | vec3 irradiance = vec3(0.0); 17 | 18 | // tangent space calculation from origin point 19 | vec3 up = vec3(0.0, 1.0, 0.0); 20 | vec3 right = cross(up, N); 21 | up = cross(N, right); 22 | 23 | float sampleDelta = 0.025; 24 | float nrSamples = 0.0f; 25 | for(float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta) 26 | { 27 | for(float theta = 0.0; theta < 0.5 * PI; theta += sampleDelta) 28 | { 29 | // spherical to cartesian (in tangent space) 30 | vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)); 31 | // tangent space to world 32 | vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N; 33 | 34 | irradiance += texture(u_env_map, sampleVec).rgb * cos(theta) * sin(theta); 35 | nrSamples++; 36 | } 37 | } 38 | irradiance = PI * irradiance * (1.0 / float(nrSamples)); 39 | 40 | f_color = vec4(irradiance, 1.0); 41 | } 42 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | //! Error Handling 2 | 3 | use crate::__gl; 4 | 5 | use crate::device::Device; 6 | use crate::pipeline::{Pipeline, Shader}; 7 | use std::{error, fmt, result}; 8 | 9 | /// Error return codes 10 | /// 11 | /// Error handling in `grr` only deals with runtime-only detectable errors. 12 | /// 13 | /// Other error codes returned by OpenGL are either treated as API miss use (see `Valid Usage` sections), 14 | /// or indicate driver or implementation issues. 15 | /// 16 | /// API validation is provided by the debug functionality on device creation. 17 | #[derive(Debug, PartialEq, Eq, Clone)] 18 | pub enum Error { 19 | OutOfMemory, 20 | 21 | /// Shader compilation failure. 22 | CompileError(Shader), 23 | 24 | /// Link pipeline failure. 25 | LinkError(Pipeline), 26 | } 27 | 28 | /// A specialized Result type for `grr` operations. 29 | pub type Result = result::Result; 30 | 31 | impl Device { 32 | pub(crate) unsafe fn get_error(&self) -> Result<()> { 33 | let err = self.0.GetError(); 34 | match err { 35 | __gl::OUT_OF_MEMORY => Err(Error::OutOfMemory), 36 | _ => Ok(()), 37 | } 38 | } 39 | } 40 | 41 | impl error::Error for Error {} 42 | 43 | impl fmt::Display for Error { 44 | fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { 45 | match *self { 46 | Error::OutOfMemory => write!(fmt, "OutOfMemory"), 47 | Error::CompileError(_) => write!(fmt, "CompileError"), 48 | Error::LinkError(_) => write!(fmt, "LinkError"), 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.8 (Panthera tigris) 2 | 3 | ### Added 4 | 5 | - Setting depth bias 6 | - Storage Images 7 | - Add vec2/vec4 uniform constant 8 | - Add query result retrieval 9 | - Expose context for raw calls 10 | - Add a bunch of new formats :tada: 11 | - Default implementation for pipeline states 12 | - Device/Queue submission commands 13 | - Transfer operations (host <-> buffer <-> image <-> attachment) 14 | - Image type accessors 15 | 16 | ### Changed 17 | - Rename `bind_shader_storage_buffers` to `bind_storage_buffers` 18 | - Shader and Pipeline creation may optionally print the shader log 19 | 20 | ### Fixed 21 | - Linear and Nearest filters were swapped 22 | - Incorrect `draw_indexed_indirect_from_host` stride 23 | 24 | # 0.7 (Feral Cat) 25 | 26 | ### Added 27 | 28 | - Implement `std::error::Error` for `grr::Error` 29 | - Support for Mesh/Task shaders (NV) 30 | - Unbinding for indirect buffers 31 | 32 | ### Changed 33 | - Resources are now `Copy`-able, removes references in all functions 34 | - Split `GraphicsPipelineDesc` into `VertexPipelineDesc` and `MeshPipelineDesc` 35 | 36 | # 0.6 (Puma) 37 | 38 | ### Fixed 39 | - Indexed draw: index pointer was independent of the index ty 40 | 41 | ### Changed 42 | - Add some missing `pub`s for structs 43 | 44 | # 0.5 (Glyph Cat) 45 | 46 | ### Changed 47 | - Adjust Host->Image copy API to specify passed host data 48 | 49 | # 0.4 (Oncilla) 50 | 51 | ### Added 52 | - Add more delete functions 53 | - Framebuffer invalidation 54 | - Improve error handling 55 | - Debug utilities (messages, markers and labels) 56 | - Buffer copies 57 | - Indirect execution commands 58 | - Multisampling support 59 | - Device limits 60 | 61 | # 0.3 (Bay Cat) 62 | 63 | ### Added 64 | - Graphics Pipeline States: 65 | - Input Assembly 66 | - Color Blend 67 | - Depth Stencil 68 | - rasterization 69 | - Seamless Cubemap Filtering 70 | - Automatic Mipmap generation 71 | - Uniform Constants 72 | - Uniform Buffers 73 | - 2D Array/Cube/3D image creation 74 | - Bind framebuffers 75 | - Set framebuffer attachments 76 | - Added a few more image formats 77 | 78 | ### Fixed 79 | - Clearing of depth/stencil attachments 80 | 81 | # 0.2 (Caracal) 82 | 83 | ### Added 84 | - Add `set_viewport` and `set_scissor` commands. 85 | - Add support for samplers (`create_sampler`, `bind_sampler` and `delete_sampler`). 86 | 87 | ### Changed 88 | - Enforce zero-to-one depth range for clip volume. 89 | - Moved vertex divisors to vertex buffer binding. 90 | 91 | ### Fixed 92 | - Fix vertex attribute locations. 93 | 94 | 95 | # 0.1 (Initial release) 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # grr! 4 | ##### A bare metal library for OpenGL 4.5+ 5 | 6 |
7 | 8 | [![crates.io](https://img.shields.io/crates/v/grr.svg)](https://crates.io/crates/grr) 9 | [![docs](https://docs.rs/grr/badge.svg)](https://docs.rs/grr) 10 | [![license-mit](https://img.shields.io/badge/license-MIT-green.svg?style=flat-square)](LICENSE-MIT) 11 | [![license-apache2](https://img.shields.io/badge/license-APACHE2-green.svg?style=flat-square)](LICENSE-Apache2) 12 | [![Build Status](https://github.com/msiglreith/grr/workflows/ci/badge.svg?style=flat)](https://github.com/msiglreith/grr/actions) 13 | 14 | ```toml 15 | [dependencies] 16 | grr = "0.8" 17 | ``` 18 | 19 | `grr` aims at providing a thin layer above OpenGL 4.5+, exposing a modern API orientated on Vulkan. 20 | The sole purpose of the library is to have a cleaned up API for **fast prototyping**. 21 | 22 | ## Features 23 | - API is built around **direct state access** 24 | - Following Vulkan terminology and function names 25 | - Only provided latest GL functionality 26 | - Enforce sRGB framebuffer handling 27 | 28 | ## Getting started 29 | Checkout our [Hello Triangle](https://github.com/msiglreith/grr/blob/master/examples/triangle.rs) example to see the library in action! 30 | 31 | ## Example 32 | ```rust 33 | // Bind graphics pipeline (shaders) 34 | grr.bind_pipeline(&pipeline); 35 | // Configure vertex attributes 36 | grr.bind_vertex_array(&vertex_array); 37 | // Bind vertex buffers for fetching attribute data 38 | grr.bind_vertex_buffers( 39 | &vertex_array, 40 | 0, 41 | &[grr::VertexBufferView { 42 | buffer: &triangle_data, 43 | offset: 0, 44 | stride: (std::mem::size_of::() * 5) as _, 45 | input_rate: grr::InputRate::Vertex, 46 | }] 47 | ); 48 | 49 | // Clear default framebuffer 50 | grr.clear_attachment(grr::Framebuffer::DEFAULT, grr::ClearAttachment::ColorFloat(0, [0.5, 0.5, 0.5, 1.0])); 51 | // Draw triangles 52 | grr.draw(grr::Primitive::Triangles, 0..3, 0..1); 53 | 54 | // Present on screen! 55 | window.swap_buffers().unwrap(); 56 | ``` 57 | 58 | ## Examples 59 | 60 | #### Hello Triangle 61 | 62 | ``` 63 | cargo run --example triangle 64 | ``` 65 | 66 | #### Device Information 67 | 68 | ``` 69 | cargo run --example device 70 | ``` 71 | 72 | #### Texture (Logo) 73 | 74 | ``` 75 | cargo run --example texture 76 | ``` 77 | 78 | #### (Maybe-)Physically-based Rendering (IBL) 79 | 80 |

81 | 82 |

83 | 84 | Assets (model and HDRI) need to be extracted into `examples/assets` before running it! 85 | 86 | ``` 87 | cargo run --example pbr --release 88 | ``` 89 | 90 | * Example ported from/based on the PBR tutorial from https://learnopengl.com/ (CC BY-NC 4.0). 91 | * `Cerberus` model by Andrew Maximov (https://artisaverb.info/Cerberus.html) 92 | * `Popcorn Lobby` HDRI from sIBL Archive (http://www.hdrlabs.com/sibl/archive.html) 93 | -------------------------------------------------------------------------------- /examples/pbr/assets/Shaders/brdf_integration.fs: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | layout (location = 0) in vec2 a_uv; 4 | 5 | out vec2 f_color; 6 | 7 | const float PI = 3.14159265359; 8 | 9 | // http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html 10 | // efficient VanDerCorpus calculation. 11 | float RadicalInverse_VdC(uint bits) 12 | { 13 | bits = (bits << 16u) | (bits >> 16u); 14 | bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); 15 | bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); 16 | bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); 17 | bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); 18 | return float(bits) * 2.3283064365386963e-10; // / 0x100000000 19 | } 20 | 21 | vec2 Hammersley(uint i, uint N) 22 | { 23 | return vec2(float(i)/float(N), RadicalInverse_VdC(i)); 24 | } 25 | 26 | vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) 27 | { 28 | float a = roughness*roughness; 29 | 30 | float phi = 2.0 * PI * Xi.x; 31 | float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y)); 32 | float sinTheta = sqrt(1.0 - cosTheta*cosTheta); 33 | 34 | // from spherical coordinates to cartesian coordinates - halfway vector 35 | vec3 H; 36 | H.x = cos(phi) * sinTheta; 37 | H.y = sin(phi) * sinTheta; 38 | H.z = cosTheta; 39 | 40 | // from tangent-space H vector to world-space sample vector 41 | vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); 42 | vec3 tangent = normalize(cross(up, N)); 43 | vec3 bitangent = cross(N, tangent); 44 | 45 | vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z; 46 | return normalize(sampleVec); 47 | } 48 | 49 | float GeometrySchlickGGX(float NdotV, float roughness) 50 | { 51 | // note that we use a different k for IBL 52 | float a = roughness; 53 | float k = (a * a) / 2.0; 54 | 55 | float nom = NdotV; 56 | float denom = NdotV * (1.0 - k) + k; 57 | 58 | return nom / denom; 59 | } 60 | 61 | float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) 62 | { 63 | float NdotV = max(dot(N, V), 0.0); 64 | float NdotL = max(dot(N, L), 0.0); 65 | float ggx2 = GeometrySchlickGGX(NdotV, roughness); 66 | float ggx1 = GeometrySchlickGGX(NdotL, roughness); 67 | 68 | return ggx1 * ggx2; 69 | } 70 | 71 | vec2 IntegrateBRDF(float NdotV, float roughness) 72 | { 73 | vec3 V; 74 | V.x = sqrt(1.0 - NdotV*NdotV); 75 | V.y = 0.0; 76 | V.z = NdotV; 77 | 78 | float A = 0.0; 79 | float B = 0.0; 80 | 81 | vec3 N = vec3(0.0, 0.0, 1.0); 82 | 83 | const uint SAMPLE_COUNT = 1024u; 84 | for(uint i = 0u; i < SAMPLE_COUNT; ++i) 85 | { 86 | // generates a sample vector that's biased towards the 87 | // preferred alignment direction (importance sampling). 88 | vec2 Xi = Hammersley(i, SAMPLE_COUNT); 89 | vec3 H = ImportanceSampleGGX(Xi, N, roughness); 90 | vec3 L = normalize(2.0 * dot(V, H) * H - V); 91 | 92 | float NdotL = max(L.z, 0.0); 93 | float NdotH = max(H.z, 0.0); 94 | float VdotH = max(dot(V, H), 0.0); 95 | 96 | if(NdotL > 0.0) 97 | { 98 | float G = GeometrySmith(N, V, L, roughness); 99 | float G_Vis = (G * VdotH) / (NdotH * NdotV); 100 | float Fc = pow(1.0 - VdotH, 5.0); 101 | 102 | A += (1.0 - Fc) * G_Vis; 103 | B += Fc * G_Vis; 104 | } 105 | } 106 | A /= float(SAMPLE_COUNT); 107 | B /= float(SAMPLE_COUNT); 108 | return vec2(A, B); 109 | } 110 | // ---------------------------------------------------------------------------- 111 | void main() 112 | { 113 | vec2 integratedBRDF = IntegrateBRDF(a_uv.x, a_uv.y); 114 | f_color = integratedBRDF; 115 | } -------------------------------------------------------------------------------- /examples/pbr/camera.rs: -------------------------------------------------------------------------------- 1 | use nalgebra_glm as glm; 2 | use winit::event::{ElementState, KeyboardInput, VirtualKeyCode}; 3 | 4 | #[derive(Debug, Copy, Clone)] 5 | enum Direction { 6 | Positive, 7 | Negative, 8 | } 9 | 10 | #[derive(Debug)] 11 | struct InputState { 12 | forward: Option, 13 | rot_x: Option, 14 | rot_y: Option, 15 | } 16 | 17 | impl InputState { 18 | fn handle_event(&mut self, input: KeyboardInput) { 19 | let KeyboardInput { 20 | virtual_keycode, 21 | state, 22 | .. 23 | } = input; 24 | match (state, virtual_keycode) { 25 | (ElementState::Pressed, Some(VirtualKeyCode::W)) => { 26 | self.forward = Some(Direction::Positive) 27 | } 28 | (ElementState::Pressed, Some(VirtualKeyCode::S)) => { 29 | self.forward = Some(Direction::Negative) 30 | } 31 | (ElementState::Released, Some(VirtualKeyCode::W)) 32 | | (ElementState::Released, Some(VirtualKeyCode::S)) => self.forward = None, 33 | 34 | (ElementState::Pressed, Some(VirtualKeyCode::Left)) => { 35 | self.rot_y = Some(Direction::Positive) 36 | } 37 | (ElementState::Pressed, Some(VirtualKeyCode::Up)) => { 38 | self.rot_x = Some(Direction::Positive) 39 | } 40 | (ElementState::Pressed, Some(VirtualKeyCode::Right)) => { 41 | self.rot_y = Some(Direction::Negative) 42 | } 43 | (ElementState::Pressed, Some(VirtualKeyCode::Down)) => { 44 | self.rot_x = Some(Direction::Negative) 45 | } 46 | (ElementState::Released, Some(VirtualKeyCode::Left)) 47 | | (ElementState::Released, Some(VirtualKeyCode::Right)) => self.rot_y = None, 48 | (ElementState::Released, Some(VirtualKeyCode::Up)) 49 | | (ElementState::Released, Some(VirtualKeyCode::Down)) => self.rot_x = None, 50 | _ => (), 51 | } 52 | } 53 | } 54 | 55 | #[derive(Debug)] 56 | pub struct Camera { 57 | position: glm::Vec3, 58 | rotation: glm::Vec3, 59 | input: InputState, 60 | } 61 | 62 | impl Camera { 63 | pub fn new(position: glm::Vec3, rotation: glm::Vec3) -> Self { 64 | Camera { 65 | position, 66 | rotation, 67 | input: InputState { 68 | forward: None, 69 | rot_x: None, 70 | rot_y: None, 71 | }, 72 | } 73 | } 74 | 75 | pub fn handle_event(&mut self, input: KeyboardInput) { 76 | self.input.handle_event(input); 77 | } 78 | 79 | pub fn update(&mut self, dt: f32) { 80 | let move_speed = 90.0 * dt; 81 | let rot_speed = 2.0 * dt; 82 | 83 | self.position += Self::map_direction(self.input.forward) * move_speed * self.view_dir(); 84 | self.rotation.x += Self::map_direction(self.input.rot_x) * rot_speed; 85 | self.rotation.y += Self::map_direction(self.input.rot_y) * rot_speed; 86 | } 87 | 88 | pub fn view_dir(&self) -> glm::Vec3 { 89 | glm::rotate_z_vec3( 90 | &glm::rotate_y_vec3( 91 | &glm::rotate_x_vec3(&glm::vec3(0.0, 0.0, -1.0), self.rotation.x), 92 | self.rotation.y, 93 | ), 94 | self.rotation.z, 95 | ) 96 | } 97 | 98 | pub fn position(&self) -> glm::Vec3 { 99 | self.position 100 | } 101 | 102 | pub fn view(&self) -> glm::Mat4 { 103 | glm::look_at( 104 | &self.position, 105 | &(self.position + self.view_dir()), 106 | &glm::vec3(0.0, 1.0, 0.0), 107 | ) 108 | } 109 | 110 | fn map_direction(direction: Option) -> f32 { 111 | match direction { 112 | Some(Direction::Positive) => 1.0, 113 | Some(Direction::Negative) => -1.0, 114 | None => 0.0, 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /examples/pbr/assets/Shaders/cubemap_specular_filtered.fs: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | layout (location = 0) in vec3 a_pos; 4 | 5 | out vec4 f_color; 6 | 7 | layout (binding = 0) uniform samplerCube u_env_map; 8 | layout (location = 2) uniform float u_roughness; 9 | 10 | const float PI = 3.14159265359; 11 | // ---------------------------------------------------------------------------- 12 | float DistributionGGX(vec3 N, vec3 H, float roughness) 13 | { 14 | float a = roughness*roughness; 15 | float a2 = a*a; 16 | float NdotH = max(dot(N, H), 0.0); 17 | float NdotH2 = NdotH*NdotH; 18 | 19 | float nom = a2; 20 | float denom = (NdotH2 * (a2 - 1.0) + 1.0); 21 | denom = PI * denom * denom; 22 | 23 | return nom / denom; 24 | } 25 | // ---------------------------------------------------------------------------- 26 | // http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html 27 | // efficient VanDerCorpus calculation. 28 | float RadicalInverse_VdC(uint bits) 29 | { 30 | bits = (bits << 16u) | (bits >> 16u); 31 | bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); 32 | bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); 33 | bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); 34 | bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); 35 | return float(bits) * 2.3283064365386963e-10; // / 0x100000000 36 | } 37 | // ---------------------------------------------------------------------------- 38 | vec2 Hammersley(uint i, uint N) 39 | { 40 | return vec2(float(i)/float(N), RadicalInverse_VdC(i)); 41 | } 42 | // ---------------------------------------------------------------------------- 43 | vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) 44 | { 45 | float a = roughness*roughness; 46 | 47 | float phi = 2.0 * PI * Xi.x; 48 | float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y)); 49 | float sinTheta = sqrt(1.0 - cosTheta*cosTheta); 50 | 51 | // from spherical coordinates to cartesian coordinates - halfway vector 52 | vec3 H; 53 | H.x = cos(phi) * sinTheta; 54 | H.y = sin(phi) * sinTheta; 55 | H.z = cosTheta; 56 | 57 | // from tangent-space H vector to world-space sample vector 58 | vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); 59 | vec3 tangent = normalize(cross(up, N)); 60 | vec3 bitangent = cross(N, tangent); 61 | 62 | vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z; 63 | return normalize(sampleVec); 64 | } 65 | // ---------------------------------------------------------------------------- 66 | void main() 67 | { 68 | vec3 N = normalize(a_pos); 69 | 70 | // make the simplyfying assumption that V equals R equals the normal 71 | vec3 R = N; 72 | vec3 V = R; 73 | 74 | const uint SAMPLE_COUNT = 1024u; 75 | vec3 prefilteredColor = vec3(0.0); 76 | float totalWeight = 0.0; 77 | 78 | for(uint i = 0u; i < SAMPLE_COUNT; ++i) 79 | { 80 | // generates a sample vector that's biased towards the preferred alignment direction (importance sampling). 81 | vec2 Xi = Hammersley(i, SAMPLE_COUNT); 82 | vec3 H = ImportanceSampleGGX(Xi, N, u_roughness); 83 | vec3 L = normalize(2.0 * dot(V, H) * H - V); 84 | 85 | float NdotL = max(dot(N, L), 0.0); 86 | if(NdotL > 0.0) 87 | { 88 | // sample from the environment's mip level based on roughness/pdf 89 | float D = DistributionGGX(N, H, u_roughness); 90 | float NdotH = max(dot(N, H), 0.0); 91 | float HdotV = max(dot(H, V), 0.0); 92 | float pdf = D * NdotH / (4.0 * HdotV) + 0.0001; 93 | 94 | float resolution = 512.0; // resolution of source cubemap (per face) 95 | float saTexel = 4.0 * PI / (6.0 * resolution * resolution); 96 | float saSample = 1.0 / (float(SAMPLE_COUNT) * pdf + 0.0001); 97 | 98 | float mipLevel = u_roughness == 0.0 ? 0.0 : 0.5 * log2(saSample / saTexel); 99 | 100 | prefilteredColor += textureLod(u_env_map, L, mipLevel).rgb * NdotL; 101 | totalWeight += NdotL; 102 | } 103 | } 104 | 105 | prefilteredColor = prefilteredColor / totalWeight; 106 | 107 | f_color = vec4(prefilteredColor, 1.0); 108 | } 109 | -------------------------------------------------------------------------------- /src/sync.rs: -------------------------------------------------------------------------------- 1 | use crate::__gl; 2 | 3 | use crate::Device; 4 | 5 | bitflags!( 6 | /// Memory barrier. 7 | pub struct Barrier: u32 { 8 | /// Read access to a vertex buffer. 9 | /// 10 | /// Bound via `bind_vertex_buffers`, used in drawing commands. 11 | const VERTEX_ATTRIBUTE_READ = __gl::VERTEX_ATTRIB_ARRAY_BARRIER_BIT; 12 | 13 | /// Read access to an index buffer. 14 | /// 15 | /// Bound via `bind_index_buffer`, used in indexed drawing commands. 16 | const INDEX_READ = __gl::ELEMENT_ARRAY_BARRIER_BIT; 17 | 18 | /// Read access to a uniform buffer. 19 | /// 20 | /// Bound via `bind_uniform_buffers`. 21 | const UNIFORM_READ = __gl::UNIFORM_BARRIER_BIT; 22 | 23 | /// Read access to a sampled image. 24 | /// 25 | /// Bound via `bind_image_views`. 26 | const SAMPLED_IMAGE_READ = __gl::TEXTURE_FETCH_BARRIER_BIT; 27 | 28 | /// Read/Write access to a storage image. 29 | /// 30 | /// Bound via `bind_storage_image_views`. 31 | const STORAGE_IMAGE_RW = __gl::SHADER_IMAGE_ACCESS_BARRIER_BIT; 32 | 33 | /// Read access to an indirect command buffer. 34 | /// 35 | /// Bound via `bind_draw_indirect_buffer` or `bind_dispatch_indirect_buffer`. 36 | const INDIRECT_COMMAND_READ = __gl::COMMAND_BARRIER_BIT; 37 | 38 | /// Read/write access to a buffer in transfer operations to/from images or attachments. 39 | /// 40 | /// Used in `copy_attachment_to_buffer` and `copy_buffer_to_image`. 41 | const BUFFER_IMAGE_TRANSFER_RW = __gl::PIXEL_BUFFER_BARRIER_BIT; 42 | 43 | /// Read/write access to an image in transfer operation. 44 | const IMAGE_TRANSFER_RW = __gl::TEXTURE_UPDATE_BARRIER_BIT; 45 | 46 | /// Read/write access to an buffer in a transfer operation and mapping operations. 47 | const BUFFER_TRANSFER_RW = __gl::BUFFER_UPDATE_BARRIER_BIT; 48 | 49 | /// Read/write access to framebuffer attachments. 50 | const FRAMEBUFFER_RW = __gl::FRAMEBUFFER_BARRIER_BIT; 51 | 52 | /// 53 | const TRANSFORM_FEEDBACK_WRITE = __gl::TRANSFORM_FEEDBACK_BARRIER_BIT; 54 | 55 | /// Read/write access to atomic counters. 56 | const ATOMIC_COUNTER_RW = __gl::ATOMIC_COUNTER_BARRIER_BIT; 57 | 58 | /// Read/write access to storage buffers. 59 | /// 60 | /// Bound via `bind_storage_buffers`. 61 | const STORAGE_BUFFER_RW = __gl::SHADER_STORAGE_BARRIER_BIT; 62 | 63 | /// Inserts a image (or texture) barrier to control read/write access 64 | /// of fragments in subsequent draw calls. 65 | /// 66 | /// Image barriers are required to prevent rendering feedback loops 67 | /// in case of reading texels of an image which is bound to the current 68 | /// framebuffer as attachment. 69 | /// 70 | /// The barrier will ensure that writes to the texel are finished and caches 71 | /// have been invalidated. 72 | const INPUT_ATTACHMENT_READ = 0x8000_0000; 73 | 74 | const ALL = __gl::ALL_BARRIER_BITS; 75 | } 76 | ); 77 | 78 | bitflags!( 79 | /// Memory barrier for by-region dependencies. 80 | pub struct RegionBarrier: u32 { 81 | const UNIFORM_READ = __gl::UNIFORM_BARRIER_BIT; 82 | const SAMPLED_IMAGE_READ = __gl::TEXTURE_FETCH_BARRIER_BIT; 83 | const STORAGE_IMAGE_RW = __gl::SHADER_IMAGE_ACCESS_BARRIER_BIT; 84 | const STORAGE_BUFFER_RW = __gl::SHADER_STORAGE_BARRIER_BIT; 85 | const FRAMEBUFFER_RW = __gl::FRAMEBUFFER_BARRIER_BIT; 86 | const ATOMIC_COUNTER_RW = __gl::ATOMIC_COUNTER_BARRIER_BIT; 87 | } 88 | ); 89 | 90 | impl Device { 91 | /// 92 | pub unsafe fn memory_barrier(&self, mut flags: Barrier) { 93 | if flags.contains(Barrier::INPUT_ATTACHMENT_READ) { 94 | self.0.TextureBarrier(); 95 | } 96 | 97 | if flags != Barrier::ALL { 98 | flags.remove(Barrier::INPUT_ATTACHMENT_READ); 99 | if flags.is_empty() { 100 | return; 101 | } 102 | } 103 | 104 | self.0.MemoryBarrier(flags.bits()); 105 | } 106 | 107 | /// 108 | pub unsafe fn memory_barrier_by_region(&self, flags: RegionBarrier) { 109 | self.0.MemoryBarrierByRegion(flags.bits()); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/query.rs: -------------------------------------------------------------------------------- 1 | use crate::__gl; 2 | use crate::__gl::types::GLuint; 3 | 4 | use crate::device::Device; 5 | 6 | /// 7 | #[repr(u32)] 8 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 9 | pub enum QueryType { 10 | /// 11 | Timestamp = __gl::TIMESTAMP, 12 | /// 13 | TimeElapsed = __gl::TIME_ELAPSED, 14 | /// 15 | Occlusion = __gl::ANY_SAMPLES_PASSED, 16 | /// 17 | OcclusionConservative = __gl::ANY_SAMPLES_PASSED_CONSERVATIVE, 18 | /// 19 | OcclusionPrecision = __gl::SAMPLES_PASSED, 20 | /// 21 | InputAssemblyVertices = __gl::VERTICES_SUBMITTED, 22 | /// 23 | InputAssemblyPrimitives = __gl::PRIMITIVES_SUBMITTED, 24 | /// Number of vertex shader invocations. 25 | VertexShaderInvocations = __gl::VERTEX_SHADER_INVOCATIONS, 26 | /// Number of geometry shader invocations. 27 | GeometryShaderInvocations = __gl::GEOMETRY_SHADER_INVOCATIONS, 28 | /// 29 | GeometryShaderPrimitives = __gl::GEOMETRY_SHADER_PRIMITIVES_EMITTED, 30 | /// 31 | TransformFeedbackPrimitivesWritten = __gl::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 32 | /// 33 | TransformFeedbackOverflow = __gl::TRANSFORM_FEEDBACK_OVERFLOW, 34 | /// 35 | TransformFeedbackStreamOverflow = __gl::TRANSFORM_FEEDBACK_STREAM_OVERFLOW, 36 | /// Number of input primitives for the primitive clipping stage. 37 | ClippingInvocations = __gl::CLIPPING_INPUT_PRIMITIVES, 38 | /// Number of output primitives for the primitive clipping stage. 39 | ClippingPrimitives = __gl::CLIPPING_OUTPUT_PRIMITIVES, 40 | /// Number of fragment shader invocations. 41 | FragmentShaderInvocations = __gl::FRAGMENT_SHADER_INVOCATIONS, 42 | /// Number of patches processed by tessellation control shader. 43 | TessellationControlShaderPatches = __gl::TESS_CONTROL_SHADER_PATCHES, 44 | /// Number of tessellation evaluation shader invocations. 45 | TessellationEvaluationShaderInvocations = __gl::TESS_EVALUATION_SHADER_INVOCATIONS, 46 | /// Number of compute shader invocations. 47 | ComputeShaderInvocations = __gl::COMPUTE_SHADER_INVOCATIONS, 48 | } 49 | 50 | bitflags!( 51 | pub struct QueryResultFlags: u8 { 52 | const WAIT = 0x1; 53 | } 54 | ); 55 | 56 | /// 57 | #[repr(u32)] 58 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 59 | pub enum ConditionalMode { 60 | /// 61 | NoWait = __gl::QUERY_NO_WAIT, 62 | /// 63 | NoWaitInverted = __gl::QUERY_NO_WAIT_INVERTED, 64 | /// Wait for query results available. 65 | Wait = __gl::QUERY_WAIT, 66 | /// Wait for query results available (inverted condition). 67 | WaitInverted = __gl::QUERY_WAIT_INVERTED, 68 | /// 69 | WaitByRegion = __gl::QUERY_BY_REGION_WAIT, 70 | /// 71 | WaitByRegionInverted = __gl::QUERY_BY_REGION_WAIT_INVERTED, 72 | } 73 | 74 | #[derive(Clone, Copy)] 75 | pub struct Query { 76 | raw: GLuint, 77 | ty: QueryType, 78 | } 79 | 80 | impl Device { 81 | pub unsafe fn create_query(&self, ty: QueryType) -> Query { 82 | let mut query = 0; 83 | self.0.CreateQueries(ty as _, 1, &mut query as *mut _); 84 | Query { raw: query, ty } 85 | } 86 | 87 | pub unsafe fn begin_query(&self, query: Query) { 88 | #[allow(clippy::match_single_binding)] 89 | let index = match query.ty { 90 | _ => 0, 91 | }; 92 | 93 | self.0.BeginQueryIndexed(query.ty as _, index, query.raw); 94 | } 95 | 96 | pub unsafe fn end_query(&self, query: Query) { 97 | #[allow(clippy::match_single_binding)] 98 | let index = match query.ty { 99 | _ => 0, 100 | }; 101 | 102 | self.0.EndQueryIndexed(query.ty as _, index); 103 | } 104 | 105 | pub unsafe fn write_timestamp(&self, query: Query) { 106 | self.0.QueryCounter(query.raw, __gl::TIMESTAMP); 107 | } 108 | 109 | pub unsafe fn get_query_result_u32(&self, query: Query, flags: QueryResultFlags) -> u32 { 110 | let mut result = 0; 111 | let flags = if flags.contains(QueryResultFlags::WAIT) { 112 | __gl::QUERY_RESULT 113 | } else { 114 | __gl::QUERY_RESULT_NO_WAIT 115 | }; 116 | self.0.GetQueryObjectuiv(query.raw, flags, &mut result); 117 | result 118 | } 119 | 120 | pub unsafe fn get_query_result_u64(&self, query: Query, flags: QueryResultFlags) -> u64 { 121 | let mut result = 0; 122 | let flags = if flags.contains(QueryResultFlags::WAIT) { 123 | __gl::QUERY_RESULT 124 | } else { 125 | __gl::QUERY_RESULT_NO_WAIT 126 | }; 127 | self.0.GetQueryObjectui64v(query.raw, flags, &mut result); 128 | result 129 | } 130 | 131 | pub unsafe fn begin_conditional_rendering(&self, query: Query, mode: ConditionalMode) { 132 | self.0.BeginConditionalRender(query.raw, mode as _); 133 | } 134 | 135 | pub unsafe fn end_conditional_rendering(&self) { 136 | self.0.EndConditionalRender(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /examples/pbr/assets/Shaders/pbr.fs: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | layout (location = 0) in vec3 a_pos; 4 | layout (location = 1) in vec2 a_uv; 5 | layout (location = 2) in vec3 a_normal; 6 | 7 | out vec4 f_color; 8 | 9 | layout (binding = 0) uniform sampler2D u_albedo; 10 | layout (binding = 1) uniform sampler2D u_normals; 11 | layout (binding = 2) uniform sampler2D u_metalness; 12 | layout (binding = 3) uniform sampler2D u_roughness; 13 | layout (binding = 4) uniform sampler2D u_occlusion; 14 | 15 | // Image Based Lighting 16 | layout (binding = 5) uniform sampler2D u_brdf_lut; 17 | layout (binding = 6) uniform samplerCube u_env_prefiltered; 18 | layout (binding = 7) uniform samplerCube u_env_irradiance; 19 | 20 | layout (location = 3) uniform vec3 u_camera_pos; 21 | 22 | const float PI = 3.14159265359; 23 | // ---------------------------------------------------------------------------- 24 | // Easy trick to get tangent-normals to world-space to keep PBR code simplified. 25 | // Don't worry if you don't get what's going on; you generally want to do normal 26 | // mapping the usual way for performance anways; I do plan make a note of this 27 | // technique somewhere later in the normal mapping tutorial. 28 | vec3 getNormalFromMap() 29 | { 30 | vec3 tangentNormal = texture(u_normals, a_uv).xyz; 31 | 32 | vec3 Q1 = dFdx(a_pos); 33 | vec3 Q2 = dFdy(a_pos); 34 | vec2 st1 = dFdx(a_uv); 35 | vec2 st2 = dFdy(a_uv); 36 | 37 | vec3 N = normalize(a_normal); 38 | vec3 T = normalize(Q1*st2.t - Q2*st1.t); 39 | vec3 B = normalize(-Q1 * st2.s + Q2 * st1.s ); 40 | mat3 TBN = mat3(T, B, N); 41 | 42 | return normalize(TBN * tangentNormal); 43 | } 44 | // ---------------------------------------------------------------------------- 45 | float DistributionGGX(vec3 N, vec3 H, float roughness) 46 | { 47 | float a = roughness*roughness; 48 | float a2 = a*a; 49 | float NdotH = max(dot(N, H), 0.0); 50 | float NdotH2 = NdotH*NdotH; 51 | 52 | float nom = a2; 53 | float denom = (NdotH2 * (a2 - 1.0) + 1.0); 54 | denom = PI * denom * denom; 55 | 56 | return nom / denom; 57 | } 58 | // ---------------------------------------------------------------------------- 59 | float GeometrySchlickGGX(float NdotV, float roughness) 60 | { 61 | float r = (roughness + 1.0); 62 | float k = (r*r) / 8.0; 63 | 64 | float nom = NdotV; 65 | float denom = NdotV * (1.0 - k) + k; 66 | 67 | return nom / denom; 68 | } 69 | // ---------------------------------------------------------------------------- 70 | float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) 71 | { 72 | float NdotV = max(dot(N, V), 0.0); 73 | float NdotL = max(dot(N, L), 0.0); 74 | float ggx2 = GeometrySchlickGGX(NdotV, roughness); 75 | float ggx1 = GeometrySchlickGGX(NdotL, roughness); 76 | 77 | return ggx1 * ggx2; 78 | } 79 | // ---------------------------------------------------------------------------- 80 | vec3 fresnelSchlick(float cosTheta, vec3 F0) 81 | { 82 | return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); 83 | } 84 | // ---------------------------------------------------------------------------- 85 | vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) 86 | { 87 | return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); 88 | } 89 | 90 | float srgb_to_linear(float x) { 91 | if (x <= 0.04045) { 92 | return x / 12.92; 93 | } else { 94 | return pow((x + 0.055) / 1.055, 2.4); 95 | } 96 | } 97 | 98 | vec3 srgb_to_linear(vec3 v) { 99 | return vec3(srgb_to_linear(v.x), srgb_to_linear(v.y), srgb_to_linear(v.z)); 100 | } 101 | 102 | vec3 tonemap(vec3 color) { 103 | return color / (color + 1.0); 104 | } 105 | 106 | void main() { 107 | vec3 albedo = texture(u_albedo, a_uv).rgb; 108 | vec3 normals = texture(u_normals, a_uv).rgb; 109 | float metalness = texture(u_metalness, a_uv).r; 110 | float roughness = texture(u_roughness, a_uv).r; 111 | float ao = texture(u_occlusion, a_uv).r; 112 | 113 | vec3 N = getNormalFromMap(); 114 | vec3 V = normalize(u_camera_pos - a_pos); 115 | vec3 R = reflect(-V, N); 116 | 117 | vec3 F0 = vec3(0.04); 118 | F0 = mix(F0, albedo, metalness); 119 | 120 | vec3 F = fresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); 121 | 122 | vec3 kS = F; 123 | vec3 kD = 1.0 - kS; 124 | kD *= 1.0 - metalness; 125 | 126 | vec3 irradiance = texture(u_env_irradiance, N).rgb; 127 | vec3 diffuse = irradiance * albedo; 128 | 129 | // sample both the pre-filter map and the BRDF lut and combine them together as per the Split-Sum approximation to get the IBL specular part. 130 | const float MAX_REFLECTION_LOD = 4.0; 131 | vec3 prefilteredColor = textureLod(u_env_prefiltered, R, roughness * MAX_REFLECTION_LOD).rgb; 132 | vec2 brdf = texture(u_brdf_lut, vec2(max(dot(N, V), 0.0), roughness)).rg; 133 | vec3 specular = prefilteredColor * (F * brdf.x + brdf.y); 134 | 135 | vec3 ambient = (kD * diffuse + specular) * ao; 136 | 137 | f_color = vec4(tonemap(ambient), 1.0); 138 | } 139 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Bare metal OpenGL 4.5+ wrapper 2 | //! 3 | //! ## Overview 4 | //! 5 | //! `grr` aims at providing a modern and clean looking API with focus on 6 | //! direct state access. The terminology used follows mostly Vulkan and OpenGL. 7 | //! 8 | //! ## Initialization 9 | //! 10 | //! The main entry point for working with the library is a [`Device`](struct.Device.html). 11 | //! A device wraps an OpenGL context which needs to be created and managed externally. 12 | //! The documentation and examples will all use `glutin` for window and context management. 13 | //! 14 | //! ```no_run 15 | //! extern crate glutin; 16 | //! extern crate grr; 17 | //! 18 | //! fn main() -> Result<(), Box> { 19 | //! let mut events_loop = glutin::event_loop::EventLoop::new(); 20 | //! let wb = glutin::window::WindowBuilder::new() 21 | //! .with_title("Hello grr!") 22 | //! .with_inner_size(glutin::dpi::LogicalSize::new(1024.0, 768.0)); 23 | //! let window = unsafe { 24 | //! glutin::ContextBuilder::new() 25 | //! .with_vsync(true) 26 | //! .with_srgb(true) 27 | //! .with_gl_debug_flag(true) 28 | //! .build_windowed(wb, &events_loop)? 29 | //! .make_current() 30 | //! .unwrap() 31 | //! }; 32 | //! 33 | //! let grr = unsafe { 34 | //! grr::Device::new( 35 | //! |symbol| window.get_proc_address(symbol) as *const _, 36 | //! grr::Debug::Enable { 37 | //! callback: |_, _, _, _, msg| { 38 | //! println!("{:?}", msg); 39 | //! }, 40 | //! flags: grr::DebugReport::all(), 41 | //! }, 42 | //! ) 43 | //! }; 44 | //! 45 | //! Ok(()) 46 | //! } 47 | //! ``` 48 | //! 49 | //! # Modules 50 | //! 51 | //! The API has multiple concepts which interplay with each other. The main of interacting with the library is via 52 | //! function calls on a [`Device`](struct.Device.html) object. The calls often translate directly to one GL call. 53 | //! All other objects created are opaque handles! 54 | //! 55 | //! * **Resource**: Objects with associated memory. Can be a [`Buffer`](struct.Buffer.html) (untyped) or an [`Image`](struct.Image.html). 56 | //! * **Pipeline**: There currently are two sort of pipelines supported: 57 | //! [*Graphics*](struct.Device.html#method.create_graphics_pipeline) and 58 | //! [*Compute*](struct.Device.html#method.create_compute_pipeline) 59 | //! * **Framebuffer**: Assembles the attachments ([`ImageView`](struct.ImageView.html) or [`RenderBuffer`](struct.RenderBuffer.html)) for draw calls 60 | //! * **Sampler**: Configures image filtering. An [`Image`](struct.Image.html) is bound together with a [`Sampler`](struct.Sampler.html) to a texture 61 | //! unit for access in a shader stage. 62 | //! * **Vertex Array**: Specifies the vertex attributes and bindings for the input assembler stage. Buffers are bound to a [`VertexArray`](struct.VertexArray.html) 63 | //! to declare the memory region to fetch attribute data from. 64 | 65 | #![allow(clippy::missing_safety_doc)] 66 | 67 | #[macro_use] 68 | extern crate bitflags; 69 | 70 | mod __gl; 71 | 72 | mod buffer; 73 | mod command; 74 | mod debug; 75 | mod device; 76 | mod error; 77 | mod format; 78 | mod framebuffer; 79 | mod image; 80 | mod pipeline; 81 | mod query; 82 | mod sampler; 83 | mod sync; 84 | mod transfer; 85 | mod vertex; 86 | 87 | pub use crate::{ 88 | buffer::*, command::*, debug::*, device::*, error::*, format::*, framebuffer::*, image::*, 89 | pipeline::*, query::*, sampler::*, sync::*, transfer::*, vertex::*, 90 | }; 91 | 92 | pub const WHOLE_SIZE: u64 = !0; 93 | 94 | /// 95 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 96 | pub struct Region { 97 | pub x: i32, 98 | pub y: i32, 99 | /// Width 100 | pub w: i32, 101 | /// Height 102 | pub h: i32, 103 | } 104 | 105 | /// Starting location for copying from or to texture data. 106 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 107 | pub struct Offset { 108 | pub x: i32, 109 | pub y: i32, 110 | pub z: i32, 111 | } 112 | 113 | impl Offset { 114 | pub const ORIGIN: Offset = Offset { x: 0, y: 0, z: 0 }; 115 | } 116 | 117 | /// 118 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 119 | pub struct Extent { 120 | pub width: u32, 121 | pub height: u32, 122 | pub depth: u32, 123 | } 124 | 125 | /// Comparison operator. 126 | /// 127 | /// Used in depth test, stencil test and sampling depth textures. 128 | #[repr(u32)] 129 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 130 | pub enum Compare { 131 | Less = __gl::LESS, 132 | LessEqual = __gl::LEQUAL, 133 | Greater = __gl::GREATER, 134 | GreaterEqual = __gl::GEQUAL, 135 | Equal = __gl::EQUAL, 136 | NotEqual = __gl::NOTEQUAL, 137 | Always = __gl::ALWAYS, 138 | Never = __gl::NEVER, 139 | } 140 | 141 | /// View a slice as raw byte slice. 142 | /// 143 | /// Reinterprets the passed data as raw memory. 144 | /// Be aware of possible packing and aligning rules by Rust compared to OpenGL. 145 | pub fn as_u8_slice(data: &[T]) -> &[u8] { 146 | let len = std::mem::size_of::() * data.len(); 147 | unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, len) } 148 | } 149 | -------------------------------------------------------------------------------- /src/debug.rs: -------------------------------------------------------------------------------- 1 | use crate::__gl; 2 | use crate::__gl::types::{GLenum, GLuint}; 3 | use crate::device::Device; 4 | 5 | /// Message filter. 6 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 7 | pub enum MsgFilter { 8 | /// Referencing all values of the type `T`. 9 | All, 10 | /// 11 | Some(T), 12 | } 13 | 14 | impl MsgFilter { 15 | fn as_gl(self) -> GLenum { 16 | match self { 17 | MsgFilter::All => __gl::DONT_CARE, 18 | MsgFilter::Some(v) => v as _, 19 | } 20 | } 21 | } 22 | 23 | impl MsgFilter { 24 | fn as_gl(self) -> GLenum { 25 | match self { 26 | MsgFilter::All => __gl::DONT_CARE, 27 | MsgFilter::Some(v) => v as _, 28 | } 29 | } 30 | } 31 | 32 | bitflags!( 33 | /// Debug report flags. 34 | /// 35 | /// Denotes which events will trigger a debug report. 36 | pub struct DebugReport: GLenum { 37 | const NOTIFICATION = __gl::DEBUG_SEVERITY_NOTIFICATION; 38 | const WARNING = __gl::DEBUG_SEVERITY_MEDIUM; 39 | const ERROR = __gl::DEBUG_SEVERITY_HIGH; 40 | const PERFORMANCE_WARNING = __gl::DEBUG_SEVERITY_LOW; 41 | const FULL = Self::NOTIFICATION.bits | Self::WARNING.bits | Self::ERROR.bits | Self::PERFORMANCE_WARNING.bits; 42 | } 43 | ); 44 | 45 | /// Debug message source. 46 | #[repr(u32)] 47 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 48 | pub enum DebugSource { 49 | Api = __gl::DEBUG_SOURCE_API, 50 | ShaderCompiler = __gl::DEBUG_SOURCE_SHADER_COMPILER, 51 | Wsi = __gl::DEBUG_SOURCE_WINDOW_SYSTEM, 52 | ThirdParty = __gl::DEBUG_SOURCE_THIRD_PARTY, 53 | Application = __gl::DEBUG_SOURCE_APPLICATION, 54 | Other = __gl::DEBUG_SOURCE_OTHER, 55 | } 56 | 57 | /// Debug message type. 58 | #[repr(u32)] 59 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 60 | pub enum DebugType { 61 | Error = __gl::DEBUG_TYPE_ERROR, 62 | Deprecated = __gl::DEBUG_TYPE_DEPRECATED_BEHAVIOR, 63 | UndefinedBehavior = __gl::DEBUG_TYPE_UNDEFINED_BEHAVIOR, 64 | Performance = __gl::DEBUG_TYPE_PERFORMANCE, 65 | Portability = __gl::DEBUG_TYPE_PORTABILITY, 66 | Marker = __gl::DEBUG_TYPE_MARKER, 67 | PushGroup = __gl::DEBUG_TYPE_PUSH_GROUP, 68 | PopGroup = __gl::DEBUG_TYPE_POP_GROUP, 69 | Other = __gl::DEBUG_TYPE_OTHER, 70 | } 71 | 72 | /// 73 | pub type DebugCallback = fn(DebugReport, DebugSource, DebugType, u32, &str); 74 | 75 | /// 76 | #[repr(u32)] 77 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 78 | pub enum ObjectType { 79 | Buffer = __gl::BUFFER, 80 | Shader = __gl::SHADER, 81 | Image = __gl::TEXTURE, 82 | VertexArray = __gl::VERTEX_ARRAY, 83 | Pipeline = __gl::PROGRAM, 84 | Framebuffer = __gl::FRAMEBUFFER, 85 | Renderbuffer = __gl::RENDERBUFFER, 86 | Sampler = __gl::SAMPLER, 87 | } 88 | 89 | pub trait Object: Copy { 90 | const TYPE: ObjectType; 91 | 92 | fn handle(&self) -> GLuint; 93 | } 94 | 95 | pub(crate) unsafe fn set_debug_message_control( 96 | ctxt: &__gl::Gl, 97 | enable: bool, 98 | src: MsgFilter, 99 | ty: MsgFilter, 100 | flags: DebugReport, 101 | ids: Option<&[u32]>, 102 | ) { 103 | let src = src.as_gl(); 104 | let ty = ty.as_gl(); 105 | let num_ids = match ids { 106 | Some(ids) => ids.len() as i32, 107 | None => 0, 108 | }; 109 | let id_ptr = match ids { 110 | Some(ids) => ids.as_ptr(), 111 | None => std::ptr::null(), 112 | }; 113 | let enable = if enable { __gl::TRUE } else { __gl::FALSE }; 114 | 115 | if flags.contains(DebugReport::NOTIFICATION) { 116 | ctxt.DebugMessageControl( 117 | src, 118 | ty, 119 | DebugReport::NOTIFICATION.bits(), 120 | num_ids, 121 | id_ptr, 122 | enable, 123 | ); 124 | } 125 | if flags.contains(DebugReport::WARNING) { 126 | ctxt.DebugMessageControl( 127 | src, 128 | ty, 129 | DebugReport::WARNING.bits(), 130 | num_ids, 131 | id_ptr, 132 | enable, 133 | ); 134 | } 135 | if flags.contains(DebugReport::ERROR) { 136 | ctxt.DebugMessageControl(src, ty, DebugReport::ERROR.bits(), num_ids, id_ptr, enable); 137 | } 138 | if flags.contains(DebugReport::PERFORMANCE_WARNING) { 139 | ctxt.DebugMessageControl( 140 | src, 141 | ty, 142 | DebugReport::PERFORMANCE_WARNING.bits(), 143 | num_ids, 144 | id_ptr, 145 | enable, 146 | ); 147 | } 148 | } 149 | 150 | impl Device { 151 | /// Associate a name with an object. 152 | pub unsafe fn object_name(&self, object: T, name: &str) { 153 | let label = name.as_bytes(); 154 | self.0.ObjectLabel( 155 | T::TYPE as _, 156 | object.handle(), 157 | label.len() as _, 158 | label.as_ptr() as *const _, 159 | ); 160 | } 161 | 162 | pub unsafe fn enable_debug_message( 163 | &self, 164 | src: MsgFilter, 165 | ty: MsgFilter, 166 | flags: DebugReport, 167 | ids: Option<&[u32]>, 168 | ) { 169 | set_debug_message_control(&self.0, true, src, ty, flags, ids); 170 | } 171 | 172 | pub unsafe fn disable_debug_message( 173 | &self, 174 | src: MsgFilter, 175 | ty: MsgFilter, 176 | flags: DebugReport, 177 | ids: Option<&[u32]>, 178 | ) { 179 | set_debug_message_control(&self.0, false, src, ty, flags, ids); 180 | } 181 | 182 | pub unsafe fn begin_debug_marker(&self, src: DebugSource, id: u32, msg: &str) { 183 | self.0 184 | .PushDebugGroup(src as _, id, msg.len() as _, msg.as_ptr() as *const _); 185 | } 186 | 187 | pub unsafe fn end_debug_marker(&self) { 188 | self.0.PopDebugGroup(); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /examples/triangle.rs: -------------------------------------------------------------------------------- 1 | use raw_gl_context::{GlConfig, GlContext, Profile}; 2 | use winit::{ 3 | dpi::LogicalSize, 4 | event::{Event, WindowEvent}, 5 | event_loop::{ControlFlow, EventLoop}, 6 | window::WindowBuilder, 7 | }; 8 | 9 | const VERTEX_SRC: &str = r#" 10 | #version 450 core 11 | layout (location = 0) in vec2 v_pos; 12 | layout (location = 1) in vec3 v_color; 13 | 14 | layout (location = 0) out vec3 a_color; 15 | 16 | void main() { 17 | a_color = v_color; 18 | gl_Position = vec4(v_pos, 0.0, 1.0); 19 | } 20 | "#; 21 | 22 | const FRAGMENT_SRC: &str = r#" 23 | #version 450 core 24 | layout (location = 0) in vec3 a_color; 25 | out vec4 f_color; 26 | 27 | void main() { 28 | f_color = vec4(a_color, 1.0); 29 | } 30 | "#; 31 | 32 | const VERTICES: [f32; 15] = [ 33 | -0.5, -0.5, 1.0, 0.0, 0.0, 0.5, -0.5, 0.0, 1.0, 0.0, 0.0, 0.5, 0.0, 0.0, 1.0, 34 | ]; 35 | 36 | fn main() -> anyhow::Result<()> { 37 | unsafe { 38 | let event_loop = EventLoop::new(); 39 | 40 | let window = WindowBuilder::new() 41 | .with_title("grr :: triangle") 42 | .with_inner_size(LogicalSize::new(1024.0, 768.0)) 43 | .build(&event_loop)?; 44 | 45 | let context = GlContext::create( 46 | &window, 47 | GlConfig { 48 | version: (4, 5), 49 | profile: Profile::Core, 50 | red_bits: 8, 51 | blue_bits: 8, 52 | green_bits: 8, 53 | alpha_bits: 0, 54 | depth_bits: 0, 55 | stencil_bits: 0, 56 | samples: None, 57 | srgb: true, 58 | double_buffer: true, 59 | vsync: true, 60 | }, 61 | ) 62 | .unwrap(); 63 | 64 | context.make_current(); 65 | 66 | let grr = grr::Device::new( 67 | |symbol| context.get_proc_address(symbol) as *const _, 68 | grr::Debug::Enable { 69 | callback: |report, _, _, _, msg| { 70 | println!("{:?}: {:?}", report, msg); 71 | }, 72 | flags: grr::DebugReport::FULL, 73 | }, 74 | ); 75 | 76 | let vs = grr.create_shader( 77 | grr::ShaderStage::Vertex, 78 | grr::ShaderSource::Glsl, 79 | VERTEX_SRC.as_bytes(), 80 | grr::ShaderFlags::VERBOSE, 81 | )?; 82 | let fs = grr.create_shader( 83 | grr::ShaderStage::Fragment, 84 | grr::ShaderSource::Glsl, 85 | FRAGMENT_SRC.as_bytes(), 86 | grr::ShaderFlags::VERBOSE, 87 | )?; 88 | 89 | let pipeline = grr.create_graphics_pipeline( 90 | grr::VertexPipelineDesc { 91 | vertex_shader: vs, 92 | tessellation_control_shader: None, 93 | tessellation_evaluation_shader: None, 94 | geometry_shader: None, 95 | fragment_shader: Some(fs), 96 | }, 97 | grr::PipelineFlags::VERBOSE, 98 | )?; 99 | 100 | let vertex_array = grr.create_vertex_array(&[ 101 | grr::VertexAttributeDesc { 102 | location: 0, 103 | binding: 0, 104 | format: grr::VertexFormat::Xy32Float, 105 | offset: 0, 106 | }, 107 | grr::VertexAttributeDesc { 108 | location: 1, 109 | binding: 0, 110 | format: grr::VertexFormat::Xyz32Float, 111 | offset: (2 * std::mem::size_of::()) as _, 112 | }, 113 | ])?; 114 | 115 | let triangle_data = 116 | grr.create_buffer_from_host(grr::as_u8_slice(&VERTICES), grr::MemoryFlags::empty())?; 117 | 118 | event_loop.run(move |event, _, control_flow| { 119 | *control_flow = ControlFlow::Wait; 120 | 121 | match event { 122 | Event::WindowEvent { 123 | event: WindowEvent::CloseRequested, 124 | .. 125 | } => *control_flow = ControlFlow::Exit, 126 | Event::LoopDestroyed => { 127 | grr.delete_shaders(&[vs, fs]); 128 | grr.delete_pipeline(pipeline); 129 | grr.delete_buffer(triangle_data); 130 | grr.delete_vertex_array(vertex_array); 131 | } 132 | Event::RedrawRequested(_) => { 133 | let size = window.inner_size(); 134 | 135 | grr.bind_pipeline(pipeline); 136 | grr.bind_vertex_array(vertex_array); 137 | grr.bind_vertex_buffers( 138 | vertex_array, 139 | 0, 140 | &[grr::VertexBufferView { 141 | buffer: triangle_data, 142 | offset: 0, 143 | stride: (std::mem::size_of::() * 5) as _, 144 | input_rate: grr::InputRate::Vertex, 145 | }], 146 | ); 147 | 148 | grr.set_viewport( 149 | 0, 150 | &[grr::Viewport { 151 | x: 0.0, 152 | y: 0.0, 153 | w: size.width as _, 154 | h: size.height as _, 155 | n: 0.0, 156 | f: 1.0, 157 | }], 158 | ); 159 | grr.set_scissor( 160 | 0, 161 | &[grr::Region { 162 | x: 0, 163 | y: 0, 164 | w: size.width as _, 165 | h: size.height as _, 166 | }], 167 | ); 168 | 169 | grr.clear_attachment( 170 | grr::Framebuffer::DEFAULT, 171 | grr::ClearAttachment::ColorFloat(0, [0.5, 0.5, 0.5, 1.0]), 172 | ); 173 | grr.draw(grr::Primitive::Triangles, 0..3, 0..1); 174 | 175 | context.swap_buffers(); 176 | } 177 | _ => (), 178 | } 179 | }) 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/sampler.rs: -------------------------------------------------------------------------------- 1 | //! Sampler. 2 | 3 | use crate::__gl; 4 | use crate::__gl::types::{GLenum, GLuint}; 5 | 6 | use crate::debug::{Object, ObjectType}; 7 | use crate::device::Device; 8 | use crate::error::Result; 9 | use crate::Compare; 10 | 11 | use std::ops::Range; 12 | 13 | /// Sampler handle. 14 | #[repr(transparent)] 15 | #[derive(Clone, Copy)] 16 | pub struct Sampler(GLuint); 17 | 18 | impl Object for Sampler { 19 | const TYPE: ObjectType = ObjectType::Sampler; 20 | fn handle(&self) -> GLuint { 21 | self.0 22 | } 23 | } 24 | 25 | impl Device { 26 | /// Create a sampler object. 27 | pub unsafe fn create_sampler(&self, desc: SamplerDesc) -> Result { 28 | let mut sampler = 0; 29 | self.0.CreateSamplers(1, &mut sampler); 30 | self.get_error()?; 31 | 32 | // Texture min filter 33 | let min_filter = map_min_filter(desc.min_filter, desc.mip_map); 34 | self.0 35 | .SamplerParameteri(sampler, __gl::TEXTURE_MIN_FILTER, min_filter as _); 36 | 37 | // Texture mag filter 38 | let mag_filter = match desc.mag_filter { 39 | Filter::Nearest => __gl::NEAREST, 40 | Filter::Linear => __gl::LINEAR, 41 | }; 42 | self.0 43 | .SamplerParameteri(sampler, __gl::TEXTURE_MAG_FILTER, mag_filter as _); 44 | 45 | // Texture address wrap modes 46 | self.0 47 | .SamplerParameteri(sampler, __gl::TEXTURE_WRAP_S, desc.address.0 as _); 48 | self.0 49 | .SamplerParameteri(sampler, __gl::TEXTURE_WRAP_T, desc.address.1 as _); 50 | self.0 51 | .SamplerParameteri(sampler, __gl::TEXTURE_WRAP_R, desc.address.2 as _); 52 | 53 | // LOD bias 54 | self.0 55 | .SamplerParameterf(sampler, __gl::TEXTURE_LOD_BIAS, desc.lod_bias); 56 | // LOD range 57 | self.0 58 | .SamplerParameterf(sampler, __gl::TEXTURE_MIN_LOD, desc.lod.start); 59 | self.0 60 | .SamplerParameterf(sampler, __gl::TEXTURE_MAX_LOD, desc.lod.end); 61 | 62 | // Texture comparison mode 63 | let (compare_mode, compare_op): (GLenum, Option) = match desc.compare { 64 | Some(op) => (__gl::COMPARE_REF_TO_TEXTURE, Some(op as _)), 65 | None => (__gl::NONE, None), 66 | }; 67 | self.0 68 | .SamplerParameteri(sampler, __gl::TEXTURE_COMPARE_MODE, compare_mode as _); 69 | 70 | if let Some(op) = compare_op { 71 | self.0 72 | .SamplerParameteri(sampler, __gl::TEXTURE_COMPARE_FUNC, op as _); 73 | } 74 | 75 | // Border color 76 | self.0.SamplerParameterfv( 77 | sampler, 78 | __gl::TEXTURE_BORDER_COLOR, 79 | desc.border_color.as_ptr(), 80 | ); 81 | 82 | Ok(Sampler(sampler)) 83 | } 84 | 85 | /// Bind samplers to specific texture units. 86 | pub unsafe fn bind_samplers(&self, first: u32, samplers: &[Sampler]) { 87 | let samplers = samplers.iter().map(|s| s.0).collect::>(); 88 | self.0 89 | .BindSamplers(first, samplers.len() as _, samplers.as_ptr()); 90 | } 91 | 92 | // Delete a sampler. 93 | pub unsafe fn delete_sampler(&self, sampler: Sampler) { 94 | self.delete_samplers(&[sampler]) 95 | } 96 | 97 | /// Delete multiple samplers. 98 | pub unsafe fn delete_samplers(&self, samplers: &[Sampler]) { 99 | self.0.DeleteSamplers( 100 | samplers.len() as _, 101 | samplers.as_ptr() as *const _, // newtype 102 | ); 103 | } 104 | } 105 | 106 | /// Sampler Descriptor. 107 | #[derive(Debug, Clone)] 108 | pub struct SamplerDesc { 109 | pub min_filter: Filter, 110 | pub mag_filter: Filter, 111 | pub mip_map: Option, 112 | pub address: (SamplerAddress, SamplerAddress, SamplerAddress), 113 | pub lod_bias: f32, 114 | pub lod: Range, 115 | pub compare: Option, 116 | pub border_color: [f32; 4], 117 | } 118 | 119 | impl Default for SamplerDesc { 120 | fn default() -> SamplerDesc { 121 | SamplerDesc { 122 | min_filter: Filter::Nearest, 123 | mag_filter: Filter::Linear, 124 | mip_map: Some(Filter::Linear), 125 | address: ( 126 | SamplerAddress::Repeat, 127 | SamplerAddress::Repeat, 128 | SamplerAddress::Repeat, 129 | ), 130 | lod_bias: 0.0, 131 | lod: -1000.0..1000.0, 132 | compare: None, 133 | border_color: [0.0, 0.0, 0.0, 0.0], 134 | } 135 | } 136 | } 137 | 138 | /// Filter options for computing pixels when the texture maps to an 139 | /// area different from one texture element. 140 | #[repr(u32)] 141 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 142 | pub enum Filter { 143 | /// Use the closest texel, by Manhattan distance. 144 | Nearest = __gl::NEAREST, 145 | 146 | /// Use a weighted average of the four texels closest to the 147 | /// pixel. 148 | Linear = __gl::LINEAR, 149 | } 150 | 151 | fn map_min_filter(filter: Filter, mip_map: Option) -> GLenum { 152 | match (filter, mip_map) { 153 | (Filter::Nearest, None) => __gl::NEAREST, 154 | (Filter::Nearest, Some(Filter::Nearest)) => __gl::NEAREST_MIPMAP_NEAREST, 155 | (Filter::Nearest, Some(Filter::Linear)) => __gl::NEAREST_MIPMAP_LINEAR, 156 | (Filter::Linear, None) => __gl::LINEAR, 157 | (Filter::Linear, Some(Filter::Nearest)) => __gl::LINEAR_MIPMAP_NEAREST, 158 | (Filter::Linear, Some(Filter::Linear)) => __gl::LINEAR_MIPMAP_LINEAR, 159 | } 160 | } 161 | 162 | /// Sampler addressing mode. 163 | /// 164 | /// Specifies how coordinates outide of the texture coordinate system (`[0, 1]`) are treated during 165 | /// sampling operations. 166 | #[repr(u32)] 167 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 168 | pub enum SamplerAddress { 169 | /// Strip off the integer part of the coordinate, effectively 170 | /// repeating the texture across the entire space. 171 | Repeat = __gl::REPEAT, 172 | 173 | /// Strip of the integer part of the coordinate, but mirror the 174 | /// coordinate when the interger is odd. This will create repeated 175 | /// images that are alternately flipped. 176 | MirrorRepeat = __gl::MIRRORED_REPEAT, 177 | 178 | /// Clamp the coordinate to [0.0, 1.0]. 179 | ClampEdge = __gl::CLAMP_TO_EDGE, 180 | 181 | /// Use the border color for coordinates outside [0.0, 1.0]. 182 | ClampBorder = __gl::CLAMP_TO_BORDER, 183 | 184 | /// Coordinates in the range [-1.0, 0.0] will be mirrored onto 185 | /// [0.0, 1.0]. Outside of [-1.0, 1.0], behavior is the same as `ClampEdge`. 186 | MirrorClampEdge = __gl::MIRROR_CLAMP_TO_EDGE, 187 | } 188 | -------------------------------------------------------------------------------- /src/format.rs: -------------------------------------------------------------------------------- 1 | use crate::__gl; 2 | 3 | #[allow(non_camel_case_types)] 4 | #[repr(u32)] 5 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 6 | /// The `Format` enum represents a sized internal format of texture 7 | /// storage. The naming convention closely follows that of Vulkan, but 8 | /// it has a close correspondence with OpenGL. 9 | /// 10 | /// * The '_UNORM' suffix denotes unsigned normalized formats. They are 11 | /// represented as unsigned integers internally, remapped to the [0.0, 12 | /// 1.0] floating point range in shaders. These are equivalent to the 13 | /// constants with no suffix in OpenGL. 14 | /// 15 | /// * The '_SNORM' suffix denotes signed normalized formats. They are 16 | /// represented as signed integers, remapped to the [-1.0, 1.0] 17 | /// floating point range in shaders. This suffix is the same as in 18 | /// OpenGL. 19 | /// 20 | /// * The '_SFLOAT' suffix denotes floating point formats, equivalent 21 | /// to the OpenGL 'F' suffix. 22 | /// 23 | /// * The '_SINT' suffix denotes signed integer formats, exposed to 24 | /// shaders unmodified as integers. This is equivalent to the OpenGL 25 | /// 'I' suffix. 26 | /// 27 | /// * The '_UINT' suffix denotes unsigned integer formats, exposed to 28 | /// shaders as unsigned integers. This is equivalent to the OpenGL 29 | /// 'UI' suffix. 30 | /// 31 | /// * The '_SRGB' suffix denotes sRGB formats, which are all unsigned 32 | /// normalized integers. Textures in this format are assumed to be in 33 | /// the sRGB color space. Shaders reading from this format will 34 | /// automatically convert the color components to a linear color 35 | /// space, so the shader will only see linear values. Because 36 | /// `GL_FRAMEBUFFER_SRGB` is enabled by default in `grr`, when 37 | /// outputting from a shader to a render target with an '_SRGB' 38 | /// format, OpenGL will convert the color components to an sRGB color 39 | /// space automatically. Alpha components, if they exist, are treated 40 | /// as linear throughout. 41 | /// 42 | /// Each component is followed by the number of bits used to represent 43 | /// it. 44 | pub enum Format { 45 | // unsigned normalized integer formats 46 | R8_UNORM = __gl::R8, 47 | R8G8_UNORM = __gl::RG8, 48 | R8G8B8_UNORM = __gl::RGB8, 49 | R8G8B8A8_UNORM = __gl::RGBA8, 50 | 51 | R16_UNORM = __gl::R16, 52 | R16G16_UNORM = __gl::RG16, 53 | R16G16B16_UNORM = __gl::RGB16, 54 | R16G16B16A16_UNORM = __gl::RGBA16, 55 | 56 | // signed normalized integer formats 57 | R8_SNORM = __gl::R8_SNORM, 58 | R8G8_SNORM = __gl::RG8_SNORM, 59 | R8G8B8_SNORM = __gl::RGB8_SNORM, 60 | R8G8B8A8_SNORM = __gl::RGBA8_SNORM, 61 | 62 | R16_SNORM = __gl::R16_SNORM, 63 | R16G16_SNORM = __gl::RG16_SNORM, 64 | R16G16B16_SNORM = __gl::RGB16_SNORM, 65 | R16G16B16A16_SNORM = __gl::RGBA16_SNORM, 66 | 67 | // floating point formats 68 | R16_SFLOAT = __gl::R16F, 69 | R16G16_SFLOAT = __gl::RG16F, 70 | R16G16B16_SFLOAT = __gl::RGB16F, 71 | R16G16B16A16_SFLOAT = __gl::RGBA16F, 72 | 73 | R32_SFLOAT = __gl::R32F, 74 | R32G32_SFLOAT = __gl::RG32F, 75 | R32G32B32_SFLOAT = __gl::RGB32F, 76 | R32G32B32A32_SFLOAT = __gl::RGBA32F, 77 | 78 | // signed integer formats 79 | R8_SINT = __gl::R8I, 80 | R8G8_SINT = __gl::RG8I, 81 | R8G8B8_SINT = __gl::RGB8I, 82 | R8G8B8A8_SINT = __gl::RGBA8I, 83 | 84 | R16_SINT = __gl::R16I, 85 | R16G16_SINT = __gl::RG16I, 86 | R16G16B16_SINT = __gl::RGB16I, 87 | R16G16B16A16_SINT = __gl::RGBA16I, 88 | 89 | R32_SINT = __gl::R32I, 90 | R32G32_SINT = __gl::RG32I, 91 | R32G32B32_SINT = __gl::RGB32I, 92 | R32G32B32A32_SINT = __gl::RGBA32I, 93 | 94 | // unsigned integer formats 95 | R8_UINT = __gl::R8UI, 96 | R8G8_UINT = __gl::RG8UI, 97 | R8G8B8_UINT = __gl::RGB8UI, 98 | R8G8B8A8_UINT = __gl::RGBA8UI, 99 | 100 | R16_UINT = __gl::R16UI, 101 | R16G16_UINT = __gl::RG16UI, 102 | R16G16B16_UINT = __gl::RGB16UI, 103 | R16G16B16A16_UINT = __gl::RGBA16UI, 104 | 105 | R32_UINT = __gl::R32UI, 106 | R32G32_UINT = __gl::RG32UI, 107 | R32G32B32_UINT = __gl::RGB32UI, 108 | R32G32B32A32_UINT = __gl::RGBA32UI, 109 | 110 | // sRGB normalized integer formats. 111 | R8G8B8_SRGB = __gl::SRGB8, 112 | 113 | /// sRGB8 color space with a linear alpha 114 | R8G8B8A8_SRGB = __gl::SRGB8_ALPHA8, 115 | 116 | // depth and stencil formats 117 | D16_UNORM = __gl::DEPTH_COMPONENT16, 118 | D24_UNORM = __gl::DEPTH_COMPONENT24, 119 | D32_UNORM = __gl::DEPTH_COMPONENT32, 120 | D32_SFLOAT = __gl::DEPTH_COMPONENT32F, 121 | 122 | S8_UINT = __gl::STENCIL_INDEX8, 123 | 124 | D24_UNORM_S8_UINT = __gl::DEPTH24_STENCIL8, 125 | D32_SFLOAT_S8_UINT = __gl::DEPTH32F_STENCIL8, 126 | } 127 | 128 | impl Format { 129 | /// Return the number of components of the pixel format. 130 | pub fn num_components(self) -> u32 { 131 | self.base_format().num_components() 132 | } 133 | 134 | /// Return the corresponding base format for this format. 135 | pub fn base_format(self) -> BaseFormat { 136 | use Format::*; 137 | match self { 138 | R8_UNORM | R16_UNORM | R8_SNORM | R16_SNORM | R8_SINT | R16_SINT | R32_SINT 139 | | R8_UINT | R16_UINT | R32_UINT | R16_SFLOAT | R32_SFLOAT => BaseFormat::R, 140 | 141 | R8G8_UNORM | R16G16_UNORM | R8G8_SNORM | R16G16_SNORM | R8G8_SINT | R16G16_SINT 142 | | R32G32_SINT | R8G8_UINT | R16G16_UINT | R32G32_UINT | R16G16_SFLOAT 143 | | R32G32_SFLOAT => BaseFormat::RG, 144 | 145 | R8G8B8_UNORM | R16G16B16_UNORM | R8G8B8_SNORM | R16G16B16_SNORM | R8G8B8_SINT 146 | | R16G16B16_SINT | R32G32B32_SINT | R8G8B8_UINT | R16G16B16_UINT | R32G32B32_UINT 147 | | R16G16B16_SFLOAT | R32G32B32_SFLOAT | R8G8B8_SRGB => BaseFormat::RGB, 148 | 149 | R8G8B8A8_UNORM | R16G16B16A16_UNORM | R8G8B8A8_SNORM | R16G16B16A16_SNORM 150 | | R8G8B8A8_SINT | R16G16B16A16_SINT | R32G32B32A32_SINT | R8G8B8A8_UINT 151 | | R16G16B16A16_UINT | R32G32B32A32_UINT | R16G16B16A16_SFLOAT | R32G32B32A32_SFLOAT 152 | | R8G8B8A8_SRGB => BaseFormat::RGBA, 153 | 154 | D32_SFLOAT | D16_UNORM | D24_UNORM | D32_UNORM => BaseFormat::Depth, 155 | 156 | S8_UINT => BaseFormat::Stencil, 157 | 158 | D32_SFLOAT_S8_UINT | D24_UNORM_S8_UINT => BaseFormat::DepthStencil, 159 | } 160 | } 161 | } 162 | 163 | #[allow(non_camel_case_types)] 164 | #[repr(u32)] 165 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 166 | pub enum BaseFormat { 167 | R = __gl::RED, 168 | RG = __gl::RG, 169 | RGB = __gl::RGB, 170 | RGBA = __gl::RGBA, 171 | Depth = __gl::DEPTH_COMPONENT, 172 | DepthStencil = __gl::DEPTH_STENCIL, 173 | Stencil = __gl::STENCIL_INDEX, 174 | } 175 | 176 | impl BaseFormat { 177 | /// Return the number of components that compose this format. 178 | pub fn num_components(self) -> u32 { 179 | use BaseFormat::*; 180 | match self { 181 | R => 1, 182 | RG => 2, 183 | RGB => 3, 184 | RGBA => 4, 185 | Depth => 1, 186 | DepthStencil => 2, 187 | Stencil => 1, 188 | } 189 | } 190 | } 191 | 192 | #[allow(non_camel_case_types)] 193 | #[repr(u32)] 194 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 195 | pub enum FormatLayout { 196 | U8 = __gl::UNSIGNED_BYTE, 197 | U16 = __gl::UNSIGNED_SHORT, 198 | U32 = __gl::UNSIGNED_INT, 199 | I8 = __gl::BYTE, 200 | I16 = __gl::SHORT, 201 | I32 = __gl::INT, 202 | F16 = __gl::HALF_FLOAT, 203 | F32 = __gl::FLOAT, 204 | U24U8 = __gl::UNSIGNED_INT_24_8, 205 | F32U8 = __gl::FLOAT_32_UNSIGNED_INT_24_8_REV, 206 | // TODO 207 | } 208 | -------------------------------------------------------------------------------- /src/device.rs: -------------------------------------------------------------------------------- 1 | use crate::__gl; 2 | use crate::__gl::types::{GLchar, GLenum, GLsizei, GLuint}; 3 | 4 | use std::os::raw::c_void; 5 | use std::{ffi, mem}; 6 | 7 | use crate::debug::{self, DebugCallback, DebugReport}; 8 | 9 | /// Logical device, representation one or multiple physical devices (hardware or software). 10 | /// 11 | /// This wraps an existing GL context and acts as the main API interface. 12 | /// It's the responsibility of the user to keep the context alive. 13 | pub struct Device(pub(crate) __gl::Gl, Option>); 14 | 15 | /// Device debug control. 16 | pub enum Debug { 17 | Enable { callback: F, flags: DebugReport }, 18 | Disable, 19 | } 20 | 21 | impl Device { 22 | /// Create a new device from an existing context. 23 | /// 24 | /// The context must be initialized with GL 4.5+ core profile. 25 | /// The passed `loader` is used to obtain the function pointers from the context. 26 | pub unsafe fn new(loader: F, debug: Debug) -> Self 27 | where 28 | F: FnMut(&str) -> *const c_void, 29 | { 30 | let ctxt = __gl::Gl::load_with(loader); 31 | 32 | let cb = match debug { 33 | Debug::Enable { callback, flags } => { 34 | extern "system" fn callback_ffi( 35 | source: GLenum, 36 | gltype: GLenum, 37 | id: GLuint, 38 | severity: GLenum, 39 | _length: GLsizei, 40 | message: *const GLchar, 41 | user_param: *mut c_void, 42 | ) { 43 | unsafe { 44 | let cb = Box::from_raw(user_param as *mut DebugCallback); 45 | let msg = ffi::CStr::from_ptr(message).to_str().unwrap(); 46 | cb( 47 | mem::transmute(severity), 48 | mem::transmute(source), 49 | mem::transmute(gltype), 50 | id, 51 | msg, 52 | ); 53 | Box::into_raw(cb); 54 | } 55 | } 56 | 57 | let cb = Box::new(callback); 58 | let cb_raw = Box::into_raw(cb); 59 | ctxt.Enable(__gl::DEBUG_OUTPUT); 60 | ctxt.DebugMessageCallback(Some(callback_ffi), cb_raw as *mut _); 61 | ctxt.DebugMessageControl( 62 | __gl::DONT_CARE, 63 | __gl::DONT_CARE, 64 | __gl::DONT_CARE, 65 | 0, 66 | std::ptr::null(), 67 | __gl::FALSE, 68 | ); 69 | debug::set_debug_message_control( 70 | &ctxt, 71 | true, 72 | debug::MsgFilter::All, 73 | debug::MsgFilter::All, 74 | flags, 75 | None, 76 | ); 77 | Some(Box::from_raw(cb_raw)) 78 | } 79 | Debug::Disable => { 80 | ctxt.Disable(__gl::DEBUG_OUTPUT); 81 | None 82 | } 83 | }; 84 | 85 | // Enforce sRGB frmaebuffer handling 86 | ctxt.Enable(__gl::FRAMEBUFFER_SRGB); 87 | // Enforce lower-left window coordinate system with [0; 1] depth range 88 | ctxt.ClipControl(__gl::LOWER_LEFT, __gl::ZERO_TO_ONE); 89 | // Always enable scissor testing 90 | ctxt.Enable(__gl::SCISSOR_TEST); 91 | ctxt.Enable(__gl::TEXTURE_CUBE_MAP_SEAMLESS); 92 | ctxt.Enable(__gl::PROGRAM_POINT_SIZE); 93 | ctxt.Enable(__gl::SAMPLE_MASK); 94 | 95 | Device(ctxt, cb) 96 | } 97 | 98 | /// Return the underlying context for the device 99 | pub unsafe fn context(&self) -> &__gl::Gl { 100 | &self.0 101 | } 102 | 103 | pub unsafe fn limits(&self) -> DeviceLimits { 104 | DeviceLimits { 105 | max_compute_work_group_invocations: self 106 | .get_u32(__gl::MAX_COMPUTE_WORK_GROUP_INVOCATIONS, None), 107 | max_compute_work_group_count: [ 108 | self.get_u32(__gl::MAX_COMPUTE_WORK_GROUP_COUNT, Some(0)), 109 | self.get_u32(__gl::MAX_COMPUTE_WORK_GROUP_COUNT, Some(1)), 110 | self.get_u32(__gl::MAX_COMPUTE_WORK_GROUP_COUNT, Some(2)), 111 | ], 112 | max_compute_work_group_size: [ 113 | self.get_u32(__gl::MAX_COMPUTE_WORK_GROUP_SIZE, Some(0)), 114 | self.get_u32(__gl::MAX_COMPUTE_WORK_GROUP_SIZE, Some(1)), 115 | self.get_u32(__gl::MAX_COMPUTE_WORK_GROUP_SIZE, Some(2)), 116 | ], 117 | max_compute_shared_memory_size: self 118 | .get_u32(__gl::MAX_COMPUTE_SHARED_MEMORY_SIZE, None), 119 | max_clip_distances: self.get_u32(__gl::MAX_CLIP_DISTANCES, None), 120 | max_cull_distances: self.get_u32(__gl::MAX_CULL_DISTANCES, None), 121 | max_viewports: self.get_u32(__gl::MAX_VIEWPORTS, None), 122 | max_framebuffer_width: self.get_u32(__gl::MAX_FRAMEBUFFER_WIDTH, None), 123 | max_framebuffer_height: self.get_u32(__gl::MAX_FRAMEBUFFER_HEIGHT, None), 124 | max_framebuffer_layers: self.get_u32(__gl::MAX_FRAMEBUFFER_LAYERS, None), 125 | max_color_attachments: self.get_u32(__gl::MAX_COLOR_ATTACHMENTS, None), 126 | max_viewport_dimensions: [ 127 | self.get_u32(__gl::MAX_VIEWPORT_DIMS, Some(0)), 128 | self.get_u32(__gl::MAX_VIEWPORT_DIMS, Some(1)), 129 | ], 130 | max_vertex_input_attributes: self.get_u32(__gl::MAX_VERTEX_ATTRIBS, None), 131 | max_vertex_input_bindings: self.get_u32(__gl::MAX_VERTEX_ATTRIB_BINDINGS, None), 132 | max_vertex_input_attribute_offset: self 133 | .get_u32(__gl::MAX_VERTEX_ATTRIB_RELATIVE_OFFSET, None), 134 | max_vertex_input_binding_stride: self.get_u32(__gl::MAX_VERTEX_ATTRIB_STRIDE, None), 135 | max_vertex_output_components: self.get_u32(__gl::MAX_VERTEX_OUTPUT_COMPONENTS, None), 136 | } 137 | } 138 | 139 | pub unsafe fn features(&self) -> DeviceFeatures { 140 | DeviceFeatures {} 141 | } 142 | 143 | /// Submit all pending operations for device execution. 144 | /// 145 | /// This function may return before all operations have finished executing. 146 | pub unsafe fn submit(&self) { 147 | self.0.Flush(); 148 | } 149 | 150 | /// Wait on the host for execution of all outstanding device operations. 151 | pub unsafe fn wait_idle(&self) { 152 | self.0.Finish(); 153 | } 154 | 155 | unsafe fn get_u32(&self, target: GLenum, index: Option) -> u32 { 156 | self.get_i32(target, index) as _ 157 | } 158 | 159 | unsafe fn get_i32(&self, target: GLenum, index: Option) -> i32 { 160 | let mut value = 0; 161 | match index { 162 | Some(i) => self.0.GetIntegeri_v(target, i as _, &mut value), 163 | None => self.0.GetIntegerv(target, &mut value), 164 | } 165 | value 166 | } 167 | } 168 | 169 | #[derive(Clone, Debug)] 170 | pub struct DeviceLimits { 171 | /// Maximum number of total invocations in a single workgroup. 172 | pub max_compute_work_group_invocations: u32, 173 | /// Maximum number of local workgroups per dispatch call. 174 | pub max_compute_work_group_count: [u32; 3], 175 | /// Maximum size of a local workgroup in each dimensions. 176 | pub max_compute_work_group_size: [u32; 3], 177 | /// Maximum size in bytes of all shared memory variables in the compute pipeline. 178 | pub max_compute_shared_memory_size: u32, 179 | /// Maximum number of clip distances in a shader stage. 180 | /// 181 | /// Minimum value: 8 182 | pub max_clip_distances: u32, 183 | /// Maximum number of cull distances in a shader stage. 184 | /// 185 | /// Minimum value: 8 186 | pub max_cull_distances: u32, 187 | 188 | pub max_viewports: u32, 189 | 190 | pub max_viewport_dimensions: [u32; 2], 191 | 192 | pub max_framebuffer_width: u32, 193 | 194 | pub max_framebuffer_height: u32, 195 | 196 | pub max_framebuffer_layers: u32, 197 | 198 | pub max_color_attachments: u32, 199 | 200 | pub max_vertex_input_attributes: u32, 201 | 202 | pub max_vertex_input_bindings: u32, 203 | 204 | pub max_vertex_input_attribute_offset: u32, 205 | 206 | pub max_vertex_input_binding_stride: u32, 207 | 208 | pub max_vertex_output_components: u32, 209 | } 210 | 211 | /// Additional features supported by the device. 212 | /// 213 | /// These may correspond to OpenGL extensions or feature of higher OpenGL versions. 214 | #[derive(Clone, Debug)] 215 | pub struct DeviceFeatures {} 216 | -------------------------------------------------------------------------------- /src/framebuffer.rs: -------------------------------------------------------------------------------- 1 | //! Framebuffers 2 | 3 | use crate::__gl; 4 | use crate::__gl::types::{GLenum, GLuint}; 5 | 6 | use crate::debug::{Object, ObjectType}; 7 | use crate::device::Device; 8 | use crate::error::Result; 9 | use crate::{Format, ImageView, Region}; 10 | 11 | /// Attachment clearing description. 12 | pub enum ClearAttachment { 13 | ColorInt(usize, [i32; 4]), 14 | ColorUint(usize, [u32; 4]), 15 | ColorFloat(usize, [f32; 4]), 16 | Depth(f32), 17 | Stencil(i32), 18 | DepthStencil(f32, i32), 19 | } 20 | 21 | /// Attachment reference. 22 | #[derive(Copy, Clone, Debug)] 23 | pub enum Attachment { 24 | Color(usize), 25 | Depth, 26 | Stencil, 27 | DepthStencil, 28 | } 29 | 30 | impl Attachment { 31 | fn target(&self) -> GLenum { 32 | match *self { 33 | Attachment::Color(slot) => __gl::COLOR_ATTACHMENT0 + slot as u32, 34 | Attachment::Depth => __gl::DEPTH_ATTACHMENT, 35 | Attachment::Stencil => __gl::STENCIL_ATTACHMENT, 36 | Attachment::DepthStencil => __gl::DEPTH_STENCIL_ATTACHMENT, 37 | } 38 | } 39 | } 40 | 41 | /// 42 | pub enum AttachmentView { 43 | Image(ImageView), 44 | Renderbuffer(Renderbuffer), 45 | } 46 | 47 | /// Framebuffer handle. 48 | #[repr(transparent)] 49 | #[derive(Clone, Copy)] 50 | pub struct Framebuffer(pub(crate) GLuint); 51 | 52 | impl Framebuffer { 53 | /// Default framebuffer handle. 54 | /// 55 | /// Thie is the base framebuffer associated with the context. 56 | /// It also represents the internal swapchain for presentation. 57 | pub const DEFAULT: Self = Framebuffer(0); 58 | } 59 | 60 | impl Object for Framebuffer { 61 | const TYPE: ObjectType = ObjectType::Framebuffer; 62 | fn handle(&self) -> GLuint { 63 | self.0 64 | } 65 | } 66 | 67 | /// Renderbuffer handle. 68 | #[repr(transparent)] 69 | #[derive(Clone, Copy)] 70 | pub struct Renderbuffer(GLuint); 71 | 72 | impl Object for Renderbuffer { 73 | const TYPE: ObjectType = ObjectType::Renderbuffer; 74 | fn handle(&self) -> GLuint { 75 | self.0 76 | } 77 | } 78 | 79 | impl Device { 80 | /// Create a new framebuffer. 81 | pub unsafe fn create_framebuffer(&self) -> Result { 82 | let mut framebuffer = 0; 83 | self.0.CreateFramebuffers(1, &mut framebuffer); 84 | self.get_error()?; 85 | 86 | Ok(Framebuffer(framebuffer)) 87 | } 88 | 89 | /// Delete a framebuffer. 90 | pub unsafe fn delete_framebuffer(&self, framebuffer: Framebuffer) { 91 | self.delete_framebuffers(&[framebuffer]) 92 | } 93 | 94 | /// Delete multiple framebuffers. 95 | pub unsafe fn delete_framebuffers(&self, framebuffers: &[Framebuffer]) { 96 | self.0.DeleteFramebuffers( 97 | framebuffers.len() as _, 98 | framebuffers.as_ptr() as *const _, // newtype 99 | ); 100 | } 101 | 102 | /// Create a new framebuffer. 103 | pub unsafe fn create_renderbuffer( 104 | &self, 105 | format: Format, 106 | width: u32, 107 | height: u32, 108 | samples: u32, 109 | ) -> Result { 110 | let mut renderbuffer = 0; 111 | self.0.CreateRenderbuffers(1, &mut renderbuffer); 112 | self.get_error()?; 113 | 114 | if samples > 1 { 115 | self.0.NamedRenderbufferStorageMultisample( 116 | renderbuffer, 117 | samples as _, 118 | format as _, 119 | width as _, 120 | height as _, 121 | ); 122 | } else { 123 | self.0 124 | .NamedRenderbufferStorage(renderbuffer, format as _, width as _, height as _); 125 | } 126 | 127 | Ok(Renderbuffer(renderbuffer)) 128 | } 129 | 130 | /// Delete a renderbuffer. 131 | pub unsafe fn delete_renderbuffer(&self, renderbuffer: Renderbuffer) { 132 | self.delete_renderbuffers(&[renderbuffer]) 133 | } 134 | 135 | /// Delete multiple renderbuffers. 136 | pub unsafe fn delete_renderbuffers(&self, renderbuffers: &[Renderbuffer]) { 137 | self.0.DeleteRenderbuffers( 138 | renderbuffers.len() as _, 139 | renderbuffers.as_ptr() as *const _, // newtype 140 | ); 141 | } 142 | 143 | /// Clear framebuffer attachment. 144 | pub unsafe fn clear_attachment(&self, fb: Framebuffer, cv: ClearAttachment) { 145 | match cv { 146 | ClearAttachment::ColorInt(id, color) => { 147 | self.0 148 | .ClearNamedFramebufferiv(fb.0, __gl::COLOR, id as _, color.as_ptr()); 149 | } 150 | ClearAttachment::ColorUint(id, color) => { 151 | self.0 152 | .ClearNamedFramebufferuiv(fb.0, __gl::COLOR, id as _, color.as_ptr()); 153 | } 154 | ClearAttachment::ColorFloat(id, color) => { 155 | self.0 156 | .ClearNamedFramebufferfv(fb.0, __gl::COLOR, id as _, color.as_ptr()); 157 | } 158 | ClearAttachment::Depth(depth) => { 159 | self.0 160 | .ClearNamedFramebufferfv(fb.0, __gl::DEPTH, 0, &depth as *const _); 161 | } 162 | ClearAttachment::Stencil(stencil) => { 163 | self.0 164 | .ClearNamedFramebufferiv(fb.0, __gl::STENCIL, 0, &stencil as *const _); 165 | } 166 | ClearAttachment::DepthStencil(depth, stencil) => { 167 | self.0 168 | .ClearNamedFramebufferfi(fb.0, __gl::DEPTH_STENCIL, 0, depth, stencil); 169 | } 170 | } 171 | } 172 | 173 | /// 174 | pub unsafe fn invalidate_attachments( 175 | &self, 176 | framebuffer: Framebuffer, 177 | attachments: &[Attachment], 178 | region: Region, 179 | ) { 180 | let attachments = attachments 181 | .iter() 182 | .map(|att| att.target()) 183 | .collect::>(); 184 | 185 | self.0.InvalidateNamedFramebufferSubData( 186 | framebuffer.0, 187 | attachments.len() as _, 188 | attachments.as_ptr(), 189 | region.x, 190 | region.y, 191 | region.w, 192 | region.h, 193 | ) 194 | } 195 | 196 | /// Bind a framebuffer for draw and read commands. 197 | /// 198 | /// This will overwrite both (draw and read) binding points. 199 | pub unsafe fn bind_framebuffer(&self, framebuffer: Framebuffer) { 200 | self.0.BindFramebuffer(__gl::FRAMEBUFFER, framebuffer.0); 201 | } 202 | 203 | /// Bind a framebuffer for draw commands. 204 | pub unsafe fn bind_draw_framebuffer(&self, framebuffer: Framebuffer) { 205 | self.0 206 | .BindFramebuffer(__gl::DRAW_FRAMEBUFFER, framebuffer.0); 207 | } 208 | 209 | /// Bind a framebuffer for read commands. 210 | pub unsafe fn bind_read_framebuffer(&self, framebuffer: Framebuffer) { 211 | self.0 212 | .BindFramebuffer(__gl::READ_FRAMEBUFFER, framebuffer.0); 213 | } 214 | 215 | /// Bind attachments to the framebuffer. 216 | /// 217 | /// All previously bound attachments become invalid. 218 | pub unsafe fn bind_attachments( 219 | &self, 220 | framebuffer: Framebuffer, 221 | attachments: &[(Attachment, AttachmentView)], 222 | ) { 223 | assert_ne!( 224 | framebuffer.0, 0, 225 | "The default framebuffer can't be changed." 226 | ); 227 | 228 | for (attachment, view) in attachments { 229 | let target = attachment.target(); 230 | match *view { 231 | AttachmentView::Image(image) => { 232 | self.0 233 | .NamedFramebufferTexture(framebuffer.0, target, image.0, 0); 234 | } 235 | AttachmentView::Renderbuffer(renderbuffer) => { 236 | self.0.NamedFramebufferRenderbuffer( 237 | framebuffer.0, 238 | target, 239 | __gl::RENDERBUFFER, 240 | renderbuffer.0, 241 | ); 242 | } 243 | } 244 | } 245 | } 246 | 247 | /// Specify color attachments. 248 | /// 249 | /// Defines the color render targets for the next draw calls. 250 | /// This builds the link between fragment outputs in the fragment shader 251 | /// and attachments bound on the framebuffer. 252 | pub unsafe fn set_color_attachments(&self, framebuffer: Framebuffer, attachments: &[u32]) { 253 | assert_ne!( 254 | framebuffer.0, 0, 255 | "The default framebuffer can't be changed." 256 | ); 257 | 258 | let attachments = attachments 259 | .iter() 260 | .map(|i| i + __gl::COLOR_ATTACHMENT0) 261 | .collect::>(); 262 | self.0.NamedFramebufferDrawBuffers( 263 | framebuffer.0, 264 | attachments.len() as _, 265 | attachments.as_ptr(), 266 | ); 267 | } 268 | 269 | /// Specify read attachment. 270 | pub unsafe fn set_read_attachment(&self, framebuffer: Framebuffer, attachment: u32) { 271 | assert_ne!( 272 | framebuffer.0, 0, 273 | "The default framebuffer can't be changed." 274 | ); 275 | 276 | self.0 277 | .NamedFramebufferReadBuffer(framebuffer.0, __gl::COLOR_ATTACHMENT0 + attachment); 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /examples/texture.rs: -------------------------------------------------------------------------------- 1 | use raw_gl_context::{GlConfig, GlContext, Profile}; 2 | use winit::{ 3 | dpi::LogicalSize, 4 | event::{Event, WindowEvent}, 5 | event_loop::{ControlFlow, EventLoop}, 6 | window::WindowBuilder, 7 | }; 8 | 9 | use std::path::Path; 10 | 11 | const VERTEX_SRC: &str = r#" 12 | #version 450 core 13 | layout (location = 0) in vec2 v_pos; 14 | layout (location = 1) in vec2 v_uv; 15 | 16 | layout (location = 0) out vec2 a_uv; 17 | 18 | void main() { 19 | a_uv = v_uv; 20 | gl_Position = vec4(v_pos, 0.0, 1.0); 21 | } 22 | "#; 23 | 24 | const FRAGMENT_SRC: &str = r#" 25 | #version 450 core 26 | layout (location = 0) in vec2 a_uv; 27 | out vec4 f_color; 28 | 29 | layout (binding = 3) uniform sampler2D u_texture; 30 | 31 | void main() { 32 | f_color = texture(u_texture, a_uv); 33 | } 34 | "#; 35 | 36 | const VERTICES: [f32; 16] = [ 37 | -0.5, -0.5, 0.0, 1.0, // bottom-left 38 | 0.5, -0.5, 1.0, 1.0, // bottom-right 39 | 0.5, 0.5, 1.0, 0.0, // top-right 40 | -0.5, 0.5, 0.0, 0.0, // top-left 41 | ]; 42 | 43 | const INDICES: [u16; 6] = [0, 1, 2, 2, 3, 0]; 44 | 45 | fn main() -> anyhow::Result<()> { 46 | unsafe { 47 | let event_loop = EventLoop::new(); 48 | 49 | let window = WindowBuilder::new() 50 | .with_title("grr :: texture") 51 | .with_inner_size(LogicalSize::new(1024.0, 768.0)) 52 | .build(&event_loop)?; 53 | 54 | let context = GlContext::create( 55 | &window, 56 | GlConfig { 57 | version: (4, 5), 58 | profile: Profile::Core, 59 | red_bits: 8, 60 | blue_bits: 8, 61 | green_bits: 8, 62 | alpha_bits: 0, 63 | depth_bits: 0, 64 | stencil_bits: 0, 65 | samples: None, 66 | srgb: true, 67 | double_buffer: true, 68 | vsync: true, 69 | }, 70 | ) 71 | .unwrap(); 72 | 73 | context.make_current(); 74 | 75 | let grr = grr::Device::new( 76 | |symbol| context.get_proc_address(symbol) as *const _, 77 | grr::Debug::Enable { 78 | callback: |_, _, _, _, msg| { 79 | println!("{:?}", msg); 80 | }, 81 | flags: grr::DebugReport::FULL, 82 | }, 83 | ); 84 | 85 | let vs = grr.create_shader( 86 | grr::ShaderStage::Vertex, 87 | grr::ShaderSource::Glsl, 88 | VERTEX_SRC.as_bytes(), 89 | grr::ShaderFlags::VERBOSE, 90 | )?; 91 | let fs = grr.create_shader( 92 | grr::ShaderStage::Fragment, 93 | grr::ShaderSource::Glsl, 94 | FRAGMENT_SRC.as_bytes(), 95 | grr::ShaderFlags::VERBOSE, 96 | )?; 97 | 98 | let pipeline = grr.create_graphics_pipeline( 99 | grr::VertexPipelineDesc { 100 | vertex_shader: vs, 101 | tessellation_control_shader: None, 102 | tessellation_evaluation_shader: None, 103 | geometry_shader: None, 104 | fragment_shader: Some(fs), 105 | }, 106 | grr::PipelineFlags::VERBOSE, 107 | )?; 108 | 109 | let vertex_array = grr.create_vertex_array(&[ 110 | grr::VertexAttributeDesc { 111 | location: 0, 112 | binding: 0, 113 | format: grr::VertexFormat::Xy32Float, 114 | offset: 0, 115 | }, 116 | grr::VertexAttributeDesc { 117 | location: 1, 118 | binding: 0, 119 | format: grr::VertexFormat::Xy32Float, 120 | offset: (2 * std::mem::size_of::()) as _, 121 | }, 122 | ])?; 123 | 124 | let vertex_buffer = 125 | grr.create_buffer_from_host(grr::as_u8_slice(&VERTICES), grr::MemoryFlags::empty())?; 126 | let index_buffer = 127 | grr.create_buffer_from_host(grr::as_u8_slice(&INDICES), grr::MemoryFlags::empty())?; 128 | 129 | let img = image::open(&Path::new("info/grr_logo.png")) 130 | .unwrap() 131 | .to_rgba(); 132 | let img_width = img.width(); 133 | let img_height = img.height(); 134 | let img_data = img.into_raw(); 135 | 136 | let (texture, texture_view) = grr.create_image_and_view( 137 | grr::ImageType::D2 { 138 | width: img_width, 139 | height: img_height, 140 | layers: 1, 141 | samples: 1, 142 | }, 143 | grr::Format::R8G8B8A8_SRGB, 144 | 1, 145 | )?; 146 | grr.object_name(texture, "grr logo"); 147 | 148 | grr.copy_host_to_image( 149 | &img_data, 150 | texture, 151 | grr::HostImageCopy { 152 | host_layout: grr::MemoryLayout { 153 | base_format: grr::BaseFormat::RGBA, 154 | format_layout: grr::FormatLayout::U8, 155 | row_length: img_width, 156 | image_height: img_height, 157 | alignment: 4, 158 | }, 159 | image_subresource: grr::SubresourceLayers { 160 | level: 0, 161 | layers: 0..1, 162 | }, 163 | image_offset: grr::Offset { x: 0, y: 0, z: 0 }, 164 | image_extent: grr::Extent { 165 | width: img_width, 166 | height: img_height, 167 | depth: 1, 168 | }, 169 | }, 170 | ); 171 | 172 | let sampler = grr.create_sampler(grr::SamplerDesc { 173 | min_filter: grr::Filter::Linear, 174 | mag_filter: grr::Filter::Linear, 175 | mip_map: None, 176 | address: ( 177 | grr::SamplerAddress::ClampEdge, 178 | grr::SamplerAddress::ClampEdge, 179 | grr::SamplerAddress::ClampEdge, 180 | ), 181 | lod_bias: 0.0, 182 | lod: 0.0..10.0, 183 | compare: None, 184 | border_color: [0.0, 0.0, 0.0, 1.0], 185 | })?; 186 | 187 | let color_blend = grr::ColorBlend { 188 | attachments: vec![grr::ColorBlendAttachment { 189 | blend_enable: true, 190 | color: grr::BlendChannel { 191 | src_factor: grr::BlendFactor::SrcAlpha, 192 | dst_factor: grr::BlendFactor::OneMinusSrcAlpha, 193 | blend_op: grr::BlendOp::Add, 194 | }, 195 | alpha: grr::BlendChannel { 196 | src_factor: grr::BlendFactor::SrcAlpha, 197 | dst_factor: grr::BlendFactor::OneMinusSrcAlpha, 198 | blend_op: grr::BlendOp::Add, 199 | }, 200 | }], 201 | }; 202 | 203 | event_loop.run(move |event, _, control_flow| { 204 | *control_flow = ControlFlow::Wait; 205 | 206 | match event { 207 | Event::WindowEvent { 208 | event: WindowEvent::CloseRequested, 209 | .. 210 | } => *control_flow = ControlFlow::Exit, 211 | Event::LoopDestroyed => { 212 | grr.delete_shaders(&[vs, fs]); 213 | grr.delete_pipeline(pipeline); 214 | grr.delete_sampler(sampler); 215 | grr.delete_image_view(texture_view); 216 | grr.delete_image(texture); 217 | grr.delete_vertex_array(vertex_array); 218 | grr.delete_buffers(&[vertex_buffer, index_buffer]); 219 | } 220 | Event::RedrawRequested(_) => { 221 | let size = window.inner_size(); 222 | 223 | grr.bind_pipeline(pipeline); 224 | grr.bind_vertex_array(vertex_array); 225 | grr.bind_color_blend_state(&color_blend); 226 | 227 | grr.bind_image_views(3, &[texture_view]); 228 | grr.bind_samplers(3, &[sampler]); 229 | 230 | grr.bind_index_buffer(vertex_array, index_buffer); 231 | grr.bind_vertex_buffers( 232 | vertex_array, 233 | 0, 234 | &[grr::VertexBufferView { 235 | buffer: vertex_buffer, 236 | offset: 0, 237 | stride: (std::mem::size_of::() * 4) as _, 238 | input_rate: grr::InputRate::Vertex, 239 | }], 240 | ); 241 | 242 | grr.set_viewport( 243 | 0, 244 | &[grr::Viewport { 245 | x: 0.0, 246 | y: 0.0, 247 | w: size.width as _, 248 | h: size.height as _, 249 | n: 0.0, 250 | f: 1.0, 251 | }], 252 | ); 253 | grr.set_scissor( 254 | 0, 255 | &[grr::Region { 256 | x: 0, 257 | y: 0, 258 | w: size.width as _, 259 | h: size.height as _, 260 | }], 261 | ); 262 | 263 | grr.clear_attachment( 264 | grr::Framebuffer::DEFAULT, 265 | grr::ClearAttachment::ColorFloat(0, [0.9, 0.9, 0.9, 1.0]), 266 | ); 267 | grr.draw_indexed(grr::Primitive::Triangles, grr::IndexTy::U16, 0..6, 0..1, 0); 268 | 269 | context.swap_buffers(); 270 | } 271 | _ => (), 272 | } 273 | }) 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /examples/multi_context.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::c_void; 2 | use glutin::{ 3 | dpi::LogicalSize, 4 | event::{Event, WindowEvent}, 5 | event_loop::{ControlFlow, EventLoop}, 6 | window::WindowBuilder, 7 | }; 8 | 9 | const VERTEX_SRC: &str = r#" 10 | #version 450 core 11 | layout (location = 0) in vec2 v_pos; 12 | layout (location = 1) in vec3 v_color; 13 | 14 | layout (location = 0) out vec3 a_color; 15 | 16 | void main() { 17 | a_color = v_color; 18 | gl_Position = vec4(v_pos, 0.0, 1.0); 19 | } 20 | "#; 21 | 22 | const FRAGMENT_SRC: &str = r#" 23 | #version 450 core 24 | layout (location = 0) in vec3 a_color; 25 | out vec4 f_color; 26 | 27 | void main() { 28 | f_color = vec4(a_color, 1.0); 29 | } 30 | "#; 31 | 32 | const VERTICES: [f32; 15] = [ 33 | -0.5, -0.5, 1.0, 0.0, 0.0, 0.5, -0.5, 0.0, 1.0, 0.0, 0.0, 0.5, 0.0, 0.0, 1.0, 34 | ]; 35 | 36 | pub struct ErasedWindowContext(Option>); 37 | 38 | impl ErasedWindowContext { 39 | pub fn swap_buffers(&self) -> Result<(), glutin::ContextError> { 40 | self.0.as_ref().unwrap().swap_buffers() 41 | } 42 | 43 | pub fn resize(&self, size: glutin::dpi::PhysicalSize) { 44 | self.0.as_ref().unwrap().resize(size) 45 | } 46 | 47 | pub fn new(ctxt: glutin::ContextWrapper) -> Self { 48 | Self(Some(unsafe { ctxt.treat_as_current() })) 49 | } 50 | 51 | pub unsafe fn make_current(&mut self) -> Result<(), glutin::ContextError> { 52 | let ctxt = self.0.take().unwrap(); 53 | let result = ctxt.make_current(); 54 | match result { 55 | Ok(ctxt) => { 56 | self.0 = Some(ctxt); 57 | Ok(()) 58 | } 59 | Err((ctxt, err)) => { 60 | self.0 = Some(ctxt.treat_as_current()); 61 | Err(err) 62 | } 63 | } 64 | } 65 | 66 | pub fn get_proc_address(&self, addr: &str) -> *const c_void { 67 | self.0.as_ref().unwrap().get_proc_address(addr) 68 | } 69 | } 70 | 71 | pub struct ErasedContext(Option>); 72 | 73 | impl ErasedContext { 74 | pub fn new(ctxt: glutin::Context) -> Self { 75 | Self(Some(unsafe { ctxt.treat_as_current() })) 76 | } 77 | 78 | pub unsafe fn make_current(&mut self) -> Result<(), glutin::ContextError> { 79 | let ctxt = self.0.take().unwrap(); 80 | let result = ctxt.make_current(); 81 | match result { 82 | Ok(ctxt) => { 83 | self.0 = Some(ctxt); 84 | Ok(()) 85 | } 86 | Err((ctxt, err)) => { 87 | self.0 = Some(ctxt.treat_as_current()); 88 | Err(err) 89 | } 90 | } 91 | } 92 | 93 | pub fn get_proc_address(&self, addr: &str) -> *const c_void { 94 | self.0.as_ref().unwrap().get_proc_address(addr) 95 | } 96 | } 97 | 98 | fn main() -> anyhow::Result<()> { 99 | unsafe { 100 | let event_loop = EventLoop::new(); 101 | 102 | let context = glutin::ContextBuilder::new() 103 | .with_srgb(true) 104 | .with_gl_debug_flag(true) 105 | .build_headless(&event_loop, (1, 1).into()) 106 | .unwrap(); 107 | 108 | let wb = WindowBuilder::new() 109 | .with_title("grr - MultiContext") 110 | .with_inner_size(LogicalSize::new(1024.0, 768.0)); 111 | let window = glutin::ContextBuilder::new() 112 | .with_vsync(true) 113 | .with_srgb(true) 114 | .with_gl_debug_flag(true) 115 | .with_shared_lists(&context) 116 | .build_windowed(wb, &event_loop) 117 | .unwrap(); 118 | 119 | let (present_ctxt, window) = window.split(); 120 | let mut present_ctxt = ErasedWindowContext::new(present_ctxt); 121 | present_ctxt.make_current().unwrap(); 122 | 123 | let swapchain = grr::Device::new( 124 | |symbol| present_ctxt.get_proc_address(symbol) as *const _, 125 | grr::Debug::Disable, 126 | ); 127 | 128 | let present_fbo = swapchain.create_framebuffer()?; 129 | 130 | let mut context = ErasedContext::new(context); 131 | context.make_current().unwrap(); 132 | 133 | let grr = grr::Device::new( 134 | |symbol| context.get_proc_address(symbol) as *const _, 135 | grr::Debug::Enable { 136 | callback: |_, _, _, _, msg| { 137 | println!("{:?}", msg); 138 | }, 139 | flags: grr::DebugReport::FULL, 140 | }, 141 | ); 142 | 143 | let vs = grr.create_shader( 144 | grr::ShaderStage::Vertex, 145 | grr::ShaderSource::Glsl, 146 | VERTEX_SRC.as_bytes(), 147 | grr::ShaderFlags::VERBOSE, 148 | )?; 149 | let fs = grr.create_shader( 150 | grr::ShaderStage::Fragment, 151 | grr::ShaderSource::Glsl, 152 | FRAGMENT_SRC.as_bytes(), 153 | grr::ShaderFlags::VERBOSE, 154 | )?; 155 | 156 | let pipeline = grr.create_graphics_pipeline( 157 | grr::VertexPipelineDesc { 158 | vertex_shader: vs, 159 | tessellation_control_shader: None, 160 | tessellation_evaluation_shader: None, 161 | geometry_shader: None, 162 | fragment_shader: Some(fs), 163 | }, 164 | grr::PipelineFlags::VERBOSE, 165 | )?; 166 | 167 | let vertex_array = grr.create_vertex_array(&[ 168 | grr::VertexAttributeDesc { 169 | location: 0, 170 | binding: 0, 171 | format: grr::VertexFormat::Xy32Float, 172 | offset: 0, 173 | }, 174 | grr::VertexAttributeDesc { 175 | location: 1, 176 | binding: 0, 177 | format: grr::VertexFormat::Xyz32Float, 178 | offset: (2 * std::mem::size_of::()) as _, 179 | }, 180 | ])?; 181 | 182 | let triangle_data = 183 | grr.create_buffer_from_host(grr::as_u8_slice(&VERTICES), grr::MemoryFlags::empty())?; 184 | 185 | let ctxt_fbo = grr.create_framebuffer()?; 186 | 187 | let size = window.inner_size(); 188 | let present_image = grr.create_image( 189 | grr::ImageType::D2 { 190 | width: size.width as _, 191 | height: size.height as _, 192 | layers: 1, 193 | samples: 1, 194 | }, 195 | grr::Format::R8G8B8A8_SRGB, 196 | 1, 197 | )?; 198 | 199 | event_loop.run(move |event, _, control_flow| { 200 | *control_flow = ControlFlow::Wait; 201 | 202 | match event { 203 | Event::WindowEvent { 204 | event: WindowEvent::CloseRequested, 205 | .. 206 | } => *control_flow = ControlFlow::Exit, 207 | Event::LoopDestroyed => { 208 | grr.delete_shaders(&[vs, fs]); 209 | grr.delete_pipeline(pipeline); 210 | grr.delete_buffer(triangle_data); 211 | grr.delete_vertex_array(vertex_array); 212 | } 213 | Event::RedrawRequested(_) => { 214 | let size = window.inner_size(); 215 | 216 | context.make_current().unwrap(); 217 | grr.bind_pipeline(pipeline); 218 | grr.bind_vertex_array(vertex_array); 219 | grr.bind_vertex_buffers( 220 | vertex_array, 221 | 0, 222 | &[grr::VertexBufferView { 223 | buffer: triangle_data, 224 | offset: 0, 225 | stride: (std::mem::size_of::() * 5) as _, 226 | input_rate: grr::InputRate::Vertex, 227 | }], 228 | ); 229 | 230 | grr.set_viewport( 231 | 0, 232 | &[grr::Viewport { 233 | x: 0.0, 234 | y: 0.0, 235 | w: size.width as _, 236 | h: size.height as _, 237 | n: 0.0, 238 | f: 1.0, 239 | }], 240 | ); 241 | grr.set_scissor( 242 | 0, 243 | &[grr::Region { 244 | x: 0, 245 | y: 0, 246 | w: size.width as _, 247 | h: size.height as _, 248 | }], 249 | ); 250 | 251 | grr.bind_draw_framebuffer(ctxt_fbo); 252 | grr.set_color_attachments(ctxt_fbo, &[0]); 253 | grr.bind_attachments( 254 | ctxt_fbo, 255 | &[( 256 | grr::Attachment::Color(0), 257 | grr::AttachmentView::Image(present_image.as_view()), 258 | )], 259 | ); 260 | 261 | grr.clear_attachment( 262 | ctxt_fbo, 263 | grr::ClearAttachment::ColorFloat(0, [0.5, 0.5, 0.5, 1.0]), 264 | ); 265 | grr.draw(grr::Primitive::Triangles, 0..3, 0..1); 266 | 267 | present_ctxt.make_current().unwrap(); 268 | 269 | swapchain.set_color_attachments(present_fbo, &[0]); 270 | swapchain.bind_attachments( 271 | present_fbo, 272 | &[( 273 | grr::Attachment::Color(0), 274 | grr::AttachmentView::Image(present_image.as_view()), 275 | )], 276 | ); 277 | 278 | let screen = grr::Region { 279 | x: 0, 280 | y: 0, 281 | w: size.width as _, 282 | h: size.height as _, 283 | }; 284 | swapchain.blit( 285 | present_fbo, 286 | screen, 287 | grr::Framebuffer::DEFAULT, 288 | screen, 289 | grr::Filter::Linear, 290 | ); 291 | present_ctxt.swap_buffers().unwrap(); 292 | } 293 | _ => (), 294 | } 295 | }) 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/buffer.rs: -------------------------------------------------------------------------------- 1 | //! Buffer 2 | 3 | use crate::__gl; 4 | use crate::__gl::types::{GLbitfield, GLuint}; 5 | use crate::{ 6 | debug::{Object, ObjectType}, 7 | device::Device, 8 | error::Result, 9 | WHOLE_SIZE, 10 | }; 11 | 12 | use std::{mem, ops::Range, ptr, slice}; 13 | 14 | /// 15 | #[derive(Clone, Copy)] 16 | pub struct Buffer(pub(crate) GLuint, GLbitfield); 17 | 18 | impl Object for Buffer { 19 | const TYPE: ObjectType = ObjectType::Buffer; 20 | fn handle(&self) -> GLuint { 21 | self.0 22 | } 23 | } 24 | 25 | /// Buffer Range. 26 | /// 27 | /// Specifies a subrange of a buffer resource. 28 | pub struct BufferRange { 29 | pub buffer: Buffer, 30 | pub offset: u64, 31 | pub size: u64, 32 | } 33 | 34 | impl BufferRange { 35 | pub const fn whole(buffer: Buffer) -> Self { 36 | BufferRange { 37 | buffer, 38 | offset: 0, 39 | size: WHOLE_SIZE, 40 | } 41 | } 42 | } 43 | 44 | impl Device { 45 | unsafe fn create_buffer_impl( 46 | &self, 47 | size: isize, 48 | data_ptr: *const (), 49 | memory: MemoryFlags, 50 | ) -> Result { 51 | let flags = { 52 | let mut flags = 0; 53 | if !memory.contains(MemoryFlags::DEVICE_LOCAL) { 54 | flags |= __gl::CLIENT_STORAGE_BIT; 55 | } 56 | if memory.contains(MemoryFlags::COHERENT) { 57 | flags |= __gl::MAP_COHERENT_BIT | __gl::MAP_PERSISTENT_BIT; 58 | } 59 | if memory.contains(MemoryFlags::CPU_MAP_READ) { 60 | flags |= __gl::MAP_READ_BIT | __gl::MAP_PERSISTENT_BIT; 61 | } 62 | if memory.contains(MemoryFlags::CPU_MAP_WRITE) { 63 | flags |= __gl::MAP_WRITE_BIT | __gl::MAP_PERSISTENT_BIT; 64 | } 65 | if memory.contains(MemoryFlags::DYNAMIC) { 66 | flags |= __gl::DYNAMIC_STORAGE_BIT; 67 | } 68 | flags 69 | }; 70 | 71 | let mut buffer = 0; 72 | { 73 | self.0.CreateBuffers(1, &mut buffer); 74 | self.get_error()?; 75 | self.0 76 | .NamedBufferStorage(buffer, size, data_ptr as *const _, flags); 77 | self.get_error()?; 78 | } 79 | 80 | Ok(Buffer(buffer, flags)) 81 | } 82 | 83 | /// Create a new empty buffer. 84 | /// 85 | /// # Parameters 86 | /// 87 | /// - `size`: Length in bytes of the associated storage memory. 88 | /// - `memory`: Properties of the internal memory slice. Indicating the usage 89 | /// and locality of the allocation. 90 | pub unsafe fn create_buffer(&self, size: u64, memory: MemoryFlags) -> Result { 91 | self.create_buffer_impl(size as _, ptr::null(), memory) 92 | } 93 | 94 | /// Create a new buffer from host memory data. 95 | /// 96 | /// # Parameters 97 | /// 98 | /// - `data`: Host data, which will copied into the buffer on creation. 99 | /// - `memory`: Properties of the internal memory slice. Indicating the usage 100 | /// and locality of the allocation. 101 | pub unsafe fn create_buffer_from_host( 102 | &self, 103 | data: &[u8], 104 | memory: MemoryFlags, 105 | ) -> Result { 106 | self.create_buffer_impl(data.len() as _, data.as_ptr() as *const _, memory) 107 | } 108 | 109 | /// Persistently map memory to host accessible virtual memory. 110 | /// 111 | /// # Valid usage 112 | /// 113 | /// - `range.end` may not be larger than the size of the buffer. 114 | /// - `range.start` must be smaller than `range.end` 115 | /// - `buffer` must be created with `CPU_MAP_READ` or `CPU_MAP_WRITE` flags. 116 | /// - `range.end - range.start` must be a multiple of the size of `T` 117 | /// - If the buffer has not been created with `CPU_MAP_READ` the host should 118 | /// not read from the returned slice. 119 | /// - If the buffer has not been created with `CPU_MAP_WRITE` the host should 120 | /// not write to the returned slice. 121 | /// - A buffer can not be mapped multiple times. 122 | /// 123 | /// # Return 124 | /// 125 | /// Returns a typed slice of the mapped memory range. 126 | #[allow(clippy::mut_from_ref)] 127 | pub unsafe fn map_buffer( 128 | &self, 129 | buffer: Buffer, 130 | range: Range, 131 | mapping: MappingFlags, 132 | ) -> &mut [T] { 133 | let len = range.end - range.start; 134 | let stride = mem::size_of::(); 135 | assert_eq!(len % stride as u64, 0); 136 | 137 | let mut flags = 0; 138 | 139 | if mapping.contains(MappingFlags::UNSYNCHRONIZED) { 140 | flags |= __gl::MAP_UNSYNCHRONIZED_BIT; 141 | } 142 | flags |= buffer.1 143 | & (__gl::MAP_COHERENT_BIT 144 | | __gl::MAP_PERSISTENT_BIT 145 | | __gl::MAP_READ_BIT 146 | | __gl::MAP_WRITE_BIT); 147 | 148 | let stride = mem::size_of::(); 149 | 150 | let ptr = { 151 | self.0 152 | .MapNamedBufferRange(buffer.0, range.start as _, len as _, flags) 153 | as *mut _ 154 | }; 155 | 156 | slice::from_raw_parts_mut(ptr, len as usize / stride) 157 | } 158 | 159 | /// Unmap a buffer from virtual host memory. 160 | /// 161 | /// # Valid usage 162 | /// 163 | /// - The buffer must be currently mapped. 164 | /// 165 | /// # Return 166 | /// 167 | /// Returns if the unmapping operation was successfull. 168 | pub unsafe fn unmap_buffer(&self, buffer: Buffer) -> bool { 169 | self.0.UnmapNamedBuffer(buffer.0) != 0 170 | } 171 | 172 | /// Delete a buffer. 173 | pub unsafe fn delete_buffer(&self, buffer: Buffer) { 174 | self.delete_buffers(&[buffer]); 175 | } 176 | 177 | /// Delete multiple buffers. 178 | pub unsafe fn delete_buffers(&self, buffers: &[Buffer]) { 179 | let buffers = buffers.iter().map(|buffer| buffer.0).collect::>(); 180 | 181 | self.0.DeleteBuffers(buffers.len() as _, buffers.as_ptr()); 182 | } 183 | 184 | /// Copy memory from the host into the buffer memory. 185 | pub unsafe fn copy_host_to_buffer(&self, buffer: Buffer, offset: isize, data: &[u8]) { 186 | self.0 187 | .NamedBufferSubData(buffer.0, offset, data.len() as _, data.as_ptr() as *const _); 188 | } 189 | 190 | /// Bind buffer ranges as uniform buffers. 191 | /// 192 | /// Shader can access the buffer memory as readonly. 193 | pub unsafe fn bind_uniform_buffers(&self, first: u32, ranges: &[BufferRange]) { 194 | let buffers = ranges.iter().map(|view| view.buffer.0).collect::>(); 195 | let offsets = ranges 196 | .iter() 197 | .map(|view| view.offset as _) 198 | .collect::>(); 199 | let sizes = ranges 200 | .iter() 201 | .map(|view| { 202 | if view.size == WHOLE_SIZE { 203 | (self.get_buffer_size(view.buffer) - view.offset as u64) as _ 204 | } else { 205 | view.size as _ 206 | } 207 | }) 208 | .collect::>(); 209 | 210 | self.0.BindBuffersRange( 211 | __gl::UNIFORM_BUFFER, 212 | first, 213 | ranges.len() as _, 214 | buffers.as_ptr(), 215 | offsets.as_ptr(), 216 | sizes.as_ptr(), 217 | ); 218 | } 219 | 220 | /// Bind buffer ranges as shader storage buffers. 221 | /// 222 | /// Shaders can access the buffer memory as readwrite. 223 | pub unsafe fn bind_storage_buffers(&self, first: u32, ranges: &[BufferRange]) { 224 | let buffers = ranges.iter().map(|view| view.buffer.0).collect::>(); 225 | let offsets = ranges 226 | .iter() 227 | .map(|view| view.offset as _) 228 | .collect::>(); 229 | let sizes = ranges 230 | .iter() 231 | .map(|view| { 232 | if view.size == WHOLE_SIZE { 233 | (self.get_buffer_size(view.buffer) - view.offset as u64) as _ 234 | } else { 235 | view.size as _ 236 | } 237 | }) 238 | .collect::>(); 239 | 240 | self.0.BindBuffersRange( 241 | __gl::SHADER_STORAGE_BUFFER, 242 | first, 243 | ranges.len() as _, 244 | buffers.as_ptr(), 245 | offsets.as_ptr(), 246 | sizes.as_ptr(), 247 | ); 248 | } 249 | 250 | /// Bind indirect buffer for draw commands. 251 | pub unsafe fn bind_draw_indirect_buffer(&self, buffer: Buffer) { 252 | self.0.BindBuffer(__gl::DRAW_INDIRECT_BUFFER, buffer.0); 253 | } 254 | 255 | /// Unbind indirect buffer for draw commands. 256 | pub unsafe fn unbind_draw_indirect_buffer(&self) { 257 | self.0.BindBuffer(__gl::DRAW_INDIRECT_BUFFER, 0); 258 | } 259 | 260 | /// Bind indirect buffer for dispatch commands. 261 | pub unsafe fn bind_dispatch_indirect_buffer(&self, buffer: Buffer) { 262 | self.0.BindBuffer(__gl::DISPATCH_INDIRECT_BUFFER, buffer.0); 263 | } 264 | 265 | /// Unbind indirect buffer for draw commands. 266 | pub unsafe fn unbind_dispatch_indirect_buffer(&self) { 267 | self.0.BindBuffer(__gl::DISPATCH_INDIRECT_BUFFER, 0); 268 | } 269 | 270 | /// Bind the buffer for reading / packing pixels from framebuffer attachments or images. 271 | pub(crate) unsafe fn bind_pixel_pack_buffer(&self, buffer: Buffer) { 272 | self.0.BindBuffer(__gl::PIXEL_PACK_BUFFER, buffer.0); 273 | } 274 | 275 | /// Unbind the buffer for reading / packing pixels from framebuffer attachments or images. 276 | pub(crate) unsafe fn unbind_pixel_pack_buffer(&self) { 277 | self.0.BindBuffer(__gl::PIXEL_PACK_BUFFER, 0); 278 | } 279 | 280 | /// Bind the buffer for writing / unpacking pixels to framebuffer attachments or images. 281 | pub(crate) unsafe fn bind_pixel_unpack_buffer(&self, buffer: Buffer) { 282 | self.0.BindBuffer(__gl::PIXEL_UNPACK_BUFFER, buffer.0); 283 | } 284 | 285 | /// Unbind the buffer for writing / unpacking pixels to framebuffer attachments or images. 286 | pub(crate) unsafe fn unbind_pixel_unpack_buffer(&self) { 287 | self.0.BindBuffer(__gl::PIXEL_UNPACK_BUFFER, 0); 288 | } 289 | 290 | /// Bind parameter buffer for indirect commands. 291 | /// 292 | /// Requires GL 4.6 293 | pub unsafe fn bind_parameter_buffer(&self, buffer: Buffer) { 294 | self.0.BindBuffer(__gl::PARAMETER_BUFFER, buffer.0); 295 | } 296 | 297 | pub(crate) unsafe fn get_buffer_size(&self, buffer: Buffer) -> u64 { 298 | let mut size = 0; 299 | self.0 300 | .GetNamedBufferParameteri64v(buffer.0, __gl::BUFFER_SIZE, &mut size); 301 | size as _ 302 | } 303 | } 304 | 305 | bitflags!( 306 | /// Memory property flags. 307 | pub struct MemoryFlags: u8 { 308 | /// Device local memory on the GPU. 309 | const DEVICE_LOCAL = 0x1; 310 | 311 | /// CPU-GPU coherent. 312 | const COHERENT = 0x2; 313 | 314 | /// CPU can read from mapped memory. 315 | const CPU_MAP_READ = 0x4; 316 | 317 | /// CPU can write to mapped memory. 318 | const CPU_MAP_WRITE = 0x8; 319 | 320 | /// Required for copies to buffer from host memory. 321 | const DYNAMIC = 0x10; 322 | } 323 | ); 324 | 325 | bitflags!( 326 | /// Memory mapping flags. 327 | pub struct MappingFlags: u8 { 328 | /// Driver won't synchronize memory access. 329 | /// 330 | /// The user needs to manually synchonize access via fences. 331 | const UNSYNCHRONIZED = 0x1; 332 | } 333 | ); 334 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/image.rs: -------------------------------------------------------------------------------- 1 | //! Image storage and views. 2 | 3 | use crate::__gl; 4 | use crate::__gl::types::{GLenum, GLuint}; 5 | 6 | use std::ops::Range; 7 | 8 | use crate::buffer::BufferRange; 9 | use crate::debug::{Object, ObjectType}; 10 | use crate::device::Device; 11 | use crate::error::Result; 12 | use crate::format::Format; 13 | use crate::Extent; 14 | 15 | /// Image resource handle. 16 | /// 17 | /// Images ares multidimensional, formatted data arrays. 18 | /// Images are, together with buffers, resources and are typed representations 19 | /// of a memory slice. 20 | /// 21 | /// Images are often also called textures. We only denote with an image 22 | /// the actual **storage** of the data, meaning the memory with the 23 | /// associated layout metadata. 24 | /// 25 | /// The API only uses images directly when the function call 26 | /// affects the underlying memory (e.g copy operations). 27 | #[derive(Clone, Copy)] 28 | pub struct Image { 29 | pub(crate) raw: GLuint, 30 | pub(crate) target: GLenum, 31 | } 32 | 33 | impl Object for Image { 34 | const TYPE: ObjectType = ObjectType::Image; 35 | fn handle(&self) -> GLuint { 36 | self.raw 37 | } 38 | } 39 | 40 | impl Image { 41 | /// Get image view from current image. 42 | /// 43 | /// # Valid Usage 44 | /// 45 | /// - These image view **must** not be deleted. 46 | pub fn as_view(&self) -> ImageView { 47 | ImageView(self.raw) 48 | } 49 | } 50 | 51 | /// Image dimensionality type. 52 | /// 53 | /// Layer, as in arrays or cube maps, don't affect the dimensionality type. 54 | #[derive(Clone, Copy)] 55 | pub enum ImageType { 56 | // One dimensional image. 57 | D1 { 58 | // Width. 59 | width: u32, 60 | 61 | // Number of layers. 62 | // 63 | // `1` for non-array textures. 64 | layers: u32, 65 | }, 66 | // Two dimensional image. 67 | D2 { 68 | // Width. 69 | width: u32, 70 | 71 | // Height. 72 | height: u32, 73 | 74 | // Number of layers. 75 | // 76 | // `1` for non-array textures. 77 | layers: u32, 78 | 79 | samples: u32, 80 | }, 81 | // Three dimensional image. 82 | D3 { 83 | // Width. 84 | width: u32, 85 | 86 | // Height. 87 | height: u32, 88 | 89 | // Depth. 90 | depth: u32, 91 | }, 92 | } 93 | 94 | impl ImageType { 95 | /// Return the number of texels in the top layer of the image. 96 | pub fn num_texels(&self) -> usize { 97 | match *self { 98 | ImageType::D1 { width, .. } => width as usize, 99 | ImageType::D2 { width, height, .. } => width as usize * height as usize, 100 | ImageType::D3 { 101 | width, 102 | height, 103 | depth, 104 | .. 105 | } => width as usize * height as usize * depth as usize, 106 | } 107 | } 108 | 109 | /// Return the width of the image. 110 | pub fn width(&self) -> u32 { 111 | match *self { 112 | ImageType::D1 { width, .. } 113 | | ImageType::D2 { width, .. } 114 | | ImageType::D3 { width, .. } => width, 115 | } 116 | } 117 | 118 | /// Return the height of the image. 119 | pub fn height(&self) -> u32 { 120 | match *self { 121 | ImageType::D1 { .. } => 1, 122 | ImageType::D2 { height, .. } | ImageType::D3 { height, .. } => height, 123 | } 124 | } 125 | 126 | /// Return the height of the image. 127 | pub fn depth(&self) -> u32 { 128 | match *self { 129 | ImageType::D1 { .. } | ImageType::D2 { .. } => 1, 130 | ImageType::D3 { depth, .. } => depth, 131 | } 132 | } 133 | 134 | /// Full extent of the image dimensions for a single layer. 135 | pub fn full_extent(&self) -> Extent { 136 | Extent { 137 | width: self.width(), 138 | height: self.height(), 139 | depth: self.depth(), 140 | } 141 | } 142 | 143 | /// Return the number of samples in a texel of the image. 144 | pub fn samples(&self) -> u32 { 145 | match *self { 146 | ImageType::D1 { .. } | ImageType::D3 { .. } => 1, 147 | ImageType::D2 { samples, .. } => samples, 148 | } 149 | } 150 | 151 | /// Return the number of layers in the texutre. 152 | pub fn layers(&self) -> u32 { 153 | match *self { 154 | ImageType::D1 { layers, .. } | ImageType::D2 { layers, .. } => layers, 155 | ImageType::D3 { .. } => 1, 156 | } 157 | } 158 | 159 | fn view_ty(&self) -> ImageViewType { 160 | match *self { 161 | ImageType::D1 { layers: 1, .. } => ImageViewType::D1, 162 | ImageType::D1 { .. } => ImageViewType::D1Array, 163 | ImageType::D2 { layers: 1, .. } => ImageViewType::D2, 164 | ImageType::D2 { .. } => ImageViewType::D2Array, 165 | ImageType::D3 { .. } => ImageViewType::D3, 166 | } 167 | } 168 | } 169 | 170 | /// Image view handle. 171 | /// 172 | /// Image Views denote subranges of an image storage. Pipelines will 173 | /// only access image data via views. Views alias the memory of the associated 174 | /// image. 175 | #[repr(transparent)] 176 | #[derive(Copy, Clone)] 177 | pub struct ImageView(pub(crate) GLuint); 178 | 179 | impl Object for ImageView { 180 | const TYPE: ObjectType = ObjectType::Image; // internally it's an image 181 | fn handle(&self) -> GLuint { 182 | self.0 183 | } 184 | } 185 | 186 | /// Image View type. 187 | /// 188 | /// An `ImageViewType` maps roughly to OpenGL texture targets. 189 | #[derive(Debug, Clone, Copy)] 190 | pub enum ImageViewType { 191 | D1, 192 | D2, 193 | D3, 194 | Cube, 195 | D1Array, 196 | D2Array, 197 | CubeArray, 198 | } 199 | 200 | /// Subresource of an image. 201 | #[derive(Debug, Clone)] 202 | pub struct SubresourceRange { 203 | /// Range of mip levels. 204 | pub levels: Range, 205 | /// Range of array layers. 206 | pub layers: Range, 207 | } 208 | 209 | #[derive(Debug, Clone)] 210 | pub struct SubresourceLayers { 211 | /// Mipmap level. 212 | pub level: u32, 213 | /// Range of array layers. 214 | pub layers: Range, 215 | } 216 | 217 | impl Device { 218 | /// 219 | pub unsafe fn create_image(&self, ty: ImageType, format: Format, levels: u32) -> Result { 220 | let target = match ty { 221 | ImageType::D1 { layers: 1, .. } => __gl::TEXTURE_1D, 222 | ImageType::D1 { .. } => __gl::TEXTURE_1D_ARRAY, 223 | ImageType::D2 { 224 | layers: 1, 225 | samples: 1, 226 | .. 227 | } => __gl::TEXTURE_2D, 228 | ImageType::D2 { samples: 1, .. } => __gl::TEXTURE_2D_ARRAY, 229 | ImageType::D2 { layers: 1, .. } => __gl::TEXTURE_2D_MULTISAMPLE, 230 | ImageType::D2 { .. } => __gl::TEXTURE_2D_MULTISAMPLE_ARRAY, 231 | ImageType::D3 { .. } => __gl::TEXTURE_3D, 232 | }; 233 | 234 | let mut image = 0; 235 | self.0.CreateTextures(target, 1, &mut image); 236 | self.get_error()?; 237 | 238 | match ty { 239 | ImageType::D1 { width, layers: 1 } => { 240 | self.0 241 | .TextureStorage1D(image, levels as _, format as _, width as _); 242 | } 243 | ImageType::D1 { 244 | width, 245 | layers: height, 246 | } 247 | | ImageType::D2 { 248 | width, 249 | height, 250 | layers: 1, 251 | samples: 1, 252 | } => { 253 | self.0 254 | .TextureStorage2D(image, levels as _, format as _, width as _, height as _); 255 | } 256 | ImageType::D2 { 257 | width, 258 | height, 259 | layers: depth, 260 | samples: 1, 261 | } 262 | | ImageType::D3 { 263 | width, 264 | height, 265 | depth, 266 | } => { 267 | self.0.TextureStorage3D( 268 | image, 269 | levels as _, 270 | format as _, 271 | width as _, 272 | height as _, 273 | depth as _, 274 | ); 275 | } 276 | _ => unimplemented!(), 277 | } 278 | self.get_error()?; 279 | 280 | Ok(Image { raw: image, target }) 281 | } 282 | 283 | /// Create a texel buffer. 284 | pub unsafe fn create_texel_buffer(&self, buffer: BufferRange, format: Format) -> Result { 285 | let mut image = 0; 286 | self.0.CreateTextures(__gl::TEXTURE_BUFFER, 1, &mut image); 287 | self.get_error()?; 288 | 289 | self.0.TextureBufferRange( 290 | image, 291 | format as _, 292 | buffer.buffer.0, 293 | buffer.offset as _, 294 | buffer.size as _, 295 | ); 296 | self.get_error()?; 297 | 298 | Ok(Image { 299 | raw: image, 300 | target: __gl::TEXTURE_BUFFER, 301 | }) 302 | } 303 | 304 | /// Delete an images. 305 | pub unsafe fn delete_image(&self, image: Image) { 306 | self.delete_images(&[image]); 307 | } 308 | 309 | /// Delete multiple images. 310 | pub unsafe fn delete_images(&self, images: &[Image]) { 311 | let images = images.iter().map(|i| i.raw).collect::>(); 312 | 313 | self.0 314 | .DeleteTextures(images.len() as _, images.as_ptr() as *const _); 315 | } 316 | 317 | /// Create an image view from an image. 318 | pub unsafe fn create_image_view( 319 | &self, 320 | image: Image, 321 | ty: ImageViewType, 322 | format: Format, 323 | range: SubresourceRange, 324 | ) -> Result { 325 | let target = match ty { 326 | ImageViewType::D1 => __gl::TEXTURE_1D, 327 | ImageViewType::D2 if image.target == __gl::TEXTURE_2D_MULTISAMPLE => { 328 | __gl::TEXTURE_2D_MULTISAMPLE 329 | } 330 | ImageViewType::D2 => __gl::TEXTURE_2D, 331 | ImageViewType::D3 => __gl::TEXTURE_3D, 332 | ImageViewType::Cube => __gl::TEXTURE_CUBE_MAP, 333 | ImageViewType::D1Array => __gl::TEXTURE_1D_ARRAY, 334 | ImageViewType::D2Array if image.target == __gl::TEXTURE_2D_MULTISAMPLE_ARRAY => { 335 | __gl::TEXTURE_2D_MULTISAMPLE_ARRAY 336 | } 337 | ImageViewType::D2Array => __gl::TEXTURE_2D_ARRAY, 338 | ImageViewType::CubeArray => __gl::TEXTURE_CUBE_MAP_ARRAY, 339 | }; 340 | let mut view = 0; 341 | self.0.GenTextures(1, &mut view); 342 | self.get_error()?; 343 | 344 | self.0.TextureView( 345 | view, 346 | target, 347 | image.raw, 348 | format as _, 349 | range.levels.start, 350 | range.levels.end - range.levels.start, 351 | range.layers.start, 352 | range.layers.end - range.layers.start, 353 | ); 354 | self.get_error()?; 355 | 356 | Ok(ImageView(view)) 357 | } 358 | 359 | /// Create an image and an associated view. 360 | /// 361 | /// The image view type is derived from the `ImageType`. 362 | /// It creates either non-arrayed or arrayed view types. 363 | pub unsafe fn create_image_and_view( 364 | &self, 365 | ty: ImageType, 366 | format: Format, 367 | levels: u32, 368 | ) -> Result<(Image, ImageView)> { 369 | let image = self.create_image(ty, format, levels)?; 370 | let view_ty = ty.view_ty(); 371 | let image_view = self.create_image_view( 372 | image, 373 | view_ty, 374 | format, 375 | SubresourceRange { 376 | levels: 0..levels, 377 | layers: 0..ty.layers(), 378 | }, 379 | )?; 380 | 381 | Ok((image, image_view)) 382 | } 383 | 384 | /// Delete an image views. 385 | pub unsafe fn delete_image_view(&self, view: ImageView) { 386 | self.delete_image_views(&[view]); 387 | } 388 | 389 | /// Delete multipe image views. 390 | pub unsafe fn delete_image_views(&self, views: &[ImageView]) { 391 | self.0.DeleteTextures( 392 | views.len() as _, 393 | views.as_ptr() as *const _, // newtype 394 | ); 395 | } 396 | 397 | /// Bind image views to texture units. 398 | pub unsafe fn bind_image_views(&self, first: u32, views: &[ImageView]) { 399 | let views = views.iter().map(|view| view.0).collect::>(); 400 | self.0.BindTextures(first, views.len() as _, views.as_ptr()); 401 | } 402 | 403 | /// Bind image views to storage image units. 404 | pub unsafe fn bind_storage_image_views(&self, first: u32, views: &[ImageView]) { 405 | let views = views.iter().map(|view| view.0).collect::>(); 406 | self.0 407 | .BindImageTextures(first, views.len() as _, views.as_ptr()); 408 | } 409 | 410 | /// Generate mipmaps. 411 | /// 412 | /// This generates the remaining mipmap levels using the base layer 413 | /// via downscaling. The number of levels are determined on resource 414 | /// creation. 415 | /// 416 | /// The downscaling filter is implementation dependent! 417 | pub unsafe fn generate_mipmaps(&self, image: Image) { 418 | self.0.GenerateTextureMipmap(image.raw); 419 | } 420 | } 421 | -------------------------------------------------------------------------------- /src/vertex.rs: -------------------------------------------------------------------------------- 1 | use crate::__gl; 2 | use crate::__gl::types::GLuint; 3 | 4 | use crate::buffer::Buffer; 5 | use crate::debug::{Object, ObjectType}; 6 | use crate::device::Device; 7 | use crate::error::Result; 8 | 9 | /// Vertex array handle. 10 | #[repr(transparent)] 11 | #[derive(Clone, Copy)] 12 | pub struct VertexArray(pub(crate) GLuint); 13 | 14 | impl Object for VertexArray { 15 | const TYPE: ObjectType = ObjectType::VertexArray; 16 | fn handle(&self) -> GLuint { 17 | self.0 18 | } 19 | } 20 | 21 | /// Buffer representation for vertex attributes 22 | #[derive(Clone)] 23 | pub struct VertexBufferView { 24 | /// Buffer handle for this buffer binding. 25 | pub buffer: Buffer, 26 | /// Vertex binding start address relative to the buffer. 27 | pub offset: u64, 28 | /// Distance in bytes between two consecutive elements. 29 | pub stride: u32, 30 | /// Vertex attribute addressing. 31 | pub input_rate: InputRate, 32 | } 33 | 34 | /// Vertex attribute format and binding. 35 | #[derive(Debug, Clone, PartialEq, Eq)] 36 | pub struct VertexAttributeDesc { 37 | /// Shader binding location of the attribute. 38 | pub location: u32, 39 | /// Vertex buffer binding number which is the data source. 40 | pub binding: u32, 41 | /// Size and type of the vertex data. 42 | pub format: VertexFormat, 43 | /// Byte offset of this attribute relative to the start of an element in the vertex binding. 44 | pub offset: u32, 45 | } 46 | 47 | /// Vertex attribute addresssing. 48 | /// 49 | /// Specifies if the vertex attribute address depends on vertex index or instance index. 50 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 51 | pub enum InputRate { 52 | /// Vertex index addressing. 53 | /// 54 | /// Attribute data is fetched from the bound vertex buffers depending on the current vertex index. 55 | Vertex, 56 | /// Instance index addressing. 57 | /// 58 | /// Attribute data is fetched from the bound vertex buffers depending on the current instance index. 59 | /// The `divisor` further defines how many consecutive instances will use the same vertex attribute data. 60 | /// The instance index will be divided by the divisor to donate the addressing index. 61 | Instance { divisor: usize }, 62 | } 63 | 64 | /// Vertex attribute formats. 65 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 66 | pub enum VertexFormat { 67 | X8Int, 68 | X8Uint, 69 | X8Unorm, 70 | X8Inorm, 71 | X8Uscaled, 72 | X8Iscaled, 73 | 74 | Xy8Int, 75 | Xy8Uint, 76 | Xy8Unorm, 77 | Xy8Inorm, 78 | Xy8Uscaled, 79 | Xy8Iscaled, 80 | 81 | Xyz8Int, 82 | Xyz8Uint, 83 | Xyz8Unorm, 84 | Xyz8Inorm, 85 | Xyz8Uscaled, 86 | Xyz8Iscaled, 87 | 88 | Xyzw8Int, 89 | Xyzw8Uint, 90 | Xyzw8Unorm, 91 | Xyzw8Inorm, 92 | Xyzw8Uscaled, 93 | Xyzw8Iscaled, 94 | 95 | X16Int, 96 | X16Uint, 97 | X16Float, 98 | X16Unorm, 99 | X16Inorm, 100 | X16Uscaled, 101 | X16Iscaled, 102 | 103 | Xy16Int, 104 | Xy16Uint, 105 | Xy16Float, 106 | Xy16Unorm, 107 | Xy16Inorm, 108 | Xy16Uscaled, 109 | Xy16Iscaled, 110 | 111 | Xyz16Int, 112 | Xyz16Uint, 113 | Xyz16Float, 114 | Xyz16Unorm, 115 | Xyz16Inorm, 116 | Xyz16Uscaled, 117 | Xyz16Iscaled, 118 | 119 | Xyzw16Int, 120 | Xyzw16Uint, 121 | Xyzw16Float, 122 | Xyzw16Unorm, 123 | Xyzw16Inorm, 124 | Xyzw16Uscaled, 125 | Xyzw16Iscaled, 126 | 127 | X32Int, 128 | X32Uint, 129 | X32Float, 130 | X32Unorm, 131 | X32Inorm, 132 | X32Uscaled, 133 | X32Iscaled, 134 | 135 | Xy32Int, 136 | Xy32Uint, 137 | Xy32Float, 138 | Xy32Unorm, 139 | Xy32Inorm, 140 | Xy32Uscaled, 141 | Xy32Iscaled, 142 | 143 | Xyz32Int, 144 | Xyz32Uint, 145 | Xyz32Float, 146 | Xyz32Unorm, 147 | Xyz32Inorm, 148 | Xyz32Uscaled, 149 | Xyz32Iscaled, 150 | 151 | Xyzw32Int, 152 | Xyzw32Uint, 153 | Xyzw32Float, 154 | Xyzw32Unorm, 155 | Xyzw32Inorm, 156 | Xyzw32Uscaled, 157 | Xyzw32Iscaled, 158 | 159 | X64Float, 160 | Xy64Float, 161 | Xyz64Float, 162 | Xyzw64Float, 163 | } 164 | 165 | impl Device { 166 | /// Create a new vertex array, storing information for the input assembler. 167 | /// 168 | /// The vertex array specified the vertex attributes and their binding to 169 | /// vertex buffer objects. 170 | pub unsafe fn create_vertex_array( 171 | &self, 172 | attributes: &[VertexAttributeDesc], 173 | ) -> Result { 174 | let mut vao = 0; 175 | self.0.CreateVertexArrays(1, &mut vao); 176 | self.get_error()?; 177 | 178 | enum VertexBase { 179 | Int, 180 | Float, 181 | Double, 182 | } 183 | 184 | for desc in attributes { 185 | let (base, num, ty, norm) = match desc.format { 186 | VertexFormat::X8Int => (VertexBase::Int, 1, __gl::BYTE, false), 187 | VertexFormat::X8Uint => (VertexBase::Int, 1, __gl::UNSIGNED_BYTE, false), 188 | VertexFormat::X8Unorm => (VertexBase::Float, 1, __gl::UNSIGNED_BYTE, true), 189 | VertexFormat::X8Inorm => (VertexBase::Float, 1, __gl::BYTE, true), 190 | VertexFormat::X8Uscaled => (VertexBase::Float, 1, __gl::UNSIGNED_BYTE, false), 191 | VertexFormat::X8Iscaled => (VertexBase::Float, 1, __gl::BYTE, false), 192 | 193 | VertexFormat::Xy8Int => (VertexBase::Int, 2, __gl::BYTE, false), 194 | VertexFormat::Xy8Uint => (VertexBase::Int, 2, __gl::UNSIGNED_BYTE, false), 195 | VertexFormat::Xy8Unorm => (VertexBase::Float, 2, __gl::UNSIGNED_BYTE, true), 196 | VertexFormat::Xy8Inorm => (VertexBase::Float, 2, __gl::BYTE, true), 197 | VertexFormat::Xy8Uscaled => (VertexBase::Float, 2, __gl::UNSIGNED_BYTE, false), 198 | VertexFormat::Xy8Iscaled => (VertexBase::Float, 2, __gl::BYTE, false), 199 | 200 | VertexFormat::Xyz8Int => (VertexBase::Int, 3, __gl::BYTE, false), 201 | VertexFormat::Xyz8Uint => (VertexBase::Int, 3, __gl::UNSIGNED_BYTE, false), 202 | VertexFormat::Xyz8Unorm => (VertexBase::Float, 3, __gl::UNSIGNED_BYTE, true), 203 | VertexFormat::Xyz8Inorm => (VertexBase::Float, 3, __gl::BYTE, true), 204 | VertexFormat::Xyz8Uscaled => (VertexBase::Float, 3, __gl::UNSIGNED_BYTE, false), 205 | VertexFormat::Xyz8Iscaled => (VertexBase::Float, 3, __gl::BYTE, false), 206 | 207 | VertexFormat::Xyzw8Int => (VertexBase::Int, 4, __gl::BYTE, false), 208 | VertexFormat::Xyzw8Uint => (VertexBase::Int, 4, __gl::UNSIGNED_BYTE, false), 209 | VertexFormat::Xyzw8Unorm => (VertexBase::Float, 4, __gl::UNSIGNED_BYTE, true), 210 | VertexFormat::Xyzw8Inorm => (VertexBase::Float, 4, __gl::BYTE, true), 211 | VertexFormat::Xyzw8Uscaled => (VertexBase::Float, 4, __gl::UNSIGNED_BYTE, false), 212 | VertexFormat::Xyzw8Iscaled => (VertexBase::Float, 4, __gl::BYTE, false), 213 | 214 | VertexFormat::X16Int => (VertexBase::Int, 1, __gl::SHORT, false), 215 | VertexFormat::X16Uint => (VertexBase::Int, 1, __gl::UNSIGNED_SHORT, false), 216 | VertexFormat::X16Float => (VertexBase::Float, 1, __gl::HALF_FLOAT, false), 217 | VertexFormat::X16Unorm => (VertexBase::Float, 1, __gl::UNSIGNED_SHORT, true), 218 | VertexFormat::X16Inorm => (VertexBase::Float, 1, __gl::SHORT, true), 219 | VertexFormat::X16Uscaled => (VertexBase::Float, 1, __gl::UNSIGNED_SHORT, false), 220 | VertexFormat::X16Iscaled => (VertexBase::Float, 1, __gl::SHORT, false), 221 | 222 | VertexFormat::Xy16Int => (VertexBase::Int, 2, __gl::SHORT, false), 223 | VertexFormat::Xy16Uint => (VertexBase::Int, 2, __gl::UNSIGNED_SHORT, false), 224 | VertexFormat::Xy16Float => (VertexBase::Float, 2, __gl::HALF_FLOAT, false), 225 | VertexFormat::Xy16Unorm => (VertexBase::Float, 2, __gl::UNSIGNED_SHORT, true), 226 | VertexFormat::Xy16Inorm => (VertexBase::Float, 2, __gl::SHORT, true), 227 | VertexFormat::Xy16Uscaled => (VertexBase::Float, 2, __gl::UNSIGNED_SHORT, false), 228 | VertexFormat::Xy16Iscaled => (VertexBase::Float, 2, __gl::SHORT, false), 229 | 230 | VertexFormat::Xyz16Int => (VertexBase::Int, 3, __gl::SHORT, false), 231 | VertexFormat::Xyz16Uint => (VertexBase::Int, 3, __gl::UNSIGNED_SHORT, false), 232 | VertexFormat::Xyz16Float => (VertexBase::Float, 3, __gl::HALF_FLOAT, false), 233 | VertexFormat::Xyz16Unorm => (VertexBase::Float, 3, __gl::UNSIGNED_SHORT, true), 234 | VertexFormat::Xyz16Inorm => (VertexBase::Float, 3, __gl::SHORT, true), 235 | VertexFormat::Xyz16Uscaled => (VertexBase::Float, 3, __gl::UNSIGNED_SHORT, false), 236 | VertexFormat::Xyz16Iscaled => (VertexBase::Float, 3, __gl::SHORT, false), 237 | 238 | VertexFormat::Xyzw16Int => (VertexBase::Int, 4, __gl::SHORT, false), 239 | VertexFormat::Xyzw16Uint => (VertexBase::Int, 4, __gl::UNSIGNED_SHORT, false), 240 | VertexFormat::Xyzw16Float => (VertexBase::Float, 4, __gl::HALF_FLOAT, false), 241 | VertexFormat::Xyzw16Unorm => (VertexBase::Float, 4, __gl::UNSIGNED_SHORT, true), 242 | VertexFormat::Xyzw16Inorm => (VertexBase::Float, 4, __gl::SHORT, true), 243 | VertexFormat::Xyzw16Uscaled => (VertexBase::Float, 4, __gl::UNSIGNED_SHORT, false), 244 | VertexFormat::Xyzw16Iscaled => (VertexBase::Float, 4, __gl::SHORT, false), 245 | 246 | VertexFormat::X32Int => (VertexBase::Int, 1, __gl::INT, false), 247 | VertexFormat::X32Uint => (VertexBase::Int, 1, __gl::UNSIGNED_INT, false), 248 | VertexFormat::X32Float => (VertexBase::Float, 1, __gl::FLOAT, false), 249 | VertexFormat::X32Unorm => (VertexBase::Float, 1, __gl::UNSIGNED_INT, true), 250 | VertexFormat::X32Inorm => (VertexBase::Float, 1, __gl::INT, true), 251 | VertexFormat::X32Uscaled => (VertexBase::Float, 1, __gl::UNSIGNED_INT, false), 252 | VertexFormat::X32Iscaled => (VertexBase::Float, 1, __gl::INT, false), 253 | 254 | VertexFormat::Xy32Int => (VertexBase::Int, 2, __gl::INT, false), 255 | VertexFormat::Xy32Uint => (VertexBase::Int, 2, __gl::UNSIGNED_INT, false), 256 | VertexFormat::Xy32Float => (VertexBase::Float, 2, __gl::FLOAT, false), 257 | VertexFormat::Xy32Unorm => (VertexBase::Float, 2, __gl::UNSIGNED_INT, true), 258 | VertexFormat::Xy32Inorm => (VertexBase::Float, 2, __gl::INT, true), 259 | VertexFormat::Xy32Uscaled => (VertexBase::Float, 2, __gl::UNSIGNED_INT, false), 260 | VertexFormat::Xy32Iscaled => (VertexBase::Float, 2, __gl::INT, false), 261 | 262 | VertexFormat::Xyz32Int => (VertexBase::Int, 3, __gl::INT, false), 263 | VertexFormat::Xyz32Uint => (VertexBase::Int, 3, __gl::UNSIGNED_INT, false), 264 | VertexFormat::Xyz32Float => (VertexBase::Float, 3, __gl::FLOAT, false), 265 | VertexFormat::Xyz32Unorm => (VertexBase::Float, 3, __gl::UNSIGNED_INT, true), 266 | VertexFormat::Xyz32Inorm => (VertexBase::Float, 3, __gl::INT, true), 267 | VertexFormat::Xyz32Uscaled => (VertexBase::Float, 3, __gl::UNSIGNED_INT, false), 268 | VertexFormat::Xyz32Iscaled => (VertexBase::Float, 3, __gl::INT, false), 269 | 270 | VertexFormat::Xyzw32Int => (VertexBase::Int, 4, __gl::INT, false), 271 | VertexFormat::Xyzw32Uint => (VertexBase::Int, 4, __gl::UNSIGNED_INT, false), 272 | VertexFormat::Xyzw32Float => (VertexBase::Float, 4, __gl::FLOAT, false), 273 | VertexFormat::Xyzw32Unorm => (VertexBase::Float, 4, __gl::UNSIGNED_INT, true), 274 | VertexFormat::Xyzw32Inorm => (VertexBase::Float, 4, __gl::INT, true), 275 | VertexFormat::Xyzw32Uscaled => (VertexBase::Float, 4, __gl::UNSIGNED_INT, false), 276 | VertexFormat::Xyzw32Iscaled => (VertexBase::Float, 4, __gl::INT, false), 277 | 278 | VertexFormat::X64Float => (VertexBase::Double, 1, __gl::DOUBLE, false), 279 | VertexFormat::Xy64Float => (VertexBase::Double, 2, __gl::DOUBLE, false), 280 | VertexFormat::Xyz64Float => (VertexBase::Double, 3, __gl::DOUBLE, false), 281 | VertexFormat::Xyzw64Float => (VertexBase::Double, 4, __gl::DOUBLE, false), 282 | }; 283 | 284 | self.0.EnableVertexArrayAttrib(vao, desc.location); 285 | match base { 286 | VertexBase::Int => { 287 | self.0 288 | .VertexArrayAttribIFormat(vao, desc.location, num, ty, desc.offset); 289 | } 290 | VertexBase::Float => { 291 | self.0.VertexArrayAttribFormat( 292 | vao, 293 | desc.location, 294 | num, 295 | ty, 296 | norm as _, 297 | desc.offset, 298 | ); 299 | } 300 | VertexBase::Double => { 301 | self.0 302 | .VertexArrayAttribLFormat(vao, desc.location, num, ty, desc.offset); 303 | } 304 | } 305 | 306 | self.0 307 | .VertexArrayAttribBinding(vao, desc.location, desc.binding); 308 | } 309 | 310 | Ok(VertexArray(vao)) 311 | } 312 | 313 | /// Delete a vertex array. 314 | pub unsafe fn delete_vertex_array(&self, vao: VertexArray) { 315 | self.delete_vertex_arrays(&[vao]); 316 | } 317 | 318 | /// Delete multiple vertex arrays. 319 | pub unsafe fn delete_vertex_arrays(&self, vao: &[VertexArray]) { 320 | self.0.DeleteVertexArrays( 321 | vao.len() as _, 322 | vao.as_ptr() as *const _, // newtype 323 | ); 324 | } 325 | 326 | /// Bind a vertex array for usage. 327 | pub unsafe fn bind_vertex_array(&self, vao: VertexArray) { 328 | self.0.BindVertexArray(vao.0); 329 | } 330 | 331 | /// Bind vertex buffers to a vertex array. 332 | pub unsafe fn bind_vertex_buffers( 333 | &self, 334 | vao: VertexArray, 335 | first: u32, 336 | views: &[VertexBufferView], 337 | ) { 338 | let buffers = views.iter().map(|view| view.buffer.0).collect::>(); 339 | 340 | let offsets = views 341 | .iter() 342 | .map(|view| view.offset as _) 343 | .collect::>(); 344 | 345 | let strides = views 346 | .iter() 347 | .map(|view| view.stride as _) 348 | .collect::>(); 349 | 350 | self.0.VertexArrayVertexBuffers( 351 | vao.0, 352 | first, 353 | views.len() as _, 354 | buffers.as_ptr(), 355 | offsets.as_ptr(), 356 | strides.as_ptr(), 357 | ); 358 | 359 | for (binding, view) in views.iter().enumerate() { 360 | let divisor = match view.input_rate { 361 | InputRate::Vertex => 0, 362 | InputRate::Instance { divisor } => divisor, 363 | }; 364 | 365 | self.0 366 | .VertexArrayBindingDivisor(vao.0, first + binding as u32, divisor as _); 367 | } 368 | } 369 | 370 | /// Bind a index buffer to a vertex array. 371 | pub unsafe fn bind_index_buffer(&self, vao: VertexArray, buffer: Buffer) { 372 | self.0.VertexArrayElementBuffer(vao.0, buffer.0); 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /src/transfer.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | BaseFormat, Buffer, BufferRange, Device, Extent, FormatLayout, Image, Offset, Region, 3 | SubresourceLayers, __gl, WHOLE_SIZE, 4 | }; 5 | 6 | /// Specifies the layout of the host or buffer memory. 7 | #[derive(Debug, Copy, Clone)] 8 | pub struct MemoryLayout { 9 | /// 10 | pub base_format: BaseFormat, 11 | /// 12 | pub format_layout: FormatLayout, 13 | /// 14 | pub row_length: u32, 15 | /// 16 | pub image_height: u32, 17 | /// 18 | pub alignment: u32, 19 | } 20 | 21 | #[derive(Debug, Clone)] 22 | pub struct ImageCopy { 23 | /// Layers of the source image. 24 | pub src_subresource: SubresourceLayers, 25 | /// Initial x,y,z texel offsets in the subregion of the source image. 26 | pub src_offset: Offset, 27 | /// Layers of the destination image. 28 | pub dst_subresource: SubresourceLayers, 29 | /// Initial x,y,z texel offsets in the subregion of the destination image. 30 | pub dst_offset: Offset, 31 | /// Size of texels to copy in the subregion of of the source/destination image. 32 | pub extent: Extent, 33 | } 34 | 35 | #[derive(Debug, Clone)] 36 | pub struct BufferImageCopy { 37 | /// Offset in bytes from the start of the source/destination buffer. 38 | pub buffer_offset: u64, 39 | /// Layout of the source/destination buffer. 40 | pub buffer_layout: MemoryLayout, 41 | /// Layers of the source/destination image. 42 | pub image_subresource: SubresourceLayers, 43 | /// Initial x,y,z texel offsets in the subregion of the source/destination image. 44 | pub image_offset: Offset, 45 | /// Size of texels to copy in the subregion of of the source/destination image. 46 | pub image_extent: Extent, 47 | } 48 | 49 | #[derive(Debug, Clone)] 50 | pub struct HostImageCopy { 51 | /// Layout of the source/destination host memory. 52 | pub host_layout: MemoryLayout, 53 | /// Layers of the source/destination image. 54 | pub image_subresource: SubresourceLayers, 55 | /// Initial x,y,z texel offsets in the subregion of the source/destination image. 56 | pub image_offset: Offset, 57 | /// Size of texels to copy in the subregion of of the source/destination image. 58 | pub image_extent: Extent, 59 | } 60 | 61 | impl Device { 62 | pub(crate) unsafe fn set_pixel_unpack_params(&self, layout: &MemoryLayout) { 63 | self.0 64 | .PixelStorei(__gl::UNPACK_ALIGNMENT, layout.alignment as _); 65 | self.0 66 | .PixelStorei(__gl::UNPACK_IMAGE_HEIGHT, layout.image_height as _); 67 | self.0 68 | .PixelStorei(__gl::UNPACK_ROW_LENGTH, layout.row_length as _); 69 | } 70 | 71 | pub(crate) unsafe fn set_pixel_pack_params(&self, layout: &MemoryLayout) { 72 | self.0 73 | .PixelStorei(__gl::PACK_ALIGNMENT, layout.alignment as _); 74 | self.0 75 | .PixelStorei(__gl::PACK_IMAGE_HEIGHT, layout.image_height as _); 76 | self.0 77 | .PixelStorei(__gl::PACK_ROW_LENGTH, layout.row_length as _); 78 | } 79 | 80 | /// Copy image data from a location to device memory. 81 | /// 82 | /// Location can be either a buffer or a host memory, depending on 83 | /// whether or not pixel_unpack_buffer is bound. 84 | unsafe fn copy_to_image( 85 | &self, 86 | image: Image, 87 | subresource: SubresourceLayers, 88 | offset: Offset, 89 | extent: Extent, 90 | data_ptr: *const __gl::types::GLvoid, 91 | layout: MemoryLayout, 92 | ) { 93 | self.set_pixel_unpack_params(&layout); 94 | match image.target { 95 | __gl::TEXTURE_1D if subresource.layers == (0..1) => self.0.TextureSubImage1D( 96 | image.raw, 97 | subresource.level as _, 98 | offset.x, 99 | extent.width as _, 100 | layout.base_format as _, 101 | layout.format_layout as _, 102 | data_ptr, 103 | ), 104 | __gl::TEXTURE_1D_ARRAY => self.0.TextureSubImage2D( 105 | image.raw, 106 | subresource.level as _, 107 | offset.x, 108 | subresource.layers.start as _, 109 | extent.width as _, 110 | (subresource.layers.end - subresource.layers.start) as _, 111 | layout.base_format as _, 112 | layout.format_layout as _, 113 | data_ptr, 114 | ), 115 | __gl::TEXTURE_2D if subresource.layers == (0..1) => self.0.TextureSubImage2D( 116 | image.raw, 117 | subresource.level as _, 118 | offset.x, 119 | offset.y, 120 | extent.width as _, 121 | extent.height as _, 122 | layout.base_format as _, 123 | layout.format_layout as _, 124 | data_ptr, 125 | ), 126 | __gl::TEXTURE_2D_ARRAY => self.0.TextureSubImage3D( 127 | image.raw, 128 | subresource.level as _, 129 | offset.x, 130 | offset.y, 131 | subresource.layers.start as _, 132 | extent.width as _, 133 | extent.height as _, 134 | (subresource.layers.end - subresource.layers.start) as _, 135 | layout.base_format as _, 136 | layout.format_layout as _, 137 | data_ptr, 138 | ), 139 | __gl::TEXTURE_3D if subresource.layers == (0..1) => self.0.TextureSubImage3D( 140 | image.raw, 141 | subresource.level as _, 142 | offset.x, 143 | offset.y, 144 | offset.z, 145 | extent.width as _, 146 | extent.height as _, 147 | extent.depth as _, 148 | layout.base_format as _, 149 | layout.format_layout as _, 150 | data_ptr, 151 | ), 152 | _ => unimplemented!(), // panic!("Invalid target image: {}", image.target), 153 | } 154 | } 155 | 156 | /// Copy image data from host memory to device memory. 157 | pub unsafe fn copy_host_to_image( 158 | &self, 159 | src_host: &[T], 160 | dst_image: Image, 161 | region: HostImageCopy, 162 | ) { 163 | self.unbind_pixel_unpack_buffer(); 164 | self.copy_to_image( 165 | dst_image, 166 | region.image_subresource, 167 | region.image_offset, 168 | region.image_extent, 169 | src_host.as_ptr() as *const _, 170 | region.host_layout, 171 | ); 172 | } 173 | 174 | /// Copy image data from buffer to device memory. 175 | pub unsafe fn copy_buffer_to_image( 176 | &self, 177 | src_buffer: Buffer, 178 | dst_image: Image, 179 | region: BufferImageCopy, 180 | ) { 181 | self.bind_pixel_unpack_buffer(src_buffer); 182 | self.copy_to_image( 183 | dst_image, 184 | region.image_subresource, 185 | region.image_offset, 186 | region.image_extent, 187 | region.buffer_offset as *const _, 188 | region.buffer_layout, 189 | ); 190 | } 191 | 192 | unsafe fn map_subresource_region( 193 | image: Image, 194 | subresource: &SubresourceLayers, 195 | offset: Offset, 196 | extent: Extent, 197 | ) -> (Offset, Extent) { 198 | match image.target { 199 | __gl::TEXTURE_1D => ( 200 | Offset { 201 | x: offset.x, 202 | y: 0, 203 | z: 0, 204 | }, 205 | Extent { 206 | width: extent.width, 207 | height: 1, 208 | depth: 1, 209 | }, 210 | ), 211 | __gl::TEXTURE_1D_ARRAY => ( 212 | Offset { 213 | x: offset.x, 214 | y: subresource.layers.start as _, 215 | z: 0, 216 | }, 217 | Extent { 218 | width: extent.width, 219 | height: (subresource.layers.end - subresource.layers.start) as _, 220 | depth: 1, 221 | }, 222 | ), 223 | __gl::TEXTURE_2D => ( 224 | Offset { 225 | x: offset.x, 226 | y: offset.y, 227 | z: 0, 228 | }, 229 | Extent { 230 | width: extent.width, 231 | height: extent.height, 232 | depth: 1, 233 | }, 234 | ), 235 | __gl::TEXTURE_2D_ARRAY => ( 236 | Offset { 237 | x: offset.x, 238 | y: offset.y, 239 | z: subresource.layers.start as _, 240 | }, 241 | Extent { 242 | width: extent.width, 243 | height: extent.height, 244 | depth: (subresource.layers.end - subresource.layers.start) as _, 245 | }, 246 | ), 247 | __gl::TEXTURE_3D => (offset, extent), 248 | _ => { 249 | // todo 250 | unimplemented!( 251 | "Cannot copy from image for multisample, cube array, or buffer textures" 252 | ); 253 | } 254 | } 255 | } 256 | 257 | unsafe fn copy_image_to( 258 | &self, 259 | image: Image, 260 | subresource: SubresourceLayers, 261 | offset: Offset, 262 | extent: Extent, 263 | layout: MemoryLayout, 264 | (buf_size, buf_ptr): (u32, *mut __gl::types::GLvoid), 265 | ) { 266 | self.set_pixel_pack_params(&layout); 267 | let (offset, extent) = Self::map_subresource_region(image, &subresource, offset, extent); 268 | self.0.GetTextureSubImage( 269 | image.raw, 270 | subresource.level as _, 271 | offset.x, 272 | offset.y, 273 | offset.z, 274 | extent.width as _, 275 | extent.height as _, 276 | extent.depth as _, 277 | layout.base_format as _, 278 | layout.format_layout as _, 279 | buf_size as _, 280 | buf_ptr, 281 | ); 282 | } 283 | 284 | /// Copy image data from device memory to a host array. 285 | pub unsafe fn copy_image_to_host( 286 | &self, 287 | src_image: Image, 288 | dst_host: &mut [T], 289 | region: HostImageCopy, 290 | ) { 291 | self.unbind_pixel_pack_buffer(); 292 | self.copy_image_to( 293 | src_image, 294 | region.image_subresource, 295 | region.image_offset, 296 | region.image_extent, 297 | region.host_layout, 298 | ( 299 | (dst_host.len() * std::mem::size_of::()) as _, 300 | dst_host.as_mut_ptr() as _, 301 | ), 302 | ); 303 | } 304 | 305 | /// Copy image data from device memory to a buffer object. 306 | pub unsafe fn copy_image_to_buffer( 307 | &self, 308 | src_image: Image, 309 | dst_buffer: Buffer, 310 | region: BufferImageCopy, 311 | ) { 312 | self.bind_pixel_pack_buffer(dst_buffer); 313 | let buffer_size = self.get_buffer_size(dst_buffer) - region.buffer_offset; 314 | self.copy_image_to( 315 | src_image, 316 | region.image_subresource, 317 | region.image_offset, 318 | region.image_extent, 319 | region.buffer_layout, 320 | (buffer_size as _, region.buffer_offset as _), 321 | ); 322 | } 323 | 324 | /// Read a region of pixel data from the current read framebuffer 325 | /// into the provided storage slice. 326 | /// 327 | /// # Remarks: 328 | /// 329 | /// The transfer in `copy_attachement_to_host` is done 330 | /// synchronously; the method won't return until the transfer is 331 | /// complete. 332 | /// 333 | /// # See also 334 | /// 335 | /// * [copy_attachement_to_buffer](struct.Device.html#method.copy_attachment_to_buffer) 336 | /// for an asynchronous alternative. 337 | pub unsafe fn copy_attachment_to_host( 338 | &self, 339 | region: Region, 340 | layout: MemoryLayout, 341 | data: &mut [T], 342 | ) { 343 | self.set_pixel_pack_params(&layout); 344 | self.unbind_pixel_pack_buffer(); 345 | self.0.ReadnPixels( 346 | region.x, 347 | region.y, 348 | region.w as _, 349 | region.h as _, 350 | layout.base_format as _, 351 | layout.format_layout as _, 352 | (data.len() * std::mem::size_of::()) as _, 353 | data.as_mut_ptr() as _, 354 | ); 355 | } 356 | 357 | /// Read a region of pixel data from the current read framebuffer 358 | /// into a buffer object. 359 | /// 360 | /// # Remarks: 361 | /// 362 | /// The transfer for `copy_attachment_to_buffer` is asynchronous. 363 | pub unsafe fn copy_attachment_to_buffer( 364 | &self, 365 | region: Region, 366 | layout: MemoryLayout, 367 | buffer_range: BufferRange, 368 | ) { 369 | self.set_pixel_pack_params(&layout); 370 | self.bind_pixel_pack_buffer(buffer_range.buffer); 371 | self.0.ReadnPixels( 372 | region.x, 373 | region.y, 374 | region.w as _, 375 | region.h as _, 376 | layout.base_format as _, 377 | layout.format_layout as _, 378 | buffer_range.size as _, 379 | buffer_range.offset as _, 380 | ); 381 | } 382 | 383 | pub unsafe fn copy_image(&self, src_image: Image, dst_image: Image, region: ImageCopy) { 384 | let (src_offset, _) = Self::map_subresource_region( 385 | src_image, 386 | ®ion.src_subresource, 387 | region.src_offset, 388 | region.extent, 389 | ); 390 | let (dst_offset, extent) = Self::map_subresource_region( 391 | dst_image, 392 | ®ion.dst_subresource, 393 | region.dst_offset, 394 | region.extent, 395 | ); 396 | self.0.CopyImageSubData( 397 | src_image.raw, 398 | src_image.target, 399 | region.src_subresource.level as _, 400 | src_offset.x, 401 | src_offset.y, 402 | src_offset.z, 403 | dst_image.raw, 404 | dst_image.target, 405 | region.dst_subresource.level as _, 406 | dst_offset.x, 407 | dst_offset.y, 408 | dst_offset.z, 409 | extent.width as _, 410 | extent.height as _, 411 | extent.depth as _, 412 | ); 413 | } 414 | 415 | /// Copy data from one buffer into another buffer. 416 | /// 417 | /// # Valid usage 418 | /// 419 | /// - `src_buffer` and `dst_buffer` **must** be valid handles. 420 | /// - `src_offset` **must** be less than the size of `src_buffer`. 421 | /// - `dst_offset` **must** be less than the size of `dst_buffer`. 422 | /// - `size` **must** be less than or equal the size of `src_buffer` minus `src_offset`. 423 | /// - `size` **must** be less than or equal the size of `dst_buffer` minus `dst_offset`. 424 | /// - The source and destination region **must** not overlap in memory. 425 | pub unsafe fn copy_buffer( 426 | &self, 427 | src_buffer: Buffer, 428 | src_offset: u64, 429 | dst_buffer: Buffer, 430 | dst_offset: u64, 431 | size: u64, 432 | ) { 433 | self.0.CopyNamedBufferSubData( 434 | src_buffer.0, 435 | dst_buffer.0, 436 | src_offset as _, 437 | dst_offset as _, 438 | size as _, 439 | ); 440 | } 441 | 442 | /// Fill a region of a buffer with a fixed value 443 | pub unsafe fn fill_buffer(&self, buffer: BufferRange, value: u32) { 444 | let size = if buffer.size == WHOLE_SIZE { 445 | ((self.get_buffer_size(buffer.buffer) - buffer.offset as u64) & !0x3) as _ 446 | } else { 447 | buffer.size as _ 448 | }; 449 | 450 | self.0.ClearNamedBufferSubData( 451 | buffer.buffer.0, 452 | __gl::R32UI, 453 | buffer.offset as _, 454 | size, 455 | __gl::RED, 456 | __gl::UNSIGNED_INT, 457 | &value as *const _ as _, 458 | ); 459 | } 460 | } 461 | -------------------------------------------------------------------------------- /src/command.rs: -------------------------------------------------------------------------------- 1 | //! Drawing and Dispatching related commands. 2 | 3 | use crate::__gl; 4 | use crate::{Device, Filter, Framebuffer, Pipeline, Region}; 5 | use std::{mem, ops::Range}; 6 | 7 | /// Primitve topology. 8 | /// 9 | /// Specifies how the input assembler (fixed-function) of the graphics pipeline will 10 | /// assemble primitives based on the incoming vertex data. 11 | #[repr(u32)] 12 | #[derive(Copy, Clone, Debug)] 13 | pub enum Primitive { 14 | /// Independent vertex points. 15 | /// 16 | /// One vertex corresponds to one point. The size of a point can be changed 17 | /// dynamically in the vertex, geometry or tessellation evaluation stage. 18 | /// A point is rendered as square. 19 | /// 20 | /// GLSL: `gl_PointSize`. 21 | Points = __gl::POINTS, 22 | 23 | /// Lines segment list. 24 | /// 25 | /// Every two consecutive vertices will form a line segment. 26 | Lines = __gl::LINES, 27 | 28 | /// Lines segment strip. 29 | /// 30 | /// The vertices will build a connected list of line segments. 31 | LineStrip = __gl::LINE_STRIP, 32 | 33 | /// Lines segment loop. 34 | /// 35 | /// The vertices will build a connected list of line segments, 36 | /// where the last vertex also connects to the first. 37 | LineLoop = __gl::LINE_LOOP, 38 | 39 | /// Triangle list. 40 | /// 41 | /// Three consecutive vertices will form triangle. 42 | Triangles = __gl::TRIANGLES, 43 | 44 | /// Triangle strip. 45 | /// 46 | /// A series of connected triangles. The first three vertices form 47 | /// the first triangle, and each subsequent version forms a 48 | /// triangle along with the previous two verices. The ordering of 49 | /// the vertices is swapped on alternating triangles so that the 50 | /// winding direction is consistent among all triangles in the 51 | /// strip. 52 | TriangleStrip = __gl::TRIANGLE_STRIP, 53 | LinesAdjacency = __gl::LINES_ADJACENCY, 54 | LinesStripAdjacency = __gl::LINE_STRIP_ADJACENCY, 55 | 56 | /// Triangle list with adjacency information. 57 | /// 58 | /// Six vertices specify each triangle, along with information 59 | /// about vertices adjacent to that triangle. If vertices (0, 1, 60 | /// 2, 3, 4, 5) are specified, then: 61 | /// 62 | /// * (0, 2, 4) form the vertices of the actual triangle. 63 | /// * (0, 1, 2), (2, 3, 4), and (4, 5, 0) would form triangles 64 | /// that are adjacent to the main triangle. 65 | /// 66 | /// Only geometry shaders have access to the adjacent vertices. 67 | TrianglesAdjacency = __gl::TRIANGLES_ADJACENCY, 68 | TrianglesStripAdjacency = __gl::TRIANGLE_STRIP_ADJACENCY, 69 | Patches = __gl::PATCHES, 70 | } 71 | 72 | /// Index size. 73 | /// 74 | /// Specifies the size of indices during indexed draw calls. 75 | #[repr(u32)] 76 | #[derive(Copy, Clone, Debug)] 77 | pub enum IndexTy { 78 | /// 8-bit unsigned integer. 79 | U8 = __gl::UNSIGNED_BYTE, 80 | // 16-bit unsigned integer. 81 | U16 = __gl::UNSIGNED_SHORT, 82 | // 32-bit unsigned integer. 83 | U32 = __gl::UNSIGNED_INT, 84 | } 85 | 86 | impl IndexTy { 87 | fn size(self) -> u32 { 88 | match self { 89 | IndexTy::U8 => 1, 90 | IndexTy::U16 => 2, 91 | IndexTy::U32 => 4, 92 | } 93 | } 94 | } 95 | 96 | /// Viewport transformation. 97 | /// 98 | /// Viewport transformation is part of the fixed-function Vertex Post-Processing pipeline. 99 | /// The returning vertex positions (GLSL: `gl_Position`) in clip space are transformed to 100 | /// normalized device coordinates (NDC) by perspective division. Viewport transformation 101 | /// further converts the NDC into framebuffer coordinates. 102 | /// 103 | /// During the geometry a primitive will be assigned a viewport index (GLSL: `gl_ViewportIndex`) 104 | /// either automatically or manually. This index controls which viewport from the bounded 105 | /// viewports will be selected for applying the transformation for the current **primitive**. 106 | pub struct Viewport { 107 | /// Offset (x). 108 | pub x: f32, 109 | /// Offset (y). 110 | pub y: f32, 111 | /// Width. 112 | pub w: f32, 113 | /// Height. 114 | pub h: f32, 115 | // Near. 116 | pub n: f64, 117 | // Far. 118 | pub f: f64, 119 | } 120 | 121 | /// Uniform constant. 122 | /// 123 | /// Small values which can be written directly by the API. 124 | /// No additional buffers and binding calls are required. 125 | /// 126 | /// ## Example 127 | /// 128 | /// GLSL: `layout (location = 0) uniform mat4 u_perspective;` 129 | pub enum Constant { 130 | /// 32-bit single precision floating point. 131 | F32(f32), 132 | /// 2 elements single precision floating point vector. 133 | Vec2([f32; 2]), 134 | /// 2 elements 32-bit unsigned integer vector. 135 | UVec2([u32; 2]), 136 | /// 2 elements 32-bit signed integer vector. 137 | IVec2([i32; 2]), 138 | /// 3 elements single precision floating point vector. 139 | Vec3([f32; 3]), 140 | /// 4 elements single precision floating point vector. 141 | Vec4([f32; 4]), 142 | /// 2x2 elements single precision floating point matrix. 143 | Mat2x2([[f32; 2]; 2]), 144 | /// 3x3 elements single precision floating point matrix. 145 | Mat3x3([[f32; 3]; 3]), 146 | /// 4x4 elements single precision floating point matrix. 147 | Mat4x4([[f32; 4]; 4]), 148 | /// 32-bit signed integer 149 | I32(i32), 150 | /// 2 elements 32-bit signed integer. 151 | Ivec2([i32; 2]), 152 | /// 3 elements 32-bit signed integer. 153 | Ivec3([i32; 3]), 154 | /// 4 elements 32-bit signed integer. 155 | Ivec4([i32; 4]), 156 | /// 32-bit unsigned integer 157 | U32(u32), 158 | /// 2 elements 32-bit unsigned integer. 159 | Uvec2([u32; 2]), 160 | /// 3 elements 32-bit unsigned integer. 161 | Uvec3([u32; 3]), 162 | /// 4 elements 32-bit unsigned integer. 163 | Uvec4([u32; 4]), 164 | /// Boolean value, 165 | Bool(bool), 166 | /// 2-element boolean vector. 167 | Bvec2([bool; 2]), 168 | /// 3-element boolean vector. 169 | Bvec3([bool; 3]), 170 | /// 4-element boolean vector. 171 | Bvec4([bool; 4]), 172 | } 173 | 174 | /// Indirect draw command structure. 175 | #[repr(C)] 176 | #[derive(Copy, Clone, Debug)] 177 | pub struct DrawIndirectCmd { 178 | /// 179 | pub vertex_count: u32, 180 | /// 181 | pub instance_count: u32, 182 | /// 183 | pub first_vertex: u32, 184 | /// 185 | pub first_instance: u32, 186 | } 187 | 188 | /// Indirect (indexed) draw command structure. 189 | #[repr(C)] 190 | #[derive(Copy, Clone, Debug)] 191 | pub struct DrawIndexedIndirectCmd { 192 | /// 193 | pub index_count: u32, 194 | /// 195 | pub instance_count: u32, 196 | /// 197 | pub first_index: u32, 198 | /// 199 | pub base_vertex: i32, 200 | /// 201 | pub first_instance: u32, 202 | } 203 | 204 | /// Indirect dispatch command structure. 205 | #[repr(C)] 206 | #[derive(Copy, Clone, Debug)] 207 | pub struct DispatchIndirectCmd { 208 | /// Number of local workgroups in x dimension. 209 | pub x: u32, 210 | /// Number of local workgroups in y dimension. 211 | pub y: u32, 212 | /// Number of local workgroups in z dimension. 213 | pub z: u32, 214 | } 215 | 216 | impl Device { 217 | /// Set uniform constants for a pipeline. 218 | pub unsafe fn bind_uniform_constants( 219 | &self, 220 | pipeline: Pipeline, 221 | first: u32, 222 | constants: &[Constant], 223 | ) { 224 | for (i, constant) in constants.iter().enumerate() { 225 | let location = first as i32 + i as i32; 226 | match constant { 227 | Constant::U32(val) => { 228 | self.0.ProgramUniform1ui(pipeline.0, location, *val as _); 229 | } 230 | Constant::Uvec2(v) => { 231 | self.0.ProgramUniform2ui(pipeline.0, location, v[0], v[1]); 232 | } 233 | Constant::Uvec3(v) => { 234 | self.0 235 | .ProgramUniform3ui(pipeline.0, location, v[0], v[1], v[2]); 236 | } 237 | Constant::Uvec4(v) => { 238 | self.0 239 | .ProgramUniform4ui(pipeline.0, location, v[0], v[1], v[2], v[3]); 240 | } 241 | Constant::F32(val) => { 242 | self.0.ProgramUniform1f(pipeline.0, location, *val as _); 243 | } 244 | Constant::Vec2(v) => { 245 | self.0.ProgramUniform2f(pipeline.0, location, v[0], v[1]); 246 | } 247 | Constant::UVec2(v) => { 248 | self.0.ProgramUniform2ui(pipeline.0, location, v[0], v[1]); 249 | } 250 | Constant::IVec2(v) => { 251 | self.0.ProgramUniform2i(pipeline.0, location, v[0], v[1]); 252 | } 253 | Constant::Vec3(v) => { 254 | self.0 255 | .ProgramUniform3f(pipeline.0, location, v[0], v[1], v[2]); 256 | } 257 | Constant::Vec4(v) => { 258 | self.0 259 | .ProgramUniform4f(pipeline.0, location, v[0], v[1], v[2], v[3]); 260 | } 261 | Constant::I32(val) => { 262 | self.0.ProgramUniform1i(pipeline.0, location, *val as _); 263 | } 264 | Constant::Ivec2(v) => { 265 | self.0.ProgramUniform2i(pipeline.0, location, v[0], v[1]); 266 | } 267 | Constant::Ivec3(v) => { 268 | self.0 269 | .ProgramUniform3i(pipeline.0, location, v[0], v[1], v[2]); 270 | } 271 | Constant::Ivec4(v) => { 272 | self.0 273 | .ProgramUniform4i(pipeline.0, location, v[0], v[1], v[2], v[3]); 274 | } 275 | Constant::Bool(val) => { 276 | self.0.ProgramUniform1i(pipeline.0, location, *val as _); 277 | } 278 | Constant::Bvec2(v) => { 279 | self.0 280 | .ProgramUniform2i(pipeline.0, location, v[0] as _, v[1] as _); 281 | } 282 | Constant::Bvec3(v) => { 283 | self.0 284 | .ProgramUniform3i(pipeline.0, location, v[0] as _, v[1] as _, v[2] as _); 285 | } 286 | Constant::Bvec4(v) => { 287 | self.0.ProgramUniform4i( 288 | pipeline.0, location, v[0] as _, v[1] as _, v[2] as _, v[3] as _, 289 | ); 290 | } 291 | Constant::Mat2x2(mat) => { 292 | self.0.ProgramUniformMatrix2fv( 293 | pipeline.0, 294 | location, 295 | 1, 296 | __gl::FALSE, 297 | mat.as_ptr() as *const _, 298 | ); 299 | } 300 | Constant::Mat3x3(mat) => { 301 | self.0.ProgramUniformMatrix3fv( 302 | pipeline.0, 303 | location, 304 | 1, 305 | __gl::FALSE, 306 | mat.as_ptr() as *const _, 307 | ); 308 | } 309 | Constant::Mat4x4(mat) => { 310 | self.0.ProgramUniformMatrix4fv( 311 | pipeline.0, 312 | location, 313 | 1, 314 | __gl::FALSE, 315 | mat.as_ptr() as *const _, 316 | ); 317 | } 318 | } 319 | } 320 | } 321 | 322 | /// Set viewport transformation parameters. 323 | /// 324 | /// The viewport determines the mapping from NDC (normalized device coordinates) 325 | /// into framebuffer coordinates. 326 | /// 327 | /// See [Viewport](../command/struct.Viewport.html) for more information 328 | /// about the viewport transformation. 329 | pub unsafe fn set_viewport(&self, first: u32, viewports: &[Viewport]) { 330 | let rects = viewports 331 | .iter() 332 | .flat_map(|viewport| vec![viewport.x, viewport.y, viewport.w, viewport.h]) 333 | .collect::>(); 334 | 335 | self.0 336 | .ViewportArrayv(first, viewports.len() as _, rects.as_ptr()); 337 | 338 | let depth_ranges = viewports 339 | .iter() 340 | .flat_map(|viewport| vec![viewport.n, viewport.f]) 341 | .collect::>(); 342 | 343 | self.0 344 | .DepthRangeArrayv(first, viewports.len() as _, depth_ranges.as_ptr()); 345 | } 346 | 347 | /// Set scissor rectangles for viewports. 348 | /// 349 | /// # Valid usage 350 | /// 351 | /// - Every active viewport needs an associated scissor. 352 | pub unsafe fn set_scissor(&self, first: u32, scissors: &[Region]) { 353 | let scissors_raw = scissors 354 | .iter() 355 | .flat_map(|scissor| vec![scissor.x, scissor.y, scissor.w, scissor.h]) 356 | .collect::>(); 357 | 358 | self.0 359 | .ScissorArrayv(first, scissors.len() as _, scissors_raw.as_ptr()); 360 | } 361 | 362 | /// Set depth bias factors. 363 | pub unsafe fn set_depth_bias(&self, constant_factor: f32, slope_factor: f32) { 364 | self.0.PolygonOffset(slope_factor, constant_factor); 365 | } 366 | 367 | /// Submit a (non-indexed) draw call. 368 | /// 369 | /// # Valid usage 370 | /// 371 | /// - There must be a valid graphics pipeline currently bound. 372 | /// - There must be a valid vertex array currently bound. 373 | /// - For each attribute in the bound vertex array there must be a vertex buffer bound 374 | /// at the specified binding slot. 375 | /// - For each attribute in the bound vertex array there must be a vertex attribute 376 | /// specified in the shader with matching format and location. 377 | /// - The access vertices must be in bound of the vertex buffers bound. 378 | /// - `vertices.end` must be larger than `vertices.start`. 379 | /// - `vertices.end - vertices.start` must be allow assembling complete primitives. 380 | /// - `instances.end` must be larger than `instances.start`. 381 | pub unsafe fn draw(&self, primitive: Primitive, vertices: Range, instance: Range) { 382 | self.0.DrawArraysInstancedBaseInstance( 383 | primitive as _, 384 | vertices.start as _, 385 | (vertices.end - vertices.start) as _, 386 | (instance.end - instance.start) as _, 387 | instance.start as _, 388 | ); 389 | } 390 | 391 | /// Submit an indexed draw call. 392 | /// 393 | /// # Valid usage 394 | /// 395 | /// - There must be a valid graphics pipeline currently bound. 396 | /// - There must be a valid vertex array currently bound. 397 | /// - For each attribute in the bound vertex array there must be a vertex buffer bound 398 | /// at the specified binding slot. 399 | /// - For each attribute in the bound vertex array there must be a vertex attribute 400 | /// specified in the shader with matching format and location. 401 | /// - The access vertices must be in bound of the vertex buffers bound. 402 | /// - `indices.end` must be larger than `indices.start`. 403 | /// - `indices.end - indices.start` must allow to assemble complete primitives. 404 | /// - `instances.end` must be larger than `instances.start`. 405 | pub unsafe fn draw_indexed( 406 | &self, 407 | primitive: Primitive, 408 | index_ty: IndexTy, 409 | indices: Range, 410 | instance: Range, 411 | base_vertex: i32, 412 | ) { 413 | self.0.DrawElementsInstancedBaseVertexBaseInstance( 414 | primitive as _, 415 | (indices.end - indices.start) as _, 416 | index_ty as _, 417 | (index_ty.size() * indices.start) as _, 418 | (instance.end - instance.start) as _, 419 | base_vertex, 420 | instance.start as _, 421 | ); 422 | } 423 | 424 | /// Submit an indirect draw call. 425 | /// 426 | /// # Valid Usage 427 | /// 428 | /// - There must be a valid graphics pipeline currently bound. 429 | /// - There must be a valid draw indirect buffer currently bound. 430 | pub unsafe fn draw_indirect(&self, primitive: Primitive, offset: u64, count: u32, stride: u32) { 431 | self.0 432 | .MultiDrawArraysIndirect(primitive as _, offset as _, count as _, stride as _); 433 | } 434 | 435 | /// Submit an indirect draw call. 436 | pub unsafe fn draw_indirect_from_host(&self, primitive: Primitive, data: &[DrawIndirectCmd]) { 437 | self.0.MultiDrawArraysIndirect( 438 | primitive as _, 439 | data.as_ptr() as *const _, 440 | data.len() as _, 441 | mem::size_of::() as _, 442 | ); 443 | } 444 | 445 | /// Indirect draw call. 446 | pub unsafe fn draw_indexed_indirect( 447 | &self, 448 | primitive: Primitive, 449 | index_ty: IndexTy, 450 | offset: u64, 451 | count: u32, 452 | stride: u32, 453 | ) { 454 | self.0.MultiDrawElementsIndirect( 455 | primitive as _, 456 | index_ty as _, 457 | offset as _, 458 | count as _, 459 | stride as _, 460 | ); 461 | } 462 | 463 | /// Indirect (indexed) draw call. 464 | pub unsafe fn draw_indexed_indirect_from_host( 465 | &self, 466 | primitive: Primitive, 467 | index_ty: IndexTy, 468 | data: &[DrawIndexedIndirectCmd], 469 | ) { 470 | self.0.MultiDrawElementsIndirect( 471 | primitive as _, 472 | index_ty as _, 473 | data.as_ptr() as *const _, 474 | data.len() as _, 475 | mem::size_of::() as _, 476 | ); 477 | } 478 | 479 | /// Dispatch a workgroup for computation. 480 | /// 481 | /// # Valid usage 482 | /// 483 | /// - There must be a valid compute shader currently bound. 484 | pub unsafe fn dispatch(&self, x: u32, y: u32, z: u32) { 485 | self.0.DispatchCompute(x, y, z); 486 | } 487 | 488 | /// 489 | pub unsafe fn dispatch_indirect(&self, offset: u64) { 490 | self.0.DispatchComputeIndirect(offset as _); 491 | } 492 | 493 | /// 494 | pub unsafe fn blit( 495 | &self, 496 | src: Framebuffer, 497 | src_region: Region, 498 | dst: Framebuffer, 499 | dst_region: Region, 500 | filter: Filter, 501 | ) { 502 | self.0.BlitNamedFramebuffer( 503 | src.0, 504 | dst.0, 505 | src_region.x, 506 | src_region.x, 507 | src_region.w, 508 | src_region.h, 509 | dst_region.x, 510 | dst_region.x, 511 | dst_region.w, 512 | dst_region.h, 513 | __gl::COLOR_BUFFER_BIT, 514 | filter as _, 515 | ); 516 | } 517 | 518 | /// 519 | pub unsafe fn draw_mesh_tasks_nv(&self, task_count: u32, first_task: u32) { 520 | self.0.DrawMeshTasksNV(first_task, task_count); 521 | } 522 | 523 | /// 524 | pub unsafe fn draw_mesh_tasks_indirect_nv(&self, offset: u64, draw_count: u32, stride: u32) { 525 | self.0 526 | .MultiDrawMeshTasksIndirectNV(offset as _, draw_count as _, stride as _); 527 | } 528 | 529 | /// 530 | pub unsafe fn draw_mesh_tasks_indirect_count_nv( 531 | &self, 532 | offset: u64, 533 | count_buffer_offset: u64, 534 | max_draw_count: u32, 535 | stride: u32, 536 | ) { 537 | self.0.MultiDrawMeshTasksIndirectCountNV( 538 | offset as _, 539 | count_buffer_offset as _, 540 | max_draw_count as _, 541 | stride as _, 542 | ); 543 | } 544 | } 545 | --------------------------------------------------------------------------------