Raw Triangle Vklite¶
This example draws one RGB triangle with manual vklite commands.
Preview¶
Run And Adapt¶
Commands below assume a Datoviz source checkout and start at the repository root. Use your configured build environment; Python routes additionally require local bindings.
| Route | Availability | Command or action |
|---|---|---|
| C | Canonical native source | just example-c advanced/raw_triangle_vklite (build and run), or rerun ./build/examples/c/advanced/raw_triangle_vklite |
| Python | No verified adaptation on this page | Start from the C source. |
| Browser | Native only | raw vklite command recording is a native runtime example |
Use this example as capability or integration evidence, not as a minimal copy-paste template. Start from the nearest supported, copy-safe example and add this feature after verifying the linked API reference.
What To Look For¶
The triangle is not a scene visual. The example compiles the inline GLSL shaders, declares the vec2 position plus vec3 color vertex layout, uploads three vertices to a vertex buffer, and records the draw into the command buffer provided by DvzCanvas. The saved PNG should show a red/green/blue triangle over a dark clear color.
This low-level workflow is useful when you need custom Vulkan drawing but still want Datoviz to handle the offscreen canvas, frame image, submission, and PNG capture plumbing.
Usage: ./raw_triangle_vklite
Render one frame, save raw_triangle_vklite.png.
NOTE: this example intentionally uses an offscreen canvas. Hosted onscreen integration is shown separately in external_surface_glfw.c.
Source¶
/*
* Copyright (c) 2021 Cyrille Rossant and contributors. All rights reserved.
* Licensed under the MIT license. See LICENSE file in the project root for details.
* SPDX-License-Identifier: MIT
*/
/* raw_triangle_vklite - This example draws one RGB triangle with manual vklite commands.
*
* Scenario: advanced_raw_triangle_vklite
* Style: advanced, native-only, low-level vklite/canvas, 1920x1080 output target
*
* What to look for: the triangle is not a scene visual. The example compiles the inline GLSL
* shaders, declares the vec2 position plus vec3 color vertex layout, uploads three vertices to a
* vertex buffer, and records the draw into the command buffer provided by DvzCanvas. The saved PNG
* should show a red/green/blue triangle over a dark clear color.
*
* This low-level workflow is useful when you need custom Vulkan drawing but still want Datoviz to
* handle the offscreen canvas, frame image, submission, and PNG capture plumbing.
*
* Usage:
* ./raw_triangle_vklite
*
* Render one frame, save raw_triangle_vklite.png.
*
* NOTE: this example intentionally uses an offscreen canvas. Hosted onscreen integration is shown
* separately in external_surface_glfw.c.
*
* Build: just example-c advanced/raw_triangle_vklite
* Run: ./build/examples/c/advanced/raw_triangle_vklite
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "datoviz/canvas.h"
#include "datoviz/stream/frame_stream.h"
#include "datoviz/vk/enums.h"
#include "datoviz/vk/gpu_ctx.h"
#include "datoviz/vklite.h"
#include "datoviz/window.h"
#include "example_common.h"
/*************************************************************************************************/
/* Constants */
/*************************************************************************************************/
#define WIDTH EXAMPLE_OUTPUT_WIDTH
#define HEIGHT EXAMPLE_OUTPUT_HEIGHT
/*************************************************************************************************/
/* Vertex layout */
/*************************************************************************************************/
typedef struct
{
float x, y;
float r, g, b;
} Vertex;
static const Vertex TRIANGLE[3] = {
{ 0.0f, 0.6f, 1.0f, 0.2f, 0.2f},
{-0.6f, -0.6f, 0.2f, 1.0f, 0.2f},
{ 0.6f, -0.6f, 0.2f, 0.2f, 1.0f},
};
/*************************************************************************************************/
/* Shaders */
/*************************************************************************************************/
static const char* VERT_GLSL =
"#version 450\n"
"layout(location = 0) in vec2 inPos;\n"
"layout(location = 1) in vec3 inColor;\n"
"layout(location = 0) out vec3 fragColor;\n"
"void main() {\n"
" gl_Position = vec4(inPos, 0.0, 1.0);\n"
" fragColor = inColor;\n"
"}\n";
static const char* FRAG_GLSL =
"#version 450\n"
"layout(location = 0) in vec3 fragColor;\n"
"layout(location = 0) out vec4 outColor;\n"
"void main() { outColor = vec4(fragColor, 1.0); }\n";
/*************************************************************************************************/
/* Triangle state */
/*************************************************************************************************/
typedef struct
{
DvzDevice* device;
DvzVma* alloc;
DvzSlots* slots;
DvzGraphics* pipeline;
DvzBuffer* vbuf;
} TriState;
static int tri_state_create(TriState* s, DvzDevice* device, DvzVma* alloc)
{
s->device = device;
s->alloc = alloc;
/* Compile shaders */
uint64_t vs_sz = 0, fs_sz = 0;
uint32_t* vs_spv = dvz_compile_glsl("vertex", VERT_GLSL, &vs_sz);
uint32_t* fs_spv = dvz_compile_glsl("fragment", FRAG_GLSL, &fs_sz);
if (!vs_spv || !fs_spv) {
dvz_fprintf(stderr, "GLSL compilation failed\n");
return -1;
}
DvzShader* vs = dvz_shader_create_wrapper();
DvzShader* fs = dvz_shader_create_wrapper();
dvz_shader(device, vs_sz, vs_spv, vs);
dvz_shader(device, fs_sz, fs_spv, fs);
free(vs_spv);
free(fs_spv);
/* Empty pipeline layout (no descriptors, no push constants) */
s->slots = dvz_slots_create_wrapper();
dvz_slots(device, s->slots);
dvz_slots_create(s->slots);
/* Graphics pipeline */
s->pipeline = dvz_graphics_create_wrapper();
dvz_graphics(device, s->pipeline);
dvz_graphics_shader(s->pipeline, VK_SHADER_STAGE_VERTEX_BIT, dvz_shader_handle(vs));
dvz_graphics_shader(s->pipeline, VK_SHADER_STAGE_FRAGMENT_BIT, dvz_shader_handle(fs));
dvz_graphics_vertex_binding(s->pipeline, 0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX);
dvz_graphics_vertex_attr(s->pipeline, 0, 0, VK_FORMAT_R32G32_SFLOAT,
offsetof(Vertex, x));
dvz_graphics_vertex_attr(s->pipeline, 0, 1, VK_FORMAT_R32G32B32_SFLOAT,
offsetof(Vertex, r));
dvz_graphics_primitive(s->pipeline, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0);
dvz_graphics_attachment_color(s->pipeline, 0, DVZ_DEFAULT_COLOR_FORMAT);
dvz_graphics_layout(s->pipeline, dvz_slots_handle(s->slots));
dvz_graphics_viewport(s->pipeline, 0, 0, 0, 0, 0, 1, DVZ_GRAPHICS_FLAGS_DYNAMIC);
dvz_graphics_scissor(s->pipeline, 0, 0, 0, 0, DVZ_GRAPHICS_FLAGS_DYNAMIC);
int rc = dvz_graphics_create(s->pipeline);
if (rc != 0) {
dvz_fprintf(stderr, "Pipeline creation failed\n");
return -1;
}
dvz_shader_destroy(vs);
dvz_shader_free(vs);
dvz_shader_destroy(fs);
dvz_shader_free(fs);
/* Vertex buffer */
s->vbuf = dvz_buffer_create_wrapper();
dvz_buffer(device, alloc, s->vbuf);
dvz_buffer_size(s->vbuf, sizeof(TRIANGLE));
dvz_buffer_usage(s->vbuf, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
dvz_buffer_flags(s->vbuf, DVZ_ALLOC_MAPPED | DVZ_ALLOC_HOST_ACCESS_SEQUENTIAL_WRITE);
if (dvz_buffer_create(s->vbuf) != 0) {
dvz_fprintf(stderr, "Vertex buffer creation failed\n");
return -1;
}
dvz_buffer_upload(s->vbuf, 0, sizeof(TRIANGLE), TRIANGLE);
return 0;
}
static void tri_state_destroy(TriState* s)
{
dvz_buffer_destroy(s->vbuf);
dvz_buffer_free(s->vbuf);
dvz_graphics_destroy(s->pipeline);
dvz_graphics_free(s->pipeline);
dvz_slots_destroy(s->slots);
dvz_slots_free(s->slots);
}
/*************************************************************************************************/
/* Draw callback: same for every backend */
/*************************************************************************************************/
static void draw_triangle(DvzCanvas* canvas, const DvzStreamFrame* frame, void* user_data)
{
(void)canvas;
TriState* s = (TriState*)user_data;
VkCommandBuffer cmd = frame->command_buffer;
if (cmd == VK_NULL_HANDLE)
return;
DvzCommands* cmds = dvz_commands_create_wrapper();
dvz_commands_wrap(s->device, cmd, cmds);
DvzRendering* rendering = dvz_rendering_create_wrapper();
dvz_cmd_rendering_default(cmds, frame->image_view,
frame->extent.width, frame->extent.height,
(VkClearValue){.color.float32 = {0.05f, 0.05f, 0.08f, 1.0f}},
rendering);
dvz_cmd_rendering_begin(cmds, rendering);
dvz_cmd_bind_graphics(cmds, s->pipeline);
/* Viewport and scissor must be set dynamically for dynamic rendering */
VkViewport vp = {
.x = 0, .y = 0,
.width = (float)frame->extent.width,
.height = (float)frame->extent.height,
.minDepth = 0.0f, .maxDepth = 1.0f,
};
VkRect2D sc = {{0, 0}, frame->extent};
vkCmdSetViewport(cmd, 0, 1, &vp);
vkCmdSetScissor(cmd, 0, 1, &sc);
DvzSize offset = 0;
dvz_cmd_bind_vertex_buffers(cmds, 0, 1, s->vbuf, &offset);
dvz_cmd_draw(cmds, 0, 3, 0, 1);
dvz_cmd_rendering_end(cmds);
dvz_rendering_free(rendering);
dvz_commands_free(cmds);
}
/*************************************************************************************************/
/* Main */
/*************************************************************************************************/
int main(int argc, char** argv)
{
(void)argc;
/* GPU context: dynamicRendering + synchronization2 required by DvzCanvas. */
DvzGpuCtxConfig gpu_cfg = dvz_gpu_ctx_config();
VkPhysicalDeviceVulkan12Features feat12 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.timelineSemaphore = VK_TRUE,
};
dvz_gpu_ctx_config_features12(&gpu_cfg, &feat12);
VkPhysicalDeviceVulkan13Features feat13 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
.dynamicRendering = VK_TRUE,
.synchronization2 = VK_TRUE,
};
dvz_gpu_ctx_config_features13(&gpu_cfg, &feat13);
DvzGpuCtx* ctx = dvz_gpu_ctx(&gpu_cfg);
if (!ctx) { dvz_fprintf(stderr, "GPU context creation failed\n"); return 1; }
/* Window + canvas */
DvzWindowHost* host = dvz_window_host();
DvzWindowConfig wcfg = dvz_window_config();
wcfg.width = WIDTH;
wcfg.height = HEIGHT;
DvzWindow* window = dvz_window_create(host, DVZ_BACKEND_OFFSCREEN, &wcfg);
if (!window) { dvz_fprintf(stderr, "Window creation failed\n"); dvz_gpu_ctx_destroy(ctx); return 1; }
DvzCanvasConfig ccfg = dvz_canvas_config();
ccfg.window = window;
ccfg.device = dvz_gpu_ctx_device(ctx);
ccfg.render_mode = DVZ_CANVAS_RENDER_MODE_OFFSCREEN;
DvzCanvas* canvas = dvz_canvas_create(&ccfg);
if (!canvas) { dvz_fprintf(stderr, "Canvas creation failed\n"); dvz_window_destroy(window); dvz_window_host_destroy(host); dvz_gpu_ctx_destroy(ctx); return 1; }
/* Triangle state: compile shaders, build pipeline, upload vertices */
TriState state = {0};
if (tri_state_create(&state, dvz_gpu_ctx_device(ctx), dvz_gpu_ctx_alloc(ctx)) != 0) {
dvz_canvas_destroy(canvas);
dvz_window_destroy(window);
dvz_window_host_destroy(host);
dvz_gpu_ctx_destroy(ctx);
return 1;
}
dvz_canvas_set_draw_callback(canvas, draw_triangle, &state);
/* Frame loop */
dvz_window_host_poll(host);
int rc = dvz_canvas_frame(canvas);
if (rc == DVZ_CANVAS_FRAME_READY)
dvz_canvas_submit(canvas);
char png_out[512];
example_outpath(argv[0], "raw_triangle_vklite.png", png_out, sizeof(png_out));
dvz_canvas_capture_png(canvas, png_out);
dvz_fprintf(stdout, "raw_triangle_vklite: saved %s\n", png_out);
/* Cleanup */
tri_state_destroy(&state);
dvz_canvas_destroy(canvas);
dvz_window_destroy(window);
dvz_window_host_destroy(host);
dvz_gpu_ctx_destroy(ctx);
return 0;
}
Example details
- ID:
advanced_raw_triangle_vklite - Category:
advanced - Lane:
advanced - Status:
experimental - Source:
examples/c/advanced/raw_triangle_vklite.c - Approved adaptation starter:
no - Browser support: Native only
- Browser note: raw vklite command recording is a native runtime example
- Browser capability tags:
vklite,canvas - Validation:
smoke+png
Tags
advanced, vklite, canvas, low-level
Data
| Field | Value |
|---|---|
kind |
synthetic |