Uniforms and window scaling

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+}