@@ -23,6 +23,7 @@ use std::collections::HashSet;
23
23
use std:: { borrow:: Cow , mem} ;
24
24
use text:: { Glyph , TextRunStyle } ;
25
25
use wgpu:: util:: DeviceExt ;
26
+ use metal:: * ;
26
27
27
28
pub const BLEND : Option < wgpu:: BlendState > = Some ( wgpu:: BlendState {
28
29
color : wgpu:: BlendComponent {
@@ -56,11 +57,195 @@ pub struct WgpuRichTextBrush {
56
57
textures_version : usize ,
57
58
}
58
59
60
+ #[ repr( C ) ]
61
+ #[ derive( Debug , Copy , Clone ) ]
62
+ struct Globals {
63
+ transform : [ f32 ; 16 ] ,
64
+ }
65
+
59
66
#[ derive( Debug ) ]
60
67
pub struct MetalRichTextBrush {
61
- // Metal-specific fields will be added here
62
- current_transform : [ f32 ; 16 ] ,
68
+ pipeline_state : RenderPipelineState ,
69
+ vertex_buffer : Buffer ,
70
+ uniform_buffer : Buffer ,
71
+ sampler : SamplerState ,
63
72
supported_vertex_buffer : usize ,
73
+ current_transform : [ f32 ; 16 ] ,
74
+ }
75
+
76
+ impl MetalRichTextBrush {
77
+ pub fn new ( context : & MetalContext ) -> Self {
78
+ let supported_vertex_buffer = 500 ;
79
+
80
+ // Create Metal shader library
81
+ let shader_source = include_str ! ( "rich_text.metal" ) ;
82
+ let library = context
83
+ . device
84
+ . new_library_with_source ( shader_source, & CompileOptions :: new ( ) )
85
+ . expect ( "Failed to create shader library" ) ;
86
+
87
+ let vertex_function = library
88
+ . get_function ( "vs_main" , None )
89
+ . expect ( "Failed to get vertex function" ) ;
90
+ let fragment_function = library
91
+ . get_function ( "fs_main" , None )
92
+ . expect ( "Failed to get fragment function" ) ;
93
+
94
+ // Create vertex descriptor for rich text rendering
95
+ let vertex_descriptor = VertexDescriptor :: new ( ) ;
96
+ let attributes = vertex_descriptor. attributes ( ) ;
97
+
98
+ // Position (attribute 0) - vec4<f32>
99
+ attributes. object_at ( 0 ) . unwrap ( ) . set_format ( MTLVertexFormat :: Float4 ) ;
100
+ attributes. object_at ( 0 ) . unwrap ( ) . set_offset ( 0 ) ;
101
+ attributes. object_at ( 0 ) . unwrap ( ) . set_buffer_index ( 0 ) ;
102
+
103
+ // Color (attribute 1) - vec4<f32>
104
+ attributes. object_at ( 1 ) . unwrap ( ) . set_format ( MTLVertexFormat :: Float4 ) ;
105
+ attributes. object_at ( 1 ) . unwrap ( ) . set_offset ( 16 ) ;
106
+ attributes. object_at ( 1 ) . unwrap ( ) . set_buffer_index ( 0 ) ;
107
+
108
+ // UV (attribute 2) - vec2<f32>
109
+ attributes. object_at ( 2 ) . unwrap ( ) . set_format ( MTLVertexFormat :: Float2 ) ;
110
+ attributes. object_at ( 2 ) . unwrap ( ) . set_offset ( 32 ) ;
111
+ attributes. object_at ( 2 ) . unwrap ( ) . set_buffer_index ( 0 ) ;
112
+
113
+ // Layers (attribute 3) - vec2<i32>
114
+ attributes. object_at ( 3 ) . unwrap ( ) . set_format ( MTLVertexFormat :: Int2 ) ;
115
+ attributes. object_at ( 3 ) . unwrap ( ) . set_offset ( 40 ) ;
116
+ attributes. object_at ( 3 ) . unwrap ( ) . set_buffer_index ( 0 ) ;
117
+
118
+ // Set up buffer layout
119
+ let layouts = vertex_descriptor. layouts ( ) ;
120
+ layouts. object_at ( 0 ) . unwrap ( ) . set_stride ( std:: mem:: size_of :: < Vertex > ( ) as u64 ) ;
121
+ layouts. object_at ( 0 ) . unwrap ( ) . set_step_function ( MTLVertexStepFunction :: PerVertex ) ;
122
+ layouts. object_at ( 0 ) . unwrap ( ) . set_step_rate ( 1 ) ;
123
+
124
+ // Create render pipeline
125
+ let pipeline_descriptor = RenderPipelineDescriptor :: new ( ) ;
126
+ pipeline_descriptor. set_vertex_function ( Some ( & vertex_function) ) ;
127
+ pipeline_descriptor. set_fragment_function ( Some ( & fragment_function) ) ;
128
+ pipeline_descriptor. set_vertex_descriptor ( Some ( & vertex_descriptor) ) ;
129
+
130
+ // Set up blending for text rendering
131
+ let color_attachment = pipeline_descriptor. color_attachments ( ) . object_at ( 0 ) . unwrap ( ) ;
132
+ color_attachment. set_pixel_format ( MTLPixelFormat :: BGRA8Unorm ) ;
133
+ color_attachment. set_blending_enabled ( true ) ;
134
+ color_attachment. set_source_rgb_blend_factor ( MTLBlendFactor :: SourceAlpha ) ;
135
+ color_attachment. set_destination_rgb_blend_factor ( MTLBlendFactor :: OneMinusSourceAlpha ) ;
136
+ color_attachment. set_source_alpha_blend_factor ( MTLBlendFactor :: SourceAlpha ) ;
137
+ color_attachment. set_destination_alpha_blend_factor ( MTLBlendFactor :: OneMinusSourceAlpha ) ;
138
+
139
+ let pipeline_state = context
140
+ . device
141
+ . new_render_pipeline_state ( & pipeline_descriptor)
142
+ . expect ( "Failed to create render pipeline state" ) ;
143
+
144
+ // Create vertex buffer
145
+ let vertex_buffer = context. device . new_buffer (
146
+ ( mem:: size_of :: < Vertex > ( ) * supported_vertex_buffer) as u64 ,
147
+ MTLResourceOptions :: StorageModeShared ,
148
+ ) ;
149
+ vertex_buffer. set_label ( "sugarloaf::rich_text vertex buffer" ) ;
150
+
151
+ // Create uniform buffer
152
+ let uniform_buffer = context. device . new_buffer (
153
+ mem:: size_of :: < Globals > ( ) as u64 ,
154
+ MTLResourceOptions :: StorageModeShared ,
155
+ ) ;
156
+ uniform_buffer. set_label ( "sugarloaf::rich_text uniform buffer" ) ;
157
+
158
+ // Create sampler for texture sampling
159
+ let sampler_descriptor = SamplerDescriptor :: new ( ) ;
160
+ sampler_descriptor. set_min_filter ( MTLSamplerMinMagFilter :: Linear ) ;
161
+ sampler_descriptor. set_mag_filter ( MTLSamplerMinMagFilter :: Linear ) ;
162
+ sampler_descriptor. set_mip_filter ( MTLSamplerMipFilter :: Linear ) ;
163
+ let sampler = context. device . new_sampler ( & sampler_descriptor) ;
164
+
165
+ Self {
166
+ pipeline_state,
167
+ vertex_buffer,
168
+ uniform_buffer,
169
+ sampler,
170
+ supported_vertex_buffer,
171
+ current_transform : [ 0.0 ; 16 ] ,
172
+ }
173
+ }
174
+
175
+ pub fn resize ( & mut self , transform : [ f32 ; 16 ] ) {
176
+ if self . current_transform != transform {
177
+ let globals = Globals { transform } ;
178
+ let contents = self . uniform_buffer . contents ( ) as * mut Globals ;
179
+ unsafe {
180
+ * contents = globals;
181
+ }
182
+ self . current_transform = transform;
183
+ }
184
+ }
185
+
186
+ pub fn render (
187
+ & mut self ,
188
+ vertices : & [ Vertex ] ,
189
+ images : & ImageCache ,
190
+ render_encoder : & RenderCommandEncoderRef ,
191
+ context : & MetalContext , // Add context to get proper window size
192
+ ) {
193
+ println ! ( "Metal rich text: {} vertices - ATLAS CONNECTION ISSUE: layers=[0,0], uv=[0,0]" , vertices. len( ) ) ;
194
+
195
+ if vertices. is_empty ( ) {
196
+ return ;
197
+ }
198
+
199
+ // Expand vertex buffer if needed
200
+ if vertices. len ( ) > self . supported_vertex_buffer {
201
+ // For now, just skip rendering if too many vertices
202
+ // In production, you'd want to expand the buffer
203
+ return ;
204
+ }
205
+
206
+ // Copy vertex data to buffer
207
+ let vertex_data = self . vertex_buffer . contents ( ) as * mut Vertex ;
208
+ unsafe {
209
+ std:: ptr:: copy_nonoverlapping ( vertices. as_ptr ( ) , vertex_data, vertices. len ( ) ) ;
210
+ }
211
+
212
+ // Set up render state
213
+ render_encoder. set_render_pipeline_state ( & self . pipeline_state ) ;
214
+ render_encoder. set_vertex_buffer ( 0 , Some ( & self . vertex_buffer ) , 0 ) ;
215
+
216
+ // Use proper orthographic projection matching the quad renderer
217
+ use crate :: components:: core:: orthographic_projection;
218
+ let transform = orthographic_projection ( context. size . width , context. size . height ) ;
219
+
220
+ // Update uniform buffer with correct transform
221
+ let uniform_data = self . uniform_buffer . contents ( ) as * mut [ f32 ; 16 ] ;
222
+ unsafe {
223
+ * uniform_data = transform;
224
+ }
225
+
226
+ render_encoder. set_vertex_buffer ( 1 , Some ( & self . uniform_buffer ) , 0 ) ;
227
+
228
+ // Set sampler
229
+ render_encoder. set_fragment_sampler_state ( 0 , Some ( & self . sampler ) ) ;
230
+
231
+ // Set Metal textures for glyph atlas
232
+ if let Some ( ( color_texture, mask_texture) ) = images. get_metal_textures ( ) {
233
+ render_encoder. set_fragment_texture ( 0 , Some ( color_texture) ) ;
234
+ render_encoder. set_fragment_texture ( 1 , Some ( mask_texture) ) ;
235
+ }
236
+
237
+ // Draw vertices - make sure we're drawing the right number for triangles
238
+ println ! ( "Metal: Drawing {} vertices at positions: {:?}" ,
239
+ vertices. len( ) ,
240
+ vertices. iter( ) . take( 6 ) . map( |v| v. pos) . collect:: <Vec <_>>( ) ) ;
241
+
242
+ // Use triangle primitive type - each set of 3 vertices forms a triangle
243
+ render_encoder. draw_primitives (
244
+ MTLPrimitiveType :: Triangle ,
245
+ 0 ,
246
+ vertices. len ( ) as u64 ,
247
+ ) ;
248
+ }
64
249
}
65
250
66
251
pub struct RichTextBrush {
@@ -78,11 +263,8 @@ impl RichTextBrush {
78
263
ContextType :: Wgpu ( wgpu_context) => {
79
264
RichTextBrushType :: Wgpu ( WgpuRichTextBrush :: new ( wgpu_context) )
80
265
}
81
- ContextType :: Metal ( _metal_context) => {
82
- RichTextBrushType :: Metal ( MetalRichTextBrush {
83
- current_transform : [ 0.0 ; 16 ] ,
84
- supported_vertex_buffer : 500 ,
85
- } )
266
+ ContextType :: Metal ( metal_context) => {
267
+ RichTextBrushType :: Metal ( MetalRichTextBrush :: new ( metal_context) )
86
268
}
87
269
} ;
88
270
@@ -625,6 +807,16 @@ impl RichTextBrush {
625
807
}
626
808
}
627
809
810
+ pub fn render_metal (
811
+ & mut self ,
812
+ context : & MetalContext , // Add context parameter
813
+ render_encoder : & metal:: RenderCommandEncoderRef ,
814
+ ) {
815
+ if let RichTextBrushType :: Metal ( brush) = & mut self . brush_type {
816
+ brush. render ( & self . vertices , & self . images , render_encoder, context) ;
817
+ }
818
+ }
819
+
628
820
pub fn resize ( & mut self , context : & mut Context ) {
629
821
let transform = match & context. inner {
630
822
ContextType :: Wgpu ( wgpu_ctx) => {
@@ -652,7 +844,7 @@ impl RichTextBrush {
652
844
}
653
845
}
654
846
RichTextBrushType :: Metal ( metal_brush) => {
655
- metal_brush. current_transform = transform;
847
+ metal_brush. resize ( transform) ;
656
848
}
657
849
}
658
850
}
0 commit comments