make DrawTarget setup world read only. add render_resource_sets_system
This commit is contained in:
parent
1d44b4034f
commit
a8f5402ff1
@ -163,6 +163,10 @@ impl<T> AssetStorage<T> {
|
|||||||
pub fn get_mut(&mut self, handle: &Handle<T>) -> Option<&mut T> {
|
pub fn get_mut(&mut self, handle: &Handle<T>) -> Option<&mut T> {
|
||||||
self.assets.get_mut(&handle.id)
|
self.assets.get_mut(&handle.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item=(Handle<T>, &T)> {
|
||||||
|
self.assets.iter().map(|(k,v)| (Handle::new(*k), v))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> GetBytes for Handle<T> {
|
impl<T> GetBytes for Handle<T> {
|
||||||
|
@ -16,7 +16,7 @@ pub trait DrawTarget {
|
|||||||
);
|
);
|
||||||
fn setup(
|
fn setup(
|
||||||
&mut self,
|
&mut self,
|
||||||
_world: &mut World,
|
_world: &World,
|
||||||
_resources: &Resources,
|
_resources: &Resources,
|
||||||
_renderer: &mut dyn Renderer,
|
_renderer: &mut dyn Renderer,
|
||||||
_pipeline_handle: Handle<PipelineDescriptor>,
|
_pipeline_handle: Handle<PipelineDescriptor>,
|
||||||
|
@ -62,7 +62,7 @@ impl DrawTarget for AssignedBatchesDrawTarget {
|
|||||||
|
|
||||||
fn setup(
|
fn setup(
|
||||||
&mut self,
|
&mut self,
|
||||||
world: &mut World,
|
world: &World,
|
||||||
resources: &Resources,
|
resources: &Resources,
|
||||||
renderer: &mut dyn Renderer,
|
renderer: &mut dyn Renderer,
|
||||||
pipeline_handle: Handle<PipelineDescriptor>,
|
pipeline_handle: Handle<PipelineDescriptor>,
|
||||||
@ -89,8 +89,8 @@ impl DrawTarget for AssignedBatchesDrawTarget {
|
|||||||
log::trace!("{:#?}", batch);
|
log::trace!("{:#?}", batch);
|
||||||
renderer.setup_bind_groups(&mut batch.render_resource_assignments, pipeline_descriptor);
|
renderer.setup_bind_groups(&mut batch.render_resource_assignments, pipeline_descriptor);
|
||||||
for batched_entity in batch.entities.iter() {
|
for batched_entity in batch.entities.iter() {
|
||||||
let mut renderable = world
|
let renderable = world
|
||||||
.get_component_mut::<Renderable>(*batched_entity)
|
.get_component::<Renderable>(*batched_entity)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if !renderable.is_visible || renderable.is_instanced {
|
if !renderable.is_visible || renderable.is_instanced {
|
||||||
continue;
|
continue;
|
||||||
@ -102,7 +102,7 @@ impl DrawTarget for AssignedBatchesDrawTarget {
|
|||||||
batch.render_resource_assignments.id
|
batch.render_resource_assignments.id
|
||||||
);
|
);
|
||||||
renderer.setup_bind_groups(
|
renderer.setup_bind_groups(
|
||||||
&mut renderable.render_resource_assignments,
|
&renderable.render_resource_assignments,
|
||||||
pipeline_descriptor,
|
pipeline_descriptor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use legion::prelude::*;
|
|||||||
use crate::{
|
use crate::{
|
||||||
draw_target::DrawTarget,
|
draw_target::DrawTarget,
|
||||||
mesh::Mesh,
|
mesh::Mesh,
|
||||||
pipeline::{PipelineDescriptor, ShaderPipelineAssignments},
|
pipeline::{PipelineDescriptor, PipelineAssignments},
|
||||||
render_resource::{
|
render_resource::{
|
||||||
resource_name, EntityRenderResourceAssignments, RenderResourceAssignments, ResourceInfo,
|
resource_name, EntityRenderResourceAssignments, RenderResourceAssignments, ResourceInfo,
|
||||||
},
|
},
|
||||||
@ -23,7 +23,7 @@ impl DrawTarget for AssignedMeshesDrawTarget {
|
|||||||
render_pass: &mut dyn RenderPass,
|
render_pass: &mut dyn RenderPass,
|
||||||
pipeline_handle: Handle<PipelineDescriptor>,
|
pipeline_handle: Handle<PipelineDescriptor>,
|
||||||
) {
|
) {
|
||||||
let shader_pipeline_assignments = resources.get::<ShaderPipelineAssignments>().unwrap();
|
let shader_pipeline_assignments = resources.get::<PipelineAssignments>().unwrap();
|
||||||
let entity_render_resource_assignments =
|
let entity_render_resource_assignments =
|
||||||
resources.get::<EntityRenderResourceAssignments>().unwrap();
|
resources.get::<EntityRenderResourceAssignments>().unwrap();
|
||||||
let mut current_mesh_handle = None;
|
let mut current_mesh_handle = None;
|
||||||
@ -78,16 +78,16 @@ impl DrawTarget for AssignedMeshesDrawTarget {
|
|||||||
|
|
||||||
fn setup(
|
fn setup(
|
||||||
&mut self,
|
&mut self,
|
||||||
world: &mut World,
|
world: &World,
|
||||||
resources: &Resources,
|
resources: &Resources,
|
||||||
renderer: &mut dyn Renderer,
|
renderer: &mut dyn Renderer,
|
||||||
pipeline_handle: Handle<PipelineDescriptor>,
|
pipeline_handle: Handle<PipelineDescriptor>,
|
||||||
pipeline_descriptor: &PipelineDescriptor,
|
pipeline_descriptor: &PipelineDescriptor,
|
||||||
) {
|
) {
|
||||||
let shader_pipeline_assignments = resources.get::<ShaderPipelineAssignments>().unwrap();
|
let pipeline_assignments = resources.get::<PipelineAssignments>().unwrap();
|
||||||
let entity_render_resource_assignments =
|
let entity_render_resource_assignments =
|
||||||
resources.get::<EntityRenderResourceAssignments>().unwrap();
|
resources.get::<EntityRenderResourceAssignments>().unwrap();
|
||||||
let assigned_render_resource_assignments = shader_pipeline_assignments
|
let assigned_render_resource_assignments = pipeline_assignments
|
||||||
.assignments
|
.assignments
|
||||||
.get(&pipeline_handle);
|
.get(&pipeline_handle);
|
||||||
let mut global_render_resource_assignments =
|
let mut global_render_resource_assignments =
|
||||||
@ -98,13 +98,13 @@ impl DrawTarget for AssignedMeshesDrawTarget {
|
|||||||
let entity = entity_render_resource_assignments
|
let entity = entity_render_resource_assignments
|
||||||
.get(*assignment_id)
|
.get(*assignment_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut renderable = world.get_component_mut::<Renderable>(*entity).unwrap();
|
let renderable = world.get_component::<Renderable>(*entity).unwrap();
|
||||||
if !renderable.is_visible || renderable.is_instanced {
|
if !renderable.is_visible || renderable.is_instanced {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.setup_bind_groups(
|
renderer.setup_bind_groups(
|
||||||
&mut renderable.render_resource_assignments,
|
&renderable.render_resource_assignments,
|
||||||
pipeline_descriptor,
|
pipeline_descriptor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ impl DrawTarget for UiDrawTarget {
|
|||||||
|
|
||||||
fn setup(
|
fn setup(
|
||||||
&mut self,
|
&mut self,
|
||||||
_world: &mut World,
|
_world: &World,
|
||||||
resources: &Resources,
|
resources: &Resources,
|
||||||
renderer: &mut dyn Renderer,
|
renderer: &mut dyn Renderer,
|
||||||
_pipeline_handle: Handle<PipelineDescriptor>,
|
_pipeline_handle: Handle<PipelineDescriptor>,
|
||||||
|
@ -36,7 +36,7 @@ use self::{
|
|||||||
pass::passes::ForwardPassBuilder,
|
pass::passes::ForwardPassBuilder,
|
||||||
pipeline::{
|
pipeline::{
|
||||||
pipelines::ForwardPipelineBuilder, PipelineCompiler, PipelineDescriptor,
|
pipelines::ForwardPipelineBuilder, PipelineCompiler, PipelineDescriptor,
|
||||||
ShaderPipelineAssignments, VertexBufferDescriptors,
|
PipelineAssignments, VertexBufferDescriptors,
|
||||||
},
|
},
|
||||||
render_graph::RenderGraph,
|
render_graph::RenderGraph,
|
||||||
render_resource::{
|
render_resource::{
|
||||||
@ -107,7 +107,7 @@ impl AppPlugin for RenderPlugin {
|
|||||||
.add_resource(AssetStorage::<Shader>::new())
|
.add_resource(AssetStorage::<Shader>::new())
|
||||||
.add_resource(AssetStorage::<StandardMaterial>::new())
|
.add_resource(AssetStorage::<StandardMaterial>::new())
|
||||||
.add_resource(AssetStorage::<PipelineDescriptor>::new())
|
.add_resource(AssetStorage::<PipelineDescriptor>::new())
|
||||||
.add_resource(ShaderPipelineAssignments::new())
|
.add_resource(PipelineAssignments::new())
|
||||||
.add_resource(VertexBufferDescriptors::default())
|
.add_resource(VertexBufferDescriptors::default())
|
||||||
.add_resource(PipelineCompiler::new())
|
.add_resource(PipelineCompiler::new())
|
||||||
.add_resource(RenderResourceAssignments::default())
|
.add_resource(RenderResourceAssignments::default())
|
||||||
|
@ -151,7 +151,7 @@ impl PipelineCompiler {
|
|||||||
fn update_shader_assignments(
|
fn update_shader_assignments(
|
||||||
&mut self,
|
&mut self,
|
||||||
vertex_buffer_descriptors: &VertexBufferDescriptors,
|
vertex_buffer_descriptors: &VertexBufferDescriptors,
|
||||||
shader_pipeline_assignments: &mut ShaderPipelineAssignments,
|
shader_pipeline_assignments: &mut PipelineAssignments,
|
||||||
renderer: &dyn Renderer,
|
renderer: &dyn Renderer,
|
||||||
pipeline_storage: &mut AssetStorage<PipelineDescriptor>,
|
pipeline_storage: &mut AssetStorage<PipelineDescriptor>,
|
||||||
shader_storage: &mut AssetStorage<Shader>,
|
shader_storage: &mut AssetStorage<Shader>,
|
||||||
@ -222,13 +222,13 @@ impl PipelineCompiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ShaderPipelineAssignments {
|
pub struct PipelineAssignments {
|
||||||
pub assignments: HashMap<Handle<PipelineDescriptor>, Vec<RenderResourceAssignmentsId>>,
|
pub assignments: HashMap<Handle<PipelineDescriptor>, Vec<RenderResourceAssignmentsId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShaderPipelineAssignments {
|
impl PipelineAssignments {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
ShaderPipelineAssignments {
|
PipelineAssignments {
|
||||||
assignments: HashMap::new(),
|
assignments: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -244,7 +244,7 @@ pub fn update_shader_assignments(
|
|||||||
// lots of string + hashset allocations. sees uniform_resource_provider for more context
|
// lots of string + hashset allocations. sees uniform_resource_provider for more context
|
||||||
{
|
{
|
||||||
let mut shader_pipeline_assignments =
|
let mut shader_pipeline_assignments =
|
||||||
resources.get_mut::<ShaderPipelineAssignments>().unwrap();
|
resources.get_mut::<PipelineAssignments>().unwrap();
|
||||||
let mut pipeline_compiler = resources.get_mut::<PipelineCompiler>().unwrap();
|
let mut pipeline_compiler = resources.get_mut::<PipelineCompiler>().unwrap();
|
||||||
let mut shader_storage = resources.get_mut::<AssetStorage<Shader>>().unwrap();
|
let mut shader_storage = resources.get_mut::<AssetStorage<Shader>>().unwrap();
|
||||||
let vertex_buffer_descriptors = resources.get::<VertexBufferDescriptors>().unwrap();
|
let vertex_buffer_descriptors = resources.get::<VertexBufferDescriptors>().unwrap();
|
||||||
|
@ -15,7 +15,6 @@ use std::collections::{HashMap, HashSet};
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct RenderGraph {
|
pub struct RenderGraph {
|
||||||
pub pipeline_descriptors: HashSet<Handle<PipelineDescriptor>>,
|
pub pipeline_descriptors: HashSet<Handle<PipelineDescriptor>>,
|
||||||
// TODO: make this ordered
|
|
||||||
pub pass_descriptors: HashMap<String, PassDescriptor>,
|
pub pass_descriptors: HashMap<String, PassDescriptor>,
|
||||||
pub pass_pipelines: HashMap<String, Vec<Handle<PipelineDescriptor>>>,
|
pub pass_pipelines: HashMap<String, Vec<Handle<PipelineDescriptor>>>,
|
||||||
pub resource_providers: Vec<Box<dyn ResourceProvider + Send + Sync>>,
|
pub resource_providers: Vec<Box<dyn ResourceProvider + Send + Sync>>,
|
||||||
|
@ -68,7 +68,7 @@ impl RenderResourceAssignments {
|
|||||||
.insert(name.to_string(), (vertices_resource, indices_resource));
|
.insert(name.to_string(), (vertices_resource, indices_resource));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_or_update_render_resource_set_id(
|
pub fn update_render_resource_set_id(
|
||||||
&mut self,
|
&mut self,
|
||||||
bind_group_descriptor: &BindGroupDescriptor,
|
bind_group_descriptor: &BindGroupDescriptor,
|
||||||
) -> Option<RenderResourceSetId> {
|
) -> Option<RenderResourceSetId> {
|
||||||
@ -191,23 +191,23 @@ mod tests {
|
|||||||
equal_assignments.set("a", RenderResource(1));
|
equal_assignments.set("a", RenderResource(1));
|
||||||
equal_assignments.set("b", RenderResource(2));
|
equal_assignments.set("b", RenderResource(2));
|
||||||
|
|
||||||
let set_id = assignments.get_or_update_render_resource_set_id(&bind_group_descriptor);
|
let set_id = assignments.update_render_resource_set_id(&bind_group_descriptor);
|
||||||
assert_ne!(set_id, None);
|
assert_ne!(set_id, None);
|
||||||
|
|
||||||
let different_set_id =
|
let different_set_id =
|
||||||
different_assignments.get_or_update_render_resource_set_id(&bind_group_descriptor);
|
different_assignments.update_render_resource_set_id(&bind_group_descriptor);
|
||||||
assert_ne!(different_set_id, None);
|
assert_ne!(different_set_id, None);
|
||||||
assert_ne!(different_set_id, set_id);
|
assert_ne!(different_set_id, set_id);
|
||||||
|
|
||||||
let equal_set_id =
|
let equal_set_id =
|
||||||
equal_assignments.get_or_update_render_resource_set_id(&bind_group_descriptor);
|
equal_assignments.update_render_resource_set_id(&bind_group_descriptor);
|
||||||
assert_ne!(equal_set_id, None);
|
assert_ne!(equal_set_id, None);
|
||||||
assert_eq!(equal_set_id, set_id);
|
assert_eq!(equal_set_id, set_id);
|
||||||
|
|
||||||
let mut unmatched_assignments = RenderResourceAssignments::default();
|
let mut unmatched_assignments = RenderResourceAssignments::default();
|
||||||
unmatched_assignments.set("a", RenderResource(1));
|
unmatched_assignments.set("a", RenderResource(1));
|
||||||
let unmatched_set_id =
|
let unmatched_set_id =
|
||||||
unmatched_assignments.get_or_update_render_resource_set_id(&bind_group_descriptor);
|
unmatched_assignments.update_render_resource_set_id(&bind_group_descriptor);
|
||||||
assert_eq!(unmatched_set_id, None);
|
assert_eq!(unmatched_set_id, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ pub trait Renderer {
|
|||||||
);
|
);
|
||||||
fn setup_bind_groups(
|
fn setup_bind_groups(
|
||||||
&mut self,
|
&mut self,
|
||||||
render_resource_assignments: &mut RenderResourceAssignments,
|
render_resource_assignments: &RenderResourceAssignments,
|
||||||
pipeline_descriptor: &PipelineDescriptor,
|
pipeline_descriptor: &PipelineDescriptor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
mod wgpu_render_context;
|
mod wgpu_render_context;
|
||||||
mod wgpu_render_resource_context;
|
mod wgpu_render_resource_context;
|
||||||
mod wgpu_transactional_render_resource_context;
|
mod wgpu_transactional_render_resource_context;
|
||||||
|
mod systems;
|
||||||
|
|
||||||
pub use wgpu_render_context::*;
|
pub use wgpu_render_context::*;
|
||||||
pub use wgpu_render_resource_context::*;
|
pub use wgpu_render_resource_context::*;
|
||||||
pub use wgpu_transactional_render_resource_context::*;
|
pub use wgpu_transactional_render_resource_context::*;
|
||||||
|
pub use systems::*;
|
62
bevy_wgpu/src/renderer_2/systems.rs
Normal file
62
bevy_wgpu/src/renderer_2/systems.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
use bevy_asset::AssetStorage;
|
||||||
|
use bevy_render::{
|
||||||
|
pipeline::{PipelineCompiler, PipelineDescriptor, PipelineAssignments},
|
||||||
|
render_graph::RenderGraph,
|
||||||
|
render_resource::{EntityRenderResourceAssignments, RenderResourceAssignments}, Renderable,
|
||||||
|
};
|
||||||
|
use legion::prelude::*;
|
||||||
|
|
||||||
|
pub fn render_resource_sets_system() -> Box<dyn Schedulable> {
|
||||||
|
SystemBuilder::new("update_render_resource_sets")
|
||||||
|
.read_resource::<RenderGraph>()
|
||||||
|
.read_resource::<AssetStorage<PipelineDescriptor>>()
|
||||||
|
.read_resource::<PipelineCompiler>()
|
||||||
|
.read_resource::<PipelineAssignments>()
|
||||||
|
.read_resource::<EntityRenderResourceAssignments>()
|
||||||
|
.write_resource::<RenderResourceAssignments>()
|
||||||
|
.write_component::<Renderable>()
|
||||||
|
.build(
|
||||||
|
|_,
|
||||||
|
world,
|
||||||
|
(
|
||||||
|
render_graph,
|
||||||
|
pipelines,
|
||||||
|
pipeline_compiler,
|
||||||
|
pipeline_assignments,
|
||||||
|
entity_render_resource_assignments,
|
||||||
|
global_render_resource_assignments,
|
||||||
|
),
|
||||||
|
_| {
|
||||||
|
// PERF: consider doing a par-iter over all renderable components so this can be parallelized
|
||||||
|
for handle in render_graph.pipeline_descriptors.iter() {
|
||||||
|
for compiled_pipeline_handle in pipeline_compiler.iter_compiled_pipelines(*handle).unwrap() {
|
||||||
|
if let Some(compiled_pipeline_assignments) =
|
||||||
|
pipeline_assignments.assignments.get(compiled_pipeline_handle)
|
||||||
|
{
|
||||||
|
let compiled_pipeline = pipelines.get(compiled_pipeline_handle).unwrap();
|
||||||
|
let pipeline_layout = compiled_pipeline.get_layout().unwrap();
|
||||||
|
|
||||||
|
for bind_group in pipeline_layout.bind_groups.iter() {
|
||||||
|
global_render_resource_assignments.update_render_resource_set_id(bind_group);
|
||||||
|
}
|
||||||
|
|
||||||
|
for assignment_id in compiled_pipeline_assignments.iter() {
|
||||||
|
let entity = entity_render_resource_assignments
|
||||||
|
.get(*assignment_id)
|
||||||
|
.unwrap();
|
||||||
|
let mut renderable = world.get_component_mut::<Renderable>(*entity).unwrap();
|
||||||
|
if !renderable.is_visible || renderable.is_instanced {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for bind_group in pipeline_layout.bind_groups.iter() {
|
||||||
|
renderable.render_resource_assignments.update_render_resource_set_id(bind_group);
|
||||||
|
// TODO: also setup bind groups here if possible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
@ -3,7 +3,7 @@ use super::{
|
|||||||
WgpuRenderPass, WgpuResources,
|
WgpuRenderPass, WgpuResources,
|
||||||
};
|
};
|
||||||
use crate::renderer_2::{
|
use crate::renderer_2::{
|
||||||
WgpuRenderContext, WgpuRenderResourceContext, WgpuTransactionalRenderResourceContext,
|
WgpuRenderContext, WgpuRenderResourceContext, WgpuTransactionalRenderResourceContext, render_resource_sets_system,
|
||||||
};
|
};
|
||||||
use bevy_app::{EventReader, Events};
|
use bevy_app::{EventReader, Events};
|
||||||
use bevy_asset::{AssetStorage, Handle};
|
use bevy_asset::{AssetStorage, Handle};
|
||||||
@ -467,6 +467,7 @@ impl Renderer for WgpuRenderer {
|
|||||||
self.create_queued_textures(resources);
|
self.create_queued_textures(resources);
|
||||||
let mut encoder = self.encoder.take().unwrap();
|
let mut encoder = self.encoder.take().unwrap();
|
||||||
|
|
||||||
|
render_resource_sets_system().run(world, resources);
|
||||||
// setup draw targets
|
// setup draw targets
|
||||||
let mut render_graph = resources.get_mut::<RenderGraph>().unwrap();
|
let mut render_graph = resources.get_mut::<RenderGraph>().unwrap();
|
||||||
render_graph.setup_pipeline_draw_targets(world, resources, self);
|
render_graph.setup_pipeline_draw_targets(world, resources, self);
|
||||||
@ -665,19 +666,19 @@ impl Renderer for WgpuRenderer {
|
|||||||
|
|
||||||
fn setup_bind_groups(
|
fn setup_bind_groups(
|
||||||
&mut self,
|
&mut self,
|
||||||
render_resource_assignments: &mut RenderResourceAssignments,
|
render_resource_assignments: &RenderResourceAssignments,
|
||||||
pipeline_descriptor: &PipelineDescriptor,
|
pipeline_descriptor: &PipelineDescriptor,
|
||||||
) {
|
) {
|
||||||
let pipeline_layout = pipeline_descriptor.get_layout().unwrap();
|
let pipeline_layout = pipeline_descriptor.get_layout().unwrap();
|
||||||
for bind_group in pipeline_layout.bind_groups.iter() {
|
for bind_group in pipeline_layout.bind_groups.iter() {
|
||||||
if let Some(render_resource_set_id) =
|
if let Some((render_resource_set_id, _indices)) =
|
||||||
render_resource_assignments.get_or_update_render_resource_set_id(bind_group)
|
render_resource_assignments.get_render_resource_set_id(bind_group.id)
|
||||||
{
|
{
|
||||||
if let None = self
|
if let None = self
|
||||||
.global_context
|
.global_context
|
||||||
.render_resources
|
.render_resources
|
||||||
.wgpu_resources
|
.wgpu_resources
|
||||||
.get_bind_group(bind_group.id, render_resource_set_id)
|
.get_bind_group(bind_group.id, *render_resource_set_id)
|
||||||
{
|
{
|
||||||
self.global_context
|
self.global_context
|
||||||
.render_resources
|
.render_resources
|
||||||
|
Loading…
Reference in New Issue
Block a user