Partial: remove pipelines - render pass & draw call fixes

Francesco Pasa 10 months ago

src/helpers.c   M +86

 20     alba_vector_print(&attribute->uv);
 21     printf("}\n");
 22 }
 23+
 24+WGPUBuffer create_buffer(
 25+    const AlbaWindow* window,
 26+    const uint64_t size, const void* data,
 27+    const WGPUBufferUsageFlags flags
 28+)
 29+{
 30+    WGPUBufferDescriptor buffer_options = {0};
 31+    buffer_options.usage = WGPUBufferUsage_CopyDst | flags;
 32+    buffer_options.size = size;
 33+    const WGPUBuffer buffer = wgpuDeviceCreateBuffer(window->device, &buffer_options);
 34+    wgpuQueueWriteBuffer(window->queue, buffer, 0, data, size);
 35+    return buffer;
 36+}
 37+
 38+void release_buffer(const WGPUBuffer buffer)
 39+{
 40+    if (buffer != NULL)
 41+    {
 42+        wgpuBufferDestroy(buffer);
 43+        wgpuBufferRelease(buffer);
 44+    }
 45+}
 46+
 47+WGPUBuffer update_buffer(
 48+    const AlbaWindow* window, WGPUBuffer buffer, const WGPUBufferUsage usage, AlbaArray data
 49+)
 50+{
 51+    // Deallocate old buffer if any
 52+    release_buffer(buffer);
 53+    // Copy data to new buffer
 54+    buffer = create_buffer(window, data.length * data.element_size, data.data, usage);
 55+    // Clear local copy for next frame
 56+    alba_array_clear(&data);
 57+    return buffer;
 58+}
 59+
 60+WGPUTexture create_texture(
 61+    const AlbaWindow* window,
 62+    const WGPUTextureUsage usage,
 63+    const AlbaVector size,
 64+    const WGPUTextureFormat format, const uint8_t samples,
 65+    const void* data
 66+)
 67+{
 68+    WGPUTextureDescriptor texture_options = {0};
 69+    texture_options.usage = usage;
 70+    texture_options.format = format;
 71+    texture_options.dimension = WGPUTextureDimension_2D;
 72+    texture_options.size.width = size.x;
 73+    texture_options.size.height = size.y;
 74+    texture_options.size.depthOrArrayLayers = 1;
 75+    texture_options.sampleCount = samples;
 76+    texture_options.mipLevelCount = 1;
 77+
 78+    const WGPUTexture texture = wgpuDeviceCreateTexture(window->device, &texture_options);
 79+
 80+    if (data != NULL)
 81+    {
 82+        const uint64_t bpp = format == WGPUTextureFormat_R8Unorm ? 1 : 4;
 83+
 84+        WGPUImageCopyTexture destination = {0};
 85+        destination.texture = texture;
 86+        destination.aspect = WGPUTextureAspect_All;
 87+
 88+        WGPUTextureDataLayout source = {0};
 89+        source.bytesPerRow = size.x * bpp;
 90+        source.rowsPerImage = size.y;
 91+
 92+        wgpuQueueWriteTexture(
 93+            window->queue,
 94+            &destination, data,
 95+            size.x * size.y * bpp,
 96+            &source,
 97+            &texture_options.size
 98+        );
 99+    }
100+
101+    return texture;
102+}
103+
104+void release_texture(const WGPUTexture texture)
105+{
106+    wgpuTextureDestroy(texture);
107+    wgpuTextureRelease(texture);
108+}

src/internal.h   M +14 -10

 9 
10 #define NO_TEXTURE {-1, -1}
11 
12+// Window
13 typedef struct
14 {
15     // if set, the next frame will update the buffers
39     WGPUAdapter adapter;
40     WGPUDevice device;
41     WGPUQueue queue;
42+    WGPUCommandEncoder command_encoder;
43     WGPUShaderModule shaders;
44     WGPURenderPipeline pipeline;
45     WGPUBuffer uniforms;
75 
76 // Helpers
77 WGPUSurface get_window_surface(WGPUInstance instance, AlbaWindow* window);
78+
79+WGPUBuffer create_buffer(
80+    const AlbaWindow* window,
81+    uint64_t size, const void* data,
82+    WGPUBufferUsageFlags flags
83+);
84+void release_buffer(WGPUBuffer buffer);
85+WGPUBuffer update_buffer(
86+    const AlbaWindow* window, WGPUBuffer buffer, WGPUBufferUsage usage, AlbaArray data
87+);
88+
89 WGPUTexture create_texture(
90     const AlbaWindow* window,
91     WGPUTextureUsage usage,
 93     WGPUTextureFormat format, uint8_t samples,
 94     const void* data
 95 );
 96-
 97-// Drawng
 98-void draw_triangles_indexed_render_pass(
 99-    DrawCall* data,
100-    uint32_t num_vertices,
101-    const AlbaVector* vertices,
102-    const AlbaAttribute* attributes,
103-    uint32_t num_indices,
104-    uint32_t* indices
105-);
106+void release_texture(WGPUTexture texture);
107 
108 // Global variables
109 static int glfw_initialized = 0;

src/window.c   M +71 -145

11 
12 extern char _binary_shaders_wgsl_start[];
13 
14-WGPUBuffer create_buffer(
15-    const AlbaWindow* window,
16-    const uint64_t size, const void* data,
17-    const WGPUBufferUsageFlags flags
18-)
19-{
20-    WGPUBufferDescriptor buffer_options = {0};
21-    buffer_options.usage = WGPUBufferUsage_CopyDst | flags;
22-    buffer_options.size = size;
23-    const WGPUBuffer buffer = wgpuDeviceCreateBuffer(window->device, &buffer_options);
24-    wgpuQueueWriteBuffer(window->queue, buffer, 0, data, size);
25-    return buffer;
26-}
27-
28-void clear_buffer(const WGPUBuffer buffer)
29-{
30-    if (buffer != NULL)
31-    {
32-        wgpuBufferDestroy(buffer);
33-        wgpuBufferRelease(buffer);
34-    }
35-}
36-
37-WGPUBuffer update_buffer(
38-    const AlbaWindow* window, WGPUBuffer buffer, const WGPUBufferUsage usage, AlbaArray data
39-)
40-{
41-    // Deallocate old buffer if any
42-    clear_buffer(buffer);
43-    // Copy data to new buffer
44-    buffer = create_buffer(window, data.length * data.element_size, data.data, usage);
45-    // Clear local copy for next frame
46-    alba_array_clear(&data);
47-    return buffer;
48-}
49-
50 void on_receive_adapter(
51     const WGPURequestAdapterStatus status,
52     const WGPUAdapter adapter,
 58     fprintf(stderr, "error (%d): %s", type, message);
 59 }
 60 
 61-WGPUTexture create_texture(
 62-    const AlbaWindow* window,
 63-    const WGPUTextureUsage usage,
 64-    const AlbaVector size,
 65-    const WGPUTextureFormat format, const uint8_t samples,
 66-    const void* data
 67-)
 68-{
 69-    WGPUTextureDescriptor texture_options = {0};
 70-    texture_options.usage = usage;
 71-    texture_options.format = format;
 72-    texture_options.dimension = WGPUTextureDimension_2D;
 73-    texture_options.size.width = size.x;
 74-    texture_options.size.height = size.y;
 75-    texture_options.size.depthOrArrayLayers = 1;
 76-    texture_options.sampleCount = samples;
 77-    texture_options.mipLevelCount = 1;
 78-
 79-    const WGPUTexture texture = wgpuDeviceCreateTexture(window->device, &texture_options);
 80-
 81-    if (data != NULL)
 82-    {
 83-        const uint64_t bpp = format == WGPUTextureFormat_R8Unorm ? 1 : 4;
 84-
 85-        WGPUImageCopyTexture destination = {0};
 86-        destination.texture = texture;
 87-        destination.aspect = WGPUTextureAspect_All;
 88-
 89-        WGPUTextureDataLayout source = {0};
 90-        source.bytesPerRow = size.x * bpp;
 91-        source.rowsPerImage = size.y;
 92-
 93-        wgpuQueueWriteTexture(
 94-            window->queue,
 95-            &destination, data,
 96-            size.x * size.y * bpp,
 97-            &source,
 98-            &texture_options.size
 99-        );
100-    }
101-
102-    return texture;
103-}
104 
105 // TODO: reorganize code
106 WGPUPipelineLayout configure_resources(const AlbaWindow* window, DrawCall* data)
342         on_receive_device,
343         &window->device
344     );
345-
346-    // Errors
347     wgpuDeviceSetUncapturedErrorCallback(window->device, on_error, NULL);
348-    // Queue
349-    window->queue = wgpuDeviceGetQueue(window->device);
350 
351+    window->queue = wgpuDeviceGetQueue(window->device);
352+    window->command_encoder = wgpuDeviceCreateCommandEncoder(window->device, NULL);
353 
354     configure_pipeline(window);
355     configure_surface(window);
356 
357-    // This is necessary to initialize buffers
358-    window->layers[0].drawing.dirty = 1;
359-    window->layers[0].text.dirty = 1;
360-
361     return window;
362 }
363 
372     alba_array_release(&window->draw_calls);
373 
374     wgpuBindGroupRelease(window->uniforms_bind_group);
375-    clear_buffer(window->uniforms);
376+    release_buffer(window->uniforms);
377     wgpuRenderPipelineRelease(window->pipeline);
378     wgpuShaderModuleRelease(window->shaders);
379+    wgpuCommandEncoderRelease(window->command_encoder);
380     wgpuQueueRelease(window->queue);
381     wgpuDeviceRelease(window->device);
382     wgpuAdapterRelease(window->adapter);
418     dst->a = src.a;
419 }
420 
421-void render_pass(
422-    AlbaWindow* window,
423-    const WGPUTextureView frame,
424-    const WGPUTextureView render_target,
425-    const int clear,
426-    DrawCall* data
427+
428+void draw_objects(
429+    const AlbaWindow* window,
430+    const WGPURenderPassEncoder render_pass
431 )
432 {
433-    if (data->dirty)
434+    wgpuRenderPassEncoderSetPipeline(render_pass, window->pipeline);
435+    for (uint64_t i = 0; i < window->draw_calls.length; i++)
436     {
437-        // TODO: optimize (and only recreate the necessary pipeline)
438-        configure_pipeline(window);
439-        data->vertices = update_buffer(window, data->vertices, WGPUBufferUsage_Vertex, data->new_vertices);
440-        data->attributes = update_buffer(window, data->attributes, WGPUBufferUsage_Vertex, data->new_attributes);
441-        data->indices = update_buffer(window, data->indices, WGPUBufferUsage_Index, data->new_indices);
442-        data->dirty = 0;
443-    }
444+        DrawCall* draw_call = &window->draw_calls.data[i];
445 
446-    const WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(window->device, NULL);
447+        wgpuRenderPassEncoderSetBindGroup(render_pass, 0, draw_call->bind_group, 0, NULL);
448 
449-    // Configures rendering
450-    WGPURenderPassColorAttachment render_pass_attachment_options = {0};
451-    render_pass_attachment_options.view = render_target;
452-    render_pass_attachment_options.loadOp = clear ? WGPULoadOp_Clear : WGPULoadOp_Load;
453-    render_pass_attachment_options.storeOp = WGPUStoreOp_Store;
454-    render_pass_attachment_options.resolveTarget = frame;
455-    copy_color(window->options.clear_color, &render_pass_attachment_options.clearValue);
456-
457-    WGPURenderPassDescriptor render_pass_options = {0};
458-    render_pass_options.colorAttachmentCount = 1;
459-    render_pass_options.colorAttachments = &render_pass_attachment_options;
460-
461-    const WGPURenderPassEncoder render_pass = wgpuCommandEncoderBeginRenderPass(
462-        encoder, &render_pass_options);
463+        if (draw_call->dirty)
464+        {
465+            // // TODO: optimize (and only recreate the necessary pipeline)
466+            // configure_pipeline(window);
467+            draw_call->vertices = update_buffer(
468+                window, draw_call->vertices, WGPUBufferUsage_Vertex, draw_call->new_vertices);
469+            draw_call->attributes = update_buffer(
470+                window, draw_call->attributes, WGPUBufferUsage_Vertex, draw_call->new_attributes);
471+            draw_call->indices = update_buffer(
472+                window, draw_call->indices, WGPUBufferUsage_Index, draw_call->new_indices);
473+            draw_call->dirty = 0;
474+        }
475 
476-    wgpuRenderPassEncoderSetPipeline(render_pass, data->pipeline);
477-    wgpuRenderPassEncoderSetBindGroup(render_pass, 0, data->bind_group, 0, NULL);
478+        const uint32_t vertex_size = wgpuBufferGetSize(draw_call->vertices);
479+        const uint32_t attributes_size = wgpuBufferGetSize(draw_call->attributes);
480+        const uint32_t indices_size = wgpuBufferGetSize(draw_call->indices);
481+        if (vertex_size > 0)
482+        {
483+            wgpuRenderPassEncoderSetVertexBuffer(
484+                render_pass, 0, draw_call->vertices, 0, vertex_size);
485+            wgpuRenderPassEncoderSetVertexBuffer(
486+                render_pass, 1, draw_call->attributes, 0, attributes_size);
487+            wgpuRenderPassEncoderSetIndexBuffer(
488+                render_pass, draw_call->indices, WGPUIndexFormat_Uint32, 0, indices_size);
489+            wgpuRenderPassEncoderDrawIndexed(
490+                render_pass, indices_size / sizeof(uint32_t), 1, 0, 0, 0);
491+        }
492 
493-    const uint32_t vertex_size = wgpuBufferGetSize(data->vertices);
494-    const uint32_t attributes_size = wgpuBufferGetSize(data->attributes);
495-    const uint32_t indices_size = wgpuBufferGetSize(data->indices);
496-    if (vertex_size > 0)
497-    {
498-        wgpuRenderPassEncoderSetVertexBuffer(render_pass, 0, data->vertices, 0, vertex_size);
499-        wgpuRenderPassEncoderSetVertexBuffer(render_pass, 1, data->attributes, 0, attributes_size);
500-        wgpuRenderPassEncoderSetIndexBuffer(render_pass, data->indices, WGPUIndexFormat_Uint32, 0,
501-                                            indices_size);
502-        wgpuRenderPassEncoderDrawIndexed(render_pass, indices_size / sizeof(uint32_t), 1, 0, 0, 0);
503+        // Encode render command and send to GPU
504+        const WGPUCommandBuffer command = wgpuCommandEncoderFinish(window->command_encoder, NULL);
505+        wgpuQueueSubmit(window->queue, 1, &command);
506+        wgpuCommandBufferRelease(command);
507     }
508-
509-    wgpuRenderPassEncoderEnd(render_pass);
510-
511-    // Encode render command and send to GPU
512-    const WGPUCommandBuffer command = wgpuCommandEncoderFinish(encoder, NULL);
513-    wgpuQueueSubmit(window->queue, 1, &command);
514-
515-    wgpuCommandBufferRelease(command);
516-    wgpuRenderPassEncoderRelease(render_pass);
517-    wgpuCommandEncoderRelease(encoder);
518 }
519 
520 void alba_window_render(AlbaWindow* window)
521 {
522+    // Setup textures for rendering
523+    // --------------------------------------------
524     WGPUSurfaceTexture frame;
525     wgpuSurfaceGetCurrentTexture(window->surface, &frame);
526     if (!frame_status_is_valid(window, frame))
506         return;
507     }
508 
509+    // Draw commands
510+    // --------------------------------------------
511+    const WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(window->device, NULL);
512+
513+    // Configures rendering
514+    WGPURenderPassColorAttachment render_pass_attachment_options = {0};
515+    render_pass_attachment_options.view = render_target_view;
516+    render_pass_attachment_options.loadOp = WGPULoadOp_Clear;
517+    render_pass_attachment_options.storeOp = WGPUStoreOp_Store;
518+    render_pass_attachment_options.resolveTarget = frame;
519+    copy_color(window->options.clear_color, &render_pass_attachment_options.clearValue);
520+
521+    WGPURenderPassDescriptor render_pass_options = {0};
522+    render_pass_options.colorAttachmentCount = 1;
523+    render_pass_options.colorAttachments = &render_pass_attachment_options;
524+
525+    const WGPURenderPassEncoder render_pass = wgpuCommandEncoderBeginRenderPass(
526+        encoder, &render_pass_options);
527+
528     // Draw calls
529-    render_pass(window, frame_view, render_target_view, 1, &window->layers[0].drawing);
530+    draw_objects(window, render_pass);
531 
532-    // Update screen
533+    // Update screen & cleanup
534+    // --------------------------------------------
535     wgpuSurfacePresent(window->surface);
536 
537+    wgpuRenderPassEncoderEnd(render_pass);
538+    wgpuRenderPassEncoderRelease(render_pass);
539+
540     wgpuTextureViewRelease(render_target_view);
541+    release_texture(render_target);
542     wgpuTextureViewRelease(frame_view);
543-    wgpuTextureDestroy(render_target);
544-    wgpuTextureRelease(render_target);
545-    wgpuTextureDestroy(frame.texture);
546-    wgpuTextureRelease(frame.texture);
547+    release_texture(frame.texture);
548 }
549 
550