diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index c8f3bcbda7..e4abdd6c3d 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -49,6 +49,7 @@ struct DirectXRenderPipelines { shadow_pipeline: PipelineState, quad_pipeline: PipelineState, paths_pipeline: PipelineState, + paths_indirect_draw_buffer: ID3D11Buffer, underline_pipeline: PipelineState, mono_sprites: PipelineState, poly_sprites: PipelineState, @@ -61,6 +62,14 @@ struct DirectXGlobalElements { blend_state_for_pr: ID3D11BlendState, } +#[repr(C)] +struct DrawInstancedIndirectArgs { + vertex_count_per_instance: u32, + instance_count: u32, + start_vertex_location: u32, + start_instance_location: u32, +} + // #[cfg(not(feature = "enable-renderdoc"))] // struct DirectComposition { // comp_device: IDCompositionDevice, @@ -122,6 +131,7 @@ impl DirectXRenderer { [0.0, 0.0, 0.0, 0.0], &self.globals.blend_state, )?; + println!("--> Drawing scene"); for batch in scene.batches() { match batch { PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows), @@ -273,41 +283,64 @@ impl DirectXRenderer { if paths.is_empty() { return Ok(()); } + println!("Drawing {} paths", paths.len()); let mut vertices = Vec::new(); let mut sprites = Vec::with_capacity(paths.len()); - for path in paths { - let tile = &path_tiles[&path.id]; - let texture_view = self.atlas.get_texture_view(tile.texture_id); - let origin = path.bounds.intersect(&path.content_mask.bounds).origin; - let sprites = [PathSprite { - bounds: Bounds { - origin: origin.map(|p| p.floor()), - size: tile.bounds.size.map(Into::into), + let mut draw_indirect_commands = Vec::with_capacity(paths.len()); + let mut start_vertex_location = 0; + for (i, path) in paths.iter().enumerate() { + draw_indirect_commands.push(DrawInstancedIndirectArgs { + vertex_count_per_instance: path.vertices.len() as u32, + instance_count: 1, + start_vertex_location, + start_instance_location: i as u32, + }); + start_vertex_location += path.vertices.len() as u32; + + vertices.extend(path.vertices.iter().map(|v| PathVertex { + xy_position: v.xy_position, + content_mask: ContentMask { + bounds: path.content_mask.bounds, }, + })); + + sprites.push(PathSprite { + bounds: path.bounds, color: path.color, - tile: (*tile).clone(), - }]; - update_buffer_capacity( - &self.pipelines.paths_pipeline, - std::mem::size_of::(), - 1, - &self.devices.device, - ) - .map(|input| update_pipeline(&mut self.pipelines.paths_pipeline, input)); - update_buffer( + }); + } + + update_buffer_capacity( + &self.pipelines.paths_pipeline, + std::mem::size_of::(), + sprites.len(), + &self.devices.device, + ) + .map(|input| update_pipeline(&mut self.pipelines.paths_pipeline, input)); + update_buffer( + &self.devices.device_context, + &self.pipelines.paths_pipeline.buffer, + &sprites, + )?; + update_indirect_buffer( + &self.devices.device_context, + &self.pipelines.paths_indirect_draw_buffer, + &draw_indirect_commands, + )?; + prepare_indirect_draws( + &self.devices.device_context, + &self.pipelines.paths_pipeline, + &self.context.viewport, + &self.globals.global_params_buffer, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + )?; + + for i in 0..paths.len() { + draw_indirect( &self.devices.device_context, - &self.pipelines.paths_pipeline.buffer, - &sprites, - )?; - draw_with_texture( - &self.devices.device_context, - &self.pipelines.paths_pipeline, - &texture_view, - &self.context.viewport, - &self.globals.global_params_buffer, - &self.globals.sampler, - 1, - )?; + &self.pipelines.paths_indirect_draw_buffer, + (i * std::mem::size_of::()) as u32, + ); } Ok(()) } @@ -484,11 +517,13 @@ impl DirectXRenderPipelines { std::mem::size_of::(), 32, )?; + let paths_indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?; Ok(Self { shadow_pipeline, quad_pipeline, paths_pipeline, + paths_indirect_draw_buffer, underline_pipeline, mono_sprites, poly_sprites, @@ -884,6 +919,27 @@ fn create_buffer_view( Ok([view]) } +fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: u32) -> Result { + // let desc = D3D11_BUFFER_DESC { + // ByteWidth: std::mem::size_of::() as u32 * buffer_size, + // Usage: D3D11_USAGE_DYNAMIC, + // BindFlags: D3D11_BIND_INDIRECT_DRAW.0 as u32, + // MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32, + // ..Default::default() + // }; + let desc = D3D11_BUFFER_DESC { + ByteWidth: std::mem::size_of::() as u32 * buffer_size, + Usage: D3D11_USAGE_DYNAMIC, + BindFlags: D3D11_BIND_INDEX_BUFFER.0 as u32, + CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, + MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32, + StructureByteStride: std::mem::size_of::() as u32, + }; + let mut buffer = None; + unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?; + Ok(buffer.unwrap()) +} + fn update_global_params( device_context: &ID3D11DeviceContext, buffer: &[Option; 1], @@ -964,6 +1020,50 @@ fn update_buffer( Ok(()) } +fn update_indirect_buffer( + device_context: &ID3D11DeviceContext, + buffer: &ID3D11Buffer, + data: &[DrawInstancedIndirectArgs], +) -> Result<()> { + unsafe { + let mut dest = std::mem::zeroed(); + device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?; + std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len()); + device_context.Unmap(buffer, 0); + } + Ok(()) +} + +fn prepare_indirect_draws( + device_context: &ID3D11DeviceContext, + pipeline: &PipelineState, + viewport: &[D3D11_VIEWPORT], + global_params: &[Option], + topology: D3D_PRIMITIVE_TOPOLOGY, +) -> Result<()> { + unsafe { + device_context.VSSetShaderResources(1, Some(&pipeline.view)); + device_context.PSSetShaderResources(1, Some(&pipeline.view)); + device_context.IASetPrimitiveTopology(topology); + device_context.RSSetViewports(Some(viewport)); + device_context.VSSetShader(&pipeline.vertex, None); + device_context.PSSetShader(&pipeline.fragment, None); + device_context.VSSetConstantBuffers(0, Some(global_params)); + device_context.PSSetConstantBuffers(0, Some(global_params)); + } + Ok(()) +} + +fn draw_indirect( + device_context: &ID3D11DeviceContext, + indirect_draw_buffer: &ID3D11Buffer, + offset: u32, +) { + unsafe { + device_context.DrawInstancedIndirect(indirect_draw_buffer, offset); + } +} + fn draw_normal( device_context: &ID3D11DeviceContext, pipeline: &PipelineState, @@ -1026,6 +1126,7 @@ mod shader_resources { use windows_core::{HSTRING, PCSTR}; pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result { + println!("Building shader: {}", entry); unsafe { let mut entry = entry.to_owned(); let mut target = target.to_owned(); @@ -1039,6 +1140,11 @@ mod shader_resources { target.push_str("\0"); let entry_point = PCSTR::from_raw(entry.as_ptr()); let target_cstr = PCSTR::from_raw(target.as_ptr()); + println!( + "Compiling shader: {} with target: {}", + entry_point.display(), + target_cstr.display() + ); #[cfg(debug_assertions)] let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; #[cfg(not(debug_assertions))] @@ -1054,6 +1160,7 @@ mod shader_resources { &mut compile_blob, Some(&mut error_blob), ); + println!("Shader compile result: {:?}", ret); if ret.is_err() { let Some(error_blob) = error_blob else { return Err(anyhow::anyhow!("{ret:?}")); @@ -1064,10 +1171,9 @@ mod shader_resources { string_len, string_len, ); - return Err(anyhow::anyhow!( - "Compile error: {}", - String::from_utf8_lossy(&error_string_encode) - )); + let error_string = String::from_utf8_lossy(&error_string_encode); + println!("Shader compile error: {}", error_string); + return Err(anyhow::anyhow!("Compile error: {}", error_string)); } Ok(compile_blob.unwrap()) } diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 0234f6bdc7..fbb5414321 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -81,28 +81,36 @@ struct TransformationMatrix { static const float M_PI_F = 3.141592653f; static const float3 GRAYSCALE_FACTORS = float3(0.2126f, 0.7152f, 0.0722f); -float4 to_device_position(float2 unit_vertex, Bounds bounds) { - float2 position = unit_vertex * bounds.size + bounds.origin; +float4 to_device_position_impl(float2 position) { float2 device_position = position / global_viewport_size * float2(2.0, -2.0) + float2(-1.0, 1.0); return float4(device_position, 0., 1.); } -float4 distance_from_clip_rect(float2 unit_vertex, Bounds bounds, Bounds clip_bounds) { +float4 to_device_position(float2 unit_vertex, Bounds bounds) { float2 position = unit_vertex * bounds.size + bounds.origin; + return to_device_position_impl(position); +} + +float4 distance_from_clip_rect_impl(float2 position, Bounds clip_bounds) { return float4(position.x - clip_bounds.origin.x, clip_bounds.origin.x + clip_bounds.size.x - position.x, position.y - clip_bounds.origin.y, clip_bounds.origin.y + clip_bounds.size.y - position.y); } +float4 distance_from_clip_rect(float2 unit_vertex, Bounds bounds, Bounds clip_bounds) { + float2 position = unit_vertex * bounds.size + bounds.origin; + return distance_from_clip_rect_impl(position, clip_bounds); +} + // Convert linear RGB to sRGB float3 linear_to_srgb(float3 color) { - return pow(color, float3(2.2)); + return pow(color, float3(2.2, 2.2, 2.2)); } // Convert sRGB to linear RGB float3 srgb_to_linear(float3 color) { - return pow(color, float3(1.0 / 2.2)); + return pow(color, float3(1.0 / 2.2, 1.0 / 2.2, 1.0 / 2.2)); } /// Hsla to linear RGBA conversion. @@ -268,23 +276,23 @@ float quad_sdf(float2 pt, Bounds bounds, Corners corner_radii) { } GradientColor prepare_gradient_color(uint tag, uint color_space, Hsla solid, Hsla color0, Hsla color1) { - GradientColor res; + GradientColor output; if (tag == 0) { - res.solid = hsla_to_rgba(solid); + output.solid = hsla_to_rgba(solid); } else if (tag == 1) { - res.color0 = hsla_to_rgba(color0); - res.color1 = hsla_to_rgba(color1); + output.color0 = hsla_to_rgba(color0); + output.color1 = hsla_to_rgba(color1); // Prepare color space in vertex for avoid conversion // in fragment shader for performance reasons if (color_space == 1) { // Oklab - res.color0 = srgb_to_oklab(res.color0); - res.color1 = srgb_to_oklab(res.color1); + output.color0 = srgb_to_oklab(output.color0); + output.color1 = srgb_to_oklab(output.color1); } } - return res; + return output; } float4 gradient_color(Background background, @@ -456,22 +464,21 @@ struct Quad { struct QuadVertexOutput { float4 position: SV_Position; - // float4 border_color: COLOR0; - float4 border_color: FLAT; - uint quad_id: FLAT; - float4 background_solid: FLAT; - float4 background_color0: FLAT; - float4 background_color1: FLAT; + nointerpolation float4 border_color: COLOR0; + nointerpolation uint quad_id: TEXCOORD0; + nointerpolation float4 background_solid: COLOR1; + nointerpolation float4 background_color0: COLOR2; + nointerpolation float4 background_color1: COLOR3; float4 clip_distance: SV_ClipDistance; }; struct QuadFragmentInput { - uint quad_id: FLAT; + nointerpolation uint quad_id: TEXCOORD0; float4 position: SV_Position; - float4 border_color: FLAT; - float4 background_solid: FLAT; - float4 background_color0: FLAT; - float4 background_color1: FLAT; + nointerpolation float4 border_color: COLOR0; + nointerpolation float4 background_solid: COLOR1; + nointerpolation float4 background_color0: COLOR2; + nointerpolation float4 background_color1: COLOR3; }; StructuredBuffer quads: register(t1); @@ -566,55 +573,11 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target { return color * float4(1., 1., 1., saturate(0.5 - distance)); } -/* -** -** Path raster -** -*/ - struct PathVertex { float2 xy_position; - float2 st_position; Bounds content_mask; }; -struct PathRasterizationOutput { - float4 position: SV_Position; - float2 st_position: TEXCOORD0; - float4 clip_distances: SV_ClipDistance; -}; - -struct PathRasterizationInput { - float4 position: SV_Position; - float2 st_position: TEXCOORD0; -}; - -StructuredBuffer path_vertices: register(t1); - -PathRasterizationOutput path_rasterization_vertex(uint vertex_id: SV_VertexID) { - PathVertex vertex = path_vertices[vertex_id]; - PathRasterizationOutput output; - float2 device_position = vertex.xy_position / global_viewport_size * float2(2.0, -2.0) + float2(-1.0, 1.0); - float2 tl = vertex.xy_position - vertex.content_mask.origin; - float2 br = vertex.content_mask.origin + vertex.content_mask.size - vertex.xy_position; - - output.position = float4(device_position, 0.0, 1.0); - output.st_position = vertex.st_position; - output.clip_distances = float4(tl.x, br.x, tl.y, br.y); - return output; -} - -float4 path_rasterization_fragment(PathRasterizationInput input): SV_Target { - float2 dx = ddx(input.st_position); - float2 dy = ddy(input.st_position); - float2 gradient = float2((2. * input.st_position.x) * dx.x - dx.y, - (2. * input.st_position.x) * dy.x - dy.y); - float f = (input.st_position.x * input.st_position.x) - input.st_position.y; - float distance = f / length(gradient); - float alpha = saturate(0.5 - distance); - return float4(alpha, 0., 0., 1.); -} - /* ** ** Paths @@ -624,27 +587,27 @@ float4 path_rasterization_fragment(PathRasterizationInput input): SV_Target { struct PathSprite { Bounds bounds; Background color; - AtlasTile tile; }; struct PathVertexOutput { float4 position: SV_Position; - float2 tile_position: POSITION1; - uint sprite_id: FLAT; - float4 solid_color: FLAT; - float4 color0: FLAT; - float4 color1: FLAT; + float4 clip_distance: SV_ClipDistance; + nointerpolation uint sprite_id: TEXCOORD0; + nointerpolation float4 solid_color: COLOR0; + nointerpolation float4 color0: COLOR1; + nointerpolation float4 color1: COLOR2; }; -StructuredBuffer path_sprites: register(t1); +StructuredBuffer path_vertices: register(t1); +StructuredBuffer path_sprites: register(t2); PathVertexOutput paths_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_InstanceID) { - float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u)); + PathVertex v = path_vertices[vertex_id]; PathSprite sprite = path_sprites[instance_id]; - // Don't apply content mask because it was already accounted for when rasterizing the path. + PathVertexOutput output; - output.position = to_device_position(unit_vertex, sprite.bounds); - output.tile_position = to_tile_position(unit_vertex, sprite.tile); + output.position = to_device_position_impl(v.xy_position); + output.clip_distance = distance_from_clip_rect_impl(v.xy_position, v.content_mask); output.sprite_id = instance_id; GradientColor gradient = prepare_gradient_color( @@ -662,13 +625,15 @@ PathVertexOutput paths_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_ } float4 paths_fragment(PathVertexOutput input): SV_Target { - float sample = t_sprite.Sample(s_sprite, input.tile_position).r; - float mask = 1.0 - abs(1.0 - sample % 2.0); + float4 zero = 0.0; + if (any(input.clip_distance < zero)) { + return zero; + } + PathSprite sprite = path_sprites[input.sprite_id]; Background background = sprite.color; float4 color = gradient_color(background, input.position.xy, sprite.bounds, input.solid_color, input.color0, input.color1); - color.a *= mask; return color; }