Skip to content

Commit c0e2cbb

Browse files
committed
Add WebGPU image loading and displaying example
1 parent 2b29028 commit c0e2cbb

File tree

2 files changed

+167
-1
lines changed

2 files changed

+167
-1
lines changed

docs/Image-Loading-and-Displaying-Examples.md

Lines changed: 167 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- [Example for DirectX12 users](#example-for-directx12-users)
1111
- [Example for SDL_Renderer users](#example-for-sdl_renderer-users)
1212
- [Example for Vulkan users](#example-for-vulkan-users)
13+
- [Example for WebGPU users](#example-for-webgpu-users)
1314
- [Loading from Memory](#loading-from-memory)
1415
- [About Texture Coordinates](#about-texture-coordinates)
1516

@@ -937,11 +938,176 @@ RemoveTexture(&my_texture);
937938
938939
----
939940
941+
## Example for WebGPU users
942+
943+
We will here use [stb_image.h](https://github.com/nothings/stb/blob/master/stb_image.h) to load images from disk.
944+
945+
Add at the top of one of your source file:
946+
```cpp
947+
#define _CRT_SECURE_NO_WARNINGS
948+
#define STB_IMAGE_IMPLEMENTATION
949+
#include "stb_image.h"
950+
951+
// Simple helper function to load an image into a WebGPU texture with common settings
952+
bool LoadTextureFromMemory(const void * data,
953+
size_t data_size,
954+
WGPUDevice device,
955+
WGPUQueue queue,
956+
WGPUTexture * out_texture,
957+
WGPUTextureView * out_texture_view,
958+
int * out_width,
959+
int * out_height) {
960+
// Load image
961+
int image_width = 0;
962+
int image_height = 0;
963+
// Ask stbi to output 4 channels since WebGPU doesn't have 3-channels texture format
964+
const int output_channels = 4;
965+
void * const image_data = stbi_load_from_memory((const unsigned char *)data,
966+
(int)data_size,
967+
&image_width,
968+
&image_height,
969+
NULL,
970+
output_channels);
971+
if (image_data == NULL)
972+
return false;
973+
974+
// Create a WebGPU texture
975+
WGPUTextureDescriptor texture_desc;
976+
texture_desc.nextInChain = NULL;
977+
texture_desc.label = NULL;
978+
texture_desc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding;
979+
texture_desc.dimension = WGPUTextureDimension_2D;
980+
texture_desc.size.width = (uint32_t)image_width;
981+
texture_desc.size.height = (uint32_t)image_height;
982+
texture_desc.size.depthOrArrayLayers = 1;
983+
texture_desc.format = WGPUTextureFormat_RGBA8Unorm;
984+
texture_desc.mipLevelCount = 1;
985+
texture_desc.sampleCount = 1;
986+
texture_desc.viewFormatCount = 0;
987+
texture_desc.viewFormats = NULL;
988+
WGPUTexture image_texture = wgpuDeviceCreateTexture(device, &texture_desc);
989+
990+
// Write loaded data to texture
991+
WGPUImageCopyTexture dest;
992+
dest.nextInChain = NULL;
993+
dest.texture = image_texture;
994+
dest.mipLevel = 0;
995+
dest.origin.x = 0;
996+
dest.origin.y = 0;
997+
dest.origin.z = 0;
998+
dest.aspect = WGPUTextureAspect_All;
999+
1000+
WGPUTextureDataLayout src;
1001+
src.nextInChain = NULL;
1002+
src.offset = 0;
1003+
src.bytesPerRow = (uint32_t)(output_channels * image_width);
1004+
src.rowsPerImage = (uint32_t)image_height;
1005+
1006+
const size_t bytes = src.bytesPerRow * src.rowsPerImage;
1007+
WGPUExtent3D write_size;
1008+
write_size.width = (uint32_t)image_width;
1009+
write_size.height = (uint32_t)image_height;
1010+
write_size.depthOrArrayLayers = 1;
1011+
wgpuQueueWriteTexture(queue, &dest, image_data, bytes, &src, &write_size);
1012+
1013+
// Create a WebGPU texture view
1014+
WGPUTextureViewDescriptor view_desc;
1015+
view_desc.nextInChain = NULL;
1016+
view_desc.label = NULL;
1017+
view_desc.format = WGPUTextureFormat_RGBA8Unorm;
1018+
view_desc.dimension = WGPUTextureViewDimension_2D;
1019+
view_desc.baseMipLevel = 0;
1020+
view_desc.mipLevelCount = 1;
1021+
view_desc.baseArrayLayer = 0;
1022+
view_desc.arrayLayerCount = 1;
1023+
view_desc.aspect = WGPUTextureAspect_All;
1024+
1025+
*out_texture = image_texture;
1026+
*out_texture_view = wgpuTextureCreateView(image_texture, &view_desc);
1027+
*out_width = image_width;
1028+
*out_height = image_height;
1029+
1030+
return true;
1031+
}
1032+
1033+
// Open and read a file, then forward to LoadTextureFromMemory()
1034+
bool LoadTextureFromFile(const char * file_name,
1035+
WGPUDevice device,
1036+
WGPUQueue queue,
1037+
WGPUTexture * out_texture,
1038+
WGPUTextureView * out_texture_view,
1039+
int * out_width,
1040+
int * out_height) {
1041+
FILE * f = fopen(file_name, "rb");
1042+
if (f == NULL)
1043+
return false;
1044+
fseek(f, 0, SEEK_END);
1045+
long file_size = ftell(f);
1046+
if (file_size == -1)
1047+
return false;
1048+
fseek(f, 0, SEEK_SET);
1049+
void * file_data = IM_ALLOC(file_size);
1050+
fread(file_data, 1, (size_t)file_size, f);
1051+
bool ret = LoadTextureFromMemory(file_data,
1052+
(size_t)file_size,
1053+
device,
1054+
queue,
1055+
out_texture,
1056+
out_texture_view,
1057+
out_width,
1058+
out_height);
1059+
IM_FREE(file_data);
1060+
return ret;
1061+
}
1062+
```
1063+
1064+
After initializing a `WGPUDevice` and a `WGPUQueue`, load our texture:
1065+
```cpp
1066+
WGPUDevice device = /* ... */;
1067+
WGPUQueue queue = /* ... */;
1068+
1069+
WGPUTexture my_texture = NULL;
1070+
WGPUTextureView my_texture_view = NULL;
1071+
int my_image_width = 0;
1072+
int my_image_height = 0;
1073+
bool ret = LoadTextureFromFile("../../MyImage01.jpg",
1074+
device,
1075+
queue,
1076+
&my_texture,
1077+
&my_texture_view,
1078+
&my_image_width,
1079+
&my_image_height);
1080+
IM_ASSERT(ret);
1081+
```
1082+
1083+
In the snippet of code above, we added an assert `IM_ASSERT(ret)` to check if the image file was loaded correctly. You may also use your Debugger and confirm that `my_image_texture` is not zero and that `my_image_width` `my_image_height` are correct.
1084+
1085+
Now that we have a WebGPU texture view and its dimensions, we can display it in our main loop:
1086+
```cpp
1087+
ImGui::Begin("WebGPU Texture Test");
1088+
ImGui::Text("pointer = %p", my_texture_view);
1089+
ImGui::Text("size = %d x %d", my_image_width, my_image_height);
1090+
ImGui::Image((void *)my_texture_view, ImVec2(my_image_width, my_image_height));
1091+
ImGui::End();
1092+
```
1093+
1094+
Don't forget to clean the memory at the end of the program:
1095+
```cpp
1096+
wgpuTextureViewRelease(my_texture_view);
1097+
wgpuTextureRelease(my_texture);
1098+
```
1099+
1100+
![image](WebGPU_image_loading_screenshot.png)
1101+
1102+
##### [Return to Index](#index)
1103+
1104+
----
1105+
9401106
## Loading from Memory
9411107
9421108
If instead of loading from a file you would like to load from memory, you can call `stbi_load_from_memory()` instead of `stbi_load()`.
9431109
All `LoadTextureFromFile()` function could be reworked to call `LoadTextureFromMemory()` which contains most of the code.
944-
See our implementation of `LoadTextureFromFile()` for OpenGL, DX11 and DX12 examples.
1110+
See our implementation of `LoadTextureFromFile()` for OpenGL, DX11, DX12 and WebGPU examples.
9451111
9461112
##### [Return to Index](#index)
9471113
211 KB
Loading

0 commit comments

Comments
 (0)