Uniforms and window scaling
Francesco Pasa 11 months ago
Francesco Pasa 11 months ago
demo.c M +6 -6
9 AlbaWindow* window = create_window(&options);
10
11 const float vertices[] = {
12- -0.5, -0.5,
13- +0.5, -0.5,
14- +0.5, +0.5,
15+ 100, 100,
16+ 100, 600,
17+ 200, 100,
18 //
19- 0.8, 0.0,
20- -0.5, 0.75
21+ 0, 20,
22+ 20, 0
23 };
24 const float color[] = {
25 1.0, 0.0, 0.0, 1.0,
24 1.0, 1.0, 1.0, 0.2,
25 };
26 const uint32_t indices[] = {0, 1, 2, 0, 3, 4};
27- draw_triangles(window, 5, 6, vertices, color, indices);
28+ draw_triangles_indexed(window, 5, vertices, color, 6, indices);
29
30 while (!window_should_close(window))
31 {
include/alba.h M +19 -1
60 WGPUQueue queue;
61 WGPUShaderModule shaders;
62 WGPURenderPipeline pipeline;
63+ WGPUBindGroup bind_group;
64 // Drawing
65 int dirty; // if set, the next frame will update the buffers
66 FloatArray new_vertices;
69 WGPUBuffer attributes;
70 Uint32Array new_indices;
71 WGPUBuffer indices;
72+ WGPUBuffer uniforms;
73 } AlbaWindow;
74
75 AlbaWindow* create_window(const AlbaWindowOptions* options);
76 uint32_t window_should_close(const AlbaWindow* window);
77+// Returns window size, in scaled coordinates (e.g. will honor
78+// the windowing system scaling). The point at this coordinate
79+// is at the bottom right of the window.
80+void window_get_size(const AlbaWindow* window, float* width, float* height);
81 void window_render(AlbaWindow* window);
82 void window_release(AlbaWindow* window);
83
84+// Low level drawing
85 void draw_triangles(
86 AlbaWindow* window,
87 uint64_t num_vertices,
88- uint64_t num_indices,
89+ const float* vertices,
90+ const float* attributes
91+);
92+void draw_triangles_indexed(
93+ AlbaWindow* window,
94+ uint64_t num_vertices,
95 const float* vertices,
96 const float* attributes,
97+ uint64_t num_indices,
98 const uint32_t* indices
99 );
100
101+// Higher level drawing
102+void draw_triangle(AlbaWindow* window, const float vertices[6], AlbaColor color);
103+void draw_rect(AlbaWindow* window, const float vertices[8], AlbaColor color);
104+void draw_rect_aa(AlbaWindow* window, float x0, float y0, float x1, float y1, AlbaColor color);
105+
106 #endif // ALBA_H
src/drawing.c M +19 -2
1+#include <stdlib.h>
2+
3 #include "alba.h"
4
5-void draw_triangles(
6+void draw_triangles_indexed(
7 AlbaWindow* window,
8 const uint64_t num_vertices,
9- const uint64_t num_indices,
10 const float* vertices,
11 const float* attributes,
12+ const uint64_t num_indices,
13 const uint32_t* indices
14 )
15 {
16 uint32_array_extend(&window->new_indices, num_indices, indices);
17 window->dirty = 1;
18 }
19+
20+void draw_triangles(
21+ AlbaWindow* window,
22+ const uint64_t num_vertices,
23+ const float* vertices,
24+ const float* attributes
25+)
26+{
27+ uint32_t* indices = malloc(num_vertices * sizeof(uint32_t));
28+ for (uint64_t i = 0; i < num_vertices; i++)
29+ {
30+ indices[i] = i;
31+ }
32+ uint32_array_extend(&window->new_indices, num_vertices, indices);
33+}
src/shaders.wgsl M +8 -2
1+@group(0) @binding(0) var<uniform> scale: vec2f;
2+
3 struct VertexInput {
4 @location(0) position: vec2f,
5 @location(1) color: vec4f,
14 @vertex
15 fn vertex_shader(in: VertexInput) -> VertexOutput {
16 var out: VertexOutput;
17- out.position = vec4f(in.position, 0.0, 1.0);
18+ out.position = vec4f(
19+ in.position.x / scale.x - 1,
20+ 1 - in.position.y / scale.y,
21+ 0, 1
22+ );
23 out.color = in.color;
24 return out;
25 }
26 @fragment
27 fn fragment_shader(in: VertexOutput) -> @location(0) vec4f {
28 return in.color;
29-}
30\ No newline at end of file
31+}
src/window.c M +69 -4
59 printf("error (%d): %s", type, message);
60 }
61
62+WGPUPipelineLayout configure_uniforms(AlbaWindow* window)
63+{
64+ WGPUBufferDescriptor uniform_options = {0};
65+ uniform_options.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform;
66+ uniform_options.size = 2 * sizeof(float);
67+ window->uniforms = wgpuDeviceCreateBuffer(window->device, &uniform_options);
68+
69+ // @binding(0)
70+ WGPUBindGroupLayoutEntry bind_group_layout_entry = {0};
71+ bind_group_layout_entry.binding = 0;
72+ bind_group_layout_entry.visibility = WGPUShaderStage_Vertex;
73+ bind_group_layout_entry.buffer.type = WGPUBufferBindingType_Uniform;
74+
75+ WGPUBindGroupLayoutDescriptor bind_group_layout_options = {0};
76+ bind_group_layout_options.entryCount = 1;
77+ bind_group_layout_options.entries = &bind_group_layout_entry;
78+ const WGPUBindGroupLayout bind_group_layout = wgpuDeviceCreateBindGroupLayout(
79+ window->device, &bind_group_layout_options);
80+
81+ // @group(0)
82+ WGPUBindGroupEntry bind_group_entry = {0};
83+ bind_group_entry.binding = 0;
84+ bind_group_entry.buffer = window->uniforms;
85+ bind_group_entry.size = uniform_options.size;
86+
87+ WGPUBindGroupDescriptor binding_group_options = {0};
88+ binding_group_options.layout = bind_group_layout;
89+ binding_group_options.entryCount = 1;
90+ binding_group_options.entries = &bind_group_entry;
91+
92+ window->bind_group = wgpuDeviceCreateBindGroup(window->device, &binding_group_options);
93+
94+ // A pipeline can have multiple bind groups
95+ WGPUPipelineLayoutDescriptor pipeline_layout_options = {0};
96+ pipeline_layout_options.bindGroupLayoutCount = 1;
97+ pipeline_layout_options.bindGroupLayouts = &bind_group_layout;
98+ const WGPUPipelineLayout pipeline_layout = wgpuDeviceCreatePipelineLayout(
99+ window->device, &pipeline_layout_options);
100+
101+ wgpuBindGroupLayoutRelease(bind_group_layout);
102+
103+ return pipeline_layout;
104+}
105+
106 void configure_surface(const AlbaWindow* window)
107 {
108 int width, height;
109+ float x_scale, y_scale;
110 glfwGetWindowSize(window->glfw_window, &width, &height);
111+ glfwGetWindowContentScale(window->glfw_window, &x_scale, &y_scale);
112
113 if (width == 0 || height == 0) return;
114
115+ // Update uniforms
116+ const float scale[2] = {width / (2. * x_scale), height / (2. * y_scale)};
117+ wgpuQueueWriteBuffer(window->queue, window->uniforms, 0, &scale, 2 * sizeof(uint32_t));
118+
119 const WGPUTextureFormat format = wgpuSurfaceGetPreferredFormat(window->surface, window->adapter);
120
121 WGPUSurfaceConfiguration surface_options = {0};
128 wgpuSurfaceConfigure(window->surface, &surface_options);
129 }
130
131+
132 void configure_pipeline(AlbaWindow* window)
133 {
134 WGPUShaderModuleWGSLDescriptor shader_options = {0};
201 fragment_state.targets = &color_state;
202 pipleine_options.fragment = &fragment_state;
203
204+ // Uniform buffer
205+ pipleine_options.layout = configure_uniforms(window);
206+
207 window->pipeline = wgpuDeviceCreateRenderPipeline(window->device, &pipleine_options);
208 }
209
264 // Queue
265 window->queue = wgpuDeviceGetQueue(window->device);
266
267- // Surface configuration
268- configure_surface(window);
269-
270- // Load and configure shaders
271 configure_pipeline(window);
272+ configure_surface(window);
273
274 // This is necessary to initialize buffers
275 window->dirty = 1;
429 // Draw calls
430 const WGPURenderPassEncoder render_pass = wgpuCommandEncoderBeginRenderPass(
431 encoder, &render_pass_options);
432+
433 wgpuRenderPassEncoderSetPipeline(render_pass, window->pipeline);
434+ wgpuRenderPassEncoderSetBindGroup(render_pass, 0, window->bind_group, 0, NULL);
435
436 const uint32_t vertex_size = wgpuBufferGetSize(window->vertices);
437 const uint32_t attributes_size = wgpuBufferGetSize(window->attributes);
443 wgpuRenderPassEncoderSetIndexBuffer(render_pass, window->indices, WGPUIndexFormat_Uint32, 0, indices_size);
444 wgpuRenderPassEncoderDrawIndexed(render_pass, indices_size / sizeof(uint32_t), 1, 0, 0, 0);
445 }
446+
447 wgpuRenderPassEncoderEnd(render_pass);
448
449 // Encode render command and send to GPU
459 wgpuTextureViewRelease(view);
460 wgpuTextureRelease(frame.texture);
461 }
462+
463+void window_get_size(const AlbaWindow* window, float* width, float* height)
464+{
465+ int raw_width, raw_height;
466+ float x_scale, y_scale;
467+ glfwGetWindowSize(window->glfw_window, &raw_width, &raw_height);
468+ glfwGetWindowContentScale(window->glfw_window, &x_scale, &y_scale);
469+
470+ *width = raw_width / x_scale;
471+ *height = raw_height / y_scale;
472+}