Browse Source

Implemented ArrayBuffer and first basic system

raster
Bergmann89 4 years ago
parent
commit
62c621d645
14 changed files with 543 additions and 196 deletions
  1. +1
    -1
      async-ecs
  2. +267
    -0
      glc/src/array_buffer.rs
  3. +17
    -0
      glc/src/error.rs
  4. +2
    -0
      glc/src/lib.rs
  5. +3
    -0
      glc/src/misc.rs
  6. +85
    -152
      glc/src/shader.rs
  7. +1
    -1
      space-crush/Cargo.toml
  8. +14
    -8
      space-crush/src/app.rs
  9. +34
    -0
      space-crush/src/error.rs
  10. +6
    -0
      space-crush/src/lib.rs
  11. +11
    -34
      space-crush/src/main.rs
  12. +82
    -0
      space-crush/src/render/background.rs
  13. +3
    -0
      space-crush/src/render/mod.rs
  14. +17
    -0
      space-crush/src/render/shader/noise.frag

+ 1
- 1
async-ecs

@@ -1 +1 @@
Subproject commit 4a29fa124c3dfde75949d606d014de1cd554e923
Subproject commit b2e25967b55b968d8955d285a67fceaa6a257271

+ 267
- 0
glc/src/array_buffer.rs View File

@@ -0,0 +1,267 @@
use std::mem::size_of;
use std::ops::{Deref, DerefMut};
use std::ptr::null;
use std::slice::from_raw_parts_mut;

use crate::{error::Error, misc::AsEnum};

/* ArrayBuffer */

pub struct ArrayBuffer {
id: gl::GLuint,
target: Target,
size: usize,
}

impl ArrayBuffer {
pub fn new(target: Target) -> Result<Self, Error> {
let mut id = 0;

Error::checked(|| gl::create_buffers(1, &mut id))?;

Ok(Self {
id,
target,
size: 0,
})
}

pub fn id(&self) -> gl::GLuint {
self.id
}

pub fn bind(&self) {
gl::bind_buffer(self.target.as_enum(), self.id);
}

pub fn unbind(&self) {
gl::bind_buffer(self.target.as_enum(), 0);
}

pub fn buffer_data<T>(&mut self, usage: Usage, data: &[T]) -> Result<(), Error> {
let size = data.len() * size_of::<T>();

Error::checked(|| {
gl::named_buffer_data(
self.id,
size as gl::GLsizeiptr,
data.as_ptr() as *const _,
usage.as_enum(),
)
})?;

self.size = size;

Ok(())
}

pub fn buffer_size(&mut self, usage: Usage, size: usize) -> Result<(), Error> {
Error::checked(|| {
gl::named_buffer_data(self.id, size as gl::GLsizeiptr, null(), usage.as_enum())
})?;

self.size = size;

Ok(())
}

pub fn map<T>(&self) -> Result<Map<'_, T>, Error> {
Ok(Map {
data: self.inner_map(gl::READ_ONLY)?,
buf: self,
})
}

pub fn map_mut<T>(&mut self, write_only: bool) -> Result<MapMut<'_, T>, Error> {
Ok(MapMut {
data: self.inner_map(if write_only {
gl::WRITE_ONLY
} else {
gl::READ_WRITE
})?,
buf: self,
})
}

pub fn map_range<T>(
&self,
byte_offset: usize,
value_count: Option<usize>,
) -> Result<Map<'_, T>, Error> {
Ok(Map {
data: self.inner_map_range(gl::READ_ONLY, byte_offset, value_count)?,
buf: self,
})
}

pub fn map_range_mut<T>(
&mut self,
write_only: bool,
byte_offset: usize,
value_count: Option<usize>,
) -> Result<MapMut<'_, T>, Error> {
let access = if write_only {
gl::WRITE_ONLY
} else {
gl::READ_WRITE
};

Ok(MapMut {
data: self.inner_map_range(access, byte_offset, value_count)?,
buf: self,
})
}

fn inner_map<T>(&self, access: gl::GLenum) -> Result<&mut [T], Error> {
let ptr = Error::err_if(&null(), gl::map_named_buffer(self.id, access))?;
let count = self.size / size_of::<T>();

Ok(unsafe { from_raw_parts_mut(ptr as *mut T, count) })
}

pub fn inner_map_range<T>(
&self,
access: gl::GLenum,
byte_offset: usize,
value_count: Option<usize>,
) -> Result<&mut [T], Error> {
let count = value_count.unwrap_or_else(|| {
if self.size > byte_offset {
(self.size - byte_offset) / size_of::<T>()
} else {
0
}
});

if byte_offset + count * size_of::<T>() > self.size {
return Err(Error::InvalidParameter);
}

let size = count * size_of::<T>();
let ptr = Error::err_if(
&null(),
gl::map_named_buffer_range(self.id, byte_offset as isize, size as isize, access),
)?;

Ok(unsafe { from_raw_parts_mut(ptr as *mut T, count) })
}
}

/* Map */

pub struct Map<'a, T> {
buf: &'a ArrayBuffer,
data: &'a [T],
}

impl<T> Drop for Map<'_, T> {
fn drop(&mut self) {
gl::unmap_named_buffer(self.buf.id);
}
}

impl<T> Deref for Map<'_, T> {
type Target = [T];

fn deref(&self) -> &Self::Target {
self.data
}
}

/* MapMut */

pub struct MapMut<'a, T> {
buf: &'a ArrayBuffer,
data: &'a mut [T],
}

impl<T> Drop for MapMut<'_, T> {
fn drop(&mut self) {
gl::unmap_named_buffer(self.buf.id);
}
}

impl<T> Deref for MapMut<'_, T> {
type Target = [T];

fn deref(&self) -> &Self::Target {
self.data
}
}

impl<T> DerefMut for MapMut<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.data
}
}

/* Target */

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Target {
ArrayBuffer,
AtomicCounterBuffer,
CopyReadBuffer,
CopyWriteBuffer,
DispatchIndirectBuffer,
DrawIndirectBuffer,
ElementArrayBuffer,
PixelPackBuffer,
PixelUnpackBuffer,
QueryBuffer,
ShaderStorageBuffer,
TextureBuffer,
TransformFeedbackBuffer,
UniformBuffer,
}

impl AsEnum for Target {
fn as_enum(&self) -> gl::GLenum {
match self {
Self::ArrayBuffer => gl::ARRAY_BUFFER,
Self::AtomicCounterBuffer => gl::ATOMIC_COUNTER_BUFFER,
Self::CopyReadBuffer => gl::COPY_READ_BUFFER,
Self::CopyWriteBuffer => gl::COPY_WRITE_BUFFER,
Self::DispatchIndirectBuffer => gl::DISPATCH_INDIRECT_BUFFER,
Self::DrawIndirectBuffer => gl::DRAW_INDIRECT_BUFFER,
Self::ElementArrayBuffer => gl::ELEMENT_ARRAY_BUFFER,
Self::PixelPackBuffer => gl::PIXEL_PACK_BUFFER,
Self::PixelUnpackBuffer => gl::PIXEL_UNPACK_BUFFER,
Self::QueryBuffer => gl::QUERY_BUFFER,
Self::ShaderStorageBuffer => gl::SHADER_STORAGE_BUFFER,
Self::TextureBuffer => gl::TEXTURE_BUFFER,
Self::TransformFeedbackBuffer => gl::TRANSFORM_FEEDBACK_BUFFER,
Self::UniformBuffer => gl::UNIFORM_BUFFER,
}
}
}

/* Usage */

pub enum Usage {
StreamDraw,
StreamRead,
StreamCopy,
StaticDraw,
StaticRead,
StatidCopy,
DynamicDraw,
DynamicRead,
DynamicCopy,
}

impl AsEnum for Usage {
fn as_enum(&self) -> gl::GLenum {
match self {
Self::StreamDraw => gl::STREAM_DRAW,
Self::StreamRead => gl::STREAM_READ,
Self::StreamCopy => gl::STREAM_COPY,
Self::StaticDraw => gl::STATIC_DRAW,
Self::StaticRead => gl::STATIC_READ,
Self::StatidCopy => gl::STATIC_COPY,
Self::DynamicDraw => gl::DYNAMIC_DRAW,
Self::DynamicRead => gl::DYNAMIC_READ,
Self::DynamicCopy => gl::DYNAMIC_COPY,
}
}
}

+ 17
- 0
glc/src/error.rs View File

@@ -19,6 +19,9 @@ pub enum Error {

#[error("Error while linking shader program: {0}")]
ShaderLink(String),

#[error("Invalid Parameter!")]
InvalidParameter,
}

impl Error {
@@ -32,6 +35,20 @@ impl Error {
Ok(value)
}
}

pub fn checked<F, T>(f: F) -> Result<T, Self>
where
F: FnOnce() -> T,
{
gl::get_error();

let ret = f();

match gl::get_error() {
0 => Ok(ret),
err => Err(Self::GlError(err)),
}
}
}

impl From<IoError> for Error {


+ 2
- 0
glc/src/lib.rs View File

@@ -1,2 +1,4 @@
pub mod array_buffer;
pub mod error;
pub mod misc;
pub mod shader;

+ 3
- 0
glc/src/misc.rs View File

@@ -0,0 +1,3 @@
pub trait AsEnum {
fn as_enum(&self) -> gl::GLenum;
}

+ 85
- 152
glc/src/shader.rs View File

@@ -1,99 +1,54 @@
use std::fs::read_to_string;
use std::iter::FromIterator;
use std::path::Path;
use std::ptr::{null, null_mut};
use std::str::from_utf8;

use crate::error::Error;
use crate::{error::Error, misc::AsEnum};

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Type {
Vertex,
Compute,
Fragment,
Geometry,
TesslationControl,
TesslationEvaluation,
}

/* Shader */
/* Programm */

pub struct Shader {
id: Option<gl::GLuint>,
type_: Type,
source: String,
compiled: bool,
#[derive(Default)]
pub struct Program {
id: gl::GLuint,
}

impl Shader {
pub fn from_string(type_: Type, source: String) -> Self {
Self {
id: None,
type_,
source,
compiled: false,
}
impl Program {
pub fn from_shaders<I>(iter: I) -> Result<Self, Error>
where
I: IntoIterator<Item = Shader>,
{
Self::from_shaders_result(iter.into_iter().map(Ok))
}

pub fn from_file<P>(type_: Type, path: P) -> Result<Self, Error>
pub fn from_shaders_result<I>(iter: I) -> Result<Self, Error>
where
P: AsRef<Path>,
I: IntoIterator<Item = Result<Shader, Error>>,
{
let source = read_to_string(path)?;
let id = gl::create_program();
let id = Error::err_if(&0, id)?;

Ok(Self::from_string(type_, source))
}

pub fn id(&mut self) -> Result<gl::GLuint, Error> {
if self.id.is_none() {
let type_ = match self.type_ {
Type::Vertex => gl::VERTEX_SHADER,
Type::Compute => gl::COMPUTE_SHADER,
Type::Fragment => gl::FRAGMENT_SHADER,
Type::Geometry => gl::GEOMETRY_SHADER,
Type::TesslationControl => gl::TESS_CONTROL_SHADER,
Type::TesslationEvaluation => gl::TESS_EVALUATION_SHADER,
};

let id = gl::create_shader(type_);
let id = Error::err_if(&0, id)?;
self.id = Some(id)
let shaders = iter.into_iter().collect::<Result<Vec<_>, _>>()?;
for shader in &shaders {
gl::attach_shader(id, shader.id());
}

Ok(self.id.unwrap())
}
gl::link_program(id);

pub fn id_opt(&self) -> Option<gl::GLuint> {
self.id
}

pub fn type_(&self) -> Type {
self.type_
}

pub fn compile(&mut self) -> Result<(), Error> {
if self.compiled {
return Ok(());
for shader in &shaders {
gl::detach_shader(id, shader.id());
}

let id = self.id()?;
let source = self.source.as_ptr() as *const i8;
let source: *const *const i8 = &source;

gl::shader_source(id, 1, source, null());
gl::compile_shader(id);

let mut success = 1;
gl::get_shader_iv(id, gl::COMPILE_STATUS, &mut success);
gl::get_program_iv(id, gl::LINK_STATUS, &mut success);

if success != 1 {
let mut len = 0;
gl::get_shader_iv(id, gl::INFO_LOG_LENGTH, &mut len);
gl::get_program_iv(id, gl::INFO_LOG_LENGTH, &mut len);

let mut buffer = Vec::<u8>::with_capacity(len as usize + 1);
buffer.resize(len as usize, 0);

gl::get_shader_info_log(
gl::get_program_info_log(
id,
len,
null_mut(),
@@ -102,85 +57,59 @@ impl Shader {

let msg = from_utf8(&buffer)?;

return Err(Error::ShaderCompile(msg.into()));
}

self.compiled = true;

Ok(())
}
}

impl Drop for Shader {
fn drop(&mut self) {
if let Some(id) = &self.id {
gl::delete_shader(*id);
}
}
}

/* Programm */

#[derive(Default)]
pub struct Program {
id: Option<gl::GLuint>,
shaders: Vec<Shader>,
compiled: bool,
}

impl Program {
pub fn id(&mut self) -> Result<gl::GLuint, Error> {
if self.id.is_none() {
let id = gl::create_program();
let id = Error::err_if(&0, id)?;
self.id = Some(id)
return Err(Error::ShaderLink(msg.into()));
}

Ok(self.id.unwrap())
Ok(Program { id })
}

pub fn id_opt(&self) -> Option<gl::GLuint> {
pub fn id(&self) -> gl::GLuint {
self.id
}

pub fn bind(&self) {
if let Some(id) = &self.id {
gl::use_program(*id);
} else {
gl::use_program(0);
}
gl::use_program(self.id);
}

pub fn unbind(&self) {
gl::use_program(0);
}
}

pub fn link(&mut self, keep_shaders: bool) -> Result<(), Error> {
if self.compiled {
return Ok(());
}
impl Drop for Program {
fn drop(&mut self) {
gl::delete_program(self.id);
}
}

let id = self.id()?;
/* Shader */

for shader in &mut self.shaders {
shader.compile()?;
}
pub struct Shader {
id: gl::GLuint,
}

for shader in &mut self.shaders {
gl::attach_shader(id, shader.id()?);
}
impl Shader {
pub fn from_string(type_: Type, source: String) -> Result<Self, Error> {
let id = gl::create_shader(type_.as_enum());
let id = Error::err_if(&0, id)?;

let source = source.as_ptr() as *const i8;
let source: *const *const i8 = &source;

gl::shader_source(id, 1, source, null());
gl::compile_shader(id);

let mut success = 1;
gl::get_program_iv(id, gl::LINK_STATUS, &mut success);
gl::get_shader_iv(id, gl::COMPILE_STATUS, &mut success);

if success != 1 {
let mut len = 0;
gl::get_program_iv(id, gl::INFO_LOG_LENGTH, &mut len);
gl::get_shader_iv(id, gl::INFO_LOG_LENGTH, &mut len);

let mut buffer = Vec::<u8>::with_capacity(len as usize + 1);
buffer.resize(len as usize, 0);

gl::get_program_info_log(
gl::get_shader_info_log(
id,
len,
null_mut(),
@@ -189,49 +118,53 @@ impl Program {

let msg = from_utf8(&buffer)?;

return Err(Error::ShaderLink(msg.into()));
return Err(Error::ShaderCompile(msg.into()));
}

for shader in &mut self.shaders {
gl::detach_shader(id, shader.id()?);
}
Ok(Self { id })
}

if !keep_shaders {
self.shaders.clear();
}
pub fn from_file<P>(type_: Type, path: P) -> Result<Self, Error>
where
P: AsRef<Path>,
{
let source = read_to_string(path)?;

self.compiled = true;
Self::from_string(type_, source)
}

Ok(())
pub fn id(&self) -> gl::GLuint {
self.id
}
}

impl Extend<Shader> for Program {
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = Shader>,
{
self.shaders.extend(iter);
impl Drop for Shader {
fn drop(&mut self) {
gl::delete_shader(self.id);
}
}

impl FromIterator<Shader> for Program {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = Shader>,
{
Self {
id: None,
shaders: iter.into_iter().collect(),
compiled: false,
}
}
/* Type */

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Type {
Vertex,
Compute,
Fragment,
Geometry,
TesslationControl,
TesslationEvaluation,
}

impl Drop for Program {
fn drop(&mut self) {
if let Some(id) = &self.id {
gl::delete_program(*id);
impl AsEnum for Type {
fn as_enum(&self) -> gl::GLenum {
match self {
Self::Vertex => gl::VERTEX_SHADER,
Self::Compute => gl::COMPUTE_SHADER,
Self::Fragment => gl::FRAGMENT_SHADER,
Self::Geometry => gl::GEOMETRY_SHADER,
Self::TesslationControl => gl::TESS_CONTROL_SHADER,
Self::TesslationEvaluation => gl::TESS_EVALUATION_SHADER,
}
}
}

+ 1
- 1
space-crush/Cargo.toml View File

@@ -10,7 +10,7 @@ async-ecs = "0.1"
async-ecs-derive = "0.1"
env_logger = "0.8"
futures = "0.3"
gl = { version = "0.1", features = [ "use_log_crate" ] }
gl = { version = "0.1", features = [ "use_log_crate", "generate_debug" ] }
glc = "0.1"
glutin = "0.25"
log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] }


+ 14
- 8
space-crush/src/app.rs View File

@@ -1,20 +1,26 @@
use async_ecs::World;
use async_ecs::{Dispatcher, World};

use crate::{render::Background, Error};

pub struct App {
world: World,
dispatcher: Dispatcher,
}

impl App {
pub fn new() -> Self {
let world = World::default();
pub fn new() -> Result<Self, Error> {
let mut world = World::default();
let dispatcher = Dispatcher::setup_builder(&mut world)
.with_local(Background::new()?, "render_background", &[])?
.build();

Self { world }
Ok(Self { world, dispatcher })
}

pub fn progress(&mut self) {
let _world = &mut self.world;
pub async fn progress(&mut self) -> Result<(), Error> {
self.dispatcher.dispatch(&self.world).await?;
self.world.maintain().await;

gl::clear_color(0.1, 0.1, 0.1, 1.0);
gl::clear(gl::COLOR_BUFFER_BIT);
Ok(())
}
}

+ 34
- 0
space-crush/src/error.rs View File

@@ -0,0 +1,34 @@
use async_ecs::dispatcher::Error as DispatcherError;
use glc::error::Error as GlcError;
use glutin::CreationError as GlutinCreationError;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
#[error("GLC Error: {0}")]
GlcError(GlcError),

#[error("ECS Dispatcher Error: {0}")]
DispatcherError(DispatcherError),

#[error("glutin Creation Error: {0}")]
GlutinCreationError(GlutinCreationError),
}

impl From<GlcError> for Error {
fn from(err: GlcError) -> Self {
Self::GlcError(err)
}
}

impl From<DispatcherError> for Error {
fn from(err: DispatcherError) -> Self {
Self::DispatcherError(err)
}
}

impl From<GlutinCreationError> for Error {
fn from(err: GlutinCreationError) -> Self {
Self::GlutinCreationError(err)
}
}

+ 6
- 0
space-crush/src/lib.rs View File

@@ -0,0 +1,6 @@
mod app;
mod error;
mod render;

pub use app::App;
pub use error::Error;

+ 11
- 34
space-crush/src/main.rs View File

@@ -1,22 +1,17 @@
mod app;

use std::io::Error as IoError;

use glutin::{
dpi::{PhysicalPosition, PhysicalSize},
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
platform::desktop::EventLoopExtDesktop,
window::WindowBuilder,
ContextBuilder, CreationError, GlProfile,
ContextBuilder, GlProfile,
};
use log::info;
use thiserror::Error;
use log::{error, info};
use tokio::{runtime::Builder, task::LocalSet};

use app::App;
use space_crush::{App, Error};

fn main() -> Result<(), Error> {
fn main() {
env_logger::builder()
.filter_level(log::LevelFilter::Trace)
.format_timestamp_nanos()
@@ -25,9 +20,12 @@ fn main() -> Result<(), Error> {
let rt = Builder::new_multi_thread()
.worker_threads(num_cpus::get() + 4)
.enable_all()
.build()?;
.build()
.unwrap();

rt.block_on(async move { LocalSet::new().run_until(run()).await })
if let Err(err) = rt.block_on(async move { LocalSet::new().run_until(run()).await }) {
error!("Error while executinr application: {}", err);
}
}

async fn run() -> Result<(), Error> {
@@ -56,7 +54,7 @@ async fn run() -> Result<(), Error> {
gl::load_with(|s| context.get_proc_address(s));

let mut run = true;
let mut app = App::new();
let mut app = App::new()?;

loop {
event_loop.run_return(|event, _target, flow_control| {
@@ -81,7 +79,7 @@ async fn run() -> Result<(), Error> {
break;
}

app.progress();
app.progress().await?;

context.swap_buffers().unwrap();
}
@@ -91,26 +89,5 @@ async fn run() -> Result<(), Error> {
Ok(())
}

#[derive(Debug, Error)]
enum Error {
#[error("IO Error: {0}")]
IoError(IoError),

#[error("OpenGL Context Error: {0}")]
OpenGlContext(CreationError),
}

impl From<IoError> for Error {
fn from(err: IoError) -> Self {
Self::IoError(err)
}
}

impl From<CreationError> for Error {
fn from(err: CreationError) -> Self {
Self::OpenGlContext(err)
}
}

const WINDOW_WIDTH: u32 = 1280;
const WINDOW_HEIGHT: u32 = 720;

+ 82
- 0
space-crush/src/render/background.rs View File

@@ -0,0 +1,82 @@
use std::mem::size_of;

use async_ecs::System;
use glc::{
array_buffer::{ArrayBuffer, Target, Usage},
shader::{Program, Shader, Type},
};

use crate::Error;

pub struct Background {
program: Program,
array_buffer: ArrayBuffer,
}

impl Background {
pub fn new() -> Result<Self, Error> {
let shaders = vec![(Type::Fragment, include_str!("shader/noise.frag"))];
let program = Program::from_shaders_result(
shaders
.into_iter()
.map(|(t, s)| Shader::from_string(t, s.into())),
)?;

let mut array_buffer = ArrayBuffer::new(Target::ArrayBuffer)?;
array_buffer.buffer_size(Usage::StaticDraw, 4 * size_of::<Vertex>())?;

{
let mut buf = array_buffer.map_mut(false)?;
buf[0] = Vertex {
x: -1.0,
y: -1.0,
s: -1.0,
t: -1.0,
};
buf[1] = Vertex {
x: 1.0,
y: -1.0,
s: 1.0,
t: -1.0,
};
buf[2] = Vertex {
x: 1.0,
y: 1.0,
s: 1.0,
t: 1.0,
};
buf[3] = Vertex {
x: -1.0,
y: 1.0,
s: -1.0,
t: 1.0,
};
}

Ok(Self {
program,
array_buffer,
})
}
}

impl<'a> System<'a> for Background {
type SystemData = ();

fn run(&mut self, (): Self::SystemData) {
let _program = &self.program;
let _array_buffer = &self.array_buffer;

gl::clear_color(0.1, 0.1, 0.1, 1.0);
gl::clear(gl::COLOR_BUFFER_BIT);
}
}

#[repr(C, packed)]
#[derive(Debug, Copy, Clone, PartialEq)]
struct Vertex {
pub x: f32,
pub y: f32,
pub s: f32,
pub t: f32,
}

+ 3
- 0
space-crush/src/render/mod.rs View File

@@ -0,0 +1,3 @@
mod background;

pub use background::Background;

+ 17
- 0
space-crush/src/render/shader/noise.frag View File

@@ -0,0 +1,17 @@
const float NOISE_RANGE = 0.05;
const float NOISE_BASE = 0.05;

float random(vec4 seed)
{
return fract(sin(dot(seed, vec4(12.9898, 78.233, 45.164, 53.1324))) * 43758.5453);
}

void main(void)
{
float rnd = random(gl_ModelViewProjectionMatrix * gl_FragCoord);
vec4 color = vec4(NOISE_BASE + NOISE_RANGE * rnd);

color.a = 1.0;

gl_FragColor = color;
}

Loading…
Cancel
Save