Add a unified GPU allocator
This commit is contained in:
parent
754a44ae22
commit
7771fd877d
|
@ -10,7 +10,9 @@ homepage = "https://github.com/servo/pathfinder"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
|
fxhash = "0.2"
|
||||||
half = "1.5"
|
half = "1.5"
|
||||||
|
log = "0.4"
|
||||||
|
|
||||||
[dependencies.image]
|
[dependencies.image]
|
||||||
version = "0.23"
|
version = "0.23"
|
||||||
|
|
|
@ -0,0 +1,400 @@
|
||||||
|
// pathfinder/gpu/src/gpu/allocator.rs
|
||||||
|
//
|
||||||
|
// Copyright © 2020 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
//! GPU memory management.
|
||||||
|
|
||||||
|
use crate::{BufferData, BufferTarget, BufferUploadMode, Device, TextureFormat};
|
||||||
|
use fxhash::FxHashMap;
|
||||||
|
use pathfinder_geometry::vector::Vector2I;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::default::Default;
|
||||||
|
use std::mem;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
// Everything above 16 MB is allocated exactly.
|
||||||
|
const MAX_BUFFER_SIZE_CLASS: u64 = 16 * 1024 * 1024;
|
||||||
|
|
||||||
|
// Number of seconds before unused memory is purged.
|
||||||
|
//
|
||||||
|
// TODO(pcwalton): jemalloc uses a sigmoidal decay curve here. Consider something similar.
|
||||||
|
const DECAY_TIME: f32 = 0.250;
|
||||||
|
|
||||||
|
// Number of seconds before we can reuse an object buffer.
|
||||||
|
//
|
||||||
|
// This helps avoid stalls. This is admittedly a bit of a hack.
|
||||||
|
const REUSE_TIME: f32 = 0.015;
|
||||||
|
|
||||||
|
pub struct GPUMemoryAllocator<D> where D: Device {
|
||||||
|
buffers_in_use: FxHashMap<BufferID, BufferAllocation<D>>,
|
||||||
|
textures_in_use: FxHashMap<TextureID, TextureAllocation<D>>,
|
||||||
|
framebuffers_in_use: FxHashMap<FramebufferID, FramebufferAllocation<D>>,
|
||||||
|
free_objects: VecDeque<FreeObject<D>>,
|
||||||
|
next_buffer_id: BufferID,
|
||||||
|
next_texture_id: TextureID,
|
||||||
|
next_framebuffer_id: FramebufferID,
|
||||||
|
bytes_committed: u64,
|
||||||
|
bytes_allocated: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BufferAllocation<D> where D: Device {
|
||||||
|
buffer: D::Buffer,
|
||||||
|
size: u64,
|
||||||
|
tag: BufferTag,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TextureAllocation<D> where D: Device {
|
||||||
|
texture: D::Texture,
|
||||||
|
descriptor: TextureDescriptor,
|
||||||
|
tag: TextureTag,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FramebufferAllocation<D> where D: Device {
|
||||||
|
framebuffer: D::Framebuffer,
|
||||||
|
descriptor: TextureDescriptor,
|
||||||
|
tag: FramebufferTag,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FreeObject<D> where D: Device {
|
||||||
|
timestamp: Instant,
|
||||||
|
kind: FreeObjectKind<D>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FreeObjectKind<D> where D: Device {
|
||||||
|
Buffer { id: BufferID, allocation: BufferAllocation<D> },
|
||||||
|
Texture { id: TextureID, allocation: TextureAllocation<D> },
|
||||||
|
Framebuffer { id: FramebufferID, allocation: FramebufferAllocation<D> },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct TextureDescriptor {
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
format: TextureFormat,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct BufferID(pub u64);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct TextureID(pub u64);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct FramebufferID(pub u64);
|
||||||
|
|
||||||
|
// For debugging and profiling.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
||||||
|
pub struct BufferTag(pub &'static str);
|
||||||
|
|
||||||
|
// For debugging and profiling.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub struct TextureTag(pub &'static str);
|
||||||
|
|
||||||
|
// For debugging and profiling.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub struct FramebufferTag(pub &'static str);
|
||||||
|
|
||||||
|
impl<D> GPUMemoryAllocator<D> where D: Device {
|
||||||
|
pub fn new() -> GPUMemoryAllocator<D> {
|
||||||
|
GPUMemoryAllocator {
|
||||||
|
buffers_in_use: FxHashMap::default(),
|
||||||
|
textures_in_use: FxHashMap::default(),
|
||||||
|
framebuffers_in_use: FxHashMap::default(),
|
||||||
|
free_objects: VecDeque::new(),
|
||||||
|
next_buffer_id: BufferID(0),
|
||||||
|
next_texture_id: TextureID(0),
|
||||||
|
next_framebuffer_id: FramebufferID(0),
|
||||||
|
bytes_committed: 0,
|
||||||
|
bytes_allocated: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate_buffer<T>(&mut self, device: &D, size: u64, tag: BufferTag) -> BufferID {
|
||||||
|
let mut byte_size = size * mem::size_of::<T>() as u64;
|
||||||
|
if byte_size < MAX_BUFFER_SIZE_CLASS {
|
||||||
|
byte_size = byte_size.next_power_of_two();
|
||||||
|
}
|
||||||
|
|
||||||
|
let now = Instant::now();
|
||||||
|
|
||||||
|
for free_object_index in 0..self.free_objects.len() {
|
||||||
|
match self.free_objects[free_object_index] {
|
||||||
|
FreeObject {
|
||||||
|
ref timestamp,
|
||||||
|
kind: FreeObjectKind::Buffer { ref allocation, .. },
|
||||||
|
} if allocation.size == byte_size &&
|
||||||
|
(now - *timestamp).as_secs_f32() >= REUSE_TIME => {}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
let (id, mut allocation) = match self.free_objects.remove(free_object_index) {
|
||||||
|
Some(FreeObject { kind: FreeObjectKind::Buffer { id, allocation }, .. }) => {
|
||||||
|
(id, allocation)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
allocation.tag = tag;
|
||||||
|
self.bytes_committed += allocation.size;
|
||||||
|
self.buffers_in_use.insert(id, allocation);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = device.create_buffer(BufferUploadMode::Dynamic);
|
||||||
|
device.allocate_buffer::<u8>(&buffer,
|
||||||
|
BufferData::Uninitialized(byte_size as usize),
|
||||||
|
BufferTarget::Vertex);
|
||||||
|
|
||||||
|
let id = self.next_buffer_id;
|
||||||
|
self.next_buffer_id.0 += 1;
|
||||||
|
|
||||||
|
debug!("mapping buffer: {:?} {} ({}x{}) {:?}",
|
||||||
|
id,
|
||||||
|
byte_size,
|
||||||
|
size,
|
||||||
|
mem::size_of::<T>(),
|
||||||
|
tag);
|
||||||
|
|
||||||
|
self.buffers_in_use.insert(id, BufferAllocation { buffer, size: byte_size, tag });
|
||||||
|
self.bytes_allocated += byte_size;
|
||||||
|
self.bytes_committed += byte_size;
|
||||||
|
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate_texture(&mut self,
|
||||||
|
device: &D,
|
||||||
|
size: Vector2I,
|
||||||
|
format: TextureFormat,
|
||||||
|
tag: TextureTag)
|
||||||
|
-> TextureID {
|
||||||
|
let descriptor = TextureDescriptor {
|
||||||
|
width: size.x() as u32,
|
||||||
|
height: size.y() as u32,
|
||||||
|
format,
|
||||||
|
};
|
||||||
|
let byte_size = descriptor.byte_size();
|
||||||
|
|
||||||
|
for free_object_index in 0..self.free_objects.len() {
|
||||||
|
match self.free_objects[free_object_index] {
|
||||||
|
FreeObject { kind: FreeObjectKind::Texture { ref allocation, .. }, .. } if
|
||||||
|
allocation.descriptor == descriptor => {}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
let (id, mut allocation) = match self.free_objects.remove(free_object_index) {
|
||||||
|
Some(FreeObject { kind: FreeObjectKind::Texture { id, allocation }, .. }) => {
|
||||||
|
(id, allocation)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
allocation.tag = tag;
|
||||||
|
self.bytes_committed += allocation.descriptor.byte_size();
|
||||||
|
self.textures_in_use.insert(id, allocation);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("mapping texture: {:?} {:?}", descriptor, tag);
|
||||||
|
|
||||||
|
let texture = device.create_texture(format, size);
|
||||||
|
let id = self.next_texture_id;
|
||||||
|
self.next_texture_id.0 += 1;
|
||||||
|
|
||||||
|
self.textures_in_use.insert(id, TextureAllocation { texture, descriptor, tag });
|
||||||
|
|
||||||
|
self.bytes_allocated += byte_size;
|
||||||
|
self.bytes_committed += byte_size;
|
||||||
|
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate_framebuffer(&mut self,
|
||||||
|
device: &D,
|
||||||
|
size: Vector2I,
|
||||||
|
format: TextureFormat,
|
||||||
|
tag: FramebufferTag)
|
||||||
|
-> FramebufferID {
|
||||||
|
let descriptor = TextureDescriptor {
|
||||||
|
width: size.x() as u32,
|
||||||
|
height: size.y() as u32,
|
||||||
|
format,
|
||||||
|
};
|
||||||
|
let byte_size = descriptor.byte_size();
|
||||||
|
|
||||||
|
for free_object_index in 0..self.free_objects.len() {
|
||||||
|
match self.free_objects[free_object_index].kind {
|
||||||
|
FreeObjectKind::Framebuffer { ref allocation, .. } if allocation.descriptor ==
|
||||||
|
descriptor => {}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
let (id, mut allocation) = match self.free_objects.remove(free_object_index) {
|
||||||
|
Some(FreeObject { kind: FreeObjectKind::Framebuffer { id, allocation }, .. }) => {
|
||||||
|
(id, allocation)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
allocation.tag = tag;
|
||||||
|
self.bytes_committed += allocation.descriptor.byte_size();
|
||||||
|
self.framebuffers_in_use.insert(id, allocation);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("mapping framebuffer: {:?} {:?}", descriptor, tag);
|
||||||
|
|
||||||
|
let texture = device.create_texture(format, size);
|
||||||
|
let framebuffer = device.create_framebuffer(texture);
|
||||||
|
let id = self.next_framebuffer_id;
|
||||||
|
self.next_framebuffer_id.0 += 1;
|
||||||
|
|
||||||
|
self.framebuffers_in_use.insert(id, FramebufferAllocation {
|
||||||
|
framebuffer,
|
||||||
|
descriptor,
|
||||||
|
tag,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.bytes_allocated += byte_size;
|
||||||
|
self.bytes_committed += byte_size;
|
||||||
|
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn purge_if_needed(&mut self) {
|
||||||
|
let now = Instant::now();
|
||||||
|
loop {
|
||||||
|
match self.free_objects.front() {
|
||||||
|
Some(FreeObject { timestamp, .. }) if (now - *timestamp).as_secs_f32() >=
|
||||||
|
DECAY_TIME => {}
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
match self.free_objects.pop_front() {
|
||||||
|
None => break,
|
||||||
|
Some(FreeObject { kind: FreeObjectKind::Buffer { allocation, .. }, .. }) => {
|
||||||
|
debug!("purging buffer: {}", allocation.size);
|
||||||
|
self.bytes_allocated -= allocation.size;
|
||||||
|
}
|
||||||
|
Some(FreeObject { kind: FreeObjectKind::Texture { allocation, .. }, .. }) => {
|
||||||
|
debug!("purging texture: {:?}", allocation.descriptor);
|
||||||
|
self.bytes_allocated -= allocation.descriptor.byte_size();
|
||||||
|
}
|
||||||
|
Some(FreeObject { kind: FreeObjectKind::Framebuffer { allocation, .. }, .. }) => {
|
||||||
|
debug!("purging framebuffer: {:?}", allocation.descriptor);
|
||||||
|
self.bytes_allocated -= allocation.descriptor.byte_size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_buffer(&mut self, id: BufferID) {
|
||||||
|
let allocation = self.buffers_in_use
|
||||||
|
.remove(&id)
|
||||||
|
.expect("Attempted to free unallocated buffer!");
|
||||||
|
self.bytes_committed -= allocation.size;
|
||||||
|
self.free_objects.push_back(FreeObject {
|
||||||
|
timestamp: Instant::now(),
|
||||||
|
kind: FreeObjectKind::Buffer { id, allocation },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_texture(&mut self, id: TextureID) {
|
||||||
|
let allocation = self.textures_in_use
|
||||||
|
.remove(&id)
|
||||||
|
.expect("Attempted to free unallocated texture!");
|
||||||
|
let byte_size = allocation.descriptor.byte_size();
|
||||||
|
self.bytes_committed -= byte_size;
|
||||||
|
self.free_objects.push_back(FreeObject {
|
||||||
|
timestamp: Instant::now(),
|
||||||
|
kind: FreeObjectKind::Texture { id, allocation },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_framebuffer(&mut self, id: FramebufferID) {
|
||||||
|
let allocation = self.framebuffers_in_use
|
||||||
|
.remove(&id)
|
||||||
|
.expect("Attempted to free unallocated framebuffer!");
|
||||||
|
let byte_size = allocation.descriptor.byte_size();
|
||||||
|
self.bytes_committed -= byte_size;
|
||||||
|
self.free_objects.push_back(FreeObject {
|
||||||
|
timestamp: Instant::now(),
|
||||||
|
kind: FreeObjectKind::Framebuffer { id, allocation },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_buffer(&self, id: BufferID) -> &D::Buffer {
|
||||||
|
&self.buffers_in_use[&id].buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_texture(&self, id: TextureID) -> &D::Texture {
|
||||||
|
&self.textures_in_use[&id].texture
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_framebuffer(&self, id: FramebufferID) -> &D::Framebuffer {
|
||||||
|
&self.framebuffers_in_use[&id].framebuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn bytes_allocated(&self) -> u64 {
|
||||||
|
self.bytes_allocated
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn bytes_committed(&self) -> u64 {
|
||||||
|
self.bytes_committed
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn dump(&self) {
|
||||||
|
println!("GPU memory dump");
|
||||||
|
println!("---------------");
|
||||||
|
|
||||||
|
println!("Buffers:");
|
||||||
|
let mut ids: Vec<BufferID> = self.buffers_in_use.keys().cloned().collect();
|
||||||
|
ids.sort();
|
||||||
|
for id in ids {
|
||||||
|
let allocation = &self.buffers_in_use[&id];
|
||||||
|
println!("id {:?}: {:?} ({:?} B)", id, allocation.tag, allocation.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Textures:");
|
||||||
|
let mut ids: Vec<TextureID> = self.textures_in_use.keys().cloned().collect();
|
||||||
|
ids.sort();
|
||||||
|
for id in ids {
|
||||||
|
let allocation = &self.textures_in_use[&id];
|
||||||
|
println!("id {:?}: {:?} {:?}x{:?} {:?} ({:?} B)",
|
||||||
|
id,
|
||||||
|
allocation.tag,
|
||||||
|
allocation.descriptor.width,
|
||||||
|
allocation.descriptor.height,
|
||||||
|
allocation.descriptor.format,
|
||||||
|
allocation.descriptor.byte_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Framebuffers:");
|
||||||
|
let mut ids: Vec<FramebufferID> = self.framebuffers_in_use.keys().cloned().collect();
|
||||||
|
ids.sort();
|
||||||
|
for id in ids {
|
||||||
|
let allocation = &self.framebuffers_in_use[&id];
|
||||||
|
println!("id {:?}: {:?} {:?}x{:?} {:?} ({:?} B)",
|
||||||
|
id,
|
||||||
|
allocation.tag,
|
||||||
|
allocation.descriptor.width,
|
||||||
|
allocation.descriptor.height,
|
||||||
|
allocation.descriptor.format,
|
||||||
|
allocation.descriptor.byte_size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextureDescriptor {
|
||||||
|
fn byte_size(&self) -> u64 {
|
||||||
|
self.width as u64 * self.height as u64 * self.format.bytes_per_pixel() as u64
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,10 @@
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
|
pub mod allocator;
|
||||||
|
|
||||||
use half::f16;
|
use half::f16;
|
||||||
use image::ImageFormat;
|
use image::ImageFormat;
|
||||||
|
@ -21,11 +25,13 @@ use pathfinder_geometry::transform3d::Transform4F;
|
||||||
use pathfinder_geometry::vector::{Vector2I, vec2i};
|
use pathfinder_geometry::vector::{Vector2I, vec2i};
|
||||||
use pathfinder_resources::ResourceLoader;
|
use pathfinder_resources::ResourceLoader;
|
||||||
use pathfinder_simd::default::{F32x2, F32x4, I32x2};
|
use pathfinder_simd::default::{F32x2, F32x4, I32x2};
|
||||||
|
use std::ops::Range;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub trait Device: Sized {
|
pub trait Device: Sized {
|
||||||
type Buffer;
|
type Buffer;
|
||||||
|
type BufferDataReceiver;
|
||||||
type Fence;
|
type Fence;
|
||||||
type Framebuffer;
|
type Framebuffer;
|
||||||
type ImageParameter;
|
type ImageParameter;
|
||||||
|
@ -40,6 +46,8 @@ pub trait Device: Sized {
|
||||||
type VertexArray;
|
type VertexArray;
|
||||||
type VertexAttr;
|
type VertexAttr;
|
||||||
|
|
||||||
|
fn backend_name(&self) -> &'static str;
|
||||||
|
fn device_name(&self) -> String;
|
||||||
fn feature_level(&self) -> FeatureLevel;
|
fn feature_level(&self) -> FeatureLevel;
|
||||||
fn create_texture(&self, format: TextureFormat, size: Vector2I) -> Self::Texture;
|
fn create_texture(&self, format: TextureFormat, size: Vector2I) -> Self::Texture;
|
||||||
fn create_texture_from_data(&self, format: TextureFormat, size: Vector2I, data: TextureDataRef)
|
fn create_texture_from_data(&self, format: TextureFormat, size: Vector2I, data: TextureDataRef)
|
||||||
|
@ -90,6 +98,8 @@ pub trait Device: Sized {
|
||||||
fn upload_to_texture(&self, texture: &Self::Texture, rect: RectI, data: TextureDataRef);
|
fn upload_to_texture(&self, texture: &Self::Texture, rect: RectI, data: TextureDataRef);
|
||||||
fn read_pixels(&self, target: &RenderTarget<Self>, viewport: RectI)
|
fn read_pixels(&self, target: &RenderTarget<Self>, viewport: RectI)
|
||||||
-> Self::TextureDataReceiver;
|
-> Self::TextureDataReceiver;
|
||||||
|
fn read_buffer(&self, buffer: &Self::Buffer, target: BufferTarget, range: Range<usize>)
|
||||||
|
-> Self::BufferDataReceiver;
|
||||||
fn begin_commands(&self);
|
fn begin_commands(&self);
|
||||||
fn end_commands(&self);
|
fn end_commands(&self);
|
||||||
fn draw_arrays(&self, index_count: u32, render_state: &RenderState<Self>);
|
fn draw_arrays(&self, index_count: u32, render_state: &RenderState<Self>);
|
||||||
|
@ -108,6 +118,8 @@ pub trait Device: Sized {
|
||||||
fn recv_timer_query(&self, query: &Self::TimerQuery) -> Duration;
|
fn recv_timer_query(&self, query: &Self::TimerQuery) -> Duration;
|
||||||
fn try_recv_texture_data(&self, receiver: &Self::TextureDataReceiver) -> Option<TextureData>;
|
fn try_recv_texture_data(&self, receiver: &Self::TextureDataReceiver) -> Option<TextureData>;
|
||||||
fn recv_texture_data(&self, receiver: &Self::TextureDataReceiver) -> TextureData;
|
fn recv_texture_data(&self, receiver: &Self::TextureDataReceiver) -> TextureData;
|
||||||
|
fn try_recv_buffer(&self, receiver: &Self::BufferDataReceiver) -> Option<Vec<u8>>;
|
||||||
|
fn recv_buffer(&self, receiver: &Self::BufferDataReceiver) -> Vec<u8>;
|
||||||
|
|
||||||
fn create_texture_from_png(&self,
|
fn create_texture_from_png(&self,
|
||||||
resources: &dyn ResourceLoader,
|
resources: &dyn ResourceLoader,
|
||||||
|
@ -131,6 +143,30 @@ pub trait Device: Sized {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn upload_png_to_texture(&self,
|
||||||
|
resources: &dyn ResourceLoader,
|
||||||
|
name: &str,
|
||||||
|
texture: &Self::Texture,
|
||||||
|
format: TextureFormat) {
|
||||||
|
let data = resources.slurp(&format!("textures/{}.png", name)).unwrap();
|
||||||
|
let image = image::load_from_memory_with_format(&data, ImageFormat::Png).unwrap();
|
||||||
|
match format {
|
||||||
|
TextureFormat::R8 => {
|
||||||
|
let image = image.to_luma();
|
||||||
|
let size = vec2i(image.width() as i32, image.height() as i32);
|
||||||
|
let rect = RectI::new(Vector2I::default(), size);
|
||||||
|
self.upload_to_texture(&texture, rect, TextureDataRef::U8(&image))
|
||||||
|
}
|
||||||
|
TextureFormat::RGBA8 => {
|
||||||
|
let image = image.to_rgba();
|
||||||
|
let size = vec2i(image.width() as i32, image.height() as i32);
|
||||||
|
let rect = RectI::new(Vector2I::default(), size);
|
||||||
|
self.upload_to_texture(&texture, rect, TextureDataRef::U8(&image))
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn create_program_from_shader_names(
|
fn create_program_from_shader_names(
|
||||||
&self,
|
&self,
|
||||||
resources: &dyn ResourceLoader,
|
resources: &dyn ResourceLoader,
|
||||||
|
@ -170,7 +206,7 @@ pub enum FeatureLevel {
|
||||||
D3D11,
|
D3D11,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum TextureFormat {
|
pub enum TextureFormat {
|
||||||
R8,
|
R8,
|
||||||
R16F,
|
R16F,
|
||||||
|
@ -182,10 +218,11 @@ pub enum TextureFormat {
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum VertexAttrType {
|
pub enum VertexAttrType {
|
||||||
F32,
|
F32,
|
||||||
I16,
|
|
||||||
I8,
|
I8,
|
||||||
U16,
|
I16,
|
||||||
|
I32,
|
||||||
U8,
|
U8,
|
||||||
|
U16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
@ -258,6 +295,7 @@ pub struct RenderState<'a, D> where D: Device {
|
||||||
pub uniforms: &'a [UniformBinding<'a, D::Uniform>],
|
pub uniforms: &'a [UniformBinding<'a, D::Uniform>],
|
||||||
pub textures: &'a [TextureBinding<'a, D::TextureParameter, D::Texture>],
|
pub textures: &'a [TextureBinding<'a, D::TextureParameter, D::Texture>],
|
||||||
pub images: &'a [ImageBinding<'a, D::ImageParameter, D::Texture>],
|
pub images: &'a [ImageBinding<'a, D::ImageParameter, D::Texture>],
|
||||||
|
pub storage_buffers: &'a [(&'a D::StorageBuffer, &'a D::Buffer)],
|
||||||
pub viewport: RectI,
|
pub viewport: RectI,
|
||||||
pub options: RenderOptions,
|
pub options: RenderOptions,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue