TextureCopy render graph node, texture_resource_system

Removed textures are now freed
This commit is contained in:
Carter Anderson 2020-05-18 11:31:11 -07:00
parent 33d4d5f562
commit c5a78d4616
11 changed files with 227 additions and 107 deletions

1
.vscode/launch.json vendored
View File

@ -1809,6 +1809,7 @@
"type": "lldb",
"request": "launch",
"name": "Debug example 'text'",
"env": { "CARGO_MANIFEST_DIR": "${workspaceFolder}" },
"cargo": {
"args": [
"build",

View File

@ -2,7 +2,7 @@ use crate::{
update_asset_storage_system, AssetChannel, AssetLoader, AssetServer, ChannelAssetHandler,
Handle, HandleId,
};
use bevy_app::{stage, AppBuilder, Events};
use bevy_app::{AppBuilder, Events};
use bevy_core::bytes::GetBytes;
use legion::prelude::*;
use std::{
@ -146,7 +146,10 @@ impl AddAsset for AppBuilder {
T: Send + Sync + 'static,
{
self.init_resource::<Assets<T>>()
.add_system_to_stage(stage::POST_UPDATE, Assets::<T>::asset_event_system.system())
.add_system_to_stage(
super::stage::ASSET_EVENTS,
Assets::<T>::asset_event_system.system(),
)
.add_event::<AssetEvent<T>>()
}

View File

@ -1,10 +1,10 @@
mod asset_server;
mod assets;
#[cfg(feature = "filesystem_watcher")]
pub mod filesystem_watcher;
mod handle;
mod load_request;
mod loader;
#[cfg(feature = "filesystem_watcher")]
pub mod filesystem_watcher;
pub use asset_server::*;
pub use assets::*;
@ -17,6 +17,7 @@ use legion::prelude::IntoSystem;
pub mod stage {
pub const LOAD_ASSETS: &str = "load_assets";
pub const ASSET_EVENTS: &str = "asset_events";
}
#[derive(Default)]
@ -24,9 +25,13 @@ pub struct AssetPlugin;
impl AppPlugin for AssetPlugin {
fn build(&self, app: &mut AppBuilder) {
app.add_stage_before(bevy_app::stage::PRE_UPDATE, stage::LOAD_ASSETS)
app.add_stage_before(bevy_app::stage::PRE_UPDATE, stage::LOAD_ASSETS)
.add_stage_after(bevy_app::stage::POST_UPDATE, stage::ASSET_EVENTS)
.init_resource::<AssetServer>();
#[cfg(feature = "filesystem_watcher")]
app.add_system_to_stage(stage::LOAD_ASSETS, AssetServer::filesystem_watcher_system.system());
app.add_system_to_stage(
stage::LOAD_ASSETS,
AssetServer::filesystem_watcher_system.system(),
);
}
}
}

View File

@ -15,7 +15,7 @@ struct EntityArchetypeAttributeArgs {
pub tag: Option<bool>,
}
#[proc_macro_derive(Resource)]
#[proc_macro_derive(Resource, attributes(module))]
pub fn derive_resource(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let fields = match &ast.data {

View File

@ -4,13 +4,19 @@ use crate::{
RenderPassDepthStencilAttachmentDescriptor, StoreOp, TextureAttachment,
},
render_graph::{
nodes::{Camera2dNode, CameraNode, PassNode, WindowSwapChainNode, WindowTextureNode},
nodes::{
Camera2dNode, CameraNode, PassNode, TextureCopyNode, WindowSwapChainNode,
WindowTextureNode,
},
RenderGraph,
},
texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage},
texture::{
Extent3d, Texture, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage,
},
Color,
};
use bevy_app::GetEventReader;
use bevy_asset::AssetEvent;
use bevy_window::{WindowCreated, WindowReference, WindowResized};
use legion::prelude::Resources;
@ -27,6 +33,7 @@ pub mod node {
pub const PRIMARY_SWAP_CHAIN: &str = "swapchain";
pub const CAMERA: &str = "camera";
pub const CAMERA2D: &str = "camera2d";
pub const TEXTURE_COPY: &str = "texture_copy";
pub const MAIN_DEPTH_TEXTURE: &str = "main_pass_depth_texture";
pub const MAIN_PASS: &str = "main_pass";
}
@ -60,6 +67,10 @@ impl BaseRenderGraphBuilder for RenderGraph {
resources: &Resources,
config: &BaseRenderGraphConfig,
) -> &mut Self {
self.add_node(
node::TEXTURE_COPY,
TextureCopyNode::new(resources.get_event_reader::<AssetEvent<Texture>>()),
);
if config.add_3d_camera {
self.add_system_node(node::CAMERA, CameraNode::default());
}
@ -115,6 +126,8 @@ impl BaseRenderGraphBuilder for RenderGraph {
}),
);
self.add_node_edge(node::TEXTURE_COPY, node::MAIN_PASS)
.unwrap();
if config.add_3d_camera {
self.add_node_edge(node::CAMERA, node::MAIN_PASS).unwrap();
}

View File

@ -46,7 +46,7 @@ use legion::prelude::IntoSystem;
use mesh::mesh_resource_provider_system;
use render_graph::RenderGraph;
use render_resource::EntitiesWaitingForAssets;
use texture::PngTextureLoader;
use texture::{PngTextureLoader, TextureResourceSystemState};
pub static RENDER_RESOURCE_STAGE: &str = "render_resource";
pub static RENDER_STAGE: &str = "render";
@ -66,31 +66,37 @@ impl Default for RenderPlugin {
impl AppPlugin for RenderPlugin {
fn build(&self, app: &mut AppBuilder) {
let mut render_graph = RenderGraph::default();
if let Some(ref config) = self.base_render_graph_config {
render_graph.add_base_graph(app.resources(), config);
}
app.add_stage_after(stage::POST_UPDATE, RENDER_RESOURCE_STAGE)
app.add_stage_after(bevy_asset::stage::ASSET_EVENTS, RENDER_RESOURCE_STAGE)
.add_stage_after(RENDER_RESOURCE_STAGE, RENDER_STAGE)
.add_asset::<Mesh>()
.add_asset::<Texture>()
.add_asset::<Shader>()
.add_asset::<PipelineDescriptor>()
.add_asset_loader(PngTextureLoader::default())
.add_resource(render_graph)
.init_resource::<RenderGraph>()
.init_resource::<PipelineAssignments>()
.init_resource::<PipelineCompiler>()
.init_resource::<RenderResourceAssignments>()
.init_resource::<VertexBufferDescriptors>()
.init_resource::<EntityRenderResourceAssignments>()
.init_resource::<EntitiesWaitingForAssets>()
.init_resource::<TextureResourceSystemState>()
.add_system(entity_render_resource_assignments_system())
.init_system_to_stage(stage::POST_UPDATE, camera::camera_update_system)
.add_system_to_stage(
stage::PRE_UPDATE,
EntitiesWaitingForAssets::clear_system.system(),
)
.init_system_to_stage(RENDER_RESOURCE_STAGE, mesh_resource_provider_system);
.init_system_to_stage(RENDER_RESOURCE_STAGE, mesh_resource_provider_system)
.add_system_to_stage(
RENDER_RESOURCE_STAGE,
Texture::texture_resource_system.system(),
);
if let Some(ref config) = self.base_render_graph_config {
let resources = app.resources();
let mut render_graph = resources.get_mut::<RenderGraph>().unwrap();
render_graph.add_base_graph(resources, config);
}
}
}

View File

@ -4,6 +4,7 @@ mod pass_node;
mod uniform_node;
mod window_swapchain_node;
mod window_texture_node;
mod texture_copy_node;
pub use camera2d_node::*;
pub use camera_node::*;
@ -11,3 +12,4 @@ pub use pass_node::*;
pub use uniform_node::*;
pub use window_swapchain_node::*;
pub use window_texture_node::*;
pub use texture_copy_node::*;

View File

@ -0,0 +1,71 @@
use crate::{
render_graph::{Node, ResourceSlots},
render_resource::{BufferInfo, BufferUsage},
renderer::RenderContext,
texture::{Texture, TextureDescriptor, TEXTURE_ASSET_INDEX},
};
use bevy_app::{EventReader, Events};
use bevy_asset::{AssetEvent, Assets};
use legion::prelude::*;
pub struct TextureCopyNode {
pub texture_event_reader: EventReader<AssetEvent<Texture>>,
}
impl TextureCopyNode {
pub fn new(texture_event_reader: EventReader<AssetEvent<Texture>>) -> Self {
TextureCopyNode {
texture_event_reader,
}
}
}
impl Node for TextureCopyNode {
fn update(
&mut self,
_world: &World,
resources: &Resources,
render_context: &mut dyn RenderContext,
_input: &ResourceSlots,
_output: &mut ResourceSlots,
) {
let texture_events = resources.get::<Events<AssetEvent<Texture>>>().unwrap();
let textures = resources.get::<Assets<Texture>>().unwrap();
for event in self.texture_event_reader.iter(&texture_events) {
match event {
AssetEvent::Created { handle } | AssetEvent::Modified { handle } => {
if let Some(texture) = textures.get(&handle) {
let texture_descriptor: TextureDescriptor = texture.into();
let texture_buffer = render_context.resources().create_buffer_with_data(
BufferInfo {
buffer_usage: BufferUsage::COPY_SRC,
..Default::default()
},
&texture.data,
);
let texture_resource = render_context
.resources()
.get_asset_resource(*handle, TEXTURE_ASSET_INDEX)
.unwrap();
// TODO: bytes_per_row could be incorrect for some texture formats
render_context.copy_buffer_to_texture(
texture_buffer,
0,
(4 * texture.width) as u32,
texture_resource,
[0, 0, 0],
0,
0,
texture_descriptor.size.clone(),
);
render_context.resources().remove_buffer(texture_buffer);
}
}
AssetEvent::Removed { .. } => {}
}
}
}
}

View File

@ -13,7 +13,6 @@ use crate::{
use bevy_asset::{Assets, Handle};
use legion::prelude::*;
use std::{collections::HashMap, marker::PhantomData};
use texture::{SamplerDescriptor, Texture, TextureDescriptor};
pub const BIND_BUFFER_ALIGNMENT: usize = 256;
#[derive(Debug)]
@ -398,7 +397,6 @@ where
"uniform_resource_provider::<{}>",
std::any::type_name::<T>()
))
.read_resource::<Assets<Texture>>()
.read_resource::<RenderResources>()
.read_resource::<EntitiesWaitingForAssets>()
// TODO: this write on RenderResourceAssignments will prevent this system from running in parallel with other systems that do the same
@ -407,7 +405,7 @@ where
.build(
move |_,
world,
(textures, render_resources, entities_waiting_for_assets),
(render_resources, entities_waiting_for_assets),
(read_uniform_query, write_uniform_query)| {
let render_resource_context = &*render_resources.context;
@ -442,8 +440,6 @@ where
setup_uniform_texture_resources::<T>(
entity,
&uniforms,
&mut command_queue,
textures,
render_resource_context,
entities_waiting_for_assets,
&mut renderable.render_resource_assignments,
@ -557,7 +553,6 @@ where
// TODO: maybe run "update" here
SystemBuilder::new("uniform_resource_provider")
.read_resource::<Assets<T>>()
.read_resource::<Assets<Texture>>()
.read_resource::<RenderResources>()
.read_resource::<EntitiesWaitingForAssets>()
// TODO: this write on RenderResourceAssignments will prevent this system from running in parallel with other systems that do the same
@ -566,7 +561,7 @@ where
.build(
move |_,
world,
(assets, textures, render_resources, entities_waiting_for_assets),
(assets, render_resources, entities_waiting_for_assets),
(read_handle_query, write_handle_query)| {
let render_resource_context = &*render_resources.context;
uniform_buffer_arrays.reset_new_item_counts();
@ -607,8 +602,6 @@ where
setup_uniform_texture_resources::<T>(
entity,
&uniforms,
&mut command_queue,
textures,
render_resource_context,
entities_waiting_for_assets,
&mut renderable.render_resource_assignments,
@ -694,87 +687,37 @@ where
fn setup_uniform_texture_resources<T>(
entity: Entity,
uniforms: &T,
command_queue: &mut CommandQueue,
textures: &Assets<Texture>,
render_resource_context: &dyn RenderResourceContext,
entities_waiting_for_assets: &EntitiesWaitingForAssets,
render_resource_assignments: &mut RenderResourceAssignments,
) where
T: AsUniforms,
T: AsUniforms,
{
for field_info in T::get_field_infos().iter() {
let bind_type = uniforms.get_field_bind_type(&field_info.name);
match bind_type {
Some(FieldBindType::Texture) => {
let texture_handle = uniforms
.get_uniform_texture(&field_info.texture_name)
.unwrap();
let (texture_resource, sampler_resource) = match render_resource_context
if let Some(FieldBindType::Texture) = bind_type {
if let Some(texture_handle) = uniforms.get_uniform_texture(&field_info.texture_name) {
if let Some(texture_resource) = render_resource_context
.get_asset_resource(texture_handle, texture::TEXTURE_ASSET_INDEX)
{
Some(texture_resource) => (
texture_resource,
render_resource_context
.get_asset_resource(texture_handle, texture::SAMPLER_ASSET_INDEX)
.unwrap(),
),
None => {
if let Some(texture) = textures.get(&texture_handle) {
let texture_descriptor: TextureDescriptor = texture.into();
let texture_resource =
render_resource_context.create_texture(texture_descriptor);
let texture_buffer = render_resource_context.create_buffer_with_data(
BufferInfo {
buffer_usage: BufferUsage::COPY_SRC,
..Default::default()
},
&texture.data,
);
// TODO: bytes_per_row could be incorrect for some texture formats
command_queue.copy_buffer_to_texture(
texture_buffer,
0,
(4 * texture.width) as u32,
texture_resource,
[0, 0, 0],
0,
0,
texture_descriptor.size.clone(),
);
command_queue.free_buffer(texture_buffer);
let sampler_descriptor: SamplerDescriptor = texture.into();
let sampler_resource =
render_resource_context.create_sampler(&sampler_descriptor);
render_resource_context.set_asset_resource(
texture_handle,
texture_resource,
0,
);
render_resource_context.set_asset_resource(
texture_handle,
sampler_resource,
1,
);
(texture_resource, sampler_resource)
} else {
entities_waiting_for_assets.add(entity);
continue;
}
}
};
render_resource_assignments.set(
field_info.texture_name,
RenderResourceAssignment::Texture(texture_resource),
);
render_resource_assignments.set(
field_info.sampler_name,
RenderResourceAssignment::Sampler(sampler_resource),
);
let sampler_resource = render_resource_context
.get_asset_resource(texture_handle, texture::SAMPLER_ASSET_INDEX)
.unwrap();
render_resource_assignments.set(
field_info.texture_name,
RenderResourceAssignment::Texture(texture_resource),
);
render_resource_assignments.set(
field_info.sampler_name,
RenderResourceAssignment::Sampler(sampler_resource),
);
continue;
} else {
entities_waiting_for_assets.add(entity);
}
} else {
entities_waiting_for_assets.add(entity);
}
_ => {}
}
}
}

View File

@ -1,6 +1,13 @@
use crate::shader::ShaderDefSuffixProvider;
use bevy_asset::Handle;
use std::fs::File;
use super::{SamplerDescriptor, TextureDescriptor};
use crate::{
renderer::{RenderResourceContext, RenderResources},
shader::ShaderDefSuffixProvider,
};
use bevy_app::{EventReader, Events};
use bevy_asset::{AssetEvent, Assets, Handle};
use bevy_derive::Resource;
use legion::prelude::*;
use std::{collections::HashSet, fs::File};
pub const TEXTURE_ASSET_INDEX: usize = 0;
pub const SAMPLER_ASSET_INDEX: usize = 1;
@ -38,6 +45,74 @@ impl Texture {
height,
}
}
pub fn texture_resource_system(
mut state: ResMut<TextureResourceSystemState>,
render_resources: Res<RenderResources>,
textures: Res<Assets<Texture>>,
texture_events: Res<Events<AssetEvent<Texture>>>,
) {
let render_resources = &*render_resources.context;
let mut changed_textures = HashSet::new();
for event in state.event_reader.iter(&texture_events) {
match event {
AssetEvent::Created { handle } => {
changed_textures.insert(*handle);
}
AssetEvent::Modified { handle } => {
changed_textures.insert(*handle);
Self::remove_current_texture_resources(render_resources, *handle);
}
AssetEvent::Removed { handle } => {
Self::remove_current_texture_resources(render_resources, *handle);
// if texture was modified and removed in the same update, ignore the modification
// events are ordered so future modification events are ok
changed_textures.remove(handle);
}
}
}
for texture_handle in changed_textures.iter() {
if let Some(texture) = textures.get(texture_handle) {
let texture_descriptor: TextureDescriptor = texture.into();
let texture_resource = render_resources.create_texture(texture_descriptor);
let sampler_descriptor: SamplerDescriptor = texture.into();
let sampler_resource = render_resources.create_sampler(&sampler_descriptor);
render_resources.set_asset_resource(
*texture_handle,
texture_resource,
TEXTURE_ASSET_INDEX,
);
render_resources.set_asset_resource(
*texture_handle,
sampler_resource,
SAMPLER_ASSET_INDEX,
);
}
}
}
fn remove_current_texture_resources(
render_resources: &dyn RenderResourceContext,
handle: Handle<Texture>,
) {
if let Some(resource) = render_resources.get_asset_resource(handle, TEXTURE_ASSET_INDEX) {
render_resources.remove_texture(resource);
render_resources.remove_asset_resource(handle, TEXTURE_ASSET_INDEX);
}
if let Some(resource) = render_resources.get_asset_resource(handle, SAMPLER_ASSET_INDEX) {
render_resources.remove_sampler(resource);
render_resources.remove_asset_resource(handle, SAMPLER_ASSET_INDEX);
}
}
}
#[derive(Resource)]
#[module(meta = false)]
pub struct TextureResourceSystemState {
event_reader: EventReader<AssetEvent<Texture>>,
}
impl ShaderDefSuffixProvider for Option<Handle<Texture>> {

View File

@ -23,7 +23,7 @@ impl Default for Label {
}
impl Label {
// PERF: this is horrendously inefficient. (1) new texture every frame (2) no atlas (3) new texture for every label
// PERF: this is horrendously inefficient. (1) new texture per label per frame (2) no atlas
pub fn label_system(
mut color_materials: ResMut<Assets<ColorMaterial>>,
mut textures: ResMut<Assets<Texture>>,
@ -41,11 +41,12 @@ impl Label {
);
let material = color_materials.get_or_insert_with(*color_material_handle, || ColorMaterial::from(Handle::<Texture>::new()));
if let Some(texture) = material.texture {
// TODO: remove texture
}
if let Some(texture_handle) = material.texture {
textures.set(texture_handle, texture);
material.texture = Some(textures.add(texture));
} else {
material.texture = Some(textures.add(texture));
}
}
}
}