rafx-api 0.0.16

Rendering framework built on an extensible asset pipeline
Documentation
use crate::gles2::conversions::{Gles2BlendState, Gles2DepthStencilState, Gles2RasterizerState};
use crate::gles2::gles2_bindings::types::GLenum;
use crate::gles2::reflection::FieldIndex;
use crate::gles2::{
    LocationId, ProgramId, RafxDeviceContextGles2, RafxRootSignatureGles2, RafxShaderGles2,
};
use crate::{
    RafxComputePipelineDef, RafxDescriptorIndex, RafxGraphicsPipelineDef, RafxPipelineType,
    RafxResult, RafxRootSignature, MAX_DESCRIPTOR_SET_LAYOUTS,
};
use rafx_base::trust_cell::TrustCell;
use std::sync::Arc;

#[derive(Debug, Clone)]
pub(crate) struct Gles2Attribute {
    pub(crate) buffer_index: u32,
    pub(crate) location: u32,
    pub(crate) channel_count: u32,
    pub(crate) gl_type: GLenum,
    pub(crate) stride: u32,
    pub(crate) is_normalized: bool,
    pub(crate) byte_offset: u32,
}

#[derive(Debug)]
pub(crate) struct Gles2PipelineInfo {
    pub(crate) gl_rasterizer_state: Gles2RasterizerState,
    pub(crate) gl_depth_stencil_state: Gles2DepthStencilState,
    pub(crate) gl_blend_state: Gles2BlendState,
    pub(crate) gl_topology: GLenum,
    pub(crate) gl_attributes: Vec<Gles2Attribute>,
    pub(crate) program_id: ProgramId,
    resource_locations: Vec<Option<LocationId>>,
    uniform_field_locations: Vec<Option<LocationId>>,
    pub(crate) root_signature: RafxRootSignatureGles2,
    pub(crate) last_descriptor_updates: TrustCell<[u64; MAX_DESCRIPTOR_SET_LAYOUTS]>,
    pub(crate) last_bound_by_command_pool: TrustCell<u32>,
}

impl Gles2PipelineInfo {
    pub fn resource_location(
        &self,
        descriptor_index: RafxDescriptorIndex,
        element_index: u32,
    ) -> &Option<LocationId> {
        let descriptor = self.root_signature.descriptor(descriptor_index).unwrap();
        &self.resource_locations
            [(descriptor.first_location_index.unwrap() + element_index) as usize]
    }

    pub fn uniform_member_location(
        &self,
        field_index: FieldIndex,
    ) -> &Option<LocationId> {
        &self.uniform_field_locations[field_index.0 as usize]
    }
}

#[derive(Debug)]
pub struct RafxPipelineGles2 {
    pipeline_type: RafxPipelineType,
    // It's a RafxRootSignatureGles2, but stored as RafxRootSignature so we can return refs to it
    root_signature: RafxRootSignature,
    _shader: RafxShaderGles2,
    gl_pipeline_info: Arc<Gles2PipelineInfo>,
}

impl Drop for RafxPipelineGles2 {
    fn drop(&mut self) {
        let device_context = self
            .root_signature
            .gles2_root_signature()
            .unwrap()
            .device_context();
        device_context
            .gl_context()
            .gl_destroy_program(self.gl_pipeline_info.program_id)
            .unwrap();
    }
}

impl RafxPipelineGles2 {
    pub fn pipeline_type(&self) -> RafxPipelineType {
        self.pipeline_type
    }

    pub fn root_signature(&self) -> &RafxRootSignature {
        &self.root_signature
    }

    pub fn gl_program_id(&self) -> ProgramId {
        self.gl_pipeline_info.program_id
    }

    pub(crate) fn gl_pipeline_info(&self) -> &Arc<Gles2PipelineInfo> {
        &self.gl_pipeline_info
    }

    pub fn new_graphics_pipeline(
        device_context: &RafxDeviceContextGles2,
        pipeline_def: &RafxGraphicsPipelineDef,
    ) -> RafxResult<Self> {
        let gl_context = device_context.gl_context();
        let shader = pipeline_def.shader.gles2_shader().unwrap();

        // Create a new program so that we can customize the vertex attributes
        let program_id = gl_context.gl_create_program()?;
        gl_context.gl_attach_shader(program_id, shader.gl_vertex_shader().shader_id())?;
        gl_context.gl_attach_shader(program_id, shader.gl_fragment_shader().shader_id())?;

        let gl_root_signature = pipeline_def.root_signature.gles2_root_signature().unwrap();

        let mut gl_attributes = Vec::with_capacity(pipeline_def.vertex_layout.attributes.len());

        for attribute in &pipeline_def.vertex_layout.attributes {
            if attribute.location >= device_context.device_info().max_vertex_attribute_count {
                Err(format!(
                    "Vertex attribute location {} exceeds max of {}",
                    attribute.location,
                    device_context.device_info().max_vertex_attribute_count
                ))?;
            }

            gl_context.gl_bind_attrib_location(
                program_id,
                attribute.location,
                attribute.gl_attribute_name.as_ref().unwrap(),
            )?;

            let gl_type = attribute
                .format
                .gles2_type()
                .ok_or_else(|| format!("Unsupported format {:?}", attribute.format))?;

            let buffer = &pipeline_def.vertex_layout.buffers[attribute.buffer_index as usize];

            gl_attributes.push(Gles2Attribute {
                buffer_index: attribute.buffer_index,
                location: attribute.location,
                channel_count: attribute.format.channel_count(),
                gl_type,
                stride: buffer.stride,
                is_normalized: attribute.format.is_normalized(),
                byte_offset: attribute.byte_offset,
            });
        }

        gl_context.link_shader_program(program_id)?;

        if device_context.inner.validate_shaders {
            gl_context.validate_shader_program(program_id)?;
        }

        let mut resource_locations = Vec::with_capacity(gl_root_signature.inner.descriptors.len());
        for resource in &gl_root_signature.inner.descriptors {
            if let Some(first_location_index) = resource.first_location_index {
                for i in 0..resource.element_count {
                    let location_name = &gl_root_signature.inner.location_names
                        [(first_location_index + i) as usize];
                    resource_locations
                        .push(gl_context.gl_get_uniform_location(program_id, location_name)?);
                }
            }
        }

        let all_uniform_fields = gl_root_signature.inner.uniform_reflection.fields();
        let mut uniform_field_locations = Vec::with_capacity(all_uniform_fields.len());
        for field in all_uniform_fields {
            uniform_field_locations
                .push(gl_context.gl_get_uniform_location(program_id, &field.name)?);
        }

        let gl_topology = pipeline_def
            .primitive_topology
            .gles2_topology()
            .ok_or_else(|| {
                format!(
                    "GL ES 2.0 does not support topology {:?}",
                    pipeline_def.primitive_topology
                )
            })?;

        let mut gl_pipeline_info = Gles2PipelineInfo {
            last_bound_by_command_pool: TrustCell::new(0),
            gl_rasterizer_state: pipeline_def.rasterizer_state.into(),
            gl_depth_stencil_state: pipeline_def.depth_state.into(),
            gl_blend_state: pipeline_def.blend_state.gles2_blend_state()?,
            gl_topology,
            gl_attributes,
            program_id,
            resource_locations,
            uniform_field_locations,
            root_signature: gl_root_signature.clone(),
            last_descriptor_updates: Default::default(),
        };

        // Front face needs to be reversed because we render GL with a flipped Y axis:
        // - rafx-shader-processor uses spirv-cross to patch vertex shaders to flip the Y axis
        // - textures can be sampled using V+ in down direction (the way that DX, vulkan, metal work)
        // - we flip the image right-side-up when we do the final present
        gl_pipeline_info.gl_rasterizer_state.front_face = pipeline_def
            .rasterizer_state
            .front_face
            .gles2_front_face(true);

        Ok(RafxPipelineGles2 {
            root_signature: pipeline_def.root_signature.clone(),
            pipeline_type: RafxPipelineType::Graphics,
            _shader: shader.clone(),
            gl_pipeline_info: Arc::new(gl_pipeline_info),
        })
    }

    pub fn new_compute_pipeline(
        _device_context: &RafxDeviceContextGles2,
        _pipeline_def: &RafxComputePipelineDef,
    ) -> RafxResult<Self> {
        unimplemented!("GL ES 2.0 does not support compute pipelines");
    }
}