From: Josh Simmons Date: Tue, 14 May 2024 16:54:31 +0000 (+0200) Subject: shark: Apply display transform in compute shader X-Git-Url: https://git.nega.tv//gitweb.cgi?a=commitdiff_plain;h=18dcf6d800164222424421970224fc91fd288aab;p=josh%2Fnarcissus shark: Apply display transform in compute shader --- diff --git a/engine/narcissus-gpu/src/backend/vulkan/convert.rs b/engine/narcissus-gpu/src/backend/vulkan/convert.rs index c1acbfb..d2ee3eb 100644 --- a/engine/narcissus-gpu/src/backend/vulkan/convert.rs +++ b/engine/narcissus-gpu/src/backend/vulkan/convert.rs @@ -158,7 +158,8 @@ pub fn vulkan_shader_stage_flags(stage_flags: ShaderStageFlags) -> vk::ShaderSta pub fn vulkan_descriptor_type(binding_type: BindingType) -> vk::DescriptorType { match binding_type { BindingType::Sampler => vk::DescriptorType::Sampler, - BindingType::Image => vk::DescriptorType::SampledImage, + BindingType::StorageImage => vk::DescriptorType::StorageImage, + BindingType::SampledImage => vk::DescriptorType::SampledImage, BindingType::UniformBuffer => vk::DescriptorType::UniformBuffer, BindingType::StorageBuffer => vk::DescriptorType::StorageBuffer, BindingType::DynamicUniformBuffer => vk::DescriptorType::UniformBufferDynamic, diff --git a/engine/narcissus-gpu/src/backend/vulkan/mod.rs b/engine/narcissus-gpu/src/backend/vulkan/mod.rs index 46f63aa..b7e1b02 100644 --- a/engine/narcissus-gpu/src/backend/vulkan/mod.rs +++ b/engine/narcissus-gpu/src/backend/vulkan/mod.rs @@ -267,12 +267,19 @@ impl VulkanTransientBufferAllocator { } } +struct VulkanTouchedSwapchain { + image: vk::Image, + layout: vk::ImageLayout, + access_mask: vk::AccessFlags2, + stage_mask: vk::PipelineStageFlags2, +} + struct VulkanCmdEncoder { #[cfg(debug_assertions)] in_render_pass: bool, command_buffer: vk::CommandBuffer, bound_pipeline: Option, - swapchains_touched: HashMap, + swapchains_touched: HashMap, } impl Default for VulkanCmdEncoder { @@ -1571,6 +1578,60 @@ impl Device for VulkanDevice { } } + fn cmd_compute_touch_swapchain(&self, cmd_encoder: &mut CmdEncoder, image: Image) { + let cmd_encoder = self.cmd_encoder_mut(cmd_encoder); + + match self.image_pool.lock().get(image.0) { + Some(VulkanImageHolder::Swapchain(image)) => { + assert!( + !cmd_encoder.swapchains_touched.contains_key(&image.surface), + "swapchain attached multiple times in a command buffer" + ); + cmd_encoder.swapchains_touched.insert( + image.surface, + VulkanTouchedSwapchain { + image: image.image, + layout: vk::ImageLayout::General, + access_mask: vk::AccessFlags2::SHADER_STORAGE_WRITE, + stage_mask: vk::PipelineStageFlags2::COMPUTE_SHADER, + }, + ); + + // Transition swapchain image to shader storage write + let image_memory_barriers = &[vk::ImageMemoryBarrier2 { + src_stage_mask: vk::PipelineStageFlags2::COMPUTE_SHADER, + src_access_mask: vk::AccessFlags2::NONE, + dst_stage_mask: vk::PipelineStageFlags2::COMPUTE_SHADER, + dst_access_mask: vk::AccessFlags2::SHADER_STORAGE_WRITE, + src_queue_family_index: self.universal_queue_family_index, + dst_queue_family_index: self.universal_queue_family_index, + old_layout: vk::ImageLayout::Undefined, + new_layout: vk::ImageLayout::General, + image: image.image, + subresource_range: vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: !0, + base_array_layer: 0, + layer_count: !0, + }, + ..default() + }]; + + let dependency_info = vk::DependencyInfo { + image_memory_barriers: image_memory_barriers.into(), + ..default() + }; + + unsafe { + self.device_fn + .cmd_pipeline_barrier2(cmd_encoder.command_buffer, &dependency_info) + }; + } + _ => panic!(), + } + } + fn cmd_barrier( &self, cmd_encoder: &mut CmdEncoder, @@ -1786,7 +1847,7 @@ impl Device for VulkanDevice { ..default() } } - TypedBind::Image(images) => { + TypedBind::SampledImage(images) => { let image_infos_iter = images.iter().map(|(image_layout, image)| { let image_view = self.image_pool.lock().get(image.0).unwrap().image_view(); vk::DescriptorImageInfo { @@ -1809,6 +1870,29 @@ impl Device for VulkanDevice { ..default() } } + TypedBind::StorageImage(images) => { + let image_infos_iter = images.iter().map(|(image_layout, image)| { + let image_view = self.image_pool.lock().get(image.0).unwrap().image_view(); + vk::DescriptorImageInfo { + image_layout: match image_layout { + ImageLayout::Optimal => vk::ImageLayout::ReadOnlyOptimal, + ImageLayout::General => vk::ImageLayout::General, + }, + image_view, + sampler: vk::Sampler::null(), + } + }); + let image_infos = arena.alloc_slice_fill_iter(image_infos_iter); + vk::WriteDescriptorSet { + dst_set: descriptor_set, + dst_binding: bind.binding, + dst_array_element: bind.array_element, + descriptor_count: image_infos.len() as u32, + descriptor_type: vk::DescriptorType::StorageImage, + image_info: image_infos.as_ptr(), + ..default() + } + } TypedBind::UniformBuffer(buffers) => { let buffer_infos_iter = buffers.iter().map(|buffer_arg| { let (buffer, offset, range) = self.unwrap_buffer_arg(buffer_arg); @@ -1946,10 +2030,12 @@ impl Device for VulkanDevice { ); cmd_encoder.swapchains_touched.insert( image.surface, - ( - image.image, - vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT, - ), + VulkanTouchedSwapchain { + image: image.image, + layout: vk::ImageLayout::AttachmentOptimal, + access_mask: vk::AccessFlags2::COLOR_ATTACHMENT_WRITE, + stage_mask: vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT, + }, ); // transition swapchain image to attachment optimal @@ -2144,16 +2230,22 @@ impl Device for VulkanDevice { #[cfg(debug_assertions)] debug_assert!(!cmd_encoder.in_render_pass); - for &(image, _) in cmd_encoder.swapchains_touched.values() { + for &VulkanTouchedSwapchain { + image, + layout, + access_mask, + stage_mask, + } in cmd_encoder.swapchains_touched.values() + { // transition swapchain image from attachment optimal to present src let image_memory_barriers = &[vk::ImageMemoryBarrier2 { - src_stage_mask: vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT, - src_access_mask: vk::AccessFlags2::COLOR_ATTACHMENT_WRITE, - dst_stage_mask: vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT, + src_stage_mask: stage_mask, + src_access_mask: access_mask, + dst_stage_mask: stage_mask, dst_access_mask: vk::AccessFlags2::NONE, src_queue_family_index: self.universal_queue_family_index, dst_queue_family_index: self.universal_queue_family_index, - old_layout: vk::ImageLayout::AttachmentOptimal, + old_layout: layout, new_layout: vk::ImageLayout::PresentSrcKhr, image, subresource_range: vk::ImageSubresourceRange { @@ -2183,7 +2275,16 @@ impl Device for VulkanDevice { let mut signal_semaphores = Vec::new(); if !cmd_encoder.swapchains_touched.is_empty() { - for (surface, (_, stage_mask)) in cmd_encoder.swapchains_touched.drain() { + for ( + surface, + VulkanTouchedSwapchain { + image: _, + layout: _, + access_mask: _, + stage_mask, + }, + ) in cmd_encoder.swapchains_touched.drain() + { self.touch_swapchain( frame, surface, diff --git a/engine/narcissus-gpu/src/lib.rs b/engine/narcissus-gpu/src/lib.rs index 5ac92f4..491c788 100644 --- a/engine/narcissus-gpu/src/lib.rs +++ b/engine/narcissus-gpu/src/lib.rs @@ -458,7 +458,8 @@ pub enum IndexType { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum BindingType { Sampler, - Image, + SampledImage, + StorageImage, UniformBuffer, StorageBuffer, DynamicUniformBuffer, @@ -490,7 +491,8 @@ pub enum BufferArg<'a> { pub enum TypedBind<'a> { Sampler(&'a [Sampler]), - Image(&'a [(ImageLayout, Image)]), + SampledImage(&'a [(ImageLayout, Image)]), + StorageImage(&'a [(ImageLayout, Image)]), UniformBuffer(&'a [BufferArg<'a>]), StorageBuffer(&'a [BufferArg<'a>]), } @@ -785,6 +787,8 @@ pub trait Device { index_type: IndexType, ); + fn cmd_compute_touch_swapchain(&self, cmd_encoder: &mut CmdEncoder, image: Image); + fn cmd_set_pipeline(&self, cmd_encoder: &mut CmdEncoder, pipeline: Pipeline); fn cmd_set_viewports(&self, cmd_encoder: &mut CmdEncoder, viewports: &[Viewport]); diff --git a/title/shark-shaders/build.rs b/title/shark-shaders/build.rs index ff53b83..6b48486 100644 --- a/title/shark-shaders/build.rs +++ b/title/shark-shaders/build.rs @@ -7,6 +7,7 @@ const SHADER_ROOT: &str = "shaders"; enum ShaderStage { Vertex, Fragment, + Compute, } impl ShaderStage { @@ -14,6 +15,7 @@ impl ShaderStage { match self { ShaderStage::Vertex => "vert", ShaderStage::Fragment => "frag", + ShaderStage::Compute => "comp", } } } @@ -24,7 +26,7 @@ struct Shader { name: &'static str, } -const SHADERS: [Shader; 4] = [ +const SHADERS: [Shader; 5] = [ Shader { stage: ShaderStage::Vertex, name: "basic", @@ -33,6 +35,10 @@ const SHADERS: [Shader; 4] = [ stage: ShaderStage::Fragment, name: "basic", }, + Shader { + stage: ShaderStage::Compute, + name: "display_transform", + }, Shader { stage: ShaderStage::Vertex, name: "ui", diff --git a/title/shark-shaders/shaders/display_transform.comp.glsl b/title/shark-shaders/shaders/display_transform.comp.glsl new file mode 100644 index 0000000..1b440dc --- /dev/null +++ b/title/shark-shaders/shaders/display_transform.comp.glsl @@ -0,0 +1,30 @@ +#version 460 + +layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout (set = 0, binding = 0) uniform sampler linear_sampler; + +layout (set = 0, binding = 1) uniform texture3D tony_mc_mapface_lut; +layout (set = 0, binding = 2, rgba16f) uniform readonly image2D render_target; +layout (set = 0, binding = 3, rgba16f) uniform writeonly image2D swapchain_image; + +float srgb_oetf(float a) { + return (.0031308f >= a) ? 12.92f * a : 1.055f * pow(a, .4166666666666667f) - .055f; +} + +vec3 srgb_oetf(vec3 a) { + return vec3(srgb_oetf(a.r), srgb_oetf(a.g), srgb_oetf(a.b)); +} + +vec3 tony_mc_mapface(vec3 stimulus) { + const vec3 encoded = stimulus / (stimulus + 1.0); + const float LUT_DIMS = 48.0; + const vec3 uv = (encoded * ((LUT_DIMS - 1.0) / LUT_DIMS) + 0.5 / LUT_DIMS); + return textureLod(sampler3D(tony_mc_mapface_lut, linear_sampler), uv, 0.0).rgb; +} + +void main() { + vec3 stimulus = imageLoad(render_target, ivec2(gl_GlobalInvocationID.xy)).rgb; + vec3 srgb = srgb_oetf(tony_mc_mapface(stimulus)); + imageStore(swapchain_image, ivec2(gl_GlobalInvocationID.xy), vec4(srgb, 1.0)); +} diff --git a/title/shark-shaders/shaders/ui.frag.glsl b/title/shark-shaders/shaders/ui.frag.glsl index e8955e9..26af4ae 100644 --- a/title/shark-shaders/shaders/ui.frag.glsl +++ b/title/shark-shaders/shaders/ui.frag.glsl @@ -1,6 +1,6 @@ #version 460 -layout(set = 0, binding = 4) uniform sampler texSampler; +layout(set = 0, binding = 4) uniform sampler linear_sampler; layout(set = 0, binding = 5) uniform texture2D tex; layout(location = 0) in vec2 texcoord; @@ -8,6 +8,6 @@ layout(location = 1) in vec4 color; layout(location = 0) out vec4 outColor; void main() { - float coverage = texture(sampler2D(tex, texSampler), texcoord).r; + float coverage = texture(sampler2D(tex, linear_sampler), texcoord).r; outColor = color * coverage; } diff --git a/title/shark/data/tony_mc_mapface.dds b/title/shark/data/tony_mc_mapface.dds new file mode 100644 index 0000000..8d84cd9 Binary files /dev/null and b/title/shark/data/tony_mc_mapface.dds differ diff --git a/title/shark/src/main.rs b/title/shark/src/main.rs index 37aef79..624197f 100644 --- a/title/shark/src/main.rs +++ b/title/shark/src/main.rs @@ -2,6 +2,7 @@ use std::fmt::Write; use std::path::Path; use std::time::{Duration, Instant}; +use narcissus_core::dds; use renderdoc_sys as rdoc; use fonts::{FontFamily, Fonts}; @@ -12,15 +13,18 @@ use narcissus_font::{FontCollection, GlyphCache, HorizontalMetrics}; use narcissus_gpu::{ create_device, Access, Bind, BufferImageCopy, BufferUsageFlags, ClearValue, CmdEncoder, Device, DeviceExt, Extent2d, Extent3d, Frame, Image, ImageAspectFlags, ImageBarrier, ImageDesc, - ImageDimension, ImageFormat, ImageLayout, ImageTiling, ImageUsageFlags, IndexType, LoadOp, - MemoryLocation, Offset2d, PersistentBuffer, RenderingAttachment, RenderingDesc, Scissor, - StoreOp, SwapchainImage, ThreadToken, TypedBind, Viewport, + ImageDimension, ImageFormat, ImageLayout, ImageSubresourceRange, ImageTiling, ImageUsageFlags, + IndexType, LoadOp, MemoryLocation, Offset2d, PersistentBuffer, RenderingAttachment, + RenderingDesc, Scissor, StoreOp, SwapchainImage, ThreadToken, TypedBind, Viewport, }; use narcissus_image as image; use narcissus_maths::{ clamp, perlin_noise3, sin_pi_f32, vec3, Affine3, Deg, HalfTurn, Mat3, Mat4, Point3, Vec3, }; -use pipelines::{BasicPipeline, BasicUniforms, PrimitiveInstance, PrimitiveVertex, UiPipeline}; +use pipelines::{ + BasicPipeline, BasicUniforms, DisplayTransformPipeline, PrimitiveInstance, PrimitiveVertex, + UiPipeline, +}; use spring::simple_spring_damper_exact; mod fonts; @@ -522,10 +526,10 @@ impl<'a> UiState<'a> { } } -struct Model<'device> { +struct Model<'gpu> { indices: u32, - vertex_buffer: PersistentBuffer<'device>, - index_buffer: PersistentBuffer<'device>, + vertex_buffer: PersistentBuffer<'gpu>, + index_buffer: PersistentBuffer<'gpu>, } enum ModelRes { @@ -536,33 +540,37 @@ impl ModelRes { const MAX_MODELS: usize = 1; } -struct Models<'device>([Model<'device>; ModelRes::MAX_MODELS]); +struct Models<'gpu>([Model<'gpu>; ModelRes::MAX_MODELS]); enum ImageRes { + TonyMcMapfaceLut, Shark, } impl ImageRes { - const MAX_IMAGES: usize = 1; + const MAX_IMAGES: usize = 2; } struct Images([Image; ImageRes::MAX_IMAGES]); type Gpu = dyn Device + 'static; -struct DrawState<'device> { - gpu: &'device Gpu, +struct DrawState<'gpu> { + gpu: &'gpu Gpu, basic_pipeline: BasicPipeline, ui_pipeline: UiPipeline, + display_transform_pipeline: DisplayTransformPipeline, width: u32, height: u32, depth_image: Image, + render_target_image: Image, + glyph_atlas_image: Image, - models: Models<'device>, + models: Models<'gpu>, images: Images, transforms: Vec, @@ -673,6 +681,101 @@ fn load_images(gpu: &Gpu, thread_token: &ThreadToken) -> Images { image } + fn load_dds

( + gpu: &Gpu, + frame: &Frame, + thread_token: &ThreadToken, + cmd_encoder: &mut CmdEncoder, + path: P, + ) -> Image + where + P: AsRef, + { + let image_data = std::fs::read(path.as_ref()).unwrap(); + let dds = dds::Dds::from_buffer(&image_data).unwrap(); + let header_dxt10 = dds.header_dxt10.unwrap(); + + let width = dds.header.width; + let height = dds.header.height; + let depth = dds.header.depth; + + let dimension = match header_dxt10.resource_dimension { + dds::D3D10ResourceDimension::Texture1d => ImageDimension::Type1d, + dds::D3D10ResourceDimension::Texture2d => ImageDimension::Type2d, + dds::D3D10ResourceDimension::Texture3d => ImageDimension::Type3d, + _ => panic!(), + }; + + let format = match header_dxt10.dxgi_format { + dds::DxgiFormat::R9G9B9E5_SHAREDEXP => ImageFormat::E5B9G9R9_UFLOAT, + _ => panic!(), + }; + + let image = gpu.create_image(&ImageDesc { + memory_location: MemoryLocation::Device, + host_mapped: false, + usage: ImageUsageFlags::SAMPLED | ImageUsageFlags::TRANSFER, + dimension, + format, + tiling: ImageTiling::Optimal, + width, + height, + depth, + layer_count: 1, + mip_levels: 1, + }); + + gpu.cmd_barrier( + cmd_encoder, + None, + &[ImageBarrier { + prev_access: &[Access::None], + next_access: &[Access::TransferWrite], + prev_layout: ImageLayout::Optimal, + next_layout: ImageLayout::Optimal, + subresource_range: ImageSubresourceRange::default(), + image, + }], + ); + + let buffer = gpu.request_transient_buffer_with_data( + frame, + thread_token, + BufferUsageFlags::TRANSFER, + dds.data, + ); + + gpu.cmd_copy_buffer_to_image( + cmd_encoder, + buffer.to_arg(), + image, + ImageLayout::Optimal, + &[BufferImageCopy { + image_extent: Extent3d { + width, + height, + depth, + }, + ..default() + }], + ); + + gpu.cmd_barrier( + cmd_encoder, + None, + &[ImageBarrier { + prev_access: &[Access::TransferWrite], + next_access: &[Access::ShaderSampledImageRead], + prev_layout: ImageLayout::Optimal, + next_layout: ImageLayout::Optimal, + subresource_range: ImageSubresourceRange::default(), + image, + }], + ); + + image + } + let images; let frame = gpu.begin_frame(); { @@ -682,13 +785,22 @@ fn load_images(gpu: &Gpu, thread_token: &ThreadToken) -> Images { { let cmd_encoder = &mut cmd_encoder; - images = Images([load_image( - gpu, - frame, - thread_token, - cmd_encoder, - "title/shark/data/blåhaj.png", - )]); + images = Images([ + load_dds( + gpu, + frame, + thread_token, + cmd_encoder, + "title/shark/data/tony_mc_mapface.dds", + ), + load_image( + gpu, + frame, + thread_token, + cmd_encoder, + "title/shark/data/blåhaj.png", + ), + ]); } gpu.submit(frame, cmd_encoder); @@ -698,10 +810,11 @@ fn load_images(gpu: &Gpu, thread_token: &ThreadToken) -> Images { images } -impl<'device> DrawState<'device> { - fn new(gpu: &'device Gpu, thread_token: &ThreadToken) -> Self { +impl<'gpu> DrawState<'gpu> { + fn new(gpu: &'gpu Gpu, thread_token: &ThreadToken) -> Self { let basic_pipeline = BasicPipeline::new(gpu); let ui_pipeline = UiPipeline::new(gpu); + let primitive_pipeline = DisplayTransformPipeline::new(gpu); let models = load_models(gpu); let images = load_images(gpu, thread_token); @@ -710,9 +823,11 @@ impl<'device> DrawState<'device> { gpu, basic_pipeline, ui_pipeline, + display_transform_pipeline: primitive_pipeline, width: 0, height: 0, depth_image: default(), + render_target_image: default(), glyph_atlas_image: default(), models, images, @@ -783,21 +898,23 @@ impl<'device> DrawState<'device> { ); let clip_from_model = clip_from_camera * camera_from_model; + let atlas_width = ui_state.glyph_cache.width() as u32; + let atlas_height = ui_state.glyph_cache.height() as u32; + let mut cmd_encoder = self.gpu.request_cmd_encoder(frame, thread_token); { let cmd_encoder = &mut cmd_encoder; - if width != self.width || height != self.height { - gpu.destroy_image(frame, self.depth_image); - self.depth_image = gpu.create_image(&ImageDesc { + if self.glyph_atlas_image.is_null() { + self.glyph_atlas_image = gpu.create_image(&ImageDesc { memory_location: MemoryLocation::Device, host_mapped: false, - usage: ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT, + usage: ImageUsageFlags::SAMPLED | ImageUsageFlags::TRANSFER, dimension: ImageDimension::Type2d, - format: ImageFormat::DEPTH_F32, + format: ImageFormat::R8_SRGB, tiling: ImageTiling::Optimal, - width, - height, + width: atlas_width, + height: atlas_height, depth: 1, layer_count: 1, mip_levels: 1, @@ -808,26 +925,40 @@ impl<'device> DrawState<'device> { None, &[ImageBarrier::layout_optimal( &[Access::None], - &[Access::DepthStencilAttachmentWrite], - self.depth_image, - ImageAspectFlags::DEPTH, + &[Access::FragmentShaderSampledImageRead], + self.glyph_atlas_image, + ImageAspectFlags::COLOR, )], ); - - self.width = width; - self.height = height; } - if self.glyph_atlas_image.is_null() { - let image = gpu.create_image(&ImageDesc { + if width != self.width || height != self.height { + gpu.destroy_image(frame, self.depth_image); + gpu.destroy_image(frame, self.render_target_image); + + self.depth_image = gpu.create_image(&ImageDesc { memory_location: MemoryLocation::Device, - usage: ImageUsageFlags::SAMPLED | ImageUsageFlags::TRANSFER, host_mapped: false, + usage: ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT, dimension: ImageDimension::Type2d, - format: ImageFormat::R8_SRGB, + format: ImageFormat::DEPTH_F32, tiling: ImageTiling::Optimal, - width: ui_state.glyph_cache.width() as u32, - height: ui_state.glyph_cache.height() as u32, + width, + height, + depth: 1, + layer_count: 1, + mip_levels: 1, + }); + + self.render_target_image = gpu.create_image(&ImageDesc { + memory_location: MemoryLocation::Device, + host_mapped: false, + usage: ImageUsageFlags::COLOR_ATTACHMENT | ImageUsageFlags::STORAGE, + dimension: ImageDimension::Type2d, + format: ImageFormat::RGBA16_FLOAT, + tiling: ImageTiling::Optimal, + width, + height, depth: 1, layer_count: 1, mip_levels: 1, @@ -838,24 +969,20 @@ impl<'device> DrawState<'device> { None, &[ImageBarrier::layout_optimal( &[Access::None], - &[Access::ShaderSampledImageRead], - image, - ImageAspectFlags::COLOR, + &[Access::DepthStencilAttachmentWrite], + self.depth_image, + ImageAspectFlags::DEPTH, )], ); - self.glyph_atlas_image = image; + self.width = width; + self.height = height; } - let atlas_width = ui_state.glyph_cache.width() as u32; - let atlas_height = ui_state.glyph_cache.height() as u32; - let (touched_glyphs, glyph_texture) = ui_state.glyph_cache.update_atlas(); // If the atlas has been updated, we need to upload it to the GPU. if let Some(texture) = glyph_texture { - let image = self.glyph_atlas_image; - let buffer = gpu.request_transient_buffer_with_data( frame, thread_token, @@ -869,7 +996,7 @@ impl<'device> DrawState<'device> { &[ImageBarrier::layout_optimal( &[Access::ShaderSampledImageRead], &[Access::TransferWrite], - image, + self.glyph_atlas_image, ImageAspectFlags::COLOR, )], ); @@ -877,7 +1004,7 @@ impl<'device> DrawState<'device> { gpu.cmd_copy_buffer_to_image( cmd_encoder, buffer.to_arg(), - image, + self.glyph_atlas_image, ImageLayout::Optimal, &[BufferImageCopy { image_extent: Extent3d { @@ -895,12 +1022,23 @@ impl<'device> DrawState<'device> { &[ImageBarrier::layout_optimal( &[Access::TransferWrite], &[Access::FragmentShaderSampledImageRead], - image, + self.glyph_atlas_image, ImageAspectFlags::COLOR, )], ); } + gpu.cmd_barrier( + cmd_encoder, + None, + &[ImageBarrier::layout_optimal( + &[Access::None], + &[Access::ColorAttachmentWrite], + self.render_target_image, + ImageAspectFlags::COLOR, + )], + ); + gpu.cmd_begin_rendering( cmd_encoder, &RenderingDesc { @@ -909,7 +1047,7 @@ impl<'device> DrawState<'device> { width, height, color_attachments: &[RenderingAttachment { - image: swapchain_image, + image: self.render_target_image, load_op: LoadOp::Clear(ClearValue::ColorF32([1.0, 1.0, 1.0, 1.0])), store_op: StoreOp::Store, }], @@ -1005,7 +1143,7 @@ impl<'device> DrawState<'device> { Bind { binding: 3, array_element: 0, - typed: TypedBind::Image(&[(ImageLayout::Optimal, image)]), + typed: TypedBind::SampledImage(&[(ImageLayout::Optimal, image)]), }, ], ); @@ -1104,7 +1242,7 @@ impl<'device> DrawState<'device> { Bind { binding: 5, array_element: 0, - typed: TypedBind::Image(&[( + typed: TypedBind::SampledImage(&[( ImageLayout::Optimal, self.glyph_atlas_image, )]), @@ -1126,6 +1264,60 @@ impl<'device> DrawState<'device> { } gpu.cmd_end_rendering(cmd_encoder); + + gpu.cmd_barrier( + cmd_encoder, + None, + &[ImageBarrier { + prev_access: &[Access::ColorAttachmentWrite], + prev_layout: ImageLayout::Optimal, + next_access: &[Access::ShaderOtherRead], + next_layout: ImageLayout::General, + image: self.render_target_image, + subresource_range: ImageSubresourceRange::default(), + }], + ); + + gpu.cmd_compute_touch_swapchain(cmd_encoder, swapchain_image); + + gpu.cmd_set_pipeline(cmd_encoder, self.display_transform_pipeline.pipeline); + + gpu.cmd_set_bind_group( + frame, + cmd_encoder, + self.display_transform_pipeline.bind_group_layout, + 0, + &[ + Bind { + binding: 0, + array_element: 0, + typed: TypedBind::Sampler(&[self.display_transform_pipeline.sampler]), + }, + Bind { + binding: 1, + array_element: 0, + typed: TypedBind::SampledImage(&[( + ImageLayout::Optimal, + self.images.0[ImageRes::TonyMcMapfaceLut as usize], + )]), + }, + Bind { + binding: 2, + array_element: 0, + typed: TypedBind::StorageImage(&[( + ImageLayout::General, + self.render_target_image, + )]), + }, + Bind { + binding: 3, + array_element: 0, + typed: TypedBind::StorageImage(&[(ImageLayout::General, swapchain_image)]), + }, + ], + ); + + gpu.cmd_dispatch(cmd_encoder, (self.width + 7) / 8, (self.height + 7) / 8, 1); } gpu.submit(frame, cmd_encoder); } @@ -1177,7 +1369,7 @@ pub fn main() { { let frame = &frame; - let formats = &[ImageFormat::RGBA8_SRGB]; + let formats = &[ImageFormat::A2R10G10B10_UNORM]; let mut swapchain_images = [Image::default(); 1]; let SwapchainImage { width, height } = loop { @@ -1191,7 +1383,7 @@ pub fn main() { window.upcast(), drawable_width, drawable_height, - ImageUsageFlags::COLOR_ATTACHMENT, + ImageUsageFlags::COLOR_ATTACHMENT | ImageUsageFlags::STORAGE, formats, &mut swapchain_images, ) { @@ -1199,7 +1391,7 @@ pub fn main() { } }; - let [swapchain_image_srgb] = swapchain_images; + let [swapchain_image_unorm] = swapchain_images; let tick_start = Instant::now(); 'tick: loop { @@ -1283,7 +1475,7 @@ pub fn main() { &game_state, width, height, - swapchain_image_srgb, + swapchain_image_unorm, ); } diff --git a/title/shark/src/pipelines/basic.rs b/title/shark/src/pipelines/basic.rs index ac04836..08df2a3 100644 --- a/title/shark/src/pipelines/basic.rs +++ b/title/shark/src/pipelines/basic.rs @@ -64,7 +64,7 @@ impl BasicPipeline { BindGroupLayoutEntryDesc { slot: 3, stages: ShaderStageFlags::ALL, - binding_type: BindingType::Image, + binding_type: BindingType::SampledImage, count: 1, }, ], @@ -90,7 +90,7 @@ impl BasicPipeline { }, bind_group_layouts: &[uniforms_bind_group_layout, storage_bind_group_layout], layout: GraphicsPipelineLayout { - color_attachment_formats: &[ImageFormat::RGBA8_SRGB], + color_attachment_formats: &[ImageFormat::RGBA16_FLOAT], depth_attachment_format: Some(ImageFormat::DEPTH_F32), stencil_attachment_format: None, }, diff --git a/title/shark/src/pipelines/display_transform.rs b/title/shark/src/pipelines/display_transform.rs new file mode 100644 index 0000000..8891fb6 --- /dev/null +++ b/title/shark/src/pipelines/display_transform.rs @@ -0,0 +1,76 @@ +use narcissus_gpu::{ + BindGroupLayout, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType, + ComputePipelineDesc, Pipeline, Sampler, SamplerAddressMode, SamplerDesc, SamplerFilter, + ShaderDesc, ShaderStageFlags, +}; + +use crate::Gpu; + +#[allow(unused)] +#[repr(C)] +pub struct DisplayTransformUniforms { + pub width: u32, + pub height: u32, +} + +pub struct DisplayTransformPipeline { + pub bind_group_layout: BindGroupLayout, + pub pipeline: Pipeline, + pub sampler: Sampler, +} + +impl DisplayTransformPipeline { + pub fn new(gpu: &Gpu) -> Self { + let bind_group_layout = gpu.create_bind_group_layout(&BindGroupLayoutDesc { + entries: &[ + BindGroupLayoutEntryDesc { + slot: 0, + stages: ShaderStageFlags::COMPUTE, + binding_type: BindingType::Sampler, + count: 1, + }, + BindGroupLayoutEntryDesc { + slot: 1, + stages: ShaderStageFlags::COMPUTE, + binding_type: BindingType::SampledImage, + count: 1, + }, + BindGroupLayoutEntryDesc { + slot: 2, + stages: ShaderStageFlags::COMPUTE, + binding_type: BindingType::StorageImage, + count: 1, + }, + BindGroupLayoutEntryDesc { + slot: 3, + stages: ShaderStageFlags::COMPUTE, + binding_type: BindingType::StorageImage, + count: 1, + }, + ], + }); + + let sampler = gpu.create_sampler(&SamplerDesc { + filter: SamplerFilter::Bilinear, + address_mode: SamplerAddressMode::Clamp, + compare_op: None, + mip_lod_bias: 0.0, + min_lod: 0.0, + max_lod: 0.0, + }); + + let pipeline = gpu.create_compute_pipeline(&ComputePipelineDesc { + shader: ShaderDesc { + entry: c"main", + code: shark_shaders::DISPLAY_TRANSFORM_COMP_SPV, + }, + bind_group_layouts: &[bind_group_layout], + }); + + Self { + bind_group_layout, + pipeline, + sampler, + } + } +} diff --git a/title/shark/src/pipelines/mod.rs b/title/shark/src/pipelines/mod.rs index 2626abb..02b802f 100644 --- a/title/shark/src/pipelines/mod.rs +++ b/title/shark/src/pipelines/mod.rs @@ -1,6 +1,9 @@ mod basic; +mod display_transform; mod ui; pub use basic::{BasicPipeline, BasicUniforms, Vertex}; pub use ui::{PrimitiveInstance, PrimitiveVertex, UiPipeline, UiUniforms}; + +pub use display_transform::DisplayTransformPipeline; diff --git a/title/shark/src/pipelines/ui.rs b/title/shark/src/pipelines/ui.rs index 03ef8f3..11053ea 100644 --- a/title/shark/src/pipelines/ui.rs +++ b/title/shark/src/pipelines/ui.rs @@ -87,7 +87,7 @@ impl UiPipeline { BindGroupLayoutEntryDesc { slot: 5, stages: ShaderStageFlags::ALL, - binding_type: BindingType::Image, + binding_type: BindingType::SampledImage, count: 1, }, ], @@ -113,7 +113,7 @@ impl UiPipeline { }, bind_group_layouts: &[bind_group_layout], layout: GraphicsPipelineLayout { - color_attachment_formats: &[ImageFormat::RGBA8_SRGB], + color_attachment_formats: &[ImageFormat::RGBA16_FLOAT], depth_attachment_format: Some(ImageFormat::DEPTH_F32), stencil_attachment_format: None, },