d3d12: filter chain pass

This commit is contained in:
chyyran 2023-02-01 00:16:06 -05:00 committed by Ronny Chan
parent e1154ac52a
commit 03b4a7574e
10 changed files with 280 additions and 48 deletions

View file

@ -5,8 +5,8 @@ use crate::error;
use crate::error::assume_d3d12_init;
pub struct D3D12ConstantBuffer {
buffer: D3D12Buffer,
desc: D3D12_CONSTANT_BUFFER_VIEW_DESC,
pub buffer: D3D12Buffer,
pub desc: D3D12_CONSTANT_BUFFER_VIEW_DESC,
}
pub struct D3D12Buffer {

View file

@ -1,6 +1,6 @@
use std::collections::VecDeque;
use crate::{error};
use crate::heap::{D3D12DescriptorHeap, CpuStagingHeap, ResourceWorkHeap, SamplerWorkHeap};
use crate::heap::{D3D12DescriptorHeap, CpuStagingHeap, ResourceWorkHeap, SamplerWorkHeap, RenderTargetHeap};
use crate::samplers::SamplerSet;
use crate::luts::LutTexture;
use librashader_presets::{ShaderPreset, TextureConfig};
@ -14,17 +14,14 @@ use std::path::Path;
use windows::core::Interface;
use windows::w;
use windows::Win32::Foundation::CloseHandle;
use windows::Win32::Graphics::Direct3D12::{
ID3D12CommandAllocator, ID3D12CommandQueue, ID3D12Device, ID3D12Fence,
ID3D12GraphicsCommandList, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_DESC,
D3D12_COMMAND_QUEUE_FLAG_NONE, D3D12_FENCE_FLAG_NONE,
};
use windows::Win32::Graphics::Direct3D12::{ID3D12CommandAllocator, ID3D12CommandQueue, ID3D12Device, ID3D12Fence, ID3D12GraphicsCommandList, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_DESC, D3D12_COMMAND_QUEUE_FLAG_NONE, D3D12_FENCE_FLAG_NONE, ID3D12DescriptorHeap};
use windows::Win32::System::Threading::{CreateEventA, ResetEvent, WaitForSingleObject};
use windows::Win32::System::WindowsProgramming::INFINITE;
use librashader_common::{ImageFormat, Size};
use librashader_common::{ImageFormat, Size, Viewport};
use librashader_reflect::back::{CompileReflectShader, CompileShader};
use librashader_reflect::reflect::ReflectShader;
use librashader_reflect::reflect::semantics::{MAX_BINDINGS_COUNT, ShaderSemantics, TextureSemantics, UniformBinding};
use librashader_runtime::binding::TextureInput;
use librashader_runtime::uniforms::UniformStorage;
use crate::buffer::{D3D12Buffer, D3D12ConstantBuffer};
use crate::filter_pass::FilterPass;
@ -32,7 +29,7 @@ use crate::framebuffer::OwnedImage;
use crate::graphics_pipeline::{D3D12GraphicsPipeline, D3D12RootSignature};
use crate::mipmap::D3D12MipmapGen;
use crate::quad_render::DrawQuad;
use crate::texture::InputTexture;
use crate::texture::{InputTexture, OutputTexture};
type ShaderPassMeta = ShaderPassArtifact<impl CompileReflectShader<HLSL, GlslangCompilation>>;
@ -48,6 +45,10 @@ pub struct FilterChainD3D12 {
pub(crate) feedback_framebuffers: Box<[OwnedImage]>,
pub(crate) history_framebuffers: VecDeque<OwnedImage>,
staging_heap: D3D12DescriptorHeap<CpuStagingHeap>,
rtv_heap: D3D12DescriptorHeap<RenderTargetHeap>,
pub texture_heap: ID3D12DescriptorHeap,
pub sampler_heap: ID3D12DescriptorHeap,
}
pub(crate) struct FilterCommon {
@ -97,6 +98,10 @@ impl FilterChainD3D12 {
D3D12DescriptorHeap::new(device,
(MAX_BINDINGS_COUNT as usize) *
shader_count + 2048 + lut_count)?;
let mut rtv_heap =
D3D12DescriptorHeap::new(device,
(MAX_BINDINGS_COUNT as usize) *
shader_count + 2048 + lut_count)?;
@ -104,7 +109,8 @@ impl FilterChainD3D12 {
let root_signature = D3D12RootSignature::new(device)?;
let filters = FilterChainD3D12::init_passes(device, &root_signature, passes, &semantics)?;
let (texture_heap, sampler_heap, filters)
= FilterChainD3D12::init_passes(device, &root_signature, passes, &semantics)?;
@ -172,10 +178,13 @@ impl FilterChainD3D12 {
history_textures,
},
staging_heap,
rtv_heap,
passes: filters,
output_framebuffers: output_framebuffers.into_boxed_slice(),
feedback_framebuffers: feedback_framebuffers.into_boxed_slice(),
history_framebuffers,
texture_heap,
sampler_heap
})
}
@ -329,7 +338,7 @@ impl FilterChainD3D12 {
root_signature: &D3D12RootSignature,
passes: Vec<ShaderPassMeta>,
semantics: &ShaderSemantics,)
-> error::Result<Vec<FilterPass>> {
-> error::Result<(ID3D12DescriptorHeap, ID3D12DescriptorHeap, Vec<FilterPass>)> {
let mut filters = Vec::new();
let shader_count = passes.len();
@ -337,8 +346,8 @@ impl FilterChainD3D12 {
D3D12DescriptorHeap::<ResourceWorkHeap>::new(device,
(MAX_BINDINGS_COUNT as usize) *
shader_count)?;
let work_heaps = unsafe {
work_heap.suballocate(shader_count)
let (work_heaps, texture_heap_handle) = unsafe {
work_heap.suballocate(MAX_BINDINGS_COUNT as usize)
};
@ -346,12 +355,12 @@ impl FilterChainD3D12 {
D3D12DescriptorHeap::new(device,
(MAX_BINDINGS_COUNT as usize) * shader_count)?;
let sampler_work_heaps = unsafe {
sampler_work_heap.suballocate(shader_count)
let (sampler_work_heaps, sampler_heap_handle) = unsafe {
sampler_work_heap.suballocate(MAX_BINDINGS_COUNT as usize)
};
for (index, (((config, source, mut reflect),
texture_heap), sampler_heap))
mut texture_heap), mut sampler_heap))
in passes.into_iter()
.zip(work_heaps)
.zip(sampler_work_heaps)
@ -411,6 +420,8 @@ impl FilterChainD3D12 {
uniform_bindings.insert(UniformBinding::TextureSize(*semantics), param.offset);
}
let texture_heap = texture_heap.alloc_range()?;
let sampler_heap = sampler_heap.alloc_range()?;
filters.push(FilterPass {
reflection,
uniform_bindings,
@ -426,7 +437,84 @@ impl FilterChainD3D12 {
}
Ok(filters)
Ok((texture_heap_handle, sampler_heap_handle, filters))
}
/// Process a frame with the input image.
pub fn frame(
&mut self,
input: InputTexture,
viewport: &Viewport<OutputTexture>,
frame_count: usize,
options: Option<&()>,
) -> error::Result<()>
{
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled);
let passes = &mut self.passes[0..max];
if passes.is_empty() {
return Ok(());
}
let filter = passes[0].config.filter;
let wrap_mode = passes[0].config.wrap_mode;
for ((texture, fbo), pass) in self
.common
.feedback_textures
.iter_mut()
.zip(self.feedback_framebuffers.iter())
.zip(passes.iter())
{
*texture = Some(fbo.create_shader_resource_view(&mut self.staging_heap,
pass.config.filter,
pass.config.wrap_mode)?);
}
for (texture, fbo) in self
.common
.history_textures
.iter_mut()
.zip(self.history_framebuffers.iter())
{
*texture = Some(fbo.create_shader_resource_view(&mut self.staging_heap,
filter,
wrap_mode)?);
}
let original = &input;
let mut source = &input;
// rescale render buffers to ensure all bindings are valid.
let mut source_size = source.size();
let mut iterator = passes.iter_mut().enumerate().peekable();
while let Some((index, pass)) = iterator.next() {
let should_mipmap = iterator
.peek()
.map(|(_, p)| p.config.mipmap_input)
.unwrap_or(false);
// let next_size = self.output_framebuffers[index].scale(
// pass.config.scaling.clone(),
// pass.get_format(),
// &viewport.output.size,
// &source_size,
// should_mipmap,
// )?;
//
// self.feedback_framebuffers[index].scale(
// pass.config.scaling.clone(),
// pass.get_format(),
// &viewport.output.size,
// &source_size,
// should_mipmap,
// )?;
// source_size = next_size;
}
Ok(())
}
}

View file

@ -1,19 +1,24 @@
use rustc_hash::FxHashMap;
use windows::core::Interface;
use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Direct3D11::ID3D11Device;
use windows::Win32::Graphics::Direct3D12::ID3D12Device;
use librashader_common::{ImageFormat, Size};
use windows::Win32::Graphics::Direct3D12::{D3D12_RENDER_PASS_BEGINNING_ACCESS, D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD, D3D12_RENDER_PASS_ENDING_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE, D3D12_RENDER_PASS_FLAG_NONE, D3D12_RENDER_PASS_RENDER_TARGET_DESC, D3D12_VIEWPORT, ID3D12CommandList, ID3D12Device, ID3D12GraphicsCommandList, ID3D12GraphicsCommandList4};
use librashader_common::{ImageFormat, Size, Viewport};
use librashader_preprocess::ShaderSource;
use librashader_presets::ShaderPassConfig;
use librashader_reflect::reflect::semantics::{MemberOffset, TextureBinding, UniformBinding};
use librashader_reflect::reflect::ShaderReflection;
use librashader_runtime::binding::{BindSemantics, TextureInput};
use librashader_runtime::uniforms::UniformStorage;
use librashader_runtime::quad::QuadType;
use librashader_runtime::uniforms::{UniformStorage, UniformStorageAccess};
use crate::buffer::D3D12ConstantBuffer;
use crate::{error, util};
use crate::filter_chain::FilterCommon;
use crate::graphics_pipeline::D3D12GraphicsPipeline;
use crate::heap::{D3D12DescriptorHeap, D3D12DescriptorHeapSlot, ResourceWorkHeap, SamplerWorkHeap};
use crate::render_target::RenderTarget;
use crate::samplers::SamplerSet;
use crate::texture::InputTexture;
use crate::texture::{InputTexture, OutputTexture};
pub(crate) struct FilterPass {
pub(crate) pipeline: D3D12GraphicsPipeline,
@ -23,8 +28,8 @@ pub(crate) struct FilterPass {
pub uniform_storage: UniformStorage,
pub(crate) push_cbuffer: Option<D3D12ConstantBuffer>,
pub(crate) ubo_cbuffer: Option<D3D12ConstantBuffer>,
pub(crate) texture_heap: D3D12DescriptorHeap<ResourceWorkHeap>,
pub(crate) sampler_heap: D3D12DescriptorHeap<SamplerWorkHeap>,
pub(crate) texture_heap: [D3D12DescriptorHeapSlot<ResourceWorkHeap>; 16],
pub(crate) sampler_heap: [D3D12DescriptorHeapSlot<SamplerWorkHeap>; 16],
pub source: ShaderSource,
}
@ -34,7 +39,7 @@ impl TextureInput for InputTexture {
self.size
}
}
//
impl BindSemantics for FilterPass {
type InputTexture = InputTexture;
type SamplerSet = SamplerSet;
@ -87,18 +92,15 @@ impl FilterPass {
frame_direction: i32,
fb_size: Size<u32>,
viewport_size: Size<u32>,
mut descriptors: (
&'a mut [D3D12DescriptorHeapSlot<ResourceWorkHeap>; 16],
&'a mut [D3D12DescriptorHeapSlot<SamplerWorkHeap>; 16],
),
original: &InputTexture,
source: &InputTexture,
) {
Self::bind_semantics(
&(),
&parent.samplers,
&mut self.uniform_storage,
&mut descriptors,
&mut (&mut self.texture_heap, &mut self.sampler_heap),
mvp,
frame_count,
frame_direction,
@ -118,4 +120,121 @@ impl FilterPass {
&parent.config.parameters,
);
}
/// preconditions
/// rootsig is bound
/// descriptor heaps are bound
/// input must be ready to read from
/// output must be ready to write to
pub(crate) fn draw(
&mut self,
cmd: &ID3D12GraphicsCommandList,
pass_index: usize,
parent: &FilterCommon,
frame_count: u32,
frame_direction: i32,
viewport: &Viewport<OutputTexture>,
original: &InputTexture,
source: &InputTexture,
output: RenderTarget,
vbo_type: QuadType,
) -> error::Result<()> {
parent.draw_quad.bind_vertices(cmd, vbo_type);
unsafe {
cmd.SetPipelineState(&self.pipeline.handle);
}
self.build_semantics(
pass_index,
parent,
output.mvp,
frame_count,
frame_direction,
output.output.size,
viewport.output.size,
original,
source,
);
// todo: write directly to persistently bound cbuffer.
if let Some(ubo) = &self.reflection.ubo
&& let Some(cbuffer) = &mut self.ubo_cbuffer
&& ubo.size != 0
{
{
let guard = cbuffer.buffer.map(None)?;
guard.slice.copy_from_slice(self.uniform_storage.ubo_slice());
}
unsafe {
cmd.SetGraphicsRootConstantBufferView(2, cbuffer.desc.BufferLocation)
}
}
if let Some(push) = &self.reflection.push_constant
&& let Some(cbuffer) = &mut self.push_cbuffer
&& push.size != 0
{
{
let guard = cbuffer.buffer.map(None)?;
guard.slice.copy_from_slice(self.uniform_storage.push_slice());
}
unsafe {
cmd.SetGraphicsRootConstantBufferView(3, cbuffer.desc.BufferLocation)
}
}
unsafe {
cmd.SetGraphicsRootDescriptorTable(0, *self.texture_heap[0].as_ref());
cmd.SetGraphicsRootDescriptorTable(0, *self.sampler_heap[0].as_ref());
}
// todo: check for non-renderpass.
let cmd = cmd.cast::<ID3D12GraphicsCommandList4>()?;
unsafe {
let pass = [D3D12_RENDER_PASS_RENDER_TARGET_DESC {
cpuDescriptor: *output.output.descriptor.as_ref(),
BeginningAccess: D3D12_RENDER_PASS_BEGINNING_ACCESS {
Type: D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD,
..Default::default()
},
EndingAccess: D3D12_RENDER_PASS_ENDING_ACCESS {
Type: D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
Anonymous: Default::default(),
},
}];
cmd.BeginRenderPass(Some(&pass), None, D3D12_RENDER_PASS_FLAG_NONE)
}
unsafe {
cmd.RSSetViewports(&[D3D12_VIEWPORT {
TopLeftX: output.x,
TopLeftY: output.y,
Width: output.output.size.width as f32,
Height: output.output.size.height as f32,
MinDepth: 0.0,
MaxDepth: 1.0,
}]);
cmd.RSSetScissorRects(&[RECT {
left: 0,
top: 0,
right: output.output.size.width as i32,
bottom: output.output.size.height as i32,
}]);
// todo put this in drawquad
cmd.DrawInstanced(4, 1, 0, 0)
}
unsafe {
cmd.EndRenderPass()
}
Ok(())
}
}

View file

@ -4,8 +4,8 @@ use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
use librashader_runtime::scaling::MipmapSize;
use crate::error;
use crate::error::assume_d3d12_init;
use crate::heap::{CpuStagingHeap, D3D12DescriptorHeap};
use crate::texture::InputTexture;
use crate::heap::{CpuStagingHeap, D3D12DescriptorHeap, RenderTargetHeap};
use crate::texture::{InputTexture, OutputTexture};
use crate::util::d3d12_get_closest_format;
#[derive(Debug, Clone)]
@ -113,8 +113,8 @@ impl OwnedImage {
Ok(InputTexture::new(descriptor, self.size, self.format, wrap_mode, filter))
}
pub(crate) fn create_render_target_view(&self, heap: &mut D3D12DescriptorHeap<CpuStagingHeap>,
filter: FilterMode, wrap_mode: WrapMode) -> error::Result<InputTexture> {
pub(crate) fn create_render_target_view(&self, heap: &mut D3D12DescriptorHeap<RenderTargetHeap>
) -> error::Result<OutputTexture> {
let descriptor = heap.alloc_slot()?;
@ -133,6 +133,6 @@ impl OwnedImage {
self.device.CreateRenderTargetView(&self.handle, Some(&rtv_desc), *descriptor.as_ref());
}
Ok(InputTexture::new(descriptor, self.size, self.format, wrap_mode, filter))
Ok(OutputTexture::new(descriptor, self.size))
}
}

View file

@ -8,7 +8,7 @@ use crate::{error, util};
use crate::quad_render::DrawQuad;
pub struct D3D12GraphicsPipeline {
pipeline_state: ID3D12PipelineState,
pub(crate) handle: ID3D12PipelineState,
}
const D3D12_SLANG_ROOT_PARAMETERS: &[D3D12_ROOT_PARAMETER; 4] = &[
@ -198,7 +198,7 @@ impl D3D12GraphicsPipeline {
};
Ok(D3D12GraphicsPipeline {
pipeline_state,
handle: pipeline_state,
})
}
}

View file

@ -3,7 +3,7 @@ use std::cell::RefCell;
use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::Arc;
use windows::Win32::Graphics::Direct3D12::{ID3D12DescriptorHeap, ID3D12Device, D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_DESCRIPTOR_HEAP_DESC, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_GPU_DESCRIPTOR_HANDLE, D3D12_DESCRIPTOR_HEAP_TYPE};
use windows::Win32::Graphics::Direct3D12::{ID3D12DescriptorHeap, ID3D12Device, D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_DESCRIPTOR_HEAP_DESC, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_GPU_DESCRIPTOR_HANDLE, D3D12_DESCRIPTOR_HEAP_TYPE, D3D12_DESCRIPTOR_HEAP_TYPE_RTV};
#[const_trait]
pub trait D3D12HeapType {
@ -17,6 +17,9 @@ pub struct SamplerPaletteHeap;
#[derive(Clone)]
pub struct CpuStagingHeap;
#[derive(Clone)]
pub struct RenderTargetHeap;
#[derive(Clone)]
pub struct ResourceWorkHeap;
@ -47,6 +50,18 @@ impl const D3D12HeapType for CpuStagingHeap {
}
}
impl const D3D12HeapType for RenderTargetHeap {
// Lut texture heaps are CPU only and get bound to the descriptor heap of the shader.
fn get_desc(size: usize) -> D3D12_DESCRIPTOR_HEAP_DESC {
D3D12_DESCRIPTOR_HEAP_DESC {
Type: D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
NumDescriptors: size as u32,
Flags: D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
NodeMask: 0,
}
}
}
impl D3D12ShaderVisibleHeapType for ResourceWorkHeap {}
impl const D3D12HeapType for ResourceWorkHeap {
// Lut texture heaps are CPU only and get bound to the descriptor heap of the shader.
@ -73,7 +88,6 @@ impl const D3D12HeapType for SamplerWorkHeap {
}
}
#[derive(Clone)]
pub struct D3D12DescriptorHeapSlot<T> {
cpu_handle: D3D12_CPU_DESCRIPTOR_HANDLE,
gpu_handle: Option<D3D12_GPU_DESCRIPTOR_HANDLE>,
@ -191,7 +205,7 @@ impl<T> D3D12DescriptorHeap<T> {
/// descriptors allocated for it.
///
/// size must also divide equally into the size of the heap.
pub unsafe fn suballocate(self, size: usize) -> Vec<D3D12DescriptorHeap<T>> {
pub unsafe fn suballocate(self, size: usize) -> (Vec<D3D12DescriptorHeap<T>>, ID3D12DescriptorHeap) {
// has to be called right after creation.
assert_eq!(Arc::strong_count(&self.0), 1,
"D3D12DescriptorHeap::suballocate can only be callled immediately after creation.");
@ -237,11 +251,11 @@ impl<T> D3D12DescriptorHeap<T> {
start += size;
}
heaps.into_iter()
(heaps.into_iter()
.map(|inner| D3D12DescriptorHeap(
Arc::new(RefCell::new(inner)),
PhantomData::default()))
.collect()
.collect(), inner.heap)
}
pub fn alloc_slot(&mut self) -> error::Result<D3D12DescriptorHeapSlot<T>> {

View file

@ -15,6 +15,7 @@ mod graphics_pipeline;
mod buffer;
mod framebuffer;
mod texture;
mod render_target;
#[cfg(test)]
mod tests {

View file

@ -5,6 +5,7 @@ use windows::w;
use windows::Win32::Graphics::Direct3D::{D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP};
use windows::Win32::Graphics::Direct3D12::{D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, D3D12_INPUT_ELEMENT_DESC, D3D12_VERTEX_BUFFER_VIEW, ID3D12Device, ID3D12GraphicsCommandList, ID3D12Resource};
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_R32G32_FLOAT;
use librashader_runtime::quad::QuadType;
use crate::buffer::{D3D12Buffer};
#[repr(C)]
@ -70,7 +71,7 @@ impl DrawQuad {
})
}
pub fn bind_vertices(&self, cmd: &ID3D12GraphicsCommandList) {
pub fn bind_vertices(&self, cmd: &ID3D12GraphicsCommandList, _vbo_type: QuadType) {
unsafe {
cmd.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
cmd.IASetVertexBuffers(0, Some(&[self.view]));

View file

@ -0,0 +1,9 @@
use crate::texture::OutputTexture;
pub(crate) struct RenderTarget<'a> {
pub x: f32,
pub y: f32,
pub mvp: &'a [f32; 16],
pub output: OutputTexture,
}

View file

@ -1,6 +1,6 @@
use windows::Win32::Graphics::Direct3D12::{D3D12_CPU_DESCRIPTOR_HANDLE};
use librashader_common::{FilterMode, ImageFormat, Size, WrapMode};
use crate::heap::{CpuStagingHeap, D3D12DescriptorHeapSlot};
use crate::heap::{CpuStagingHeap, D3D12DescriptorHeapSlot, RenderTargetHeap};
pub(crate) enum InputDescriptor {
Owned(D3D12DescriptorHeapSlot<CpuStagingHeap>),
@ -8,7 +8,7 @@ pub(crate) enum InputDescriptor {
}
pub(crate) enum OutputDescriptor {
Owned(D3D12DescriptorHeapSlot<CpuStagingHeap>),
Owned(D3D12DescriptorHeapSlot<RenderTargetHeap>),
Raw(D3D12_CPU_DESCRIPTOR_HANDLE)
}
@ -31,12 +31,12 @@ impl AsRef<D3D12_CPU_DESCRIPTOR_HANDLE> for OutputDescriptor {
}
pub struct OutputTexture {
descriptor: OutputDescriptor,
size: Size<u32>,
pub(crate) descriptor: OutputDescriptor,
pub(crate) size: Size<u32>,
}
impl OutputTexture {
pub fn new(handle: D3D12DescriptorHeapSlot<CpuStagingHeap>,
pub fn new(handle: D3D12DescriptorHeapSlot<RenderTargetHeap>,
size: Size<u32>,
) -> OutputTexture {
let descriptor = OutputDescriptor::Owned(handle);