How to upload a texture to the GPU ?¶
In order to use textures and render targets, haiku provides hk_image_t
and associated utility functions in haiku/graphics.h
.
In this tutorial, we will define a texture as a CPU ressource that you’ll want to use/sample later inside a shader on the GPU.
We’ll imagine that you had previously loaded a .jpg
or .png
file from disc to CPU using any image library (like stb_image.h
).
Here’s the full course of actions:
Create the CPU buffer containing the texture pixels
Create the GPU image that will be used inside as your texture
Transfer pixels from the CPU buffer to the GPU image.
Create the buffer¶
// You previously loaded a rgba8 texture
// with the following variables:
// - texture_width : int;
// - texture_height : int;
// - pixels: unsigned char* ;
hk_buffer_t my_pixelbuf = hkgfx_buffer_create(device, &(hk_gfx_buffer_desc){
.usage_flags = HK_BUFFER_USAGE_TRANSFER_SRC_BIT,
.memory_type = HK_MEMORY_TYPE_CPU_ONLY,
.bytesize = texture_width*texture_height*4*sizeof(uint8_t),
.dataptr = texdata
});
// You can now free(pixels);
Create the image¶
hk_image_t my_texture = hkgfx_image_create(device, &(hk_gfx_image_desc){
.type = HK_IMAGE_TYPE_2D,
.extent = {.width = texture_width, .height = texture_height},
.levels = 1,
.usage_flags = HK_IMAGE_USAGE_TRANSFER_DST_BIT | HK_IMAGE_USAGE_SAMPLED_BIT,
.memory_type = HK_MEMORY_TYPE_GPU_ONLY
});
Upload pixels to the GPU¶
hk_context_t ctx = hkgfx_context_create(device);
hkgfx_context_begin(ctx);
{
// First we transfer the texture content to the image
hkgfx_context_image_barrier(ctx, &(hk_gfx_barrier_image_params){
.image = my_texture,
.prev_state = HK_IMAGE_STATE_UNDEFINED,
.next_state = HK_IMAGE_STATE_TRANSFER_DST
});
hkgfx_context_copy_buffer_to_image(ctx, my_pixelbuf, my_texture, &(hk_gfx_cmd_image_buffer_copy_params){
.aspect = HK_IMAGE_ASPECT_COLOR_BIT,
.region = {
.extent = {.width = texture_width, .height = texture_height, .depth = 1},
.layer_count = 1,
}
});
}
hkgfx_context_end(ctx);
hkgfx_device_submit(device, &(hk_gfx_submit_params){.context = ctx});
hkgfx_device_wait(device);
// after that, your texture is uploaded the GPU.
// You can cleanup my_pixelbuf using hkgfx_buffer_destroy(device, my_pixelbuf);
How to set a view from a texture ?¶
In order to use textures within a shader or as a render target, haiku provides hk_view_t
and associated utility functions in haiku/graphics.h
.
hk_view_t
is used for bindgroups and render targets while hk_image_t
is used in barriers, copy, resolve and blit commands.
Create the image ans its view¶
hk_image_t my_texture = hkgfx_image_create(device, &(hk_gfx_image_desc){
.type = HK_IMAGE_TYPE_2D,
.extent = {.width = texture_width, .height = texture_height},
.levels = 1,
.usage_flags = HK_IMAGE_USAGE_TRANSFER_DST_BIT | HK_IMAGE_USAGE_SAMPLED_BIT,
.memory_type = HK_MEMORY_TYPE_GPU_ONLY
});
/* [...] */
hk_view_t my_texture_view = hkgfx_view_create(device, &(hk_gfx_view_desc){
.src_image = my_texture_view,
.type = HK_IMAGE_TYPE_2D,
.aspect_flags = HK_IMAGE_ASPECT_COLOR_BIT,
});
Usecase: bindgroup¶
hk_bindgroup_t mybindgroup = hkgfx_bindgroup_create(device,&(hk_gfx_bindgroup_desc){
.layout = my_pipeline_layout,
.images = {
{.image_view = my_texture_view, .sampler = my_texture_sampler }
}
});
Usecase: render target¶
hkgfx_context_render_begin(rendering_context, &(hk_gfx_render_targets_params){
.layer_count = 1,
.color_count = 1,
.render_area = { .extent = {app_frame_width, app_frame_height} },
.color_attachments = {
{.target_render = my_texture_view, .clear_color = {.value_f32 = {0.2f,0.2f,0.2f,1.f}}}
},
});
hkgfx_context_set_pipeline(rendering_context, mypipeline);
hkgfx_context_set_bindings(rendering_context, mypipeline, 0, mybindgroup);
hkgfx_context_draw(rendering_context, 3, 1, 0, 0);
hkgfx_context_render_end(rendering_context);