Skip to content

Commit

Permalink
Associate texture parameters in shaders with specific texture units, …
Browse files Browse the repository at this point in the history
…to work

around a macOS Radeon driver bug.

Also, ensure there is always a dummy texture bound to every parameter in the
OpenGL backend.

These together stop the macOS Radeon drivers from recompiling the shader on
every drawcall.

Possibly addresses rust-windowing#300.
  • Loading branch information
pcwalton committed May 13, 2020
1 parent cbceee8 commit ef0c0e0
Show file tree
Hide file tree
Showing 7 changed files with 567 additions and 318 deletions.
192 changes: 145 additions & 47 deletions gl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,43 @@ use pathfinder_geometry::vector::Vector2I;
use pathfinder_gpu::{BlendFactor, BlendOp, BufferData, BufferTarget, BufferUploadMode, ClearOps};
use pathfinder_gpu::{ComputeDimensions, ComputeState, DepthFunc, Device, FeatureLevel};
use pathfinder_gpu::{ImageAccess, ImageBinding, Primitive, ProgramKind, RenderOptions};
use pathfinder_gpu::{RenderState, RenderTarget, ShaderKind, StencilFunc, TextureData};
use pathfinder_gpu::{RenderState, RenderTarget, ShaderKind, StencilFunc, TextureBinding, TextureData};
use pathfinder_gpu::{TextureDataRef, TextureFormat, TextureSamplingFlags, UniformData};
use pathfinder_gpu::{VertexAttrClass, VertexAttrDescriptor, VertexAttrType};
use pathfinder_resources::ResourceLoader;
use pathfinder_simd::default::F32x4;
use std::cell::RefCell;
use std::ffi::CString;
use std::mem;
use std::ptr;
use std::str;
use std::time::Duration;

const DUMMY_TEXTURE_LENGTH: i32 = 16;

pub struct GLDevice {
version: GLVersion,
default_framebuffer: GLuint,
dummy_texture: GLTexture,
}

impl GLDevice {
#[inline]
pub fn new(version: GLVersion, default_framebuffer: GLuint) -> GLDevice {
GLDevice {
version,
default_framebuffer,
}
let dummy_texture = GLTexture {
gl_texture: 0,
size: Vector2I::zero(),
format: TextureFormat::RGBA8,
};

let mut device = GLDevice { version, default_framebuffer, dummy_texture };
let dummy_texture_data =
[0; DUMMY_TEXTURE_LENGTH as usize * DUMMY_TEXTURE_LENGTH as usize * 4];
device.dummy_texture =
device.create_texture_from_data(TextureFormat::RGBA8,
Vector2I::splat(DUMMY_TEXTURE_LENGTH),
TextureDataRef::U8(&dummy_texture_data));
device
}

pub fn set_default_framebuffer(&mut self, framebuffer: GLuint) {
Expand All @@ -64,9 +78,10 @@ impl GLDevice {

self.use_program(render_state.program);
self.bind_vertex_array(render_state.vertex_array);
for (texture_unit, texture) in render_state.textures.iter().enumerate() {
self.bind_texture(texture, texture_unit as u32);
}

self.bind_textures_and_images(&render_state.program,
&render_state.textures,
&render_state.images);

render_state.uniforms.iter().for_each(|(uniform, data)| self.set_uniform(uniform, data));

Expand All @@ -75,12 +90,10 @@ impl GLDevice {

fn set_compute_state(&self, compute_state: &ComputeState<GLDevice>) {
self.use_program(compute_state.program);
for (texture_unit, texture) in compute_state.textures.iter().enumerate() {
self.bind_texture(texture, texture_unit as u32);
}
for (image_unit, image) in compute_state.images.iter().enumerate() {
self.bind_image(image, image_unit as u32);
}

self.bind_textures_and_images(&compute_state.program,
&compute_state.textures,
&compute_state.images);

compute_state.uniforms.iter().for_each(|(uniform, data)| self.set_uniform(uniform, data));

Expand All @@ -89,6 +102,43 @@ impl GLDevice {
}
}

fn bind_textures_and_images(&self,
program: &GLProgram,
texture_bindings: &[TextureBinding<GLTextureParameter, GLTexture>],
image_bindings: &[ImageBinding<GLImageParameter, GLTexture>]) {
let (mut textures_bound, mut images_bound) = (0, 0);
for &(texture_parameter, texture) in texture_bindings {
self.bind_texture(texture, texture_parameter.texture_unit);
textures_bound |= 1 << texture_parameter.texture_unit as u64;
}
for image_binding in image_bindings {
self.bind_image(image_binding);
images_bound |= 1 << image_binding.0.image_unit as u64;
}

unsafe {
let parameters = program.parameters.borrow();
for (texture_unit, uniform) in parameters.textures.iter().enumerate() {
if (textures_bound & (1 << texture_unit as u64)) == 0 {
self.bind_texture(&self.dummy_texture, texture_unit as GLuint);
}
gl::Uniform1i(uniform.location, texture_unit as GLint); ck();
}
for (image_unit, uniform) in parameters.images.iter().enumerate() {
if (images_bound & (1 << image_unit as u64)) == 0 {
gl::BindImageTexture(image_unit as GLuint,
self.dummy_texture.gl_texture,
0,
gl::FALSE,
0,
gl::READ_ONLY,
gl::RGBA8 as GLenum); ck();
}
gl::Uniform1i(uniform.location, image_unit as GLint); ck();
}
}
}

fn set_render_options(&self, render_options: &RenderOptions) {
unsafe {
// Set blend.
Expand Down Expand Up @@ -183,21 +233,6 @@ impl GLDevice {
UniformData::Vec4(data) => {
gl::Uniform4f(uniform.location, data.x(), data.y(), data.z(), data.w()); ck();
}
UniformData::TextureUnit(unit) | UniformData::ImageUnit(unit) => {
gl::Uniform1i(uniform.location, unit as GLint); ck();
}
}
}
}

// Workaround for a macOS driver bug, it seems.
fn unset_uniform(&self, uniform: &GLUniform, data: &UniformData) {
unsafe {
match *data {
UniformData::TextureUnit(_) => {
gl::Uniform1i(uniform.location, 0); ck();
}
_ => {}
}
}
}
Expand All @@ -218,10 +253,18 @@ impl GLDevice {

fn reset_render_state(&self, render_state: &RenderState<GLDevice>) {
self.reset_render_options(&render_state.options);
for texture_unit in 0..(render_state.textures.len() as u32) {
self.unbind_texture(texture_unit);

unsafe {
for image_binding in render_state.images {
self.unbind_image(image_binding.0.image_unit);
gl::Uniform1i(image_binding.0.uniform.location, 0); ck();
}
for texture_binding in render_state.textures {
self.unbind_texture(texture_binding.0.texture_unit);
gl::Uniform1i(texture_binding.0.uniform.location, 0); ck();
}
}
render_state.uniforms.iter().for_each(|(uniform, data)| self.unset_uniform(uniform, data));

self.unuse_program();
self.unbind_vertex_array();
}
Expand All @@ -230,15 +273,18 @@ impl GLDevice {
for &(storage_buffer, _) in compute_state.storage_buffers {
self.unset_storage_buffer(storage_buffer);
}
for image_unit in 0..(compute_state.images.len() as u32) {
self.unbind_image(image_unit);
}
for texture_unit in 0..(compute_state.textures.len() as u32) {
self.unbind_texture(texture_unit);
}
for (uniform, data) in compute_state.uniforms {
self.unset_uniform(uniform, data);

unsafe {
for image_binding in compute_state.images {
self.unbind_image(image_binding.0.image_unit);
gl::Uniform1i(image_binding.0.uniform.location, 0); ck();
}
for texture_binding in compute_state.textures {
self.unbind_texture(texture_binding.0.texture_unit);
gl::Uniform1i(texture_binding.0.uniform.location, 0); ck();
}
}

self.unuse_program();
}

Expand Down Expand Up @@ -266,11 +312,13 @@ impl Device for GLDevice {
type Buffer = GLBuffer;
type Fence = GLFence;
type Framebuffer = GLFramebuffer;
type ImageParameter = GLImageParameter;
type Program = GLProgram;
type Shader = GLShader;
type StorageBuffer = GLStorageBuffer;
type Texture = GLTexture;
type TextureDataReceiver = GLTextureDataReceiver;
type TextureParameter = GLTextureParameter;
type TimerQuery = GLTimerQuery;
type Uniform = GLUniform;
type VertexArray = GLVertexArray;
Expand Down Expand Up @@ -402,7 +450,9 @@ impl Device for GLDevice {
}
}

GLProgram { gl_program, shaders }
let parameters = GLProgramParameters { textures: vec![], images: vec![] };

GLProgram { gl_program, shaders, parameters: RefCell::new(parameters) }
}

#[inline]
Expand Down Expand Up @@ -439,6 +489,34 @@ impl Device for GLDevice {
GLUniform { location }
}

fn get_texture_parameter(&self, program: &GLProgram, name: &str) -> GLTextureParameter {
let uniform = self.get_uniform(program, name);
let mut parameters = program.parameters.borrow_mut();
let index = match parameters.textures.iter().position(|u| *u == uniform) {
Some(index) => index,
None => {
let index = parameters.textures.len();
parameters.textures.push(uniform.clone());
index
}
};
GLTextureParameter { uniform, texture_unit: index as GLuint }
}

fn get_image_parameter(&self, program: &GLProgram, name: &str) -> GLImageParameter {
let uniform = self.get_uniform(program, name);
let mut parameters = program.parameters.borrow_mut();
let index = match parameters.images.iter().position(|u| *u == uniform) {
Some(index) => index,
None => {
let index = parameters.images.len();
parameters.images.push(uniform.clone());
index
}
};
GLImageParameter { uniform, image_unit: index as GLuint }
}

fn get_storage_buffer(&self, _: &Self::Program, _: &str, binding: u32) -> GLStorageBuffer {
GLStorageBuffer { location: binding as GLint }
}
Expand Down Expand Up @@ -872,15 +950,15 @@ impl GLDevice {
}
}

fn bind_image(&self, binding: &ImageBinding<GLDevice>, unit: u32) {
fn bind_image(&self, binding: &ImageBinding<GLImageParameter, GLTexture>) {
unsafe {
gl::BindImageTexture(unit,
binding.texture.gl_texture,
gl::BindImageTexture(binding.0.image_unit,
binding.1.gl_texture,
0,
gl::FALSE,
0,
binding.access.to_gl_access(),
binding.texture.format.gl_internal_format() as GLenum); ck();
binding.2.to_gl_access(),
binding.1.format.gl_internal_format() as GLenum); ck();
}
}

Expand Down Expand Up @@ -1109,11 +1187,23 @@ impl Drop for GLBuffer {
}
}

#[derive(Debug)]
#[derive(Clone, PartialEq, Debug)]
pub struct GLUniform {
location: GLint,
}

#[derive(Debug)]
pub struct GLTextureParameter {
uniform: GLUniform,
texture_unit: GLuint,
}

#[derive(Debug)]
pub struct GLImageParameter {
uniform: GLUniform,
image_unit: GLuint,
}

#[derive(Debug)]
pub struct GLStorageBuffer {
location: GLint,
Expand All @@ -1123,6 +1213,7 @@ pub struct GLProgram {
pub gl_program: GLuint,
#[allow(dead_code)]
shaders: ProgramKind<GLShader>,
parameters: RefCell<GLProgramParameters>,
}

impl Drop for GLProgram {
Expand All @@ -1133,6 +1224,13 @@ impl Drop for GLProgram {
}
}

pub struct GLProgramParameters {
// Mapping from texture unit number to uniform location.
textures: Vec<GLUniform>,
// Mapping from image unit number to uniform location.
images: Vec<GLUniform>,
}

pub struct GLShader {
gl_shader: GLuint,
}
Expand Down
28 changes: 15 additions & 13 deletions gpu/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ pub trait Device: Sized {
type Buffer;
type Fence;
type Framebuffer;
type ImageParameter;
type Program;
type Shader;
type StorageBuffer;
type Texture;
type TextureParameter;
type TextureDataReceiver;
type TimerQuery;
type Uniform;
Expand All @@ -57,6 +59,8 @@ pub trait Device: Sized {
local_size: ComputeDimensions);
fn get_vertex_attr(&self, program: &Self::Program, name: &str) -> Option<Self::VertexAttr>;
fn get_uniform(&self, program: &Self::Program, name: &str) -> Self::Uniform;
fn get_texture_parameter(&self, program: &Self::Program, name: &str) -> Self::TextureParameter;
fn get_image_parameter(&self, program: &Self::Program, name: &str) -> Self::ImageParameter;
fn get_storage_buffer(&self, program: &Self::Program, name: &str, binding: u32)
-> Self::StorageBuffer;
fn bind_buffer(&self,
Expand Down Expand Up @@ -237,8 +241,6 @@ pub enum UniformData {
Vec2(F32x2),
Vec3([f32; 3]),
Vec4(F32x4),
TextureUnit(u32),
ImageUnit(u32),
}

#[derive(Clone, Copy)]
Expand All @@ -253,27 +255,27 @@ pub struct RenderState<'a, D> where D: Device {
pub program: &'a D::Program,
pub vertex_array: &'a D::VertexArray,
pub primitive: Primitive,
pub uniforms: &'a [(&'a D::Uniform, UniformData)],
pub textures: &'a [&'a D::Texture],
pub images: &'a [ImageBinding<'a, D>],
pub uniforms: &'a [UniformBinding<'a, D::Uniform>],
pub textures: &'a [TextureBinding<'a, D::TextureParameter, D::Texture>],
pub images: &'a [ImageBinding<'a, D::ImageParameter, D::Texture>],
pub viewport: RectI,
pub options: RenderOptions,
}

#[derive(Clone)]
pub struct ComputeState<'a, D> where D: Device {
pub program: &'a D::Program,
pub uniforms: &'a [(&'a D::Uniform, UniformData)],
pub textures: &'a [&'a D::Texture],
pub images: &'a [ImageBinding<'a, D>],
pub uniforms: &'a [UniformBinding<'a, D::Uniform>],
pub textures: &'a [TextureBinding<'a, D::TextureParameter, D::Texture>],
pub images: &'a [ImageBinding<'a, D::ImageParameter, D::Texture>],
pub storage_buffers: &'a [(&'a D::StorageBuffer, &'a D::Buffer)],
}

#[derive(Clone, Debug)]
pub struct ImageBinding<'a, D> where D: Device {
pub texture: &'a D::Texture,
pub access: ImageAccess,
}
pub type UniformBinding<'a, U> = (&'a U, UniformData);

pub type TextureBinding<'a, TP, T> = (&'a TP, &'a T);

pub type ImageBinding<'a, IP, T> = (&'a IP, &'a T, ImageAccess);

#[derive(Clone, Debug)]
pub struct RenderOptions {
Expand Down
Loading

0 comments on commit ef0c0e0

Please sign in to comment.