From 8bc6f68b3bca9c864cafd1a8f080822ffb09d9e5 Mon Sep 17 00:00:00 2001 From: Sven Balzer <4653051+Kyuusokuna@users.noreply.github.com> Date: Fri, 1 May 2026 19:55:39 +0200 Subject: [PATCH] convert gpu map format from buffer to texture2d --- src/main.cpp | 278 ++++++++++++++++++++++++----------------- src/shaders/world.wgsl | 27 ++-- 2 files changed, 178 insertions(+), 127 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 9419919..c692597 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -261,10 +261,12 @@ struct Map { Uint32 version; i32vec2 size; - Uint32 *tiles; + Uint16 *tiles; char name[64]; - WGPUBuffer gpu_buffer; + + WGPUTexture texture; + WGPUBindGroup bind_group; }; static Map current_map; @@ -410,6 +412,98 @@ static WGPUBuffer create_buffer(WGPUBufferUsage usage, Uint32 num_bytes, void *d return buffer; } +static Uint32 wgpuTextureFormatGetSize(WGPUTextureFormat format) { + switch (format) { + default: assert(false); return 1; + + case WGPUTextureFormat_R8Unorm: return 1; + case WGPUTextureFormat_R16Uint: return 2; + case WGPUTextureFormat_RGBA8UnormSrgb: return 4; + } +} + +static WGPUTexture create_shader_texture(const char *name, void *data, uint32_t width, uint32_t height, WGPUTextureFormat format) { + WGPUTextureDescriptor descriptor = { + .label = { .data = name, .length = WGPU_STRLEN }, + .usage = WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst, + .dimension = WGPUTextureDimension_2D, + .size = { .width = width, .height = height, .depthOrArrayLayers = 1 }, + .format = format, + .mipLevelCount = 1, + .sampleCount = 1, + .viewFormatCount = 0, + .viewFormats = NULL, + }; + + WGPUTexture texture = wgpuDeviceCreateTexture(device, &descriptor); + if (!texture) { + log_error("Failed to create texture."); + return NULL; + } + + if (data) { + Uint32 texel_size = wgpuTextureFormatGetSize(format); + + WGPUTexelCopyTextureInfo destination = { + .texture = texture, + .mipLevel = 0, + .origin = { .x = 0, .y = 0, .z = 0 }, + .aspect = WGPUTextureAspect_All, + }; + + WGPUTexelCopyBufferLayout data_layout = { + .offset = 0, + .bytesPerRow = width * texel_size, + .rowsPerImage = height, + }; + + WGPUExtent3D extent = { + .width = width, + .height = height, + .depthOrArrayLayers = 1, + }; + + wgpuQueueWriteTexture(queue, &destination, data, width * height * texel_size, &data_layout, &extent); + } + + return texture; +} + +static WGPUTexture create_shader_texture(const char *path) { + char path_to_load[256] = ASSETS_PATH; + SDL_strlcat(path_to_load, path, SDL_arraysize(path_to_load)); + + int width = 0, height = 0, channels = 0; + stbi_uc *data = stbi_load(path_to_load, &width, &height, &channels, 0); + if (!data) { + log_error("Failed to load texture (\"%s\").", path_to_load); + return NULL; + } + + WGPUTexture result = create_shader_texture(path, (char *)data, width, height, channels == 4 ? WGPUTextureFormat_RGBA8UnormSrgb : WGPUTextureFormat_R8Unorm); + if (!result) { + log_error("Failed to load texture (\"%s\").", path_to_load); + stbi_image_free(data); + return NULL; + } + stbi_image_free(data); + + return result; +} + +static void update_texture(WGPUTexture texture, u32vec2 origin, u32vec2 size, void *data, WGPUTexelCopyBufferLayout data_layout) { + WGPUTexelCopyTextureInfo info = { + .texture = texture, + .mipLevel = 0, + .origin = { origin.x, origin.y, 0 }, + .aspect = WGPUTextureAspect_All, + }; + + WGPUExtent3D extent = { size.x, size.y, 1 }; + + wgpuQueueWriteTexture(queue, &info, data, data_layout.bytesPerRow * data_layout.rowsPerImage, &data_layout, &extent); +} + #define MAP_FILE_VERSION (2u) static bool save_map(Map map) { @@ -488,7 +582,7 @@ static bool load_map(const char *name, Map *result) { return false; } - result->tiles = (Uint32*)malloc(result->size.x * result->size.y * sizeof(Uint32)); + result->tiles = (Uint16*)malloc(result->size.x * result->size.y * sizeof(Uint16)); for (int i = 0; i < result->size.x * result->size.y; i++) { if (result->version == 2) { @@ -511,18 +605,28 @@ static bool load_map(const char *name, Map *result) { assert(false && "Tried to load an unsupported map version."); log_error("Tried to load an unsupported map version. Aborting."); free(result->tiles); - return 1; + return false; } } char buffer_name[256] = "Map "; SDL_strlcat(buffer_name, result->name, SDL_arraysize(buffer_name)); - result->gpu_buffer = create_buffer(WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst, result->size.x * result->size.y * sizeof(Uint32), result->tiles, buffer_name); - if (!result->gpu_buffer) { - log_error("Failed to create buffer. Exiting."); - return 1; - } + result->texture = create_shader_texture("map_texture", result->tiles, result->size.x, result->size.y, WGPUTextureFormat_R16Uint); + WGPUTextureView texture_view = wgpuTextureCreateView(result->texture, NULL); + + WGPUBindGroupEntry map_bind_group_entries[] = { + { .binding = 0, .textureView = texture_view }, + }; + + WGPUBindGroupDescriptor map_bind_group_descriptor = { + .label = { .data = "map_bind_group", .length = WGPU_STRLEN }, + .layout = wgpuRenderPipelineGetBindGroupLayout(world_render_pipeline, 2), + .entryCount = SDL_arraysize(map_bind_group_entries), + .entries = map_bind_group_entries, + }; + result->bind_group = wgpuDeviceCreateBindGroup(device, &map_bind_group_descriptor); + wgpuTextureViewRelease(texture_view); SDL_Log("Loaded map file."); return true; @@ -532,14 +636,17 @@ static void unload_map(Map *map) { map->size = i32vec2(0, 0); free(map->tiles); SDL_free(map->name); - wgpuBufferRelease(map->gpu_buffer); + wgpuTextureRelease(map->texture); + wgpuBindGroupRelease(map->bind_group); } static void change_map_size(Map *map, char direction, int amount) { - WGPUBuffer old_gpu_buffer = map->gpu_buffer; - Uint32 *old_map = map->tiles; - Sint32 old_map_width = map->size.x; - Sint32 old_map_height = map->size.y; + wgpuBindGroupRelease(map->bind_group); + wgpuTextureRelease(map->texture); + + Uint16*old_map = map->tiles; + Sint32 old_map_width = map->size.x; + Sint32 old_map_height = map->size.y; Sint32 new_x_offset = 0; Sint32 new_y_offset = 0; @@ -585,13 +692,14 @@ static void change_map_size(Map *map, char direction, int amount) { to_fill_y_offset = old_map_height; } - map->tiles = (Uint32 *)malloc(map->size.x * map->size.y * sizeof(Uint32)); + map->tiles = (Uint16 *)malloc(map->size.x * map->size.y * sizeof(Uint16)); for (int y = 0; y < min(old_map_height, map->size.y); y++) { for (int x = 0; x < min(old_map_width, map->size.x); x++) { map->tiles[(y + new_y_offset) * map->size.x + (x + new_x_offset)] = old_map[(y + old_y_offset) * old_map_width + (x + old_x_offset)]; } } + free(old_map); for (int y = 0; y < to_fill_height; y++) { for (int x = 0; x < to_fill_width; x++) { @@ -601,81 +709,21 @@ static void change_map_size(Map *map, char direction, int amount) { player.position = clamp(player.position, i32vec2(0, 0), map->size - 2); - map->gpu_buffer = create_buffer(WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst, map->size.x * map->size.y * sizeof(Uint32), map->tiles, "world_buffer"); - if (!map->gpu_buffer) { - log_error("Failed to create buffer. Exiting."); - exit(1); - } + create_shader_texture("map_texture", map->tiles, map->size.x, map->size.y, WGPUTextureFormat_R16Uint); + WGPUTextureView texture_view = wgpuTextureCreateView(map->texture, NULL); - free(old_map); - wgpuBufferRelease(old_gpu_buffer); -} - -static WGPUTexture create_shader_texture(const char *name, const char *data, uint32_t width, uint32_t height, int channels) { - WGPUTextureDescriptor descriptor = { - .label = { .data = name, .length = WGPU_STRLEN }, - .usage = WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst, - .dimension = WGPUTextureDimension_2D, - .size = { .width = width, .height = height, .depthOrArrayLayers = 1 }, - .format = channels == 4 ? WGPUTextureFormat_RGBA8UnormSrgb : WGPUTextureFormat_R8Unorm, - .mipLevelCount = 1, - .sampleCount = 1, - .viewFormatCount = 0, - .viewFormats = NULL, + WGPUBindGroupEntry map_bind_group_entries[] = { + { .binding = 0, .textureView = texture_view }, }; - WGPUTexture texture = wgpuDeviceCreateTexture(device, &descriptor); - if (!texture) { - log_error("Failed to create texture."); - return NULL; - } - - if (data) { - WGPUTexelCopyTextureInfo destination = { - .texture = texture, - .mipLevel = 0, - .origin = { .x = 0, .y = 0, .z = 0 }, - .aspect = WGPUTextureAspect_All, - }; - - WGPUTexelCopyBufferLayout data_layout = { - .offset = 0, - .bytesPerRow = width * channels, - .rowsPerImage = height, - }; - - WGPUExtent3D extent = { - .width = width, - .height = height, - .depthOrArrayLayers = 1, - }; - - wgpuQueueWriteTexture(queue, &destination, data, width * height * channels, &data_layout, &extent); - } - - return texture; -} - -static WGPUTexture create_shader_texture(const char *path) { - char path_to_load[256] = ASSETS_PATH; - SDL_strlcat(path_to_load, path, SDL_arraysize(path_to_load)); - - int width = 0, height = 0, channels = 0; - stbi_uc *data = stbi_load(path_to_load, &width, &height, &channels, 0); - if (!data) { - log_error("Failed to load texture (\"%s\").", path_to_load); - return NULL; - } - - WGPUTexture result = create_shader_texture(path, (char *)data, width, height, channels); - if (!result) { - log_error("Failed to load texture (\"%s\").", path_to_load); - stbi_image_free(data); - return NULL; - } - stbi_image_free(data); - - return result; + WGPUBindGroupDescriptor map_bind_group_descriptor = { + .label = { .data = "map_bind_group", .length = WGPU_STRLEN }, + .layout = wgpuRenderPipelineGetBindGroupLayout(world_render_pipeline, 2), + .entryCount = SDL_arraysize(map_bind_group_entries), + .entries = map_bind_group_entries, + }; + map->bind_group = wgpuDeviceCreateBindGroup(device, &map_bind_group_descriptor); + wgpuTextureViewRelease(texture_view); } static void blit(char *dst, Sint32 dst_pitch, Sint32 dst_x, Sint32 dst_y, char *src, Sint32 src_pitch, Sint32 width, Sint32 height, int components = 4) { @@ -1012,9 +1060,31 @@ static bool recreate_graphics_pipelines() { WGPUBindGroupLayout world_bind_group_layout = wgpuDeviceCreateBindGroupLayout(device, &world_bind_group_layout_descriptor); + WGPUBindGroupLayoutEntry map_bind_group_layout_entries[] = { + { + .binding = 0, + .visibility = WGPUShaderStage_Vertex, + + .texture = { + .sampleType = WGPUTextureSampleType_Uint, + .viewDimension = WGPUTextureViewDimension_2D, + .multisampled = false, + }, + }, + }; + + WGPUBindGroupLayoutDescriptor map_bind_group_layout_descriptor = { + .label = { .data = "map_bind_group_layout", .length = WGPU_STRLEN }, + .entryCount = SDL_arraysize(map_bind_group_layout_entries), + .entries = map_bind_group_layout_entries, + }; + + WGPUBindGroupLayout map_bind_group_layout = wgpuDeviceCreateBindGroupLayout(device, &map_bind_group_layout_descriptor); + WGPUBindGroupLayout world_bind_group_layouts[] = { frame_data_bind_group_layout, world_bind_group_layout, + map_bind_group_layout, }; WGPUPipelineLayoutDescriptor world_pipeline_layout_descriptor = { @@ -1037,22 +1107,7 @@ static bool recreate_graphics_pipelines() { WGPUShaderModule world_shader = wgpuDeviceCreateShaderModule(device, &world_shader_descriptor); - WGPUVertexAttribute instance_buffer_attributes[] = { - { - .format = WGPUVertexFormat_Uint32, - .offset = 0, - .shaderLocation = 0, - }, - }; - - WGPUVertexBufferLayout vertex_buffer_layouts[] = { - { - .stepMode = WGPUVertexStepMode_Instance, - .arrayStride = sizeof(Uint32), - .attributeCount = SDL_arraysize(instance_buffer_attributes), - .attributes = instance_buffer_attributes, - }, - }; + WGPUVertexBufferLayout vertex_buffer_layouts[] = {}; WGPUFragmentState world_fragment_state = { .module = world_shader, @@ -1357,7 +1412,7 @@ static void change_map_tile(Sint32 pos_x, Sint32 pos_y, TileKind kind) { if (0 <= pos_x + 0 && pos_x + 0 < current_map.size.x && 0 <= pos_y + 0 && pos_y + 0 < current_map.size.y) current_map.tiles[(pos_y + 0) * current_map.size.x + pos_x + 0] = find_matching_tile(corner_infos[3]); - update_buffer(current_map.gpu_buffer, 0, current_map.size.x * current_map.size.y * sizeof(Uint16), current_map.tiles); + update_texture(current_map.texture, { 0, 0 }, current_map.size, current_map.tiles, { 0, (Uint32)(current_map.size.x * sizeof(Uint16)), (Uint32)current_map.size.y }); } static void SameLineOrWrap(const ImVec2& size) { @@ -1792,8 +1847,7 @@ static void process_event_editor(SDL_Event event) { current_map.tiles[x + current_map.size.x * y] = selected_tile; } } - - update_buffer(current_map.gpu_buffer, 0, current_map.size.x * current_map.size.y * sizeof(Uint32), current_map.tiles); + update_texture(current_map.texture, { 0, 0 }, current_map.size, current_map.tiles, { 0, (Uint32)(current_map.size.x * sizeof(Uint16)), (Uint32)current_map.size.y }); } dragging_tile_change = false; @@ -2089,15 +2143,15 @@ static void render_editor(WGPURenderPassColorAttachment framebuffer) { WGPURenderPassEncoder render_pass_encoder = wgpuCommandEncoderBeginRenderPass(command_encoder, &render_pass_descriptor); wgpuRenderPassEncoderSetBindGroup(render_pass_encoder, 0, per_frame_bind_group, 0, NULL); - { // Draw Map + { ZoneScopedN("Draw Map"); wgpuRenderPassEncoderSetPipeline(render_pass_encoder, world_render_pipeline); - wgpuRenderPassEncoderSetVertexBuffer(render_pass_encoder, 0, current_map.gpu_buffer, 0, WGPU_WHOLE_SIZE); wgpuRenderPassEncoderSetBindGroup(render_pass_encoder, 1, world_bind_group, 0, NULL); + wgpuRenderPassEncoderSetBindGroup(render_pass_encoder, 2, current_map.bind_group, 0, NULL); wgpuRenderPassEncoderDraw(render_pass_encoder, 6, current_map.size.y * current_map.size.x, 0, 0); } - if (show_grid) { // Draw Grid + if (show_grid) { ZoneScopedN("Draw Grid"); Uint32 num_grid_cells = current_map.size.y * current_map.size.x; @@ -2256,15 +2310,15 @@ static void render_game(WGPURenderPassColorAttachment framebuffer) { WGPURenderPassEncoder render_pass_encoder = wgpuCommandEncoderBeginRenderPass(command_encoder, &render_pass_descriptor); wgpuRenderPassEncoderSetBindGroup(render_pass_encoder, 0, per_frame_bind_group, 0, NULL); - { // Draw Map + { ZoneScopedN("Draw Map"); wgpuRenderPassEncoderSetPipeline(render_pass_encoder, world_render_pipeline); - wgpuRenderPassEncoderSetVertexBuffer(render_pass_encoder, 0, current_map.gpu_buffer, 0, WGPU_WHOLE_SIZE); wgpuRenderPassEncoderSetBindGroup(render_pass_encoder, 1, world_bind_group, 0, NULL); + wgpuRenderPassEncoderSetBindGroup(render_pass_encoder, 2, current_map.bind_group, 0, NULL); wgpuRenderPassEncoderDraw(render_pass_encoder, 6, current_map.size.y * current_map.size.x, 0, 0); } - { // Draw Player + { ZoneScopedN("Draw Player"); wgpuRenderPassEncoderSetPipeline(render_pass_encoder, basic_render_pipeline); wgpuRenderPassEncoderSetIndexBuffer(render_pass_encoder, index_buffer, WGPUIndexFormat_Uint16, 0, WGPU_WHOLE_SIZE); diff --git a/src/shaders/world.wgsl b/src/shaders/world.wgsl index 7816bfb..56176f5 100644 --- a/src/shaders/world.wgsl +++ b/src/shaders/world.wgsl @@ -1,11 +1,6 @@ struct VertexShaderInput { - // Per Vertex - @builtin(vertex_index) vertex_index: u32, - - // Per Instance + @builtin(vertex_index) vertex_index: u32, @builtin(instance_index) instance_index: u32, - - @location(0) tile: u32, }; struct VertexShaderOutput { @@ -30,22 +25,24 @@ struct Per_Frame_Data { @group(0) @binding(0) var view_projection_matrix: mat4x4; @group(0) @binding(1) var per_frame: Per_Frame_Data; +@group(2) @binding(0) var map_texture: texture_2d; + @vertex fn main_vertex(input: VertexShaderInput) -> VertexShaderOutput { var output: VertexShaderOutput; - let tile_pos = vec2(f32(input.instance_index % per_frame.map_width), f32(input.instance_index / per_frame.map_width)) - vec2(0.5, 0.5); + let tile_pos = vec2(input.instance_index % per_frame.map_width, input.instance_index / per_frame.map_width); + output.tile = textureLoad(map_texture, tile_pos, 0).r; + switch (input.vertex_index) { - case 0: { output.pos = vec4(tile_pos + vec2(-0.5, 0.5), 0, 1) * view_projection_matrix; output.uv = vec2(0, 0); } - case 1: { output.pos = vec4(tile_pos + vec2(-0.5, -0.5), 0, 1) * view_projection_matrix; output.uv = vec2(0, 1); } - case 2: { output.pos = vec4(tile_pos + vec2( 0.5, -0.5), 0, 1) * view_projection_matrix; output.uv = vec2(1, 1); } - case 3: { output.pos = vec4(tile_pos + vec2(-0.5, 0.5), 0, 1) * view_projection_matrix; output.uv = vec2(0, 0); } - case 4: { output.pos = vec4(tile_pos + vec2( 0.5, -0.5), 0, 1) * view_projection_matrix; output.uv = vec2(1, 1); } - case 5: { output.pos = vec4(tile_pos + vec2( 0.5, 0.5), 0, 1) * view_projection_matrix; output.uv = vec2(1, 0); } + case 0: { output.pos = vec4(vec2(tile_pos) - vec2(0.5, 0.5) + vec2(-0.5, 0.5), 0, 1) * view_projection_matrix; output.uv = vec2(0, 0); } + case 1: { output.pos = vec4(vec2(tile_pos) - vec2(0.5, 0.5) + vec2(-0.5, -0.5), 0, 1) * view_projection_matrix; output.uv = vec2(0, 1); } + case 2: { output.pos = vec4(vec2(tile_pos) - vec2(0.5, 0.5) + vec2( 0.5, -0.5), 0, 1) * view_projection_matrix; output.uv = vec2(1, 1); } + case 3: { output.pos = vec4(vec2(tile_pos) - vec2(0.5, 0.5) + vec2(-0.5, 0.5), 0, 1) * view_projection_matrix; output.uv = vec2(0, 0); } + case 4: { output.pos = vec4(vec2(tile_pos) - vec2(0.5, 0.5) + vec2( 0.5, -0.5), 0, 1) * view_projection_matrix; output.uv = vec2(1, 1); } + case 5: { output.pos = vec4(vec2(tile_pos) - vec2(0.5, 0.5) + vec2( 0.5, 0.5), 0, 1) * view_projection_matrix; output.uv = vec2(1, 0); } default: {} } - output.tile = input.tile; - return output; }