#include "renderer.h" #include #include typedef struct R_Buffer_Impl { WGPUBuffer buffer; Uint32 num_bytes; R_Buffer_Usage usage; } R_Buffer_Impl; typedef struct R_Texture_Impl { WGPUTexture texture; WGPUTextureView view; Uint32 width; Uint32 height; bool multisampled; R_Texture_Format format; } R_Texture_Impl; R_Texture R_surface; static bool init_done; static SDL_Window *window; static WGPUInstance instance; static WGPUDevice device; static WGPUQueue queue; static WGPUSurface surface; static WGPUSurfaceConfiguration surface_configuration; static R_Texture_Impl surface_texture; static R_Texture framebuffer; static WGPUCommandEncoder command_encoder; static WGPURenderPassEncoder pass_encoder; static WGPURenderPipeline shaders[R_SHADER_COUNT]; static R_Shader current_shader; static WGPUSampler bilinear_sampler; static WGPUTextureFormat texture_formats[] = { [R_TEXTURE_FORMAT_R8_UNORM] = WGPUTextureFormat_R8Unorm, [R_TEXTURE_FORMAT_R16_UINT] = WGPUTextureFormat_R16Uint, [R_TEXTURE_FORMAT_RGBA8_UNORM] = WGPUTextureFormat_RGBA8Unorm, [R_TEXTURE_FORMAT_RGBA8_UNORM_SRGB] = WGPUTextureFormat_RGBA8UnormSrgb, [R_TEXTURE_FORMAT_BGRA8_UNORM] = WGPUTextureFormat_BGRA8Unorm, [R_TEXTURE_FORMAT_BGRA8_UNORM_SRGB] = WGPUTextureFormat_BGRA8UnormSrgb, }; static_assert(SDL_arraysize(texture_formats) == R_TEXTURE_FORMAT_COUNT); static Uint32 texel_sizes[] = { [R_TEXTURE_FORMAT_R8_UNORM] = 1, [R_TEXTURE_FORMAT_R16_UINT] = 2, [R_TEXTURE_FORMAT_RGBA8_UNORM] = 4, [R_TEXTURE_FORMAT_RGBA8_UNORM_SRGB] = 4, [R_TEXTURE_FORMAT_BGRA8_UNORM] = 4, [R_TEXTURE_FORMAT_BGRA8_UNORM_SRGB] = 4, }; static_assert(SDL_arraysize(texel_sizes) == R_TEXTURE_FORMAT_COUNT); static WGPUTextureUsage texture_usages[] = { [R_TEXTURE_USAGE_SAMPLED] = WGPUTextureUsage_TextureBinding, [R_TEXTURE_USAGE_STORAGE] = WGPUTextureUsage_TextureBinding, [R_TEXTURE_USAGE_TARGET] = WGPUTextureUsage_RenderAttachment, }; static_assert(SDL_arraysize(texture_usages) == R_TEXTURE_USAGE_COUNT); static WGPUBufferUsage buffer_usages[] = { [R_BUFFER_USAGE_UNIFORM] = WGPUBufferUsage_Uniform, [R_BUFFER_USAGE_STORAGE] = WGPUBufferUsage_Storage, [R_BUFFER_USAGE_VERTEX] = WGPUBufferUsage_Vertex, [R_BUFFER_USAGE_INDEX] = WGPUBufferUsage_Index, }; static_assert(SDL_arraysize(buffer_usages) == R_BUFFER_USAGE_COUNT); static WGPULoadOp load_ops[] = { [R_LOAD_OP_LOAD] = WGPULoadOp_Load, [R_LOAD_OP_CLEAR] = WGPULoadOp_Clear, }; static_assert(SDL_arraysize(load_ops) == R_LOAD_OP_COUNT); static WGPUStoreOp store_ops[] = { [R_STORE_OP_STORE] = WGPUStoreOp_Store, [R_STORE_OP_DISCARD] = WGPUStoreOp_Discard, }; static_assert(SDL_arraysize(store_ops) == R_STORE_OP_COUNT); R_Texture renderer_texture_create(R_Texture_Format format, R_Texture_Usage usage, bool multisampled, Uint32 width, Uint32 height, void *data, const char *debug_name) { WGPUTextureDescriptor descriptor = { .label = { .data = debug_name, .length = WGPU_STRLEN }, .usage = texture_usages[usage] | WGPUTextureUsage_CopyDst, .dimension = WGPUTextureDimension_2D, .size = { .width = width, .height = height, .depthOrArrayLayers = 1 }, .format = texture_formats[format], .mipLevelCount = 1, .sampleCount = multisampled ? 4 : 1, .viewFormatCount = 0, .viewFormats = NULL, }; WGPUTexture texture = wgpuDeviceCreateTexture(device, &descriptor); if (!texture) return NULL; WGPUTextureView view = wgpuTextureCreateView(texture, NULL); if (!view) { wgpuTextureRelease(texture); return NULL; } if (data) { Uint32 texel_size = texel_sizes[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); } R_Texture_Impl *result = SDL_calloc(1, sizeof(R_Texture_Impl)); if (!result) { wgpuTextureViewRelease(view); wgpuTextureRelease(texture); return NULL; } *result = (R_Texture_Impl){ .texture = texture, .view = view, .width = width, .height = height, .multisampled = multisampled, .format = format, }; return result; } void renderer_texture_update(R_Texture texture, Uint32 offset_x, Uint32 offset_y, Uint32 width, Uint32 height, void *data, Uint32 bytes_per_row) { WGPUTexelCopyTextureInfo info = { .texture = texture->texture, .mipLevel = 0, .origin = { offset_x, offset_y, 0 }, .aspect = WGPUTextureAspect_All, }; WGPUTexelCopyBufferLayout data_layout = { .offset = 0, .bytesPerRow = bytes_per_row, .rowsPerImage = height, }; WGPUExtent3D extent = { width, height, 1 }; wgpuQueueWriteTexture(queue, &info, data, data_layout.bytesPerRow * data_layout.rowsPerImage, &data_layout, &extent); } void renderer_texture_destroy(R_Texture texture) { wgpuTextureViewRelease(texture->view); wgpuTextureRelease(texture->texture); SDL_free(texture); } Uint32 renderer_texture_get_width(R_Texture texture) { return texture->width; } Uint32 renderer_texture_get_height(R_Texture texture) { return texture->height; } bool renderer_texture_get_multisampled(R_Texture texture) { return texture->multisampled; } R_Texture_Format renderer_texture_get_format(R_Texture texture) { return texture->format; } R_Buffer renderer_buffer_create(R_Buffer_Usage usage, Uint32 num_bytes, void *data, const char *debug_name) { WGPUBufferDescriptor descriptor = { .label = { .data = debug_name, .length = WGPU_STRLEN }, .usage = buffer_usages[usage] | WGPUBufferUsage_CopyDst, .size = num_bytes, .mappedAtCreation = data != NULL, }; WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor); if (!buffer) return NULL; if (data) { void *mapped_data = wgpuBufferGetMappedRange(buffer, 0, num_bytes); memcpy(mapped_data, data, num_bytes); wgpuBufferUnmap(buffer); } R_Buffer_Impl *result = SDL_calloc(1, sizeof(R_Buffer_Impl)); if (!result) { wgpuBufferRelease(buffer); return NULL; } *result = (R_Buffer_Impl){ .buffer = buffer, .num_bytes = num_bytes, .usage = usage, }; return result; } void renderer_buffer_update(R_Buffer buffer, Uint32 offset, Uint32 num_bytes, void *data) { wgpuQueueWriteBuffer(queue, buffer->buffer, offset, data, num_bytes); } void renderer_buffer_destroy(R_Buffer buffer) { wgpuBufferRelease(buffer->buffer); SDL_free(buffer); } Uint32 renderer_buffer_get_size(R_Buffer buffer) { return buffer->num_bytes; } R_Buffer_Usage renderer_buffer_get_usage(R_Buffer buffer){ return buffer->usage; } void renderer_frame_begin() { SDL_assert(command_encoder == NULL); SDL_assert(R_surface == NULL); Sint32 window_width = 0, window_height = 0; SDL_GetWindowSizeInPixels(window, &window_width, &window_height); if (surface_configuration.width != window_width || surface_configuration.height != window_height) { surface_configuration.width = window_width; surface_configuration.height = window_height; wgpuSurfaceConfigure(surface, &surface_configuration); } WGPUSurfaceTexture wgpu_surface_texture; wgpuSurfaceGetCurrentTexture(surface, &wgpu_surface_texture); if (wgpu_surface_texture.status != WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal && wgpu_surface_texture.status != WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal) { SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to get current surface texture (%x). Exiting.", wgpu_surface_texture.status); SDL_assert_always(false); // TODO: recovery on outdated or device loss return; } surface_texture.texture = wgpu_surface_texture.texture; surface_texture.view = wgpuTextureCreateView(wgpu_surface_texture.texture, NULL); surface_texture.width = surface_configuration.width; surface_texture.height = surface_configuration.height; command_encoder = wgpuDeviceCreateCommandEncoder(device, NULL); R_surface = &surface_texture; } void renderer_frame_end() { SDL_assert(command_encoder); SDL_assert(R_surface); if (pass_encoder) { wgpuRenderPassEncoderEnd(pass_encoder); wgpuRenderPassEncoderRelease(pass_encoder); pass_encoder = NULL; } wgpuTextureViewRelease(surface_texture.view); surface_texture.view = NULL; WGPUCommandBuffer command_buffer = wgpuCommandEncoderFinish(command_encoder, NULL); wgpuCommandEncoderRelease(command_encoder); command_encoder = NULL; wgpuQueueSubmit(queue, 1, &command_buffer); wgpuCommandBufferRelease(command_buffer); wgpuSurfacePresent(surface); wgpuTextureRelease(surface_texture.texture); surface_texture.texture = NULL; R_surface = NULL; } void renderer_frame_set_target(R_Texture target, R_Texture resolve_target, R_Load_Op load_op, R_Store_Op store_op, R_Color clear_color) { SDL_assert(command_encoder); if (pass_encoder) { wgpuRenderPassEncoderEnd(pass_encoder); wgpuRenderPassEncoderRelease(pass_encoder); } pass_encoder = wgpuCommandEncoderBeginRenderPass(command_encoder, &(WGPURenderPassDescriptor){ .colorAttachmentCount = 1, .colorAttachments = &(WGPURenderPassColorAttachment) { .view = target->view, .depthSlice = WGPU_DEPTH_SLICE_UNDEFINED, .resolveTarget = resolve_target ? resolve_target->view : NULL, .loadOp = load_ops[load_op], .storeOp = store_ops[store_op], .clearValue = { .r = clear_color.r, .g = clear_color.g, .b = clear_color.b, .a = clear_color.a }, }, }); } void renderer_frame_set_shader(R_Shader shader) { wgpuRenderPassEncoderSetPipeline(pass_encoder, shaders[shader]); current_shader = shader; } void renderer_frame_draw(R_Buffer vertex_buffer, R_Buffer index_buffer, R_Buffer instance_buffer, Uint32 num_indices, Uint32 num_instances, const R_Draw_Resources *resources) { SDL_assert(pass_encoder); if (resources->num_vertex_sampled_textures || resources->num_vertex_storage_textures || resources->num_vertex_storage_buffers) { WGPUBindGroupEntry *entries = SDL_stack_alloc(WGPUBindGroupEntry, resources->num_vertex_sampled_textures + resources->num_vertex_storage_textures + resources->num_vertex_storage_buffers); for (int i = 0; i < resources->num_vertex_sampled_textures; i++) { entries[i] = (WGPUBindGroupEntry){ .binding = i, .textureView = resources->vertex_sampled_textures[i]->view, }; } Uint32 entry_offset = resources->num_vertex_sampled_textures; for (int i = 0; i < resources->num_vertex_storage_textures; i++) { entries[i] = (WGPUBindGroupEntry){ .binding = i, .textureView = resources->vertex_storage_textures[i]->view, }; } entry_offset += resources->num_vertex_storage_textures; for (int i = 0; i < resources->num_vertex_storage_buffers; i++) { entries[entry_offset + i] = (WGPUBindGroupEntry){ .binding = entry_offset + i, .buffer = resources->vertex_storage_buffers[i]->buffer, .offset = 0, .size = WGPU_WHOLE_SIZE, }; } WGPUBindGroup bind_group = wgpuDeviceCreateBindGroup(device, &(WGPUBindGroupDescriptor){ .layout = wgpuRenderPipelineGetBindGroupLayout(shaders[current_shader], 0), .entryCount = resources->num_vertex_sampled_textures + resources->num_vertex_storage_textures + resources->num_vertex_storage_buffers, .entries = entries, }); SDL_stack_free(entries); wgpuRenderPassEncoderSetBindGroup(pass_encoder, 0, bind_group, 0, NULL); wgpuBindGroupRelease(bind_group); } else { wgpuRenderPassEncoderSetBindGroup(pass_encoder, 0, NULL, 0, NULL); } if (resources->num_vertex_uniform_buffers) { WGPUBindGroupEntry *entries = SDL_stack_alloc(WGPUBindGroupEntry, resources->num_vertex_uniform_buffers); for (int i = 0; i < resources->num_vertex_uniform_buffers; i++) { entries[i] = (WGPUBindGroupEntry){ .binding = i, .buffer = resources->vertex_uniform_buffers[i]->buffer, .offset = 0, .size = WGPU_WHOLE_SIZE, }; } WGPUBindGroup bind_group = wgpuDeviceCreateBindGroup(device, &(WGPUBindGroupDescriptor){ .layout = wgpuRenderPipelineGetBindGroupLayout(shaders[current_shader], 1), .entryCount = resources->num_vertex_uniform_buffers, .entries = entries, }); SDL_stack_free(entries); wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, bind_group, 0, NULL); wgpuBindGroupRelease(bind_group); } else { wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, NULL, 0, NULL); } if (resources->num_fragment_sampled_textures || resources->num_fragment_storage_textures || resources->num_fragment_storage_buffers) { WGPUBindGroupEntry *entries = SDL_stack_alloc(WGPUBindGroupEntry, resources->num_fragment_sampled_textures * 2 + resources->num_fragment_storage_textures + resources->num_fragment_storage_buffers); for (int i = 0; i < resources->num_fragment_sampled_textures; i++) { entries[i * 2] = (WGPUBindGroupEntry){ .binding = i * 2, .textureView = resources->fragment_sampled_textures[i]->view, }; entries[i * 2 + 1] = (WGPUBindGroupEntry){ .binding = i * 2 + 1, .sampler = bilinear_sampler, }; } Uint32 entry_offset = resources->num_fragment_sampled_textures * 2; for (int i = 0; i < resources->num_fragment_storage_textures; i++) { entries[entry_offset + i] = (WGPUBindGroupEntry){ .binding = entry_offset + i, .textureView = resources->fragment_storage_textures[i]->view, }; } entry_offset += resources->num_fragment_storage_textures; for (int i = 0; i < resources->num_fragment_storage_buffers; i++) { entries[entry_offset + i] = (WGPUBindGroupEntry){ .binding = entry_offset + i, .buffer = resources->fragment_storage_buffers[i]->buffer, .offset = 0, .size = WGPU_WHOLE_SIZE, }; } WGPUBindGroup bind_group = wgpuDeviceCreateBindGroup(device, &(WGPUBindGroupDescriptor){ .layout = wgpuRenderPipelineGetBindGroupLayout(shaders[current_shader], 2), .entryCount = resources->num_fragment_sampled_textures * 2 + resources->num_fragment_storage_textures + resources->num_fragment_storage_buffers, .entries = entries, }); SDL_stack_free(entries); wgpuRenderPassEncoderSetBindGroup(pass_encoder, 2, bind_group, 0, NULL); wgpuBindGroupRelease(bind_group); } else { wgpuRenderPassEncoderSetBindGroup(pass_encoder, 2, NULL, 0, NULL); } if (resources->num_fragment_uniform_buffers) { WGPUBindGroupEntry *entries = SDL_stack_alloc(WGPUBindGroupEntry, resources->num_fragment_uniform_buffers); for (int i = 0; i < resources->num_fragment_uniform_buffers; i++) { entries[i] = (WGPUBindGroupEntry){ .binding = i, .buffer = resources->fragment_uniform_buffers[i]->buffer, .offset = 0, .size = WGPU_WHOLE_SIZE, }; } WGPUBindGroup bind_group = wgpuDeviceCreateBindGroup(device, &(WGPUBindGroupDescriptor){ .layout = wgpuRenderPipelineGetBindGroupLayout(shaders[current_shader], 3), .entryCount = resources->num_fragment_uniform_buffers, .entries = entries, }); SDL_stack_free(entries); wgpuRenderPassEncoderSetBindGroup(pass_encoder, 3, bind_group, 0, NULL); wgpuBindGroupRelease(bind_group); } else { wgpuRenderPassEncoderSetBindGroup(pass_encoder, 3, NULL, 0, NULL); } if (vertex_buffer) wgpuRenderPassEncoderSetVertexBuffer(pass_encoder, 0, vertex_buffer->buffer, 0, WGPU_WHOLE_SIZE); if (instance_buffer) wgpuRenderPassEncoderSetVertexBuffer(pass_encoder, vertex_buffer ? 1 : 0, instance_buffer->buffer, 0, WGPU_WHOLE_SIZE); if (index_buffer) { wgpuRenderPassEncoderSetIndexBuffer(pass_encoder, index_buffer->buffer, WGPUIndexFormat_Uint16, 0, WGPU_WHOLE_SIZE); wgpuRenderPassEncoderDrawIndexed(pass_encoder, num_indices, num_instances, 0, 0, 0); } else { wgpuRenderPassEncoderDraw(pass_encoder, num_indices, num_instances, 0, 0); } } static WGPUSurface create_wgpu_surface_for_SDL_window(SDL_Window *window) { SDL_PropertiesID properties = SDL_GetWindowProperties(window); #if defined(SDL_PLATFORM_LINUX) const char *display_system = SDL_GetCurrentVideoDriver(); if (SDL_strcmp(display_system, "wayland") == 0) { void *display = SDL_GetPointerProperty(properties, SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, NULL); void *surface = SDL_GetPointerProperty(properties, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, NULL); if (!display || !surface) return NULL; WGPUSurfaceSourceWaylandSurface surface_source = { .chain = { .next = NULL, .sType = WGPUSType_SurfaceSourceWaylandSurface }, .display = display, .surface = surface, }; WGPUSurfaceDescriptor descriptor = { .nextInChain = &surface_source.chain, .label = { .data = NULL, .length = WGPU_STRLEN }, }; return wgpuInstanceCreateSurface(instance, &descriptor); } else if (SDL_strcmp(display_system, "x11") == 0) { void *display = SDL_GetPointerProperty(properties, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, NULL); uint64_t xlib_window = SDL_GetNumberProperty (properties, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0); if (!display || !xlib_window) return NULL; WGPUSurfaceSourceXlibWindow surface_source = { .chain = { .next = NULL, .sType = WGPUSType_SurfaceSourceXlibWindow }, .display = display, .window = xlib_window, }; WGPUSurfaceDescriptor descriptor = { .nextInChain = &surface_source.chain, .label = { .data = NULL, .length = WGPU_STRLEN }, }; return wgpuInstanceCreateSurface(instance, &descriptor); } else { SDL_LogError(SDL_LOG_CATEGORY_GPU, "create_wgpu_surface_for_SDL_window is not implemented for this display system (%s).", display_system); return NULL; } #elif defined(SDL_PLATFORM_WINDOWS) void *hinstance = SDL_GetPointerProperty(properties, SDL_PROP_WINDOW_WIN32_INSTANCE_POINTER, NULL); void *hwnd = SDL_GetPointerProperty(properties, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); if (!hinstance || !hwnd) return NULL; WGPUSurfaceSourceWindowsHWND surface_source = { .chain = { .next = NULL, .sType = WGPUSType_SurfaceSourceWindowsHWND }, .hinstance = hinstance, .hwnd = hwnd, }; WGPUSurfaceDescriptor descriptor = { .nextInChain = &surface_source.chain, .label = { .data = NULL, .length = WGPU_STRLEN }, }; return wgpuInstanceCreateSurface(instance, &descriptor); #else static_assert(false, "create_wgpu_surface_for_SDL_window is not implemented for this platform."); #endif return NULL; } static void device_request_callback(WGPURequestDeviceStatus status, WGPUDevice device_, WGPUStringView message, void *userdata1, void *userdata2) { if (status != WGPURequestDeviceStatus_Success) { SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to request webgpu device."); init_done = true; return; } device = device_; queue = wgpuDeviceGetQueue(device); init_done = true; } static void adapter_request_callback(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void *userdata1, void *userdata2) { if (status != WGPURequestAdapterStatus_Success) { SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to request webgpu adapter."); init_done = true; return; } WGPURequestDeviceCallbackInfo request_device_callback_info = { .mode = WGPUCallbackMode_AllowProcessEvents, .callback = device_request_callback, }; WGPUDeviceDescriptor device_descriptor = { .label = {}, .requiredFeatureCount = 0, .requiredFeatures = NULL, .requiredLimits = NULL, .defaultQueue = {}, .deviceLostCallbackInfo = {}, .uncapturedErrorCallbackInfo = {}, }; wgpuAdapterRequestDevice(adapter, &device_descriptor, request_device_callback_info); } bool renderer_init(SDL_Window *window_) { window = window_; instance = wgpuCreateInstance(NULL); if (!instance) { SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create webgpu instance."); return false; } surface = create_wgpu_surface_for_SDL_window(window); if (!surface) { SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create webgpu surface for SDL window."); return false; } WGPURequestAdapterCallbackInfo request_adapter_callback_info = { .mode = WGPUCallbackMode_AllowProcessEvents, .callback = adapter_request_callback, }; WGPURequestAdapterOptions request_adapter_options = { .featureLevel = WGPUFeatureLevel_Core, .powerPreference = WGPUPowerPreference_HighPerformance, .forceFallbackAdapter = false, .backendType = WGPUBackendType_Vulkan, .compatibleSurface = surface, }; wgpuInstanceRequestAdapter(instance, &request_adapter_options, request_adapter_callback_info); while (!init_done) { wgpuInstanceProcessEvents(instance); } if (!device) { SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to get webgpu device."); return false; } surface_texture.format = R_TEXTURE_FORMAT_BGRA8_UNORM_SRGB; surface_configuration = (WGPUSurfaceConfiguration){ .device = device, .format = WGPUTextureFormat_BGRA8UnormSrgb, .usage = WGPUTextureUsage_RenderAttachment, .width = 1280, .height = 720, .viewFormatCount = 0, .viewFormats = NULL, .alphaMode = WGPUCompositeAlphaMode_Opaque, .presentMode = WGPUPresentMode_Fifo, }; wgpuSurfaceConfigure(surface, &surface_configuration); WGPUSamplerDescriptor bilinear_sampler_descriptor = { .label = { .data = "bilinear_sampler", .length = WGPU_STRLEN }, .addressModeU = WGPUAddressMode_ClampToEdge, .addressModeV = WGPUAddressMode_ClampToEdge, .addressModeW = WGPUAddressMode_ClampToEdge, .magFilter = WGPUFilterMode_Linear, .minFilter = WGPUFilterMode_Linear, .mipmapFilter = WGPUMipmapFilterMode_Nearest, .maxAnisotropy = 1, }; bilinear_sampler = wgpuDeviceCreateSampler(device, &bilinear_sampler_descriptor); WGPUBlendState blend_state = { .color = { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_SrcAlpha, .dstFactor = WGPUBlendFactor_OneMinusSrcAlpha }, .alpha = { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_SrcAlpha, .dstFactor = WGPUBlendFactor_OneMinusSrcAlpha }, }; WGPUColorTargetState color_target_state = { .format = surface_configuration.format, .blend = &blend_state, .writeMask = WGPUColorWriteMask_All, }; // Shaders { // Character const char shader_source[] = { #embed "shaders/wgsl/character.wgsl" }; WGPUShaderModule shader = wgpuDeviceCreateShaderModule(device, &(WGPUShaderModuleDescriptor){ .nextInChain = (WGPUChainedStruct *)&(WGPUShaderSourceWGSL){ .chain = { .next = NULL, .sType = WGPUSType_ShaderSourceWGSL }, .code = { .data = shader_source, .length = SDL_arraysize(shader_source) }, }, .label = { .data = "character shader", .length = WGPU_STRLEN } }); shaders[R_SHADER_CHARACTER] = wgpuDeviceCreateRenderPipeline(device, &(WGPURenderPipelineDescriptor){ .label = { .data = "character render_pipeline", .length = WGPU_STRLEN }, .layout = wgpuDeviceCreatePipelineLayout(device, &(WGPUPipelineLayoutDescriptor){ .label = { .data = "character pipeline_layout", .length = WGPU_STRLEN }, .bindGroupLayoutCount = 4, .bindGroupLayouts = (WGPUBindGroupLayout[]){ wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 0, .entries = (WGPUBindGroupLayoutEntry[]){ }, }), wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 1, .entries = (WGPUBindGroupLayoutEntry[]){ { .binding = 0, .visibility = WGPUShaderStage_Vertex, .buffer = { .type = WGPUBufferBindingType_Uniform } }, }, }), wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 2, .entries = (WGPUBindGroupLayoutEntry[]){ { .binding = 0, .visibility = WGPUShaderStage_Fragment, .texture = { .sampleType = WGPUTextureSampleType_Float, .viewDimension = WGPUTextureViewDimension_2D } }, { .binding = 1, .visibility = WGPUShaderStage_Fragment, .sampler = { .type = WGPUSamplerBindingType_Filtering } }, }, }), wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 1, .entries = (WGPUBindGroupLayoutEntry[]){ { .binding = 0, .visibility = WGPUShaderStage_Fragment, .buffer = { .type = WGPUBufferBindingType_Uniform } }, }, }), }, }), .vertex = { .module = shader, .entryPoint = { .data = "main_vertex", .length = WGPU_STRLEN }, .constantCount = 0, .constants = NULL, .bufferCount = 1, .buffers = (WGPUVertexBufferLayout[]){ { .stepMode = WGPUVertexStepMode_Instance, .arrayStride = 12, .attributeCount = 2, .attributes = (WGPUVertexAttribute[]){ { .format = WGPUVertexFormat_Float32x2, .offset = 0, .shaderLocation = 2, }, { .format = WGPUVertexFormat_Uint32, .offset = 8, .shaderLocation = 3, }, }, }, }, }, .primitive = { .topology = WGPUPrimitiveTopology_TriangleList, .stripIndexFormat = WGPUIndexFormat_Undefined, .frontFace = WGPUFrontFace_CCW, .cullMode = WGPUCullMode_Back, .unclippedDepth = false, }, .depthStencil = NULL, .multisample = { .count = 4, .mask = ~0u, .alphaToCoverageEnabled = false, }, .fragment = &(WGPUFragmentState){ .module = shader, .entryPoint = { .data = "main_fragment", .length = WGPU_STRLEN }, .constantCount = 0, .constants = NULL, .targetCount = 1, .targets = &color_target_state, }, }); wgpuShaderModuleRelease(shader); } { // Character Shadow const char shader_source[] = { #embed "shaders/wgsl/character_shadow.wgsl" }; WGPUShaderModule shader = wgpuDeviceCreateShaderModule(device, &(WGPUShaderModuleDescriptor){ .nextInChain = (WGPUChainedStruct *)&(WGPUShaderSourceWGSL){ .chain = { .next = NULL, .sType = WGPUSType_ShaderSourceWGSL }, .code = { .data = shader_source, .length = SDL_arraysize(shader_source) }, }, .label = { .data = "character_shadow shader", .length = WGPU_STRLEN } }); shaders[R_SHADER_CHARACTER_SHADOW] = wgpuDeviceCreateRenderPipeline(device, &(WGPURenderPipelineDescriptor){ .label = { .data = "character_shadow render_pipeline", .length = WGPU_STRLEN }, .layout = wgpuDeviceCreatePipelineLayout(device, &(WGPUPipelineLayoutDescriptor){ .label = { .data = "character_shadow pipeline_layout", .length = WGPU_STRLEN }, .bindGroupLayoutCount = 4, .bindGroupLayouts = (WGPUBindGroupLayout[]){ wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 0, .entries = (WGPUBindGroupLayoutEntry[]){ }, }), wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 1, .entries = (WGPUBindGroupLayoutEntry[]){ { .binding = 0, .visibility = WGPUShaderStage_Vertex, .buffer = { .type = WGPUBufferBindingType_Uniform } }, }, }), wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 2, .entries = (WGPUBindGroupLayoutEntry[]){ { .binding = 0, .visibility = WGPUShaderStage_Fragment, .texture = { .sampleType = WGPUTextureSampleType_Float, .viewDimension = WGPUTextureViewDimension_2D } }, { .binding = 1, .visibility = WGPUShaderStage_Fragment, .sampler = { .type = WGPUSamplerBindingType_Filtering } }, }, }), wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 1, .entries = (WGPUBindGroupLayoutEntry[]){ { .binding = 0, .visibility = WGPUShaderStage_Fragment, .buffer = { .type = WGPUBufferBindingType_Uniform } }, }, }), }, }), .vertex = { .module = shader, .entryPoint = { .data = "main_vertex", .length = WGPU_STRLEN }, .constantCount = 0, .constants = NULL, .bufferCount = 1, .buffers = (WGPUVertexBufferLayout[]){ { .stepMode = WGPUVertexStepMode_Instance, .arrayStride = 12, .attributeCount = 2, .attributes = (WGPUVertexAttribute[]){ { .format = WGPUVertexFormat_Float32x2, .offset = 0, .shaderLocation = 2, }, { .format = WGPUVertexFormat_Uint32, .offset = 8, .shaderLocation = 3, }, }, }, }, }, .primitive = { .topology = WGPUPrimitiveTopology_TriangleList, .stripIndexFormat = WGPUIndexFormat_Undefined, .frontFace = WGPUFrontFace_CCW, .cullMode = WGPUCullMode_Back, .unclippedDepth = false, }, .depthStencil = NULL, .multisample = { .count = 4, .mask = ~0u, .alphaToCoverageEnabled = false, }, .fragment = &(WGPUFragmentState){ .module = shader, .entryPoint = { .data = "main_fragment", .length = WGPU_STRLEN }, .constantCount = 0, .constants = NULL, .targetCount = 1, .targets = &color_target_state, }, }); wgpuShaderModuleRelease(shader); } { // World const char shader_source[] = { #embed "shaders/wgsl/world.wgsl" }; WGPUShaderModule shader = wgpuDeviceCreateShaderModule(device, &(WGPUShaderModuleDescriptor){ .nextInChain = (WGPUChainedStruct *)&(WGPUShaderSourceWGSL){ .chain = { .next = NULL, .sType = WGPUSType_ShaderSourceWGSL }, .code = { .data = shader_source, .length = SDL_arraysize(shader_source) }, }, .label = { .data = "world shader", .length = WGPU_STRLEN } }); shaders[R_SHADER_WORLD] = wgpuDeviceCreateRenderPipeline(device, &(WGPURenderPipelineDescriptor){ .label = { .data = "world render_pipeline", .length = WGPU_STRLEN }, .layout = wgpuDeviceCreatePipelineLayout(device, &(WGPUPipelineLayoutDescriptor){ .label = { .data = "world pipeline_layout", .length = WGPU_STRLEN }, .bindGroupLayoutCount = 4, .bindGroupLayouts = (WGPUBindGroupLayout[]){ wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 1, .entries = (WGPUBindGroupLayoutEntry[]){ { .binding = 0, .visibility = WGPUShaderStage_Vertex, .texture = { .sampleType = WGPUTextureSampleType_Uint, .viewDimension = WGPUTextureViewDimension_2D } }, }, }), wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 2, .entries = (WGPUBindGroupLayoutEntry[]){ { .binding = 0, .visibility = WGPUShaderStage_Vertex, .buffer = { .type = WGPUBufferBindingType_Uniform } }, { .binding = 1, .visibility = WGPUShaderStage_Vertex, .buffer = { .type = WGPUBufferBindingType_Uniform } }, }, }), wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 3, .entries = (WGPUBindGroupLayoutEntry[]){ { .binding = 0, .visibility = WGPUShaderStage_Fragment, .texture = { .sampleType = WGPUTextureSampleType_Float, .viewDimension = WGPUTextureViewDimension_2D } }, { .binding = 1, .visibility = WGPUShaderStage_Fragment, .sampler = { .type = WGPUSamplerBindingType_Filtering } }, { .binding = 2, .visibility = WGPUShaderStage_Fragment, .buffer = { .type = WGPUBufferBindingType_ReadOnlyStorage } }, }, }), wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 1, .entries = (WGPUBindGroupLayoutEntry[]){ { .binding = 0, .visibility = WGPUShaderStage_Fragment, .buffer = { .type = WGPUBufferBindingType_Uniform } }, }, }), }, }), .vertex = { .module = shader, .entryPoint = { .data = "main_vertex", .length = WGPU_STRLEN }, .constantCount = 0, .constants = NULL, .bufferCount = 0, .buffers = (WGPUVertexBufferLayout[]){ }, }, .primitive = { .topology = WGPUPrimitiveTopology_TriangleList, .stripIndexFormat = WGPUIndexFormat_Undefined, .frontFace = WGPUFrontFace_CCW, .cullMode = WGPUCullMode_Back, .unclippedDepth = false, }, .depthStencil = NULL, .multisample = { .count = 4, .mask = ~0u, .alphaToCoverageEnabled = false, }, .fragment = &(WGPUFragmentState){ .module = shader, .entryPoint = { .data = "main_fragment", .length = WGPU_STRLEN }, .constantCount = 0, .constants = NULL, .targetCount = 1, .targets = &color_target_state, }, }); wgpuShaderModuleRelease(shader); } { // Grid const char shader_source[] = { #embed "shaders/wgsl/grid.wgsl" }; WGPUShaderModule shader = wgpuDeviceCreateShaderModule(device, &(WGPUShaderModuleDescriptor){ .nextInChain = (WGPUChainedStruct *)&(WGPUShaderSourceWGSL){ .chain = { .next = NULL, .sType = WGPUSType_ShaderSourceWGSL }, .code = { .data = shader_source, .length = SDL_arraysize(shader_source) }, }, .label = { .data = "grid shader", .length = WGPU_STRLEN } }); shaders[R_SHADER_GRID] = wgpuDeviceCreateRenderPipeline(device, &(WGPURenderPipelineDescriptor){ .label = { .data = "grid render_pipeline", .length = WGPU_STRLEN }, .layout = wgpuDeviceCreatePipelineLayout(device, &(WGPUPipelineLayoutDescriptor){ .label = { .data = "grid pipeline_layout", .length = WGPU_STRLEN }, .bindGroupLayoutCount = 4, .bindGroupLayouts = (WGPUBindGroupLayout[]){ wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 0, .entries = (WGPUBindGroupLayoutEntry[]){ }, }), wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 2, .entries = (WGPUBindGroupLayoutEntry[]){ { .binding = 0, .visibility = WGPUShaderStage_Vertex, .buffer = { .type = WGPUBufferBindingType_Uniform } }, { .binding = 1, .visibility = WGPUShaderStage_Vertex, .buffer = { .type = WGPUBufferBindingType_Uniform } }, }, }), wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 0, .entries = (WGPUBindGroupLayoutEntry[]){ }, }), wgpuDeviceCreateBindGroupLayout(device, &(WGPUBindGroupLayoutDescriptor){ .entryCount = 0, .entries = (WGPUBindGroupLayoutEntry[]){ }, }), }, }), .vertex = { .module = shader, .entryPoint = { .data = "main_vertex", .length = WGPU_STRLEN }, .constantCount = 0, .constants = NULL, .bufferCount = 0, .buffers = NULL, }, .primitive = { .topology = WGPUPrimitiveTopology_LineList, .stripIndexFormat = WGPUIndexFormat_Undefined, .frontFace = WGPUFrontFace_CCW, .cullMode = WGPUCullMode_Back, .unclippedDepth = false, }, .depthStencil = NULL, .multisample = { .count = 4, .mask = ~0u, .alphaToCoverageEnabled = false, }, .fragment = &(WGPUFragmentState){ .module = shader, .entryPoint = { .data = "main_fragment", .length = WGPU_STRLEN }, .constantCount = 0, .constants = NULL, .targetCount = 1, .targets = &color_target_state, }, }); wgpuShaderModuleRelease(shader); } for (int i = 0; i < R_SHADER_COUNT; i++) { SDL_assert(shaders[i]); } return true; } void ImGui_ImplRenderer_Init() { cImGui_ImplWGPU_Init(&(ImGui_ImplWGPU_InitInfo){ .Device = device, .NumFramesInFlight = 3, .RenderTargetFormat = surface_configuration.format, .PipelineMultisampleState = { .count = 4, .mask = ~0, .alphaToCoverageEnabled = false, }, }); } void ImGui_ImplRenderer_NewFrame() { cImGui_ImplWGPU_NewFrame(); } void ImGui_ImplRenderer_Shutdown() { cImGui_ImplWGPU_Shutdown(); } void ImGui_ImplRenderer_RenderDrawData(void* draw_data) { cImGui_ImplWGPU_RenderDrawData(draw_data, pass_encoder); } Uint64 ImGui_ImplRenderer_GetTextureID(R_Texture texture) { return (Uint64)texture->view; }